/** * */ package minefantasy.entity; import java.util.List; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import minefantasy.api.tactic.ISpecialSenses; import minefantasy.entity.ai.drake.*; import minefantasy.item.ItemListMF; import minefantasy.system.MF_Calculate; import minefantasy.system.cfg; import minefantasy.system.data_minefantasy; import net.minecraft.block.Block; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityAgeable; import net.minecraft.entity.EntityCreature; import net.minecraft.entity.EntityLeashKnot; import net.minecraft.entity.EntityList; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingData; import net.minecraft.entity.EnumCreatureAttribute; import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.entity.ai.*; import net.minecraft.entity.item.EntityBoat; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.item.EntityItemFrame; import net.minecraft.entity.item.EntityMinecart; import net.minecraft.entity.item.EntityPainting; import net.minecraft.entity.monster.EntitySkeleton; import net.minecraft.entity.passive.EntityVillager; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.ChunkCoordinates; import net.minecraft.util.DamageSource; import net.minecraft.util.MathHelper; import net.minecraft.util.MovingObjectPosition; import net.minecraft.world.World; import net.minecraft.world.WorldType; /**SPEED: DNDspeed * 0.06; *(nonAI) DNDspeed * 0.14; * Human speed: 5DND, = 0.7MC * XP = DNDxp/10 * SPAWN: DNDlvl * 3 * * * Stats: * XP: 20 * Spawn = 300 * Age: 3h20m * Speed 7 * Hp 30 * Dam 4 (2.0H) * @author AnonymousProductions * */ public class EntityDrake extends EntityDaymob implements ISpecialSenses, IGrowable{ private int mouthAngle = 0; public int tailIdleY; public int armScratch; public int scratchTime; public int tailDirY; private int age = 100; private int maxAge = 100; private final int dataWMin = 13; /** * Displays what state of AI the mob is in * @if_0 normal * @if_1 looking for home * @if_2 protect egg */ public byte state; private int[] home = new int[]{0,0,0}; private boolean hasHome; public EntityDrake(World world) { super(world); stepHeight = 1; syncStats(); this.experienceValue = 20; setHealth(this.getMaxHealth()); this.tasks.addTask(0, new EntityAISwimming(this)); this.tasks.addTask(2, new EntityAIAttackOnCollide(this, EntityPlayer.class, 1.0F, true)); this.tasks.addTask(3, new EntityAIAttackOnCollide(this, EntityLiving.class, 1.0F, false)); this.tasks.addTask(3, new EntityAIFindCave(this, 1.0F)); this.tasks.addTask(5, new EntityAIMoveThroughVillage(this, 1.0F, false)); this.tasks.addTask(6, new EntityAIWander(this, 0.8F)); this.tasks.addTask(7, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F)); this.tasks.addTask(7, new EntityAILookIdle(this)); this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false)); this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityPlayer.class, 0, true)); this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityVillager.class, 0, false)); } @Override protected void applyEntityAttributes() { super.applyEntityAttributes(); this.getEntityAttribute(SharedMonsterAttributes.followRange).setAttribute(32.0D); this.getEntityAttribute(SharedMonsterAttributes.movementSpeed).setAttribute(getMoveSpeed()); this.getEntityAttribute(SharedMonsterAttributes.maxHealth).setAttribute(30.0F); } public EntityDrake(World world, double x, double y, double z, int a) { this(world); setPosition(x, y, z); setAge(a); } /** * Will return how many at most can spawn in a chunk at once. */ public int getMaxSpawnedInChunk() { return 2; } private void setAge(int i) { age = i; float scw = getScaleOnAge()*1.5F * getScaleOnBreed(); float sch = getScaleOnAge()*2.5F * getScaleOnBreed(); setSize(scw, sch); posY += 0.02D; this.setSpeed(getMoveSpeed()); this.setMaxHealthStat(this.getMaxHealthStat()); } /** * Takes a coordinate in and returns a weight to determine how likely this creature will try to path to the block. * Args: x, y, z */ public float getBlockPathWeight(int x, int y, int z) { float weight; if(state == 1) { return 0.5F - this.worldObj.getLightBrightness(x, y, z); } return this.worldObj.getBlockId(x, y - 1, z) == Block.grass.blockID ? 10.0F : this.worldObj.getLightBrightness(x, y, z) - 0.5F; } @Override public boolean attackEntityFrom(DamageSource source, int dam) { if(source.getEntity() != null && source.getEntity() instanceof EntityLiving && getLifeStage() <= 1) { AxisAlignedBB search = this.boundingBox.expand(32, 32, 32); List<EntityDrake> friends = worldObj.getEntitiesWithinAABB(EntityDrake.class, search); for(EntityDrake drake : friends) { drake.setAttackTarget((EntityLiving)source.getEntity()); } } return super.attackEntityFrom(source, dam); } @Override public boolean interact(EntityPlayer player) { if(player.getCurrentEquippedItem() == null && this.getLifeStage() == 0)player.swingItem(); if(player.getCurrentEquippedItem() == null && this.getLifeStage() == 0 && rand.nextInt(5) == 0) { player.attackEntityFrom(DamageSource.causeMobDamage(this), 1); mouthAngle = 10; this.playLivingSound(); } return super.interact(player); } public float getMaxHealthStat() { return (int)((30 * getScaleOnAge())*this.getTerritoryBuff()) * getBreedHpMod(); } private float getBreedHpMod() { return 1.0F + (2.0F*getBreed()); } private float getBreedAcMod() { return 1.0F + (1.0F*getBreed()); } private float getBreedDamMod() { return 1.0F + (0.6F*getBreed()); } public void setMaxHealthStat(float f) { this.getEntityAttribute(SharedMonsterAttributes.maxHealth).setAttribute(f); } public void setSpeed(float speed) { this.getEntityAttribute(SharedMonsterAttributes.movementSpeed).setAttribute(speed); } @Override public int getTalkInterval() { return 240; } @Override public void entityInit() { super.entityInit(); dataWatcher.addObject(dataWMin, Integer.valueOf(mouthAngle)); dataWatcher.addObject(dataWMin+1, Integer.valueOf(0)); dataWatcher.addObject(dataWMin+2, Integer.valueOf(age)); dataWatcher.addObject(dataWMin+3, Integer.valueOf(0)); } @SideOnly(Side.CLIENT) public float getMouthAngle() { int angle = mouthAngle; if(angle < -10)angle = -10; if(angle > 10)angle = 10; return angle*2; } @SideOnly(Side.CLIENT) public float getTailYAngle() { int angle = tailIdleY; if(angle > 20)angle = 20; return angle; } @Override public void onLivingUpdate() { super.onLivingUpdate(); if(worldObj.getTotalWorldTime() <= 2) setDead(); if(mouthAngle > 0)mouthAngle --; if(worldObj.isRemote) { mouthAngle = dataWatcher.getWatchableObjectInt(dataWMin); age = dataWatcher.getWatchableObjectInt(dataWMin+2); } else { if(ticksExisted % 20 == 0) { } dataWatcher.updateObject(dataWMin, Integer.valueOf(mouthAngle)); dataWatcher.updateObject(dataWMin+1, Integer.valueOf(isIdle() ? 1 : 0)); dataWatcher.updateObject(dataWMin+2, Integer.valueOf(age)); if(this.ticksExisted % getAgeMultiplier() == 0) { grow(); } if(this.ticksExisted % 100 == 0) { this.heal(1); } if(!hasHome && state == 0 && this.getLifeStage() == 3 && rand.nextInt(8000) == 0) { AxisAlignedBB bb = this.boundingBox.expand(64, 32, 64); if(worldObj.getEntitiesWithinAABB(EntityDrake.class, bb).size() < 5) state = 1; } } //ANIMATE if(worldObj.isRemote) { if(tailDirY == 0)//- { tailIdleY -=3; if(tailIdleY <= -15)tailDirY = 1; } if(tailDirY == 1)//- { tailIdleY +=3; if(tailIdleY >= 15)tailDirY = 0; } if(tailIdleY == 0) { if(rand.nextInt(3) == 0) { tailDirY = 2; } } if(rand.nextInt(100) == 0) { tailDirY = rand.nextInt(1); } if(scratchTime > 0) { if(!isIdle()) { scratchTime = armScratch = 0; } scratchTime --; armScratch --; if(armScratch <= 0) armScratch = 5; } else { if(rand.nextInt(500) == 0 && isIdle()) { scratchTime = 30 + rand.nextInt(50); armScratch = 5; } else { armScratch = 0; } } } if(this.state == 1 && !hasHome) { if(!worldObj.canBlockSeeTheSky((int)posX, (int)posY, (int)posZ)) { int chance = (int)worldObj.getBlockLightValue((int)posX, (int)posY, (int)posZ); if(worldObj.getBlockId((int)posX, (int)posY-1, (int)posZ) == Block.stone.blockID) chance += 9000; if(chance < 5 || rand.nextInt(1000000) < chance) { AxisAlignedBB search = boundingBox.expand(64, 32, 64); List drakes = worldObj.getEntitiesWithinAABB(EntityDrake.class, search); if(drakes.size() < 8) settle((int)posX, (int)posY-1, (int)posZ); } } } if(hasHome) { double distance = this.getDistance(home[0], home[1], home[2]); if(distance > 8) { this.setAttackTarget(null); getNavigator().tryMoveToXYZ(home[0], home[1], home[2], getMoveSpeed()); } } } public float getMoveSpeed() { return 0.5F * this.getScaleOnAge(); } private void syncStats() { this.setSpeed(getMoveSpeed()); this.setMaxHealthStat(this.getMaxHealthStat()); } private int getAgeMultiplier() { return (int)MF_Calculate.getTicksForMinutes(2); } private boolean isIdle() { if(worldObj.isRemote) { return dataWatcher.getWatchableObjectInt(dataWMin+1) == 1; } else { return this.getAttackTarget() == null && getNavigator().noPath(); } } @Override public void playLivingSound() { super.playLivingSound(); mouthAngle = 20; } @Override public int getTotalArmorValue() { return (int)(5 * getScaleOnAge()*getBreedAcMod()); } @Override protected boolean isAIEnabled() { return true; } @Override public boolean getCanSpawnHere() { if(rand.nextInt(10) != 0) { return false; } AxisAlignedBB bb = this.boundingBox.expand(200, 100, 200); List<EntityDrake> list2 = worldObj.getEntitiesWithinAABB(EntityDrake.class, bb); if(list2.size() > 5) { return false; } //if(worldObj.getWorldInfo().getTerrainType() == WorldType.FLAT) //return false; return super.getCanSpawnHere(); } public boolean attackEntityAsMob(Entity entity) { if(!this.canEntityBeSeen(entity))return false; mouthAngle = 11; return super.attackEntityAsMob(entity); } @Override public float getAttackStrength(Entity entity) { return (int)((5 * this.getScaleOnAge()) * getTerritoryBuff() * getBreedDamMod()); } /** * Returns the sound this mob makes while it's alive. */ protected String getLivingSound() { return data_minefantasy.sound("mob.drakeidle"); } /** * Returns the sound this mob makes when it is hurt. */ protected String getHurtSound() { return data_minefantasy.sound("mob.drakehurt"); } /** * Returns the sound this mob makes on death. */ protected String getDeathSound() { return data_minefantasy.sound("mob.drakeidle"); } /** * Plays step sound at given x, y, z for the entity */ protected void playStepSound(int par1, int par2, int par3, int par4) { this.playSound(data_minefantasy.sound("mob.drakestep"), 0.15F, 1.0F); } /** * Returns the item ID for the item the mob drops on death. */ protected int getDropItemId() { return 0; } protected void dropRareDrop(int par1) { } @SideOnly(Side.CLIENT) public float getScratchForDisplay() { int time = 5-armScratch; return (100/5)*time; } /** * (abstract) Protected helper method to write subclass entity data to NBT. */ public void writeEntityToNBT(NBTTagCompound nbt) { super.writeEntityToNBT(nbt); nbt.setInteger("Age", age); nbt.setInteger("Breed", getBreed()); nbt.setByte("DarkeState", state); nbt.setIntArray("Territory", home); nbt.setBoolean("hasHome", hasHome); } /** * (abstract) Protected helper method to read subclass entity data from NBT. */ public void readEntityFromNBT(NBTTagCompound nbt) { super.readEntityFromNBT(nbt); setAge(nbt.getInteger("Age")); state = nbt.getByte("DarkeState"); home = nbt.getIntArray("Territory"); hasHome = nbt.getBoolean("hasHome"); if(nbt.hasKey("Breed")) { setBreed(nbt.getInteger("Breed")); } } @Override public void setAttackTarget(EntityLivingBase entity) { if(getLifeStage() >= 2) super.setAttackTarget(entity); } @Override public int getViewingArc() { return 270; } @Override public int getHearing() { return 0; } @Override public int getSight() { return 15; } @Override public float getTotalScale() { return getScaleOnAge() * getScaleOnBreed() *0.8F; } private float getScaleOnAge() { return age > 10 ? 0.01F * age : 0.1F; } @Override public float getSoundPitch() { float pitchDown = 1F*getScaleOnAge(); return 2F - pitchDown; } @Override public float getSoundVolume() { float volumeUp = 0.75F*getScaleOnAge(); return 0.25F + volumeUp; } /** * Gets the stage of life * @return 0:Hatchling_______1:Young_______2:Juvenile_______3: Adult */ public byte getLifeStage() { if(age > 60) return 3;//ADULT if(age > 30) return 2;//JUVENILE if(age > 10) return 1;//YOUNG if(age > 0) return 0;//HATCHLING return 3;//ADAULT } private void spawnHatchlings() { if(worldObj.isRemote) return; int amount = rand.nextInt(3) + 1; for(int a = 0; a < amount; a++) { EntityDrake hatchling = new EntityDrake(worldObj, posX, posY, posZ, 1); worldObj.spawnEntityInWorld(hatchling); } } public boolean isChild() { return this.getLifeStage() <= 1; } public void grow() { if(hasRoomToGrow() && age < maxAge) { int age2 = age + 1; setAge(age2); } } public boolean hasRoomToGrow() { for (int var1 = 0; var1 < 8; ++var1) { float var2 = ((float)((var1 >> 0) % 2) - 0.5F) * this.width * 1.1F; float var3 = ((float)((var1 >> 1) % 2) - 0.5F) * 0.1F; float var4 = ((float)((var1 >> 2) % 2) - 0.5F) * this.width * 1.1F; int var5 = MathHelper.floor_double(this.posX + (double)var2); int var6 = MathHelper.floor_double(this.posY + (double)this.getEyeHeight() + (double)var3); int var7 = MathHelper.floor_double(this.posZ + (double)var4); if (this.worldObj.isBlockNormalCube(var5, var6, var7)) { return false; } } return true; } private void settle(int x, int y, int z) { if(hasHome) return; if(state == 2) return; System.out.println("Drake Settle"); //SET HOME this.home[0] = x; this.home[1] = y; this.home[2] = z; hasHome = true; //SETS BUFF ON DIFFICULTY //Easy = 50%inc //Normal = 2x //Hard = 3x float buff = getTerritoryBuff(); this.state = 2; int heal = (int)(this.getMaxHealth() * (getTerritoryBuff() - 1F)); heal(heal); this.spawnHatchlings(); } /** * * @return The multiplier of stats when territory owned */ private float getTerritoryBuff() { if(state != 2) return 1F; int diff = worldObj.difficultySetting; float buff = 1.0F; if(diff == 2)buff = 1.5F; if(diff == 3)buff = 2.0F; return buff; } @Override public double getDistanceToSpawn() { return cfg.drakeDistance; } @Override protected void dropFewItems(boolean playerKill, int looting) { int counter; int amount = this.rand.nextInt(2) + this.rand.nextInt(1 + looting) + (int)(getScaleOnAge()); for (counter = 0; counter < amount; ++counter) { if (this.isBurning()) { this.dropItem(ItemListMF.drakeCooked.itemID, 1); } else { this.dropItem(ItemListMF.drakeRaw.itemID, 1); } } amount = (getScaleOnAge()>0.5F ? 1 : 0) + this.rand.nextInt(1 + looting); for (counter = 0; counter < amount; ++counter) { this.dropItem(ItemListMF.misc.itemID, 1, ItemListMF.hideDrake); } } public EntityItem dropItem(int id, int num, int dam) { return this.dropItemWithOffset(id, num, dam, 0.0F); } public EntityItem dropItemWithOffset(int id, int stack, int damage, float offset) { return this.entityDropItem(new ItemStack(id, stack, damage), offset); } @Override public void applyEntityCollision(Entity entity) { if(entity instanceof EntitySkeleton && ridingEntity == null && !entity.isRiding()) { entity.mountEntity(this); this.onUpdate(); entity.onUpdate(); } super.applyEntityCollision(entity); } @Override public double getMountedYOffset() { return (super.getMountedYOffset() + 0.5D)*this.getTotalScale(); } private void setBreed(int i) { dataWatcher.updateObject(dataWMin+3, i); } public int getBreed() { return dataWatcher.getWatchableObjectInt(dataWMin+3); } @Override public EntityLivingData onSpawnWithEgg(EntityLivingData data) { double distance = getDistanceAway(); if(distance > getDistanceToSpawn()*3.0F) { setBreed(2); } else if(distance > getDistanceToSpawn()*2.0F) { setBreed(1); } else { setBreed(0); } setAge(100); if(rand.nextInt(3) == 0) setAge(50); return super.onSpawnWithEgg(data); } private double getDistanceAway() { ChunkCoordinates spawn = worldObj.getSpawnPoint(); return this.getDistance(spawn.posX, spawn.posY, spawn.posZ); } private float getScaleOnBreed() { if(getBreed() == 2) { return 1.1F; } return 1.0F; } @SideOnly(Side.CLIENT) public String getTexture() { int b = getBreed(); if(b == 1) { return "DrakeBlue"; } if(b == 2) { return "DrakeGold"; } return "Drake"; } }