de.sanandrew.mods.claysoldiers.entity.EntityClayMan.java Source code

Java tutorial

Introduction

Here is the source code for de.sanandrew.mods.claysoldiers.entity.EntityClayMan.java

Source

/*******************************************************************************************************************
 * Authors:   SanAndreasP, CliffracerX
 * Copyright: SanAndreasP, SilverChiren and CliffracerX
 * License:   Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported
 *            (http://creativecommons.org/licenses/by-nc-sa/3.0/)
 *******************************************************************************************************************/
package de.sanandrew.mods.claysoldiers.entity;

import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
import cpw.mods.fml.common.FMLLog;
import de.sanandrew.core.manpack.util.EnumNbtTypes;
import de.sanandrew.core.manpack.util.javatuples.Pair;
import de.sanandrew.core.manpack.util.javatuples.Triplet;
import de.sanandrew.mods.claysoldiers.entity.mount.IMount;
import de.sanandrew.mods.claysoldiers.entity.projectile.ISoldierProjectile;
import de.sanandrew.mods.claysoldiers.network.PacketManager;
import de.sanandrew.mods.claysoldiers.network.ParticlePacketSender;
import de.sanandrew.mods.claysoldiers.util.*;
import de.sanandrew.mods.claysoldiers.util.soldier.ClaymanTeam;
import de.sanandrew.mods.claysoldiers.util.soldier.EnumMethodState;
import de.sanandrew.mods.claysoldiers.util.soldier.effect.ASoldierEffect;
import de.sanandrew.mods.claysoldiers.util.soldier.effect.SoldierEffectInst;
import de.sanandrew.mods.claysoldiers.util.soldier.effect.SoldierEffects;
import de.sanandrew.mods.claysoldiers.util.soldier.upgrade.ASoldierUpgrade;
import de.sanandrew.mods.claysoldiers.util.soldier.upgrade.SoldierUpgradeInst;
import de.sanandrew.mods.claysoldiers.util.soldier.upgrade.SoldierUpgrades;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityCreature;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.projectile.EntityThrowable;
import net.minecraft.item.ItemDye;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.util.*;
import net.minecraft.world.World;
import org.apache.commons.lang3.mutable.MutableDouble;
import org.apache.commons.lang3.mutable.MutableFloat;
import org.apache.logging.log4j.Level;

import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

public class EntityClayMan extends EntityCreature implements IDisruptable {
    private static final int DW_TEAM = 20;
    private static final int DW_MISC_COLOR = 21;
    private static final int DW_IS_TEXTURE_RARE_OR_UNIQUE = 22;
    private static final int DW_TEXTURE_INDEX = 23;

    public final SoldierCloakHelper cloakHelper = new SoldierCloakHelper();

    public ItemStack dollItem = null;
    public Triplet<Double, Double, Double> knockBack = Triplet.with(0.7D, 0.7D, 0.7D);
    public boolean canMove = true;
    public boolean nexusSpawn = false;

    private final Map<ASoldierUpgrade, SoldierUpgradeInst> p_upgrades = new ConcurrentHashMap<>();
    private final Map<ASoldierEffect, SoldierEffectInst> p_effects = new ConcurrentHashMap<>();
    private final long[] p_upgradeRenderFlags = new long[2];
    private final long[] p_effectRenderFlags = new long[2];

    private Entity p_targetFollow = null;
    private Collection p_entitiesInRange;

    public EntityClayMan(World world) {
        super(world);

        this.stepHeight = 0.1F;
        this.renderDistanceWeight = 5.0D;

        this.setSize(0.17F, 0.4F);

        this.yOffset = 0.01F;
        this.ignoreFrustumCheck = true;

        this.jumpMovementFactor = 0.2F;
    }

    public EntityClayMan(World world, String team) {
        this(world);
        this.dataWatcher.updateObject(DW_TEAM, team);

        this.setupTexture(this.rand.nextInt(8196) == 0, false);
    }

    @Override
    public void moveEntity(double motionX, double motionY, double motionZ) {
        if (this.canMove) {
            super.moveEntity(motionX, motionY, motionZ);
        } else {
            super.moveEntity(0.0D, motionY > 0.0D ? motionY / 2.0D : motionY, 0.0D);
        }
    }

    @Override
    public void disrupt() {
        if (!this.getCustomNameTag().startsWith("[UNDISRUPTABLE]")) {
            this.attackEntityFrom(IDisruptable.DISRUPT_DAMAGE, 99999);
        }
    }

    @Override
    public void onUpdate() {
        super.onUpdate();

        this.cloakHelper.onUpdate(this.posX, this.posY, this.posZ);

        if (!this.canMove) {
            this.motionX = 0.0F;
            this.motionZ = 0.0F;
            this.isJumping = false;
        }

        this.canMove = true;

        Iterator<Map.Entry<ASoldierUpgrade, SoldierUpgradeInst>> iterUpgrades = p_upgrades.entrySet().iterator();
        while (iterUpgrades.hasNext()) {
            if (!this.worldObj.isRemote) {
                SoldierUpgradeInst upg = iterUpgrades.next().getValue();
                if (upg.getUpgrade().onUpdate(this, upg)) {
                    iterUpgrades.remove();
                }
            } else {
                SoldierUpgradeInst upg = iterUpgrades.next().getValue();
                upg.getUpgrade().onClientUpdate(this, upg);
            }
        }
        Iterator<Map.Entry<ASoldierEffect, SoldierEffectInst>> iterEffects = p_effects.entrySet().iterator();
        while (iterEffects.hasNext()) {
            if (!this.worldObj.isRemote) {
                SoldierEffectInst upg = iterEffects.next().getValue();
                if (upg.getEffect().onUpdate(this, upg)) {
                    iterEffects.remove();
                }
            } else {
                SoldierEffectInst upg = iterEffects.next().getValue();
                upg.getEffect().onClientUpdate(this, upg);
            }
        }

        if ((ticksExisted % 5) == 0) {
            this.updateUpgradeEffectRenders();
        }

    }

    @Override
    public void writeEntityToNBT(NBTTagCompound nbt) {
        super.writeEntityToNBT(nbt);

        nbt.setString("team", this.getClayTeam());
        nbt.setByte("miscColor", this.dataWatcher.getWatchableObjectByte(DW_MISC_COLOR));
        nbt.setByte("isRareOrUnique", this.dataWatcher.getWatchableObjectByte(DW_IS_TEXTURE_RARE_OR_UNIQUE));
        nbt.setInteger("textureIndex", this.dataWatcher.getWatchableObjectInt(DW_TEXTURE_INDEX));
        nbt.setBoolean("canMove", this.canMove);
        nbt.setBoolean("nexusSpawned", this.nexusSpawn);

        if (this.dollItem != null) {
            NBTTagCompound stackNbt = new NBTTagCompound();
            this.dollItem.writeToNBT(stackNbt);
            nbt.setTag("dollItem", stackNbt);
        }

        NBTTagList upgNbtList = new NBTTagList();
        for (SoldierUpgradeInst upg : this.p_upgrades.values()) {
            NBTTagCompound savedUpg = new NBTTagCompound();
            savedUpg.setString("name", SoldierUpgrades.getName(upg.getUpgrade()));
            savedUpg.setTag("data", upg.getNbtTag());
            NBTTagCompound upgItem = upg.saveStoredItemToNBT();
            if (upgItem != null) {
                savedUpg.setTag("item", upgItem);
            }
            upgNbtList.appendTag(savedUpg);
        }
        nbt.setTag("upgrade", upgNbtList);

        NBTTagList effNbtList = new NBTTagList();
        for (SoldierEffectInst eff : this.p_effects.values()) {
            NBTTagCompound savedEff = new NBTTagCompound();
            savedEff.setString("name", SoldierEffects.getEffectName(eff.getEffect()));
            savedEff.setTag("data", eff.getNbtTag());
            effNbtList.appendTag(savedEff);
        }
        nbt.setTag("effect", effNbtList);
    }

    @Override
    public void readEntityFromNBT(NBTTagCompound nbt) {
        super.readEntityFromNBT(nbt);

        this.dataWatcher.updateObject(DW_TEAM, nbt.getString("team"));

        this.dataWatcher.updateObject(DW_MISC_COLOR, nbt.getByte("miscColor"));
        this.dataWatcher.updateObject(DW_IS_TEXTURE_RARE_OR_UNIQUE, nbt.getByte("isRareOrUnique"));
        this.dataWatcher.updateObject(DW_TEXTURE_INDEX, nbt.getInteger("textureIndex"));

        this.canMove = nbt.getBoolean("canMove");
        this.nexusSpawn = nbt.getBoolean("nexusSpawned");

        if (nbt.hasKey("dollItem")) {
            this.dollItem = ItemStack.loadItemStackFromNBT(nbt.getCompoundTag("dollItem"));
        }

        NBTTagList upgNbtList = nbt.getTagList("upgrade", EnumNbtTypes.NBT_COMPOUND.ordinal());
        for (int i = 0; i < upgNbtList.tagCount(); i++) {
            NBTTagCompound savedUpg = upgNbtList.getCompoundTagAt(i);
            SoldierUpgradeInst upgInst = new SoldierUpgradeInst(
                    SoldierUpgrades.getUpgrade(savedUpg.getString("name")));
            upgInst.setNbtTag(savedUpg.getCompoundTag("data"));
            if (savedUpg.hasKey("item")) {
                upgInst.readStoredItemFromNBT(savedUpg.getCompoundTag("item"));
            }
            this.p_upgrades.put(upgInst.getUpgrade(), upgInst);
        }
        NBTTagList effNbtList = nbt.getTagList("effect", EnumNbtTypes.NBT_COMPOUND.ordinal());
        for (int i = 0; i < effNbtList.tagCount(); i++) {
            NBTTagCompound savedEff = effNbtList.getCompoundTagAt(i);
            SoldierEffectInst effInst = new SoldierEffectInst(SoldierEffects.getEffect(savedEff.getString("name")));
            effInst.setNbtTag(savedEff.getCompoundTag("data"));
            this.p_effects.put(effInst.getEffect(), effInst);
        }
    }

    @Override
    public Entity getEntityToAttack() {
        return this.entityToAttack;
    }

    @Override
    public boolean attackEntityFrom(DamageSource source, float damage) {
        if (!(source.getEntity() instanceof EntityPlayer) && source != IDisruptable.DISRUPT_DAMAGE) {
            if (this.ridingEntity != null && rand.nextInt(4) == 0) {
                this.ridingEntity.attackEntityFrom(source, damage);
                return false;
            }
        } else {
            damage = 10000.0F;
        }

        if (!this.worldObj.isRemote) {
            for (Entry<ASoldierUpgrade, SoldierUpgradeInst> upgrade : this.p_upgrades.entrySet()) {
                SoldierUpgradeInst upg = upgrade.getValue();
                MutableFloat newDamage = new MutableFloat(damage);
                if (upg.getUpgrade().onSoldierHurt(this, upg, source, newDamage)) {
                    return false;
                } else {
                    damage = newDamage.floatValue();
                }
            }
        }

        return super.attackEntityFrom(source, damage);
    }

    @Override
    public void onDeath(DamageSource damageSource) {
        super.onDeath(damageSource);

        if (!this.worldObj.isRemote) {
            if (damageSource.isFireDamage() && this.dollItem != null) {
                ItemStack brickItem = new ItemStack(RegistryItems.dollBrick, this.dollItem.stackSize);
                brickItem.setTagCompound(this.dollItem.getTagCompound());
                this.dollItem = brickItem;
            }

            ArrayList<ItemStack> drops = new ArrayList<>();

            if (!this.nexusSpawn) {
                if (this.dollItem != null) {
                    drops.add(this.dollItem.copy());
                }

                for (SoldierUpgradeInst upg : this.p_upgrades.values()) {
                    upg.getUpgrade().onItemDrop(this, upg, drops);
                }

                drops.removeAll(Collections.singleton(null));
                for (ItemStack drop : drops) {
                    this.entityDropItem(drop, 0.0F);
                }
            }

            for (SoldierUpgradeInst upg : this.p_upgrades.values()) {
                upg.getUpgrade().onSoldierDeath(this, upg, damageSource);
            }

            for (SoldierEffectInst eff : this.p_effects.values()) {
                eff.getEffect().onSoldierDeath(this, eff, damageSource);
            }
        }
    }

    @Override
    public void knockBack(Entity par1Entity, float par2, double par3, double par5) {
        if (!this.canMove) {
            return;
        }

        super.knockBack(par1Entity, par2, par3, par5);
        this.motionX *= knockBack.getValue0();
        this.motionY *= knockBack.getValue1();
        this.motionZ *= knockBack.getValue2();
    }

    @Override
    public float getAIMoveSpeed() {
        MutableFloat speed = new MutableFloat(0.5F);

        for (SoldierUpgradeInst upg : this.p_upgrades.values()) {
            upg.getUpgrade().getAiMoveSpeed(this, upg, speed);
        }
        for (SoldierEffectInst eff : this.p_effects.values()) {
            eff.getEffect().getAiMoveSpeed(this, eff, speed);
        }

        return speed.floatValue();
    }

    @Override
    public boolean canEntityBeSeen(Entity target) {
        return this.worldObj.func_147447_a(
                Vec3.createVectorHelper(this.posX, this.posY + this.getEyeHeight(), this.posZ),
                Vec3.createVectorHelper(target.posX, target.posY + target.getEyeHeight(), target.posZ), false, true,
                false) == null;
    }

    @Override
    public boolean canBePushed() {
        return this.canMove;
    }

    @Override
    protected void applyEntityAttributes() {
        super.applyEntityAttributes();

        this.getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue(ModConfig.soldierBaseHealth);
    }

    @Override
    protected void entityInit() {
        super.entityInit();

        this.dataWatcher.addObject(DW_TEAM, ClaymanTeam.NULL_TEAM.getTeamName());
        this.dataWatcher.addObject(DW_MISC_COLOR, (byte) 15);
        this.dataWatcher.addObject(DW_IS_TEXTURE_RARE_OR_UNIQUE, (byte) 0);
        this.dataWatcher.addObject(DW_TEXTURE_INDEX, 0);
    }

    @Override
    protected String getLivingSound() {
        return null;
    }

    @Override
    protected String getHurtSound() {
        return ModConfig.useOldHurtSound ? "claysoldiers:mob.soldier.hurt" : "dig.gravel";
    }

    @Override
    protected String getDeathSound() {
        return "step.gravel";
    }

    @Override
    protected void onDeathUpdate() {
        this.deathTime = 20;
        this.setDead();
        ParticlePacketSender.sendSoldierDeathFx(this.posX, this.posY, this.posZ, this.dimension,
                this.getClayTeam());

    }

    @Override
    protected boolean isAIEnabled() {
        return false;
    }

    @Override
    protected boolean canDespawn() {
        return false;
    }

    @Override
    protected boolean interact(EntityPlayer player) {
        if (this.worldObj.isRemote) {
            ClaySoldiersMod.proxy.switchClayCam(true, this);
        }

        return super.interact(player);
    }

    @Override
    protected void updateEntityActionState() {
        //BUGFIX: fixes movement in blocks w/o collision box (snow layer, torches, tall grass, possibly cobweb?, etc.)
        if (!this.hasPath()) {
            if (this.entityToAttack != null) {
                this.setPathToEntity(BugfixHelper.getPathEntityToEntity(this.worldObj, this, this.entityToAttack,
                        16.0F, true, false, false, true));
            } else if (this.p_targetFollow != null) {
                this.setPathToEntity(BugfixHelper.getPathEntityToEntity(this.worldObj, this, this.p_targetFollow,
                        16.0F, true, false, false, true));
            } else if ((this.rand.nextInt(180) == 0 || this.rand.nextInt(120) == 0 || this.fleeingTick > 0)
                    && this.entityAge < 100) {
                this.updateWanderPath();
            }
        }

        super.updateEntityActionState();

        if (!this.worldObj.isRemote) {
            this.p_entitiesInRange = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.getTargetArea());

            if (this.entityToAttack == null) {
                if (rand.nextInt(4) != 0 && p_targetFollow == null) {
                    Collection<EntityClayMan> claymen = this.getSoldiersInRange();
                    for (EntityClayMan uberhaxornova : claymen) {
                        if (uberhaxornova.isDead || rand.nextInt(3) != 0) {
                            continue;
                        }

                        if (!this.checkIfValidTarget(uberhaxornova)) {
                            continue;
                        }

                        this.entityToAttack = uberhaxornova;

                        break;
                    }
                } else {
                    if (this.p_targetFollow == null) {
                        Collection<EntityItem> items = this.getItemsInRange();
                        items: for (EntityItem seamus : items) {
                            if (!this.canEntityBeSeen(seamus)) {
                                continue;
                            }

                            ASoldierUpgrade upgrade = SoldierUpgrades.getUpgrade(seamus.getEntityItem());
                            if (upgrade != null) {
                                if (this.hasUpgrade(upgrade)
                                        || !upgrade.canBePickedUp(this, seamus.getEntityItem(), null)) {
                                    continue;
                                } else {
                                    for (SoldierUpgradeInst upgradeInst : this.p_upgrades.values()) {
                                        if (upgrade == upgradeInst.getUpgrade() || !upgrade.canBePickedUp(this,
                                                seamus.getEntityItem(), upgradeInst.getUpgrade())) {
                                            continue items;
                                        }
                                    }
                                }
                            } else {
                                continue;
                            }

                            this.p_targetFollow = seamus;

                            break;
                        }
                    } else {
                        if (this.p_targetFollow.isDead) {
                            this.p_targetFollow = null;
                        } else if (!this.canEntityBeSeen(this.p_targetFollow)) {
                            this.p_targetFollow = null;
                        }

                        if (this.p_targetFollow instanceof EntityItem
                                && this.p_targetFollow.getDistanceToEntity(this) < 0.5F) {
                            EntityItem itemEntity = (EntityItem) this.p_targetFollow;
                            ASoldierUpgrade upgrade = SoldierUpgrades.getUpgrade(itemEntity.getEntityItem());
                            if (upgrade != null) {
                                this.addUpgrade(upgrade, itemEntity.getEntityItem());

                                if (itemEntity.getEntityItem().stackSize <= 0) {
                                    itemEntity.setDead();
                                }

                                this.p_targetFollow = null;
                            }
                        } else if (this.p_targetFollow instanceof IMount) {
                            if (this.p_targetFollow.riddenByEntity != null) {
                                this.p_targetFollow = null;
                            } else if (this.p_targetFollow.getDistanceToEntity(this) < 0.5D) {
                                this.mountEntity(this.p_targetFollow);
                                this.p_targetFollow = null;
                            }
                        }
                    }
                    if (this.p_targetFollow == null && this.ridingEntity == null) {
                        Collection<IMount> items = this.getMountsInRange();
                        for (IMount mount : items) {
                            EntityLivingBase slyfox = (EntityLivingBase) mount;
                            if (this.rand.nextInt(4) != 0 || !this.canEntityBeSeen(slyfox)
                                    || slyfox.riddenByEntity != null) {
                                continue;
                            }

                            this.p_targetFollow = slyfox;
                            break;
                        }
                    }
                }
            } else {
                if (this.entityToAttack.isDead || !this.canEntityBeSeen(this.entityToAttack)
                        || (this.entityToAttack instanceof EntityClayMan
                                && !this.checkIfValidTarget((EntityClayMan) this.entityToAttack))) {
                    this.entityToAttack = null;
                } else if (this.attackTime == 0) {
                    this.attackTime = 5;

                    MutableFloat atkRng = new MutableFloat(this.riddenByEntity != null ? 0.6F : 0.7F);

                    for (SoldierUpgradeInst upg : this.p_upgrades.values()) {
                        upg.getUpgrade().getAttackRange(this, upg, this.entityToAttack, atkRng);
                    }

                    if (this.getDistanceToEntity(this.entityToAttack) < atkRng.floatValue()
                            && this.entityToAttack instanceof EntityLivingBase
                            && !this.entityToAttack.isEntityInvulnerable()) {
                        EntityLivingBase target = (EntityLivingBase) this.entityToAttack;
                        if (target.hurtTime == 0) {
                            MutableFloat damage = new MutableFloat(ModConfig.soldierBaseDamage);
                            if (target instanceof EntityClayMan) {
                                EntityClayMan soldierTarget = (EntityClayMan) target;
                                soldierTarget.knockBack = Triplet.with(0.8D, 0.8D, 0.8D);
                                for (SoldierUpgradeInst upg : this.p_upgrades.values()) {
                                    upg.getUpgrade().onSoldierAttack(this, upg, soldierTarget, damage);
                                }
                            }

                            if (target.attackEntityFrom(DamageSource.causeMobDamage(this), damage.getValue())
                                    && target instanceof EntityClayMan) {
                                for (SoldierUpgradeInst upg : this.p_upgrades.values()) {
                                    upg.getUpgrade().onSoldierDamage(this, upg, (EntityClayMan) target);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    @Override
    protected void updateWanderPath() {
        this.worldObj.theProfiler.startSection("stroll");
        boolean blockFound = false;
        int x = -1;
        int y = -1;
        int z = -1;
        float maxPathWeight = -99999.0F;

        for (int i = 0; i < 10; ++i) {
            int currX = MathHelper.floor_double(this.posX + this.rand.nextInt(13) - 6.0D);
            int currY = MathHelper.floor_double(this.posY + this.rand.nextInt(7) - 3.0D);
            int currZ = MathHelper.floor_double(this.posZ + this.rand.nextInt(13) - 6.0D);
            float pathWeight = this.getBlockPathWeight(currX, currY, currZ);

            if (pathWeight > maxPathWeight) {
                maxPathWeight = pathWeight;
                x = currX;
                y = currY;
                z = currZ;
                blockFound = true;
            }
        }

        if (blockFound) {
            this.setPathToEntity(
                    BugfixHelper.getEntityPathToXYZ(this.worldObj, this, x, y, z, 10.0F, true, false, false, true));
        }

        this.worldObj.theProfiler.endSection();
    }

    public void setupTexture(boolean isRare, boolean isUnique) {
        ClaymanTeam team = ClaymanTeam.getTeam(this.getClayTeam());
        if (isUnique && team.getUniqueTextures().length > 0) {
            this.dataWatcher.updateObject(DW_IS_TEXTURE_RARE_OR_UNIQUE, (byte) 2);
            this.dataWatcher.updateObject(DW_TEXTURE_INDEX, this.rand.nextInt(team.getUniqueTextures().length));
        } else if (isRare && team.getRareTextures().length > 0) {
            this.dataWatcher.updateObject(DW_IS_TEXTURE_RARE_OR_UNIQUE, (byte) 1);
            this.dataWatcher.updateObject(DW_TEXTURE_INDEX, this.rand.nextInt(team.getRareTextures().length));
        } else {
            this.dataWatcher.updateObject(DW_IS_TEXTURE_RARE_OR_UNIQUE, (byte) 0);
            this.dataWatcher.updateObject(DW_TEXTURE_INDEX, this.rand.nextInt(team.getDefaultTextures().length));
        }
    }

    public boolean isJumping() {
        return this.isJumping;
    }

    public double getLookRangeRad() {
        MutableDouble radiusMT = new MutableDouble(8.0D);

        for (SoldierUpgradeInst upg : this.p_upgrades.values()) {
            upg.getUpgrade().getLookRange(this, upg, radiusMT);
        }

        return radiusMT.getValue();
    }

    //TODO shove Functions into a seperate class file

    @SuppressWarnings("unchecked")
    public Collection<EntityClayMan> getSoldiersInRange() {
        return Collections2.transform(
                Collections2.filter(this.p_entitiesInRange, Predicates.instanceOf(EntityClayMan.class)),
                new Function<Object, EntityClayMan>() {
                    @Override
                    public EntityClayMan apply(Object input) {
                        return (EntityClayMan) input;
                    }
                });
    }

    @SuppressWarnings("unchecked")
    public Collection<EntityItem> getItemsInRange() {
        return Collections2.transform(
                Collections2.filter(this.p_entitiesInRange, Predicates.instanceOf(EntityItem.class)),
                new Function<Object, EntityItem>() {
                    @Override
                    public EntityItem apply(Object input) {
                        return (EntityItem) input;
                    }
                });
    }

    @SuppressWarnings("unchecked")
    public Collection<IMount> getMountsInRange() {
        return Collections2.transform(
                Collections2.filter(this.p_entitiesInRange, Predicates.instanceOf(IMount.class)),
                new Function<Object, IMount>() {
                    @Override
                    public IMount apply(Object input) {
                        return (IMount) input;
                    }
                });
    }

    public String getClayTeam() {
        return this.dataWatcher.getWatchableObjectString(DW_TEAM);
    }

    public ResourceLocation getTexture() {
        if (this.dataWatcher.getWatchableObjectByte(DW_IS_TEXTURE_RARE_OR_UNIQUE) == 2) {
            return ClaymanTeam.getTeam(this.dataWatcher.getWatchableObjectString(DW_TEAM))
                    .getUniqueTextures()[this.dataWatcher.getWatchableObjectInt(DW_TEXTURE_INDEX)];
        } else if (this.dataWatcher.getWatchableObjectByte(DW_IS_TEXTURE_RARE_OR_UNIQUE) == 1) {
            return ClaymanTeam.getTeam(this.dataWatcher.getWatchableObjectString(DW_TEAM))
                    .getRareTextures()[this.dataWatcher.getWatchableObjectInt(DW_TEXTURE_INDEX)];
        } else {
            return ClaymanTeam.getTeam(this.dataWatcher.getWatchableObjectString(DW_TEAM))
                    .getDefaultTextures()[this.dataWatcher.getWatchableObjectInt(DW_TEXTURE_INDEX)];
        }
    }

    public void updateUpgradeEffectRenders() {
        if (this.worldObj.isRemote) {
            for (Byte renderId : SoldierUpgrades.getRegisteredRenderIds()) {
                long renderFlag = 1L << (renderId % 64);
                int renderStorageDw = (renderId / 64);
                long dwValue = this.p_upgradeRenderFlags[renderStorageDw];

                ASoldierUpgrade upgrade = SoldierUpgrades.getUpgrade(renderId);
                if ((dwValue & renderFlag) == renderFlag) {
                    if (!this.p_upgrades.containsKey(upgrade)) {
                        this.p_upgrades.put(upgrade, new SoldierUpgradeInst(upgrade));
                    }
                } else {
                    this.p_upgrades.remove(upgrade);
                }
            }

            for (byte renderId : SoldierEffects.getRegisteredRenderIds()) {
                long renderFlag = 1L << (renderId % 64);
                int renderStorageDw = (renderId / 64);
                long dwValue = this.p_effectRenderFlags[renderStorageDw];

                ASoldierEffect effect = SoldierEffects.getEffect(renderId);

                if ((dwValue & renderFlag) == renderFlag) {
                    if (!this.p_effects.containsKey(effect)) {
                        SoldierEffectInst effectInst = new SoldierEffectInst(effect);
                        this.p_effects.put(effect, effectInst);
                        effect.onConstruct(this, effectInst);
                    }
                } else {
                    this.p_effects.remove(effect);
                }
            }
        } else {
            this.p_upgradeRenderFlags[0] = 0;
            this.p_upgradeRenderFlags[1] = 0;
            this.p_effectRenderFlags[0] = 0;
            this.p_effectRenderFlags[1] = 0;

            for (SoldierUpgradeInst upgInst : this.p_upgrades.values()) {
                byte renderId = SoldierUpgrades.getRenderId(upgInst.getUpgrade());
                if (renderId >= 0) {
                    long renderFlag = 1L << (renderId % 64);
                    int renderStorageDw = renderId / 64;
                    long prevDwValue = p_upgradeRenderFlags[renderStorageDw];

                    p_upgradeRenderFlags[renderStorageDw] = prevDwValue | renderFlag;
                }
            }

            List<Pair<Byte, NBTTagCompound>> effectNbtToClt = new ArrayList<>();
            for (SoldierEffectInst effInst : this.p_effects.values()) {
                byte renderId = SoldierEffects.getRenderId(effInst.getEffect());
                if (renderId >= 0) {
                    long renderFlag = 1L << (renderId % 64);
                    int renderStorageDw = renderId / 64;
                    long prevDwValue = this.p_effectRenderFlags[renderStorageDw];

                    this.p_effectRenderFlags[renderStorageDw] = prevDwValue | renderFlag;
                    if (effInst.getEffect().shouldNbtSyncToClient(this, effInst)) {
                        effectNbtToClt.add(Pair.with(renderId, effInst.getNbtTag()));
                    }
                }
            }

            List<Pair<Byte, NBTTagCompound>> upgradeNbtToClt = new ArrayList<>();
            for (SoldierUpgradeInst upgInst : this.p_upgrades.values()) {
                byte renderId = SoldierUpgrades.getRenderId(upgInst.getUpgrade());
                if (renderId >= 0) {
                    long renderFlag = 1L << (renderId % 64);
                    int renderStorageDw = renderId / 64;
                    long prevDwValue = this.p_upgradeRenderFlags[renderStorageDw];

                    this.p_upgradeRenderFlags[renderStorageDw] = prevDwValue | renderFlag;
                    if (upgInst.getUpgrade().shouldNbtSyncToClient(this, upgInst)) {
                        upgradeNbtToClt.add(Pair.with(renderId, upgInst.getNbtTag()));
                    }
                }
            }

            PacketManager.sendToAllAround(PacketManager.PKG_SOLDIER_RENDERS, this.dimension, this.posX, this.posY,
                    this.posZ, 64.0D,
                    Triplet.with(this.getEntityId(), this.p_upgradeRenderFlags, this.p_effectRenderFlags));

            for (Pair<Byte, NBTTagCompound> effNbt : effectNbtToClt) {
                PacketManager.sendToAllAround(PacketManager.PKG_SOLDIER_EFFECT_NBT, this.dimension, this.posX,
                        this.posY, this.posZ, 64.0D,
                        Triplet.with(this.getEntityId(), effNbt.getValue0(), effNbt.getValue1()));
            }

            for (Pair<Byte, NBTTagCompound> upgNbt : upgradeNbtToClt) {
                PacketManager.sendToAllAround(PacketManager.PKG_SOLDIER_UPGRADE_NBT, this.dimension, this.posX,
                        this.posY, this.posZ, 64.0D,
                        Triplet.with(this.getEntityId(), upgNbt.getValue0(), upgNbt.getValue1()));
            }
        }
    }

    public void onProjectileHit(ISoldierProjectile<? extends EntityThrowable> projectile,
            MovingObjectPosition movObjPos) {
        for (SoldierUpgradeInst upg : this.p_upgrades.values()) {
            upg.getUpgrade().onProjectileHit(this, upg, movObjPos, projectile);
        }
    }

    public boolean canTargetSoldier(EntityClayMan target) {
        return this.canTargetSoldier(target, true);
    }

    public boolean canTargetSoldier(EntityClayMan target, boolean withUpgradeCheck) {
        if (this.entityToAttack == null || this.entityToAttack.isDead) {
            if (withUpgradeCheck) {
                if (!this.checkIfValidTarget(target)) {
                    return false;
                }
            }
            this.entityToAttack = target;
            return true;
        } else {
            return false;
        }
    }

    public boolean hasUpgrade(ASoldierUpgrade upgrade) {
        return this.p_upgrades.containsKey(upgrade);
    }

    public boolean hasUpgrade(String upgradeName) {
        return this.hasUpgrade(SoldierUpgrades.getUpgrade(upgradeName));
    }

    public boolean hasUpgrade(Class upgradeClass) {
        for (ASoldierUpgrade upgrade : this.p_upgrades.keySet()) {
            if (upgradeClass.isInstance(upgrade)) {
                return true;
            }
        }
        return false;
    }

    public ASoldierUpgrade[] getAvailableUpgrades() {
        return this.p_upgrades.keySet().toArray(new ASoldierUpgrade[this.p_upgrades.size()]);
    }

    public SoldierUpgradeInst getUpgrade(ASoldierUpgrade upgrade) {
        if (this.hasUpgrade(upgrade)) {
            return this.p_upgrades.get(upgrade);
        } else {
            return null;
        }
    }

    public void removeEffect(ASoldierEffect effect) {
        if (this.hasEffect(effect)) {
            this.p_effects.remove(effect);
        }
    }

    public Entity getTargetFollowing() {
        return this.p_targetFollow;
    }

    public void setTargetFollowing(Entity entity) {
        this.p_targetFollow = entity;
    }

    public SoldierUpgradeInst addUpgrade(ASoldierUpgrade upgrade) {
        return this.addUpgrade(upgrade, null);
    }

    public SoldierUpgradeInst addUpgrade(ASoldierUpgrade upgrade, ItemStack stack) {
        if (!this.hasUpgrade(upgrade)) {
            SoldierUpgradeInst upgradeInst = new SoldierUpgradeInst(upgrade);

            upgrade.onConstruct(this, upgradeInst);

            if (stack != null) {
                upgrade.onPickup(this, upgradeInst, stack);
            }

            for (SoldierUpgradeInst inst : this.p_upgrades.values()) {
                inst.getUpgrade().onUpgradeAdded(this, inst, upgradeInst);
            }

            this.p_upgrades.put(upgrade, upgradeInst);

            return upgradeInst;
        } else {
            return null;
        }
    }

    public int getMiscColor() {
        return ItemDye.field_150922_c[getMiscColorIndex()];
    }

    public int getMiscColorIndex() {
        return this.dataWatcher.getWatchableObjectByte(DW_MISC_COLOR);
    }

    public void setMiscColorIndex(int colorIndex) {
        if (colorIndex >= 0 && colorIndex < ItemDye.field_150922_c.length) {
            this.dataWatcher.updateObject(DW_MISC_COLOR, (byte) colorIndex);
        }
    }

    public SoldierEffectInst addEffect(ASoldierEffect effect) {
        if (!this.hasEffect(effect)) {
            SoldierEffectInst effectInst = new SoldierEffectInst(effect);
            effect.onConstruct(this, effectInst);

            for (SoldierEffectInst existEffect : this.p_effects.values()) {
                if (!effect.isCompatibleWith(this, effectInst, existEffect)) {
                    return null;
                }
            }

            this.p_effects.put(effect, effectInst);
            return effectInst;
        } else {
            return null;
        }
    }

    public SoldierEffectInst getEffect(ASoldierEffect effect) {
        if (this.hasEffect(effect)) {
            return this.p_effects.get(effect);
        } else {
            return null;
        }
    }

    public SoldierEffectInst getEffect(String effectName) {
        return this.getEffect(SoldierEffects.getEffect(effectName));
    }

    public void applyRenderFlags(long... flags) {
        this.p_upgradeRenderFlags[0] = flags[0];
        this.p_upgradeRenderFlags[1] = flags[1];
        this.p_effectRenderFlags[0] = flags[2];
        this.p_effectRenderFlags[1] = flags[3];
    }

    public boolean hasEffect(ASoldierEffect effect) {
        return this.p_effects.containsKey(effect);
    }

    public boolean hasEffect(String effectName) {
        return this.hasEffect(SoldierEffects.getEffect(effectName));
    }

    public void throwSomethingAtEnemy(EntityLivingBase entity,
            Class<? extends ISoldierProjectile<? extends EntityThrowable>> projClass, boolean homing) {
        double d = entity.posX - posX;
        double d1 = entity.posZ - posZ;

        try {
            ISoldierProjectile<? extends EntityThrowable> projectile = projClass
                    .getConstructor(World.class, EntityLivingBase.class).newInstance(this.worldObj, this);
            projectile.initProjectile(entity, homing, this.getClayTeam());
            EntityThrowable throwable = projectile.getProjectileEntity();
            throwable.posY += 0.1D;
            double d2 = (entity.posY + entity.getEyeHeight()) - 0.10000000298023224D - throwable.posY;
            float f1 = MathHelper.sqrt_double(d * d + d1 * d1) * 0.2F;
            this.worldObj.spawnEntityInWorld(throwable);
            throwable.setThrowableHeading(d, d2 + f1, d1, 0.6F, 12.0F);
            this.attackTime = 30;
            this.hasAttacked = true;
        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException
                | InvocationTargetException e) {
            FMLLog.log(ClaySoldiersMod.MOD_LOG, Level.ERROR,
                    "%1$s cannot be instantiated! %1$s is not thrown to target!", projClass.getName());
            e.printStackTrace();
        }
    }

    /**
     * checks if the soldier is a valid target
     * @param target the soldier as a target-candidate
     * @return true, if the soldier is a valid target
     */
    private boolean checkIfValidTarget(EntityClayMan target) {
        for (SoldierEffectInst eff : this.p_effects.values()) {
            EnumMethodState result = eff.getEffect().onTargeting(this, eff, target);
            if (result == EnumMethodState.DENY) {
                return false;
            } else if (result == EnumMethodState.ALLOW) {
                return true;
            }
        }

        for (SoldierUpgradeInst upg : this.p_upgrades.values()) {
            EnumMethodState result = upg.getUpgrade().onTargeting(this, upg, target);
            if (result == EnumMethodState.DENY) {
                return false;
            } else if (result == EnumMethodState.ALLOW) {
                return true;
            }
        }

        for (SoldierUpgradeInst upg : target.p_upgrades.values()) {
            EnumMethodState result = upg.getUpgrade().onBeingTargeted(target, upg, this);
            if (result == EnumMethodState.DENY) {
                return false;
            } else if (result == EnumMethodState.ALLOW) {
                return true;
            }
        }

        return !target.getClayTeam().equals(this.getClayTeam()) && this.canEntityBeSeen(target);
    }

    private AxisAlignedBB getTargetArea() {
        double radius = getLookRangeRad();

        return AxisAlignedBB.getBoundingBox(this.posX - radius, this.posY - radius, this.posZ - radius,
                this.posX + radius, this.posY + radius, this.posZ + radius);
    }
}