base mod created
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.pathfinding.Path;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class EntityAIAttackMelee extends EntityAIBase
|
||||
{
|
||||
World world;
|
||||
protected EntityCreature attacker;
|
||||
/** An amount of decrementing ticks that allows the entity to attack once the tick reaches 0. */
|
||||
protected int attackTick;
|
||||
/** The speed with which the mob will approach the target */
|
||||
double speedTowardsTarget;
|
||||
/** When true, the mob will continue chasing its target, even if it can't find a path to them right now. */
|
||||
boolean longMemory;
|
||||
/** The PathEntity of our entity. */
|
||||
Path path;
|
||||
private int delayCounter;
|
||||
private double targetX;
|
||||
private double targetY;
|
||||
private double targetZ;
|
||||
protected final int attackInterval = 20;
|
||||
private int failedPathFindingPenalty = 0;
|
||||
private boolean canPenalize = false;
|
||||
|
||||
public EntityAIAttackMelee(EntityCreature creature, double speedIn, boolean useLongMemory)
|
||||
{
|
||||
this.attacker = creature;
|
||||
this.world = creature.world;
|
||||
this.speedTowardsTarget = speedIn;
|
||||
this.longMemory = useLongMemory;
|
||||
this.setMutexBits(3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.attacker.getAttackTarget();
|
||||
|
||||
if (entitylivingbase == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!entitylivingbase.isEntityAlive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (canPenalize)
|
||||
{
|
||||
if (--this.delayCounter <= 0)
|
||||
{
|
||||
this.path = this.attacker.getNavigator().getPathToEntityLiving(entitylivingbase);
|
||||
this.delayCounter = 4 + this.attacker.getRNG().nextInt(7);
|
||||
return this.path != null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
this.path = this.attacker.getNavigator().getPathToEntityLiving(entitylivingbase);
|
||||
|
||||
if (this.path != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.getAttackReachSqr(entitylivingbase) >= this.attacker.getDistanceSq(entitylivingbase.posX, entitylivingbase.getEntityBoundingBox().minY, entitylivingbase.posZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.attacker.getAttackTarget();
|
||||
|
||||
if (entitylivingbase == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!entitylivingbase.isEntityAlive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!this.longMemory)
|
||||
{
|
||||
return !this.attacker.getNavigator().noPath();
|
||||
}
|
||||
else if (!this.attacker.isWithinHomeDistanceFromPosition(new BlockPos(entitylivingbase)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return !(entitylivingbase instanceof EntityPlayer) || !((EntityPlayer)entitylivingbase).isSpectator() && !((EntityPlayer)entitylivingbase).isCreative();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.attacker.getNavigator().setPath(this.path, this.speedTowardsTarget);
|
||||
this.delayCounter = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.attacker.getAttackTarget();
|
||||
|
||||
if (entitylivingbase instanceof EntityPlayer && (((EntityPlayer)entitylivingbase).isSpectator() || ((EntityPlayer)entitylivingbase).isCreative()))
|
||||
{
|
||||
this.attacker.setAttackTarget((EntityLivingBase)null);
|
||||
}
|
||||
|
||||
this.attacker.getNavigator().clearPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.attacker.getAttackTarget();
|
||||
this.attacker.getLookHelper().setLookPositionWithEntity(entitylivingbase, 30.0F, 30.0F);
|
||||
double d0 = this.attacker.getDistanceSq(entitylivingbase.posX, entitylivingbase.getEntityBoundingBox().minY, entitylivingbase.posZ);
|
||||
--this.delayCounter;
|
||||
|
||||
if ((this.longMemory || this.attacker.getEntitySenses().canSee(entitylivingbase)) && this.delayCounter <= 0 && (this.targetX == 0.0D && this.targetY == 0.0D && this.targetZ == 0.0D || entitylivingbase.getDistanceSq(this.targetX, this.targetY, this.targetZ) >= 1.0D || this.attacker.getRNG().nextFloat() < 0.05F))
|
||||
{
|
||||
this.targetX = entitylivingbase.posX;
|
||||
this.targetY = entitylivingbase.getEntityBoundingBox().minY;
|
||||
this.targetZ = entitylivingbase.posZ;
|
||||
this.delayCounter = 4 + this.attacker.getRNG().nextInt(7);
|
||||
|
||||
if (this.canPenalize)
|
||||
{
|
||||
this.delayCounter += failedPathFindingPenalty;
|
||||
if (this.attacker.getNavigator().getPath() != null)
|
||||
{
|
||||
net.minecraft.pathfinding.PathPoint finalPathPoint = this.attacker.getNavigator().getPath().getFinalPathPoint();
|
||||
if (finalPathPoint != null && entitylivingbase.getDistanceSq(finalPathPoint.x, finalPathPoint.y, finalPathPoint.z) < 1)
|
||||
failedPathFindingPenalty = 0;
|
||||
else
|
||||
failedPathFindingPenalty += 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
failedPathFindingPenalty += 10;
|
||||
}
|
||||
}
|
||||
|
||||
if (d0 > 1024.0D)
|
||||
{
|
||||
this.delayCounter += 10;
|
||||
}
|
||||
else if (d0 > 256.0D)
|
||||
{
|
||||
this.delayCounter += 5;
|
||||
}
|
||||
|
||||
if (!this.attacker.getNavigator().tryMoveToEntityLiving(entitylivingbase, this.speedTowardsTarget))
|
||||
{
|
||||
this.delayCounter += 15;
|
||||
}
|
||||
}
|
||||
|
||||
this.attackTick = Math.max(this.attackTick - 1, 0);
|
||||
this.checkAndPerformAttack(entitylivingbase, d0);
|
||||
}
|
||||
|
||||
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.swingArm(EnumHand.MAIN_HAND);
|
||||
this.attacker.attackEntityAsMob(p_190102_1_);
|
||||
}
|
||||
}
|
||||
|
||||
protected double getAttackReachSqr(EntityLivingBase attackTarget)
|
||||
{
|
||||
return (double)(this.attacker.width * 2.0F * this.attacker.width * 2.0F + attackTarget.width);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.IRangedAttackMob;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
public class EntityAIAttackRanged extends EntityAIBase
|
||||
{
|
||||
/** The entity the AI instance has been applied to */
|
||||
private final EntityLiving entityHost;
|
||||
/** The entity (as a RangedAttackMob) the AI instance has been applied to. */
|
||||
private final IRangedAttackMob rangedAttackEntityHost;
|
||||
private EntityLivingBase attackTarget;
|
||||
/**
|
||||
* A decrementing tick that spawns a ranged attack once this value reaches 0. It is then set back to the
|
||||
* maxRangedAttackTime.
|
||||
*/
|
||||
private int rangedAttackTime;
|
||||
private final double entityMoveSpeed;
|
||||
private int seeTime;
|
||||
private final int attackIntervalMin;
|
||||
/** The maximum time the AI has to wait before peforming another ranged attack. */
|
||||
private final int maxRangedAttackTime;
|
||||
private final float attackRadius;
|
||||
private final float maxAttackDistance;
|
||||
|
||||
public EntityAIAttackRanged(IRangedAttackMob attacker, double movespeed, int maxAttackTime, float maxAttackDistanceIn)
|
||||
{
|
||||
this(attacker, movespeed, maxAttackTime, maxAttackTime, maxAttackDistanceIn);
|
||||
}
|
||||
|
||||
public EntityAIAttackRanged(IRangedAttackMob attacker, double movespeed, int p_i1650_4_, int maxAttackTime, float maxAttackDistanceIn)
|
||||
{
|
||||
this.rangedAttackTime = -1;
|
||||
|
||||
if (!(attacker instanceof EntityLivingBase))
|
||||
{
|
||||
throw new IllegalArgumentException("ArrowAttackGoal requires Mob implements RangedAttackMob");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.rangedAttackEntityHost = attacker;
|
||||
this.entityHost = (EntityLiving)attacker;
|
||||
this.entityMoveSpeed = movespeed;
|
||||
this.attackIntervalMin = p_i1650_4_;
|
||||
this.maxRangedAttackTime = maxAttackTime;
|
||||
this.attackRadius = maxAttackDistanceIn;
|
||||
this.maxAttackDistance = maxAttackDistanceIn * maxAttackDistanceIn;
|
||||
this.setMutexBits(3);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.entityHost.getAttackTarget();
|
||||
|
||||
if (entitylivingbase == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.attackTarget = entitylivingbase;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return this.shouldExecute() || !this.entityHost.getNavigator().noPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.attackTarget = null;
|
||||
this.seeTime = 0;
|
||||
this.rangedAttackTime = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
double d0 = this.entityHost.getDistanceSq(this.attackTarget.posX, this.attackTarget.getEntityBoundingBox().minY, this.attackTarget.posZ);
|
||||
boolean flag = this.entityHost.getEntitySenses().canSee(this.attackTarget);
|
||||
|
||||
if (flag)
|
||||
{
|
||||
++this.seeTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.seeTime = 0;
|
||||
}
|
||||
|
||||
if (d0 <= (double)this.maxAttackDistance && this.seeTime >= 20)
|
||||
{
|
||||
this.entityHost.getNavigator().clearPath();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.entityHost.getNavigator().tryMoveToEntityLiving(this.attackTarget, this.entityMoveSpeed);
|
||||
}
|
||||
|
||||
this.entityHost.getLookHelper().setLookPositionWithEntity(this.attackTarget, 30.0F, 30.0F);
|
||||
|
||||
if (--this.rangedAttackTime == 0)
|
||||
{
|
||||
if (!flag)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float f = MathHelper.sqrt(d0) / this.attackRadius;
|
||||
float lvt_5_1_ = MathHelper.clamp(f, 0.1F, 1.0F);
|
||||
this.rangedAttackEntityHost.attackEntityWithRangedAttack(this.attackTarget, lvt_5_1_);
|
||||
this.rangedAttackTime = MathHelper.floor(f * (float)(this.maxRangedAttackTime - this.attackIntervalMin) + (float)this.attackIntervalMin);
|
||||
}
|
||||
else if (this.rangedAttackTime < 0)
|
||||
{
|
||||
float f2 = MathHelper.sqrt(d0) / this.attackRadius;
|
||||
this.rangedAttackTime = MathHelper.floor(f2 * (float)(this.maxRangedAttackTime - this.attackIntervalMin) + (float)this.attackIntervalMin);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.IRangedAttackMob;
|
||||
import net.minecraft.entity.monster.EntityMob;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.item.ItemBow;
|
||||
import net.minecraft.util.EnumHand;
|
||||
|
||||
public class EntityAIAttackRangedBow<T extends EntityMob & IRangedAttackMob> extends EntityAIBase
|
||||
{
|
||||
private final T entity;
|
||||
private final double moveSpeedAmp;
|
||||
private int attackCooldown;
|
||||
private final float maxAttackDistance;
|
||||
private int attackTime = -1;
|
||||
private int seeTime;
|
||||
private boolean strafingClockwise;
|
||||
private boolean strafingBackwards;
|
||||
private int strafingTime = -1;
|
||||
|
||||
public EntityAIAttackRangedBow(T p_i47515_1_, double p_i47515_2_, int p_i47515_4_, float p_i47515_5_)
|
||||
{
|
||||
this.entity = p_i47515_1_;
|
||||
this.moveSpeedAmp = p_i47515_2_;
|
||||
this.attackCooldown = p_i47515_4_;
|
||||
this.maxAttackDistance = p_i47515_5_ * p_i47515_5_;
|
||||
this.setMutexBits(3);
|
||||
}
|
||||
|
||||
public void setAttackCooldown(int p_189428_1_)
|
||||
{
|
||||
this.attackCooldown = p_189428_1_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
return this.entity.getAttackTarget() == null ? false : this.isBowInMainhand();
|
||||
}
|
||||
|
||||
protected boolean isBowInMainhand()
|
||||
{
|
||||
return !this.entity.getHeldItemMainhand().isEmpty() && this.entity.getHeldItemMainhand().getItem() == Items.BOW;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return (this.shouldExecute() || !this.entity.getNavigator().noPath()) && this.isBowInMainhand();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
super.startExecuting();
|
||||
((IRangedAttackMob)this.entity).setSwingingArms(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
super.resetTask();
|
||||
((IRangedAttackMob)this.entity).setSwingingArms(false);
|
||||
this.seeTime = 0;
|
||||
this.attackTime = -1;
|
||||
this.entity.resetActiveHand();
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.entity.getAttackTarget();
|
||||
|
||||
if (entitylivingbase != null)
|
||||
{
|
||||
double d0 = this.entity.getDistanceSq(entitylivingbase.posX, entitylivingbase.getEntityBoundingBox().minY, entitylivingbase.posZ);
|
||||
boolean flag = this.entity.getEntitySenses().canSee(entitylivingbase);
|
||||
boolean flag1 = this.seeTime > 0;
|
||||
|
||||
if (flag != flag1)
|
||||
{
|
||||
this.seeTime = 0;
|
||||
}
|
||||
|
||||
if (flag)
|
||||
{
|
||||
++this.seeTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
--this.seeTime;
|
||||
}
|
||||
|
||||
if (d0 <= (double)this.maxAttackDistance && this.seeTime >= 20)
|
||||
{
|
||||
this.entity.getNavigator().clearPath();
|
||||
++this.strafingTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.entity.getNavigator().tryMoveToEntityLiving(entitylivingbase, this.moveSpeedAmp);
|
||||
this.strafingTime = -1;
|
||||
}
|
||||
|
||||
if (this.strafingTime >= 20)
|
||||
{
|
||||
if ((double)this.entity.getRNG().nextFloat() < 0.3D)
|
||||
{
|
||||
this.strafingClockwise = !this.strafingClockwise;
|
||||
}
|
||||
|
||||
if ((double)this.entity.getRNG().nextFloat() < 0.3D)
|
||||
{
|
||||
this.strafingBackwards = !this.strafingBackwards;
|
||||
}
|
||||
|
||||
this.strafingTime = 0;
|
||||
}
|
||||
|
||||
if (this.strafingTime > -1)
|
||||
{
|
||||
if (d0 > (double)(this.maxAttackDistance * 0.75F))
|
||||
{
|
||||
this.strafingBackwards = false;
|
||||
}
|
||||
else if (d0 < (double)(this.maxAttackDistance * 0.25F))
|
||||
{
|
||||
this.strafingBackwards = true;
|
||||
}
|
||||
|
||||
this.entity.getMoveHelper().strafe(this.strafingBackwards ? -0.5F : 0.5F, this.strafingClockwise ? 0.5F : -0.5F);
|
||||
this.entity.faceEntity(entitylivingbase, 30.0F, 30.0F);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.entity.getLookHelper().setLookPositionWithEntity(entitylivingbase, 30.0F, 30.0F);
|
||||
}
|
||||
|
||||
if (this.entity.isHandActive())
|
||||
{
|
||||
if (!flag && this.seeTime < -60)
|
||||
{
|
||||
this.entity.resetActiveHand();
|
||||
}
|
||||
else if (flag)
|
||||
{
|
||||
int i = this.entity.getItemInUseMaxCount();
|
||||
|
||||
if (i >= 20)
|
||||
{
|
||||
this.entity.resetActiveHand();
|
||||
((IRangedAttackMob)this.entity).attackEntityWithRangedAttack(entitylivingbase, ItemBow.getArrowVelocity(i));
|
||||
this.attackTime = this.attackCooldown;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (--this.attackTime <= 0 && this.seeTime >= -60)
|
||||
{
|
||||
this.entity.setActiveHand(EnumHand.MAIN_HAND);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.pathfinding.Path;
|
||||
import net.minecraft.pathfinding.PathNavigate;
|
||||
import net.minecraft.util.EntitySelectors;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public class EntityAIAvoidEntity<T extends Entity> extends EntityAIBase
|
||||
{
|
||||
private final Predicate<Entity> canBeSeenSelector;
|
||||
/** The entity we are attached to */
|
||||
protected EntityCreature entity;
|
||||
private final double farSpeed;
|
||||
private final double nearSpeed;
|
||||
protected T closestLivingEntity;
|
||||
private final float avoidDistance;
|
||||
/** The PathEntity of our entity */
|
||||
private Path path;
|
||||
/** The PathNavigate of our entity */
|
||||
private final PathNavigate navigation;
|
||||
/** Class of entity this behavior seeks to avoid */
|
||||
private final Class<T> classToAvoid;
|
||||
private final Predicate <? super T > avoidTargetSelector;
|
||||
|
||||
public EntityAIAvoidEntity(EntityCreature entityIn, Class<T> classToAvoidIn, float avoidDistanceIn, double farSpeedIn, double nearSpeedIn)
|
||||
{
|
||||
this(entityIn, classToAvoidIn, Predicates.alwaysTrue(), avoidDistanceIn, farSpeedIn, nearSpeedIn);
|
||||
}
|
||||
|
||||
public EntityAIAvoidEntity(EntityCreature entityIn, Class<T> classToAvoidIn, Predicate <? super T > avoidTargetSelectorIn, float avoidDistanceIn, double farSpeedIn, double nearSpeedIn)
|
||||
{
|
||||
this.canBeSeenSelector = new Predicate<Entity>()
|
||||
{
|
||||
public boolean apply(@Nullable Entity p_apply_1_)
|
||||
{
|
||||
return p_apply_1_.isEntityAlive() && EntityAIAvoidEntity.this.entity.getEntitySenses().canSee(p_apply_1_) && !EntityAIAvoidEntity.this.entity.isOnSameTeam(p_apply_1_);
|
||||
}
|
||||
};
|
||||
this.entity = entityIn;
|
||||
this.classToAvoid = classToAvoidIn;
|
||||
this.avoidTargetSelector = avoidTargetSelectorIn;
|
||||
this.avoidDistance = avoidDistanceIn;
|
||||
this.farSpeed = farSpeedIn;
|
||||
this.nearSpeed = nearSpeedIn;
|
||||
this.navigation = entityIn.getNavigator();
|
||||
this.setMutexBits(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
List<T> list = this.entity.world.<T>getEntitiesWithinAABB(this.classToAvoid, this.entity.getEntityBoundingBox().grow((double)this.avoidDistance, 3.0D, (double)this.avoidDistance), Predicates.and(EntitySelectors.CAN_AI_TARGET, this.canBeSeenSelector, this.avoidTargetSelector));
|
||||
|
||||
if (list.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.closestLivingEntity = list.get(0);
|
||||
Vec3d vec3d = RandomPositionGenerator.findRandomTargetBlockAwayFrom(this.entity, 16, 7, new Vec3d(this.closestLivingEntity.posX, this.closestLivingEntity.posY, this.closestLivingEntity.posZ));
|
||||
|
||||
if (vec3d == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (this.closestLivingEntity.getDistanceSq(vec3d.x, vec3d.y, vec3d.z) < this.closestLivingEntity.getDistanceSq(this.entity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.path = this.navigation.getPathToXYZ(vec3d.x, vec3d.y, vec3d.z);
|
||||
return this.path != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return !this.navigation.noPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.navigation.setPath(this.path, this.farSpeed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.closestLivingEntity = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
if (this.entity.getDistanceSq(this.closestLivingEntity) < 49.0D)
|
||||
{
|
||||
this.entity.getNavigator().setSpeed(this.nearSpeed);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.entity.getNavigator().setSpeed(this.farSpeed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
public abstract class EntityAIBase
|
||||
{
|
||||
/**
|
||||
* A bitmask telling which other tasks may not run concurrently. The test is a simple bitwise AND - if it yields
|
||||
* zero, the two tasks may run concurrently, if not - they must run exclusively from each other.
|
||||
*/
|
||||
private int mutexBits;
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public abstract boolean shouldExecute();
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return this.shouldExecute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this AI Task is interruptible by a higher (= lower value) priority task. All vanilla AITask have
|
||||
* this value set to true.
|
||||
*/
|
||||
public boolean isInterruptible()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the mutex bitflags, see getMutexBits. Flag 1 for motion, flag 2 for look/head movement, flag 4 for
|
||||
* swimming/misc. Flags can be OR'ed.
|
||||
*/
|
||||
public void setMutexBits(int mutexBitsIn)
|
||||
{
|
||||
this.mutexBits = mutexBitsIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get what actions this task will take that may potentially conflict with other tasks. The test is a simple bitwise
|
||||
* AND - if it yields zero, the two tasks may run concurrently, if not - they must run exclusively from each other.
|
||||
* See setMutextBits.
|
||||
*/
|
||||
public int getMutexBits()
|
||||
{
|
||||
return this.mutexBits;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.passive.EntityWolf;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class EntityAIBeg extends EntityAIBase
|
||||
{
|
||||
private final EntityWolf wolf;
|
||||
private EntityPlayer player;
|
||||
private final World world;
|
||||
private final float minPlayerDistance;
|
||||
private int timeoutCounter;
|
||||
|
||||
public EntityAIBeg(EntityWolf wolf, float minDistance)
|
||||
{
|
||||
this.wolf = wolf;
|
||||
this.world = wolf.world;
|
||||
this.minPlayerDistance = minDistance;
|
||||
this.setMutexBits(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
this.player = this.world.getClosestPlayerToEntity(this.wolf, (double)this.minPlayerDistance);
|
||||
return this.player == null ? false : this.hasTemptationItemInHand(this.player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
if (!this.player.isEntityAlive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (this.wolf.getDistanceSq(this.player) > (double)(this.minPlayerDistance * this.minPlayerDistance))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.timeoutCounter > 0 && this.hasTemptationItemInHand(this.player);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.wolf.setBegging(true);
|
||||
this.timeoutCounter = 40 + this.wolf.getRNG().nextInt(40);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.wolf.setBegging(false);
|
||||
this.player = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
this.wolf.getLookHelper().setLookPosition(this.player.posX, this.player.posY + (double)this.player.getEyeHeight(), this.player.posZ, 10.0F, (float)this.wolf.getVerticalFaceSpeed());
|
||||
--this.timeoutCounter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if the Player has the Bone in the hand.
|
||||
*/
|
||||
private boolean hasTemptationItemInHand(EntityPlayer player)
|
||||
{
|
||||
for (EnumHand enumhand : EnumHand.values())
|
||||
{
|
||||
ItemStack itemstack = player.getHeldItem(enumhand);
|
||||
|
||||
if (this.wolf.isTamed() && itemstack.getItem() == Items.BONE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.wolf.isBreedingItem(itemstack))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockDoor;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.world.EnumDifficulty;
|
||||
|
||||
public class EntityAIBreakDoor extends EntityAIDoorInteract
|
||||
{
|
||||
private int breakingTime;
|
||||
private int previousBreakProgress = -1;
|
||||
|
||||
public EntityAIBreakDoor(EntityLiving entityIn)
|
||||
{
|
||||
super(entityIn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (!super.shouldExecute())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!net.minecraftforge.event.ForgeEventFactory.getMobGriefingEvent(this.entity.world, this.entity) || !this.entity.world.getBlockState(this.doorPosition).getBlock().canEntityDestroy(this.entity.world.getBlockState(this.doorPosition), this.entity.world, this.doorPosition, this.entity) || !net.minecraftforge.event.ForgeEventFactory.onEntityDestroyBlock(this.entity, this.doorPosition, this.entity.world.getBlockState(this.doorPosition)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
BlockDoor blockdoor = this.doorBlock;
|
||||
return !BlockDoor.isOpen(this.entity.world, this.doorPosition);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
super.startExecuting();
|
||||
this.breakingTime = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
double d0 = this.entity.getDistanceSq(this.doorPosition);
|
||||
boolean flag;
|
||||
|
||||
if (this.breakingTime <= 240)
|
||||
{
|
||||
BlockDoor blockdoor = this.doorBlock;
|
||||
|
||||
if (!BlockDoor.isOpen(this.entity.world, this.doorPosition) && d0 < 4.0D)
|
||||
{
|
||||
flag = true;
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
|
||||
flag = false;
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
super.resetTask();
|
||||
this.entity.world.sendBlockBreakProgress(this.entity.getEntityId(), this.doorPosition, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
super.updateTask();
|
||||
|
||||
if (this.entity.getRNG().nextInt(20) == 0)
|
||||
{
|
||||
this.entity.world.playEvent(1019, this.doorPosition, 0);
|
||||
}
|
||||
|
||||
++this.breakingTime;
|
||||
int i = (int)((float)this.breakingTime / 240.0F * 10.0F);
|
||||
|
||||
if (i != this.previousBreakProgress)
|
||||
{
|
||||
this.entity.world.sendBlockBreakProgress(this.entity.getEntityId(), this.doorPosition, i);
|
||||
this.previousBreakProgress = i;
|
||||
}
|
||||
|
||||
if (this.breakingTime == 240 && this.entity.world.getDifficulty() == EnumDifficulty.HARD)
|
||||
{
|
||||
this.entity.world.setBlockToAir(this.doorPosition);
|
||||
this.entity.world.playEvent(1021, this.doorPosition, 0);
|
||||
this.entity.world.playEvent(2001, this.doorPosition, Block.getIdFromBlock(this.doorBlock));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.monster.EntityCreeper;
|
||||
|
||||
public class EntityAICreeperSwell extends EntityAIBase
|
||||
{
|
||||
/** The creeper that is swelling. */
|
||||
EntityCreeper swellingCreeper;
|
||||
/** The creeper's attack target. This is used for the changing of the creeper's state. */
|
||||
EntityLivingBase creeperAttackTarget;
|
||||
|
||||
public EntityAICreeperSwell(EntityCreeper entitycreeperIn)
|
||||
{
|
||||
this.swellingCreeper = entitycreeperIn;
|
||||
this.setMutexBits(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.swellingCreeper.getAttackTarget();
|
||||
return this.swellingCreeper.getCreeperState() > 0 || entitylivingbase != null && this.swellingCreeper.getDistanceSq(entitylivingbase) < 9.0D;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.swellingCreeper.getNavigator().clearPath();
|
||||
this.creeperAttackTarget = this.swellingCreeper.getAttackTarget();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.creeperAttackTarget = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
if (this.creeperAttackTarget == null)
|
||||
{
|
||||
this.swellingCreeper.setCreeperState(-1);
|
||||
}
|
||||
else if (this.swellingCreeper.getDistanceSq(this.creeperAttackTarget) > 49.0D)
|
||||
{
|
||||
this.swellingCreeper.setCreeperState(-1);
|
||||
}
|
||||
else if (!this.swellingCreeper.getEntitySenses().canSee(this.creeperAttackTarget))
|
||||
{
|
||||
this.swellingCreeper.setCreeperState(-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.swellingCreeper.setCreeperState(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.monster.EntityCreeper;
|
||||
import net.minecraft.entity.monster.EntityIronGolem;
|
||||
import net.minecraft.village.Village;
|
||||
|
||||
public class EntityAIDefendVillage extends EntityAITarget
|
||||
{
|
||||
EntityIronGolem irongolem;
|
||||
/** The aggressor of the iron golem's village which is now the golem's attack target. */
|
||||
EntityLivingBase villageAgressorTarget;
|
||||
|
||||
public EntityAIDefendVillage(EntityIronGolem ironGolemIn)
|
||||
{
|
||||
super(ironGolemIn, false, true);
|
||||
this.irongolem = ironGolemIn;
|
||||
this.setMutexBits(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
Village village = this.irongolem.getVillage();
|
||||
|
||||
if (village == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.villageAgressorTarget = village.findNearestVillageAggressor(this.irongolem);
|
||||
|
||||
if (this.villageAgressorTarget instanceof EntityCreeper)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (this.isSuitableTarget(this.villageAgressorTarget, false))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (this.taskOwner.getRNG().nextInt(20) == 0)
|
||||
{
|
||||
this.villageAgressorTarget = village.getNearestTargetPlayer(this.irongolem);
|
||||
return this.isSuitableTarget(this.villageAgressorTarget, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.irongolem.setAttackTarget(this.villageAgressorTarget);
|
||||
super.startExecuting();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockDoor;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.pathfinding.Path;
|
||||
import net.minecraft.pathfinding.PathNavigateGround;
|
||||
import net.minecraft.pathfinding.PathPoint;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
public abstract class EntityAIDoorInteract extends EntityAIBase
|
||||
{
|
||||
protected EntityLiving entity;
|
||||
protected BlockPos doorPosition = BlockPos.ORIGIN;
|
||||
/** The wooden door block */
|
||||
protected BlockDoor doorBlock;
|
||||
/** If is true then the Entity has stopped Door Interaction and compoleted the task. */
|
||||
boolean hasStoppedDoorInteraction;
|
||||
float entityPositionX;
|
||||
float entityPositionZ;
|
||||
|
||||
public EntityAIDoorInteract(EntityLiving entityIn)
|
||||
{
|
||||
this.entity = entityIn;
|
||||
|
||||
if (!(entityIn.getNavigator() instanceof PathNavigateGround))
|
||||
{
|
||||
throw new IllegalArgumentException("Unsupported mob type for DoorInteractGoal");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (!this.entity.collidedHorizontally)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
PathNavigateGround pathnavigateground = (PathNavigateGround)this.entity.getNavigator();
|
||||
Path path = pathnavigateground.getPath();
|
||||
|
||||
if (path != null && !path.isFinished() && pathnavigateground.getEnterDoors())
|
||||
{
|
||||
for (int i = 0; i < Math.min(path.getCurrentPathIndex() + 2, path.getCurrentPathLength()); ++i)
|
||||
{
|
||||
PathPoint pathpoint = path.getPathPointFromIndex(i);
|
||||
this.doorPosition = new BlockPos(pathpoint.x, pathpoint.y + 1, pathpoint.z);
|
||||
|
||||
if (this.entity.getDistanceSq((double)this.doorPosition.getX(), this.entity.posY, (double)this.doorPosition.getZ()) <= 2.25D)
|
||||
{
|
||||
this.doorBlock = this.getBlockDoor(this.doorPosition);
|
||||
|
||||
if (this.doorBlock != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.doorPosition = (new BlockPos(this.entity)).up();
|
||||
this.doorBlock = this.getBlockDoor(this.doorPosition);
|
||||
return this.doorBlock != null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return !this.hasStoppedDoorInteraction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.hasStoppedDoorInteraction = false;
|
||||
this.entityPositionX = (float)((double)((float)this.doorPosition.getX() + 0.5F) - this.entity.posX);
|
||||
this.entityPositionZ = (float)((double)((float)this.doorPosition.getZ() + 0.5F) - this.entity.posZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
float f = (float)((double)((float)this.doorPosition.getX() + 0.5F) - this.entity.posX);
|
||||
float f1 = (float)((double)((float)this.doorPosition.getZ() + 0.5F) - this.entity.posZ);
|
||||
float f2 = this.entityPositionX * f + this.entityPositionZ * f1;
|
||||
|
||||
if (f2 < 0.0F)
|
||||
{
|
||||
this.hasStoppedDoorInteraction = true;
|
||||
}
|
||||
}
|
||||
|
||||
private BlockDoor getBlockDoor(BlockPos pos)
|
||||
{
|
||||
IBlockState iblockstate = this.entity.world.getBlockState(pos);
|
||||
Block block = iblockstate.getBlock();
|
||||
return block instanceof BlockDoor && iblockstate.getMaterial() == Material.WOOD ? (BlockDoor)block : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockTallGrass;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.block.state.pattern.BlockStateMatcher;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class EntityAIEatGrass extends EntityAIBase
|
||||
{
|
||||
private static final Predicate<IBlockState> IS_TALL_GRASS = BlockStateMatcher.forBlock(Blocks.TALLGRASS).where(BlockTallGrass.TYPE, Predicates.equalTo(BlockTallGrass.EnumType.GRASS));
|
||||
/** The entity owner of this AITask */
|
||||
private final EntityLiving grassEaterEntity;
|
||||
/** The world the grass eater entity is eating from */
|
||||
private final World entityWorld;
|
||||
/** Number of ticks since the entity started to eat grass */
|
||||
int eatingGrassTimer;
|
||||
|
||||
public EntityAIEatGrass(EntityLiving grassEaterEntityIn)
|
||||
{
|
||||
this.grassEaterEntity = grassEaterEntityIn;
|
||||
this.entityWorld = grassEaterEntityIn.world;
|
||||
this.setMutexBits(7);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (this.grassEaterEntity.getRNG().nextInt(this.grassEaterEntity.isChild() ? 50 : 1000) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
BlockPos blockpos = new BlockPos(this.grassEaterEntity.posX, this.grassEaterEntity.posY, this.grassEaterEntity.posZ);
|
||||
|
||||
if (IS_TALL_GRASS.apply(this.entityWorld.getBlockState(blockpos)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.entityWorld.getBlockState(blockpos.down()).getBlock() == Blocks.GRASS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.eatingGrassTimer = 40;
|
||||
this.entityWorld.setEntityState(this.grassEaterEntity, (byte)10);
|
||||
this.grassEaterEntity.getNavigator().clearPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.eatingGrassTimer = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return this.eatingGrassTimer > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of ticks since the entity started to eat grass
|
||||
*/
|
||||
public int getEatingGrassTimer()
|
||||
{
|
||||
return this.eatingGrassTimer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
this.eatingGrassTimer = Math.max(0, this.eatingGrassTimer - 1);
|
||||
|
||||
if (this.eatingGrassTimer == 4)
|
||||
{
|
||||
BlockPos blockpos = new BlockPos(this.grassEaterEntity.posX, this.grassEaterEntity.posY, this.grassEaterEntity.posZ);
|
||||
|
||||
if (IS_TALL_GRASS.apply(this.entityWorld.getBlockState(blockpos)))
|
||||
{
|
||||
if (net.minecraftforge.event.ForgeEventFactory.getMobGriefingEvent(this.entityWorld, this.grassEaterEntity))
|
||||
{
|
||||
this.entityWorld.destroyBlock(blockpos, false);
|
||||
}
|
||||
|
||||
this.grassEaterEntity.eatGrassBonus();
|
||||
}
|
||||
else
|
||||
{
|
||||
BlockPos blockpos1 = blockpos.down();
|
||||
|
||||
if (this.entityWorld.getBlockState(blockpos1).getBlock() == Blocks.GRASS)
|
||||
{
|
||||
if (net.minecraftforge.event.ForgeEventFactory.getMobGriefingEvent(this.entityWorld, this.grassEaterEntity))
|
||||
{
|
||||
this.entityWorld.playEvent(2001, blockpos1, Block.getIdFromBlock(Blocks.GRASS));
|
||||
this.entityWorld.setBlockState(blockpos1, Blocks.DIRT.getDefaultState(), 2);
|
||||
}
|
||||
|
||||
this.grassEaterEntity.eatGrassBonus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
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.attributes.IAttributeInstance;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class EntityAIFindEntityNearest extends EntityAIBase
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private final EntityLiving mob;
|
||||
private final Predicate<EntityLivingBase> predicate;
|
||||
private final EntityAINearestAttackableTarget.Sorter sorter;
|
||||
private EntityLivingBase target;
|
||||
private final Class <? extends EntityLivingBase > classToCheck;
|
||||
|
||||
public EntityAIFindEntityNearest(EntityLiving mobIn, Class <? extends EntityLivingBase > p_i45884_2_)
|
||||
{
|
||||
this.mob = mobIn;
|
||||
this.classToCheck = p_i45884_2_;
|
||||
|
||||
if (mobIn instanceof EntityCreature)
|
||||
{
|
||||
LOGGER.warn("Use NearestAttackableTargetGoal.class for PathfinerMob mobs!");
|
||||
}
|
||||
|
||||
this.predicate = new Predicate<EntityLivingBase>()
|
||||
{
|
||||
public boolean apply(@Nullable EntityLivingBase p_apply_1_)
|
||||
{
|
||||
double d0 = EntityAIFindEntityNearest.this.getFollowRange();
|
||||
|
||||
if (p_apply_1_.isSneaking())
|
||||
{
|
||||
d0 *= 0.800000011920929D;
|
||||
}
|
||||
|
||||
if (p_apply_1_.isInvisible())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (double)p_apply_1_.getDistance(EntityAIFindEntityNearest.this.mob) > d0 ? false : EntityAITarget.isSuitableTarget(EntityAIFindEntityNearest.this.mob, p_apply_1_, false, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
this.sorter = new EntityAINearestAttackableTarget.Sorter(mobIn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
double d0 = this.getFollowRange();
|
||||
List<EntityLivingBase> list = this.mob.world.<EntityLivingBase>getEntitiesWithinAABB(this.classToCheck, this.mob.getEntityBoundingBox().grow(d0, 4.0D, d0), this.predicate);
|
||||
Collections.sort(list, this.sorter);
|
||||
|
||||
if (list.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.target = list.get(0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.mob.getAttackTarget();
|
||||
|
||||
if (entitylivingbase == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!entitylivingbase.isEntityAlive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
double d0 = this.getFollowRange();
|
||||
|
||||
if (this.mob.getDistanceSq(entitylivingbase) > d0 * d0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return !(entitylivingbase instanceof EntityPlayerMP) || !((EntityPlayerMP)entitylivingbase).interactionManager.isCreative();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.mob.setAttackTarget(this.target);
|
||||
super.startExecuting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.mob.setAttackTarget((EntityLivingBase)null);
|
||||
super.startExecuting();
|
||||
}
|
||||
|
||||
protected double getFollowRange()
|
||||
{
|
||||
IAttributeInstance iattributeinstance = this.mob.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE);
|
||||
return iattributeinstance == null ? 16.0D : iattributeinstance.getAttributeValue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
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.attributes.IAttributeInstance;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.scoreboard.Team;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class EntityAIFindEntityNearestPlayer extends EntityAIBase
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
/** The entity that use this AI */
|
||||
private final EntityLiving entityLiving;
|
||||
/** Use to determine if an entity correspond to specification */
|
||||
private final Predicate<Entity> predicate;
|
||||
/** Used to compare two entities */
|
||||
private final EntityAINearestAttackableTarget.Sorter sorter;
|
||||
/** The current target */
|
||||
private EntityLivingBase entityTarget;
|
||||
|
||||
public EntityAIFindEntityNearestPlayer(EntityLiving entityLivingIn)
|
||||
{
|
||||
this.entityLiving = entityLivingIn;
|
||||
|
||||
if (entityLivingIn instanceof EntityCreature)
|
||||
{
|
||||
LOGGER.warn("Use NearestAttackableTargetGoal.class for PathfinerMob mobs!");
|
||||
}
|
||||
|
||||
this.predicate = new Predicate<Entity>()
|
||||
{
|
||||
public boolean apply(@Nullable Entity p_apply_1_)
|
||||
{
|
||||
if (!(p_apply_1_ instanceof EntityPlayer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (((EntityPlayer)p_apply_1_).capabilities.disableDamage)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
double d0 = EntityAIFindEntityNearestPlayer.this.maxTargetRange();
|
||||
|
||||
if (p_apply_1_.isSneaking())
|
||||
{
|
||||
d0 *= 0.800000011920929D;
|
||||
}
|
||||
|
||||
if (p_apply_1_.isInvisible())
|
||||
{
|
||||
float f = ((EntityPlayer)p_apply_1_).getArmorVisibility();
|
||||
|
||||
if (f < 0.1F)
|
||||
{
|
||||
f = 0.1F;
|
||||
}
|
||||
|
||||
d0 *= (double)(0.7F * f);
|
||||
}
|
||||
|
||||
return (double)p_apply_1_.getDistance(EntityAIFindEntityNearestPlayer.this.entityLiving) > d0 ? false : EntityAITarget.isSuitableTarget(EntityAIFindEntityNearestPlayer.this.entityLiving, (EntityLivingBase)p_apply_1_, false, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
this.sorter = new EntityAINearestAttackableTarget.Sorter(entityLivingIn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
double d0 = this.maxTargetRange();
|
||||
List<EntityPlayer> list = this.entityLiving.world.<EntityPlayer>getEntitiesWithinAABB(EntityPlayer.class, this.entityLiving.getEntityBoundingBox().grow(d0, 4.0D, d0), this.predicate);
|
||||
Collections.sort(list, this.sorter);
|
||||
|
||||
if (list.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.entityTarget = list.get(0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.entityLiving.getAttackTarget();
|
||||
|
||||
if (entitylivingbase == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!entitylivingbase.isEntityAlive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (entitylivingbase instanceof EntityPlayer && ((EntityPlayer)entitylivingbase).capabilities.disableDamage)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Team team = this.entityLiving.getTeam();
|
||||
Team team1 = entitylivingbase.getTeam();
|
||||
|
||||
if (team != null && team1 == team)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
double d0 = this.maxTargetRange();
|
||||
|
||||
if (this.entityLiving.getDistanceSq(entitylivingbase) > d0 * d0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return !(entitylivingbase instanceof EntityPlayerMP) || !((EntityPlayerMP)entitylivingbase).interactionManager.isCreative();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.entityLiving.setAttackTarget(this.entityTarget);
|
||||
super.startExecuting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.entityLiving.setAttackTarget((EntityLivingBase)null);
|
||||
super.startExecuting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the max target range of the entiity (16 by default)
|
||||
*/
|
||||
protected double maxTargetRange()
|
||||
{
|
||||
IAttributeInstance iattributeinstance = this.entityLiving.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE);
|
||||
return iattributeinstance == null ? 16.0D : iattributeinstance.getAttributeValue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import java.util.Random;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.inventory.EntityEquipmentSlot;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class EntityAIFleeSun extends EntityAIBase
|
||||
{
|
||||
private final EntityCreature creature;
|
||||
private double shelterX;
|
||||
private double shelterY;
|
||||
private double shelterZ;
|
||||
private final double movementSpeed;
|
||||
private final World world;
|
||||
|
||||
public EntityAIFleeSun(EntityCreature theCreatureIn, double movementSpeedIn)
|
||||
{
|
||||
this.creature = theCreatureIn;
|
||||
this.movementSpeed = movementSpeedIn;
|
||||
this.world = theCreatureIn.world;
|
||||
this.setMutexBits(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (!this.world.isDaytime())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!this.creature.isBurning())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!this.world.canSeeSky(new BlockPos(this.creature.posX, this.creature.getEntityBoundingBox().minY, this.creature.posZ)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!this.creature.getItemStackFromSlot(EntityEquipmentSlot.HEAD).isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vec3d vec3d = this.findPossibleShelter();
|
||||
|
||||
if (vec3d == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.shelterX = vec3d.x;
|
||||
this.shelterY = vec3d.y;
|
||||
this.shelterZ = vec3d.z;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return !this.creature.getNavigator().noPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.creature.getNavigator().tryMoveToXYZ(this.shelterX, this.shelterY, this.shelterZ, this.movementSpeed);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Vec3d findPossibleShelter()
|
||||
{
|
||||
Random random = this.creature.getRNG();
|
||||
BlockPos blockpos = new BlockPos(this.creature.posX, this.creature.getEntityBoundingBox().minY, this.creature.posZ);
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
BlockPos blockpos1 = blockpos.add(random.nextInt(20) - 10, random.nextInt(6) - 3, random.nextInt(20) - 10);
|
||||
|
||||
if (!this.world.canSeeSky(blockpos1) && this.creature.getBlockPathWeight(blockpos1) < 0.0F)
|
||||
{
|
||||
return new Vec3d((double)blockpos1.getX(), (double)blockpos1.getY(), (double)blockpos1.getZ());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.pathfinding.PathNavigate;
|
||||
import net.minecraft.pathfinding.PathNavigateFlying;
|
||||
import net.minecraft.pathfinding.PathNavigateGround;
|
||||
import net.minecraft.pathfinding.PathNodeType;
|
||||
|
||||
public class EntityAIFollow extends EntityAIBase
|
||||
{
|
||||
private final EntityLiving entity;
|
||||
private final Predicate<EntityLiving> followPredicate;
|
||||
private EntityLiving followingEntity;
|
||||
private final double speedModifier;
|
||||
private final PathNavigate navigation;
|
||||
private int timeToRecalcPath;
|
||||
private final float stopDistance;
|
||||
private float oldWaterCost;
|
||||
private final float areaSize;
|
||||
|
||||
public EntityAIFollow(final EntityLiving p_i47417_1_, double p_i47417_2_, float p_i47417_4_, float p_i47417_5_)
|
||||
{
|
||||
this.entity = p_i47417_1_;
|
||||
this.followPredicate = new Predicate<EntityLiving>()
|
||||
{
|
||||
public boolean apply(@Nullable EntityLiving p_apply_1_)
|
||||
{
|
||||
return p_apply_1_ != null && p_i47417_1_.getClass() != p_apply_1_.getClass();
|
||||
}
|
||||
};
|
||||
this.speedModifier = p_i47417_2_;
|
||||
this.navigation = p_i47417_1_.getNavigator();
|
||||
this.stopDistance = p_i47417_4_;
|
||||
this.areaSize = p_i47417_5_;
|
||||
this.setMutexBits(3);
|
||||
|
||||
if (!(p_i47417_1_.getNavigator() instanceof PathNavigateGround) && !(p_i47417_1_.getNavigator() instanceof PathNavigateFlying))
|
||||
{
|
||||
throw new IllegalArgumentException("Unsupported mob type for FollowMobGoal");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
List<EntityLiving> list = this.entity.world.<EntityLiving>getEntitiesWithinAABB(EntityLiving.class, this.entity.getEntityBoundingBox().grow((double)this.areaSize), this.followPredicate);
|
||||
|
||||
if (!list.isEmpty())
|
||||
{
|
||||
for (EntityLiving entityliving : list)
|
||||
{
|
||||
if (!entityliving.isInvisible())
|
||||
{
|
||||
this.followingEntity = entityliving;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return this.followingEntity != null && !this.navigation.noPath() && this.entity.getDistanceSq(this.followingEntity) > (double)(this.stopDistance * this.stopDistance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.timeToRecalcPath = 0;
|
||||
this.oldWaterCost = this.entity.getPathPriority(PathNodeType.WATER);
|
||||
this.entity.setPathPriority(PathNodeType.WATER, 0.0F);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.followingEntity = null;
|
||||
this.navigation.clearPath();
|
||||
this.entity.setPathPriority(PathNodeType.WATER, this.oldWaterCost);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
if (this.followingEntity != null && !this.entity.getLeashed())
|
||||
{
|
||||
this.entity.getLookHelper().setLookPositionWithEntity(this.followingEntity, 10.0F, (float)this.entity.getVerticalFaceSpeed());
|
||||
|
||||
if (--this.timeToRecalcPath <= 0)
|
||||
{
|
||||
this.timeToRecalcPath = 10;
|
||||
double d0 = this.entity.posX - this.followingEntity.posX;
|
||||
double d1 = this.entity.posY - this.followingEntity.posY;
|
||||
double d2 = this.entity.posZ - this.followingEntity.posZ;
|
||||
double d3 = d0 * d0 + d1 * d1 + d2 * d2;
|
||||
|
||||
if (d3 > (double)(this.stopDistance * this.stopDistance))
|
||||
{
|
||||
this.navigation.tryMoveToEntityLiving(this.followingEntity, this.speedModifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.navigation.clearPath();
|
||||
EntityLookHelper entitylookhelper = this.followingEntity.getLookHelper();
|
||||
|
||||
if (d3 <= (double)this.stopDistance || entitylookhelper.getLookPosX() == this.entity.posX && entitylookhelper.getLookPosY() == this.entity.posY && entitylookhelper.getLookPosZ() == this.entity.posZ)
|
||||
{
|
||||
double d4 = this.followingEntity.posX - this.entity.posX;
|
||||
double d5 = this.followingEntity.posZ - this.entity.posZ;
|
||||
this.navigation.tryMoveToXYZ(this.entity.posX - d4, this.entity.posY, this.entity.posZ - d5, this.speedModifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import java.util.List;
|
||||
import net.minecraft.entity.monster.EntityIronGolem;
|
||||
import net.minecraft.entity.passive.EntityVillager;
|
||||
|
||||
public class EntityAIFollowGolem extends EntityAIBase
|
||||
{
|
||||
private final EntityVillager villager;
|
||||
private EntityIronGolem ironGolem;
|
||||
private int takeGolemRoseTick;
|
||||
private boolean tookGolemRose;
|
||||
|
||||
public EntityAIFollowGolem(EntityVillager villagerIn)
|
||||
{
|
||||
this.villager = villagerIn;
|
||||
this.setMutexBits(3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (this.villager.getGrowingAge() >= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!this.villager.world.isDaytime())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
List<EntityIronGolem> list = this.villager.world.<EntityIronGolem>getEntitiesWithinAABB(EntityIronGolem.class, this.villager.getEntityBoundingBox().grow(6.0D, 2.0D, 6.0D));
|
||||
|
||||
if (list.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (EntityIronGolem entityirongolem : list)
|
||||
{
|
||||
if (entityirongolem.getHoldRoseTick() > 0)
|
||||
{
|
||||
this.ironGolem = entityirongolem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return this.ironGolem != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return this.ironGolem.getHoldRoseTick() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.takeGolemRoseTick = this.villager.getRNG().nextInt(320);
|
||||
this.tookGolemRose = false;
|
||||
this.ironGolem.getNavigator().clearPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.ironGolem = null;
|
||||
this.villager.getNavigator().clearPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
this.villager.getLookHelper().setLookPositionWithEntity(this.ironGolem, 30.0F, 30.0F);
|
||||
|
||||
if (this.ironGolem.getHoldRoseTick() == this.takeGolemRoseTick)
|
||||
{
|
||||
this.villager.getNavigator().tryMoveToEntityLiving(this.ironGolem, 0.5D);
|
||||
this.tookGolemRose = true;
|
||||
}
|
||||
|
||||
if (this.tookGolemRose && this.villager.getDistanceSq(this.ironGolem) < 4.0D)
|
||||
{
|
||||
this.ironGolem.setHoldingRose(false);
|
||||
this.villager.getNavigator().clearPath();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.block.state.BlockFaceShape;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.passive.EntityTameable;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.pathfinding.PathNavigate;
|
||||
import net.minecraft.pathfinding.PathNavigateFlying;
|
||||
import net.minecraft.pathfinding.PathNavigateGround;
|
||||
import net.minecraft.pathfinding.PathNodeType;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class EntityAIFollowOwner extends EntityAIBase
|
||||
{
|
||||
private final EntityTameable tameable;
|
||||
private EntityLivingBase owner;
|
||||
World world;
|
||||
private final double followSpeed;
|
||||
private final PathNavigate petPathfinder;
|
||||
private int timeToRecalcPath;
|
||||
float maxDist;
|
||||
float minDist;
|
||||
private float oldWaterCost;
|
||||
|
||||
public EntityAIFollowOwner(EntityTameable tameableIn, double followSpeedIn, float minDistIn, float maxDistIn)
|
||||
{
|
||||
this.tameable = tameableIn;
|
||||
this.world = tameableIn.world;
|
||||
this.followSpeed = followSpeedIn;
|
||||
this.petPathfinder = tameableIn.getNavigator();
|
||||
this.minDist = minDistIn;
|
||||
this.maxDist = maxDistIn;
|
||||
this.setMutexBits(3);
|
||||
|
||||
if (!(tameableIn.getNavigator() instanceof PathNavigateGround) && !(tameableIn.getNavigator() instanceof PathNavigateFlying))
|
||||
{
|
||||
throw new IllegalArgumentException("Unsupported mob type for FollowOwnerGoal");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.tameable.getOwner();
|
||||
|
||||
if (entitylivingbase == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (entitylivingbase instanceof EntityPlayer && ((EntityPlayer)entitylivingbase).isSpectator())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (this.tameable.isSitting())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (this.tameable.getDistanceSq(entitylivingbase) < (double)(this.minDist * this.minDist))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.owner = entitylivingbase;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return !this.petPathfinder.noPath() && this.tameable.getDistanceSq(this.owner) > (double)(this.maxDist * this.maxDist) && !this.tameable.isSitting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.timeToRecalcPath = 0;
|
||||
this.oldWaterCost = this.tameable.getPathPriority(PathNodeType.WATER);
|
||||
this.tameable.setPathPriority(PathNodeType.WATER, 0.0F);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.owner = null;
|
||||
this.petPathfinder.clearPath();
|
||||
this.tameable.setPathPriority(PathNodeType.WATER, this.oldWaterCost);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
this.tameable.getLookHelper().setLookPositionWithEntity(this.owner, 10.0F, (float)this.tameable.getVerticalFaceSpeed());
|
||||
|
||||
if (!this.tameable.isSitting())
|
||||
{
|
||||
if (--this.timeToRecalcPath <= 0)
|
||||
{
|
||||
this.timeToRecalcPath = 10;
|
||||
|
||||
if (!this.petPathfinder.tryMoveToEntityLiving(this.owner, this.followSpeed))
|
||||
{
|
||||
if (!this.tameable.getLeashed() && !this.tameable.isRiding())
|
||||
{
|
||||
if (this.tameable.getDistanceSq(this.owner) >= 144.0D)
|
||||
{
|
||||
int i = MathHelper.floor(this.owner.posX) - 2;
|
||||
int j = MathHelper.floor(this.owner.posZ) - 2;
|
||||
int k = MathHelper.floor(this.owner.getEntityBoundingBox().minY);
|
||||
|
||||
for (int l = 0; l <= 4; ++l)
|
||||
{
|
||||
for (int i1 = 0; i1 <= 4; ++i1)
|
||||
{
|
||||
if ((l < 1 || i1 < 1 || l > 3 || i1 > 3) && this.isTeleportFriendlyBlock(i, j, k, l, i1))
|
||||
{
|
||||
this.tameable.setLocationAndAngles((double)((float)(i + l) + 0.5F), (double)k, (double)((float)(j + i1) + 0.5F), this.tameable.rotationYaw, this.tameable.rotationPitch);
|
||||
this.petPathfinder.clearPath();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isTeleportFriendlyBlock(int x, int p_192381_2_, int y, int p_192381_4_, int p_192381_5_)
|
||||
{
|
||||
BlockPos blockpos = new BlockPos(x + p_192381_4_, y - 1, p_192381_2_ + p_192381_5_);
|
||||
IBlockState iblockstate = this.world.getBlockState(blockpos);
|
||||
return iblockstate.getBlockFaceShape(this.world, blockpos, EnumFacing.DOWN) == BlockFaceShape.SOLID && iblockstate.canEntitySpawn(this.tameable) && this.world.isAirBlock(blockpos.up()) && this.world.isAirBlock(blockpos.up(2));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.passive.EntityTameable;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
public class EntityAIFollowOwnerFlying extends EntityAIFollowOwner
|
||||
{
|
||||
public EntityAIFollowOwnerFlying(EntityTameable p_i47416_1_, double p_i47416_2_, float p_i47416_4_, float p_i47416_5_)
|
||||
{
|
||||
super(p_i47416_1_, p_i47416_2_, p_i47416_4_, p_i47416_5_);
|
||||
}
|
||||
|
||||
protected boolean isTeleportFriendlyBlock(int x, int p_192381_2_, int y, int p_192381_4_, int p_192381_5_)
|
||||
{
|
||||
IBlockState iblockstate = this.world.getBlockState(new BlockPos(x + p_192381_4_, y - 1, p_192381_2_ + p_192381_5_));
|
||||
return (iblockstate.isTopSolid() || iblockstate.getMaterial() == Material.LEAVES) && this.world.isAirBlock(new BlockPos(x + p_192381_4_, y, p_192381_2_ + p_192381_5_)) && this.world.isAirBlock(new BlockPos(x + p_192381_4_, y + 1, p_192381_2_ + p_192381_5_));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import java.util.List;
|
||||
import net.minecraft.entity.passive.EntityAnimal;
|
||||
|
||||
public class EntityAIFollowParent extends EntityAIBase
|
||||
{
|
||||
/** The child that is following its parent. */
|
||||
EntityAnimal childAnimal;
|
||||
EntityAnimal parentAnimal;
|
||||
double moveSpeed;
|
||||
private int delayCounter;
|
||||
|
||||
public EntityAIFollowParent(EntityAnimal animal, double speed)
|
||||
{
|
||||
this.childAnimal = animal;
|
||||
this.moveSpeed = speed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (this.childAnimal.getGrowingAge() >= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
List<EntityAnimal> list = this.childAnimal.world.<EntityAnimal>getEntitiesWithinAABB(this.childAnimal.getClass(), this.childAnimal.getEntityBoundingBox().grow(8.0D, 4.0D, 8.0D));
|
||||
EntityAnimal entityanimal = null;
|
||||
double d0 = Double.MAX_VALUE;
|
||||
|
||||
for (EntityAnimal entityanimal1 : list)
|
||||
{
|
||||
if (entityanimal1.getGrowingAge() >= 0)
|
||||
{
|
||||
double d1 = this.childAnimal.getDistanceSq(entityanimal1);
|
||||
|
||||
if (d1 <= d0)
|
||||
{
|
||||
d0 = d1;
|
||||
entityanimal = entityanimal1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (entityanimal == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (d0 < 9.0D)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.parentAnimal = entityanimal;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
if (this.childAnimal.getGrowingAge() >= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!this.parentAnimal.isEntityAlive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
double d0 = this.childAnimal.getDistanceSq(this.parentAnimal);
|
||||
return d0 >= 9.0D && d0 <= 256.0D;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.delayCounter = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.parentAnimal = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
if (--this.delayCounter <= 0)
|
||||
{
|
||||
this.delayCounter = 10;
|
||||
this.childAnimal.getNavigator().tryMoveToEntityLiving(this.parentAnimal, this.moveSpeed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockCrops;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.passive.EntityVillager;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.inventory.InventoryBasic;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class EntityAIHarvestFarmland extends EntityAIMoveToBlock
|
||||
{
|
||||
/** Villager that is harvesting */
|
||||
private final EntityVillager villager;
|
||||
private boolean hasFarmItem;
|
||||
private boolean wantsToReapStuff;
|
||||
/** 0 => harvest, 1 => replant, -1 => none */
|
||||
private int currentTask;
|
||||
|
||||
public EntityAIHarvestFarmland(EntityVillager villagerIn, double speedIn)
|
||||
{
|
||||
super(villagerIn, speedIn, 16);
|
||||
this.villager = villagerIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (this.runDelay <= 0)
|
||||
{
|
||||
if (!net.minecraftforge.event.ForgeEventFactory.getMobGriefingEvent(this.villager.world, this.villager))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this.currentTask = -1;
|
||||
this.hasFarmItem = this.villager.isFarmItemInInventory();
|
||||
this.wantsToReapStuff = this.villager.wantsMoreFood();
|
||||
}
|
||||
|
||||
return super.shouldExecute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return this.currentTask >= 0 && super.shouldContinueExecuting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
super.updateTask();
|
||||
this.villager.getLookHelper().setLookPosition((double)this.destinationBlock.getX() + 0.5D, (double)(this.destinationBlock.getY() + 1), (double)this.destinationBlock.getZ() + 0.5D, 10.0F, (float)this.villager.getVerticalFaceSpeed());
|
||||
|
||||
if (this.getIsAboveDestination())
|
||||
{
|
||||
World world = this.villager.world;
|
||||
BlockPos blockpos = this.destinationBlock.up();
|
||||
IBlockState iblockstate = world.getBlockState(blockpos);
|
||||
Block block = iblockstate.getBlock();
|
||||
|
||||
if (this.currentTask == 0 && block instanceof BlockCrops && ((BlockCrops)block).isMaxAge(iblockstate))
|
||||
{
|
||||
world.destroyBlock(blockpos, true);
|
||||
}
|
||||
else if (this.currentTask == 1 && iblockstate.getMaterial() == Material.AIR)
|
||||
{
|
||||
InventoryBasic inventorybasic = this.villager.getVillagerInventory();
|
||||
|
||||
for (int i = 0; i < inventorybasic.getSizeInventory(); ++i)
|
||||
{
|
||||
ItemStack itemstack = inventorybasic.getStackInSlot(i);
|
||||
boolean flag = false;
|
||||
|
||||
if (!itemstack.isEmpty())
|
||||
{
|
||||
if (itemstack.getItem() == Items.WHEAT_SEEDS)
|
||||
{
|
||||
world.setBlockState(blockpos, Blocks.WHEAT.getDefaultState(), 3);
|
||||
flag = true;
|
||||
}
|
||||
else if (itemstack.getItem() == Items.POTATO)
|
||||
{
|
||||
world.setBlockState(blockpos, Blocks.POTATOES.getDefaultState(), 3);
|
||||
flag = true;
|
||||
}
|
||||
else if (itemstack.getItem() == Items.CARROT)
|
||||
{
|
||||
world.setBlockState(blockpos, Blocks.CARROTS.getDefaultState(), 3);
|
||||
flag = true;
|
||||
}
|
||||
else if (itemstack.getItem() == Items.BEETROOT_SEEDS)
|
||||
{
|
||||
world.setBlockState(blockpos, Blocks.BEETROOTS.getDefaultState(), 3);
|
||||
flag = true;
|
||||
}
|
||||
else if (itemstack.getItem() instanceof net.minecraftforge.common.IPlantable) {
|
||||
if(((net.minecraftforge.common.IPlantable)itemstack.getItem()).getPlantType(world,blockpos) == net.minecraftforge.common.EnumPlantType.Crop) {
|
||||
world.setBlockState(blockpos, ((net.minecraftforge.common.IPlantable)itemstack.getItem()).getPlant(world,blockpos),3);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flag)
|
||||
{
|
||||
itemstack.shrink(1);
|
||||
|
||||
if (itemstack.isEmpty())
|
||||
{
|
||||
inventorybasic.setInventorySlotContents(i, ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.currentTask = -1;
|
||||
this.runDelay = 10;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true to set given position as destination
|
||||
*/
|
||||
protected boolean shouldMoveTo(World worldIn, BlockPos pos)
|
||||
{
|
||||
Block block = worldIn.getBlockState(pos).getBlock();
|
||||
|
||||
if (block == Blocks.FARMLAND)
|
||||
{
|
||||
pos = pos.up();
|
||||
IBlockState iblockstate = worldIn.getBlockState(pos);
|
||||
block = iblockstate.getBlock();
|
||||
|
||||
if (block instanceof BlockCrops && ((BlockCrops)block).isMaxAge(iblockstate) && this.wantsToReapStuff && (this.currentTask == 0 || this.currentTask < 0))
|
||||
{
|
||||
this.currentTask = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (iblockstate.getMaterial() == Material.AIR && this.hasFarmItem && (this.currentTask == 1 || this.currentTask < 0))
|
||||
{
|
||||
this.currentTask = 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.passive.EntityTameable;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
|
||||
public class EntityAIHurtByTarget extends EntityAITarget
|
||||
{
|
||||
private final boolean entityCallsForHelp;
|
||||
/** Store the previous revengeTimer value */
|
||||
private int revengeTimerOld;
|
||||
private final Class<?>[] excludedReinforcementTypes;
|
||||
|
||||
public EntityAIHurtByTarget(EntityCreature creatureIn, boolean entityCallsForHelpIn, Class<?>... excludedReinforcementTypes)
|
||||
{
|
||||
super(creatureIn, true);
|
||||
this.entityCallsForHelp = entityCallsForHelpIn;
|
||||
this.excludedReinforcementTypes = excludedReinforcementTypes;
|
||||
this.setMutexBits(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
int i = this.taskOwner.getRevengeTimer();
|
||||
EntityLivingBase entitylivingbase = this.taskOwner.getRevengeTarget();
|
||||
return i != this.revengeTimerOld && entitylivingbase != null && this.isSuitableTarget(entitylivingbase, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.taskOwner.setAttackTarget(this.taskOwner.getRevengeTarget());
|
||||
this.target = this.taskOwner.getAttackTarget();
|
||||
this.revengeTimerOld = this.taskOwner.getRevengeTimer();
|
||||
this.unseenMemoryTicks = 300;
|
||||
|
||||
if (this.entityCallsForHelp)
|
||||
{
|
||||
this.alertOthers();
|
||||
}
|
||||
|
||||
super.startExecuting();
|
||||
}
|
||||
|
||||
protected void alertOthers()
|
||||
{
|
||||
double d0 = this.getTargetDistance();
|
||||
|
||||
for (EntityCreature entitycreature : this.taskOwner.world.getEntitiesWithinAABB(this.taskOwner.getClass(), (new AxisAlignedBB(this.taskOwner.posX, this.taskOwner.posY, this.taskOwner.posZ, this.taskOwner.posX + 1.0D, this.taskOwner.posY + 1.0D, this.taskOwner.posZ + 1.0D)).grow(d0, 10.0D, d0)))
|
||||
{
|
||||
if (this.taskOwner != entitycreature && entitycreature.getAttackTarget() == null && (!(this.taskOwner instanceof EntityTameable) || ((EntityTameable)this.taskOwner).getOwner() == ((EntityTameable)entitycreature).getOwner()) && !entitycreature.isOnSameTeam(this.taskOwner.getRevengeTarget()))
|
||||
{
|
||||
boolean flag = false;
|
||||
|
||||
for (Class<?> oclass : this.excludedReinforcementTypes)
|
||||
{
|
||||
if (entitycreature.getClass() == oclass)
|
||||
{
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!flag)
|
||||
{
|
||||
this.setEntityAttackTarget(entitycreature, this.taskOwner.getRevengeTarget());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void setEntityAttackTarget(EntityCreature creatureIn, EntityLivingBase entityLivingBaseIn)
|
||||
{
|
||||
creatureIn.setAttackTarget(entityLivingBaseIn);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.passive.EntityShoulderRiding;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
|
||||
public class EntityAILandOnOwnersShoulder extends EntityAIBase
|
||||
{
|
||||
private final EntityShoulderRiding entity;
|
||||
private EntityPlayer owner;
|
||||
private boolean isSittingOnShoulder;
|
||||
|
||||
public EntityAILandOnOwnersShoulder(EntityShoulderRiding p_i47415_1_)
|
||||
{
|
||||
this.entity = p_i47415_1_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.entity.getOwner();
|
||||
boolean flag = entitylivingbase != null && !((EntityPlayer)entitylivingbase).isSpectator() && !((EntityPlayer)entitylivingbase).capabilities.isFlying && !entitylivingbase.isInWater();
|
||||
return !this.entity.isSitting() && flag && this.entity.canSitOnShoulder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this AI Task is interruptible by a higher (= lower value) priority task. All vanilla AITask have
|
||||
* this value set to true.
|
||||
*/
|
||||
public boolean isInterruptible()
|
||||
{
|
||||
return !this.isSittingOnShoulder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.owner = (EntityPlayer)this.entity.getOwner();
|
||||
this.isSittingOnShoulder = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
if (!this.isSittingOnShoulder && !this.entity.isSitting() && !this.entity.getLeashed())
|
||||
{
|
||||
if (this.entity.getEntityBoundingBox().intersects(this.owner.getEntityBoundingBox()))
|
||||
{
|
||||
this.isSittingOnShoulder = this.entity.setEntityOnShoulder(this.owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
public class EntityAILeapAtTarget extends EntityAIBase
|
||||
{
|
||||
/** The entity that is leaping. */
|
||||
EntityLiving leaper;
|
||||
/** The entity that the leaper is leaping towards. */
|
||||
EntityLivingBase leapTarget;
|
||||
/** The entity's motionY after leaping. */
|
||||
float leapMotionY;
|
||||
|
||||
public EntityAILeapAtTarget(EntityLiving leapingEntity, float leapMotionYIn)
|
||||
{
|
||||
this.leaper = leapingEntity;
|
||||
this.leapMotionY = leapMotionYIn;
|
||||
this.setMutexBits(5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
this.leapTarget = this.leaper.getAttackTarget();
|
||||
|
||||
if (this.leapTarget == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
double d0 = this.leaper.getDistanceSq(this.leapTarget);
|
||||
|
||||
if (d0 >= 4.0D && d0 <= 16.0D)
|
||||
{
|
||||
if (!this.leaper.onGround)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.leaper.getRNG().nextInt(5) == 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return !this.leaper.onGround;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
double d0 = this.leapTarget.posX - this.leaper.posX;
|
||||
double d1 = this.leapTarget.posZ - this.leaper.posZ;
|
||||
float f = MathHelper.sqrt(d0 * d0 + d1 * d1);
|
||||
|
||||
if ((double)f >= 1.0E-4D)
|
||||
{
|
||||
this.leaper.motionX += d0 / (double)f * 0.5D * 0.800000011920929D + this.leaper.motionX * 0.20000000298023224D;
|
||||
this.leaper.motionZ += d1 / (double)f * 0.5D * 0.800000011920929D + this.leaper.motionZ * 0.20000000298023224D;
|
||||
}
|
||||
|
||||
this.leaper.motionY = (double)this.leapMotionY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import java.util.List;
|
||||
import net.minecraft.entity.passive.EntityLlama;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public class EntityAILlamaFollowCaravan extends EntityAIBase
|
||||
{
|
||||
public EntityLlama llama;
|
||||
private double speedModifier;
|
||||
private int distCheckCounter;
|
||||
|
||||
public EntityAILlamaFollowCaravan(EntityLlama llamaIn, double speedModifierIn)
|
||||
{
|
||||
this.llama = llamaIn;
|
||||
this.speedModifier = speedModifierIn;
|
||||
this.setMutexBits(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (!this.llama.getLeashed() && !this.llama.inCaravan())
|
||||
{
|
||||
List<EntityLlama> list = this.llama.world.<EntityLlama>getEntitiesWithinAABB(this.llama.getClass(), this.llama.getEntityBoundingBox().grow(9.0D, 4.0D, 9.0D));
|
||||
EntityLlama entityllama = null;
|
||||
double d0 = Double.MAX_VALUE;
|
||||
|
||||
for (EntityLlama entityllama1 : list)
|
||||
{
|
||||
if (entityllama1.inCaravan() && !entityllama1.hasCaravanTrail())
|
||||
{
|
||||
double d1 = this.llama.getDistanceSq(entityllama1);
|
||||
|
||||
if (d1 <= d0)
|
||||
{
|
||||
d0 = d1;
|
||||
entityllama = entityllama1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (entityllama == null)
|
||||
{
|
||||
for (EntityLlama entityllama2 : list)
|
||||
{
|
||||
if (entityllama2.getLeashed() && !entityllama2.hasCaravanTrail())
|
||||
{
|
||||
double d2 = this.llama.getDistanceSq(entityllama2);
|
||||
|
||||
if (d2 <= d0)
|
||||
{
|
||||
d0 = d2;
|
||||
entityllama = entityllama2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (entityllama == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (d0 < 4.0D)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!entityllama.getLeashed() && !this.firstIsLeashed(entityllama, 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.llama.joinCaravan(entityllama);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
if (this.llama.inCaravan() && this.llama.getCaravanHead().isEntityAlive() && this.firstIsLeashed(this.llama, 0))
|
||||
{
|
||||
double d0 = this.llama.getDistanceSq(this.llama.getCaravanHead());
|
||||
|
||||
if (d0 > 676.0D)
|
||||
{
|
||||
if (this.speedModifier <= 3.0D)
|
||||
{
|
||||
this.speedModifier *= 1.2D;
|
||||
this.distCheckCounter = 40;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.distCheckCounter == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.distCheckCounter > 0)
|
||||
{
|
||||
--this.distCheckCounter;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.llama.leaveCaravan();
|
||||
this.speedModifier = 2.1D;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
if (this.llama.inCaravan())
|
||||
{
|
||||
EntityLlama entityllama = this.llama.getCaravanHead();
|
||||
double d0 = (double)this.llama.getDistance(entityllama);
|
||||
float f = 2.0F;
|
||||
Vec3d vec3d = (new Vec3d(entityllama.posX - this.llama.posX, entityllama.posY - this.llama.posY, entityllama.posZ - this.llama.posZ)).normalize().scale(Math.max(d0 - 2.0D, 0.0D));
|
||||
this.llama.getNavigator().tryMoveToXYZ(this.llama.posX + vec3d.x, this.llama.posY + vec3d.y, this.llama.posZ + vec3d.z, this.speedModifier);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean firstIsLeashed(EntityLlama p_190858_1_, int p_190858_2_)
|
||||
{
|
||||
if (p_190858_2_ > 8)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (p_190858_1_.inCaravan())
|
||||
{
|
||||
if (p_190858_1_.getCaravanHead().getLeashed())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
EntityLlama entityllama = p_190858_1_.getCaravanHead();
|
||||
++p_190858_2_;
|
||||
return this.firstIsLeashed(entityllama, p_190858_2_);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.passive.EntityVillager;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
|
||||
public class EntityAILookAtTradePlayer extends EntityAIWatchClosest
|
||||
{
|
||||
private final EntityVillager villager;
|
||||
|
||||
public EntityAILookAtTradePlayer(EntityVillager villagerIn)
|
||||
{
|
||||
super(villagerIn, EntityPlayer.class, 8.0F);
|
||||
this.villager = villagerIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (this.villager.isTrading())
|
||||
{
|
||||
this.closestEntity = this.villager.getCustomer();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.monster.EntityIronGolem;
|
||||
import net.minecraft.entity.passive.EntityVillager;
|
||||
|
||||
public class EntityAILookAtVillager extends EntityAIBase
|
||||
{
|
||||
private final EntityIronGolem ironGolem;
|
||||
private EntityVillager villager;
|
||||
private int lookTime;
|
||||
|
||||
public EntityAILookAtVillager(EntityIronGolem ironGolemIn)
|
||||
{
|
||||
this.ironGolem = ironGolemIn;
|
||||
this.setMutexBits(3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (!this.ironGolem.world.isDaytime())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (this.ironGolem.getRNG().nextInt(8000) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.villager = (EntityVillager)this.ironGolem.world.findNearestEntityWithinAABB(EntityVillager.class, this.ironGolem.getEntityBoundingBox().grow(6.0D, 2.0D, 6.0D), this.ironGolem);
|
||||
return this.villager != null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return this.lookTime > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.lookTime = 400;
|
||||
this.ironGolem.setHoldingRose(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.ironGolem.setHoldingRose(false);
|
||||
this.villager = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
this.ironGolem.getLookHelper().setLookPositionWithEntity(this.villager, 30.0F, 30.0F);
|
||||
--this.lookTime;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
|
||||
public class EntityAILookIdle extends EntityAIBase
|
||||
{
|
||||
/** The entity that is looking idle. */
|
||||
private final EntityLiving idleEntity;
|
||||
/** X offset to look at */
|
||||
private double lookX;
|
||||
/** Z offset to look at */
|
||||
private double lookZ;
|
||||
/** A decrementing tick that stops the entity from being idle once it reaches 0. */
|
||||
private int idleTime;
|
||||
|
||||
public EntityAILookIdle(EntityLiving entitylivingIn)
|
||||
{
|
||||
this.idleEntity = entitylivingIn;
|
||||
this.setMutexBits(3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
return this.idleEntity.getRNG().nextFloat() < 0.02F;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return this.idleTime >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
double d0 = (Math.PI * 2D) * this.idleEntity.getRNG().nextDouble();
|
||||
this.lookX = Math.cos(d0);
|
||||
this.lookZ = Math.sin(d0);
|
||||
this.idleTime = 20 + this.idleEntity.getRNG().nextInt(20);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
--this.idleTime;
|
||||
this.idleEntity.getLookHelper().setLookPosition(this.idleEntity.posX + this.lookX, this.idleEntity.posY + (double)this.idleEntity.getEyeHeight(), this.idleEntity.posZ + this.lookZ, (float)this.idleEntity.getHorizontalFaceSpeed(), (float)this.idleEntity.getVerticalFaceSpeed());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import net.minecraft.advancements.CriteriaTriggers;
|
||||
import net.minecraft.entity.EntityAgeable;
|
||||
import net.minecraft.entity.item.EntityXPOrb;
|
||||
import net.minecraft.entity.passive.EntityAnimal;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.stats.StatList;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class EntityAIMate extends EntityAIBase
|
||||
{
|
||||
private final EntityAnimal animal;
|
||||
private final Class <? extends EntityAnimal > mateClass;
|
||||
World world;
|
||||
private EntityAnimal targetMate;
|
||||
/** Delay preventing a baby from spawning immediately when two mate-able animals find each other. */
|
||||
int spawnBabyDelay;
|
||||
/** The speed the creature moves at during mating behavior. */
|
||||
double moveSpeed;
|
||||
|
||||
public EntityAIMate(EntityAnimal animal, double speedIn)
|
||||
{
|
||||
this(animal, speedIn, animal.getClass());
|
||||
}
|
||||
|
||||
public EntityAIMate(EntityAnimal p_i47306_1_, double p_i47306_2_, Class <? extends EntityAnimal > p_i47306_4_)
|
||||
{
|
||||
this.animal = p_i47306_1_;
|
||||
this.world = p_i47306_1_.world;
|
||||
this.mateClass = p_i47306_4_;
|
||||
this.moveSpeed = p_i47306_2_;
|
||||
this.setMutexBits(3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (!this.animal.isInLove())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.targetMate = this.getNearbyMate();
|
||||
return this.targetMate != null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return this.targetMate.isEntityAlive() && this.targetMate.isInLove() && this.spawnBabyDelay < 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.targetMate = null;
|
||||
this.spawnBabyDelay = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
this.animal.getLookHelper().setLookPositionWithEntity(this.targetMate, 10.0F, (float)this.animal.getVerticalFaceSpeed());
|
||||
this.animal.getNavigator().tryMoveToEntityLiving(this.targetMate, this.moveSpeed);
|
||||
++this.spawnBabyDelay;
|
||||
|
||||
if (this.spawnBabyDelay >= 60 && this.animal.getDistanceSq(this.targetMate) < 9.0D)
|
||||
{
|
||||
this.spawnBaby();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops through nearby animals and finds another animal of the same type that can be mated with. Returns the first
|
||||
* valid mate found.
|
||||
*/
|
||||
private EntityAnimal getNearbyMate()
|
||||
{
|
||||
List<EntityAnimal> list = this.world.<EntityAnimal>getEntitiesWithinAABB(this.mateClass, this.animal.getEntityBoundingBox().grow(8.0D));
|
||||
double d0 = Double.MAX_VALUE;
|
||||
EntityAnimal entityanimal = null;
|
||||
|
||||
for (EntityAnimal entityanimal1 : list)
|
||||
{
|
||||
if (this.animal.canMateWith(entityanimal1) && this.animal.getDistanceSq(entityanimal1) < d0)
|
||||
{
|
||||
entityanimal = entityanimal1;
|
||||
d0 = this.animal.getDistanceSq(entityanimal1);
|
||||
}
|
||||
}
|
||||
|
||||
return entityanimal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns a baby animal of the same type.
|
||||
*/
|
||||
private void spawnBaby()
|
||||
{
|
||||
EntityAgeable entityageable = this.animal.createChild(this.targetMate);
|
||||
|
||||
final net.minecraftforge.event.entity.living.BabyEntitySpawnEvent event = new net.minecraftforge.event.entity.living.BabyEntitySpawnEvent(animal, targetMate, entityageable);
|
||||
final boolean cancelled = net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(event);
|
||||
entityageable = event.getChild();
|
||||
if (cancelled) {
|
||||
//Reset the "inLove" state for the animals
|
||||
this.animal.setGrowingAge(6000);
|
||||
this.targetMate.setGrowingAge(6000);
|
||||
this.animal.resetInLove();
|
||||
this.targetMate.resetInLove();
|
||||
return;
|
||||
}
|
||||
|
||||
if (entityageable != null)
|
||||
{
|
||||
EntityPlayerMP entityplayermp = this.animal.getLoveCause();
|
||||
|
||||
if (entityplayermp == null && this.targetMate.getLoveCause() != null)
|
||||
{
|
||||
entityplayermp = this.targetMate.getLoveCause();
|
||||
}
|
||||
|
||||
if (entityplayermp != null)
|
||||
{
|
||||
entityplayermp.addStat(StatList.ANIMALS_BRED);
|
||||
CriteriaTriggers.BRED_ANIMALS.trigger(entityplayermp, this.animal, this.targetMate, entityageable);
|
||||
}
|
||||
|
||||
this.animal.setGrowingAge(6000);
|
||||
this.targetMate.setGrowingAge(6000);
|
||||
this.animal.resetInLove();
|
||||
this.targetMate.resetInLove();
|
||||
entityageable.setGrowingAge(-24000);
|
||||
entityageable.setLocationAndAngles(this.animal.posX, this.animal.posY, this.animal.posZ, 0.0F, 0.0F);
|
||||
this.world.spawnEntity(entityageable);
|
||||
Random random = this.animal.getRNG();
|
||||
|
||||
for (int i = 0; i < 7; ++i)
|
||||
{
|
||||
double d0 = random.nextGaussian() * 0.02D;
|
||||
double d1 = random.nextGaussian() * 0.02D;
|
||||
double d2 = random.nextGaussian() * 0.02D;
|
||||
double d3 = random.nextDouble() * (double)this.animal.width * 2.0D - (double)this.animal.width;
|
||||
double d4 = 0.5D + random.nextDouble() * (double)this.animal.height;
|
||||
double d5 = random.nextDouble() * (double)this.animal.width * 2.0D - (double)this.animal.width;
|
||||
this.world.spawnParticle(EnumParticleTypes.HEART, this.animal.posX + d3, this.animal.posY + d4, this.animal.posZ + d5, d0, d1, d2);
|
||||
}
|
||||
|
||||
if (this.world.getGameRules().getBoolean("doMobLoot"))
|
||||
{
|
||||
this.world.spawnEntity(new EntityXPOrb(this.world, this.animal.posX, this.animal.posY, this.animal.posZ, random.nextInt(7) + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.village.Village;
|
||||
import net.minecraft.village.VillageDoorInfo;
|
||||
|
||||
public class EntityAIMoveIndoors extends EntityAIBase
|
||||
{
|
||||
private final EntityCreature entity;
|
||||
private VillageDoorInfo doorInfo;
|
||||
private int insidePosX = -1;
|
||||
private int insidePosZ = -1;
|
||||
|
||||
public EntityAIMoveIndoors(EntityCreature entityIn)
|
||||
{
|
||||
this.entity = entityIn;
|
||||
this.setMutexBits(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
BlockPos blockpos = new BlockPos(this.entity);
|
||||
|
||||
if ((!this.entity.world.isDaytime() || this.entity.world.isRaining() && !this.entity.world.getBiome(blockpos).canRain()) && this.entity.world.provider.hasSkyLight())
|
||||
{
|
||||
if (this.entity.getRNG().nextInt(50) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (this.insidePosX != -1 && this.entity.getDistanceSq((double)this.insidePosX, this.entity.posY, (double)this.insidePosZ) < 4.0D)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Village village = this.entity.world.getVillageCollection().getNearestVillage(blockpos, 14);
|
||||
|
||||
if (village == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.doorInfo = village.getDoorInfo(blockpos);
|
||||
return this.doorInfo != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return !this.entity.getNavigator().noPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.insidePosX = -1;
|
||||
BlockPos blockpos = this.doorInfo.getInsideBlockPos();
|
||||
int i = blockpos.getX();
|
||||
int j = blockpos.getY();
|
||||
int k = blockpos.getZ();
|
||||
|
||||
if (this.entity.getDistanceSq(blockpos) > 256.0D)
|
||||
{
|
||||
Vec3d vec3d = RandomPositionGenerator.findRandomTargetBlockTowards(this.entity, 14, 3, new Vec3d((double)i + 0.5D, (double)j, (double)k + 0.5D));
|
||||
|
||||
if (vec3d != null)
|
||||
{
|
||||
this.entity.getNavigator().tryMoveToXYZ(vec3d.x, vec3d.y, vec3d.z, 1.0D);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.entity.getNavigator().tryMoveToXYZ((double)i + 0.5D, (double)j, (double)k + 0.5D, 1.0D);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.insidePosX = this.doorInfo.getInsideBlockPos().getX();
|
||||
this.insidePosZ = this.doorInfo.getInsideBlockPos().getZ();
|
||||
this.doorInfo = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import java.util.List;
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.pathfinding.Path;
|
||||
import net.minecraft.pathfinding.PathNavigateGround;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.village.Village;
|
||||
import net.minecraft.village.VillageDoorInfo;
|
||||
|
||||
public class EntityAIMoveThroughVillage extends EntityAIBase
|
||||
{
|
||||
private final EntityCreature entity;
|
||||
private final double movementSpeed;
|
||||
/** The PathNavigate of our entity. */
|
||||
private Path path;
|
||||
private VillageDoorInfo doorInfo;
|
||||
private final boolean isNocturnal;
|
||||
private final List<VillageDoorInfo> doorList = Lists.<VillageDoorInfo>newArrayList();
|
||||
|
||||
public EntityAIMoveThroughVillage(EntityCreature entityIn, double movementSpeedIn, boolean isNocturnalIn)
|
||||
{
|
||||
this.entity = entityIn;
|
||||
this.movementSpeed = movementSpeedIn;
|
||||
this.isNocturnal = isNocturnalIn;
|
||||
this.setMutexBits(1);
|
||||
|
||||
if (!(entityIn.getNavigator() instanceof PathNavigateGround))
|
||||
{
|
||||
throw new IllegalArgumentException("Unsupported mob for MoveThroughVillageGoal");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
this.resizeDoorList();
|
||||
|
||||
if (this.isNocturnal && this.entity.world.isDaytime())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Village village = this.entity.world.getVillageCollection().getNearestVillage(new BlockPos(this.entity), 0);
|
||||
|
||||
if (village == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.doorInfo = this.findNearestDoor(village);
|
||||
|
||||
if (this.doorInfo == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
PathNavigateGround pathnavigateground = (PathNavigateGround)this.entity.getNavigator();
|
||||
boolean flag = pathnavigateground.getEnterDoors();
|
||||
pathnavigateground.setBreakDoors(false);
|
||||
this.path = pathnavigateground.getPathToPos(this.doorInfo.getDoorBlockPos());
|
||||
pathnavigateground.setBreakDoors(flag);
|
||||
|
||||
if (this.path != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vec3d vec3d = RandomPositionGenerator.findRandomTargetBlockTowards(this.entity, 10, 7, new Vec3d((double)this.doorInfo.getDoorBlockPos().getX(), (double)this.doorInfo.getDoorBlockPos().getY(), (double)this.doorInfo.getDoorBlockPos().getZ()));
|
||||
|
||||
if (vec3d == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
pathnavigateground.setBreakDoors(false);
|
||||
this.path = this.entity.getNavigator().getPathToXYZ(vec3d.x, vec3d.y, vec3d.z);
|
||||
pathnavigateground.setBreakDoors(flag);
|
||||
return this.path != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
if (this.entity.getNavigator().noPath())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
float f = this.entity.width + 4.0F;
|
||||
return this.entity.getDistanceSq(this.doorInfo.getDoorBlockPos()) > (double)(f * f);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.entity.getNavigator().setPath(this.path, this.movementSpeed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
if (this.entity.getNavigator().noPath() || this.entity.getDistanceSq(this.doorInfo.getDoorBlockPos()) < 16.0D)
|
||||
{
|
||||
this.doorList.add(this.doorInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private VillageDoorInfo findNearestDoor(Village villageIn)
|
||||
{
|
||||
VillageDoorInfo villagedoorinfo = null;
|
||||
int i = Integer.MAX_VALUE;
|
||||
|
||||
for (VillageDoorInfo villagedoorinfo1 : villageIn.getVillageDoorInfoList())
|
||||
{
|
||||
int j = villagedoorinfo1.getDistanceSquared(MathHelper.floor(this.entity.posX), MathHelper.floor(this.entity.posY), MathHelper.floor(this.entity.posZ));
|
||||
|
||||
if (j < i && !this.doesDoorListContain(villagedoorinfo1))
|
||||
{
|
||||
villagedoorinfo = villagedoorinfo1;
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
|
||||
return villagedoorinfo;
|
||||
}
|
||||
|
||||
private boolean doesDoorListContain(VillageDoorInfo doorInfoIn)
|
||||
{
|
||||
for (VillageDoorInfo villagedoorinfo : this.doorList)
|
||||
{
|
||||
if (doorInfoIn.getDoorBlockPos().equals(villagedoorinfo.getDoorBlockPos()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void resizeDoorList()
|
||||
{
|
||||
if (this.doorList.size() > 15)
|
||||
{
|
||||
this.doorList.remove(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public abstract class EntityAIMoveToBlock extends EntityAIBase
|
||||
{
|
||||
private final EntityCreature creature;
|
||||
private final double movementSpeed;
|
||||
/** Controls task execution delay */
|
||||
protected int runDelay;
|
||||
private int timeoutCounter;
|
||||
private int maxStayTicks;
|
||||
/** Block to move to */
|
||||
protected BlockPos destinationBlock = BlockPos.ORIGIN;
|
||||
private boolean isAboveDestination;
|
||||
private final int searchLength;
|
||||
|
||||
public EntityAIMoveToBlock(EntityCreature creature, double speedIn, int length)
|
||||
{
|
||||
this.creature = creature;
|
||||
this.movementSpeed = speedIn;
|
||||
this.searchLength = length;
|
||||
this.setMutexBits(5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (this.runDelay > 0)
|
||||
{
|
||||
--this.runDelay;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.runDelay = 200 + this.creature.getRNG().nextInt(200);
|
||||
return this.searchForDestination();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return this.timeoutCounter >= -this.maxStayTicks && this.timeoutCounter <= 1200 && this.shouldMoveTo(this.creature.world, this.destinationBlock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.creature.getNavigator().tryMoveToXYZ((double)((float)this.destinationBlock.getX()) + 0.5D, (double)(this.destinationBlock.getY() + 1), (double)((float)this.destinationBlock.getZ()) + 0.5D, this.movementSpeed);
|
||||
this.timeoutCounter = 0;
|
||||
this.maxStayTicks = this.creature.getRNG().nextInt(this.creature.getRNG().nextInt(1200) + 1200) + 1200;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
if (this.creature.getDistanceSqToCenter(this.destinationBlock.up()) > 1.0D)
|
||||
{
|
||||
this.isAboveDestination = false;
|
||||
++this.timeoutCounter;
|
||||
|
||||
if (this.timeoutCounter % 40 == 0)
|
||||
{
|
||||
this.creature.getNavigator().tryMoveToXYZ((double)((float)this.destinationBlock.getX()) + 0.5D, (double)(this.destinationBlock.getY() + 1), (double)((float)this.destinationBlock.getZ()) + 0.5D, this.movementSpeed);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.isAboveDestination = true;
|
||||
--this.timeoutCounter;
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean getIsAboveDestination()
|
||||
{
|
||||
return this.isAboveDestination;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches and sets new destination block and returns true if a suitable block (specified in {@link
|
||||
* net.minecraft.entity.ai.EntityAIMoveToBlock#shouldMoveTo(World, BlockPos) EntityAIMoveToBlock#shouldMoveTo(World,
|
||||
* BlockPos)}) can be found.
|
||||
*/
|
||||
private boolean searchForDestination()
|
||||
{
|
||||
int i = this.searchLength;
|
||||
int j = 1;
|
||||
BlockPos blockpos = new BlockPos(this.creature);
|
||||
|
||||
for (int k = 0; k <= 1; k = k > 0 ? -k : 1 - k)
|
||||
{
|
||||
for (int l = 0; l < i; ++l)
|
||||
{
|
||||
for (int i1 = 0; i1 <= l; i1 = i1 > 0 ? -i1 : 1 - i1)
|
||||
{
|
||||
for (int j1 = i1 < l && i1 > -l ? l : 0; j1 <= l; j1 = j1 > 0 ? -j1 : 1 - j1)
|
||||
{
|
||||
BlockPos blockpos1 = blockpos.add(i1, k - 1, j1);
|
||||
|
||||
if (this.creature.isWithinHomeDistanceFromPosition(blockpos1) && this.shouldMoveTo(this.creature.world, blockpos1))
|
||||
{
|
||||
this.destinationBlock = blockpos1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true to set given position as destination
|
||||
*/
|
||||
protected abstract boolean shouldMoveTo(World worldIn, BlockPos pos);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public class EntityAIMoveTowardsRestriction extends EntityAIBase
|
||||
{
|
||||
private final EntityCreature creature;
|
||||
private double movePosX;
|
||||
private double movePosY;
|
||||
private double movePosZ;
|
||||
private final double movementSpeed;
|
||||
|
||||
public EntityAIMoveTowardsRestriction(EntityCreature creatureIn, double speedIn)
|
||||
{
|
||||
this.creature = creatureIn;
|
||||
this.movementSpeed = speedIn;
|
||||
this.setMutexBits(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (this.creature.isWithinHomeDistanceCurrentPosition())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
BlockPos blockpos = this.creature.getHomePosition();
|
||||
Vec3d vec3d = RandomPositionGenerator.findRandomTargetBlockTowards(this.creature, 16, 7, new Vec3d((double)blockpos.getX(), (double)blockpos.getY(), (double)blockpos.getZ()));
|
||||
|
||||
if (vec3d == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.movePosX = vec3d.x;
|
||||
this.movePosY = vec3d.y;
|
||||
this.movePosZ = vec3d.z;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return !this.creature.getNavigator().noPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.creature.getNavigator().tryMoveToXYZ(this.movePosX, this.movePosY, this.movePosZ, this.movementSpeed);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public class EntityAIMoveTowardsTarget extends EntityAIBase
|
||||
{
|
||||
private final EntityCreature creature;
|
||||
private EntityLivingBase targetEntity;
|
||||
private double movePosX;
|
||||
private double movePosY;
|
||||
private double movePosZ;
|
||||
private final double speed;
|
||||
/** If the distance to the target entity is further than this, this AI task will not run. */
|
||||
private final float maxTargetDistance;
|
||||
|
||||
public EntityAIMoveTowardsTarget(EntityCreature creature, double speedIn, float targetMaxDistance)
|
||||
{
|
||||
this.creature = creature;
|
||||
this.speed = speedIn;
|
||||
this.maxTargetDistance = targetMaxDistance;
|
||||
this.setMutexBits(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
this.targetEntity = this.creature.getAttackTarget();
|
||||
|
||||
if (this.targetEntity == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (this.targetEntity.getDistanceSq(this.creature) > (double)(this.maxTargetDistance * this.maxTargetDistance))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vec3d vec3d = RandomPositionGenerator.findRandomTargetBlockTowards(this.creature, 16, 7, new Vec3d(this.targetEntity.posX, this.targetEntity.posY, this.targetEntity.posZ));
|
||||
|
||||
if (vec3d == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.movePosX = vec3d.x;
|
||||
this.movePosY = vec3d.y;
|
||||
this.movePosZ = vec3d.z;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return !this.creature.getNavigator().noPath() && this.targetEntity.isEntityAlive() && this.targetEntity.getDistanceSq(this.creature) < (double)(this.maxTargetDistance * this.maxTargetDistance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.targetEntity = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.creature.getNavigator().tryMoveToXYZ(this.movePosX, this.movePosY, this.movePosZ, this.speed);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.monster.EntityCreeper;
|
||||
import net.minecraft.entity.monster.EntitySkeleton;
|
||||
import net.minecraft.entity.monster.EntityZombie;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.inventory.EntityEquipmentSlot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EntitySelectors;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
|
||||
public class EntityAINearestAttackableTarget<T extends EntityLivingBase> extends EntityAITarget
|
||||
{
|
||||
protected final Class<T> targetClass;
|
||||
private final int targetChance;
|
||||
/** Instance of EntityAINearestAttackableTargetSorter. */
|
||||
protected final EntityAINearestAttackableTarget.Sorter sorter;
|
||||
protected final Predicate <? super T > targetEntitySelector;
|
||||
protected T targetEntity;
|
||||
|
||||
public EntityAINearestAttackableTarget(EntityCreature creature, Class<T> classTarget, boolean checkSight)
|
||||
{
|
||||
this(creature, classTarget, checkSight, false);
|
||||
}
|
||||
|
||||
public EntityAINearestAttackableTarget(EntityCreature creature, Class<T> classTarget, boolean checkSight, boolean onlyNearby)
|
||||
{
|
||||
this(creature, classTarget, 10, checkSight, onlyNearby, (Predicate)null);
|
||||
}
|
||||
|
||||
public EntityAINearestAttackableTarget(EntityCreature creature, Class<T> classTarget, int chance, boolean checkSight, boolean onlyNearby, @Nullable final Predicate <? super T > targetSelector)
|
||||
{
|
||||
super(creature, checkSight, onlyNearby);
|
||||
this.targetClass = classTarget;
|
||||
this.targetChance = chance;
|
||||
this.sorter = new EntityAINearestAttackableTarget.Sorter(creature);
|
||||
this.setMutexBits(1);
|
||||
this.targetEntitySelector = new Predicate<T>()
|
||||
{
|
||||
public boolean apply(@Nullable T p_apply_1_)
|
||||
{
|
||||
if (p_apply_1_ == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (targetSelector != null && !targetSelector.apply(p_apply_1_))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return !EntitySelectors.NOT_SPECTATING.apply(p_apply_1_) ? false : EntityAINearestAttackableTarget.this.isSuitableTarget(p_apply_1_, false);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (this.targetChance > 0 && this.taskOwner.getRNG().nextInt(this.targetChance) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (this.targetClass != EntityPlayer.class && this.targetClass != EntityPlayerMP.class)
|
||||
{
|
||||
List<T> list = this.taskOwner.world.<T>getEntitiesWithinAABB(this.targetClass, this.getTargetableArea(this.getTargetDistance()), this.targetEntitySelector);
|
||||
|
||||
if (list.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Collections.sort(list, this.sorter);
|
||||
this.targetEntity = list.get(0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.targetEntity = (T)this.taskOwner.world.getNearestAttackablePlayer(this.taskOwner.posX, this.taskOwner.posY + (double)this.taskOwner.getEyeHeight(), this.taskOwner.posZ, this.getTargetDistance(), this.getTargetDistance(), new Function<EntityPlayer, Double>()
|
||||
{
|
||||
@Nullable
|
||||
public Double apply(@Nullable EntityPlayer p_apply_1_)
|
||||
{
|
||||
ItemStack itemstack = p_apply_1_.getItemStackFromSlot(EntityEquipmentSlot.HEAD);
|
||||
|
||||
if (itemstack.getItem() == Items.SKULL)
|
||||
{
|
||||
int i = itemstack.getItemDamage();
|
||||
boolean flag = EntityAINearestAttackableTarget.this.taskOwner instanceof EntitySkeleton && i == 0;
|
||||
boolean flag1 = EntityAINearestAttackableTarget.this.taskOwner instanceof EntityZombie && i == 2;
|
||||
boolean flag2 = EntityAINearestAttackableTarget.this.taskOwner instanceof EntityCreeper && i == 4;
|
||||
|
||||
if (flag || flag1 || flag2)
|
||||
{
|
||||
return 0.5D;
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0D;
|
||||
}
|
||||
}, (Predicate<EntityPlayer>)this.targetEntitySelector);
|
||||
return this.targetEntity != null;
|
||||
}
|
||||
}
|
||||
|
||||
protected AxisAlignedBB getTargetableArea(double targetDistance)
|
||||
{
|
||||
return this.taskOwner.getEntityBoundingBox().grow(targetDistance, 4.0D, targetDistance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.taskOwner.setAttackTarget(this.targetEntity);
|
||||
super.startExecuting();
|
||||
}
|
||||
|
||||
public static class Sorter implements Comparator<Entity>
|
||||
{
|
||||
private final Entity entity;
|
||||
|
||||
public Sorter(Entity entityIn)
|
||||
{
|
||||
this.entity = entityIn;
|
||||
}
|
||||
|
||||
public int compare(Entity p_compare_1_, Entity p_compare_2_)
|
||||
{
|
||||
double d0 = this.entity.getDistanceSq(p_compare_1_);
|
||||
double d1 = this.entity.getDistanceSq(p_compare_2_);
|
||||
|
||||
if (d0 < d1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return d0 > d1 ? 1 : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class EntityAIOcelotAttack extends EntityAIBase
|
||||
{
|
||||
World world;
|
||||
EntityLiving entity;
|
||||
EntityLivingBase target;
|
||||
int attackCountdown;
|
||||
|
||||
public EntityAIOcelotAttack(EntityLiving theEntityIn)
|
||||
{
|
||||
this.entity = theEntityIn;
|
||||
this.world = theEntityIn.world;
|
||||
this.setMutexBits(3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.entity.getAttackTarget();
|
||||
|
||||
if (entitylivingbase == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.target = entitylivingbase;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
if (!this.target.isEntityAlive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (this.entity.getDistanceSq(this.target) > 225.0D)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return !this.entity.getNavigator().noPath() || this.shouldExecute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.target = null;
|
||||
this.entity.getNavigator().clearPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
this.entity.getLookHelper().setLookPositionWithEntity(this.target, 30.0F, 30.0F);
|
||||
double d0 = (double)(this.entity.width * 2.0F * this.entity.width * 2.0F);
|
||||
double d1 = this.entity.getDistanceSq(this.target.posX, this.target.getEntityBoundingBox().minY, this.target.posZ);
|
||||
double d2 = 0.8D;
|
||||
|
||||
if (d1 > d0 && d1 < 16.0D)
|
||||
{
|
||||
d2 = 1.33D;
|
||||
}
|
||||
else if (d1 < 225.0D)
|
||||
{
|
||||
d2 = 0.6D;
|
||||
}
|
||||
|
||||
this.entity.getNavigator().tryMoveToEntityLiving(this.target, d2);
|
||||
this.attackCountdown = Math.max(this.attackCountdown - 1, 0);
|
||||
|
||||
if (d1 <= d0)
|
||||
{
|
||||
if (this.attackCountdown <= 0)
|
||||
{
|
||||
this.attackCountdown = 20;
|
||||
this.entity.attackEntityAsMob(this.target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockBed;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.passive.EntityOcelot;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.tileentity.TileEntityChest;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class EntityAIOcelotSit extends EntityAIMoveToBlock
|
||||
{
|
||||
private final EntityOcelot ocelot;
|
||||
|
||||
public EntityAIOcelotSit(EntityOcelot ocelotIn, double p_i45315_2_)
|
||||
{
|
||||
super(ocelotIn, p_i45315_2_, 8);
|
||||
this.ocelot = ocelotIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
return this.ocelot.isTamed() && !this.ocelot.isSitting() && super.shouldExecute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
super.startExecuting();
|
||||
this.ocelot.getAISit().setSitting(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
super.resetTask();
|
||||
this.ocelot.setSitting(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
super.updateTask();
|
||||
this.ocelot.getAISit().setSitting(false);
|
||||
|
||||
if (!this.getIsAboveDestination())
|
||||
{
|
||||
this.ocelot.setSitting(false);
|
||||
}
|
||||
else if (!this.ocelot.isSitting())
|
||||
{
|
||||
this.ocelot.setSitting(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true to set given position as destination
|
||||
*/
|
||||
protected boolean shouldMoveTo(World worldIn, BlockPos pos)
|
||||
{
|
||||
if (!worldIn.isAirBlock(pos.up()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
IBlockState iblockstate = worldIn.getBlockState(pos);
|
||||
Block block = iblockstate.getBlock();
|
||||
|
||||
if (block == Blocks.CHEST)
|
||||
{
|
||||
TileEntity tileentity = worldIn.getTileEntity(pos);
|
||||
|
||||
if (tileentity instanceof TileEntityChest && ((TileEntityChest)tileentity).numPlayersUsing < 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (block == Blocks.LIT_FURNACE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (block == Blocks.BED && iblockstate.getValue(BlockBed.PART) != BlockBed.EnumPartType.HEAD)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
|
||||
public class EntityAIOpenDoor extends EntityAIDoorInteract
|
||||
{
|
||||
/** If the entity close the door */
|
||||
boolean closeDoor;
|
||||
/** The temporisation before the entity close the door (in ticks, always 20 = 1 second) */
|
||||
int closeDoorTemporisation;
|
||||
|
||||
public EntityAIOpenDoor(EntityLiving entitylivingIn, boolean shouldClose)
|
||||
{
|
||||
super(entitylivingIn);
|
||||
this.entity = entitylivingIn;
|
||||
this.closeDoor = shouldClose;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return this.closeDoor && this.closeDoorTemporisation > 0 && super.shouldContinueExecuting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.closeDoorTemporisation = 20;
|
||||
this.doorBlock.toggleDoor(this.entity.world, this.doorPosition, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
if (this.closeDoor)
|
||||
{
|
||||
this.doorBlock.toggleDoor(this.entity.world, this.doorPosition, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
--this.closeDoorTemporisation;
|
||||
super.updateTask();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.passive.EntityTameable;
|
||||
|
||||
public class EntityAIOwnerHurtByTarget extends EntityAITarget
|
||||
{
|
||||
EntityTameable tameable;
|
||||
EntityLivingBase attacker;
|
||||
private int timestamp;
|
||||
|
||||
public EntityAIOwnerHurtByTarget(EntityTameable theDefendingTameableIn)
|
||||
{
|
||||
super(theDefendingTameableIn, false);
|
||||
this.tameable = theDefendingTameableIn;
|
||||
this.setMutexBits(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (!this.tameable.isTamed())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.tameable.getOwner();
|
||||
|
||||
if (entitylivingbase == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.attacker = entitylivingbase.getRevengeTarget();
|
||||
int i = entitylivingbase.getRevengeTimer();
|
||||
return i != this.timestamp && this.isSuitableTarget(this.attacker, false) && this.tameable.shouldAttackEntity(this.attacker, entitylivingbase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.taskOwner.setAttackTarget(this.attacker);
|
||||
EntityLivingBase entitylivingbase = this.tameable.getOwner();
|
||||
|
||||
if (entitylivingbase != null)
|
||||
{
|
||||
this.timestamp = entitylivingbase.getRevengeTimer();
|
||||
}
|
||||
|
||||
super.startExecuting();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.passive.EntityTameable;
|
||||
|
||||
public class EntityAIOwnerHurtTarget extends EntityAITarget
|
||||
{
|
||||
EntityTameable tameable;
|
||||
EntityLivingBase attacker;
|
||||
private int timestamp;
|
||||
|
||||
public EntityAIOwnerHurtTarget(EntityTameable theEntityTameableIn)
|
||||
{
|
||||
super(theEntityTameableIn, false);
|
||||
this.tameable = theEntityTameableIn;
|
||||
this.setMutexBits(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (!this.tameable.isTamed())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.tameable.getOwner();
|
||||
|
||||
if (entitylivingbase == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.attacker = entitylivingbase.getLastAttackedEntity();
|
||||
int i = entitylivingbase.getLastAttackedEntityTime();
|
||||
return i != this.timestamp && this.isSuitableTarget(this.attacker, false) && this.tameable.shouldAttackEntity(this.attacker, entitylivingbase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.taskOwner.setAttackTarget(this.attacker);
|
||||
EntityLivingBase entitylivingbase = this.tameable.getOwner();
|
||||
|
||||
if (entitylivingbase != null)
|
||||
{
|
||||
this.timestamp = entitylivingbase.getLastAttackedEntityTime();
|
||||
}
|
||||
|
||||
super.startExecuting();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class EntityAIPanic extends EntityAIBase
|
||||
{
|
||||
protected final EntityCreature creature;
|
||||
protected double speed;
|
||||
protected double randPosX;
|
||||
protected double randPosY;
|
||||
protected double randPosZ;
|
||||
|
||||
public EntityAIPanic(EntityCreature creature, double speedIn)
|
||||
{
|
||||
this.creature = creature;
|
||||
this.speed = speedIn;
|
||||
this.setMutexBits(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (this.creature.getRevengeTarget() == null && !this.creature.isBurning())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.creature.isBurning())
|
||||
{
|
||||
BlockPos blockpos = this.getRandPos(this.creature.world, this.creature, 5, 4);
|
||||
|
||||
if (blockpos != null)
|
||||
{
|
||||
this.randPosX = (double)blockpos.getX();
|
||||
this.randPosY = (double)blockpos.getY();
|
||||
this.randPosZ = (double)blockpos.getZ();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return this.findRandomPosition();
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean findRandomPosition()
|
||||
{
|
||||
Vec3d vec3d = RandomPositionGenerator.findRandomTarget(this.creature, 5, 4);
|
||||
|
||||
if (vec3d == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.randPosX = vec3d.x;
|
||||
this.randPosY = vec3d.y;
|
||||
this.randPosZ = vec3d.z;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.creature.getNavigator().tryMoveToXYZ(this.randPosX, this.randPosY, this.randPosZ, this.speed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return !this.creature.getNavigator().noPath();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BlockPos getRandPos(World worldIn, Entity entityIn, int horizontalRange, int verticalRange)
|
||||
{
|
||||
BlockPos blockpos = new BlockPos(entityIn);
|
||||
int i = blockpos.getX();
|
||||
int j = blockpos.getY();
|
||||
int k = blockpos.getZ();
|
||||
float f = (float)(horizontalRange * horizontalRange * verticalRange * 2);
|
||||
BlockPos blockpos1 = null;
|
||||
BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos();
|
||||
|
||||
for (int l = i - horizontalRange; l <= i + horizontalRange; ++l)
|
||||
{
|
||||
for (int i1 = j - verticalRange; i1 <= j + verticalRange; ++i1)
|
||||
{
|
||||
for (int j1 = k - horizontalRange; j1 <= k + horizontalRange; ++j1)
|
||||
{
|
||||
blockpos$mutableblockpos.setPos(l, i1, j1);
|
||||
IBlockState iblockstate = worldIn.getBlockState(blockpos$mutableblockpos);
|
||||
|
||||
if (iblockstate.getMaterial() == Material.WATER)
|
||||
{
|
||||
float f1 = (float)((l - i) * (l - i) + (i1 - j) * (i1 - j) + (j1 - k) * (j1 - k));
|
||||
|
||||
if (f1 < f)
|
||||
{
|
||||
f = f1;
|
||||
blockpos1 = new BlockPos(blockpos$mutableblockpos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return blockpos1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import java.util.List;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.passive.EntityVillager;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public class EntityAIPlay extends EntityAIBase
|
||||
{
|
||||
private final EntityVillager villager;
|
||||
private EntityLivingBase targetVillager;
|
||||
private final double speed;
|
||||
private int playTime;
|
||||
|
||||
public EntityAIPlay(EntityVillager villagerIn, double speedIn)
|
||||
{
|
||||
this.villager = villagerIn;
|
||||
this.speed = speedIn;
|
||||
this.setMutexBits(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (this.villager.getGrowingAge() >= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (this.villager.getRNG().nextInt(400) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
List<EntityVillager> list = this.villager.world.<EntityVillager>getEntitiesWithinAABB(EntityVillager.class, this.villager.getEntityBoundingBox().grow(6.0D, 3.0D, 6.0D));
|
||||
double d0 = Double.MAX_VALUE;
|
||||
|
||||
for (EntityVillager entityvillager : list)
|
||||
{
|
||||
if (entityvillager != this.villager && !entityvillager.isPlaying() && entityvillager.getGrowingAge() < 0)
|
||||
{
|
||||
double d1 = entityvillager.getDistanceSq(this.villager);
|
||||
|
||||
if (d1 <= d0)
|
||||
{
|
||||
d0 = d1;
|
||||
this.targetVillager = entityvillager;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.targetVillager == null)
|
||||
{
|
||||
Vec3d vec3d = RandomPositionGenerator.findRandomTarget(this.villager, 16, 3);
|
||||
|
||||
if (vec3d == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return this.playTime > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
if (this.targetVillager != null)
|
||||
{
|
||||
this.villager.setPlaying(true);
|
||||
}
|
||||
|
||||
this.playTime = 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.villager.setPlaying(false);
|
||||
this.targetVillager = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
--this.playTime;
|
||||
|
||||
if (this.targetVillager != null)
|
||||
{
|
||||
if (this.villager.getDistanceSq(this.targetVillager) > 4.0D)
|
||||
{
|
||||
this.villager.getNavigator().tryMoveToEntityLiving(this.targetVillager, this.speed);
|
||||
}
|
||||
}
|
||||
else if (this.villager.getNavigator().noPath())
|
||||
{
|
||||
Vec3d vec3d = RandomPositionGenerator.findRandomTarget(this.villager, 16, 3);
|
||||
|
||||
if (vec3d == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.villager.getNavigator().tryMoveToXYZ(vec3d.x, vec3d.y, vec3d.z, this.speed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.pathfinding.PathNavigateGround;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.village.Village;
|
||||
import net.minecraft.village.VillageDoorInfo;
|
||||
|
||||
public class EntityAIRestrictOpenDoor extends EntityAIBase
|
||||
{
|
||||
private final EntityCreature entity;
|
||||
private VillageDoorInfo frontDoor;
|
||||
|
||||
public EntityAIRestrictOpenDoor(EntityCreature creatureIn)
|
||||
{
|
||||
this.entity = creatureIn;
|
||||
|
||||
if (!(creatureIn.getNavigator() instanceof PathNavigateGround))
|
||||
{
|
||||
throw new IllegalArgumentException("Unsupported mob type for RestrictOpenDoorGoal");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (this.entity.world.isDaytime())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
BlockPos blockpos = new BlockPos(this.entity);
|
||||
Village village = this.entity.world.getVillageCollection().getNearestVillage(blockpos, 16);
|
||||
|
||||
if (village == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.frontDoor = village.getNearestDoor(blockpos);
|
||||
|
||||
if (this.frontDoor == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (double)this.frontDoor.getDistanceToInsideBlockSq(blockpos) < 2.25D;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
if (this.entity.world.isDaytime())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return !this.frontDoor.getIsDetachedFromVillageFlag() && this.frontDoor.isInsideSide(new BlockPos(this.entity));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
((PathNavigateGround)this.entity.getNavigator()).setBreakDoors(false);
|
||||
((PathNavigateGround)this.entity.getNavigator()).setEnterDoors(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
((PathNavigateGround)this.entity.getNavigator()).setBreakDoors(true);
|
||||
((PathNavigateGround)this.entity.getNavigator()).setEnterDoors(true);
|
||||
this.frontDoor = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
this.frontDoor.incrementDoorOpeningRestrictionCounter();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.inventory.EntityEquipmentSlot;
|
||||
import net.minecraft.pathfinding.PathNavigateGround;
|
||||
|
||||
public class EntityAIRestrictSun extends EntityAIBase
|
||||
{
|
||||
private final EntityCreature entity;
|
||||
|
||||
public EntityAIRestrictSun(EntityCreature creature)
|
||||
{
|
||||
this.entity = creature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
return this.entity.world.isDaytime() && this.entity.getItemStackFromSlot(EntityEquipmentSlot.HEAD).isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
((PathNavigateGround)this.entity.getNavigator()).setAvoidSun(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
((PathNavigateGround)this.entity.getNavigator()).setAvoidSun(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.passive.AbstractHorse;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public class EntityAIRunAroundLikeCrazy extends EntityAIBase
|
||||
{
|
||||
private final AbstractHorse horseHost;
|
||||
private final double speed;
|
||||
private double targetX;
|
||||
private double targetY;
|
||||
private double targetZ;
|
||||
|
||||
public EntityAIRunAroundLikeCrazy(AbstractHorse horse, double speedIn)
|
||||
{
|
||||
this.horseHost = horse;
|
||||
this.speed = speedIn;
|
||||
this.setMutexBits(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (!this.horseHost.isTame() && this.horseHost.isBeingRidden())
|
||||
{
|
||||
Vec3d vec3d = RandomPositionGenerator.findRandomTarget(this.horseHost, 5, 4);
|
||||
|
||||
if (vec3d == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.targetX = vec3d.x;
|
||||
this.targetY = vec3d.y;
|
||||
this.targetZ = vec3d.z;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.horseHost.getNavigator().tryMoveToXYZ(this.targetX, this.targetY, this.targetZ, this.speed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return !this.horseHost.isTame() && !this.horseHost.getNavigator().noPath() && this.horseHost.isBeingRidden();
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
if (!this.horseHost.isTame() && this.horseHost.getRNG().nextInt(50) == 0)
|
||||
{
|
||||
Entity entity = (Entity)this.horseHost.getPassengers().get(0);
|
||||
|
||||
if (entity == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (entity instanceof EntityPlayer)
|
||||
{
|
||||
int i = this.horseHost.getTemper();
|
||||
int j = this.horseHost.getMaxTemper();
|
||||
|
||||
if (j > 0 && this.horseHost.getRNG().nextInt(j) < i && !net.minecraftforge.event.ForgeEventFactory.onAnimalTame(horseHost, (EntityPlayer)entity))
|
||||
{
|
||||
this.horseHost.setTamedBy((EntityPlayer)entity);
|
||||
return;
|
||||
}
|
||||
|
||||
this.horseHost.increaseTemper(5);
|
||||
}
|
||||
|
||||
this.horseHost.removePassengers();
|
||||
this.horseHost.makeMad();
|
||||
this.horseHost.world.setEntityState(this.horseHost, (byte)6);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.passive.EntityTameable;
|
||||
|
||||
public class EntityAISit extends EntityAIBase
|
||||
{
|
||||
private final EntityTameable tameable;
|
||||
/** If the EntityTameable is sitting. */
|
||||
private boolean isSitting;
|
||||
|
||||
public EntityAISit(EntityTameable entityIn)
|
||||
{
|
||||
this.tameable = entityIn;
|
||||
this.setMutexBits(5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (!this.tameable.isTamed())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (this.tameable.isInWater())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!this.tameable.onGround)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.tameable.getOwner();
|
||||
|
||||
if (entitylivingbase == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.tameable.getDistanceSq(entitylivingbase) < 144.0D && entitylivingbase.getRevengeTarget() != null ? false : this.isSitting;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.tameable.getNavigator().clearPath();
|
||||
this.tameable.setSitting(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.tameable.setSitting(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sitting flag.
|
||||
*/
|
||||
public void setSitting(boolean sitting)
|
||||
{
|
||||
this.isSitting = sitting;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.entity.IEntityLivingData;
|
||||
import net.minecraft.entity.effect.EntityLightningBolt;
|
||||
import net.minecraft.entity.monster.EntitySkeleton;
|
||||
import net.minecraft.entity.passive.AbstractHorse;
|
||||
import net.minecraft.entity.passive.EntitySkeletonHorse;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.inventory.EntityEquipmentSlot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.DifficultyInstance;
|
||||
|
||||
public class EntityAISkeletonRiders extends EntityAIBase
|
||||
{
|
||||
private final EntitySkeletonHorse horse;
|
||||
|
||||
public EntityAISkeletonRiders(EntitySkeletonHorse horseIn)
|
||||
{
|
||||
this.horse = horseIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
return this.horse.world.isAnyPlayerWithinRangeAt(this.horse.posX, this.horse.posY, this.horse.posZ, 10.0D);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
DifficultyInstance difficultyinstance = this.horse.world.getDifficultyForLocation(new BlockPos(this.horse));
|
||||
this.horse.setTrap(false);
|
||||
this.horse.setHorseTamed(true);
|
||||
this.horse.setGrowingAge(0);
|
||||
this.horse.world.addWeatherEffect(new EntityLightningBolt(this.horse.world, this.horse.posX, this.horse.posY, this.horse.posZ, true));
|
||||
EntitySkeleton entityskeleton = this.createSkeleton(difficultyinstance, this.horse);
|
||||
entityskeleton.startRiding(this.horse);
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
AbstractHorse abstracthorse = this.createHorse(difficultyinstance);
|
||||
EntitySkeleton entityskeleton1 = this.createSkeleton(difficultyinstance, abstracthorse);
|
||||
entityskeleton1.startRiding(abstracthorse);
|
||||
abstracthorse.addVelocity(this.horse.getRNG().nextGaussian() * 0.5D, 0.0D, this.horse.getRNG().nextGaussian() * 0.5D);
|
||||
}
|
||||
}
|
||||
|
||||
private AbstractHorse createHorse(DifficultyInstance p_188515_1_)
|
||||
{
|
||||
EntitySkeletonHorse entityskeletonhorse = new EntitySkeletonHorse(this.horse.world);
|
||||
entityskeletonhorse.onInitialSpawn(p_188515_1_, (IEntityLivingData)null);
|
||||
entityskeletonhorse.setPosition(this.horse.posX, this.horse.posY, this.horse.posZ);
|
||||
entityskeletonhorse.hurtResistantTime = 60;
|
||||
entityskeletonhorse.enablePersistence();
|
||||
entityskeletonhorse.setHorseTamed(true);
|
||||
entityskeletonhorse.setGrowingAge(0);
|
||||
entityskeletonhorse.world.spawnEntity(entityskeletonhorse);
|
||||
return entityskeletonhorse;
|
||||
}
|
||||
|
||||
private EntitySkeleton createSkeleton(DifficultyInstance p_188514_1_, AbstractHorse p_188514_2_)
|
||||
{
|
||||
EntitySkeleton entityskeleton = new EntitySkeleton(p_188514_2_.world);
|
||||
entityskeleton.onInitialSpawn(p_188514_1_, (IEntityLivingData)null);
|
||||
entityskeleton.setPosition(p_188514_2_.posX, p_188514_2_.posY, p_188514_2_.posZ);
|
||||
entityskeleton.hurtResistantTime = 60;
|
||||
entityskeleton.enablePersistence();
|
||||
|
||||
if (entityskeleton.getItemStackFromSlot(EntityEquipmentSlot.HEAD).isEmpty())
|
||||
{
|
||||
entityskeleton.setItemStackToSlot(EntityEquipmentSlot.HEAD, new ItemStack(Items.IRON_HELMET));
|
||||
}
|
||||
|
||||
entityskeleton.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, EnchantmentHelper.addRandomEnchantment(entityskeleton.getRNG(), entityskeleton.getHeldItemMainhand(), (int)(5.0F + p_188514_1_.getClampedAdditionalDifficulty() * (float)entityskeleton.getRNG().nextInt(18)), false));
|
||||
entityskeleton.setItemStackToSlot(EntityEquipmentSlot.HEAD, EnchantmentHelper.addRandomEnchantment(entityskeleton.getRNG(), entityskeleton.getItemStackFromSlot(EntityEquipmentSlot.HEAD), (int)(5.0F + p_188514_1_.getClampedAdditionalDifficulty() * (float)entityskeleton.getRNG().nextInt(18)), false));
|
||||
entityskeleton.world.spawnEntity(entityskeleton);
|
||||
return entityskeleton;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.pathfinding.PathNavigateFlying;
|
||||
import net.minecraft.pathfinding.PathNavigateGround;
|
||||
|
||||
public class EntityAISwimming extends EntityAIBase
|
||||
{
|
||||
private final EntityLiving entity;
|
||||
|
||||
public EntityAISwimming(EntityLiving entityIn)
|
||||
{
|
||||
this.entity = entityIn;
|
||||
this.setMutexBits(4);
|
||||
|
||||
if (entityIn.getNavigator() instanceof PathNavigateGround)
|
||||
{
|
||||
((PathNavigateGround)entityIn.getNavigator()).setCanSwim(true);
|
||||
}
|
||||
else if (entityIn.getNavigator() instanceof PathNavigateFlying)
|
||||
{
|
||||
((PathNavigateFlying)entityIn.getNavigator()).setCanFloat(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
return this.entity.isInWater() || this.entity.isInLava();
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
if (this.entity.getRNG().nextFloat() < 0.8F)
|
||||
{
|
||||
this.entity.getJumpHelper().setJumping();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.IEntityOwnable;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.attributes.IAttributeInstance;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.pathfinding.Path;
|
||||
import net.minecraft.pathfinding.PathPoint;
|
||||
import net.minecraft.scoreboard.Team;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
public abstract class EntityAITarget extends EntityAIBase
|
||||
{
|
||||
/** The entity that this task belongs to */
|
||||
protected final EntityCreature taskOwner;
|
||||
/** If true, EntityAI targets must be able to be seen (cannot be blocked by walls) to be suitable targets. */
|
||||
protected boolean shouldCheckSight;
|
||||
/** When true, only entities that can be reached with minimal effort will be targetted. */
|
||||
private final boolean nearbyOnly;
|
||||
/** When nearbyOnly is true: 0 -> No target, but OK to search; 1 -> Nearby target found; 2 -> Target too far. */
|
||||
private int targetSearchStatus;
|
||||
/** When nearbyOnly is true, this throttles target searching to avoid excessive pathfinding. */
|
||||
private int targetSearchDelay;
|
||||
/**
|
||||
* If @shouldCheckSight is true, the number of ticks before the interuption of this AITastk when the entity does't
|
||||
* see the target
|
||||
*/
|
||||
private int targetUnseenTicks;
|
||||
protected EntityLivingBase target;
|
||||
protected int unseenMemoryTicks;
|
||||
|
||||
public EntityAITarget(EntityCreature creature, boolean checkSight)
|
||||
{
|
||||
this(creature, checkSight, false);
|
||||
}
|
||||
|
||||
public EntityAITarget(EntityCreature creature, boolean checkSight, boolean onlyNearby)
|
||||
{
|
||||
this.unseenMemoryTicks = 60;
|
||||
this.taskOwner = creature;
|
||||
this.shouldCheckSight = checkSight;
|
||||
this.nearbyOnly = onlyNearby;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.taskOwner.getAttackTarget();
|
||||
|
||||
if (entitylivingbase == null)
|
||||
{
|
||||
entitylivingbase = this.target;
|
||||
}
|
||||
|
||||
if (entitylivingbase == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!entitylivingbase.isEntityAlive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Team team = this.taskOwner.getTeam();
|
||||
Team team1 = entitylivingbase.getTeam();
|
||||
|
||||
if (team != null && team1 == team)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
double d0 = this.getTargetDistance();
|
||||
|
||||
if (this.taskOwner.getDistanceSq(entitylivingbase) > d0 * d0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.shouldCheckSight)
|
||||
{
|
||||
if (this.taskOwner.getEntitySenses().canSee(entitylivingbase))
|
||||
{
|
||||
this.targetUnseenTicks = 0;
|
||||
}
|
||||
else if (++this.targetUnseenTicks > this.unseenMemoryTicks)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (entitylivingbase instanceof EntityPlayer && ((EntityPlayer)entitylivingbase).capabilities.disableDamage)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.taskOwner.setAttackTarget(entitylivingbase);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected double getTargetDistance()
|
||||
{
|
||||
IAttributeInstance iattributeinstance = this.taskOwner.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE);
|
||||
return iattributeinstance == null ? 16.0D : iattributeinstance.getAttributeValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.targetSearchStatus = 0;
|
||||
this.targetSearchDelay = 0;
|
||||
this.targetUnseenTicks = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.taskOwner.setAttackTarget((EntityLivingBase)null);
|
||||
this.target = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A static method used to see if an entity is a suitable target through a number of checks.
|
||||
*/
|
||||
public static boolean isSuitableTarget(EntityLiving attacker, @Nullable EntityLivingBase target, boolean includeInvincibles, boolean checkSight)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (target == attacker)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!target.isEntityAlive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!attacker.canAttackClass(target.getClass()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (attacker.isOnSameTeam(target))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (attacker instanceof IEntityOwnable && ((IEntityOwnable)attacker).getOwnerId() != null)
|
||||
{
|
||||
if (target instanceof IEntityOwnable && ((IEntityOwnable)attacker).getOwnerId().equals(((IEntityOwnable)target).getOwnerId()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target == ((IEntityOwnable)attacker).getOwner())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (target instanceof EntityPlayer && !includeInvincibles && ((EntityPlayer)target).capabilities.disableDamage)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return !checkSight || attacker.getEntitySenses().canSee(target);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method used to see if an entity is a suitable target through a number of checks. Args : entity,
|
||||
* canTargetInvinciblePlayer
|
||||
*/
|
||||
protected boolean isSuitableTarget(@Nullable EntityLivingBase target, boolean includeInvincibles)
|
||||
{
|
||||
if (!isSuitableTarget(this.taskOwner, target, includeInvincibles, this.shouldCheckSight))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!this.taskOwner.isWithinHomeDistanceFromPosition(new BlockPos(target)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.nearbyOnly)
|
||||
{
|
||||
if (--this.targetSearchDelay <= 0)
|
||||
{
|
||||
this.targetSearchStatus = 0;
|
||||
}
|
||||
|
||||
if (this.targetSearchStatus == 0)
|
||||
{
|
||||
this.targetSearchStatus = this.canEasilyReach(target) ? 1 : 2;
|
||||
}
|
||||
|
||||
if (this.targetSearchStatus == 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if this entity can find a short path to the given target.
|
||||
*/
|
||||
private boolean canEasilyReach(EntityLivingBase target)
|
||||
{
|
||||
this.targetSearchDelay = 10 + this.taskOwner.getRNG().nextInt(5);
|
||||
Path path = this.taskOwner.getNavigator().getPathToEntityLiving(target);
|
||||
|
||||
if (path == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
PathPoint pathpoint = path.getFinalPathPoint();
|
||||
|
||||
if (pathpoint == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = pathpoint.x - MathHelper.floor(target.posX);
|
||||
int j = pathpoint.z - MathHelper.floor(target.posZ);
|
||||
return (double)(i * i + j * j) <= 2.25D;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public EntityAITarget setUnseenMemoryTicks(int p_190882_1_)
|
||||
{
|
||||
this.unseenMemoryTicks = p_190882_1_;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.passive.EntityTameable;
|
||||
|
||||
public class EntityAITargetNonTamed<T extends EntityLivingBase> extends EntityAINearestAttackableTarget<T>
|
||||
{
|
||||
private final EntityTameable tameable;
|
||||
|
||||
public EntityAITargetNonTamed(EntityTameable entityIn, Class<T> classTarget, boolean checkSight, Predicate <? super T > targetSelector)
|
||||
{
|
||||
super(entityIn, classTarget, 10, checkSight, false, targetSelector);
|
||||
this.tameable = entityIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
return !this.tameable.isTamed() && super.shouldExecute();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.profiler.Profiler;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class EntityAITasks
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
/** A list of EntityAITaskEntrys in EntityAITasks. */
|
||||
public final Set<EntityAITasks.EntityAITaskEntry> taskEntries = Sets.<EntityAITasks.EntityAITaskEntry>newLinkedHashSet();
|
||||
/** A list of EntityAITaskEntrys that are currently being executed. */
|
||||
private final Set<EntityAITasks.EntityAITaskEntry> executingTaskEntries = Sets.<EntityAITasks.EntityAITaskEntry>newLinkedHashSet();
|
||||
/** Instance of Profiler. */
|
||||
private final Profiler profiler;
|
||||
private int tickCount;
|
||||
private int tickRate = 3;
|
||||
private int disabledControlFlags;
|
||||
|
||||
public EntityAITasks(Profiler profilerIn)
|
||||
{
|
||||
this.profiler = profilerIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a now AITask. Args : priority, task
|
||||
*/
|
||||
public void addTask(int priority, EntityAIBase task)
|
||||
{
|
||||
this.taskEntries.add(new EntityAITasks.EntityAITaskEntry(priority, task));
|
||||
}
|
||||
|
||||
/**
|
||||
* removes the indicated task from the entity's AI tasks.
|
||||
*/
|
||||
public void removeTask(EntityAIBase task)
|
||||
{
|
||||
Iterator<EntityAITasks.EntityAITaskEntry> iterator = this.taskEntries.iterator();
|
||||
|
||||
while (iterator.hasNext())
|
||||
{
|
||||
EntityAITasks.EntityAITaskEntry entityaitasks$entityaitaskentry = iterator.next();
|
||||
EntityAIBase entityaibase = entityaitasks$entityaitaskentry.action;
|
||||
|
||||
if (entityaibase == task)
|
||||
{
|
||||
if (entityaitasks$entityaitaskentry.using)
|
||||
{
|
||||
entityaitasks$entityaitaskentry.using = false;
|
||||
entityaitasks$entityaitaskentry.action.resetTask();
|
||||
this.executingTaskEntries.remove(entityaitasks$entityaitaskentry);
|
||||
}
|
||||
|
||||
iterator.remove();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onUpdateTasks()
|
||||
{
|
||||
this.profiler.startSection("goalSetup");
|
||||
|
||||
if (this.tickCount++ % this.tickRate == 0)
|
||||
{
|
||||
for (EntityAITasks.EntityAITaskEntry entityaitasks$entityaitaskentry : this.taskEntries)
|
||||
{
|
||||
if (entityaitasks$entityaitaskentry.using)
|
||||
{
|
||||
if (!this.canUse(entityaitasks$entityaitaskentry) || !this.canContinue(entityaitasks$entityaitaskentry))
|
||||
{
|
||||
entityaitasks$entityaitaskentry.using = false;
|
||||
entityaitasks$entityaitaskentry.action.resetTask();
|
||||
this.executingTaskEntries.remove(entityaitasks$entityaitaskentry);
|
||||
}
|
||||
}
|
||||
else if (this.canUse(entityaitasks$entityaitaskentry) && entityaitasks$entityaitaskentry.action.shouldExecute())
|
||||
{
|
||||
entityaitasks$entityaitaskentry.using = true;
|
||||
entityaitasks$entityaitaskentry.action.startExecuting();
|
||||
this.executingTaskEntries.add(entityaitasks$entityaitaskentry);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Iterator<EntityAITasks.EntityAITaskEntry> iterator = this.executingTaskEntries.iterator();
|
||||
|
||||
while (iterator.hasNext())
|
||||
{
|
||||
EntityAITasks.EntityAITaskEntry entityaitasks$entityaitaskentry1 = iterator.next();
|
||||
|
||||
if (!this.canContinue(entityaitasks$entityaitaskentry1))
|
||||
{
|
||||
entityaitasks$entityaitaskentry1.using = false;
|
||||
entityaitasks$entityaitaskentry1.action.resetTask();
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.profiler.endSection();
|
||||
|
||||
if (!this.executingTaskEntries.isEmpty())
|
||||
{
|
||||
this.profiler.startSection("goalTick");
|
||||
|
||||
for (EntityAITasks.EntityAITaskEntry entityaitasks$entityaitaskentry2 : this.executingTaskEntries)
|
||||
{
|
||||
entityaitasks$entityaitaskentry2.action.updateTask();
|
||||
}
|
||||
|
||||
this.profiler.endSection();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a specific AI Task should continue being executed.
|
||||
*/
|
||||
private boolean canContinue(EntityAITasks.EntityAITaskEntry taskEntry)
|
||||
{
|
||||
return taskEntry.action.shouldContinueExecuting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a specific AI Task can be executed, which means that all running higher (= lower int value) priority
|
||||
* tasks are compatible with it or all lower priority tasks can be interrupted.
|
||||
*/
|
||||
private boolean canUse(EntityAITasks.EntityAITaskEntry taskEntry)
|
||||
{
|
||||
if (this.executingTaskEntries.isEmpty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (this.isControlFlagDisabled(taskEntry.action.getMutexBits()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (EntityAITasks.EntityAITaskEntry entityaitasks$entityaitaskentry : this.executingTaskEntries)
|
||||
{
|
||||
if (entityaitasks$entityaitaskentry != taskEntry)
|
||||
{
|
||||
if (taskEntry.priority >= entityaitasks$entityaitaskentry.priority)
|
||||
{
|
||||
if (!this.areTasksCompatible(taskEntry, entityaitasks$entityaitaskentry))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!entityaitasks$entityaitaskentry.action.isInterruptible())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether two EntityAITaskEntries can be executed concurrently
|
||||
*/
|
||||
private boolean areTasksCompatible(EntityAITasks.EntityAITaskEntry taskEntry1, EntityAITasks.EntityAITaskEntry taskEntry2)
|
||||
{
|
||||
return (taskEntry1.action.getMutexBits() & taskEntry2.action.getMutexBits()) == 0;
|
||||
}
|
||||
|
||||
public boolean isControlFlagDisabled(int p_188528_1_)
|
||||
{
|
||||
return (this.disabledControlFlags & p_188528_1_) > 0;
|
||||
}
|
||||
|
||||
public void disableControlFlag(int p_188526_1_)
|
||||
{
|
||||
this.disabledControlFlags |= p_188526_1_;
|
||||
}
|
||||
|
||||
public void enableControlFlag(int p_188525_1_)
|
||||
{
|
||||
this.disabledControlFlags &= ~p_188525_1_;
|
||||
}
|
||||
|
||||
public void setControlFlag(int p_188527_1_, boolean p_188527_2_)
|
||||
{
|
||||
if (p_188527_2_)
|
||||
{
|
||||
this.enableControlFlag(p_188527_1_);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.disableControlFlag(p_188527_1_);
|
||||
}
|
||||
}
|
||||
|
||||
public class EntityAITaskEntry
|
||||
{
|
||||
/** The EntityAIBase object. */
|
||||
public final EntityAIBase action;
|
||||
/** Priority of the EntityAIBase */
|
||||
public final int priority;
|
||||
public boolean using;
|
||||
|
||||
public EntityAITaskEntry(int priorityIn, EntityAIBase task)
|
||||
{
|
||||
this.priority = priorityIn;
|
||||
this.action = task;
|
||||
}
|
||||
|
||||
public boolean equals(@Nullable Object p_equals_1_)
|
||||
{
|
||||
if (this == p_equals_1_)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return p_equals_1_ != null && this.getClass() == p_equals_1_.getClass() ? this.action.equals(((EntityAITasks.EntityAITaskEntry)p_equals_1_).action) : false;
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode()
|
||||
{
|
||||
return this.action.hashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Set;
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.pathfinding.PathNavigateGround;
|
||||
|
||||
public class EntityAITempt extends EntityAIBase
|
||||
{
|
||||
/** The entity using this AI that is tempted by the player. */
|
||||
private final EntityCreature temptedEntity;
|
||||
private final double speed;
|
||||
/** X position of player tempting this mob */
|
||||
private double targetX;
|
||||
/** Y position of player tempting this mob */
|
||||
private double targetY;
|
||||
/** Z position of player tempting this mob */
|
||||
private double targetZ;
|
||||
/** Tempting player's pitch */
|
||||
private double pitch;
|
||||
/** Tempting player's yaw */
|
||||
private double yaw;
|
||||
/** The player that is tempting the entity that is using this AI. */
|
||||
private EntityPlayer temptingPlayer;
|
||||
/**
|
||||
* A counter that is decremented each time the shouldExecute method is called. The shouldExecute method will always
|
||||
* return false if delayTemptCounter is greater than 0.
|
||||
*/
|
||||
private int delayTemptCounter;
|
||||
/** True if this EntityAITempt task is running */
|
||||
private boolean isRunning;
|
||||
private final Set<Item> temptItem;
|
||||
/** Whether the entity using this AI will be scared by the tempter's sudden movement. */
|
||||
private final boolean scaredByPlayerMovement;
|
||||
|
||||
public EntityAITempt(EntityCreature temptedEntityIn, double speedIn, Item temptItemIn, boolean scaredByPlayerMovementIn)
|
||||
{
|
||||
this(temptedEntityIn, speedIn, scaredByPlayerMovementIn, Sets.newHashSet(temptItemIn));
|
||||
}
|
||||
|
||||
public EntityAITempt(EntityCreature temptedEntityIn, double speedIn, boolean scaredByPlayerMovementIn, Set<Item> temptItemIn)
|
||||
{
|
||||
this.temptedEntity = temptedEntityIn;
|
||||
this.speed = speedIn;
|
||||
this.temptItem = temptItemIn;
|
||||
this.scaredByPlayerMovement = scaredByPlayerMovementIn;
|
||||
this.setMutexBits(3);
|
||||
|
||||
if (!(temptedEntityIn.getNavigator() instanceof PathNavigateGround))
|
||||
{
|
||||
throw new IllegalArgumentException("Unsupported mob type for TemptGoal");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (this.delayTemptCounter > 0)
|
||||
{
|
||||
--this.delayTemptCounter;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.temptingPlayer = this.temptedEntity.world.getClosestPlayerToEntity(this.temptedEntity, 10.0D);
|
||||
|
||||
if (this.temptingPlayer == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.isTempting(this.temptingPlayer.getHeldItemMainhand()) || this.isTempting(this.temptingPlayer.getHeldItemOffhand());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isTempting(ItemStack stack)
|
||||
{
|
||||
return this.temptItem.contains(stack.getItem());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
if (this.scaredByPlayerMovement)
|
||||
{
|
||||
if (this.temptedEntity.getDistanceSq(this.temptingPlayer) < 36.0D)
|
||||
{
|
||||
if (this.temptingPlayer.getDistanceSq(this.targetX, this.targetY, this.targetZ) > 0.010000000000000002D)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Math.abs((double)this.temptingPlayer.rotationPitch - this.pitch) > 5.0D || Math.abs((double)this.temptingPlayer.rotationYaw - this.yaw) > 5.0D)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.targetX = this.temptingPlayer.posX;
|
||||
this.targetY = this.temptingPlayer.posY;
|
||||
this.targetZ = this.temptingPlayer.posZ;
|
||||
}
|
||||
|
||||
this.pitch = (double)this.temptingPlayer.rotationPitch;
|
||||
this.yaw = (double)this.temptingPlayer.rotationYaw;
|
||||
}
|
||||
|
||||
return this.shouldExecute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.targetX = this.temptingPlayer.posX;
|
||||
this.targetY = this.temptingPlayer.posY;
|
||||
this.targetZ = this.temptingPlayer.posZ;
|
||||
this.isRunning = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.temptingPlayer = null;
|
||||
this.temptedEntity.getNavigator().clearPath();
|
||||
this.delayTemptCounter = 100;
|
||||
this.isRunning = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
this.temptedEntity.getLookHelper().setLookPositionWithEntity(this.temptingPlayer, (float)(this.temptedEntity.getHorizontalFaceSpeed() + 20), (float)this.temptedEntity.getVerticalFaceSpeed());
|
||||
|
||||
if (this.temptedEntity.getDistanceSq(this.temptingPlayer) < 6.25D)
|
||||
{
|
||||
this.temptedEntity.getNavigator().clearPath();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.temptedEntity.getNavigator().tryMoveToEntityLiving(this.temptingPlayer, this.speed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #isRunning
|
||||
*/
|
||||
public boolean isRunning()
|
||||
{
|
||||
return this.isRunning;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.passive.EntityVillager;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
|
||||
public class EntityAITradePlayer extends EntityAIBase
|
||||
{
|
||||
private final EntityVillager villager;
|
||||
|
||||
public EntityAITradePlayer(EntityVillager villagerIn)
|
||||
{
|
||||
this.villager = villagerIn;
|
||||
this.setMutexBits(5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (!this.villager.isEntityAlive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (this.villager.isInWater())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!this.villager.onGround)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (this.villager.velocityChanged)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
EntityPlayer entityplayer = this.villager.getCustomer();
|
||||
|
||||
if (entityplayer == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (this.villager.getDistanceSq(entityplayer) > 16.0D)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return entityplayer.openContainer != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.villager.getNavigator().clearPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.villager.setCustomer((EntityPlayer)null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.item.EntityItem;
|
||||
import net.minecraft.entity.passive.EntityVillager;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.inventory.InventoryBasic;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
public class EntityAIVillagerInteract extends EntityAIWatchClosest2
|
||||
{
|
||||
/** The delay before the villager throws an itemstack (in ticks) */
|
||||
private int interactionDelay;
|
||||
private final EntityVillager villager;
|
||||
|
||||
public EntityAIVillagerInteract(EntityVillager villagerIn)
|
||||
{
|
||||
super(villagerIn, EntityVillager.class, 3.0F, 0.02F);
|
||||
this.villager = villagerIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
super.startExecuting();
|
||||
|
||||
if (this.villager.canAbondonItems() && this.closestEntity instanceof EntityVillager && ((EntityVillager)this.closestEntity).wantsMoreFood())
|
||||
{
|
||||
this.interactionDelay = 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.interactionDelay = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
super.updateTask();
|
||||
|
||||
if (this.interactionDelay > 0)
|
||||
{
|
||||
--this.interactionDelay;
|
||||
|
||||
if (this.interactionDelay == 0)
|
||||
{
|
||||
InventoryBasic inventorybasic = this.villager.getVillagerInventory();
|
||||
|
||||
for (int i = 0; i < inventorybasic.getSizeInventory(); ++i)
|
||||
{
|
||||
ItemStack itemstack = inventorybasic.getStackInSlot(i);
|
||||
ItemStack itemstack1 = ItemStack.EMPTY;
|
||||
|
||||
if (!itemstack.isEmpty())
|
||||
{
|
||||
Item item = itemstack.getItem();
|
||||
|
||||
if ((item == Items.BREAD || item == Items.POTATO || item == Items.CARROT || item == Items.BEETROOT) && itemstack.getCount() > 3)
|
||||
{
|
||||
int l = itemstack.getCount() / 2;
|
||||
itemstack.shrink(l);
|
||||
itemstack1 = new ItemStack(item, l, itemstack.getMetadata());
|
||||
}
|
||||
else if (item == Items.WHEAT && itemstack.getCount() > 5)
|
||||
{
|
||||
int j = itemstack.getCount() / 2 / 3 * 3;
|
||||
int k = j / 3;
|
||||
itemstack.shrink(j);
|
||||
itemstack1 = new ItemStack(Items.BREAD, k, 0);
|
||||
}
|
||||
|
||||
if (itemstack.isEmpty())
|
||||
{
|
||||
inventorybasic.setInventorySlotContents(i, ItemStack.EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
if (!itemstack1.isEmpty())
|
||||
{
|
||||
double d0 = this.villager.posY - 0.30000001192092896D + (double)this.villager.getEyeHeight();
|
||||
EntityItem entityitem = new EntityItem(this.villager.world, this.villager.posX, d0, this.villager.posZ, itemstack1);
|
||||
float f = 0.3F;
|
||||
float f1 = this.villager.rotationYawHead;
|
||||
float f2 = this.villager.rotationPitch;
|
||||
entityitem.motionX = (double)(-MathHelper.sin(f1 * 0.017453292F) * MathHelper.cos(f2 * 0.017453292F) * 0.3F);
|
||||
entityitem.motionZ = (double)(MathHelper.cos(f1 * 0.017453292F) * MathHelper.cos(f2 * 0.017453292F) * 0.3F);
|
||||
entityitem.motionY = (double)(-MathHelper.sin(f2 * 0.017453292F) * 0.3F + 0.1F);
|
||||
entityitem.setDefaultPickupDelay();
|
||||
this.villager.world.spawnEntity(entityitem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.passive.EntityVillager;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.village.Village;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class EntityAIVillagerMate extends EntityAIBase
|
||||
{
|
||||
private final EntityVillager villager;
|
||||
private EntityVillager mate;
|
||||
private final World world;
|
||||
private int matingTimeout;
|
||||
Village village;
|
||||
|
||||
public EntityAIVillagerMate(EntityVillager villagerIn)
|
||||
{
|
||||
this.villager = villagerIn;
|
||||
this.world = villagerIn.world;
|
||||
this.setMutexBits(3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (this.villager.getGrowingAge() != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (this.villager.getRNG().nextInt(500) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.village = this.world.getVillageCollection().getNearestVillage(new BlockPos(this.villager), 0);
|
||||
|
||||
if (this.village == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (this.checkSufficientDoorsPresentForNewVillager() && this.villager.getIsWillingToMate(true))
|
||||
{
|
||||
Entity entity = this.world.findNearestEntityWithinAABB(EntityVillager.class, this.villager.getEntityBoundingBox().grow(8.0D, 3.0D, 8.0D), this.villager);
|
||||
|
||||
if (entity == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.mate = (EntityVillager)entity;
|
||||
return this.mate.getGrowingAge() == 0 && this.mate.getIsWillingToMate(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.matingTimeout = 300;
|
||||
this.villager.setMating(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.village = null;
|
||||
this.mate = null;
|
||||
this.villager.setMating(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return this.matingTimeout >= 0 && this.checkSufficientDoorsPresentForNewVillager() && this.villager.getGrowingAge() == 0 && this.villager.getIsWillingToMate(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
--this.matingTimeout;
|
||||
this.villager.getLookHelper().setLookPositionWithEntity(this.mate, 10.0F, 30.0F);
|
||||
|
||||
if (this.villager.getDistanceSq(this.mate) > 2.25D)
|
||||
{
|
||||
this.villager.getNavigator().tryMoveToEntityLiving(this.mate, 0.25D);
|
||||
}
|
||||
else if (this.matingTimeout == 0 && this.mate.isMating())
|
||||
{
|
||||
this.giveBirth();
|
||||
}
|
||||
|
||||
if (this.villager.getRNG().nextInt(35) == 0)
|
||||
{
|
||||
this.world.setEntityState(this.villager, (byte)12);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkSufficientDoorsPresentForNewVillager()
|
||||
{
|
||||
if (!this.village.isMatingSeason())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = (int)((double)((float)this.village.getNumVillageDoors()) * 0.35D);
|
||||
return this.village.getNumVillagers() < i;
|
||||
}
|
||||
}
|
||||
|
||||
private void giveBirth()
|
||||
{
|
||||
net.minecraft.entity.EntityAgeable entityvillager = this.villager.createChild(this.mate);
|
||||
this.mate.setGrowingAge(6000);
|
||||
this.villager.setGrowingAge(6000);
|
||||
this.mate.setIsWillingToMate(false);
|
||||
this.villager.setIsWillingToMate(false);
|
||||
|
||||
final net.minecraftforge.event.entity.living.BabyEntitySpawnEvent event = new net.minecraftforge.event.entity.living.BabyEntitySpawnEvent(villager, mate, entityvillager);
|
||||
if (net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(event) || event.getChild() == null) { return; }
|
||||
entityvillager = event.getChild();
|
||||
entityvillager.setGrowingAge(-24000);
|
||||
entityvillager.setLocationAndAngles(this.villager.posX, this.villager.posY, this.villager.posZ, 0.0F, 0.0F);
|
||||
this.world.spawnEntity(entityvillager);
|
||||
this.world.setEntityState(entityvillager, (byte)12);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public class EntityAIWander extends EntityAIBase
|
||||
{
|
||||
protected final EntityCreature entity;
|
||||
protected double x;
|
||||
protected double y;
|
||||
protected double z;
|
||||
protected final double speed;
|
||||
protected int executionChance;
|
||||
protected boolean mustUpdate;
|
||||
|
||||
public EntityAIWander(EntityCreature creatureIn, double speedIn)
|
||||
{
|
||||
this(creatureIn, speedIn, 120);
|
||||
}
|
||||
|
||||
public EntityAIWander(EntityCreature creatureIn, double speedIn, int chance)
|
||||
{
|
||||
this.entity = creatureIn;
|
||||
this.speed = speedIn;
|
||||
this.executionChance = chance;
|
||||
this.setMutexBits(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (!this.mustUpdate)
|
||||
{
|
||||
if (this.entity.getIdleTime() >= 100)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.entity.getRNG().nextInt(this.executionChance) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Vec3d vec3d = this.getPosition();
|
||||
|
||||
if (vec3d == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.x = vec3d.x;
|
||||
this.y = vec3d.y;
|
||||
this.z = vec3d.z;
|
||||
this.mustUpdate = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Vec3d getPosition()
|
||||
{
|
||||
return RandomPositionGenerator.findRandomTarget(this.entity, 10, 7);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
return !this.entity.getNavigator().noPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.entity.getNavigator().tryMoveToXYZ(this.x, this.y, this.z, this.speed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes task to bypass chance
|
||||
*/
|
||||
public void makeUpdate()
|
||||
{
|
||||
this.mustUpdate = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes task random possibility for execution
|
||||
*/
|
||||
public void setExecutionChance(int newchance)
|
||||
{
|
||||
this.executionChance = newchance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public class EntityAIWanderAvoidWater extends EntityAIWander
|
||||
{
|
||||
protected final float probability;
|
||||
|
||||
public EntityAIWanderAvoidWater(EntityCreature p_i47301_1_, double p_i47301_2_)
|
||||
{
|
||||
this(p_i47301_1_, p_i47301_2_, 0.001F);
|
||||
}
|
||||
|
||||
public EntityAIWanderAvoidWater(EntityCreature p_i47302_1_, double p_i47302_2_, float p_i47302_4_)
|
||||
{
|
||||
super(p_i47302_1_, p_i47302_2_);
|
||||
this.probability = p_i47302_4_;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Vec3d getPosition()
|
||||
{
|
||||
if (this.entity.isInWater())
|
||||
{
|
||||
Vec3d vec3d = RandomPositionGenerator.getLandPos(this.entity, 15, 7);
|
||||
return vec3d == null ? super.getPosition() : vec3d;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.entity.getRNG().nextFloat() >= this.probability ? RandomPositionGenerator.getLandPos(this.entity, 10, 7) : super.getPosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import java.util.Iterator;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockLeaves;
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public class EntityAIWanderAvoidWaterFlying extends EntityAIWanderAvoidWater
|
||||
{
|
||||
public EntityAIWanderAvoidWaterFlying(EntityCreature p_i47413_1_, double p_i47413_2_)
|
||||
{
|
||||
super(p_i47413_1_, p_i47413_2_);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Vec3d getPosition()
|
||||
{
|
||||
Vec3d vec3d = null;
|
||||
|
||||
if (this.entity.isInWater() || this.entity.isOverWater())
|
||||
{
|
||||
vec3d = RandomPositionGenerator.getLandPos(this.entity, 15, 15);
|
||||
}
|
||||
|
||||
if (this.entity.getRNG().nextFloat() >= this.probability)
|
||||
{
|
||||
vec3d = this.getTreePos();
|
||||
}
|
||||
|
||||
return vec3d == null ? super.getPosition() : vec3d;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Vec3d getTreePos()
|
||||
{
|
||||
BlockPos blockpos = new BlockPos(this.entity);
|
||||
BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos();
|
||||
BlockPos.MutableBlockPos blockpos$mutableblockpos1 = new BlockPos.MutableBlockPos();
|
||||
Iterable<BlockPos.MutableBlockPos> iterable = BlockPos.MutableBlockPos.getAllInBoxMutable(MathHelper.floor(this.entity.posX - 3.0D), MathHelper.floor(this.entity.posY - 6.0D), MathHelper.floor(this.entity.posZ - 3.0D), MathHelper.floor(this.entity.posX + 3.0D), MathHelper.floor(this.entity.posY + 6.0D), MathHelper.floor(this.entity.posZ + 3.0D));
|
||||
Iterator iterator = iterable.iterator();
|
||||
BlockPos blockpos1;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!iterator.hasNext())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
blockpos1 = (BlockPos)iterator.next();
|
||||
|
||||
if (!blockpos.equals(blockpos1))
|
||||
{
|
||||
Block block = this.entity.world.getBlockState(blockpos$mutableblockpos1.setPos(blockpos1).move(EnumFacing.DOWN)).getBlock();
|
||||
boolean flag = block instanceof BlockLeaves || block == Blocks.LOG || block == Blocks.LOG2;
|
||||
|
||||
if (flag && this.entity.world.isAirBlock(blockpos1) && this.entity.world.isAirBlock(blockpos$mutableblockpos.setPos(blockpos1).move(EnumFacing.UP)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Vec3d((double)blockpos1.getX(), (double)blockpos1.getY(), (double)blockpos1.getZ());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import com.google.common.base.Predicates;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.EntitySelectors;
|
||||
|
||||
public class EntityAIWatchClosest extends EntityAIBase
|
||||
{
|
||||
protected EntityLiving entity;
|
||||
/** The closest entity which is being watched by this one. */
|
||||
protected Entity closestEntity;
|
||||
/** This is the Maximum distance that the AI will look for the Entity */
|
||||
protected float maxDistanceForPlayer;
|
||||
private int lookTime;
|
||||
private final float chance;
|
||||
protected Class <? extends Entity > watchedClass;
|
||||
|
||||
public EntityAIWatchClosest(EntityLiving entityIn, Class <? extends Entity > watchTargetClass, float maxDistance)
|
||||
{
|
||||
this.entity = entityIn;
|
||||
this.watchedClass = watchTargetClass;
|
||||
this.maxDistanceForPlayer = maxDistance;
|
||||
this.chance = 0.02F;
|
||||
this.setMutexBits(2);
|
||||
}
|
||||
|
||||
public EntityAIWatchClosest(EntityLiving entityIn, Class <? extends Entity > watchTargetClass, float maxDistance, float chanceIn)
|
||||
{
|
||||
this.entity = entityIn;
|
||||
this.watchedClass = watchTargetClass;
|
||||
this.maxDistanceForPlayer = maxDistance;
|
||||
this.chance = chanceIn;
|
||||
this.setMutexBits(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (this.entity.getRNG().nextFloat() >= this.chance)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.entity.getAttackTarget() != null)
|
||||
{
|
||||
this.closestEntity = this.entity.getAttackTarget();
|
||||
}
|
||||
|
||||
if (this.watchedClass == EntityPlayer.class)
|
||||
{
|
||||
this.closestEntity = this.entity.world.getClosestPlayer(this.entity.posX, this.entity.posY, this.entity.posZ, (double)this.maxDistanceForPlayer, Predicates.and(EntitySelectors.NOT_SPECTATING, EntitySelectors.notRiding(this.entity)));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.closestEntity = this.entity.world.findNearestEntityWithinAABB(this.watchedClass, this.entity.getEntityBoundingBox().grow((double)this.maxDistanceForPlayer, 3.0D, (double)this.maxDistanceForPlayer), this.entity);
|
||||
}
|
||||
|
||||
return this.closestEntity != null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean shouldContinueExecuting()
|
||||
{
|
||||
if (!this.closestEntity.isEntityAlive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (this.entity.getDistanceSq(this.closestEntity) > (double)(this.maxDistanceForPlayer * this.maxDistanceForPlayer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.lookTime > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.lookTime = 40 + this.entity.getRNG().nextInt(40);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.closestEntity = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
this.entity.getLookHelper().setLookPosition(this.closestEntity.posX, this.closestEntity.posY + (double)this.closestEntity.getEyeHeight(), this.closestEntity.posZ, (float)this.entity.getHorizontalFaceSpeed(), (float)this.entity.getVerticalFaceSpeed());
|
||||
--this.lookTime;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
|
||||
public class EntityAIWatchClosest2 extends EntityAIWatchClosest
|
||||
{
|
||||
public EntityAIWatchClosest2(EntityLiving entitylivingIn, Class <? extends Entity > watchTargetClass, float maxDistance, float chanceIn)
|
||||
{
|
||||
super(entitylivingIn, watchTargetClass, maxDistance, chanceIn);
|
||||
this.setMutexBits(3);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.monster.EntityZombie;
|
||||
|
||||
public class EntityAIZombieAttack extends EntityAIAttackMelee
|
||||
{
|
||||
private final EntityZombie zombie;
|
||||
private int raiseArmTicks;
|
||||
|
||||
public EntityAIZombieAttack(EntityZombie zombieIn, double speedIn, boolean longMemoryIn)
|
||||
{
|
||||
super(zombieIn, speedIn, longMemoryIn);
|
||||
this.zombie = zombieIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
super.startExecuting();
|
||||
this.raiseArmTicks = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task's internal state. Called when this task is interrupted by another one
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
super.resetTask();
|
||||
this.zombie.setArmsRaised(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep ticking a continuous task that has already been started
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
super.updateTask();
|
||||
++this.raiseArmTicks;
|
||||
|
||||
if (this.raiseArmTicks >= 5 && this.attackTick < 10)
|
||||
{
|
||||
this.zombie.setArmsRaised(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.zombie.setArmsRaised(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
public class EntityFlyHelper extends EntityMoveHelper
|
||||
{
|
||||
public EntityFlyHelper(EntityLiving p_i47418_1_)
|
||||
{
|
||||
super(p_i47418_1_);
|
||||
}
|
||||
|
||||
public void onUpdateMoveHelper()
|
||||
{
|
||||
if (this.action == EntityMoveHelper.Action.MOVE_TO)
|
||||
{
|
||||
this.action = EntityMoveHelper.Action.WAIT;
|
||||
this.entity.setNoGravity(true);
|
||||
double d0 = this.posX - this.entity.posX;
|
||||
double d1 = this.posY - this.entity.posY;
|
||||
double d2 = this.posZ - this.entity.posZ;
|
||||
double d3 = d0 * d0 + d1 * d1 + d2 * d2;
|
||||
|
||||
if (d3 < 2.500000277905201E-7D)
|
||||
{
|
||||
this.entity.setMoveVertical(0.0F);
|
||||
this.entity.setMoveForward(0.0F);
|
||||
return;
|
||||
}
|
||||
|
||||
float f = (float)(MathHelper.atan2(d2, d0) * (180D / Math.PI)) - 90.0F;
|
||||
this.entity.rotationYaw = this.limitAngle(this.entity.rotationYaw, f, 10.0F);
|
||||
float f1;
|
||||
|
||||
if (this.entity.onGround)
|
||||
{
|
||||
f1 = (float)(this.speed * this.entity.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).getAttributeValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
f1 = (float)(this.speed * this.entity.getEntityAttribute(SharedMonsterAttributes.FLYING_SPEED).getAttributeValue());
|
||||
}
|
||||
|
||||
this.entity.setAIMoveSpeed(f1);
|
||||
double d4 = (double)MathHelper.sqrt(d0 * d0 + d2 * d2);
|
||||
float f2 = (float)(-(MathHelper.atan2(d1, d4) * (180D / Math.PI)));
|
||||
this.entity.rotationPitch = this.limitAngle(this.entity.rotationPitch, f2, 10.0F);
|
||||
this.entity.setMoveVertical(d1 > 0.0D ? f1 : -f1);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.entity.setNoGravity(false);
|
||||
this.entity.setMoveVertical(0.0F);
|
||||
this.entity.setMoveForward(0.0F);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
|
||||
public class EntityJumpHelper
|
||||
{
|
||||
private final EntityLiving entity;
|
||||
protected boolean isJumping;
|
||||
|
||||
public EntityJumpHelper(EntityLiving entityIn)
|
||||
{
|
||||
this.entity = entityIn;
|
||||
}
|
||||
|
||||
public void setJumping()
|
||||
{
|
||||
this.isJumping = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to actually make the entity jump if isJumping is true.
|
||||
*/
|
||||
public void doJump()
|
||||
{
|
||||
this.entity.setJumping(this.isJumping);
|
||||
this.isJumping = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
public class EntityLookHelper
|
||||
{
|
||||
private final EntityLiving entity;
|
||||
/** The amount of change that is made each update for an entity facing a direction. */
|
||||
private float deltaLookYaw;
|
||||
/** The amount of change that is made each update for an entity facing a direction. */
|
||||
private float deltaLookPitch;
|
||||
/** Whether or not the entity is trying to look at something. */
|
||||
private boolean isLooking;
|
||||
private double posX;
|
||||
private double posY;
|
||||
private double posZ;
|
||||
|
||||
public EntityLookHelper(EntityLiving entitylivingIn)
|
||||
{
|
||||
this.entity = entitylivingIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets position to look at using entity
|
||||
*/
|
||||
public void setLookPositionWithEntity(Entity entityIn, float deltaYaw, float deltaPitch)
|
||||
{
|
||||
this.posX = entityIn.posX;
|
||||
|
||||
if (entityIn instanceof EntityLivingBase)
|
||||
{
|
||||
this.posY = entityIn.posY + (double)entityIn.getEyeHeight();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.posY = (entityIn.getEntityBoundingBox().minY + entityIn.getEntityBoundingBox().maxY) / 2.0D;
|
||||
}
|
||||
|
||||
this.posZ = entityIn.posZ;
|
||||
this.deltaLookYaw = deltaYaw;
|
||||
this.deltaLookPitch = deltaPitch;
|
||||
this.isLooking = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets position to look at
|
||||
*/
|
||||
public void setLookPosition(double x, double y, double z, float deltaYaw, float deltaPitch)
|
||||
{
|
||||
this.posX = x;
|
||||
this.posY = y;
|
||||
this.posZ = z;
|
||||
this.deltaLookYaw = deltaYaw;
|
||||
this.deltaLookPitch = deltaPitch;
|
||||
this.isLooking = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates look
|
||||
*/
|
||||
public void onUpdateLook()
|
||||
{
|
||||
this.entity.rotationPitch = 0.0F;
|
||||
|
||||
if (this.isLooking)
|
||||
{
|
||||
this.isLooking = false;
|
||||
double d0 = this.posX - this.entity.posX;
|
||||
double d1 = this.posY - (this.entity.posY + (double)this.entity.getEyeHeight());
|
||||
double d2 = this.posZ - this.entity.posZ;
|
||||
double d3 = (double)MathHelper.sqrt(d0 * d0 + d2 * d2);
|
||||
float f = (float)(MathHelper.atan2(d2, d0) * (180D / Math.PI)) - 90.0F;
|
||||
float f1 = (float)(-(MathHelper.atan2(d1, d3) * (180D / Math.PI)));
|
||||
this.entity.rotationPitch = this.updateRotation(this.entity.rotationPitch, f1, this.deltaLookPitch);
|
||||
this.entity.rotationYawHead = this.updateRotation(this.entity.rotationYawHead, f, this.deltaLookYaw);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.entity.rotationYawHead = this.updateRotation(this.entity.rotationYawHead, this.entity.renderYawOffset, 10.0F);
|
||||
}
|
||||
|
||||
float f2 = MathHelper.wrapDegrees(this.entity.rotationYawHead - this.entity.renderYawOffset);
|
||||
|
||||
if (!this.entity.getNavigator().noPath())
|
||||
{
|
||||
if (f2 < -75.0F)
|
||||
{
|
||||
this.entity.rotationYawHead = this.entity.renderYawOffset - 75.0F;
|
||||
}
|
||||
|
||||
if (f2 > 75.0F)
|
||||
{
|
||||
this.entity.rotationYawHead = this.entity.renderYawOffset + 75.0F;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private float updateRotation(float p_75652_1_, float p_75652_2_, float p_75652_3_)
|
||||
{
|
||||
float f = MathHelper.wrapDegrees(p_75652_2_ - p_75652_1_);
|
||||
|
||||
if (f > p_75652_3_)
|
||||
{
|
||||
f = p_75652_3_;
|
||||
}
|
||||
|
||||
if (f < -p_75652_3_)
|
||||
{
|
||||
f = -p_75652_3_;
|
||||
}
|
||||
|
||||
return p_75652_1_ + f;
|
||||
}
|
||||
|
||||
public boolean getIsLooking()
|
||||
{
|
||||
return this.isLooking;
|
||||
}
|
||||
|
||||
public double getLookPosX()
|
||||
{
|
||||
return this.posX;
|
||||
}
|
||||
|
||||
public double getLookPosY()
|
||||
{
|
||||
return this.posY;
|
||||
}
|
||||
|
||||
public double getLookPosZ()
|
||||
{
|
||||
return this.posZ;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.pathfinding.NodeProcessor;
|
||||
import net.minecraft.pathfinding.PathNavigate;
|
||||
import net.minecraft.pathfinding.PathNodeType;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
public class EntityMoveHelper
|
||||
{
|
||||
/** The EntityLiving that is being moved */
|
||||
protected final EntityLiving entity;
|
||||
protected double posX;
|
||||
protected double posY;
|
||||
protected double posZ;
|
||||
/** Multiplier for the entity's speed attribute value */
|
||||
protected double speed;
|
||||
protected float moveForward;
|
||||
protected float moveStrafe;
|
||||
public EntityMoveHelper.Action action = EntityMoveHelper.Action.WAIT;
|
||||
|
||||
public EntityMoveHelper(EntityLiving entitylivingIn)
|
||||
{
|
||||
this.entity = entitylivingIn;
|
||||
}
|
||||
|
||||
public boolean isUpdating()
|
||||
{
|
||||
return this.action == EntityMoveHelper.Action.MOVE_TO;
|
||||
}
|
||||
|
||||
public double getSpeed()
|
||||
{
|
||||
return this.speed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the speed and location to move to
|
||||
*/
|
||||
public void setMoveTo(double x, double y, double z, double speedIn)
|
||||
{
|
||||
this.posX = x;
|
||||
this.posY = y;
|
||||
this.posZ = z;
|
||||
this.speed = speedIn;
|
||||
this.action = EntityMoveHelper.Action.MOVE_TO;
|
||||
}
|
||||
|
||||
public void strafe(float forward, float strafe)
|
||||
{
|
||||
this.action = EntityMoveHelper.Action.STRAFE;
|
||||
this.moveForward = forward;
|
||||
this.moveStrafe = strafe;
|
||||
this.speed = 0.25D;
|
||||
}
|
||||
|
||||
public void read(EntityMoveHelper that)
|
||||
{
|
||||
this.action = that.action;
|
||||
this.posX = that.posX;
|
||||
this.posY = that.posY;
|
||||
this.posZ = that.posZ;
|
||||
this.speed = Math.max(that.speed, 1.0D);
|
||||
this.moveForward = that.moveForward;
|
||||
this.moveStrafe = that.moveStrafe;
|
||||
}
|
||||
|
||||
public void onUpdateMoveHelper()
|
||||
{
|
||||
if (this.action == EntityMoveHelper.Action.STRAFE)
|
||||
{
|
||||
float f = (float)this.entity.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).getAttributeValue();
|
||||
float f1 = (float)this.speed * f;
|
||||
float f2 = this.moveForward;
|
||||
float f3 = this.moveStrafe;
|
||||
float f4 = MathHelper.sqrt(f2 * f2 + f3 * f3);
|
||||
|
||||
if (f4 < 1.0F)
|
||||
{
|
||||
f4 = 1.0F;
|
||||
}
|
||||
|
||||
f4 = f1 / f4;
|
||||
f2 = f2 * f4;
|
||||
f3 = f3 * f4;
|
||||
float f5 = MathHelper.sin(this.entity.rotationYaw * 0.017453292F);
|
||||
float f6 = MathHelper.cos(this.entity.rotationYaw * 0.017453292F);
|
||||
float f7 = f2 * f6 - f3 * f5;
|
||||
float f8 = f3 * f6 + f2 * f5;
|
||||
PathNavigate pathnavigate = this.entity.getNavigator();
|
||||
|
||||
if (pathnavigate != null)
|
||||
{
|
||||
NodeProcessor nodeprocessor = pathnavigate.getNodeProcessor();
|
||||
|
||||
if (nodeprocessor != null && nodeprocessor.getPathNodeType(this.entity.world, MathHelper.floor(this.entity.posX + (double)f7), MathHelper.floor(this.entity.posY), MathHelper.floor(this.entity.posZ + (double)f8)) != PathNodeType.WALKABLE)
|
||||
{
|
||||
this.moveForward = 1.0F;
|
||||
this.moveStrafe = 0.0F;
|
||||
f1 = f;
|
||||
}
|
||||
}
|
||||
|
||||
this.entity.setAIMoveSpeed(f1);
|
||||
this.entity.setMoveForward(this.moveForward);
|
||||
this.entity.setMoveStrafing(this.moveStrafe);
|
||||
this.action = EntityMoveHelper.Action.WAIT;
|
||||
}
|
||||
else if (this.action == EntityMoveHelper.Action.MOVE_TO)
|
||||
{
|
||||
this.action = EntityMoveHelper.Action.WAIT;
|
||||
double d0 = this.posX - this.entity.posX;
|
||||
double d1 = this.posZ - this.entity.posZ;
|
||||
double d2 = this.posY - this.entity.posY;
|
||||
double d3 = d0 * d0 + d2 * d2 + d1 * d1;
|
||||
|
||||
if (d3 < 2.500000277905201E-7D)
|
||||
{
|
||||
this.entity.setMoveForward(0.0F);
|
||||
return;
|
||||
}
|
||||
|
||||
float f9 = (float)(MathHelper.atan2(d1, d0) * (180D / Math.PI)) - 90.0F;
|
||||
this.entity.rotationYaw = this.limitAngle(this.entity.rotationYaw, f9, 90.0F);
|
||||
this.entity.setAIMoveSpeed((float)(this.speed * this.entity.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).getAttributeValue()));
|
||||
|
||||
if (d2 > (double)this.entity.stepHeight && d0 * d0 + d1 * d1 < (double)Math.max(1.0F, this.entity.width))
|
||||
{
|
||||
this.entity.getJumpHelper().setJumping();
|
||||
this.action = EntityMoveHelper.Action.JUMPING;
|
||||
}
|
||||
}
|
||||
else if (this.action == EntityMoveHelper.Action.JUMPING)
|
||||
{
|
||||
this.entity.setAIMoveSpeed((float)(this.speed * this.entity.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).getAttributeValue()));
|
||||
|
||||
if (this.entity.onGround)
|
||||
{
|
||||
this.action = EntityMoveHelper.Action.WAIT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.entity.setMoveForward(0.0F);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to rotate the first angle to become the second angle, but only allow overall direction change to at max
|
||||
* be third parameter
|
||||
*/
|
||||
protected float limitAngle(float sourceAngle, float targetAngle, float maximumChange)
|
||||
{
|
||||
float f = MathHelper.wrapDegrees(targetAngle - sourceAngle);
|
||||
|
||||
if (f > maximumChange)
|
||||
{
|
||||
f = maximumChange;
|
||||
}
|
||||
|
||||
if (f < -maximumChange)
|
||||
{
|
||||
f = -maximumChange;
|
||||
}
|
||||
|
||||
float f1 = sourceAngle + f;
|
||||
|
||||
if (f1 < 0.0F)
|
||||
{
|
||||
f1 += 360.0F;
|
||||
}
|
||||
else if (f1 > 360.0F)
|
||||
{
|
||||
f1 -= 360.0F;
|
||||
}
|
||||
|
||||
return f1;
|
||||
}
|
||||
|
||||
public double getX()
|
||||
{
|
||||
return this.posX;
|
||||
}
|
||||
|
||||
public double getY()
|
||||
{
|
||||
return this.posY;
|
||||
}
|
||||
|
||||
public double getZ()
|
||||
{
|
||||
return this.posZ;
|
||||
}
|
||||
|
||||
public static enum Action
|
||||
{
|
||||
WAIT,
|
||||
MOVE_TO,
|
||||
STRAFE,
|
||||
JUMPING;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import java.util.List;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
|
||||
public class EntitySenses
|
||||
{
|
||||
EntityLiving entity;
|
||||
/** Cache of entities which we can see */
|
||||
List<Entity> seenEntities = Lists.<Entity>newArrayList();
|
||||
/** Cache of entities which we cannot see */
|
||||
List<Entity> unseenEntities = Lists.<Entity>newArrayList();
|
||||
|
||||
public EntitySenses(EntityLiving entityIn)
|
||||
{
|
||||
this.entity = entityIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears canSeeCachePositive and canSeeCacheNegative.
|
||||
*/
|
||||
public void clearSensingCache()
|
||||
{
|
||||
this.seenEntities.clear();
|
||||
this.unseenEntities.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks, whether 'our' entity can see the entity given as argument (true) or not (false), caching the result.
|
||||
*/
|
||||
public boolean canSee(Entity entityIn)
|
||||
{
|
||||
if (this.seenEntities.contains(entityIn))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (this.unseenEntities.contains(entityIn))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.entity.world.profiler.startSection("canSee");
|
||||
boolean flag = this.entity.canEntityBeSeen(entityIn);
|
||||
this.entity.world.profiler.endSection();
|
||||
|
||||
if (flag)
|
||||
{
|
||||
this.seenEntities.add(entityIn);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.unseenEntities.add(entityIn);
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import java.util.Random;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.pathfinding.PathNavigate;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public class RandomPositionGenerator
|
||||
{
|
||||
/**
|
||||
* used to store a driection when the user passes a point to move towards or away from. WARNING: NEVER THREAD SAFE.
|
||||
* MULTIPLE findTowards and findAway calls, will share this var
|
||||
*/
|
||||
private static Vec3d staticVector = Vec3d.ZERO;
|
||||
|
||||
/**
|
||||
* finds a random target within par1(x,z) and par2 (y) blocks
|
||||
*/
|
||||
@Nullable
|
||||
public static Vec3d findRandomTarget(EntityCreature entitycreatureIn, int xz, int y)
|
||||
{
|
||||
return findRandomTargetBlock(entitycreatureIn, xz, y, (Vec3d)null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Vec3d getLandPos(EntityCreature p_191377_0_, int p_191377_1_, int p_191377_2_)
|
||||
{
|
||||
return generateRandomPos(p_191377_0_, p_191377_1_, p_191377_2_, (Vec3d)null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* finds a random target within par1(x,z) and par2 (y) blocks in the direction of the point par3
|
||||
*/
|
||||
@Nullable
|
||||
public static Vec3d findRandomTargetBlockTowards(EntityCreature entitycreatureIn, int xz, int y, Vec3d targetVec3)
|
||||
{
|
||||
staticVector = targetVec3.subtract(entitycreatureIn.posX, entitycreatureIn.posY, entitycreatureIn.posZ);
|
||||
return findRandomTargetBlock(entitycreatureIn, xz, y, staticVector);
|
||||
}
|
||||
|
||||
/**
|
||||
* finds a random target within par1(x,z) and par2 (y) blocks in the reverse direction of the point par3
|
||||
*/
|
||||
@Nullable
|
||||
public static Vec3d findRandomTargetBlockAwayFrom(EntityCreature entitycreatureIn, int xz, int y, Vec3d targetVec3)
|
||||
{
|
||||
staticVector = (new Vec3d(entitycreatureIn.posX, entitycreatureIn.posY, entitycreatureIn.posZ)).subtract(targetVec3);
|
||||
return findRandomTargetBlock(entitycreatureIn, xz, y, staticVector);
|
||||
}
|
||||
|
||||
/**
|
||||
* searches 10 blocks at random in a within par1(x,z) and par2 (y) distance, ignores those not in the direction of
|
||||
* par3Vec3, then points to the tile for which creature.getBlockPathWeight returns the highest number
|
||||
*/
|
||||
@Nullable
|
||||
private static Vec3d findRandomTargetBlock(EntityCreature entitycreatureIn, int xz, int y, @Nullable Vec3d targetVec3)
|
||||
{
|
||||
return generateRandomPos(entitycreatureIn, xz, y, targetVec3, true);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Vec3d generateRandomPos(EntityCreature p_191379_0_, int p_191379_1_, int p_191379_2_, @Nullable Vec3d p_191379_3_, boolean p_191379_4_)
|
||||
{
|
||||
PathNavigate pathnavigate = p_191379_0_.getNavigator();
|
||||
Random random = p_191379_0_.getRNG();
|
||||
boolean flag;
|
||||
|
||||
if (p_191379_0_.hasHome())
|
||||
{
|
||||
double d0 = p_191379_0_.getHomePosition().distanceSq((double)MathHelper.floor(p_191379_0_.posX), (double)MathHelper.floor(p_191379_0_.posY), (double)MathHelper.floor(p_191379_0_.posZ)) + 4.0D;
|
||||
double d1 = (double)(p_191379_0_.getMaximumHomeDistance() + (float)p_191379_1_);
|
||||
flag = d0 < d1 * d1;
|
||||
}
|
||||
else
|
||||
{
|
||||
flag = false;
|
||||
}
|
||||
|
||||
boolean flag1 = false;
|
||||
float f = -99999.0F;
|
||||
int k1 = 0;
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
for (int k = 0; k < 10; ++k)
|
||||
{
|
||||
int l = random.nextInt(2 * p_191379_1_ + 1) - p_191379_1_;
|
||||
int i1 = random.nextInt(2 * p_191379_2_ + 1) - p_191379_2_;
|
||||
int j1 = random.nextInt(2 * p_191379_1_ + 1) - p_191379_1_;
|
||||
|
||||
if (p_191379_3_ == null || (double)l * p_191379_3_.x + (double)j1 * p_191379_3_.z >= 0.0D)
|
||||
{
|
||||
if (p_191379_0_.hasHome() && p_191379_1_ > 1)
|
||||
{
|
||||
BlockPos blockpos = p_191379_0_.getHomePosition();
|
||||
|
||||
if (p_191379_0_.posX > (double)blockpos.getX())
|
||||
{
|
||||
l -= random.nextInt(p_191379_1_ / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
l += random.nextInt(p_191379_1_ / 2);
|
||||
}
|
||||
|
||||
if (p_191379_0_.posZ > (double)blockpos.getZ())
|
||||
{
|
||||
j1 -= random.nextInt(p_191379_1_ / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
j1 += random.nextInt(p_191379_1_ / 2);
|
||||
}
|
||||
}
|
||||
|
||||
BlockPos blockpos1 = new BlockPos((double)l + p_191379_0_.posX, (double)i1 + p_191379_0_.posY, (double)j1 + p_191379_0_.posZ);
|
||||
|
||||
if ((!flag || p_191379_0_.isWithinHomeDistanceFromPosition(blockpos1)) && pathnavigate.canEntityStandOnPos(blockpos1))
|
||||
{
|
||||
if (!p_191379_4_)
|
||||
{
|
||||
blockpos1 = moveAboveSolid(blockpos1, p_191379_0_);
|
||||
|
||||
if (isWaterDestination(blockpos1, p_191379_0_))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
float f1 = p_191379_0_.getBlockPathWeight(blockpos1);
|
||||
|
||||
if (f1 > f)
|
||||
{
|
||||
f = f1;
|
||||
k1 = l;
|
||||
i = i1;
|
||||
j = j1;
|
||||
flag1 = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flag1)
|
||||
{
|
||||
return new Vec3d((double)k1 + p_191379_0_.posX, (double)i + p_191379_0_.posY, (double)j + p_191379_0_.posZ);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static BlockPos moveAboveSolid(BlockPos p_191378_0_, EntityCreature p_191378_1_)
|
||||
{
|
||||
if (!p_191378_1_.world.getBlockState(p_191378_0_).getMaterial().isSolid())
|
||||
{
|
||||
return p_191378_0_;
|
||||
}
|
||||
else
|
||||
{
|
||||
BlockPos blockpos;
|
||||
|
||||
for (blockpos = p_191378_0_.up(); blockpos.getY() < p_191378_1_.world.getHeight() && p_191378_1_.world.getBlockState(blockpos).getMaterial().isSolid(); blockpos = blockpos.up())
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
return blockpos;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isWaterDestination(BlockPos p_191380_0_, EntityCreature p_191380_1_)
|
||||
{
|
||||
return p_191380_1_.world.getBlockState(p_191380_0_).getMaterial() == Material.WATER;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package net.minecraft.entity.ai.attributes;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Multimap;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.util.LowerStringMap;
|
||||
|
||||
public abstract class AbstractAttributeMap
|
||||
{
|
||||
protected final Map<IAttribute, IAttributeInstance> attributes = Maps.<IAttribute, IAttributeInstance>newHashMap();
|
||||
protected final Map<String, IAttributeInstance> attributesByName = new LowerStringMap();
|
||||
protected final Multimap<IAttribute, IAttribute> descendantsByParent = HashMultimap.<IAttribute, IAttribute>create();
|
||||
|
||||
public IAttributeInstance getAttributeInstance(IAttribute attribute)
|
||||
{
|
||||
return this.attributes.get(attribute);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IAttributeInstance getAttributeInstanceByName(String attributeName)
|
||||
{
|
||||
return this.attributesByName.get(attributeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an attribute with this AttributeMap, returns a modifiable AttributeInstance associated with this map
|
||||
*/
|
||||
public IAttributeInstance registerAttribute(IAttribute attribute)
|
||||
{
|
||||
if (this.attributesByName.containsKey(attribute.getName()))
|
||||
{
|
||||
throw new IllegalArgumentException("Attribute is already registered!");
|
||||
}
|
||||
else
|
||||
{
|
||||
IAttributeInstance iattributeinstance = this.createInstance(attribute);
|
||||
this.attributesByName.put(attribute.getName(), iattributeinstance);
|
||||
this.attributes.put(attribute, iattributeinstance);
|
||||
|
||||
for (IAttribute iattribute = attribute.getParent(); iattribute != null; iattribute = iattribute.getParent())
|
||||
{
|
||||
this.descendantsByParent.put(iattribute, attribute);
|
||||
}
|
||||
|
||||
return iattributeinstance;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract IAttributeInstance createInstance(IAttribute attribute);
|
||||
|
||||
public Collection<IAttributeInstance> getAllAttributes()
|
||||
{
|
||||
return this.attributesByName.values();
|
||||
}
|
||||
|
||||
public void onAttributeModified(IAttributeInstance instance)
|
||||
{
|
||||
}
|
||||
|
||||
public void removeAttributeModifiers(Multimap<String, AttributeModifier> modifiers)
|
||||
{
|
||||
for (Entry<String, AttributeModifier> entry : modifiers.entries())
|
||||
{
|
||||
IAttributeInstance iattributeinstance = this.getAttributeInstanceByName(entry.getKey());
|
||||
|
||||
if (iattributeinstance != null)
|
||||
{
|
||||
iattributeinstance.removeModifier(entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void applyAttributeModifiers(Multimap<String, AttributeModifier> modifiers)
|
||||
{
|
||||
for (Entry<String, AttributeModifier> entry : modifiers.entries())
|
||||
{
|
||||
IAttributeInstance iattributeinstance = this.getAttributeInstanceByName(entry.getKey());
|
||||
|
||||
if (iattributeinstance != null)
|
||||
{
|
||||
iattributeinstance.removeModifier(entry.getValue());
|
||||
iattributeinstance.applyModifier(entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package net.minecraft.entity.ai.attributes;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import net.minecraft.util.LowerStringMap;
|
||||
|
||||
public class AttributeMap extends AbstractAttributeMap
|
||||
{
|
||||
private final Set<IAttributeInstance> dirtyInstances = Sets.<IAttributeInstance>newHashSet();
|
||||
protected final Map<String, IAttributeInstance> instancesByName = new LowerStringMap();
|
||||
|
||||
public ModifiableAttributeInstance getAttributeInstance(IAttribute attribute)
|
||||
{
|
||||
return (ModifiableAttributeInstance)super.getAttributeInstance(attribute);
|
||||
}
|
||||
|
||||
public ModifiableAttributeInstance getAttributeInstanceByName(String attributeName)
|
||||
{
|
||||
IAttributeInstance iattributeinstance = super.getAttributeInstanceByName(attributeName);
|
||||
|
||||
if (iattributeinstance == null)
|
||||
{
|
||||
iattributeinstance = this.instancesByName.get(attributeName);
|
||||
}
|
||||
|
||||
return (ModifiableAttributeInstance)iattributeinstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an attribute with this AttributeMap, returns a modifiable AttributeInstance associated with this map
|
||||
*/
|
||||
public IAttributeInstance registerAttribute(IAttribute attribute)
|
||||
{
|
||||
IAttributeInstance iattributeinstance = super.registerAttribute(attribute);
|
||||
|
||||
if (attribute instanceof RangedAttribute && ((RangedAttribute)attribute).getDescription() != null)
|
||||
{
|
||||
this.instancesByName.put(((RangedAttribute)attribute).getDescription(), iattributeinstance);
|
||||
}
|
||||
|
||||
return iattributeinstance;
|
||||
}
|
||||
|
||||
protected IAttributeInstance createInstance(IAttribute attribute)
|
||||
{
|
||||
return new ModifiableAttributeInstance(this, attribute);
|
||||
}
|
||||
|
||||
public void onAttributeModified(IAttributeInstance instance)
|
||||
{
|
||||
if (instance.getAttribute().getShouldWatch())
|
||||
{
|
||||
this.dirtyInstances.add(instance);
|
||||
}
|
||||
|
||||
for (IAttribute iattribute : this.descendantsByParent.get(instance.getAttribute()))
|
||||
{
|
||||
ModifiableAttributeInstance modifiableattributeinstance = this.getAttributeInstance(iattribute);
|
||||
|
||||
if (modifiableattributeinstance != null)
|
||||
{
|
||||
modifiableattributeinstance.flagForUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Set<IAttributeInstance> getDirtyInstances()
|
||||
{
|
||||
return this.dirtyInstances;
|
||||
}
|
||||
|
||||
public Collection<IAttributeInstance> getWatchedAttributes()
|
||||
{
|
||||
Set<IAttributeInstance> set = Sets.<IAttributeInstance>newHashSet();
|
||||
|
||||
for (IAttributeInstance iattributeinstance : this.getAllAttributes())
|
||||
{
|
||||
if (iattributeinstance.getAttribute().getShouldWatch())
|
||||
{
|
||||
set.add(iattributeinstance);
|
||||
}
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package net.minecraft.entity.ai.attributes;
|
||||
|
||||
import io.netty.util.internal.ThreadLocalRandom;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
public class AttributeModifier
|
||||
{
|
||||
private final double amount;
|
||||
private final int operation;
|
||||
private final String name;
|
||||
private final UUID id;
|
||||
/** If false, this modifier is not saved in NBT. Used for "natural" modifiers like speed boost from sprinting */
|
||||
private boolean isSaved;
|
||||
|
||||
public AttributeModifier(String nameIn, double amountIn, int operationIn)
|
||||
{
|
||||
this(MathHelper.getRandomUUID(ThreadLocalRandom.current()), nameIn, amountIn, operationIn);
|
||||
}
|
||||
|
||||
public AttributeModifier(UUID idIn, String nameIn, double amountIn, int operationIn)
|
||||
{
|
||||
this.isSaved = true;
|
||||
this.id = idIn;
|
||||
this.name = nameIn;
|
||||
this.amount = amountIn;
|
||||
this.operation = operationIn;
|
||||
Validate.notEmpty(nameIn, "Modifier name cannot be empty");
|
||||
Validate.inclusiveBetween(0L, 2L, (long)operationIn, "Invalid operation");
|
||||
}
|
||||
|
||||
public UUID getID()
|
||||
{
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public int getOperation()
|
||||
{
|
||||
return this.operation;
|
||||
}
|
||||
|
||||
public double getAmount()
|
||||
{
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #isSaved
|
||||
*/
|
||||
public boolean isSaved()
|
||||
{
|
||||
return this.isSaved;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #isSaved
|
||||
*/
|
||||
public AttributeModifier setSaved(boolean saved)
|
||||
{
|
||||
this.isSaved = saved;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean equals(Object p_equals_1_)
|
||||
{
|
||||
if (this == p_equals_1_)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (p_equals_1_ != null && this.getClass() == p_equals_1_.getClass())
|
||||
{
|
||||
AttributeModifier attributemodifier = (AttributeModifier)p_equals_1_;
|
||||
|
||||
if (this.id != null)
|
||||
{
|
||||
if (!this.id.equals(attributemodifier.id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (attributemodifier.id != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode()
|
||||
{
|
||||
return this.id != null ? this.id.hashCode() : 0;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return "AttributeModifier{amount=" + this.amount + ", operation=" + this.operation + ", name='" + this.name + '\'' + ", id=" + this.id + ", serialize=" + this.isSaved + '}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package net.minecraft.entity.ai.attributes;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public abstract class BaseAttribute implements IAttribute
|
||||
{
|
||||
private final IAttribute parent;
|
||||
private final String unlocalizedName;
|
||||
private final double defaultValue;
|
||||
private boolean shouldWatch;
|
||||
|
||||
protected BaseAttribute(@Nullable IAttribute parentIn, String unlocalizedNameIn, double defaultValueIn)
|
||||
{
|
||||
this.parent = parentIn;
|
||||
this.unlocalizedName = unlocalizedNameIn;
|
||||
this.defaultValue = defaultValueIn;
|
||||
|
||||
if (unlocalizedNameIn == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Name cannot be null!");
|
||||
}
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return this.unlocalizedName;
|
||||
}
|
||||
|
||||
public double getDefaultValue()
|
||||
{
|
||||
return this.defaultValue;
|
||||
}
|
||||
|
||||
public boolean getShouldWatch()
|
||||
{
|
||||
return this.shouldWatch;
|
||||
}
|
||||
|
||||
public BaseAttribute setShouldWatch(boolean shouldWatchIn)
|
||||
{
|
||||
this.shouldWatch = shouldWatchIn;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IAttribute getParent()
|
||||
{
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
public int hashCode()
|
||||
{
|
||||
return this.unlocalizedName.hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object p_equals_1_)
|
||||
{
|
||||
return p_equals_1_ instanceof IAttribute && this.unlocalizedName.equals(((IAttribute)p_equals_1_).getName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package net.minecraft.entity.ai.attributes;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface IAttribute
|
||||
{
|
||||
String getName();
|
||||
|
||||
double clampValue(double value);
|
||||
|
||||
double getDefaultValue();
|
||||
|
||||
boolean getShouldWatch();
|
||||
|
||||
@Nullable
|
||||
IAttribute getParent();
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package net.minecraft.entity.ai.attributes;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public interface IAttributeInstance
|
||||
{
|
||||
/**
|
||||
* Get the Attribute this is an instance of
|
||||
*/
|
||||
IAttribute getAttribute();
|
||||
|
||||
double getBaseValue();
|
||||
|
||||
void setBaseValue(double baseValue);
|
||||
|
||||
Collection<AttributeModifier> getModifiersByOperation(int operation);
|
||||
|
||||
Collection<AttributeModifier> getModifiers();
|
||||
|
||||
boolean hasModifier(AttributeModifier modifier);
|
||||
|
||||
/**
|
||||
* Returns attribute modifier, if any, by the given UUID
|
||||
*/
|
||||
@Nullable
|
||||
AttributeModifier getModifier(UUID uuid);
|
||||
|
||||
void applyModifier(AttributeModifier modifier);
|
||||
|
||||
void removeModifier(AttributeModifier modifier);
|
||||
|
||||
void removeModifier(UUID p_188479_1_);
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
void removeAllModifiers();
|
||||
|
||||
double getAttributeValue();
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
package net.minecraft.entity.ai.attributes;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class ModifiableAttributeInstance implements IAttributeInstance
|
||||
{
|
||||
/** The BaseAttributeMap this attributeInstance can be found in */
|
||||
private final AbstractAttributeMap attributeMap;
|
||||
/** The Attribute this is an instance of */
|
||||
private final IAttribute genericAttribute;
|
||||
private final Map<Integer, Set<AttributeModifier>> mapByOperation = Maps.<Integer, Set<AttributeModifier>>newHashMap();
|
||||
private final Map<String, Set<AttributeModifier>> mapByName = Maps.<String, Set<AttributeModifier>>newHashMap();
|
||||
private final Map<UUID, AttributeModifier> mapByUUID = Maps.<UUID, AttributeModifier>newHashMap();
|
||||
private double baseValue;
|
||||
private boolean needsUpdate = true;
|
||||
private double cachedValue;
|
||||
|
||||
public ModifiableAttributeInstance(AbstractAttributeMap attributeMapIn, IAttribute genericAttributeIn)
|
||||
{
|
||||
this.attributeMap = attributeMapIn;
|
||||
this.genericAttribute = genericAttributeIn;
|
||||
this.baseValue = genericAttributeIn.getDefaultValue();
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
this.mapByOperation.put(Integer.valueOf(i), Sets.newHashSet());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Attribute this is an instance of
|
||||
*/
|
||||
public IAttribute getAttribute()
|
||||
{
|
||||
return this.genericAttribute;
|
||||
}
|
||||
|
||||
public double getBaseValue()
|
||||
{
|
||||
return this.baseValue;
|
||||
}
|
||||
|
||||
public void setBaseValue(double baseValue)
|
||||
{
|
||||
if (baseValue != this.getBaseValue())
|
||||
{
|
||||
this.baseValue = baseValue;
|
||||
this.flagForUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<AttributeModifier> getModifiersByOperation(int operation)
|
||||
{
|
||||
return (Collection)this.mapByOperation.get(Integer.valueOf(operation));
|
||||
}
|
||||
|
||||
public Collection<AttributeModifier> getModifiers()
|
||||
{
|
||||
Set<AttributeModifier> set = Sets.<AttributeModifier>newHashSet();
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
set.addAll(this.getModifiersByOperation(i));
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns attribute modifier, if any, by the given UUID
|
||||
*/
|
||||
@Nullable
|
||||
public AttributeModifier getModifier(UUID uuid)
|
||||
{
|
||||
return this.mapByUUID.get(uuid);
|
||||
}
|
||||
|
||||
public boolean hasModifier(AttributeModifier modifier)
|
||||
{
|
||||
return this.mapByUUID.get(modifier.getID()) != null;
|
||||
}
|
||||
|
||||
public void applyModifier(AttributeModifier modifier)
|
||||
{
|
||||
if (this.getModifier(modifier.getID()) != null)
|
||||
{
|
||||
throw new IllegalArgumentException("Modifier is already applied on this attribute!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Set<AttributeModifier> set = (Set)this.mapByName.get(modifier.getName());
|
||||
|
||||
if (set == null)
|
||||
{
|
||||
set = Sets.<AttributeModifier>newHashSet();
|
||||
this.mapByName.put(modifier.getName(), set);
|
||||
}
|
||||
|
||||
(this.mapByOperation.get(Integer.valueOf(modifier.getOperation()))).add(modifier);
|
||||
set.add(modifier);
|
||||
this.mapByUUID.put(modifier.getID(), modifier);
|
||||
this.flagForUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
protected void flagForUpdate()
|
||||
{
|
||||
this.needsUpdate = true;
|
||||
this.attributeMap.onAttributeModified(this);
|
||||
}
|
||||
|
||||
public void removeModifier(AttributeModifier modifier)
|
||||
{
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
Set<AttributeModifier> set = (Set)this.mapByOperation.get(Integer.valueOf(i));
|
||||
set.remove(modifier);
|
||||
}
|
||||
|
||||
Set<AttributeModifier> set1 = (Set)this.mapByName.get(modifier.getName());
|
||||
|
||||
if (set1 != null)
|
||||
{
|
||||
set1.remove(modifier);
|
||||
|
||||
if (set1.isEmpty())
|
||||
{
|
||||
this.mapByName.remove(modifier.getName());
|
||||
}
|
||||
}
|
||||
|
||||
this.mapByUUID.remove(modifier.getID());
|
||||
this.flagForUpdate();
|
||||
}
|
||||
|
||||
public void removeModifier(UUID p_188479_1_)
|
||||
{
|
||||
AttributeModifier attributemodifier = this.getModifier(p_188479_1_);
|
||||
|
||||
if (attributemodifier != null)
|
||||
{
|
||||
this.removeModifier(attributemodifier);
|
||||
}
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void removeAllModifiers()
|
||||
{
|
||||
Collection<AttributeModifier> collection = this.getModifiers();
|
||||
|
||||
if (collection != null)
|
||||
{
|
||||
for (AttributeModifier attributemodifier : Lists.newArrayList(collection))
|
||||
{
|
||||
this.removeModifier(attributemodifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public double getAttributeValue()
|
||||
{
|
||||
if (this.needsUpdate)
|
||||
{
|
||||
this.cachedValue = this.computeValue();
|
||||
this.needsUpdate = false;
|
||||
}
|
||||
|
||||
return this.cachedValue;
|
||||
}
|
||||
|
||||
private double computeValue()
|
||||
{
|
||||
double d0 = this.getBaseValue();
|
||||
|
||||
for (AttributeModifier attributemodifier : this.getAppliedModifiers(0))
|
||||
{
|
||||
d0 += attributemodifier.getAmount();
|
||||
}
|
||||
|
||||
double d1 = d0;
|
||||
|
||||
for (AttributeModifier attributemodifier1 : this.getAppliedModifiers(1))
|
||||
{
|
||||
d1 += d0 * attributemodifier1.getAmount();
|
||||
}
|
||||
|
||||
for (AttributeModifier attributemodifier2 : this.getAppliedModifiers(2))
|
||||
{
|
||||
d1 *= 1.0D + attributemodifier2.getAmount();
|
||||
}
|
||||
|
||||
return this.genericAttribute.clampValue(d1);
|
||||
}
|
||||
|
||||
private Collection<AttributeModifier> getAppliedModifiers(int operation)
|
||||
{
|
||||
Set<AttributeModifier> set = Sets.newHashSet(this.getModifiersByOperation(operation));
|
||||
|
||||
for (IAttribute iattribute = this.genericAttribute.getParent(); iattribute != null; iattribute = iattribute.getParent())
|
||||
{
|
||||
IAttributeInstance iattributeinstance = this.attributeMap.getAttributeInstance(iattribute);
|
||||
|
||||
if (iattributeinstance != null)
|
||||
{
|
||||
set.addAll(iattributeinstance.getModifiersByOperation(operation));
|
||||
}
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package net.minecraft.entity.ai.attributes;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
public class RangedAttribute extends BaseAttribute
|
||||
{
|
||||
private final double minimumValue;
|
||||
private final double maximumValue;
|
||||
private String description;
|
||||
|
||||
public RangedAttribute(@Nullable IAttribute parentIn, String unlocalizedNameIn, double defaultValue, double minimumValueIn, double maximumValueIn)
|
||||
{
|
||||
super(parentIn, unlocalizedNameIn, defaultValue);
|
||||
this.minimumValue = minimumValueIn;
|
||||
this.maximumValue = maximumValueIn;
|
||||
|
||||
if (minimumValueIn > maximumValueIn)
|
||||
{
|
||||
throw new IllegalArgumentException("Minimum value cannot be bigger than maximum value!");
|
||||
}
|
||||
else if (defaultValue < minimumValueIn)
|
||||
{
|
||||
throw new IllegalArgumentException("Default value cannot be lower than minimum value!");
|
||||
}
|
||||
else if (defaultValue > maximumValueIn)
|
||||
{
|
||||
throw new IllegalArgumentException("Default value cannot be bigger than maximum value!");
|
||||
}
|
||||
}
|
||||
|
||||
public RangedAttribute setDescription(String descriptionIn)
|
||||
{
|
||||
this.description = descriptionIn;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getDescription()
|
||||
{
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public double clampValue(double value)
|
||||
{
|
||||
value = MathHelper.clamp(value, this.minimumValue, this.maximumValue);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// Auto generated package-info by MCP
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package net.minecraft.entity.ai.attributes;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
@@ -0,0 +1,7 @@
|
||||
// Auto generated package-info by MCP
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package net.minecraft.entity.ai;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
Reference in New Issue
Block a user