/**
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 java.util.List;
import java.util.UUID;
import net.minecraft.block.Block;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.IEntityLivingData;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.ai.EntityAIAttackOnCollide;
import net.minecraft.entity.ai.EntityAIHurtByTarget;
import net.minecraft.entity.ai.EntityAILookIdle;
import net.minecraft.entity.ai.EntityAIMoveTowardsRestriction;
import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
import net.minecraft.entity.ai.EntityAISwimming;
import net.minecraft.entity.ai.EntityAIWander;
import net.minecraft.entity.ai.EntityAIWatchClosest;
import net.minecraft.entity.ai.attributes.AttributeModifier;
import net.minecraft.entity.ai.attributes.IAttributeInstance;
import net.minecraft.entity.monster.EntityMob;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Items;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.BlockPos;
import net.minecraft.util.DamageSource;
import net.minecraft.util.MathHelper;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import zeldaswordskills.ZSSAchievements;
import zeldaswordskills.api.block.IWhipBlock.WhipType;
import zeldaswordskills.api.damage.IDamageAoE;
import zeldaswordskills.api.entity.IEntityBackslice;
import zeldaswordskills.api.entity.IEntityEvil;
import zeldaswordskills.api.entity.IEntityLootable;
import zeldaswordskills.api.entity.IParryModifier;
import zeldaswordskills.api.item.ArmorIndex;
import zeldaswordskills.entity.IEntityVariant;
import zeldaswordskills.entity.ai.EntityAIPowerAttack;
import zeldaswordskills.entity.ai.IPowerAttacker;
import zeldaswordskills.entity.player.ZSSPlayerSkills;
import zeldaswordskills.item.ItemTreasure.Treasures;
import zeldaswordskills.item.ZSSItems;
import zeldaswordskills.ref.Config;
import zeldaswordskills.ref.Sounds;
import zeldaswordskills.skills.SkillBase;
import zeldaswordskills.skills.sword.Parry;
import zeldaswordskills.util.BiomeType;
import zeldaswordskills.util.TargetUtils;
import zeldaswordskills.util.WorldUtils;
public class EntityDarknut extends EntityMob implements IEntityBackslice, IEntityEvil, IEntityLootable, IParryModifier, IPowerAttacker, IEntityVariant
{
/**
* Returns array of default biomes in which this entity may spawn naturally
*/
public static String[] getDefaultBiomes() {
return BiomeType.getBiomeArray(null, BiomeType.ARID, BiomeType.BEACH, BiomeType.FIERY, BiomeType.MOUNTAIN, BiomeType.PLAINS);
}
/** Bonus to knockback resistance when wearing armor */
private static final UUID armorKnockbackModifierUUID = UUID.fromString("71AF0F88-82E5-49DE-B9CC-844048E33D69");
private static final AttributeModifier armorKnockbackModifier = (new AttributeModifier(armorKnockbackModifierUUID, "Armor Knockback Resistance", 1.0D, 0)).setSaved(false);
/** Movement bonus when not wearing armor */
private static final UUID armorMoveBonusUUID = UUID.fromString("B6C8CCB6-AE7B-4F14-908A-2F41BDB4D720");
private static final AttributeModifier armorMoveBonus = (new AttributeModifier(armorMoveBonusUUID, "Armor Movement Bonus", 0.35D, 1)).setSaved(false);
/** Power Attack AI only used while wearing armor */
private final EntityAIPowerAttack powerAttackAI;
/** Attack flag for model animations, updated via health update */
private static final byte ATTACK_FLAG = 0x5;
/** Flag for model to animate power attack charging up, updated via health update */
private static final byte POWER_UP_FLAG = 0x6;
/** Flag for model to animate power attack swing, updated via health update */
private static final byte POWER_ATTACK_FLAG = 0x7;
/** Flag for model to animate parry motion, updated via health update */
private static final byte PARRY_FLAG = 0x8;
/** Flag for model to animate spin attack motion, updated via health update */
private static final byte SPIN_FLAG = 0x9;
/** DataWatcher for Darknut type: 0 - normal, 1 - Mighty */
private final static int TYPE_INDEX = 16;
/** DataWatcher for armor health */
private final static int ARMOR_INDEX = 17;
/** DataWatcher for cape */
private final static int CAPE_INDEX = 18;
/** Replacement for removed 'attackTime' */
protected int attackTime;
/** Timer for attack animation; negative swings one way, positive the other */
@SideOnly(Side.CLIENT)
public int attackTimer;
/** Timer for power attack charging animation */
@SideOnly(Side.CLIENT)
public int chargeTimer;
/** Flag set when performing a power attack swing, so can use same attack timer */
@SideOnly(Side.CLIENT)
public boolean isPowerAttack;
/** Highest parry chance when this timer is zero; set to 10 after each parry */
public int parryTimer;
/** Recently hit timer */
private int recentHitTimer;
/** Number of successive hits that fell within recent hit time */
private int recentHits;
/** Timer for spin attack */
private int spinAttackTimer;
/** List of spin attack targets */
private List<EntityLivingBase> targets;
public EntityDarknut(World world) {
super(world);
powerAttackAI = getNewPowerAttackAI();
tasks.addTask(0, new EntityAISwimming(this));
tasks.addTask(2, new EntityAIAttackOnCollide(this, EntityPlayer.class, 1.0D, false));
tasks.addTask(4, new EntityAIMoveTowardsRestriction(this, 1.0D));
tasks.addTask(5, new EntityAIWander(this, 1.0D));
tasks.addTask(6, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
tasks.addTask(6, new EntityAILookIdle(this));
targetTasks.addTask(1, new EntityAIHurtByTarget(this, true));
targetTasks.addTask(2, new EntityAINearestAttackableTarget<EntityPlayer>(this, EntityPlayer.class, true));
setSize(1.0F, 3.0F);
experienceValue = 12;
}
protected EntityAIPowerAttack getNewPowerAttackAI() {
return new EntityAIPowerAttack(this, 4.0D);
}
@Override
public void entityInit() {
super.entityInit();
dataWatcher.addObject(TYPE_INDEX, (byte) 0);
dataWatcher.addObject(ARMOR_INDEX, 20.0F);
dataWatcher.addObject(CAPE_INDEX, (byte) 0);
}
@Override
protected void applyEntityAttributes() {
super.applyEntityAttributes();
getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue(50.0D);
getEntityAttribute(SharedMonsterAttributes.attackDamage).setBaseValue(5.0D); // unarmed
getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(0.225D);
getEntityAttribute(SharedMonsterAttributes.knockbackResistance).setBaseValue(0.25D);
getEntityAttribute(SharedMonsterAttributes.followRange).setBaseValue(40.0D);
}
/**
* Returns the Darknut's type: 0 - normal, 1 - Mighty
*/
public int getType() {
return (int) dataWatcher.getWatchableObjectByte(TYPE_INDEX);
}
/**
* Sets the Darknut's type: 0 - normal, 1 - Mighty
*/
@Override
public EntityDarknut setType(int type) {
dataWatcher.updateObject(TYPE_INDEX, (byte) type);
setWearingCape((type > 0 ? (byte) 60 : (byte) 0));
getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue((type > 0 ? 100.0D : 50.0D));
getEntityAttribute(SharedMonsterAttributes.attackDamage).setBaseValue((type > 0 ? 7.0D : 5.0D));
setHealth(getMaxHealth());
experienceValue = (type > 0 ? 20 : 12);
return this;
}
protected float getArmorDamage() {
return dataWatcher.getWatchableObjectFloat(ARMOR_INDEX);
}
protected void setArmorDamage(float value) {
dataWatcher.updateObject(ARMOR_INDEX, value);
}
/**
* Returns true if the Darknut's original armor is still intact
*/
public boolean isArmored() {
return getArmorDamage() > 0;
}
public boolean isSpinning() {
return spinAttackTimer > 0;
}
@Override
protected String getLivingSound() {
return Sounds.DARKNUT_LIVING;
}
@Override
protected String getHurtSound() {
return Sounds.DARKNUT_HIT;
}
@Override
protected String getDeathSound() {
return Sounds.DARKNUT_DIE;
}
@Override
public void setEquipmentBasedOnDifficulty(DifficultyInstance difficulty) {
// don't use super.addRandomArmor, as Darknuts always have certain equipment
setCurrentItemOrArmor(0, new ItemStack(ZSSItems.swordDarknut));
setCurrentItemOrArmor(ArmorIndex.EQUIPPED_CHEST, new ItemStack(Items.iron_chestplate));
applyArmorAttributeModifiers(true);
}
/**
* Adds or removes knockback resistance and movement attribute modifiers for wearing armor
* Also adds / removes specific AI tasks
*/
private void applyArmorAttributeModifiers(boolean wearingArmor) {
IAttributeInstance moveAttribute = getEntityAttribute(SharedMonsterAttributes.movementSpeed);
moveAttribute.removeModifier(armorMoveBonus);
IAttributeInstance knockbackAttribute = getEntityAttribute(SharedMonsterAttributes.knockbackResistance);
knockbackAttribute.removeModifier(armorKnockbackModifier);
tasks.removeTask(powerAttackAI);
if (wearingArmor) {
tasks.addTask(1, powerAttackAI);
knockbackAttribute.applyModifier(armorKnockbackModifier);
} else {
moveAttribute.applyModifier(armorMoveBonus);
}
}
@Override
public int getTotalArmorValue() {
return Math.min(20, super.getTotalArmorValue() + (2 * worldObj.getDifficulty().getDifficultyId()));
}
public boolean isWearingCape() {
return (dataWatcher.getWatchableObjectByte(CAPE_INDEX) > (byte) 0);
}
/**
* Grants the Darknut a cape with the given amount for health (i.e. ticks of fire damage required to burn through it)
*/
protected void setWearingCape(byte ticksRequired) {
dataWatcher.updateObject(CAPE_INDEX, ticksRequired);
}
/**
* Damages the cape by 1 point
*/
private void damageCape() {
byte b = dataWatcher.getWatchableObjectByte(CAPE_INDEX);
if (b > 0) {
setWearingCape((byte)(b - 1));
if (b == (byte) 1) {
extinguish();
}
}
}
@Override
public boolean attackEntityFrom(DamageSource source, float amount) {
boolean isPlayer = source.getEntity() instanceof EntityPlayer;
int difficulty = worldObj.getDifficulty().getDifficultyId();
if (isEntityInvulnerable(source) || isSpinning()) {
return false;
} else if (source == DamageSource.inWall && ticksExisted > 10) {
breakEnclosingBlocks(); // fall through to allow damage
} else if (source.isUnblockable() || (isPlayer && ZSSPlayerSkills.get((EntityPlayer) source.getEntity()).isSkillActive(SkillBase.armorBreak))) {
if (parryAttack(source)) {
return false;
}
return super.attackEntityFrom(source, amount);
} else if (source.getEntity() == null || source.isMagicDamage()) {
if (isArmored() && (source == DamageSource.cactus || source == DamageSource.fallingBlock)) {
return false; // don't allow cacti or falling blocks to damage Darknut while armored
} // all other vanilla null-entity damage sources should damage Darknut; can't handle modded ones
return super.attackEntityFrom(source, amount);
} else if (isWearingCape()) {
if (source.isFireDamage()) {
setFire(3);
}
return false;
} else if (source.isExplosion() && isArmored()) {
amount = damageDarknutArmor(amount * (1.25F - (0.25F * difficulty)));
if (amount < 0.5F) {
return false;
} // otherwise, allow extra damage to bleed through
} else if (!worldObj.isRemote) {
if (isArmored()) {
if (TargetUtils.isTargetInFrontOf(this, source.getEntity(), 120)) {
WorldUtils.playSoundAtEntity(this, Sounds.SWORD_STRIKE, 0.4F, 0.5F);
return false;
} else {
// Allow attack to go through for BackSlice, otherwise it will never trigger
if (isPlayer && ZSSPlayerSkills.get((EntityPlayer) source.getEntity()).isSkillActive(SkillBase.backSlice)) {
return super.attackEntityFrom(source, amount);
} else if (amount > (difficulty * 2.0F)) {
WorldUtils.playSoundAtEntity(this, Sounds.ARMOR_BREAK, 0.4F, 0.5F);
damageDarknutArmor(amount * 0.5F);
}
return false;
}
} else if (parryAttack(source)) {
return false;
} else {
if (recentHitTimer > 0) {
if (++recentHits > 3 && rand.nextFloat() < 0.15F * ((float) recentHits)) {
playLivingSound();
worldObj.playSoundAtEntity(this, Sounds.SPIN_ATTACK, 1.0F, (rand.nextFloat() * 0.4F) + 0.5F);
worldObj.setEntityState(this, SPIN_FLAG);
spinAttackTimer = 12;
recentHits = 0;
targets = worldObj.getEntitiesWithinAABB(EntityLivingBase.class, getEntityBoundingBox().expand(4.0D, 0.0D, 4.0D));
if (targets.contains(this)) {
targets.remove(this);
}
}
} else {
recentHits = 1;
}
recentHitTimer = 60;
}
}
return super.attackEntityFrom(source, amount);
}
/**
* Tries to break out of suffocating blocks
*/
protected void breakEnclosingBlocks() {
BlockPos eyePos = new BlockPos(posX, posY + getEyeHeight(), posZ);
boolean flag = false;
// smash all blocks in a 3x3x3 area around the Darknut's head
for (int i = -1; i < 2; ++i) {
for (int j = -1; j < 2; ++j) {
for (int k = -1; k < 2; ++k) {
BlockPos pos = eyePos.add(i, j, k);
Block block = worldObj.getBlockState(pos).getBlock();
float hardness = block.getBlockHardness(worldObj, pos);
if (block.isVisuallyOpaque() && hardness >= 0.0F && hardness < 20.0F && block.canEntityDestroy(worldObj, pos, this)) {
flag = true;
if (!worldObj.isRemote) {
worldObj.destroyBlock(pos, true);
}
}
}
}
}
if (flag) {
swingItem();
attackTime = 20;
worldObj.playSoundEffect(posX, posY, posZ, Sounds.ROCK_FALL, 1.0F, 1.0F);
}
}
/**
* Returns true if the Darknut was able to parry the source of damage, and
* may also disarm the attacker, if any
*/
protected boolean parryAttack(DamageSource source) {
Entity entity = source.getEntity();
if (entity == null || source.isExplosion() || (source instanceof IDamageAoE && ((IDamageAoE) source).isAoEDamage())) {
return false;
} else if (TargetUtils.isTargetInFrontOf(this, entity, 90) && rand.nextFloat() < (0.5F - (parryTimer * 0.05F))) {
worldObj.setEntityState(this, PARRY_FLAG);
parryTimer = 10;
super.swingItem();
attackTime = Math.max(attackTime, 5); // don't allow attacks until parry animation finishes
if (entity instanceof EntityLivingBase && !source.isProjectile()) {
EntityLivingBase attacker = (EntityLivingBase) entity;
if (attacker.getHeldItem() != null) {
WorldUtils.playSoundAtEntity(this, Sounds.SWORD_STRIKE, 0.4F, 0.5F);
float disarmChance = Parry.getDisarmModifier(this, attacker);
if (rand.nextFloat() < disarmChance) {
WorldUtils.dropHeldItem(attacker);
}
}
TargetUtils.knockTargetBack(attacker, this);
}
return true;
}
return false;
}
@Override
public float getOffensiveModifier(EntityLivingBase entity, ItemStack stack) {
return (0.1F * (float) worldObj.getDifficulty().getDifficultyId());
}
@Override
public float getDefensiveModifier(EntityLivingBase entity, ItemStack stack) {
return (0.1F * (float) worldObj.getDifficulty().getDifficultyId());
}
/**
* Applies the amount of damage to the darknut's armor, returning any remaining
*/
protected float damageDarknutArmor(float amount) {
float armorDamage = getArmorDamage();
float f = (amount - armorDamage);
armorDamage -= amount;
if (armorDamage < 0.1F) {
armorDamage = 0.0F;
onArmorDestroyed();
}
setArmorDamage(armorDamage);
return (f > 0F ? f : 0F);
}
/**
* Handle consequences of armor destroyed, e.g. setting current chest piece to null
*/
protected void onArmorDestroyed() {
if (getType() > 0) {
setCurrentItemOrArmor(ArmorIndex.EQUIPPED_CHEST, new ItemStack(Items.chainmail_chestplate));
} else {
setCurrentItemOrArmor(ArmorIndex.EQUIPPED_CHEST, null);
}
applyArmorAttributeModifiers(false);
}
@Override
public boolean attackEntityAsMob(Entity entity) {
if (attackTime <= 0) {
attackTime = 20;
worldObj.setEntityState(this, ATTACK_FLAG);
if (TargetUtils.isTargetInFrontOf(this, entity, 60) && attackEntity(entity, ATTACK_FLAG)) {
WorldUtils.playSoundAtEntity(this, Sounds.SWORD_STRIKE, 0.4F, 0.5F);
return true;
} else {
WorldUtils.playSoundAtEntity(this, Sounds.SWORD_MISS, 0.4F, 0.5F);
}
}
return false;
}
/**
* Actually attacks the entity, performing all the attack and damage calculations
* @param flag attack flag, e.g. POWER_ATTACK_FLAG
* @return true if attack was successful (i.e. attackEntityFrom returned true
*/
protected boolean attackEntity(Entity entity, int flag) {
float damage = (float) getEntityAttribute(SharedMonsterAttributes.attackDamage).getAttributeValue();
int k = (flag == POWER_ATTACK_FLAG || flag == SPIN_FLAG ? 1 : 0); // knockback
if (entity instanceof EntityLivingBase) {
damage += EnchantmentHelper.getModifierForCreature(this.getHeldItem(), ((EntityLivingBase) entity).getCreatureAttribute());
k += EnchantmentHelper.getKnockbackModifier(this);
}
if (entity.attackEntityFrom(getDamageSource(flag), damage)) {
if (k > 0) {
entity.addVelocity((double)(-MathHelper.sin(rotationYaw * (float) Math.PI / 180.0F) * (float) k * 0.5F), 0.1D, (double)(MathHelper.cos(rotationYaw * (float) Math.PI / 180.0F) * (float) k * 0.5F));
motionX *= 0.6D;
motionZ *= 0.6D;
}
int j = EnchantmentHelper.getFireAspectModifier(this);
if (j > 0) {
entity.setFire(j * 4);
}
if (entity instanceof EntityLivingBase) {
EnchantmentHelper.applyThornEnchantments((EntityLivingBase) entity, this);
}
EnchantmentHelper.applyArthropodEnchantments(this, entity);
return true;
}
return false;
}
@Override
public boolean isLightArrowFatal() {
return true;
}
@Override
public float getLightArrowDamage(float amount) {
return 0;
}
/**
* Returns appropriate damage source based on attack flag
* @param flag Same flag as passed to {@link #onMeleeImpact}
*/
protected DamageSource getDamageSource(int flag) {
switch(flag) {
case POWER_ATTACK_FLAG: return DamageSource.causeMobDamage(this).setDamageBypassesArmor();
default: return DamageSource.causeMobDamage(this);
}
}
@Override
public boolean allowDamageMultiplier(EntityPlayer player) {
return getArmorDamage() < 0.1F;
}
@Override
public boolean allowDisarmorment(EntityPlayer player, float damage) {
return getArmorDamage() < 0.1F;
}
@Override
public float onBackSliced(EntityPlayer attacker, int level, float damage) {
if (isArmored()) {
damageDarknutArmor(getDamageArmorAmount());
if (!isArmored()) {
attacker.triggerAchievement(ZSSAchievements.orcaCanOpener);
}
return 0.0F;
}
return damage;
}
/**
* Amount to damage Darknut's armor when backsliced
*/
protected float getDamageArmorAmount() {
return getArmorDamage();
}
@Override
public void beginPowerAttack() {
attackTime = getChargeTime(); // prevent regular attacks from occurring while charging up
worldObj.setEntityState(this, POWER_UP_FLAG);
}
@Override
public void cancelPowerAttack() {
worldObj.setEntityState(this, POWER_UP_FLAG);
}
/**
* 3 extra ticks included for animation of raising arms up to position
*/
@Override
public int getChargeTime() {
return 28 - (worldObj.getDifficulty().getDifficultyId() * 5);
}
@Override
public void performPowerAttack(EntityLivingBase target) {
worldObj.setEntityState(this, POWER_ATTACK_FLAG);
attackTime = 20;
if (TargetUtils.isTargetInFrontOf(this, target, 60.0F) && attackEntity(target, POWER_ATTACK_FLAG)) {
WorldUtils.playSoundAtEntity(this, Sounds.ARMOR_BREAK, 0.4F, 0.5F);
} else {
onAttackMissed();
}
}
@Override
public void onAttackMissed() {
WorldUtils.playSoundAtEntity(this, Sounds.SWORD_MISS, 0.4F, 0.5F);
}
@Override
public void swingItem() {} // don't allow item to swing as normal or it screws up the attack animations
@Override
protected void updateAITasks() {
if (spinAttackTimer > 0) {
++entityAge;
} else {
super.updateAITasks();
}
}
@Override
public void onLivingUpdate() {
if (isWearingCape() && isBurning()) {
damageCape();
}
super.onLivingUpdate();
if (attackTime > 0) {
--attackTime;
}
if (parryTimer > 0) {
--parryTimer;
}
if (recentHitTimer > 0) {
if (--recentHitTimer == 0) {
recentHits = 0;
}
}
if (spinAttackTimer > 0) {
--spinAttackTimer;
if (isEntityAlive()) {
rotationYaw += 30.0F;
while (rotationYaw > 360.0F) { rotationYaw -= 360.0F; }
while (rotationYaw < -360.0F) { rotationYaw += 360.0F; }
if (!worldObj.isRemote) {
List<EntityLivingBase> list = TargetUtils.acquireAllLookTargets(this, 5, 1.0D);
for (EntityLivingBase target : list) {
if (targets != null && targets.contains(target)) {
attackEntity(target, SPIN_FLAG);
targets.remove(target);
}
}
}
}
}
if (worldObj.isRemote) {
if (attackTimer > 0) {
--attackTimer;
} else if (attackTimer < 0) {
++attackTimer;
}
if (chargeTimer > 0) {
--chargeTimer;
}
}
}
@Override
@SideOnly(Side.CLIENT)
public void handleStatusUpdate(byte flag) {
switch(flag) {
case ATTACK_FLAG:
isPowerAttack = false;
if (attackTimer == 0) {
attackTimer = (rand.nextFloat() < 0.5F ? 10 : -10);
}
break;
case PARRY_FLAG:
parryTimer = 10;
super.swingItem();
break;
case POWER_UP_FLAG:
// cancel or begin charge; 3 ticks up already included in charge time, 3 ticks down
chargeTimer = (chargeTimer > 0 ? 0 : getChargeTime());
break;
case POWER_ATTACK_FLAG:
isPowerAttack = true;
attackTimer = 7;
chargeTimer = 0;
break;
case SPIN_FLAG:
spinAttackTimer = 12;
break;
default:
super.handleStatusUpdate(flag);
}
}
@Override
public void setCurrentItemOrArmor(int slot, ItemStack stack) {
super.setCurrentItemOrArmor(slot, stack);
if (!worldObj.isRemote && slot == ArmorIndex.EQUIPPED_CHEST && stack == null && getArmorDamage() > 0) {
applyArmorAttributeModifiers(false);
setArmorDamage(0F);
}
}
@Override
protected void dropFewItems(boolean recentlyHit, int lootingLevel) {
int n = rand.nextInt(Math.max(2, 3 + lootingLevel));
switch(n) {
case 0: entityDropItem(new ItemStack(Items.flint), 0.0F); break;
case 1: entityDropItem(new ItemStack(Items.coal), 0.0F); break;
default: entityDropItem(new ItemStack(Items.iron_ingot), 0.0F); break;
}
}
@Override
protected void addRandomDrop() {
switch(rand.nextInt(8)) {
case 1: entityDropItem(new ItemStack(ZSSItems.treasure,1,Treasures.KNIGHTS_CREST.ordinal()), 0.0F); break;
default: entityDropItem(new ItemStack(Items.painting), 0.0F);
}
}
@Override
public float getLootableChance(EntityPlayer player, WhipType whip) {
return (getEquipmentInSlot(ArmorIndex.EQUIPPED_CHEST) == null ? 0.25F : 0.0F);
}
@Override
public ItemStack getEntityLoot(EntityPlayer player, WhipType whip) {
return new ItemStack(ZSSItems.treasure,1,Treasures.KNIGHTS_CREST.ordinal());
}
@Override
public boolean onLootStolen(EntityPlayer player, boolean wasItemStolen) {
if (wasItemStolen) {
player.triggerAchievement(ZSSAchievements.orcaDeknighted);
}
return wasItemStolen;
}
@Override
public boolean isHurtOnTheft(EntityPlayer player, WhipType whip) {
return false;
}
@Override
public boolean getCanSpawnHere() {
return super.getCanSpawnHere() && worldObj.getTotalWorldTime() > Config.getTimeToSpawnDarknut();
}
@Override
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, IEntityLivingData data) {
data = super.onInitialSpawn(difficulty, data);
float f = difficulty.getClampedAdditionalDifficulty();
setEquipmentBasedOnDifficulty(difficulty);
if (rand.nextFloat() < (0.05F * f)) {
setType(1);
}
return data;
}
@Override
public void writeEntityToNBT(NBTTagCompound compound) {
super.writeEntityToNBT(compound);
compound.setFloat("ArmorHealth", getArmorDamage());
compound.setByte("DarknutType", dataWatcher.getWatchableObjectByte(TYPE_INDEX));
compound.setByte("CapeHealth", dataWatcher.getWatchableObjectByte(CAPE_INDEX));
}
@Override
public void readEntityFromNBT(NBTTagCompound compound) {
super.readEntityFromNBT(compound);
setArmorDamage(compound.getFloat("ArmorHealth"));
applyArmorAttributeModifiers(isArmored());
dataWatcher.updateObject(TYPE_INDEX, compound.getByte("DarknutType"));
dataWatcher.updateObject(CAPE_INDEX, compound.getByte("CapeHealth"));
}
}