/** 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.birdsofprey; import java.util.UUID; import net.minecraft.block.BlockColored; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityFlying; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.entity.passive.EntityBat; import net.minecraft.entity.passive.EntityChicken; 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.scoreboard.Team; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.DamageSource; import net.minecraft.world.World; import com.blogspot.jabelarminecraft.wildanimals.entities.IModEntity; import com.blogspot.jabelarminecraft.wildanimals.entities.ai.birdofprey.AIStates; import com.blogspot.jabelarminecraft.wildanimals.entities.ai.birdofprey.ProcessStateBirdOfPrey; import com.blogspot.jabelarminecraft.wildanimals.entities.ai.birdofprey.UpdateStateBirdOfPrey; import com.blogspot.jabelarminecraft.wildanimals.entities.serpents.EntitySerpent; import com.blogspot.jabelarminecraft.wildanimals.utilities.Utilities; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; public class EntityBirdOfPrey extends EntityFlying implements IModEntity { protected NBTTagCompound syncDataCompound = new NBTTagCompound(); public ProcessStateBirdOfPrey aiHelper; public UpdateStateBirdOfPrey aiUpdateState; // use fields for sounds to allow easy changes in child classes protected String soundHurt = "wildanimals:mob.birdsofprey.death"; protected String soundDeath = "wildanimals:mob.birdsofprey.death"; protected String soundCall = "wildanimals:mob.birdsofprey.cry"; protected String soundFlapping = "wildanimals:mob.birdsofprey.flapping"; // to ensure that multiple entities don't get synced // create a random factor per entity protected int randFactor; private Class[] preyArray = new Class[] {EntityChicken.class, EntityBat.class, EntitySerpent.class}; private final double TAMED_HEALTH = 30.0D; public EntityBirdOfPrey(World parWorld) { super(parWorld); // DEBUG System.out.println("EntityBirdOfPrey constructor(), "+"on Client=" +parWorld.isRemote+", EntityID = "+getEntityId()+", ModEntityID = "+entityUniqueID); setSize(1.0F, 1.0F); randFactor = rand.nextInt(10); // DEBUG System.out.println("randFactor = "+randFactor); setupAI(); initSyncDataCompound(); } @Override public void initSyncDataCompound() { syncDataCompound.setFloat("scaleFactor", 1.0F); syncDataCompound.setInteger("state", AIStates.STATE_TAKING_OFF); syncDataCompound.setInteger("stateCounter", 0); syncDataCompound.setBoolean("soarClockwise", worldObj.rand.nextBoolean()); syncDataCompound.setDouble("soarHeight", 126-randFactor); syncDataCompound.setInteger("stateCounter", 0); syncDataCompound.setDouble("anchorX", posX); syncDataCompound.setDouble("anchorY", posY); syncDataCompound.setDouble("anchorZ", posZ); syncDataCompound.setString("ownerUUIDString", ""); syncDataCompound.setInteger("legBandColor", 0); } // use clear tasks then build up their custom ai task list specifically @Override public void clearAITasks() { tasks.taskEntries.clear(); targetTasks.taskEntries.clear(); } @Override public void setupAI() { getNavigator().setAvoidsWater(true); clearAITasks(); // clear any tasks assigned in super classes aiHelper = new ProcessStateBirdOfPrey(this); aiUpdateState = new UpdateStateBirdOfPrey(this); } // you don't have to call this as it is called automatically during entityLiving subclass creation @Override protected void applyEntityAttributes() { super.applyEntityAttributes(); getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(0.5D); getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue(8.0D); // need to register any additional attributes getAttributeMap().registerAttribute(SharedMonsterAttributes.attackDamage); getEntityAttribute(SharedMonsterAttributes.attackDamage).setBaseValue(3.0D); } /** * Returns true if the newer Entity AI code should be run */ @Override public boolean isAIEnabled() { return true; } /** * This process the current state. */ @Override protected void updateAITick() { if (ticksExisted == 10) { // note that the setTamed also forces a full NBT sync to client String ownerUUIDString = syncDataCompound.getString("ownerUUIDString"); if (ownerUUIDString != "") { setOwnerUUIDString(ownerUUIDString); } else { setOwnerUUIDString(""); } } aiHelper.updateAITick(); } /** * This checks whether state should change */ @Override protected void updateAITasks() { super.updateAITasks(); aiUpdateState.updateAIState(); } @Override public AxisAlignedBB getBoundingBox() { return AxisAlignedBB.getBoundingBox(boundingBox.minX, boundingBox.minY, boundingBox.minZ, boundingBox.maxX, boundingBox.maxY, boundingBox.maxZ); } @Override @SideOnly(Side.CLIENT) public boolean isInRangeToRenderDist(double parDistance) { return true; // need to see them even when far away or high above } /** * returns if this entity triggers Block.onEntityWalking on the blocks they walk on. used for spiders and wolves to * prevent them from trampling crops */ @Override protected boolean canTriggerWalking() { return false; } /** * Called when the mob is falling. Calculates and applies fall damage. */ @Override protected void fall(float p_70069_1_) { } /** * Takes in the distance the entity has fallen this tick and whether its on the ground to update the fall distance * and deal fall damage if landing on the ground. Args: distanceFallenThisTick, onGround */ @Override protected void updateFallState(double p_70064_1_, boolean p_70064_3_) { } /** * Return whether this entity should NOT trigger a pressure plate or a tripwire. */ @Override public boolean doesEntityNotTriggerPressurePlate() { return true; } @Override public boolean allowLeashing() { return false; } @Override public boolean canAttackClass(Class parClass) { return true; } @Override public Item getDropItem() { return Items.feather; } @Override protected void dropFewItems(boolean parRecentlyHitByPlayer, int parLootLevel) { dropItem(Items.feather, parLootLevel+1); return; } @Override public boolean isNoDespawnRequired() { return isTamed(); } /** * Sets the active target the Task system uses for tracking */ @Override public void setAttackTarget(EntityLivingBase theTargetEntity) { // DEBUG System.out.println("Setting attack target to = "+theTargetEntity); super.setAttackTarget(theTargetEntity); } @Override protected void entityInit() { super.entityInit(); } @Override public int getTalkInterval() { return 400; } // @Override // // play step sound // protected void func_145780_a(int p_145780_1_, int p_145780_2_, int p_145780_3_, Block p_145780_4_) // { // // birds are silent when moving // } /** * Returns the sound this mob makes while it's alive. */ @Override protected String getLivingSound() { if (getState() == AIStates.STATE_TAKING_OFF || getState() == AIStates.STATE_TRAVELLING) { return soundFlapping; } else { return soundCall; } } /** * Returns the sound this mob makes when it is hurt. */ @Override protected String getHurtSound() { return soundHurt; } /** * Returns the sound this mob makes on death. */ @Override protected String getDeathSound() { return soundDeath; } /** * Returns the volume for the sounds this mob makes. */ @Override protected float getSoundVolume() { return 0.3F; } /** * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons * use this to react to sunlight and start to burn. */ @Override public void onLivingUpdate() { super.onLivingUpdate(); } /** * Called to update the entity's position/logic. */ @Override public void onUpdate() { super.onUpdate(); } /** * Called when the entity is attacked. */ @Override public boolean attackEntityFrom(DamageSource parDamageSource, float parDamageAmount) { if (isEntityInvulnerable()) { return false; } else { boolean result = super.attackEntityFrom(parDamageSource, parDamageAmount); if (parDamageSource.getEntity() instanceof EntityLivingBase) { setRevengeTarget((EntityLivingBase) parDamageSource.getEntity()); } // DEBUG System.out.println("Eagle has been attacked by "+parDamageSource.getEntity()+" with source = "+parDamageSource.getSourceOfDamage()+" and revenge target set to "+getAITarget()); return result; } } @Override public boolean attackEntityAsMob(Entity parEntity) { return parEntity.attackEntityFrom(DamageSource.causeMobDamage(this), (float)getEntityAttribute(SharedMonsterAttributes.attackDamage).getAttributeValue()); } /** * 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 parPlayer) { // DEBUG System.out.println("EntityBirdOfPrey interact()"); ItemStack itemInHand = parPlayer.inventory.getCurrentItem(); if (isTamed()) { if (itemInHand != null) { if (itemInHand.getItem() == Items.dye) { int i = BlockColored.func_150032_b(itemInHand.getMetadata()); if (i != getLegBandColor()) { setLegBandColor(i); if (!parPlayer.capabilities.isCreativeMode) { --itemInHand.stackSize; } return true; } } } } else if (itemInHand != null) { // check if raw salmon if (isTamingFood(itemInHand)) { // DEBUG System.out.println("Trying taming food"); if (rand.nextInt(3) == 0) { setTamed(parPlayer); // DEBUG System.out.println("It likes the raw salmon"); if (!parPlayer.capabilities.isCreativeMode) { --itemInHand.stackSize; } } else { spawnTamingParticles(false); } } } return super.interact(parPlayer); } public boolean isTamingFood(ItemStack parItemStack) { // check for raw salmon return (parItemStack.getItem() == Items.fish && parItemStack.getMetadata() == 1); } /** * Will return how many at most can spawn in a chunk at once. */ @Override public int getMaxSpawnedInChunk() { return 8; } /** * Determines if an entity can be despawned, used on idle far away entities */ @Override protected boolean canDespawn() { return false; } @Override public void setDead() { // DEBUG System.out.println("Setting dead"); super.setDead(); } @Override public Team getTeam() { if (getOwner() != null) { EntityLivingBase entityLivingBase = getOwner(); if (entityLivingBase != null) { return entityLivingBase.getTeam(); } } return super.getTeam(); } @Override public boolean isOnSameTeam(EntityLivingBase parEntityLivingBase) { if (getOwner() != null) { EntityLivingBase entityOwner = getOwner(); if (parEntityLivingBase == entityOwner) { return true; } if (entityOwner != null) { return entityOwner.isOnSameTeam(parEntityLivingBase); } } return super.isOnSameTeam(parEntityLivingBase); } /** * Play the taming effect, will either be hearts or smoke depending on status */ protected void spawnTamingParticles(boolean shouldSpawnHearts) { if (worldObj.isRemote) { return; } String particleType = "heart"; if (!shouldSpawnHearts) { particleType = "smoke"; } for (int i = 0; i < 7; ++i) { double d0 = rand.nextGaussian() * 0.02D; double d1 = rand.nextGaussian() * 0.02D; double d2 = rand.nextGaussian() * 0.02D; worldObj.spawnParticle(particleType, posX + rand.nextFloat() * width * 2.0F - width, posY + 0.5D + rand.nextFloat() * height, posZ + rand.nextFloat() * width * 2.0F - width, d0, d1, d2); } } /* * Indicates whether the bird is more active during night time. * Affects tendency to perch and take off from perch */ public boolean isNocturnal() { return false; } /* * Indicates proportions of the head */ public boolean isOwlType() { return false; } public boolean isTamed() { return (getOwner() != null); } @Override public void writeToNBT(NBTTagCompound parCompound) { // // DEBUG // System.out.println("Writing NBT"); super.writeToNBT(parCompound); parCompound.setTag("extendedPropsJabelar", syncDataCompound); } @Override public void readFromNBT(NBTTagCompound parCompound) { // DEBUG System.out.println("Reading NBT"); super.readFromNBT(parCompound); syncDataCompound = (NBTTagCompound) parCompound.getTag("extendedPropsJabelar"); // DEBUG System.out.println("State = "+getState()); sendEntitySyncPacket(); } // ***************************************************** // ENCAPSULATION SETTER AND GETTER METHODS // Don't forget to send sync packets in setters // ***************************************************** @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 setOwnerUUIDString(String parOwnerUUIDString) { // DEBUG System.out.println("Setting new owner with UUID ="+parOwnerUUIDString); syncDataCompound.setString("ownerUUIDString", parOwnerUUIDString); // don't forget to sync client and server sendEntitySyncPacket(); } public String getOwnerUUIDString() { return syncDataCompound.getString("ownerUUIDString"); } public void setState(int parState) { // DEBUG System.out.println("EntityBirdOfPrey setState() state changed to "+parState); syncDataCompound.setInteger("state", parState); // don't forget to sync client and server sendEntitySyncPacket(); } public int getState() { return syncDataCompound.getInteger("state"); } public void setStateCounter(int parCount) { syncDataCompound.setInteger("stateCounter", parCount); // don't forget to sync client and server sendEntitySyncPacket(); } public void decrementStateCounter() { syncDataCompound.setInteger("stateCounter", syncDataCompound.getInteger("stateCounter")-1); // don't forget to sync client and server sendEntitySyncPacket(); } public int getStateCounter() { return syncDataCompound.getInteger("stateCounter"); } public void setAnchor(double parX, double parY, double parZ) { syncDataCompound.setDouble("anchorX", parX); syncDataCompound.setDouble("anchorY", parY); syncDataCompound.setDouble("anchorZ", parZ); // don't forget to sync client and server sendEntitySyncPacket(); } public double getAnchorX() { return syncDataCompound.getDouble("anchorX"); } public double getAnchorY() { return syncDataCompound.getDouble("anchorY"); } public double getAnchorZ() { return syncDataCompound.getDouble("anchorZ"); } public EntityPlayer getOwner() { try { UUID uuid = UUID.fromString(getOwnerUUIDString()); return uuid == null ? null : Utilities.getPlayerOnServerFromUUID(uuid); } catch (IllegalArgumentException illegalargumentexception) { return null; } } public boolean setTamed(EntityLivingBase parNewOwner) { if (getOwner() != null) { // DEBUG System.out.println("There is already an owner"); return false; } else if (parNewOwner == null) { // DEBUG System.out.println("Trying to assign a null owner"); return false; } else { spawnTamingParticles(true); setAttackTarget(null); getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue(TAMED_HEALTH); setOwnerUUIDString(parNewOwner.getUniqueID().toString()); return true; } } public boolean isOwner(EntityLivingBase parEntity) { return parEntity == getOwner(); } @Override public void sendEntitySyncPacket() { Utilities.sendEntitySyncPacketToClient(this); } public void setSoarClockwise(boolean parClockwise) { syncDataCompound.setBoolean("soarClockwise", parClockwise); // don't forget to sync client and server sendEntitySyncPacket(); } public boolean getSoarClockwise() { return syncDataCompound.getBoolean("soarClockwise"); } public void setSoarHeight(double parHeight) { syncDataCompound.setDouble("soarHeight", parHeight); // don't forget to sync client and server sendEntitySyncPacket(); } public double getSoarHeight() { return syncDataCompound.getInteger("soarHeight"); } @Override public NBTTagCompound getSyncDataCompound() { return syncDataCompound; } @Override public void setSyncDataCompound(NBTTagCompound parCompound) { syncDataCompound = parCompound; } public int getRandFactor() { return randFactor; } public Class[] getPreyArray() { return preyArray; } public void setPreyArray(Class[] parPreyArray) { preyArray = parPreyArray; } public int getLegBandColor() { return syncDataCompound.getByte("legBandColor"); } public void setLegBandColor(int parLegBandColor) { syncDataCompound.setByte("legBandColor", (byte) parLegBandColor); // don't forget to sync client and server sendEntitySyncPacket(); } }