/**
Copyright (C) 2015 by jabelar
This file is part of jabelar's Minecraft Forge modding examples; 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.
For a copy of the GNU General Public License see <http://www.gnu.org/licenses/>.
*/
package com.blogspot.jabelarminecraft.wildanimals.entities.ai.birdofprey;
import java.util.Iterator;
import java.util.List;
import net.minecraft.block.BlockLeaves;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.DamageSource;
import net.minecraft.util.Vec3;
import com.blogspot.jabelarminecraft.wildanimals.entities.birdsofprey.EntityBirdOfPrey;
import com.blogspot.jabelarminecraft.wildanimals.utilities.Utilities;
/**
* @author jabelar
*
*/
public class UpdateStateBirdOfPrey
{
public EntityBirdOfPrey theBird;
// defines area on ground that bird looks for prey in
// region is double this size as this gives dimensions in each direction
public double attackRegionSize = 5.0D;
// the "one in" chance per tick that it will decide to perch if over a perchable block
public final int PERCH_CHANCE_BASE = 1;
// the percent chance per tick that when perched it will decide to take off
public final int TAKE_OFF_CHANCE_BASE = 2400;
public UpdateStateBirdOfPrey(EntityBirdOfPrey parBirdOfPrey)
{
theBird = parBirdOfPrey;
}
public void updateAIState()
{
switch (theBird.getState())
{
case AIStates.STATE_PERCHED:
{
updateStatePerched();
break;
}
case AIStates.STATE_TAKING_OFF:
{
updateStateTakingOff();
break;
}
case AIStates.STATE_SOARING:
{
updateStateSoaring();
break;
}
case AIStates.STATE_DIVING:
{
updateStateDiving();
break;
}
case AIStates.STATE_LANDING:
{
updateStateLanding();
break;
}
case AIStates.STATE_TRAVELLING:
{
updateStateTravelling();
break;
}
case AIStates.STATE_ATTACKING:
{
updateStateAttacking();
break;
}
case AIStates.STATE_SOARING_TAMED:
{
updateStateSoaringTamed();
break;
}
case AIStates.STATE_PERCHED_TAMED:
{
updateStatePerchedTamed();
break;
}
case AIStates.STATE_SEEKING:
{
updateStateSeeking();
break;
}
default:
{
// DEBUG
System.out.println("EntityBirdOfPrey OnLivingUpdate() **ERROR** unhandled state");
break;
}
}
}
/**
*
*/
private void updateStateSeeking()
{
if (theBird.isTamed())
{
processOwnerAttack();
if (theBird.getAttackTarget() != null)
{
theBird.setState(AIStates.STATE_ATTACKING);
}
else
{
theBird.setState(AIStates.STATE_TRAVELLING);
}
}
else
{
// DEBUG
System.out.println("Seeking but isn't tamed");
}
}
/**
*
*/
private void updateStatePerchedTamed()
{
// check if block perched upon has disappeared
// // DEBUG
// System.out.println("Block underneath = "+worldObj.getBlock(MathHelper.floor_double(posX), (int)posY - 1, MathHelper.floor_double(posZ)).getUnlocalizedName());
if (!hasLanded())
{
theBird.setState(AIStates.STATE_TAKING_OFF);
}
else // still solidly perched
{
// can occasionally adjust or flap, look around, or play sound to create variety
if (theBird.getRNG().nextInt(getTakeOffChance()) == 0)
{
theBird.setState(AIStates.STATE_TAKING_OFF);
// rotationYawHead = rand.nextInt(360);
}
}
}
/**
*
*/
private void updateStateSoaringTamed()
{
// climb again if drifting too low
if (theBird.posY < theBird.getSoarHeight()*0.9D)
{
// point towards owner
theBird.rotationYaw = Utilities.getYawFromVec(Vec3.createVectorHelper(
theBird.getOwner().posX - theBird.posX,
theBird.getOwner().posY - theBird.posY,
theBird.getOwner().posZ - theBird.posZ));
theBird.setState(AIStates.STATE_TRAVELLING);
}
considerAttacking();
if (theBird.getAttackTarget() == null)
{
considerPerching();
}
else
{
theBird.setState(AIStates.STATE_SEEKING);
}
}
/**
*
*/
private void updateStateAttacking()
{
EntityLivingBase target = theBird.getAttackTarget();
// check if target has been killed or despawned
if (target == null)
{
theBird.setState(AIStates.STATE_TAKING_OFF);
}
else if (target.isDead)
{
theBird.setAttackTarget(null);
theBird.setState(AIStates.STATE_TAKING_OFF);
}
else if (!Utilities.isCourseTraversable(theBird, target.posX, target.posY, target.posZ))
{
// theBird.setAttackTarget(null);
theBird.setState(AIStates.STATE_TAKING_OFF);
}
// check for hitting target
else if (theBird.getDistanceToEntity(theBird.getAttackTarget())<2.0F)
{
theBird.getAttackTarget().attackEntityFrom(
DamageSource.causeMobDamage(theBird),
(float) theBird.getEntityAttribute(SharedMonsterAttributes.attackDamage).getAttributeValue());
theBird.setState(AIStates.STATE_TAKING_OFF);
}
}
/**
*
*/
private void updateStateTravelling()
{
if (theBird.posY >= theBird.getSoarHeight())
{
// // DEBUG
// System.out.println("State changed to soaring");
if (theBird.isTamed())
{
theBird.setState(AIStates.STATE_SOARING_TAMED);
}
else
{
theBird.setState(AIStates.STATE_SOARING);
}
}
}
/**
*
*/
private void updateStateLanding()
{
if (hasLanded())
{
// DEBUG
System.out.println("Made it to perch");
if (theBird.isTamed())
{
theBird.setState(AIStates.STATE_PERCHED_TAMED);
}
else
{
theBird.setState(AIStates.STATE_PERCHED);
}
}
}
private void updateStateDiving()
{
// // DEBUG
// System.out.println("Block underneath = "+worldObj.getBlock(MathHelper.floor_double(posX), (int)posY - 1, MathHelper.floor_double(posZ)).getUnlocalizedName());
// handle case where perch target block might get destroyed before eagle gets to it
// or might get obstructed.
// if (theBird.worldObj.getTopBlock((int)theBird.posX, (int)theBird.posZ) instanceof BlockLeaves
// && Utilities.isCourseTraversable(
// theBird,
// theBird.posX,
// theBird.worldObj.getHeightValue(
// (int)theBird.posX,
// (int)theBird.posZ),
// theBird.posZ))
// {
// check if should start landing
if (theBird.getDistanceSq(
theBird.getAnchorX(),
theBird.getAnchorY(),
theBird.getAnchorZ())<25)
{
theBird.setState(AIStates.STATE_LANDING);
}
// see if made it to perch
else if (hasLanded())
{
// DEBUG
System.out.println("Made it to perch");
if (theBird.isTamed())
{
theBird.setState(AIStates.STATE_PERCHED_TAMED);
stopMoving();
}
else
{
theBird.setState(AIStates.STATE_PERCHED);
stopMoving();
}
}
// }
// else
// {
// theBird.setState(AIStates.STATE_TAKING_OFF);
// }
}
private boolean hasLanded()
{
AxisAlignedBB entityBoundingBox = theBird.boundingBox.copy().offset(0.0D, -0.5D, 0.0D);
if (!theBird.worldObj.getCollidingBoundingBoxes(theBird, entityBoundingBox).isEmpty())
{
return true;
}
else
{
return false;
}
}
protected void stopMoving()
{
theBird.motionX = 0;
theBird.motionY = 0;
theBird.motionZ = 0;
}
private void updateStateSoaring()
{
if (theBird.isTamed())
{
theBird.setState(AIStates.STATE_SOARING_TAMED);
}
else
{
// climb again if drifting too low
if (theBird.posY < theBird.getSoarHeight()*0.9D)
{
theBird.setState(AIStates.STATE_TRAVELLING);
}
considerAttacking();
if (theBird.getAttackTarget() == null)
{
considerPerching();
}
else
{
theBird.setState(AIStates.STATE_ATTACKING);
}
}
}
/**
*
*/
private void updateStateTakingOff()
{
if (theBird.posY >= theBird.getSoarHeight())
{
if (theBird.isTamed())
{
theBird.setState(AIStates.STATE_SOARING_TAMED);
}
else
{
theBird.setState(AIStates.STATE_SOARING);
}
}
}
/**
*
*/
private void updateStatePerched()
{
// check if block perched upon has disappeared
// // DEBUG
// System.out.println("Block underneath = "+worldObj.getBlock(MathHelper.floor_double(posX), (int)posY - 1, MathHelper.floor_double(posZ)).getUnlocalizedName());
// DEBUG
System.out.println("Tamed = "+theBird.isTamed());
if (theBird.isTamed())
{
theBird.setState(AIStates.STATE_PERCHED_TAMED);
}
else if (!hasLanded())
{
theBird.setState(AIStates.STATE_TAKING_OFF);
}
else // still solidly perched
{
// can occasionally adjust or flap, look around, or play sound to create variety
if (theBird.getRNG().nextInt(getTakeOffChance()) == 0)
{
theBird.setState(AIStates.STATE_TAKING_OFF);
// rotationYawHead = rand.nextInt(360);
}
// entity can get scared if player gets too close
EntityPlayer closestPlayer = theBird.worldObj.getClosestPlayerToEntity(theBird, 4.0D);
if (closestPlayer != null)
{
ItemStack theHeldItemStack = closestPlayer.inventory.getCurrentItem();
if (theHeldItemStack != null)
{
// if not holding taming food, bird will get spooked
if (!theBird.isTamingFood(theHeldItemStack))
{
theBird.setState(AIStates.STATE_TAKING_OFF);
}
}
}
}
}
public void considerPerching()
{
if (theBird.isTamed())
{
return;
}
else
{
// always try to perch starting at dusk
if (theBird.getRNG().nextInt(getPerchChance()) == 0)
{
if (theBird.worldObj.getTopBlock((int)theBird.posX, (int)theBird.posZ) instanceof BlockLeaves)
{
if (Utilities.isCourseTraversable(
theBird,
theBird.posX,
theBird.worldObj.getHeightValue(
(int)theBird.posX,
(int)theBird.posZ),
theBird.posZ))
{
theBird.setState(AIStates.STATE_DIVING);
theBird.setAnchor(
theBird.posX,
theBird.worldObj.getHeightValue(
(int)theBird.posX,
(int)theBird.posZ),
theBird.posZ);
}
}
}
}
}
public int getPerchChance()
{
if (theBird.worldObj.isRaining())
{
return 1;
}
if (theBird.isNocturnal())
{
if (theBird.worldObj.isDaytime())
{
return PERCH_CHANCE_BASE;
}
else
{
return PERCH_CHANCE_BASE * 10000;
}
}
else
{
if (theBird.worldObj.isDaytime())
{
return PERCH_CHANCE_BASE * 10000;
}
else
{
return PERCH_CHANCE_BASE;
}
}
}
public int getTakeOffChance()
{
if (theBird.worldObj.isRaining())
{
return TAKE_OFF_CHANCE_BASE * 1000;
}
if (theBird.isNocturnal())
{
if (!theBird.worldObj.isDaytime())
{
return TAKE_OFF_CHANCE_BASE;
}
else
{
return TAKE_OFF_CHANCE_BASE * 100;
}
}
else
{
if (!theBird.worldObj.isDaytime())
{
return TAKE_OFF_CHANCE_BASE * 100;
}
else
{
return TAKE_OFF_CHANCE_BASE;
}
}
}
public void considerAttacking()
{
// DEBUG
if (theBird.getAttackTarget() != null) System.out.println("Attack target = "+theBird.getAttackTarget());
// handle case where previous target becomes unsuitable
if (Utilities.isSuitableTarget(theBird, theBird.getAttackTarget(), false))
{
// DEBUG
System.out.println(theBird.getAttackTarget()+" is no longer a suitable target");
theBird.setAttackTarget(null);
}
if (theBird.isTamed())
{
processOwnerAttack();
}
else
{
processNaturalAttack(); // go for its natural prey
}
// check for revenge targets (the getAITarget() method really gives a revenge target)
if (theBird.getAttackTarget() == null && theBird.getAITarget() != null)
{
// DEBUG
System.out.println("There is a revenge target");
theBird.setAttackTarget(theBird.getAITarget());
}
}
// detect if owner has attacked something, if so set attack target to owner's target
public void processOwnerAttack()
{
EntityLivingBase theOwner = theBird.getOwner();
if (theOwner == null)
{
return;
}
else
{
EntityLivingBase possibleTarget = theOwner.getLastAttacker(); // note the get last attacker actually returns last attacked
if (Utilities.isSuitableTarget(theOwner, possibleTarget, true) &&
Utilities.isCourseTraversable(theBird, possibleTarget.posX, possibleTarget.posY, possibleTarget.posZ))
{
// // attack region on ground
// AxisAlignedBB attackRegion = AxisAlignedBB.getBoundingBox(
// theBird.posX - attackRegionSize,
// theBird.worldObj.getHeightValue((int)theBird.posX, (int)theBird.posZ) - attackRegionSize,
// theBird.posZ - attackRegionSize,
// theBird.posX + attackRegionSize,
// theBird.worldObj.getHeightValue((int)theBird.posX, (int)theBird.posZ) + attackRegionSize,
// theBird.posZ + attackRegionSize);
// if (attackRegion.isVecInside(Vec3.createVectorHelper(
// possibleTarget.posX,
// possibleTarget.posY,
// possibleTarget.posZ)))
// {
// DEBUG
System.out.println("Setting eagle target to owners target");
theBird.setAttackTarget(possibleTarget);
// }
}
}
}
// detect if there is an attack target in region on ground directly below eagle
public void processNaturalAttack()
{
// find target on ground
AxisAlignedBB attackRegion = AxisAlignedBB.getBoundingBox(
theBird.posX - attackRegionSize,
theBird.worldObj.getHeightValue((int)theBird.posX, (int)theBird.posZ) - attackRegionSize,
theBird.posZ - attackRegionSize,
theBird.posX + attackRegionSize,
theBird.worldObj.getHeightValue((int)theBird.posX, (int)theBird.posZ) + attackRegionSize,
theBird.posZ + attackRegionSize);
for (int i=0; i<theBird.getPreyArray().length; i++)
{
List possibleTargetEntities = theBird.worldObj.getEntitiesWithinAABB(theBird.getPreyArray()[i], attackRegion);
Iterator<Object> targetIterator = possibleTargetEntities.iterator();
while (targetIterator.hasNext())
{
EntityLivingBase possibleTarget = (EntityLivingBase)(targetIterator.next());
if (Utilities.isCourseTraversable(theBird, possibleTarget.posX, possibleTarget.posY, possibleTarget.posZ))
{
theBird.setAttackTarget(possibleTarget);
}
}
}
}
}