package crazypants.enderzoo.entity; import java.util.ArrayList; import java.util.List; import javax.annotation.Nullable; import crazypants.enderzoo.EnderZoo; import crazypants.enderzoo.config.Config; import crazypants.enderzoo.entity.EntityWitherCat.GrowthMode; import crazypants.enderzoo.entity.ai.EntityAIRangedAttack; import crazypants.enderzoo.potion.BrewingUtil; import crazypants.enderzoo.vec.Point3i; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.IEntityLivingData; import net.minecraft.entity.IRangedAttackMob; import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.entity.ai.EntityAIHurtByTarget; import net.minecraft.entity.ai.EntityAILookIdle; 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.monster.EntityMob; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.projectile.EntityPotion; import net.minecraft.init.Items; import net.minecraft.init.MobEffects; import net.minecraft.inventory.EntityEquipmentSlot; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.potion.PotionEffect; import net.minecraft.util.DamageSource; import net.minecraft.util.EnumHand; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.world.DifficultyInstance; import net.minecraft.world.World; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.entity.living.LivingSpawnEvent; public class EntityWitherWitch extends EntityMob implements IRangedAttackMob, IEnderZooMob { public static final String NAME = "witherwitch"; public static final int EGG_BG_COL = 0x26520D; public static final int EGG_FG_COL = 0x905E43; private ItemStack[] drops = new ItemStack[] { new ItemStack(EnderZoo.itemWitheringDust), new ItemStack(EnderZoo.itemWitheringDust), new ItemStack(EnderZoo.itemWitheringDust), BrewingUtil.createHealthPotion(false, false, true), BrewingUtil.createWitherPotion(false, true), BrewingUtil.createWitherPotion(false, true), BrewingUtil.createRegenerationPotion(false, false, true) }; private int attackTimer; private EntityLivingBase attackedWithPotion; private int healTimer; private boolean isHealing; private boolean spawned; private boolean firstUpdate = true; private final List<EntityWitherCat> cats = new ArrayList<EntityWitherCat>(); private List<NBTTagCompound> loadedCats; private final EntityAIRangedAttack rangedAttackAI; private int noActiveTargetTime; public EntityWitherWitch(World world) { super(world); rangedAttackAI = new EntityAIRangedAttack(this, 1, 60, 10); tasks.addTask(1, new EntityAISwimming(this)); tasks.addTask(1, new EntityAISwimming(this)); tasks.addTask(2, rangedAttackAI); tasks.addTask(2, new EntityAIWander(this, 1.0D)); tasks.addTask(3, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F)); tasks.addTask(3, new EntityAILookIdle(this)); targetTasks.addTask(1, new EntityAIHurtByTarget(this, false)); targetTasks.addTask(2, new EntityAINearestAttackableTarget<EntityPlayer>(this, EntityPlayer.class, true)); } @Override protected void applyEntityAttributes() { super.applyEntityAttributes(); getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.25D); MobInfo.WITHER_WITCH.applyAttributes(this); } @Override protected float applyPotionDamageCalculations(DamageSource damageSource, float damage) { //same as a vanilla witch damage = super.applyPotionDamageCalculations(damageSource, damage); if(damageSource.getTrueSource() == this) { damage = 0.0F; } if(damageSource.isMagicDamage()) { damage = (float) (damage * 0.15D); } return damage; } @Override public boolean isPotionApplicable(PotionEffect potion) { return potion.getPotion() != MobEffects.WITHER && super.isPotionApplicable(potion); } @Override protected void dropFewItems(boolean hitByPlayer, int lootingLevel) { int numDrops = rand.nextInt(1) + 1; if(lootingLevel > 0) { numDrops += rand.nextInt(lootingLevel + 1); } for (int i = 0; i < numDrops; ++i) { ItemStack item = drops[rand.nextInt(drops.length)].copy(); entityDropItem(item, 0); } } @Override @Nullable protected ResourceLocation getLootTable() { return null; // use getDropItem() instead } @Override public void setRevengeTarget(EntityLivingBase target) { EntityLivingBase curTarget = getRevengeTarget(); super.setRevengeTarget(target); if(curTarget == target || world.isRemote || target == null) { return; } float distToSrc = getDistanceToEntity(target); if(distToSrc > getNavigator().getPathSearchRange() && distToSrc < 50) { getAttributeMap().getAttributeInstance(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(distToSrc + 2); } } @Override public IEntityLivingData onInitialSpawn(DifficultyInstance di, IEntityLivingData livingData) { spawned = true; return super.onInitialSpawn(di, livingData); } @Override public void onLivingUpdate() { if(world.isRemote) { super.onLivingUpdate(); return; } if(firstUpdate) { if(spawned) { spawnCats(); } else { loadCats(); } firstUpdate = false; } doAttackActions(); manageCats(); super.onLivingUpdate(); } protected void doAttackActions() { attackTimer--; healTimer--; EntityLivingBase target = getActiveTarget(); if(target == null) { noActiveTargetTime++; } else { noActiveTargetTime = 0; } if(shouldStartHeal()) { ItemStack potion; if(rand.nextFloat() > 0.75) { potion = BrewingUtil.createRegenerationPotion(false, true, true); } else { potion = BrewingUtil.createHealthPotion(false, false, true); } setItemStackToSlot(EntityEquipmentSlot.MAINHAND, potion); healTimer = 10; isHealing = true; } else if(target != null && getHeldItem(EnumHand.MAIN_HAND).isEmpty()) { ItemStack potion; if(getActiveTarget().isPotionActive(MobEffects.WITHER)) { potion = BrewingUtil.createHarmingPotion(EntityUtil.isHardDifficulty(world), true); } else { potion = BrewingUtil.createWitherPotion(false, true); } setItemStackToSlot(EntityEquipmentSlot.MAINHAND, potion); attackTimer = 10; healTimer = 40; } else if(noActiveTargetTime > 40 && !isHealing && getHeldItem(EnumHand.MAIN_HAND).isEmpty() == false) { setItemStackToSlot(EntityEquipmentSlot.MAINHAND, ItemStack.EMPTY); attackedWithPotion = null; } //the EntityPotion class validates if this potion is throwable, and if not it logs error "ThrownPotion entity {} has no item?! if(isHealing && healTimer <= 0 && getHeldItem(EnumHand.MAIN_HAND).getItem() == Items.SPLASH_POTION ) { throwHealthPotion(); isHealing = false; } } protected EntityLivingBase getActiveTarget() { EntityLivingBase res = getAttackTarget(); if(res == null) { res = rangedAttackAI.getAttackTarget(); } return res; } protected boolean shouldStartHeal() { if(isPotionActive(MobEffects.REGENERATION)) { return false; } return getHealth() < getMaxHealth() * 0.75 && rand.nextFloat() > 0.5 && healTimer <= 0; } @Override public void attackEntityWithRangedAttack(EntityLivingBase entity, float rangeRatio) { //the EntityPotion class validates if this potion is throwable, and if not it logs error "ThrownPotion entity {} has no item?! if(attackTimer <= 0 && getHeldItem(EnumHand.MAIN_HAND).getItem() == Items.SPLASH_POTION && !isHealing) { attackedWithPotion = entity; double x = entity.posX + entity.motionX - posX; double y = entity.posY + entity.getEyeHeight() - 1.100000023841858D - posY; double z = entity.posZ + entity.motionZ - posZ; float groundDistance = MathHelper.sqrt(x * x + z * z); ItemStack potion = getHeldItem(EnumHand.MAIN_HAND); attackTimer = getHeldItem(EnumHand.MAIN_HAND).getMaxItemUseDuration(); EntityPotion entitypotion = new EntityPotion(world, this, potion); entitypotion.rotationPitch -= -20.0F; entitypotion.setThrowableHeading(x, y + groundDistance * 0.2F, z, 0.75F, 8.0F); world.spawnEntity(entitypotion); setItemStackToSlot(EntityEquipmentSlot.MAINHAND, ItemStack.EMPTY); } } protected void throwHealthPotion() { ItemStack potion = getHeldItem(EnumHand.MAIN_HAND); //if its not a splash or lingering potion it will be an error EntityPotion entitypotion = new EntityPotion(world, this, potion); Vec3d lookVec = getLookVec(); entitypotion.setThrowableHeading(lookVec.x * 0.5, -1, lookVec.z * 0.5, 0.75F, 1.0F); world.spawnEntity(entitypotion); setItemStackToSlot(EntityEquipmentSlot.MAINHAND, ItemStack.EMPTY); healTimer = 80; } public void catDied(EntityWitherCat cat) { cats.remove(cat); } private void spawnCats() { if(!Config.witherCatEnabled) { return; } int numCats = rand.nextInt(Config.witherWitchMaxCats + 1); numCats = Math.max(numCats, Config.witherWitchMinCats); for (int i = 0; i < numCats; i++) { Point3i startPoint = EntityUtil.getEntityPositionI(this); startPoint.x += 4 - rand.nextInt(9); startPoint.z += 4 - rand.nextInt(9); Point3i spawnLoc = new Point3i(); if(SpawnUtil.findClearGround(world, startPoint, spawnLoc, 2, 10, true)) { spawnCat(spawnLoc); } else { return; } } } private void spawnCat(Point3i spawnLoc) { EntityWitherCat cat = new EntityWitherCat(world); cat.onInitialSpawn(world.getDifficultyForLocation(new BlockPos(this)), null); cat.setOwner(this); cat.setPositionAndRotation(spawnLoc.x + 0.5, spawnLoc.y + 0.5, spawnLoc.z + 0.5, rotationYaw, 0); if (MinecraftForge.EVENT_BUS.post(new LivingSpawnEvent.CheckSpawn(cat, world, (float)cat.posX, (float)cat.posY, (float)cat.posZ, false))) { return; } if(!cat.getCanSpawnHere()) { return; } cats.add(cat); world.spawnEntity(cat); } @Override public void writeEntityToNBT(NBTTagCompound root) { super.writeEntityToNBT(root); if(cats.isEmpty()) { return; } NBTTagList catsList = new NBTTagList(); for (EntityWitherCat cat : cats) { if(!cat.isDead) { NBTTagCompound catRoot = new NBTTagCompound(); cat.writeToNBT(catRoot); catsList.appendTag(catRoot); } } if(catsList.tagCount() > 0) { root.setTag("cats", catsList); } } @Override public void readEntityFromNBT(NBTTagCompound root) { super.readEntityFromNBT(root); if(!root.hasKey("cats")) { return; } NBTTagList catsList = (NBTTagList) root.getTag("cats"); loadedCats = new ArrayList<NBTTagCompound>(catsList.tagCount()); for (int i = 0; i < catsList.tagCount(); i++) { NBTTagCompound catRoot = catsList.getCompoundTagAt(i); if(catRoot != null) { loadedCats.add(catRoot); } } } private void loadCats() { if(loadedCats == null) { return; } for (NBTTagCompound catRoot : loadedCats) { if(catRoot != null) { EntityWitherCat cat = new EntityWitherCat(world); cat.readFromNBT(catRoot); cat.setOwner(this); cats.add(cat); world.spawnEntity(cat); } } } protected void manageCats() { if(cats.isEmpty()) { return; } if(noActiveTargetTime > 40) { pacifyCats(); return; } EntityLivingBase currentTarget = getActiveTarget(); EntityLivingBase hitBy = getRevengeTarget(); if(hitBy == null) { //agro the cats if we have been hit or we have actually thrown a potion hitBy = attackedWithPotion; } angerCats(currentTarget, hitBy); } private void angerCats(EntityLivingBase targ, EntityLivingBase hitBy) { for (EntityWitherCat cat : cats) { if(cat.isAngry()) { if(cat.getAttackTarget() != targ) { cat.setAttackTarget(targ); } } else if(cat.getGrowthMode() != GrowthMode.GROW && hitBy != null) { cat.setGrowthMode(GrowthMode.GROW); } } } private void pacifyCats() { for (EntityWitherCat cat : cats) { if(cat.isAngry()) { cat.setGrowthMode(GrowthMode.SHRINK); if(cat.getAttackTarget() != null) { cat.setAttackTarget(null); } } } } @Override public void setSwingingArms(boolean swingingArms) { } }