package minefantasy.entity;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import com.google.common.io.ByteArrayDataInput;
import cpw.mods.fml.common.FMLCommonHandler;
import minefantasy.MineFantasyBase;
import minefantasy.api.tactic.ISpecialSenses;
import minefantasy.entity.ai.EntityAIBreakDoorAnimate;
import minefantasy.entity.ai.EntityAIUseDoorMF;
import minefantasy.item.ItemListMF;
import minefantasy.system.CombatManager;
import minefantasy.system.MF_Calculate;
import minefantasy.system.TacticalManager;
import minefantasy.system.cfg;
import minefantasy.system.data_minefantasy;
import minefantasy.system.network.PacketManagerMF;
import minefantasy.system.network.PacketUserMF;
import net.minecraft.block.Block;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.EntityLivingData;
import net.minecraft.entity.EnumCreatureAttribute;
import net.minecraft.entity.IRangedAttackMob;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.ai.EntityAIArrowAttack;
import net.minecraft.entity.ai.EntityAIAttackOnCollide;
import net.minecraft.entity.ai.EntityAIBreakDoor;
import net.minecraft.entity.ai.EntityAIHurtByTarget;
import net.minecraft.entity.ai.EntityAILeapAtTarget;
import net.minecraft.entity.ai.EntityAILookIdle;
import net.minecraft.entity.ai.EntityAIMoveThroughVillage;
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.item.EntityItem;
import net.minecraft.entity.monster.EntityGolem;
import net.minecraft.entity.monster.EntityMob;
import net.minecraft.entity.monster.EntitySkeleton;
import net.minecraft.entity.monster.EntitySpider;
import net.minecraft.entity.monster.EntityZombie;
import net.minecraft.entity.monster.IMob;
import net.minecraft.entity.passive.EntityVillager;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.projectile.EntityArrow;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.packet.Packet;
import net.minecraft.potion.Potion;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.ChunkCoordinates;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EntityDamageSourceIndirect;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraftforge.common.ForgeHooks;
import net.minecraftforge.common.ISpecialArmor.ArmorProperties;
/**
*
* @author Anonymous Productions
*
* Sources are provided for educational reasons. though small bits of
* code, or methods can be used in your own creations.
*/
public class EntitySkeletalKnight extends EntityMob implements IMob, ISpecialSenses, IRangedAttackMob, PacketUserMF {
private static final float[] enchantmentProbability = new float[] {0.0F, 0.0F, 0.1F, 0.2F};
private int rallyCooldown;
private int sprintCooldown;
private static final int dataId = 12;
private float attackRange = 2.5F;
public EntitySkeletalKnight(World world)
{
super(world);
setWeapon((byte)0);
this.tasks.addTask(3, new EntityAIArrowAttack(this, 0.25F, 20, 60, 15.0F));
this.tasks.addTask(3, new EntityAILeapAtTarget(this, 0.4F));
this.tasks.addTask(0, new EntityAISwimming(this));
this.tasks.addTask(1, new EntityAIBreakDoorAnimate(this));
this.tasks.addTask(2, new EntityAIAttackOnCollide(this, EntityPlayer.class, 1.0D, false));
this.tasks.addTask(3, new EntityAIAttackOnCollide(this, EntityLiving.class, 1.0D, true));
this.tasks.addTask(5, new EntityAIMoveThroughVillage(this, 1.0D, false));
this.tasks.addTask(6, new EntityAIWander(this, 1.0D));
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, true));
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityPlayer.class, 0, true));
this.equipmentDropChances[0] = 2.0F;
}
@Override
protected void applyEntityAttributes()
{
super.applyEntityAttributes();
this.getEntityAttribute(SharedMonsterAttributes.followRange).setAttribute(64.0D);
this.getEntityAttribute(SharedMonsterAttributes.movementSpeed).setAttribute(0.23F);
this.getEntityAttribute(SharedMonsterAttributes.attackDamage).setAttribute(2.0D);
this.getEntityAttribute(SharedMonsterAttributes.knockbackResistance).setAttribute(1.0D);
}
@Override
public void entityInit() {
super.entityInit();
this.dataWatcher.addObject(dataId, (int)0);
this.dataWatcher.addObject(dataId+1, (byte)0);
this.dataWatcher.addObject(dataId+2, (byte)0);
}
@Override
public EntityLivingData onSpawnWithEgg(EntityLivingData data)
{
this.setCanPickUpLoot(false);
enchant(defaultHeldItem);
enchant(bluntItem);
enchant(rangedItem);
enchant(stealthItem);
return super.onSpawnWithEgg(data);
}
@Override
public int getTotalArmorValue()
{
return 20;
}
private boolean isProperDistance() {
if(this.dimension != 0)
return true;
ChunkCoordinates spawn = worldObj.getSpawnPoint();
return this.getDistance(spawn.posX, spawn.posY, spawn.posZ) > getDistanceToSpawn();
}
public double getDistanceToSpawn() {
return 0;
}
@Override
public boolean getCanSpawnHere()
{
if(rand.nextInt(10) != 0)
{
return false;
}
EntityPlayer xpPlayer = getHighestXPLevel();
int lvl = 0;
if(xpPlayer != null)
{
lvl = xpPlayer.experienceLevel;
}
if(lvl < cfg.knightLvl)
{
return false;
}
if(worldObj.difficultySetting < cfg.knightDiff)
{
return false;
}
if(!super.getCanSpawnHere())
{
return false;
}
if(MineFantasyBase.isDebug())
{
System.out.println("Try Knight Spawn: " + lvl + " for " + xpPlayer.getEntityName());
}
return true;
}
private EntityPlayer getHighestXPLevel() {
EntityPlayer play = worldObj.getClosestPlayerToEntity(this, -1);
if(play != null)
{
return play;
}
return null;
}
@Override
public void onLivingUpdate()
{
super.onLivingUpdate();
if(!worldObj.isRemote)
{
if(rallyCooldown > 0)rallyCooldown --;
}
if(rallyCooldown <= 0)
{
rally(getAttackTarget());
}
if(getAttackTarget() != null)
{
if(getAttackTarget().isDead)
{
setAttackTarget(null);
}
}
if(!worldObj.isRemote)
{
int block = this.getBlockTime();
if(block > 0)block --;
dataWatcher.updateObject(dataId, block);
if(ticksExisted % 100 == 0)
{
heal(1);
}
if(ticksExisted % 10 == 0)
{
updateWeapons();
}
if(getAttackTarget() != null)
{
if(getAttackTarget().isSwingInProgress)
{
if(this.getHeldItem() == this.defaultHeldItem)
{
if(isInfront(getAttackTarget()))
{
dataWatcher.updateObject(dataId, 15);
}
}
}
if(this.getDistanceToEntity(getAttackTarget()) < 2 && rand.nextInt(5) == 0 && !isSneaking())
{
double pounceFactor = getDistanceToEntity(getAttackTarget())/8;
addVelocity((double)(-MathHelper.sin(this.rotationYaw * (float)Math.PI / 180.0F) * (float)pounceFactor * 0.5F), 0.1D, (double)(MathHelper.cos(this.rotationYaw * (float)Math.PI / 180.0F) * (float)pounceFactor * 0.5F));
jump();
}
if(this.getDistanceToEntity(getAttackTarget()) < 2 && fallDistance > 0.0 && !isOnLadder())
{
this.attackEntityAsMob(getAttackTarget());
}
}
else
{
setWeapon((byte)0);
}
sprintCooldown --;
setSneaking(getSneakStage());
if(startSprinting())
{
setSprinting(true);
}
if(stopSprinting())
{
setSprinting(false);
}
}
if(!worldObj.isRemote)
{
sendPacketToClients();
if(getAttackTarget() != null && !getAttackTarget().isDead && rand.nextInt(400) == 0)
{
if(getDistanceToEntity(getAttackTarget()) > 4 && getDistanceToEntity(getAttackTarget()) < 12)
{
throwBombAt(getAttackTarget());
return;
}
}
}
}
private void updateWeapons()
{
if(getAttackTarget() != null)
{
int AC = getAttackTarget().getTotalArmorValue();
int bluntAC = 15;
if(isSneaking())
{
this.setWeaponUsed((byte)1);
}
else if(AC >= bluntAC)
{
this.setWeaponUsed((byte)2);
}
else
{
this.setWeaponUsed((byte)0);
}
}
else
{
this.setWeaponUsed((byte)0);
}
}
private void sendPacketToClients()
{
try
{
Packet packet = PacketManagerMF.getEntityPacketInteger(this, rallyCooldown);
FMLCommonHandler.instance().getMinecraftServerInstance().getConfigurationManager().sendToAllNear(posX, posY, posZ, 16D, dimension, packet);
}
catch(Exception e){}
}
@Override
protected void jump()
{
if(onGround)
super.jump();
}
public boolean useRanged() {
return dataWatcher.getWatchableObjectByte(dataId+1)== 1;
}
/**
* @param type
* 0 = melee
* 1 = ranged
*/
private void setWeapon(byte type)
{
this.dataWatcher.updateObject(dataId+1, type);
this.setCurrentItemOrArmor(0, getNextWeapon());
}
/**
* 0 = Knight
* 1 = Guardian
*/
private int getType()
{
return 1;
}
private boolean startSprinting() {
if(getNavigator().noPath())
return false;
if(isSneaking())
{
return false;
}
if(getType() == 0)
{
return false;
}
if(sprintCooldown > 0)
{
return true;
}
if(this.getAttackTarget() != null)
{
if(this.getDistanceToEntity(getAttackTarget()) > 4)
{
sprintCooldown = 20;
return true;
}
}
return false;
}
private boolean stopSprinting() {
if(getNavigator().noPath())
return true;
if(getType() == 0)
{
return true;
}
if(sprintCooldown > 0)
{
return false;
}
if(this.getAttackTarget() != null)
{
if(this.getDistanceToEntity(getAttackTarget()) < 1.5D)
{
sprintCooldown = 0;
return true;
}
}
return true;
}
private boolean getSneakStage() {
if(getNavigator().noPath())
return false;
if(isSprinting())
{
return false;
}
if(getType() == 0)
{
return false;
}
if(this.getAttackTarget() != null)
{
if(!TacticalManager.isDetected(this, getAttackTarget()))
{
return true;
}
if(fallDistance > 3.0)
{
return true;
}
}
return false;
}
public boolean isBlocking()
{
return getBlockTime() > 0;
}
@Override
protected boolean isAIEnabled() {
return true;
}
@Override
public EnumCreatureAttribute getCreatureAttribute() {
return EnumCreatureAttribute.UNDEAD;
}
@Override
public void playStepSound(int x, int y, int z, int block) {
super.playStepSound(x, y, z, block);
float volume = 0.8F;
playSound("mob.irongolem.walk", volume, 1.0F);
if(rand.nextInt(5) == 0)
playSound("mob.irongolem.throw", volume, 1.0F);
this.playSound("mob.skeleton.step", 0.15F, 1.0F);
}
@Override
public void setAttackTarget(EntityLivingBase entity) {
if(entity instanceof EntityMob)
{
((EntityMob)entity).setAttackTarget(null);
return;
}
super.setAttackTarget(entity);
}
public void rally(EntityLivingBase entity)
{
boolean target = entity != null && !entity.isDead;
boolean success = false;
rallyCooldown = getRallyTime();
List<EntityLiving> mobs = worldObj.getEntitiesWithinAABB(EntityLiving.class,AxisAlignedBB.getAABBPool().getAABB(posX, posY, posZ, posX + 1.0D,posY + 1.0D, posZ + 1.0D).expand(64D, 4D, 64D));
for(EntityLiving minion : mobs)
{
if(minion != null && minion instanceof IMob)
{
success = true;
spawnAngryParticle(minion);
minion.heal(2);
if(target)
{
minion.setAttackTarget(entity);
minion.getNavigator().tryMoveToEntityLiving(entity, 1.0F);
}
}
}
if(success)
{
spawnAngryParticle(this);
}
}
private void spawnAngryParticle(Entity entity)
{
for (int var2 = 0; var2 < 5; ++var2)
{
double var3 = this.rand.nextGaussian() * 0.02D;
double var5 = this.rand.nextGaussian() * 0.02D;
double var7 = this.rand.nextGaussian() * 0.02D;
this.worldObj.spawnParticle("angryVillager", entity.posX + (double)(this.rand.nextFloat() * entity.width * 2.0F) - (double)entity.width, entity.posY + 1.0D + (double)(this.rand.nextFloat() * entity.height), entity.posZ + (double)(this.rand.nextFloat() * entity.width * 2.0F) - (double)entity.width, var3, var5, var7);
}
}
private int getRallyTime() {
return 400;
}
@Override
protected void damageEntity(DamageSource source, float dam)
{
if(worldObj.isRemote)return;
Entity en = source.getEntity();
if (isInfront(en) && !source.isUnblockable() && this.isBlocking())
{
dam /= 2;
}
if(!worldObj.isRemote)
{
System.out.println("Full Dam: " + dam);
}
super.damageEntity(source, dam);
}
private boolean isInfront(Entity en) {
if(en == null)
return false;
if(TacticalManager.isFlankedBy(en, this, 180))
return false;
return true;
}
@Override
public boolean attackEntityFrom(DamageSource source, float damage)
{
if(source instanceof EntityDamageSourceIndirect)
{
EntityDamageSourceIndirect src = (EntityDamageSourceIndirect)source;
if(src.getEntity() != null && src.getEntity() == this)
{
return false;
}
}
if(this.hurtTime == 0)
{
playSound("mob.skeleton.hurt", 1.0F, 1.0F);
}
if(!worldObj.isRemote)
{
System.out.println("Dam: " + damage);
}
return super.attackEntityFrom(source, damage);
}
@Override
public boolean attackEntityAsMob(Entity entity)
{
if(!this.canEntityBeSeen(entity))return false;
setWeapon((byte)0);
swingItem();
int knockback = 0;
if (entity instanceof EntityLivingBase)
{
knockback += EnchantmentHelper.getKnockbackModifier(this, (EntityLivingBase)entity);
}
if (this.isSprinting())
{
knockback+=2;
}
if (knockback > 0)
{
entity.addVelocity((double)(-MathHelper.sin(this.rotationYaw * (float)Math.PI / 180.0F) * (float)knockback * 0.5F), 0.1D, (double)(MathHelper.cos(this.rotationYaw * (float)Math.PI / 180.0F) * (float)knockback * 0.5F));
this.motionX *= 0.6D;
this.motionZ *= 0.6D;
}
return super.attackEntityAsMob(entity);
}
public void attackEntityWithRangedAttack(EntityLivingBase target, float damage)
{
if(worldObj.isRemote)return;
if(target == null)return;
if(getDistanceToEntity(target) < 2 || isSneaking() || isSprinting() || getAttackTarget() == null && getAttackTarget().isDead)
{
setWeapon((byte)0);
return;
}
setWeapon((byte)1);
EntityArrow entityarrow = new EntityArrowMF(this.worldObj, this, target, 1.6F, (float)(1 - this.worldObj.difficultySetting * 0.3D), 5);
entityarrow.setDamage(2.0D + (2.0D * worldObj.difficultySetting));
if(rand.nextInt(4) == 0)
{
entityarrow.setIsCritical(true);
}
int i = EnchantmentHelper.getEnchantmentLevel(Enchantment.power.effectId, this.getHeldItem());
int j = EnchantmentHelper.getEnchantmentLevel(Enchantment.punch.effectId, this.getHeldItem());
entityarrow.setDamage((double)(damage * 2.0F) + this.rand.nextGaussian() * 0.25D + (double)((float)this.worldObj.difficultySetting * 0.11F));
if (i > 0)
{
entityarrow.setDamage(entityarrow.getDamage() + (double)i * 0.5D + 0.5D);
}
if (j > 0)
{
entityarrow.setKnockbackStrength(j);
}
if (EnchantmentHelper.getEnchantmentLevel(Enchantment.flame.effectId, this.getHeldItem()) > 0)
{
entityarrow.setFire(100);
}
this.playSound("random.bow", 1.0F, 1.0F / (this.getRNG().nextFloat() * 0.4F + 0.8F));
this.worldObj.spawnEntityInWorld(entityarrow);
}
private void throwBombAt(EntityLivingBase target)
{
if(target == null || !this.canEntityBeSeen(target))
{
return;
}
swingItem();
EntityBombThrown bomb = new EntityBombThrown(this.worldObj, this, target, 1.0F, (float)(1 - this.worldObj.difficultySetting * 0.3D)).setID(0);
this.worldObj.spawnEntityInWorld(bomb);
}
public boolean attackEntity(Entity entity) {
return super.attackEntityAsMob(entity);
}
@Override
protected String getLivingSound() {
return "mob.skeleton";
}
public float getAttackStrength(Entity entity)
{
float dam = 2.0F;
if(getHeldItem() != null)
{
dam = (float)this.getEntityAttribute(SharedMonsterAttributes.attackDamage).getAttributeValue();
}
if (this.isPotionActive(Potion.damageBoost))
{
dam += 3 << this.getActivePotionEffect(Potion.damageBoost).getAmplifier();
}
if (this.isPotionActive(Potion.weakness))
{
dam -= 2 << this.getActivePotionEffect(Potion.weakness).getAmplifier();
}
boolean flag = this.fallDistance > 0.0F && !this.onGround && !this.isOnLadder() && !this.isInWater() && !this.isPotionActive(Potion.blindness) && this.ridingEntity == null && entity instanceof EntityLivingBase;
if (flag && dam > 0)
{
dam += this.rand.nextFloat()*(dam / 2 + 2);
}
return dam;
}
/**
* Returns the sound this mob makes when it is hurt.
*/
@Override
protected String getHurtSound() {
return "mob.skeletonhurt";
}
/**
* Returns the sound this mob makes on death.
*/
@Override
protected String getDeathSound() {
return "mob.skeletonhurt";
}
public ItemStack getNextWeapon()
{
if(useRanged())
{
return rangedItem;
}
byte wp = this.getLastWeaponUsed();
if(wp == 1)
{
return stealthItem;
}
if(wp == 2)
{
return bluntItem;
}
return defaultHeldItem;
}
private static final ItemStack defaultHeldItem = new ItemStack(
ItemListMF.broadSteel, 1);
private static final ItemStack bluntItem = new ItemStack(
ItemListMF.warhammerSteel, 1);
private static final ItemStack stealthItem = new ItemStack(
ItemListMF.daggerSteel, 1);
private static final ItemStack rangedItem = new ItemStack(
ItemListMF.bowComposite, 1);
private int getSwingSpeedModifier() {
return 6;
}
@Override
protected void dropFewItems(boolean pKill, int loot)
{
if(worldObj.isRemote)return;
int dropSize = this.rand.nextInt(5 + loot) + 4;
int count;
dropSize = this.rand.nextInt(3 + loot);
for (count = 0; count < dropSize; ++count) {
this.dropItem(Item.bone.itemID, 1);
}
ItemStack[] other = this.getDropItems(loot);
if(other.length > 0)
{
for(ItemStack drop: other)
{
// this.entityDropItem(drop, 0.0F);
}
}
for(int a = 0; a < rand.nextInt(4 + loot); a ++)
{
this.entityDropItem(new ItemStack(ItemListMF.bombMF, 1, 0) , 0.0F);
}
}
@Override
public void writeEntityToNBT(NBTTagCompound tag)
{
super.writeEntityToNBT(tag);
tag.setByte("Weapon", dataWatcher.getWatchableObjectByte(dataId+1));
tag.setByte("Melee", dataWatcher.getWatchableObjectByte(dataId+2));
}
@Override
public void readEntityFromNBT(NBTTagCompound tag)
{
super.readEntityFromNBT(tag);
if(tag.hasKey("Weapon"))
setWeapon(tag.getByte("Weapon"));
if(tag.hasKey("Melee"))
setWeaponUsed(tag.getByte("Melee"));
}
private ItemStack[] getDropItems(int looting)
{
return new ItemStack[]
{
getDamagedItem(defaultHeldItem),
getDamagedItem(bluntItem),
getDamagedItem(stealthItem),
getDamagedItem(rangedItem),
};
}
private ItemStack getDamagedItem(ItemStack itemstack)
{
if (itemstack.isItemStackDamageable())
{
int maxDamage = Math.max(itemstack.getMaxDamage() - 25, 1);
int damageDone = itemstack.getMaxDamage() - this.rand.nextInt(this.rand.nextInt(maxDamage) + 1);
if (damageDone > maxDamage)
{
damageDone = maxDamage;
}
if (damageDone < 1)
{
damageDone = 1;
}
itemstack.setItemDamage(damageDone);
}
return itemstack;
}
private int getBlockTime()
{
return dataWatcher.getWatchableObjectInt(dataId);
}
@Override
public int getViewingArc() {
return 70;
}
@Override
public int getHearing() {
return 12;
}
@Override
public int getSight() {
return -10;
}
public EntityItem dropItem(int id, int stack, int damage)
{
int d = damage/2 + rand.nextInt(damage/2);
return this.dropItemWithOffset(id, stack, d, 0.0F);
}
public EntityItem dropItemWithOffset(int id, int stack, int dam, float offset)
{
return this.entityDropItem(new ItemStack(id, stack, dam), offset);
}
protected void enchant(ItemStack item)
{
if(item != null && item.isItemEnchanted())
{
return;
}
if (item != null && this.rand.nextFloat() < enchantmentProbability[this.worldObj.difficultySetting])
{
EnchantmentHelper.addRandomEnchantment(this.rand, item, 5 + this.worldObj.difficultySetting * this.rand.nextInt(6));
}
}
@Override
public void recievePacket(ByteArrayDataInput data)
{
try
{
rallyCooldown = data.readInt();
}catch(Exception e){};
}
/**
* 0 = sword
* 1 = dagger
* 2 = blunt
*/
private byte getLastWeaponUsed()
{
return dataWatcher.getWatchableObjectByte(dataId+2);
}
/**
* 0 = sword
* 1 = dagger
* 2 = blunt
*/
private void setWeaponUsed(byte id)
{
dataWatcher.updateObject(dataId+2, id);
}
@Override
public void knockBack(Entity entity, float x, double y, double z)
{
}
}