/**
Copyright (C) 2014 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.herdanimals;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityAgeable;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.ai.EntityAIAttackOnCollide;
import net.minecraft.entity.ai.EntityAIFollowParent;
import net.minecraft.entity.ai.EntityAILeapAtTarget;
import net.minecraft.entity.ai.EntityAILookIdle;
import net.minecraft.entity.ai.EntityAIMate;
import net.minecraft.entity.ai.EntityAISwimming;
import net.minecraft.entity.ai.EntityAITempt;
import net.minecraft.entity.ai.EntityAIWander;
import net.minecraft.entity.ai.EntityAIWatchClosest;
import net.minecraft.entity.passive.EntityAnimal;
import net.minecraft.entity.passive.EntityWolf;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Items;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.potion.Potion;
import net.minecraft.util.DamageSource;
import net.minecraft.world.World;
import net.minecraftforge.common.ForgeHooks;
import com.blogspot.jabelarminecraft.wildanimals.entities.IModEntity;
import com.blogspot.jabelarminecraft.wildanimals.entities.ai.herdanimal.EntityAIHurtByTargetHerdAnimal;
import com.blogspot.jabelarminecraft.wildanimals.entities.ai.herdanimal.EntityAIPanicHerdAnimal;
import com.blogspot.jabelarminecraft.wildanimals.utilities.Utilities;
public class EntityHerdAnimal extends EntityAnimal implements IModEntity
{
protected NBTTagCompound syncDataCompound = new NBTTagCompound();
protected static final int REARING_TICKS_MAX = 20;
protected boolean isHitWithoutResistance = false ;
public EntityHerdAnimal(World par1World)
{
super(par1World);
// // DEBUG
// System.out.println("EntityHerdAnimal constructor(), entity.worldObj.isRemote = "+this.worldObj.isRemote);
setSize(0.9F, 1.3F);
initSyncDataCompound();
setupAI();
}
@Override
public void initSyncDataCompound()
{
syncDataCompound.setFloat("scaleFactor", 1.0F);
syncDataCompound.setInteger("rearingCounter", 0);
syncDataCompound.setBoolean("isRearing", false);
}
// set up AI tasks
@Override
public void setupAI()
{
getNavigator().setAvoidsWater(true);
clearAITasks(); // clear any tasks assigned in super classes
tasks.addTask(0, new EntityAISwimming(this));
tasks.addTask(1, new EntityAIPanicHerdAnimal(this));
// the leap and the collide together form an actual attack
tasks.addTask(2, new EntityAILeapAtTarget(this, 0.4F));
tasks.addTask(3, new EntityAIAttackOnCollide(this, 1.0D, true));
tasks.addTask(5, new EntityAIMate(this, 1.0D));
tasks.addTask(6, new EntityAITempt(this, 1.25D, Items.wheat, false));
tasks.addTask(7, new EntityAIFollowParent(this, 1.25D));
tasks.addTask(8, new EntityAIWander(this, 1.0D));
tasks.addTask(9, new EntityAIWatchClosest(this, EntityPlayer.class, 6.0F));
tasks.addTask(10, new EntityAILookIdle(this));
targetTasks.addTask(0, new EntityAIHurtByTargetHerdAnimal(this, true));
}
// use clear tasks for subclasses then build up their ai task list specifically
@Override
public void clearAITasks()
{
tasks.taskEntries.clear();
targetTasks.taskEntries.clear();
}
/**
* Returns true if the newer Entity AI code should be run
*/
@Override
public boolean isAIEnabled()
{
return true;
}
// you don't have to call this as it is called automatically during entityLiving subclass creation
@Override
protected void applyEntityAttributes()
{
super.applyEntityAttributes();
// standard attributes registered to EntityLivingBase
getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue(10.0D);
getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(0.20000000298023224D);
getEntityAttribute(SharedMonsterAttributes.knockbackResistance).setBaseValue(0.8D); // hard to knock back
getEntityAttribute(SharedMonsterAttributes.followRange).setBaseValue(16.0D);
// need to register any additional attributes
getAttributeMap().registerAttribute(SharedMonsterAttributes.attackDamage);
getEntityAttribute(SharedMonsterAttributes.attackDamage).setBaseValue(4.0D);
}
/**
* Returns the sound this mob makes while it's alive.
*/
@Override
protected String getLivingSound()
{
return "mob.cow.say";
}
/**
* Returns the sound this mob makes when it is hurt.
*/
@Override
protected String getHurtSound()
{
return "mob.cow.hurt";
}
/**
* Returns the sound this mob makes on death.
*/
@Override
protected String getDeathSound()
{
return "mob.cow.hurt";
}
// @Override
// protected void func_145780_a(int p_145780_1_, int p_145780_2_, int p_145780_3_, Block p_145780_4_)
// {
// playSound("mob.cow.step", 0.15F, 1.0F);
// }
/**
* Returns the volume for the sounds this mob makes.
*/
@Override
protected float getSoundVolume()
{
return 0.4F;
}
@Override
public void onUpdate()
{
super.onUpdate();
}
@Override
protected Item getDropItem()
{
return Items.leather;
}
/**
* Drop 0-2 items of this living's type. @param par1 - Whether this entity has recently been hit by a player. @param
* par2 - Level of Looting used to kill this mob.
*/
@Override
protected void dropFewItems(boolean par1, int par2)
{
int j = rand.nextInt(3) + rand.nextInt(1 + par2);
int k;
for (k = 0; k < j; ++k)
{
dropItem(Items.leather, 1);
}
j = rand.nextInt(3) + 1 + rand.nextInt(1 + par2);
for (k = 0; k < j; ++k)
{
if (isBurning())
{
dropItem(Items.cooked_beef, 1);
}
else
{
dropItem(Items.beef, 1);
}
}
}
/**
* Called when a player interacts with a mob. e.g. gets milk from a cow, gets into the saddle on a pig.
*/
@Override
public boolean interact(EntityPlayer par1EntityPlayer)
{
ItemStack itemstack = par1EntityPlayer.inventory.getCurrentItem();
if (itemstack != null && itemstack.getItem() == Items.bucket && !par1EntityPlayer.capabilities.isCreativeMode)
{
if (itemstack.stackSize-- == 1)
{
par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, new ItemStack(Items.milk_bucket));
}
else if (!par1EntityPlayer.inventory.addItemStackToInventory(new ItemStack(Items.milk_bucket)))
{
par1EntityPlayer.dropPlayerItemWithRandomChoice(new ItemStack(Items.milk_bucket, 1, 0), false);
}
return true;
}
else
{
return super.interact(par1EntityPlayer);
}
}
@Override
public EntityHerdAnimal createChild(EntityAgeable par1EntityAgeable)
{
return new EntityHerdAnimal(worldObj);
}
/**
* Called when the entity is attacked.
*/
@Override
public boolean attackEntityFrom(DamageSource par1DamageSource, float parDamageAmount)
{
// allow event cancellation
if (ForgeHooks.onLivingAttack(this, par1DamageSource, parDamageAmount)) return false;
if (isEntityInvulnerable())
{
return false; // not really "attacked" if invulnerable
}
else
{
if (worldObj.isRemote) // don't process attack on client side
{
return false;
}
else // on server side so process attack
{
entityToAttack = null;
resetInLove();;
entityAge = 0;
if (getHealth() <= 0.0F) // not really "attacked" if already dead
{
return false;
}
else if (par1DamageSource.isFireDamage() && isPotionActive(Potion.fireResistance)) // fire resistance negates fire attack
{
return false;
}
else
{
// process case of falling anvil
if ((par1DamageSource == DamageSource.anvil || par1DamageSource == DamageSource.fallingBlock) && getEquipmentInSlot(4) != null)
{
getEquipmentInSlot(4).damageItem((int)(parDamageAmount * 4.0F + rand.nextFloat() * parDamageAmount * 2.0F), this);
parDamageAmount *= 0.75F;
}
limbSwingAmount = 1.5F;
isHitWithoutResistance = true;
// process temporary resistance to damage after last damage
if (hurtResistantTime > maxHurtResistantTime / 2.0F) // more than half of max resistance time left
{
if (parDamageAmount <= lastDamage) // resist damage that is less than the last damage
{
return false;
}
// top up the damage to the larger amount during the resistance period
damageEntity(par1DamageSource, parDamageAmount - lastDamage);
lastDamage = parDamageAmount;
isHitWithoutResistance = false;
}
else // no resistance so normal hit
{
lastDamage = parDamageAmount;
prevHealth = getHealth();
hurtResistantTime = maxHurtResistantTime; // start the resistance period
damageEntity(par1DamageSource, parDamageAmount);
hurtTime = maxHurtTime = 10;
setRearing(true);
}
// process based on what is attacking
attackedAtYaw = 0.0F;
Entity entity = par1DamageSource.getEntity();
if (entity != null)
{
if (entity instanceof EntityLivingBase) // set revenge on any living entity that attacks
{
// DEBUG
System.out.println("Setting revenge target = "+entity.getClass().getSimpleName());
setRevengeTarget((EntityLivingBase)entity);
// DEBUG
System.out.println("Attack target = "+this.getAITarget().getClass().getSimpleName());
}
if (entity instanceof EntityPlayer) // identify attacking player or wolf with kill time determination
{
recentlyHit = 100;
attackingPlayer = (EntityPlayer)entity;
}
else if (entity instanceof EntityWolf)
{
EntityWolf entitywolf = (EntityWolf)entity;
if (entitywolf.isTamed())
{
recentlyHit = 100;
attackingPlayer = null;
}
}
}
if (isHitWithoutResistance)
{
worldObj.setEntityState(this, (byte)2);
// process knockback
if (par1DamageSource != DamageSource.drown)
{
setBeenAttacked(); // checks against knockback resistance, really should be merged into knockback() method
}
if (entity != null) // if damage was done by an entity
{
double d1 = entity.posX - posX;
double d0;
for (d0 = entity.posZ - posZ; d1 * d1 + d0 * d0 < 1.0E-4D; d0 = (Math.random() - Math.random()) * 0.01D)
{
d1 = (Math.random() - Math.random()) * 0.01D;
}
attackedAtYaw = (float)(Math.atan2(d0, d1) * 180.0D / Math.PI) - rotationYaw;
knockBack(entity, parDamageAmount, d1, d0);
}
else // not an entity that caused damage
{
attackedAtYaw = (int)(Math.random() * 2.0D) * 180;
}
}
// play sounds for hurt or death
// isHitWithoutResistance check helps ensure sound is played once and has time to complete
String s;
if (getHealth() <= 0.0F) // dead
{
s = getDeathSound();
if (isHitWithoutResistance && s != null)
{
playSound(s, getSoundVolume(), getSoundPitch());
}
onDeath(par1DamageSource);
}
else // hurt
{
s = getHurtSound();
if (isHitWithoutResistance && s != null)
{
playSound(s, getSoundVolume(), getSoundPitch());
}
}
return true;
}
}
}
}
@Override
public boolean attackEntityAsMob(Entity par1Entity)
{
return par1Entity.attackEntityFrom(DamageSource.causeMobDamage(this), (float) getEntityAttribute(SharedMonsterAttributes.attackDamage).getBaseValue());
}
// *****************************************************
// ENCAPSULATION SETTER AND GETTER METHODS
// Don't forget to send sync packets in setters
// *****************************************************
public void setRearing(Boolean parSetRearing)
{
if (parSetRearing && getAITarget()==null) // don't rear if already has target
{
setRearingCounter(REARING_TICKS_MAX);
syncDataCompound.setBoolean("isRearing", true);
// DEBUG
System.out.println("Rearing instead of fleeing");
System.out.println("rearingCounter = "+getRearingCounter());
}
else
{
setRearingCounter(0);
syncDataCompound.setBoolean("isRearing", false);
// DEBUG
System.out.println("Finished Rearing");
System.out.println("rearingCounter = "+getRearingCounter());
}
// don't forget to sync client and server
sendEntitySyncPacket();
}
public boolean isRearing()
{
return syncDataCompound.getBoolean("isRearing");
}
@Override
public void setScaleFactor(float parScaleFactor)
{
syncDataCompound.setFloat("scaleFactor", Math.abs(parScaleFactor));
// don't forget to sync client and server
sendEntitySyncPacket();
}
@Override
public float getScaleFactor()
{
return syncDataCompound.getFloat("scaleFactor");
}
public void setRearingCounter(int parTicks)
{
syncDataCompound.setInteger("rearingCounter", parTicks);
// don't forget to sync client and server
sendEntitySyncPacket();
}
public void decrementRearingCounter()
{
syncDataCompound.setInteger("rearingCounter", getRearingCounter()-1);
// don't forget to sync client and server
sendEntitySyncPacket();
}
public int getRearingCounter()
{
return syncDataCompound.getInteger("rearingCounter");
}
public boolean isRearingFirstTick()
{
return (syncDataCompound.getInteger("rearingCounter")==REARING_TICKS_MAX);
}
@Override
public void sendEntitySyncPacket()
{
Utilities.sendEntitySyncPacketToClient(this);
}
@Override
public NBTTagCompound getSyncDataCompound()
{
return syncDataCompound;
}
@Override
public void setSyncDataCompound(NBTTagCompound parCompound)
{
syncDataCompound = parCompound;
}
}