Added ownership to the Sentient Specter and improved its AI. Also enabled it to use a bow.
This commit is contained in:
parent
bc37851bd6
commit
38f4ea6bac
7 changed files with 691 additions and 16 deletions
|
@ -0,0 +1,167 @@
|
|||
package WayofTime.bloodmagic.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.ai.EntityAIBase;
|
||||
import net.minecraft.item.ItemBow;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import WayofTime.bloodmagic.entity.mob.EntitySentientSpecter;
|
||||
|
||||
public class EntityAIAttackRangedBow extends EntityAIBase
|
||||
{
|
||||
private final EntitySentientSpecter 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(EntitySentientSpecter specter, double speedAmplifier, int delay, float maxDistance)
|
||||
{
|
||||
this.entity = specter;
|
||||
this.moveSpeedAmp = speedAmplifier;
|
||||
this.attackCooldown = delay;
|
||||
this.maxAttackDistance = maxDistance * maxDistance;
|
||||
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() != null && this.entity.getHeldItemMainhand().getItem() instanceof ItemBow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean continueExecuting()
|
||||
{
|
||||
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();
|
||||
// this.entity.setSwingingArms(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the task
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
super.startExecuting();
|
||||
// this.entity.setSwingingArms(false);
|
||||
this.seeTime = 0;
|
||||
this.attackTime = -1;
|
||||
this.entity.resetActiveHand();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the task
|
||||
*/
|
||||
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().clearPathEntity();
|
||||
++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();
|
||||
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,147 @@
|
|||
package WayofTime.bloodmagic.entity.ai;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.ai.EntityAIBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.pathfinding.PathNavigate;
|
||||
import net.minecraft.pathfinding.PathNavigateGround;
|
||||
import net.minecraft.pathfinding.PathNodeType;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
import WayofTime.bloodmagic.entity.mob.EntitySentientSpecter;
|
||||
|
||||
public class EntityAIFollowOwner extends EntityAIBase
|
||||
{
|
||||
private EntitySentientSpecter thePet;
|
||||
private EntityLivingBase theOwner;
|
||||
World theWorld;
|
||||
private double followSpeed;
|
||||
private PathNavigate petPathfinder;
|
||||
private int timeToRecalcPath;
|
||||
float maxDist;
|
||||
float minDist;
|
||||
private float oldWaterCost;
|
||||
|
||||
public EntityAIFollowOwner(EntitySentientSpecter thePetIn, double followSpeedIn, float minDistIn, float maxDistIn)
|
||||
{
|
||||
this.thePet = thePetIn;
|
||||
this.theWorld = thePetIn.worldObj;
|
||||
this.followSpeed = followSpeedIn;
|
||||
this.petPathfinder = thePetIn.getNavigator();
|
||||
this.minDist = minDistIn;
|
||||
this.maxDist = maxDistIn;
|
||||
this.setMutexBits(3);
|
||||
|
||||
if (!(thePetIn.getNavigator() instanceof PathNavigateGround))
|
||||
{
|
||||
throw new IllegalArgumentException("Unsupported mob type for FollowOwnerGoal");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.thePet.getOwner();
|
||||
|
||||
if (entitylivingbase == null)
|
||||
{
|
||||
return false;
|
||||
} else if (entitylivingbase instanceof EntityPlayer && ((EntityPlayer) entitylivingbase).isSpectator())
|
||||
{
|
||||
return false;
|
||||
} else if (this.thePet.isStationary())
|
||||
{
|
||||
return false;
|
||||
} else if (this.thePet.getDistanceSqToEntity(entitylivingbase) < (double) (this.minDist * this.minDist))
|
||||
{
|
||||
return false;
|
||||
} else
|
||||
{
|
||||
this.theOwner = entitylivingbase;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
public boolean continueExecuting()
|
||||
{
|
||||
return !this.petPathfinder.noPath() && this.thePet.getDistanceSqToEntity(this.theOwner) > (double) (this.maxDist * this.maxDist) && !this.thePet.isStationary();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.timeToRecalcPath = 0;
|
||||
this.oldWaterCost = this.thePet.getPathPriority(PathNodeType.WATER);
|
||||
this.thePet.setPathPriority(PathNodeType.WATER, 0.0F);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the task
|
||||
*/
|
||||
public void resetTask()
|
||||
{
|
||||
this.theOwner = null;
|
||||
this.petPathfinder.clearPathEntity();
|
||||
this.thePet.setPathPriority(PathNodeType.WATER, this.oldWaterCost);
|
||||
}
|
||||
|
||||
private boolean isEmptyBlock(BlockPos pos)
|
||||
{
|
||||
IBlockState iblockstate = this.theWorld.getBlockState(pos);
|
||||
Block block = iblockstate.getBlock();
|
||||
return block == Blocks.AIR ? true : !iblockstate.isFullCube();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the task
|
||||
*/
|
||||
public void updateTask()
|
||||
{
|
||||
this.thePet.getLookHelper().setLookPositionWithEntity(this.theOwner, 10.0F, (float) this.thePet.getVerticalFaceSpeed());
|
||||
|
||||
if (!this.thePet.isStationary())
|
||||
{
|
||||
if (--this.timeToRecalcPath <= 0)
|
||||
{
|
||||
this.timeToRecalcPath = 10;
|
||||
|
||||
if (!this.petPathfinder.tryMoveToEntityLiving(this.theOwner, this.followSpeed))
|
||||
{
|
||||
if (!this.thePet.getLeashed())
|
||||
{
|
||||
if (this.thePet.getDistanceSqToEntity(this.theOwner) >= 144.0D)
|
||||
{
|
||||
int i = MathHelper.floor_double(this.theOwner.posX) - 2;
|
||||
int j = MathHelper.floor_double(this.theOwner.posZ) - 2;
|
||||
int k = MathHelper.floor_double(this.theOwner.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.theWorld.getBlockState(new BlockPos(i + l, k - 1, j + i1)).isFullyOpaque() && this.isEmptyBlock(new BlockPos(i + l, k, j + i1)) && this.isEmptyBlock(new BlockPos(i + l, k + 1, j + i1)))
|
||||
{
|
||||
this.thePet.setLocationAndAngles((double) ((float) (i + l) + 0.5F), (double) k, (double) ((float) (j + i1) + 0.5F), this.thePet.rotationYaw, this.thePet.rotationPitch);
|
||||
this.petPathfinder.clearPathEntity();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package WayofTime.bloodmagic.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.ai.EntityAITarget;
|
||||
import WayofTime.bloodmagic.entity.mob.EntitySentientSpecter;
|
||||
|
||||
public class EntityAIOwnerHurtByTarget extends EntityAITarget
|
||||
{
|
||||
EntitySentientSpecter theDefendingTameable;
|
||||
EntityLivingBase theOwnerAttacker;
|
||||
private int timestamp;
|
||||
|
||||
public EntityAIOwnerHurtByTarget(EntitySentientSpecter theDefendingTameableIn)
|
||||
{
|
||||
super(theDefendingTameableIn, false);
|
||||
this.theDefendingTameable = theDefendingTameableIn;
|
||||
this.setMutexBits(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (!this.theDefendingTameable.isTamed())
|
||||
{
|
||||
return false;
|
||||
} else
|
||||
{
|
||||
EntityLivingBase owner = this.theDefendingTameable.getOwner();
|
||||
|
||||
if (owner == null)
|
||||
{
|
||||
return false;
|
||||
} else
|
||||
{
|
||||
this.theOwnerAttacker = owner.getAITarget();
|
||||
int i = owner.getRevengeTimer();
|
||||
return i != this.timestamp && this.isSuitableTarget(this.theOwnerAttacker, false) && this.theDefendingTameable.shouldAttackEntity(this.theOwnerAttacker, owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.taskOwner.setAttackTarget(this.theOwnerAttacker);
|
||||
EntityLivingBase owner = this.theDefendingTameable.getOwner();
|
||||
|
||||
if (owner != null)
|
||||
{
|
||||
this.timestamp = owner.getRevengeTimer();
|
||||
}
|
||||
|
||||
super.startExecuting();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package WayofTime.bloodmagic.entity.ai;
|
||||
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.ai.EntityAITarget;
|
||||
import WayofTime.bloodmagic.entity.mob.EntitySentientSpecter;
|
||||
|
||||
public class EntityAIOwnerHurtTarget extends EntityAITarget
|
||||
{
|
||||
EntitySentientSpecter theEntitySentientSpecter;
|
||||
EntityLivingBase theTarget;
|
||||
private int timestamp;
|
||||
|
||||
public EntityAIOwnerHurtTarget(EntitySentientSpecter theEntitySentientSpecterIn)
|
||||
{
|
||||
super(theEntitySentientSpecterIn, false);
|
||||
this.theEntitySentientSpecter = theEntitySentientSpecterIn;
|
||||
this.setMutexBits(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (!this.theEntitySentientSpecter.isTamed())
|
||||
{
|
||||
return false;
|
||||
} else
|
||||
{
|
||||
EntityLivingBase entitylivingbase = this.theEntitySentientSpecter.getOwner();
|
||||
|
||||
if (entitylivingbase == null)
|
||||
{
|
||||
return false;
|
||||
} else
|
||||
{
|
||||
this.theTarget = entitylivingbase.getLastAttacker();
|
||||
int i = entitylivingbase.getLastAttackerTime();
|
||||
return i != this.timestamp && this.isSuitableTarget(this.theTarget, false) && this.theEntitySentientSpecter.shouldAttackEntity(this.theTarget, entitylivingbase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
public void startExecuting()
|
||||
{
|
||||
this.taskOwner.setAttackTarget(this.theTarget);
|
||||
EntityLivingBase entitylivingbase = this.theEntitySentientSpecter.getOwner();
|
||||
|
||||
if (entitylivingbase != null)
|
||||
{
|
||||
this.timestamp = entitylivingbase.getLastAttackerTime();
|
||||
}
|
||||
|
||||
super.startExecuting();
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue