diff --git a/src/main/java/WayofTime/bloodmagic/entity/ai/EntityAIRetreatToHeal.java b/src/main/java/WayofTime/bloodmagic/entity/ai/EntityAIRetreatToHeal.java new file mode 100644 index 00000000..38361986 --- /dev/null +++ b/src/main/java/WayofTime/bloodmagic/entity/ai/EntityAIRetreatToHeal.java @@ -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 extends EntityAIBase +{ + private final Predicate 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 classToAvoid; + private Predicate avoidTargetSelector; + + public EntityAIRetreatToHeal(EntitySentientSpecter theEntityIn, Class classToAvoidIn, float avoidDistanceIn, double farSpeedIn, double nearSpeedIn) + { + this(theEntityIn, classToAvoidIn, Predicates.alwaysTrue(), avoidDistanceIn, farSpeedIn, nearSpeedIn); + } + + public EntityAIRetreatToHeal(EntitySentientSpecter theEntityIn, Class classToAvoidIn, Predicate avoidTargetSelectorIn, float avoidDistanceIn, double farSpeedIn, double nearSpeedIn) + { + this.canBeSeenSelector = new Predicate() + { + 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 list = this.theEntity.worldObj.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); + } +} \ No newline at end of file diff --git a/src/main/java/WayofTime/bloodmagic/entity/mob/EntitySentientSpecter.java b/src/main/java/WayofTime/bloodmagic/entity/mob/EntitySentientSpecter.java index a3bbcb14..f54071c4 100644 --- a/src/main/java/WayofTime/bloodmagic/entity/mob/EntitySentientSpecter.java +++ b/src/main/java/WayofTime/bloodmagic/entity/mob/EntitySentientSpecter.java @@ -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(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(this, EntityCreature.class, true)); +// this.targetTasks.addTask(8, new EntityAINearestAttackableTarget(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 + { + EntitySentientSpecter entity; + + public TargetPredicate(EntitySentientSpecter entity) + { + this.entity = entity; + } + + @Override + public boolean apply(EntityMob input) + { + return entity.shouldAttackEntity(input, this.entity.getOwner()); + } + } } \ No newline at end of file diff --git a/src/main/java/WayofTime/bloodmagic/item/soul/ItemSentientSword.java b/src/main/java/WayofTime/bloodmagic/item/soul/ItemSentientSword.java index eec86001..80b7b44f 100644 --- a/src/main/java/WayofTime/bloodmagic/item/soul/ItemSentientSword.java +++ b/src/main/java/WayofTime/bloodmagic/item/soul/ItemSentientSword.java @@ -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);