/** Copyright (C) <2017> <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.mobs; import io.netty.buffer.ByteBuf; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.IRangedAttackMob; import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.DamageSource; import net.minecraft.util.Vec3; import net.minecraft.world.World; import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import zeldaswordskills.api.entity.ai.EntityAIDynamicRangedAction; import zeldaswordskills.api.entity.ai.EntityAction; import zeldaswordskills.api.entity.ai.IEntityDynamicAI; import zeldaswordskills.entity.projectile.EntityMagicSpell; import zeldaswordskills.ref.Sounds; import zeldaswordskills.util.BiomeType; import zeldaswordskills.util.WorldUtils; public class EntityDekuFire extends EntityDekuBaba implements IEntityAdditionalSpawnData, IRangedAttackMob { /** * Returns array of default biomes in which this entity may spawn naturally */ public static String[] getDefaultBiomes() { return BiomeType.getBiomeArray(new String[]{"hell"}, BiomeType.FIERY, BiomeType.JUNGLE, BiomeType.PLAINS); } public static final EntityAction ACTION_SPIT = new EntityAction(EntityDekuBase.flag_index++, 16, 7); static { EntityDekuBaba.registerAction(ACTION_SPIT); } /** Time it takes for the gland severing animation to complete */ public static final int GLAND_DURATION = 20; /** Health update flag indicating gland has been severed */ protected static final byte GLAND_FLAG = EntityDekuBase.flag_index++; private boolean has_gland; /** Timer used client side for animating the gland falling off */ public int gland_timer; public EntityDekuFire(World world) { super(world); this.has_gland = true; this.isImmuneToFire = true; } /** * Whether this Fire Baba still has its gland */ public boolean hasGland() { return has_gland; } @Override protected void applyEntityAttributes() { super.applyEntityAttributes(); this.getEntityAttribute(SharedMonsterAttributes.followRange).setBaseValue(40.0D); } @Override protected void addAITasks() { super.addAITasks(); this.tasks.addTask(2, new EntityAIDynamicRangedAction<EntityDekuFire>(this, ACTION_SPIT, 4.0F, 40.0F, 20, 60, 0.0F, true).setDifficultyScaled()); } @Override public float getActionSpeed(int action_id) { int i = getDifficultyModifier() - 2; if (action_id == ACTION_SPIT.id) { return 0.6F + (i * 0.1F); } return super.getActionSpeed(action_id); } @Override public boolean canExecute(int action_id, IEntityDynamicAI ai) { if (action_id == ACTION_SPIT.id && !hasGland()) { return false; } return super.canExecute(action_id, ai); } @Override public void performAction(int action_id, IEntityDynamicAI ai) { Entity target = getCurrentTarget(); if (isConfused()) { // do nothing } else if (action_id == ACTION_SPIT.id) { if (target instanceof EntityLivingBase) { attackEntityWithRangedAttack((EntityLivingBase) target, 1.0F); } } else { super.performAction(action_id, ai); } } /** * @param rangeRatio Distance between the target and shooter divided by the shooter's maximum range, between 0.1F and 1.0F but not used here */ @Override public void attackEntityWithRangedAttack(EntityLivingBase target, float rangeRatio) { int difficulty = worldObj.getDifficulty().getDifficultyId(); EntityMagicSpell spell = new EntityMagicSpell(worldObj, this, (EntityLivingBase) target, 0.375F + (0.125F * (float) difficulty), (float)(14 - difficulty * 4)); spell.setGravityVelocity(0.01F); // Adjust position to account for animation head position float d = 1.5F; Vec3 vec3 = this.getLook(1.0F); spell.posX = this.posX + vec3.xCoord * d; spell.posY = this.posY + (double)(this.height / 2.0F) + 0.5D; spell.posZ = this.posZ + vec3.zCoord * d; spell.setArea(0.4F + (0.2F * difficulty)); float damage = (float) this.getEntityAttribute(SharedMonsterAttributes.attackDamage).getAttributeValue(); spell.setDamage(damage); spell.setReflectChance(1.0F); spell.disableGriefing(); spell.disableTrailingParticles(); if (!worldObj.isRemote) { WorldUtils.playSoundAtEntity(this, Sounds.SPIT, 0.4F, 0.7F); worldObj.spawnEntityInWorld(spell); } } @Override public boolean isAttack(int action_id) { return action_id == ACTION_SPIT.id || super.isAttack(action_id); } @Override public void onUpdate() { super.onUpdate(); if (gland_timer != 0) { gland_timer += (gland_timer < 0 ? 1 : -1); if (gland_timer == 0) { has_gland = false; // set to false client side only after timer so it can still render } } } @Override protected float getSlashDamage(DamageSource source, float amount) { float damage = super.getSlashDamage(source, amount); if (has_gland && isSlashing(source)) { damage *= 0.25F; } return damage; } @Override protected void onProneAttack(DamageSource source, float amount) { if (!worldObj.isRemote && isSlashing(source)) { has_gland = false; // set to false immediately on server worldObj.setEntityState(this, GLAND_FLAG); } super.onProneAttack(source, amount); } @Override @SideOnly(Side.CLIENT) public void handleStatusUpdate(byte flag) { if (flag == GLAND_FLAG) { gland_timer = (rand.nextFloat() < 0.5F ? GLAND_DURATION : -GLAND_DURATION); } else { super.handleStatusUpdate(flag); } } @Override public void writeToNBT(NBTTagCompound compound) { super.writeToNBT(compound); compound.setBoolean("has_gland", has_gland); } @Override public void readFromNBT(NBTTagCompound compound) { super.readFromNBT(compound); this.has_gland = compound.getBoolean("has_gland"); } @Override public void writeSpawnData(ByteBuf buffer) { buffer.writeBoolean(has_gland); } @Override public void readSpawnData(ByteBuf buffer) { has_gland = buffer.readBoolean(); } }