/** Copyright (C) <2015> <coolAlias> This file is part of coolAlias' Zelda Sword Skills Minecraft Mod; as such, you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package zeldaswordskills.entity.ai; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.ai.EntityAIBase; public class EntityAIPowerAttack extends EntityAIBase { /** Owner of the AI */ private final EntityLiving attacker; /** IPowerAttacker instance of attacker */ private final IPowerAttacker powerAttacker; /** Current AI target */ private EntityLivingBase target; /** Base reach distance */ private double range; /** Maximum distance squared from target at which attacker is able to attack */ private double rangeSq; /** Number of ticks until next attack is allowed */ private int attackTimer; /** Number of ticks remaining until power attack triggers */ private int chargeTimer; /** * @param range Attacker's reach with this attack */ public <T extends EntityLiving & IPowerAttacker> EntityAIPowerAttack(T attacker, double range) { this.attacker = attacker; this.powerAttacker = attacker; this.range = range; this.rangeSq = (range * range); // since attackOnCollide is always executing, must use mutex 0 for now setMutexBits(0); } @Override public boolean shouldExecute() { target = attacker.getAttackTarget(); // TODO replace attackTime? or do mobs now regulate this some other way? /*if (attacker.attackTime > 0) { return false; } else*/ if (attackTimer > 0) { --attackTimer; return false; } else if (target == null || !target.isEntityAlive()) { return false; } else if (attacker.getDistanceSqToEntity(target) > (rangeSq * 2.25F)) { return false; } return attacker.onGround; } @Override public void startExecuting() { setMutexBits(3); // prevent EntityAIAttackOnCollide from executing chargeTimer = powerAttacker.getChargeTime(); powerAttacker.beginPowerAttack(); } @Override public boolean continueExecuting() { return (target != null && target.isEntityAlive() && chargeTimer > 0 && attacker.onGround && attacker.getDistanceSqToEntity(target) < (rangeSq * 2.25F)); } @Override public void updateTask() { attacker.getLookHelper().setLookPositionWithEntity(target, 30.0F, 30.0F); // helps attacker actually rotate towards target, as well as continue to slowly close distance attacker.getMoveHelper().setMoveTo(target.posX, target.posY, target.posZ, 1.0D / 3.0D); if (chargeTimer > 0 && --chargeTimer == 0) { attackTimer = 20 + attacker.worldObj.rand.nextInt(20) + attacker.worldObj.rand.nextInt(20); double d = attacker.getDistanceSq(target.posX, target.getEntityBoundingBox().minY, target.posZ); double width = (attacker.width + target.width) / 2.0F; double reach = (range + width) * (range + width); if (d <= reach && attacker.getEntitySenses().canSee(target)) { powerAttacker.performPowerAttack(target); } else { powerAttacker.onAttackMissed(); } } } @Override public void resetTask() { setMutexBits(0); // re-allow other AIs to execute chargeTimer = 0; target = null; } }