From 22afbcb3cffdc66e9173244a2fb8d178fe673f56 Mon Sep 17 00:00:00 2001 From: EdwinMindcraft Date: Sun, 18 Mar 2018 00:46:16 +0100 Subject: [PATCH] Water Guardian Stuff. Yay. --- build.properties | 2 +- src/main/java/am2/common/bosses/AM2Boss.java | 544 +++++++++--------- .../common/bosses/EntityWaterGuardian.java | 522 ++++++++--------- .../common/bosses/ai/EntityAICastSpell.java | 69 +-- .../common/bosses/ai/EntityAICloneSelf.java | 117 ++-- .../java/am2/common/items/ItemEssence.java | 2 +- 6 files changed, 638 insertions(+), 618 deletions(-) diff --git a/build.properties b/build.properties index bf2495024..d2f03f309 100644 --- a/build.properties +++ b/build.properties @@ -2,7 +2,7 @@ #Sat Dec 24 21:55:56 PST 2016 mcmapping=snapshot_20161111 version=1.5.0C -build_number=5 +build_number=6 jei_version=3.7.8.+ mcversion=1.10.2 mod_name=ArsMagica2 diff --git a/src/main/java/am2/common/bosses/AM2Boss.java b/src/main/java/am2/common/bosses/AM2Boss.java index 0065a8517..70bcaa76b 100644 --- a/src/main/java/am2/common/bosses/AM2Boss.java +++ b/src/main/java/am2/common/bosses/AM2Boss.java @@ -1,270 +1,274 @@ -package am2.common.bosses; - -import am2.ArsMagica2; -import am2.common.defs.ItemDefs; -import am2.common.defs.PotionEffectsDefs; -import am2.common.entity.EntityLightMage; -import am2.common.extensions.EntityExtension; -import net.minecraft.entity.Entity; -import net.minecraft.entity.IEntityMultiPart; -import net.minecraft.entity.SharedMonsterAttributes; -import net.minecraft.entity.ai.EntityAIHurtByTarget; -import net.minecraft.entity.ai.EntityAINearestAttackableTarget; -import net.minecraft.entity.ai.EntityAISwimming; -import net.minecraft.entity.boss.EntityDragonPart; -import net.minecraft.entity.monster.EntityMob; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.entity.projectile.EntityArrow; -import net.minecraft.potion.PotionEffect; -import net.minecraft.util.DamageSource; -import net.minecraft.util.SoundEvent; -import net.minecraft.world.BossInfo; -import net.minecraft.world.BossInfoServer; -import net.minecraft.world.World; - -public abstract class AM2Boss extends EntityMob implements IEntityMultiPart, IArsMagicaBoss{ - - protected BossActions currentAction = BossActions.IDLE; - protected int ticksInCurrentAction; - protected EntityDragonPart[] parts; - - public boolean playerCanSee = false; - private BossInfoServer bossInfo = null; - - public AM2Boss(World par1World){ - super(par1World); - if (par1World != null) - bossInfo = (BossInfoServer)(new BossInfoServer(this.getDisplayName(), getBarColor(), BossInfo.Overlay.PROGRESS)); - this.stepHeight = 1.02f; - EntityExtension.For(this).setMagicLevelWithMana(50); - initAI(); - } - - //Bosses should be able to follow players through doors and hallways, so setSize is overridden to instead add a - //damageable entity based bounding box of the specified size, unless a boss already uses parts. - @Override - public void setSize(float width, float height){ - if (parts == null){ - parts = new EntityDragonPart[]{new EntityDragonPart(this, "defaultBody", width, height){ - @Override - public void onUpdate(){ - super.onUpdate(); - this.isDead = ((Entity)entityDragonObj).isDead; - } - - @Override - public boolean shouldRenderInPass(int pass){ - return false; - } - }}; - }else{ - super.setSize(width, height); - } - } - - @Override - public boolean isAIDisabled() { - return false; - } - - @Override - public boolean isNonBoss() { - return false; - } - - @Override - protected void applyEntityAttributes(){ - super.applyEntityAttributes(); - this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(48); - } - - /** - * This contains the default AI tasks. To add new ones, override {@link #initSpecificAI()} - */ - protected void initAI(){ - //TODO this.getNavigator().setBreakDoors(true); - this.tasks.addTask(0, new EntityAISwimming(this)); - this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true)); - this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityPlayer.class, 0, true, true, null)); - this.targetTasks.addTask(3, new EntityAINearestAttackableTarget(this, EntityLightMage.class, 0, true, true, null)); - - initSpecificAI(); - } - - /** - * Initializer for class-specific AI - */ - protected abstract void initSpecificAI(); - - protected abstract BossInfo.Color getBarColor(); - - @Override - public BossActions getCurrentAction(){ - return currentAction; - } - - @Override - public void setCurrentAction(BossActions action){ - currentAction = action; - ticksInCurrentAction = 0; - } - - @Override - public int getTicksInCurrentAction(){ - return ticksInCurrentAction; - } - - @Override - public boolean isActionValid(BossActions action){ - return true; - } - - @Override - public abstract SoundEvent getAttackSound(); - - @Override - protected boolean canDespawn(){ - return false; - } - - @Override - public Entity[] getParts() { - return parts; - } - - @Override - public boolean canBeCollidedWith(){ - return false; - } - - @Override - public boolean attackEntityFrom(DamageSource par1DamageSource, float par2){ - - if (par1DamageSource == DamageSource.inWall){ - if (!worldObj.isRemote){// dead code? (calling canSnowAt() without using the result) could it be a buggy upgrade to 1.7.10? - for (int i = -1; i <= 1; ++i){ - for (int j = 0; j < 3; ++j){ - for (int k = -1; k <= 1; ++k){ - worldObj.destroyBlock(getPosition().add(i, j, k), true); - } - } - } - } - return false; - } - - if (par1DamageSource.getSourceOfDamage() != null){ - - if (par1DamageSource.getSourceOfDamage() instanceof EntityPlayer){ - EntityPlayer player = (EntityPlayer)par1DamageSource.getSourceOfDamage(); - if (player.capabilities.isCreativeMode && player.getHeldItemMainhand() != null && player.getHeldItemMainhand().getItem() == ItemDefs.woodenLeg){ - if (!worldObj.isRemote) - this.setDead(); - return false; - } - }else if (par1DamageSource.getSourceOfDamage() instanceof EntityArrow){ - Entity shooter = ((EntityArrow)par1DamageSource.getSourceOfDamage()).shootingEntity; - if (shooter != null && this.getDistanceSqToEntity(shooter) > 900){ - this.setPositionAndUpdate(shooter.posX, shooter.posY, shooter.posZ); - } - return false; - }else if (this.getDistanceSqToEntity(par1DamageSource.getSourceOfDamage()) > 900){ - Entity shooter = (par1DamageSource.getSourceOfDamage()); - if (shooter != null){ - this.setPositionAndUpdate(shooter.posX, shooter.posY, shooter.posZ); - } - } - } - - if (par2 > 7) par2 = 7; - - par2 = modifyDamageAmount(par1DamageSource, par2); - - if (par2 <= 0){ - heal(-par2); - return false; - } - - if (super.attackEntityFrom(par1DamageSource, par2)){ - this.hurtResistantTime = 40; - return true; - } - return false; - } - - protected abstract float modifyDamageAmount(DamageSource source, float damageAmt); - - public boolean attackEntityFromPart(EntityDragonPart part, DamageSource source, float damage){ - return this.attackEntityFrom(source, damage); - } - - @Override - public void onUpdate(){ - - if (parts != null && parts[0] != null && parts[0].partName == "defaultBody"){ - parts[0].setPosition(this.posX, this.posY, this.posZ); - if (worldObj.isRemote){ - parts[0].setVelocity(this.motionX, this.motionY, this.motionZ); - } - if (!parts[0].addedToChunk){ - this.worldObj.spawnEntityInWorld(parts[0]); - } - } - - this.ticksInCurrentAction++; - if (ticksInCurrentAction > this.getCurrentAction().getMaxActionTime()){ - setCurrentAction(BossActions.IDLE); - } - - if (worldObj.isRemote){ - playerCanSee = ArsMagica2.proxy.getLocalPlayer().canEntityBeSeen(this); - this.ignoreFrustumCheck = ArsMagica2.proxy.getLocalPlayer().getDistanceToEntity(this) < 32; - } - - if (bossInfo != null) - bossInfo.setPercent(this.getHealth() / this.getMaxHealth()); - - super.onUpdate(); - } - - @Override - public boolean canBeLeashedTo(EntityPlayer player) { - return false; - } - - @Override - public void addPotionEffect(PotionEffect effect){ - if (effect.getPotion() == PotionEffectsDefs.SILENCE) - return; - super.addPotionEffect(effect); - } - - public World getWorld(){ - return this.getEntityWorld(); - } - - /** - * Add the given player to the list of players tracking this entity. For instance, a player may track a boss in - * order to view its associated boss bar. - */ - @Override - public void addTrackingPlayer(EntityPlayerMP player) - { - super.addTrackingPlayer(player); - if (bossInfo != null) - this.bossInfo.addPlayer(player); - } - - /** - * Removes the given player from the list of players tracking this entity. See {@link Entity#addTrackingPlayer} for - * more information on tracking. - */ - @Override - public void removeTrackingPlayer(EntityPlayerMP player) - { - super.removeTrackingPlayer(player); - if (bossInfo != null) - this.bossInfo.removePlayer(player); - } -} +package am2.common.bosses; + +import am2.ArsMagica2; +import am2.common.defs.ItemDefs; +import am2.common.defs.PotionEffectsDefs; +import am2.common.entity.EntityLightMage; +import am2.common.extensions.EntityExtension; +import net.minecraft.entity.Entity; +import net.minecraft.entity.IEntityMultiPart; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.EntityAIHurtByTarget; +import net.minecraft.entity.ai.EntityAINearestAttackableTarget; +import net.minecraft.entity.ai.EntityAISwimming; +import net.minecraft.entity.ai.EntityAIWatchClosest; +import net.minecraft.entity.boss.EntityDragonPart; +import net.minecraft.entity.monster.EntityMob; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.entity.projectile.EntityArrow; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.DamageSource; +import net.minecraft.util.SoundEvent; +import net.minecraft.world.BossInfo; +import net.minecraft.world.BossInfoServer; +import net.minecraft.world.World; + +public abstract class AM2Boss extends EntityMob implements IEntityMultiPart, IArsMagicaBoss { + + protected BossActions currentAction = BossActions.IDLE; + protected int ticksInCurrentAction; + protected EntityDragonPart[] parts; + + public boolean playerCanSee = false; + private BossInfoServer bossInfo = null; + + public AM2Boss(World par1World) { + super(par1World); + if (par1World != null) + this.bossInfo = new BossInfoServer(this.getDisplayName(), this.getBarColor(), BossInfo.Overlay.PROGRESS); + this.stepHeight = 1.02f; + EntityExtension.For(this).setMagicLevelWithMana(50); + this.initAI(); + } + + //Bosses should be able to follow players through doors and hallways, so setSize is overridden to instead add a + //damageable entity based bounding box of the specified size, unless a boss already uses parts. + @Override + public void setSize(float width, float height) { + if (this.parts == null) { + this.parts = new EntityDragonPart[]{new EntityDragonPart(this, "defaultBody", width, height) { + + @Override + public void onUpdate() { + super.onUpdate(); + this.isDead = ((Entity) this.entityDragonObj).isDead; + } + + @Override + public boolean shouldRenderInPass(int pass) { + return false; + } + }}; + } else { + super.setSize(width, height); + } + } + + @Override + public boolean isAIDisabled() { + return false; + } + + @Override + public boolean isNonBoss() { + return false; + } + + @Override + protected void applyEntityAttributes() { + super.applyEntityAttributes(); + this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(48); + } + + /** + * This contains the default AI tasks. To add new ones, override {@link #initSpecificAI()} + */ + protected void initAI() { + //TODO this.getNavigator().setBreakDoors(true); + this.tasks.addTask(0, new EntityAISwimming(this)); + this.tasks.addTask(1, new EntityAIWatchClosest(this, EntityPlayer.class, 32.0F)); + this.tasks.addTask(2, new EntityAIWatchClosest(this, EntityLightMage.class, 32.0F)); + this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true)); + this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<>(this, EntityPlayer.class, true)); + this.targetTasks.addTask(3, new EntityAINearestAttackableTarget<>(this, EntityLightMage.class, true)); + + this.initSpecificAI(); + } + + /** + * Initializer for class-specific AI + */ + protected abstract void initSpecificAI(); + + protected abstract BossInfo.Color getBarColor(); + + @Override + public BossActions getCurrentAction() { + return this.currentAction; + } + + @Override + public void setCurrentAction(BossActions action) { + this.currentAction = action; + this.ticksInCurrentAction = 0; + } + + @Override + public int getTicksInCurrentAction() { + return this.ticksInCurrentAction; + } + + @Override + public boolean isActionValid(BossActions action) { + return true; + } + + @Override + public abstract SoundEvent getAttackSound(); + + @Override + protected boolean canDespawn() { + return false; + } + + @Override + public Entity[] getParts() { + return this.parts; + } + + @Override + public boolean canBeCollidedWith() { + return false; + } + + @Override + public boolean attackEntityFrom(DamageSource par1DamageSource, float par2) { + + if (par1DamageSource == DamageSource.inWall) { + if (!this.worldObj.isRemote) {// dead code? (calling canSnowAt() without using the result) could it be a buggy upgrade to 1.7.10? + for (int i = -1; i <= 1; ++i) { + for (int j = 0; j < 3; ++j) { + for (int k = -1; k <= 1; ++k) { + this.worldObj.destroyBlock(this.getPosition().add(i, j, k), true); + } + } + } + } + return false; + } + + if (par1DamageSource.getSourceOfDamage() != null) { + + if (par1DamageSource.getSourceOfDamage() instanceof EntityPlayer) { + EntityPlayer player = (EntityPlayer) par1DamageSource.getSourceOfDamage(); + if (player.capabilities.isCreativeMode && player.getHeldItemMainhand() != null && player.getHeldItemMainhand().getItem() == ItemDefs.woodenLeg) { + if (!this.worldObj.isRemote) + this.setDead(); + return false; + } + } else if (par1DamageSource.getSourceOfDamage() instanceof EntityArrow) { + Entity shooter = ((EntityArrow) par1DamageSource.getSourceOfDamage()).shootingEntity; + if (shooter != null && this.getDistanceSqToEntity(shooter) > 900) { + this.setPositionAndUpdate(shooter.posX, shooter.posY, shooter.posZ); + } + return false; + } else if (this.getDistanceSqToEntity(par1DamageSource.getSourceOfDamage()) > 900) { + Entity shooter = (par1DamageSource.getSourceOfDamage()); + if (shooter != null) { + this.setPositionAndUpdate(shooter.posX, shooter.posY, shooter.posZ); + } + } + } + + if (par2 > 7 && !par1DamageSource.damageType.equals(DamageSource.outOfWorld.damageType)) par2 = 7; + + par2 = this.modifyDamageAmount(par1DamageSource, par2); + + if (par2 <= 0) { + this.heal(-par2); + return false; + } + + if (super.attackEntityFrom(par1DamageSource, par2)) { + this.hurtResistantTime = 40; + return true; + } + return false; + } + + protected abstract float modifyDamageAmount(DamageSource source, float damageAmt); + + @Override + public boolean attackEntityFromPart(EntityDragonPart part, DamageSource source, float damage) { + return this.attackEntityFrom(source, damage); + } + + @Override + public void onUpdate() { + + if (this.parts != null && this.parts[0] != null && this.parts[0].partName == "defaultBody") { + this.parts[0].setPosition(this.posX, this.posY, this.posZ); + if (this.worldObj.isRemote) { + this.parts[0].setVelocity(this.motionX, this.motionY, this.motionZ); + } + if (!this.parts[0].addedToChunk) { + this.worldObj.spawnEntityInWorld(this.parts[0]); + } + } + + this.ticksInCurrentAction++; + if (this.ticksInCurrentAction > this.getCurrentAction().getMaxActionTime()) { + this.setCurrentAction(BossActions.IDLE); + } + + if (this.worldObj.isRemote) { + this.playerCanSee = ArsMagica2.proxy.getLocalPlayer().canEntityBeSeen(this); + this.ignoreFrustumCheck = ArsMagica2.proxy.getLocalPlayer().getDistanceToEntity(this) < 32; + } + + if (this.bossInfo != null) + this.bossInfo.setPercent(this.getHealth() / this.getMaxHealth()); + + super.onUpdate(); + } + + @Override + public boolean canBeLeashedTo(EntityPlayer player) { + return false; + } + + @Override + public void addPotionEffect(PotionEffect effect) { + if (effect.getPotion() == PotionEffectsDefs.SILENCE) + return; + super.addPotionEffect(effect); + } + + @Override + public World getWorld() { + return this.getEntityWorld(); + } + + /** + * Add the given player to the list of players tracking this entity. For instance, a player may track a boss in + * order to view its associated boss bar. + */ + @Override + public void addTrackingPlayer(EntityPlayerMP player) { + super.addTrackingPlayer(player); + if (this.bossInfo != null) + this.bossInfo.addPlayer(player); + } + + /** + * Removes the given player from the list of players tracking this entity. See {@link Entity#addTrackingPlayer} for + * more information on tracking. + */ + @Override + public void removeTrackingPlayer(EntityPlayerMP player) { + super.removeTrackingPlayer(player); + if (this.bossInfo != null) + this.bossInfo.removePlayer(player); + } +} diff --git a/src/main/java/am2/common/bosses/EntityWaterGuardian.java b/src/main/java/am2/common/bosses/EntityWaterGuardian.java index c34d7e98a..87c704c84 100644 --- a/src/main/java/am2/common/bosses/EntityWaterGuardian.java +++ b/src/main/java/am2/common/bosses/EntityWaterGuardian.java @@ -1,258 +1,264 @@ -package am2.common.bosses; - -import am2.api.ArsMagicaAPI; -import am2.api.affinity.Affinity; -import am2.api.sources.DamageSourceFrost; -import am2.api.sources.DamageSourceLightning; -import am2.common.bosses.ai.EntityAICastSpell; -import am2.common.bosses.ai.EntityAIChaosWaterBolt; -import am2.common.bosses.ai.EntityAICloneSelf; -import am2.common.bosses.ai.EntityAISpinAttack; -import am2.common.defs.AMSounds; -import am2.common.defs.ItemDefs; -import am2.common.extensions.EntityExtension; -import am2.common.packet.AMNetHandler; -import am2.common.utils.NPCSpells; -import net.minecraft.entity.SharedMonsterAttributes; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.network.datasync.DataParameter; -import net.minecraft.network.datasync.DataSerializers; -import net.minecraft.network.datasync.EntityDataManager; -import net.minecraft.util.DamageSource; -import net.minecraft.util.SoundCategory; -import net.minecraft.util.SoundEvent; -import net.minecraft.world.BossInfo.Color; -import net.minecraft.world.World; - -public class EntityWaterGuardian extends AM2Boss { - - private EntityWaterGuardian master; - private final EntityWaterGuardian[] clones; - private float orbitRotation; - private boolean uberSpinAvailable = false; - - private static final DataParameter IS_CLONE = EntityDataManager.createKey(EntityWaterGuardian.class, DataSerializers.BOOLEAN); - - public float spinRotation = 0; - - public EntityWaterGuardian(World par1World){ - super(par1World); - currentAction = BossActions.IDLE; - master = null; - clones = new EntityWaterGuardian[2]; - this.setSize(1.0f, 2.0f); - EntityExtension.For(this).setMagicLevelWithMana(10); - } - - @Override - protected void applyEntityAttributes(){ - super.applyEntityAttributes(); - this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(75D); - } - - public void setClones(EntityWaterGuardian clone1, EntityWaterGuardian clone2){ - clones[0] = clone1; - clones[1] = clone2; - } - - private boolean hasClones(){ - return clones[0] != null || clones[1] != null; - } - - public void clearClones(){ - if (clones[0] != null){ - clones[0].setDead(); - } - if (clones[1] != null){ - clones[1].setDead(); - } - clones[0] = null; - clones[1] = null; - } - - private void enableUberAttack(){ - uberSpinAvailable = true; - } - - public void setMaster(EntityWaterGuardian master){ - dataManager.set(IS_CLONE, true); - this.master = master; - } - - public boolean isClone(){ - return dataManager.get(IS_CLONE); - } - - public void clearMaster(){ - this.master = null; - } - - @Override - protected void initSpecificAI(){ - this.tasks.addTask(2, new EntityAIChaosWaterBolt(this)); - this.tasks.addTask(3, new EntityAICloneSelf(this)); - this.tasks.addTask(4, new EntityAICastSpell(this, NPCSpells.instance.waterBolt, 12, 23, 5, BossActions.CASTING)); - this.tasks.addTask(3, new EntityAISpinAttack(this, 0.5f, 4)); - } - - @Override - public void onUpdate(){ - - if (currentAction == BossActions.CASTING){ - uberSpinAvailable = false; - } - - if (!worldObj.isRemote && uberSpinAvailable && currentAction != BossActions.CASTING && currentAction != BossActions.IDLE){ - setCurrentAction(BossActions.IDLE); - } - - if (!worldObj.isRemote && isClone() && (master == null || ticksExisted > 400)){ - setDead(); - } - - if (worldObj.isRemote){ - updateRotations(); - } - super.onUpdate(); - } - - @Override - protected void entityInit(){ - super.entityInit(); - - this.dataManager.register(IS_CLONE, false); - } - - private void updateRotations(){ - if (!isClone()) - orbitRotation += 2f; - else - orbitRotation -= 2f; - orbitRotation %= 360; - - if (this.getCurrentAction() == BossActions.SPINNING || this.getCurrentAction() == BossActions.CASTING){ - this.spinRotation = (this.spinRotation - 30) % 360; - } - } - - public float getOrbitRotation(){ - return orbitRotation; - } - - @Override - public void setCurrentAction(BossActions action){ - super.setCurrentAction(action); - this.spinRotation = 0; - - if (!worldObj.isRemote){ - AMNetHandler.INSTANCE.sendActionUpdateToAllAround(this); - } - } - - @Override - public boolean attackEntityFrom(DamageSource par1DamageSource, float par2){ - if (par1DamageSource.getSourceOfDamage() instanceof EntityWaterGuardian) - return false; - if (isClone() && master != null){ - master.enableUberAttack(); - master.clearClones(); - }else if (hasClones()){ - clearClones(); - } - - if (!isClone() && rand.nextInt(10) < 6){ - worldObj.playSound(posX, posY, posZ, getAmbientSound(), SoundCategory.HOSTILE, 1.0f, 0.4f + rand.nextFloat() * 0.6f, false); - return false; - } - - return super.attackEntityFrom(par1DamageSource, par2); - } - - @Override - protected float modifyDamageAmount(DamageSource source, float damageAmt){ - if (source instanceof DamageSourceLightning) - damageAmt *= 2.0f; - if (source.getSourceOfDamage() != null && source.getSourceOfDamage() instanceof EntityWaterGuardian) - damageAmt = 0; - if (source instanceof DamageSourceFrost) - damageAmt = 0; - - return damageAmt; - } - - @Override - public boolean isActionValid(BossActions action){ - if (uberSpinAvailable && action != BossActions.CASTING) return false; - if (action == BossActions.CASTING){ - return uberSpinAvailable; - } - if (action == BossActions.CLONE){ - return !isClone(); - } - return true; - } - - @Override - public void writeEntityToNBT(NBTTagCompound par1nbtTagCompound){ - super.writeEntityToNBT(par1nbtTagCompound); - - par1nbtTagCompound.setBoolean("isClone", isClone()); - } - - @Override - public int getTotalArmorValue(){ - return 10; - } - - @Override - public void readEntityFromNBT(NBTTagCompound par1nbtTagCompound){ - super.readEntityFromNBT(par1nbtTagCompound); - - dataManager.set(IS_CLONE, par1nbtTagCompound.getBoolean("isClone")); - } - - - @Override - protected void dropFewItems(boolean par1, int par2){ - if (par1) - this.entityDropItem(new ItemStack(ItemDefs.infinityOrb, 1, 0), 0.0f); - - int i = rand.nextInt(4); - - for (int j = 0; j < i; j++){ - this.entityDropItem(new ItemStack(ItemDefs.essence, 1, ArsMagicaAPI.getAffinityRegistry().getId(Affinity.WATER)), 0.0f); - } - - i = rand.nextInt(10); - - if (i < 3){ - this.entityDropItem(ItemDefs.waterOrbsEnchanted.copy(), 0.0f); - } - } - - @Override - protected SoundEvent getHurtSound(){ - return AMSounds.WATER_GUARDIAN_HIT; - } - - @Override - protected SoundEvent getDeathSound(){ - return AMSounds.WATER_GUARDIAN_DEATH; - } - - @Override - protected SoundEvent getAmbientSound(){ - return AMSounds.WATER_GUARDIAN_IDLE; - } - - @Override - public SoundEvent getAttackSound(){ - return AMSounds.WATER_GUARDIAN_ATTACK; - } - - @Override - protected Color getBarColor() { - return Color.BLUE; - } -} +package am2.common.bosses; + +import am2.api.ArsMagicaAPI; +import am2.api.affinity.Affinity; +import am2.api.sources.DamageSourceFrost; +import am2.api.sources.DamageSourceLightning; +import am2.common.bosses.ai.EntityAICastSpell; +import am2.common.bosses.ai.EntityAIChaosWaterBolt; +import am2.common.bosses.ai.EntityAICloneSelf; +import am2.common.bosses.ai.EntityAISpinAttack; +import am2.common.defs.AMSounds; +import am2.common.defs.ItemDefs; +import am2.common.extensions.EntityExtension; +import am2.common.packet.AMNetHandler; +import am2.common.utils.NPCSpells; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.datasync.DataParameter; +import net.minecraft.network.datasync.DataSerializers; +import net.minecraft.network.datasync.EntityDataManager; +import net.minecraft.util.DamageSource; +import net.minecraft.util.SoundCategory; +import net.minecraft.util.SoundEvent; +import net.minecraft.world.BossInfo.Color; +import net.minecraft.world.World; + +public class EntityWaterGuardian extends AM2Boss { + + private EntityWaterGuardian master; + private final EntityWaterGuardian[] clones; + private float orbitRotation; + private boolean uberSpinAvailable = false; + + private static final DataParameter IS_CLONE = EntityDataManager.createKey(EntityWaterGuardian.class, DataSerializers.BOOLEAN); + + public float spinRotation = 0; + + public EntityWaterGuardian(World par1World){ + super(par1World); + this.currentAction = BossActions.IDLE; + this.master = null; + this.clones = new EntityWaterGuardian[2]; + this.setSize(1.0f, 2.0f); + EntityExtension.For(this).setMagicLevelWithMana(10); + } + + @Override + protected void applyEntityAttributes(){ + super.applyEntityAttributes(); + this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(75D); + } + + public void setClones(EntityWaterGuardian clone1, EntityWaterGuardian clone2){ + if (this.clones[0] != null) + this.clones[0].setDead(); + if (this.clones[1] != null) + this.clones[1].setDead(); + this.clones[0] = clone1; + this.clones[1] = clone2; + } + + private boolean hasClones(){ + return this.clones[0] != null || this.clones[1] != null; + } + + public void clearClones(){ + if (this.clones[0] != null){ + this.clones[0].setDead(); + } + if (this.clones[1] != null){ + this.clones[1].setDead(); + } + this.clones[0] = null; + this.clones[1] = null; + } + + private void enableUberAttack(){ + this.uberSpinAvailable = true; + } + + public void setMaster(EntityWaterGuardian master){ + this.dataManager.set(IS_CLONE, true); + this.master = master; + } + + public boolean isClone(){ + return this.dataManager.get(IS_CLONE); + } + + public void clearMaster(){ + this.master = null; + } + + @Override + protected void initSpecificAI(){ + this.tasks.addTask(2, new EntityAIChaosWaterBolt(this)); + this.tasks.addTask(3, new EntityAICloneSelf(this)); + this.tasks.addTask(4, new EntityAICastSpell<>(this, NPCSpells.instance.waterBolt, 12, 23, 5, BossActions.CASTING)); + this.tasks.addTask(3, new EntityAISpinAttack(this, 0.5f, 4)); + } + + @Override + public void onUpdate(){ + + if (this.currentAction == BossActions.CASTING){ + this.uberSpinAvailable = false; + } + + if (!this.worldObj.isRemote && this.uberSpinAvailable && this.currentAction != BossActions.IDLE){ + this.setCurrentAction(BossActions.IDLE); + } + + if (!this.worldObj.isRemote && this.isClone() && (this.master == null || this.ticksExisted > 400)){ + this.setDead(); + } + + if (this.worldObj.isRemote){ + this.updateRotations(); + } + super.onUpdate(); + } + + @Override + protected void entityInit(){ + super.entityInit(); + + this.dataManager.register(IS_CLONE, false); + } + + private void updateRotations(){ + if (!this.isClone()) + this.orbitRotation += 2f; + else + this.orbitRotation -= 2f; + this.orbitRotation %= 360; + + if (this.getCurrentAction() == BossActions.SPINNING || this.getCurrentAction() == BossActions.CASTING){ + this.spinRotation = (this.spinRotation - 30) % 360; + } + } + + public float getOrbitRotation(){ + return this.orbitRotation; + } + + @Override + public void setCurrentAction(BossActions action){ + super.setCurrentAction(action); + this.spinRotation = 0; + + if (!this.worldObj.isRemote){ + AMNetHandler.INSTANCE.sendActionUpdateToAllAround(this); + } + } + + @Override + public boolean attackEntityFrom(DamageSource par1DamageSource, float par2){ + if (par1DamageSource.getSourceOfDamage() instanceof EntityWaterGuardian) + return false; + if (par1DamageSource.damageType.equals(DamageSource.outOfWorld.damageType)) + return super.attackEntityFrom(par1DamageSource, par2); + if (this.isClone() && this.master != null){ + this.master.enableUberAttack(); + this.master.clearClones(); + }else if (this.hasClones()){ + this.clearClones(); + } + + if (!this.isClone() && this.rand.nextInt(10) < 6){ + this.worldObj.playSound(this.posX, this.posY, this.posZ, this.getAmbientSound(), SoundCategory.HOSTILE, 1.0f, 0.4f + this.rand.nextFloat() * 0.6f, false); + return false; + } + + return super.attackEntityFrom(par1DamageSource, par2); + } + + @Override + protected float modifyDamageAmount(DamageSource source, float damageAmt){ + if (source instanceof DamageSourceLightning) + damageAmt *= 2.0f; + if (source.getSourceOfDamage() != null && source.getSourceOfDamage() instanceof EntityWaterGuardian) + damageAmt = 0; + if (source instanceof DamageSourceFrost) + damageAmt = 0; + + return damageAmt; + } + + @Override + public boolean isActionValid(BossActions action){ + if (this.uberSpinAvailable && action != BossActions.CASTING) return false; + if (action == BossActions.CASTING){ + return this.uberSpinAvailable; + } + if (action == BossActions.CLONE){ + return !this.isClone(); + } + return true; + } + + @Override + public void writeEntityToNBT(NBTTagCompound par1nbtTagCompound){ + super.writeEntityToNBT(par1nbtTagCompound); + + par1nbtTagCompound.setBoolean("isClone", this.isClone()); + } + + @Override + public int getTotalArmorValue(){ + return 10; + } + + @Override + public void readEntityFromNBT(NBTTagCompound par1nbtTagCompound){ + super.readEntityFromNBT(par1nbtTagCompound); + + this.dataManager.set(IS_CLONE, par1nbtTagCompound.getBoolean("isClone")); + } + + + @Override + protected void dropFewItems(boolean par1, int par2){ + if (par1) + this.entityDropItem(new ItemStack(ItemDefs.infinityOrb, 1, 0), 0.0f); + + int i = this.rand.nextInt(4); + + for (int j = 0; j < i; j++){ + this.entityDropItem(new ItemStack(ItemDefs.essence, 1, ArsMagicaAPI.getAffinityRegistry().getId(Affinity.WATER)), 0.0f); + } + + i = this.rand.nextInt(10); + + if (i < 3){ + this.entityDropItem(ItemDefs.waterOrbsEnchanted.copy(), 0.0f); + } + } + + @Override + protected SoundEvent getHurtSound(){ + return AMSounds.WATER_GUARDIAN_HIT; + } + + @Override + protected SoundEvent getDeathSound(){ + return AMSounds.WATER_GUARDIAN_DEATH; + } + + @Override + protected SoundEvent getAmbientSound(){ + return AMSounds.WATER_GUARDIAN_IDLE; + } + + @Override + public SoundEvent getAttackSound(){ + return AMSounds.WATER_GUARDIAN_ATTACK; + } + + @Override + protected Color getBarColor() { + return Color.BLUE; + } +} diff --git a/src/main/java/am2/common/bosses/ai/EntityAICastSpell.java b/src/main/java/am2/common/bosses/ai/EntityAICastSpell.java index 9532aa4e8..1b3d6cfab 100644 --- a/src/main/java/am2/common/bosses/ai/EntityAICastSpell.java +++ b/src/main/java/am2/common/bosses/ai/EntityAICastSpell.java @@ -6,6 +6,7 @@ import am2.common.spell.SpellCaster; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.ai.EntityAIBase; +import net.minecraft.entity.ai.EntityAINearestAttackableTarget; import net.minecraft.item.ItemStack; import net.minecraft.util.SoundCategory; @@ -24,7 +25,7 @@ public class EntityAICastSpell extends private ISpellCastCallback callback; public EntityAICastSpell(T host, ItemStack spell, int castPoint, int duration, int cooldown, BossActions activeAction){ - this.host = (T)host; + this.host = host; this.stack = spell; this.castPoint = castPoint; this.duration = duration; @@ -35,7 +36,7 @@ public EntityAICastSpell(T host, ItemStack spell, int castPoint, int duration, i } public EntityAICastSpell(T host, ItemStack spell, int castPoint, int duration, int cooldown, BossActions activeAction, ISpellCastCallback callback){ - this.host = (T)host; + this.host = host; this.stack = spell; this.castPoint = castPoint; this.duration = duration; @@ -46,11 +47,10 @@ public EntityAICastSpell(T host, ItemStack spell, int castPoint, int duration, i @Override public boolean shouldExecute(){ - cooldownTicks--; - boolean execute = host.getCurrentAction() == BossActions.IDLE && host.getAttackTarget() != null && cooldownTicks <= 0; + boolean execute = this.host.getCurrentAction() == BossActions.IDLE && this.host.getAttackTarget() != null && --this.cooldownTicks <= 0; if (execute){ - if (callback == null || callback.shouldCast(host, stack)) - hasCasted = false; + if (this.callback == null || this.callback.shouldCast(this.host, this.stack)) + this.hasCasted = false; else execute = false; } @@ -59,50 +59,55 @@ public boolean shouldExecute(){ @Override public boolean continueExecuting(){ - return !hasCasted && host.getAttackTarget() != null && !host.getAttackTarget().isDead; + return !this.hasCasted && this.host.getAttackTarget() != null && !this.host.getAttackTarget().isDead; } @Override public void resetTask(){ - ((IArsMagicaBoss)host).setCurrentAction(BossActions.IDLE); - cooldownTicks = cooldown; - hasCasted = true; - castTicks = 0; + this.host.setCurrentAction(BossActions.IDLE); + this.cooldownTicks = this.cooldown; + this.hasCasted = true; + this.castTicks = 0; } @Override public void updateTask(){ - host.getLookHelper().setLookPositionWithEntity(host.getAttackTarget(), 30, 30); - if (host.getDistanceSqToEntity(host.getAttackTarget()) > 64){ + if (this.host.getAttackTarget() == null) { + resetTask(); + return; + } + + //this.host.getLookHelper().setLookPositionWithEntity(this.host.getAttackTarget(), 30, 30); + if (this.host.getDistanceSqToEntity(this.host.getAttackTarget()) > 64){ - double deltaZ = host.getAttackTarget().posZ - host.posZ; - double deltaX = host.getAttackTarget().posX - host.posX; + double deltaZ = this.host.getAttackTarget().posZ - this.host.posZ; + double deltaX = this.host.getAttackTarget().posX - this.host.posX; double angle = -Math.atan2(deltaZ, deltaX); - double newX = host.getAttackTarget().posX + (Math.cos(angle) * 6); - double newZ = host.getAttackTarget().posZ + (Math.sin(angle) * 6); + double newX = this.host.getAttackTarget().posX + (Math.cos(angle) * 6); + double newZ = this.host.getAttackTarget().posZ + (Math.sin(angle) * 6); - host.getNavigator().tryMoveToXYZ(newX, host.getAttackTarget().posY, newZ, 0.5f); - }else if (!host.canEntityBeSeen(host.getAttackTarget())){ - host.getNavigator().tryMoveToEntityLiving(host.getAttackTarget(), 0.5f); + this.host.getNavigator().tryMoveToXYZ(newX, this.host.getAttackTarget().posY, newZ, 0.5f); + //this.host.getNavigator().tryMoveToEntityLiving(this.host.getAttackTarget(), 0.5f); + }else if (!this.host.canEntityBeSeen(this.host.getAttackTarget())){ + this.host.getNavigator().tryMoveToEntityLiving(this.host.getAttackTarget(), 0.5f); }else{ - if (((IArsMagicaBoss)host).getCurrentAction() != activeAction) - ((IArsMagicaBoss)host).setCurrentAction(activeAction); - - castTicks++; - if (castTicks == castPoint){ - if (!host.worldObj.isRemote) - host.worldObj.playSound(host.posX, host.posY, host.posZ, ((IArsMagicaBoss)host).getAttackSound(), SoundCategory.HOSTILE, 1.0f, 1.0f, false); - host.faceEntity(host.getAttackTarget(), 180, 180); - ISpellCaster spell = stack.getCapability(SpellCaster.INSTANCE, null); + if (this.host.getCurrentAction() != this.activeAction) + this.host.setCurrentAction(this.activeAction); + + if (++this.castTicks == this.castPoint){ + if (!this.host.worldObj.isRemote) + this.host.worldObj.playSound(this.host.posX, this.host.posY, this.host.posZ, this.host.getAttackSound(), SoundCategory.HOSTILE, 1.0f, 1.0f, false); + this.host.faceEntity(this.host.getAttackTarget(), 180, 180); + ISpellCaster spell = this.stack.getCapability(SpellCaster.INSTANCE, null); if (spell != null) { - spell.cast(stack, host.worldObj, host); + spell.cast(this.stack, this.host.worldObj, this.host); } } } - if (castTicks >= duration){ - resetTask(); + if (this.castTicks >= this.duration){ + this.resetTask(); } } } diff --git a/src/main/java/am2/common/bosses/ai/EntityAICloneSelf.java b/src/main/java/am2/common/bosses/ai/EntityAICloneSelf.java index 121c51058..cc36f2dff 100644 --- a/src/main/java/am2/common/bosses/ai/EntityAICloneSelf.java +++ b/src/main/java/am2/common/bosses/ai/EntityAICloneSelf.java @@ -1,56 +1,61 @@ -package am2.common.bosses.ai; - -import am2.common.bosses.BossActions; -import am2.common.bosses.EntityWaterGuardian; -import net.minecraft.entity.EntityLivingBase; -import net.minecraft.entity.ai.EntityAIBase; - -public class EntityAICloneSelf extends EntityAIBase{ - private final EntityWaterGuardian host; - private int cooldownTicks = 0; - - public EntityAICloneSelf(EntityWaterGuardian host){ - this.host = host; - this.setMutexBits(1); - } - - @Override - public boolean shouldExecute(){ - if (cooldownTicks-- > 0 || host.getCurrentAction() != BossActions.IDLE || !host.isActionValid(BossActions.CLONE)) - return false; - EntityLivingBase AITarget = host.getAttackTarget(); - if (AITarget == null || AITarget.isDead) return false; - return true; - } - - @Override - public boolean continueExecuting(){ - if (host.getCurrentAction() == BossActions.CLONE && host.getTicksInCurrentAction() > host.getCurrentAction().getMaxActionTime()){ - host.setCurrentAction(BossActions.IDLE); - cooldownTicks = 200; - return false; - } - return true; - } - - @Override - public void updateTask(){ - if (host.getCurrentAction() != BossActions.CLONE) - host.setCurrentAction(BossActions.CLONE); - - if (!host.worldObj.isRemote && host.getCurrentAction() == BossActions.CLONE && host.getTicksInCurrentAction() == 30){ - EntityWaterGuardian clone1 = spawnClone(); - EntityWaterGuardian clone2 = spawnClone(); - - host.setClones(clone1, clone2); - } - } - - private EntityWaterGuardian spawnClone(){ - EntityWaterGuardian clone = new EntityWaterGuardian(host.worldObj); - clone.setMaster(host); - clone.setPosition(host.posX, host.posY, host.posZ); - host.worldObj.spawnEntityInWorld(clone); - return clone; - } -} +package am2.common.bosses.ai; + +import am2.common.bosses.BossActions; +import am2.common.bosses.EntityWaterGuardian; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.ai.EntityAIBase; + +public class EntityAICloneSelf extends EntityAIBase{ + private final EntityWaterGuardian host; + private int cooldownTicks = 0; + + public EntityAICloneSelf(EntityWaterGuardian host){ + this.host = host; + this.setMutexBits(1); + } + + @Override + public boolean shouldExecute(){ + if (cooldownTicks-- > 0 || host.getCurrentAction() != BossActions.IDLE || !host.isActionValid(BossActions.CLONE) || host.isClone()) + return false; + EntityLivingBase AITarget = host.getAttackTarget(); + if (AITarget == null || AITarget.isDead) return false; + return true; + } + + @Override + public boolean continueExecuting(){ + if (host.isClone()) + return false; + if (host.getCurrentAction() == BossActions.CLONE && host.getTicksInCurrentAction() > host.getCurrentAction().getMaxActionTime()){ + host.setCurrentAction(BossActions.IDLE); + cooldownTicks = 200; + return false; + } + return true; + } + + @Override + public void updateTask(){ + if (host.isClone()) + return; + + if (host.getCurrentAction() != BossActions.CLONE) + host.setCurrentAction(BossActions.CLONE); + + if (!host.worldObj.isRemote && host.getCurrentAction() == BossActions.CLONE && host.getTicksInCurrentAction() == 30){ + EntityWaterGuardian clone1 = spawnClone(); + EntityWaterGuardian clone2 = spawnClone(); + host.setClones(clone1, clone2); + this.cooldownTicks = 200; + } + } + + private EntityWaterGuardian spawnClone(){ + EntityWaterGuardian clone = new EntityWaterGuardian(host.worldObj); + clone.setMaster(host); + clone.setPosition(host.posX, host.posY, host.posZ); + host.worldObj.spawnEntityInWorld(clone); + return clone; + } +} diff --git a/src/main/java/am2/common/items/ItemEssence.java b/src/main/java/am2/common/items/ItemEssence.java index 70ca72927..f0ad29005 100644 --- a/src/main/java/am2/common/items/ItemEssence.java +++ b/src/main/java/am2/common/items/ItemEssence.java @@ -32,6 +32,6 @@ public void getSubItems(Item itemIn, CreativeTabs tab, List subItems) @Override @SideOnly(Side.CLIENT) public String getItemStackDisplayName(ItemStack stack) { - return String.format(I18n.format("item.arsmagica2:essence.name"), ArsMagicaAPI.getAffinityRegistry().getObjectById(stack.getItemDamage()).getLocalizedName()); + return I18n.format("item.arsmagica2:essence.name", ArsMagicaAPI.getAffinityRegistry().getObjectById(stack.getItemDamage()).getLocalizedName()); } }