Added retreat-and-heal AI to the Specters
This commit is contained in:
parent
5592e8661b
commit
64c06e39b5
|
@ -0,0 +1,159 @@
|
|||
package WayofTime.bloodmagic.entity.ai;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.ai.EntityAIBase;
|
||||
import net.minecraft.entity.ai.RandomPositionGenerator;
|
||||
import net.minecraft.pathfinding.Path;
|
||||
import net.minecraft.pathfinding.PathNavigate;
|
||||
import net.minecraft.util.EntitySelectors;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import WayofTime.bloodmagic.entity.mob.EntitySentientSpecter;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
|
||||
public class EntityAIRetreatToHeal<T extends Entity> extends EntityAIBase
|
||||
{
|
||||
private final Predicate<Entity> canBeSeenSelector;
|
||||
/** The entity we are attached to */
|
||||
protected EntitySentientSpecter theEntity;
|
||||
private double farSpeed;
|
||||
private double nearSpeed;
|
||||
private double safeHealDistance = 3;
|
||||
protected T closestLivingEntity;
|
||||
private float avoidDistance;
|
||||
/** The PathEntity of our entity */
|
||||
private Path entityPathEntity;
|
||||
/** The PathNavigate of our entity */
|
||||
private PathNavigate entityPathNavigate;
|
||||
private Class<T> classToAvoid;
|
||||
private Predicate<? super T> avoidTargetSelector;
|
||||
|
||||
public EntityAIRetreatToHeal(EntitySentientSpecter theEntityIn, Class<T> classToAvoidIn, float avoidDistanceIn, double farSpeedIn, double nearSpeedIn)
|
||||
{
|
||||
this(theEntityIn, classToAvoidIn, Predicates.<T>alwaysTrue(), avoidDistanceIn, farSpeedIn, nearSpeedIn);
|
||||
}
|
||||
|
||||
public EntityAIRetreatToHeal(EntitySentientSpecter theEntityIn, 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() && EntityAIRetreatToHeal.this.theEntity.getEntitySenses().canSee(p_apply_1_);
|
||||
}
|
||||
};
|
||||
this.theEntity = theEntityIn;
|
||||
this.classToAvoid = classToAvoidIn;
|
||||
this.avoidTargetSelector = avoidTargetSelectorIn;
|
||||
this.avoidDistance = avoidDistanceIn;
|
||||
this.farSpeed = farSpeedIn;
|
||||
this.nearSpeed = nearSpeedIn;
|
||||
this.entityPathNavigate = theEntityIn.getNavigator();
|
||||
this.setMutexBits(3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the EntityAIBase should begin execution.
|
||||
*/
|
||||
@Override
|
||||
public boolean shouldExecute()
|
||||
{
|
||||
if (!this.theEntity.shouldSelfHeal())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//This part almost doesn't matter
|
||||
List<T> list = this.theEntity.worldObj.<T>getEntitiesWithinAABB(this.classToAvoid, this.theEntity.getEntityBoundingBox().expand((double) this.avoidDistance, 3.0D, (double) this.avoidDistance), Predicates.and(new Predicate[] { EntitySelectors.CAN_AI_TARGET, this.canBeSeenSelector, this.avoidTargetSelector }));
|
||||
|
||||
if (list.isEmpty())
|
||||
{
|
||||
return true; //No entities nearby, so I can freely heal
|
||||
} else
|
||||
{
|
||||
this.closestLivingEntity = list.get(0);
|
||||
Vec3d vec3d = RandomPositionGenerator.findRandomTargetBlockAwayFrom(this.theEntity, 16, 7, new Vec3d(this.closestLivingEntity.posX, this.closestLivingEntity.posY, this.closestLivingEntity.posZ));
|
||||
|
||||
if (vec3d == null)
|
||||
{
|
||||
return false; //Nowhere to run, gotta fight!
|
||||
} else if (this.closestLivingEntity.getDistanceSq(vec3d.xCoord, vec3d.yCoord, vec3d.zCoord) < this.closestLivingEntity.getDistanceSqToEntity(this.theEntity))
|
||||
{
|
||||
return false; //I'll be headed off if I choose this direction.
|
||||
} else
|
||||
{
|
||||
this.entityPathEntity = this.entityPathNavigate.getPathToXYZ(vec3d.xCoord, vec3d.yCoord, vec3d.zCoord);
|
||||
return this.entityPathEntity == null ? false : this.entityPathEntity.isDestinationSame(vec3d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an in-progress EntityAIBase should continue executing
|
||||
*/
|
||||
@Override
|
||||
public boolean continueExecuting()
|
||||
{
|
||||
return this.theEntity.shouldSelfHeal();//!this.entityPathNavigate.noPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one shot task or start executing a continuous task
|
||||
*/
|
||||
@Override
|
||||
public void startExecuting()
|
||||
{
|
||||
if (this.entityPathEntity != null)
|
||||
{
|
||||
this.entityPathNavigate.setPath(this.entityPathEntity, this.farSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the task
|
||||
*/
|
||||
@Override
|
||||
public void resetTask()
|
||||
{
|
||||
this.closestLivingEntity = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the task
|
||||
*/
|
||||
@Override
|
||||
public void updateTask()
|
||||
{
|
||||
if (this.closestLivingEntity != null)
|
||||
{
|
||||
if (this.theEntity.getDistanceSqToEntity(this.closestLivingEntity) < 49.0D)
|
||||
{
|
||||
this.theEntity.getNavigator().setSpeed(this.nearSpeed);
|
||||
} else
|
||||
{
|
||||
this.theEntity.getNavigator().setSpeed(this.farSpeed);
|
||||
}
|
||||
|
||||
if (this.theEntity.ticksExisted % 20 == 0 && this.theEntity.getDistanceSqToEntity(this.closestLivingEntity) >= safeHealDistance * safeHealDistance)
|
||||
{
|
||||
healEntity();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.theEntity.ticksExisted % 20 == 0)
|
||||
{
|
||||
healEntity();
|
||||
}
|
||||
}
|
||||
|
||||
public void healEntity()
|
||||
{
|
||||
this.theEntity.absorbWillFromAuraToHeal(2);
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ import lombok.Setter;
|
|||
import net.minecraft.block.Block;
|
||||
import net.minecraft.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.IEntityOwnable;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
|
@ -37,6 +38,7 @@ import net.minecraft.nbt.NBTTagCompound;
|
|||
import net.minecraft.network.datasync.DataParameter;
|
||||
import net.minecraft.network.datasync.DataSerializers;
|
||||
import net.minecraft.network.datasync.EntityDataManager;
|
||||
import net.minecraft.potion.Potion;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.server.management.PreYggdrasilConverter;
|
||||
import net.minecraft.util.DamageSource;
|
||||
|
@ -49,16 +51,19 @@ import net.minecraft.world.Explosion;
|
|||
import net.minecraft.world.World;
|
||||
import WayofTime.bloodmagic.api.Constants;
|
||||
import WayofTime.bloodmagic.api.soul.EnumDemonWillType;
|
||||
import WayofTime.bloodmagic.demonAura.WorldDemonWillHandler;
|
||||
import WayofTime.bloodmagic.entity.ai.EntityAIAttackRangedBow;
|
||||
import WayofTime.bloodmagic.entity.ai.EntityAIFollowOwner;
|
||||
import WayofTime.bloodmagic.entity.ai.EntityAIGrabEffectsFromOwner;
|
||||
import WayofTime.bloodmagic.entity.ai.EntityAIHurtByTargetIgnoreTamed;
|
||||
import WayofTime.bloodmagic.entity.ai.EntityAIOwnerHurtByTarget;
|
||||
import WayofTime.bloodmagic.entity.ai.EntityAIOwnerHurtTarget;
|
||||
import WayofTime.bloodmagic.entity.ai.EntityAIRetreatToHeal;
|
||||
import WayofTime.bloodmagic.item.soul.ItemSentientBow;
|
||||
import WayofTime.bloodmagic.registry.ModItems;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
public class EntitySentientSpecter extends EntityMob implements IEntityOwnable
|
||||
{
|
||||
|
@ -76,7 +81,7 @@ public class EntitySentientSpecter extends EntityMob implements IEntityOwnable
|
|||
private final EntityAIAttackRangedBow aiArrowAttack = new EntityAIAttackRangedBow(this, 1.0D, 20, 15.0F);
|
||||
private final EntityAIAttackMelee aiAttackOnCollide = new EntityAIAttackMelee(this, 1.0D, false);
|
||||
|
||||
private final int attackPriority = 2;
|
||||
private final int attackPriority = 3;
|
||||
|
||||
public EntitySentientSpecter(World worldIn)
|
||||
{
|
||||
|
@ -84,12 +89,13 @@ public class EntitySentientSpecter extends EntityMob implements IEntityOwnable
|
|||
this.setSize(0.6F, 1.95F);
|
||||
// ((PathNavigateGround) getNavigator()).setCanSwim(false);
|
||||
this.tasks.addTask(0, new EntityAISwimming(this));
|
||||
this.tasks.addTask(2, new EntityAIRetreatToHeal<EntityCreature>(this, EntityCreature.class, 6.0F, 1.0D, 1.2D));
|
||||
this.tasks.addTask(attackPriority, aiAttackOnCollide);
|
||||
this.tasks.addTask(3, new EntityAIGrabEffectsFromOwner(this, 2.0D, 1.0F));
|
||||
this.tasks.addTask(4, new EntityAIFollowOwner(this, 1.0D, 10.0F, 2.0F));
|
||||
this.tasks.addTask(5, new EntityAIWander(this, 1.0D));
|
||||
this.tasks.addTask(6, new EntityAIWatchClosest(this, EntityPlayer.class, 6.0F));
|
||||
this.tasks.addTask(7, new EntityAILookIdle(this));
|
||||
this.tasks.addTask(4, new EntityAIGrabEffectsFromOwner(this, 2.0D, 1.0F));
|
||||
this.tasks.addTask(5, new EntityAIFollowOwner(this, 1.0D, 10.0F, 2.0F));
|
||||
this.tasks.addTask(6, new EntityAIWander(this, 1.0D));
|
||||
this.tasks.addTask(7, new EntityAIWatchClosest(this, EntityPlayer.class, 6.0F));
|
||||
this.tasks.addTask(8, new EntityAILookIdle(this));
|
||||
|
||||
this.targetTasks.addTask(1, new EntityAIOwnerHurtByTarget(this));
|
||||
this.targetTasks.addTask(2, new EntityAIOwnerHurtTarget(this));
|
||||
|
@ -98,7 +104,7 @@ public class EntitySentientSpecter extends EntityMob implements IEntityOwnable
|
|||
this.targetTasks.addTask(4, new EntityAIHurtByTargetIgnoreTamed(this, false, new Class[0]));
|
||||
|
||||
this.setCombatTask();
|
||||
// this.targetTasks.addTask(8, new EntityAINearestAttackableTarget<EntityCreature>(this, EntityCreature.class, true));
|
||||
// this.targetTasks.addTask(8, new EntityAINearestAttackableTarget<EntityMob>(this, EntityMob.class, 10, true, false, new TargetPredicate(this)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -144,6 +150,19 @@ public class EntitySentientSpecter extends EntityMob implements IEntityOwnable
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPotionApplicable(PotionEffect effect)
|
||||
{
|
||||
Potion potion = effect.getPotion();
|
||||
|
||||
if (potion == MobEffects.REGENERATION || potion == MobEffects.INSTANT_HEALTH) //Specter cannot be healed by normal means
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return super.isPotionApplicable(effect);
|
||||
}
|
||||
|
||||
public boolean canStealEffectFromOwner(EntityLivingBase owner, PotionEffect effect)
|
||||
{
|
||||
return effect.getPotion().isBadEffect() && this.type == EnumDemonWillType.CORROSIVE;
|
||||
|
@ -373,6 +392,41 @@ public class EntitySentientSpecter extends EntityMob implements IEntityOwnable
|
|||
return super.isEntityInvulnerable(source) && (this.type == EnumDemonWillType.DESTRUCTIVE && source.isExplosion());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param toHeal
|
||||
* @return Amount of Will consumed from the Aura to heal
|
||||
*/
|
||||
public double absorbWillFromAuraToHeal(double toHeal)
|
||||
{
|
||||
if (worldObj.isRemote)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
double will = WorldDemonWillHandler.getCurrentWill(worldObj, getPosition(), getType());
|
||||
double healthMissing = this.getMaxHealth() - this.getHealth();
|
||||
|
||||
toHeal = Math.min(healthMissing, Math.min(toHeal, will / getWillToHealth()));
|
||||
if (toHeal > 0)
|
||||
{
|
||||
this.heal((float) toHeal);
|
||||
return WorldDemonWillHandler.drainWill(worldObj, getPosition(), getType(), toHeal * getWillToHealth(), true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean shouldSelfHeal()
|
||||
{
|
||||
return this.getHealth() < this.getMaxHealth() * 0.5 && WorldDemonWillHandler.getCurrentWill(worldObj, getPosition(), getType()) > 0;
|
||||
}
|
||||
|
||||
public double getWillToHealth()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canDespawn()
|
||||
{
|
||||
|
@ -598,4 +652,20 @@ public class EntitySentientSpecter extends EntityMob implements IEntityOwnable
|
|||
{
|
||||
setOwnerId(player.getUniqueID());
|
||||
}
|
||||
|
||||
public class TargetPredicate implements Predicate<EntityMob>
|
||||
{
|
||||
EntitySentientSpecter entity;
|
||||
|
||||
public TargetPredicate(EntitySentientSpecter entity)
|
||||
{
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(EntityMob input)
|
||||
{
|
||||
return entity.shouldAttackEntity(input, this.entity.getOwner());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -71,8 +71,6 @@ public class ItemSentientSword extends ItemSword implements IDemonWillWeapon, IM
|
|||
|
||||
public static double[] movementSpeed = new double[] { 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.4 };
|
||||
|
||||
public static final boolean spawnSpecterOnClick = true;
|
||||
|
||||
public ItemSentientSword()
|
||||
{
|
||||
super(ModItems.soulToolMaterial);
|
||||
|
|
Loading…
Reference in a new issue