Package vazkii.botania.common.entity

Source Code of vazkii.botania.common.entity.EntityDoppleganger

/**
* This class was created by <Vazkii>. It's distributed as
* part of the Botania Mod. Get the Source Code in github:
* https://github.com/Vazkii/Botania
*
* Botania is Open Source and distributed under a
* Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License
* (http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_GB)
*
* File Created @ [Jul 12, 2014, 3:47:45 PM (GMT)]
*/
package vazkii.botania.common.entity;

import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import net.minecraft.block.Block;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityCreature;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.ai.EntityAISwimming;
import net.minecraft.entity.ai.EntityAIWatchClosest;
import net.minecraft.entity.monster.EntityCaveSpider;
import net.minecraft.entity.monster.EntitySkeleton;
import net.minecraft.entity.monster.EntitySpider;
import net.minecraft.entity.monster.EntityWitch;
import net.minecraft.entity.monster.EntityZombie;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.potion.Potion;
import net.minecraft.potion.PotionEffect;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.util.ChatStyle;
import net.minecraft.util.ChunkCoordinates;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.EnumDifficulty;
import net.minecraft.world.World;
import net.minecraftforge.common.util.FakePlayer;

import org.lwjgl.opengl.ARBShaderObjects;

import vazkii.botania.api.boss.IBotaniaBossWithShader;
import vazkii.botania.api.internal.ShaderCallback;
import vazkii.botania.client.core.handler.BossBarHandler;
import vazkii.botania.client.core.helper.ShaderHelper;
import vazkii.botania.common.Botania;
import vazkii.botania.common.block.ModBlocks;
import vazkii.botania.common.core.helper.Vector3;
import vazkii.botania.common.item.ModItems;
import vazkii.botania.common.lib.LibObfuscation;
import cpw.mods.fml.relauncher.ReflectionHelper;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;

public class EntityDoppleganger extends EntityCreature implements IBotaniaBossWithShader {

  public static final int SPAWN_TICKS = 100;
 
  public static final int MOB_SPAWN_START_TICKS = 20;
  public static final int MOB_SPAWN_END_TICKS = 80;
  public static final int MOB_SPAWN_BASE_TICKS = 800;
  public static final int MOB_SPAWN_TICKS = MOB_SPAWN_BASE_TICKS + MOB_SPAWN_START_TICKS + MOB_SPAWN_END_TICKS;
  public static final int MOB_SPAWN_WAVES = 10;
  public static final int MOB_SPAWN_WAVE_TIME = MOB_SPAWN_BASE_TICKS / MOB_SPAWN_WAVES;

  private static final float MAX_HP = 300F;

  private static final String TAG_INVUL_TIME = "invulTime";
  private static final String TAG_AGGRO = "aggro";
  private static final String TAG_SOURCE_X = "sourceX";
  private static final String TAG_SOURCE_Y = "sourceY";
  private static final String TAG_SOURCE_Z = "sourcesZ";
  private static final String TAG_MOB_SPAWN_TICKS = "mobSpawnTicks";

  private static final int[][] PYLON_LOCATIONS = new int[][] {
    { 4, 1, 4 },
    { 4, 1, -4 },
    { -4, 1, 4 },
    { -4, 1, -4 }
  };

  boolean spawnLandmines = false;
  boolean spawnPixies = false;

  public EntityDoppleganger(World par1World) {
    super(par1World);
    setSize(0.6F, 1.8F);
    getNavigator().setCanSwim(true);
    tasks.addTask(0, new EntityAISwimming(this));
    tasks.addTask(1, new EntityAIWatchClosest(this, EntityPlayer.class, Float.MAX_VALUE));
    isImmuneToFire = true;
    experienceValue = 825;
  }

  public static boolean spawn(EntityPlayer player, ItemStack par1ItemStack, World par3World, int par4, int par5, int par6) {
    Block block = par3World.getBlock(par4, par5, par6);
    if(block == Blocks.beacon && !par3World.isRemote) {
      if(par3World.difficultySetting == EnumDifficulty.PEACEFUL) {
        player.addChatMessage(new ChatComponentTranslation("botaniamisc.peacefulNoob").setChatStyle(new ChatStyle().setColor(EnumChatFormatting.RED)));
        return false;
      }

      for(int[] coords : PYLON_LOCATIONS) {
        int x = par4 + coords[0];
        int y = par5 + coords[1];
        int z = par6 + coords[2];

        Block blockat = par3World.getBlock(x, y, z);
        int meta = par3World.getBlockMetadata(x, y, z);
        if(blockat != ModBlocks.pylon || meta != 2) {
          player.addChatMessage(new ChatComponentTranslation("botaniamisc.needsCatalysts").setChatStyle(new ChatStyle().setColor(EnumChatFormatting.RED)));
          return false;
        }
      }

      par1ItemStack.stackSize--;
      EntityDoppleganger e = new EntityDoppleganger(par3World);
      e.setPosition(par4 + 0.5, par5 + 3, par6 + 0.5);
      e.setInvulTime(SPAWN_TICKS);
      e.setHealth(1F);
      e.setSource(par4, par5, par6);
      e.setMobSpawnTicks(MOB_SPAWN_TICKS);
      par3World.playSoundAtEntity(e, "mob.enderdragon.growl", 10F, 0.1F);
      par3World.spawnEntityInWorld(e);
      return true;
    }

    return false;
  }

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

  @Override
  protected void entityInit() {
    super.entityInit();
    dataWatcher.addObject(20, 0); // Invul Time
    dataWatcher.addObject(21, (byte) 0); // Aggro
    dataWatcher.addObject(22, 0); // TP Delay
    dataWatcher.addObject(23, 0); // Source X
    dataWatcher.addObject(24, 0); // Source Y
    dataWatcher.addObject(25, 0); // Source Z
    dataWatcher.addObject(26, 0); // Ticks spawning mobs
  }

  public int getInvulTime() {
    return dataWatcher.getWatchableObjectInt(20);
  }

  public boolean isAggored() {
    return dataWatcher.getWatchableObjectByte(21) == 1;
  }

  public int getTPDelay() {
    return dataWatcher.getWatchableObjectInt(22);
  }

  public ChunkCoordinates getSource() {
    int x = dataWatcher.getWatchableObjectInt(23);
    int y = dataWatcher.getWatchableObjectInt(24);
    int z = dataWatcher.getWatchableObjectInt(25);
    return new ChunkCoordinates(x, y, z);
  }

  public int getMobSpawnTicks() {
    return dataWatcher.getWatchableObjectInt(26);
  }

  public void setInvulTime(int time) {
    dataWatcher.updateObject(20, time);
  }

  public void setAggroed(boolean aggored) {
    dataWatcher.updateObject(21, (byte) (aggored ? 1 : 0));
  }

  public void setTPDelay(int delay) {
    dataWatcher.updateObject(22, delay);
  }

  public void setSource(int x, int y, int z) {
    dataWatcher.updateObject(23, x);
    dataWatcher.updateObject(24, y);
    dataWatcher.updateObject(25, z);
  }

  public void setMobSpawnTicks(int ticks) {
    dataWatcher.updateObject(26, ticks);
  }

  @Override
  public void writeEntityToNBT(NBTTagCompound par1nbtTagCompound) {
    super.writeEntityToNBT(par1nbtTagCompound);
    par1nbtTagCompound.setInteger(TAG_INVUL_TIME, getInvulTime());
    par1nbtTagCompound.setBoolean(TAG_AGGRO, isAggored());
    par1nbtTagCompound.setInteger(TAG_MOB_SPAWN_TICKS, getMobSpawnTicks());

    ChunkCoordinates source = getSource();
    par1nbtTagCompound.setInteger(TAG_SOURCE_X, source.posX);
    par1nbtTagCompound.setInteger(TAG_SOURCE_Y, source.posY);
    par1nbtTagCompound.setInteger(TAG_SOURCE_Z, source.posZ);
  }

  @Override
  public void readEntityFromNBT(NBTTagCompound par1nbtTagCompound) {
    super.readEntityFromNBT(par1nbtTagCompound);
    setInvulTime(par1nbtTagCompound.getInteger(TAG_INVUL_TIME));
    setAggroed(par1nbtTagCompound.getBoolean(TAG_AGGRO));
    setMobSpawnTicks(par1nbtTagCompound.getInteger(TAG_MOB_SPAWN_TICKS));

    int x = par1nbtTagCompound.getInteger(TAG_SOURCE_X);
    int y = par1nbtTagCompound.getInteger(TAG_SOURCE_Y);
    int z = par1nbtTagCompound.getInteger(TAG_SOURCE_Z);
    setSource(x, y, z);
  }

  @Override
  public boolean attackEntityFrom(DamageSource par1DamageSource, float par2) {
    if((par1DamageSource.damageType.equals("player") || par1DamageSource.getEntity() instanceof EntityPixie) && par1DamageSource.getEntity() != null && !(par1DamageSource.getEntity() instanceof FakePlayer) && getInvulTime() == 0)
      return super.attackEntityFrom(par1DamageSource, par2);
    return false;
  }

  @Override
  protected void damageEntity(DamageSource par1DamageSource, float par2) {
    super.damageEntity(par1DamageSource, par2);

    Entity attacker = par1DamageSource.getEntity();
    if(attacker != null) {
      Vector3 thisVector = Vector3.fromEntityCenter(this);
      Vector3 playerVector = Vector3.fromEntityCenter(attacker);
      Vector3 motionVector = thisVector.copy().sub(playerVector).copy().normalize().multiply(0.75);

      if(getHealth() > 0) {
        motionX = -motionVector.x;
        motionY = 0.5;
        motionZ = -motionVector.z;
        setTPDelay(4);
        spawnPixies = isAggored();
      }

      setAggroed(true);
    }
  }

  @Override
  protected void applyEntityAttributes() {
    super.applyEntityAttributes();
    getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(0.4);
    getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue(MAX_HP);
    getEntityAttribute(SharedMonsterAttributes.knockbackResistance).setBaseValue(1.0);
  }

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

  @Override
  protected void dropFewItems(boolean par1, int par2) {
    if(par1)
      entityDropItem(new ItemStack(ModItems.manaResource, 8, 5), 1F);
  }

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

    if(!worldObj.isRemote && worldObj.difficultySetting == EnumDifficulty.PEACEFUL)
      setDead();

    ChunkCoordinates source = getSource();
   
    float range = 32F;
    List<EntityPlayer> players = worldObj.getEntitiesWithinAABB(EntityPlayer.class, AxisAlignedBB.getBoundingBox(source.posX + 0.5 - range, source.posY + 0.5 - range, source.posZ + 0.5 - range, source.posX + 0.5 + range, source.posY + 0.5 + range, source.posZ + 0.5 + range));

    range = 12F;
    for(int i = 0; i < 360; i += 8) {
      float r = 0.6F;
      float g = 0F;
      float b = 0.2F;
      float m = 0.15F;
      float mv = 0.35F;

      float rad = (float) i * (float) Math.PI / 180F;
      double x = source.posX - 0.5 - Math.cos(rad) * range;
      double y = source.posY - 0.5;
      double z = source.posZ - 0.5 - Math.sin(rad) * range;
     
      Botania.proxy.wispFX(worldObj, x, y, z, r, g, b, 0.5F, (float) (Math.random() - 0.5F) * m, (float) (Math.random() - 0.5F) * mv, (float) (Math.random() - 0.5F) * m);

    }
   
    if(players.isEmpty() && !worldObj.playerEntities.isEmpty())
      setDead();
    else for(EntityPlayer player : players) {
      List<PotionEffect> remove = new ArrayList();
      Collection<PotionEffect> active = player.getActivePotionEffects();
      for(PotionEffect effect : active)
        if(effect.getDuration() < 200 && !ReflectionHelper.<Boolean, Potion>getPrivateValue(Potion.class, Potion.potionTypes[effect.getPotionID()], LibObfuscation.IS_BAD_EFFECT))
          remove.add(effect);

      active.removeAll(remove);

      player.capabilities.isFlying = player.capabilities.isFlying && player.capabilities.isCreativeMode;

      if(vazkii.botania.common.core.helper.MathHelper.pointDistanceSpace(player.posX, player.posY, player.posZ, source.posX + 0.5, source.posY + 0.5, source.posZ + 0.5) >= range) {
        Vector3 sourceVector = new Vector3(source.posX + 0.5, source.posY + 0.5, source.posZ + 0.5);
        Vector3 playerVector = Vector3.fromEntityCenter(player);
        Vector3 motion = sourceVector.copy().sub(playerVector).copy().normalize();

        player.motionX = motion.x;
        player.motionY = 0.2;
        player.motionZ = motion.z;
      }
    }

    if(isDead)
      return;

    int invul = getInvulTime();
    int mobTicks = getMobSpawnTicks();

    if(invul > 0 && mobTicks == MOB_SPAWN_TICKS) {
      if(invul < SPAWN_TICKS && invul > SPAWN_TICKS / 2 && worldObj.rand.nextInt(SPAWN_TICKS - invul + 1) == 0)
        for(int i = 0; i < 2; i++)
          spawnExplosionParticle();

      setHealth(getHealth() + (MAX_HP - 1F) / SPAWN_TICKS);
      setInvulTime(invul - 1);
      motionY = 0;
    } else {
      if(isAggored()) {
        boolean dying = getHealth() / getMaxHealth() < 0.2;
        if(dying && mobTicks > 0) {
          motionX = 0;
          motionY = 0;
          motionZ = 0;

          int reverseTicks = MOB_SPAWN_TICKS - mobTicks;
          if(reverseTicks < MOB_SPAWN_START_TICKS) {
            motionY = 0.2;
            setInvulTime(invul + 1);
          }

          if(reverseTicks > MOB_SPAWN_START_TICKS * 2 && mobTicks > MOB_SPAWN_END_TICKS && mobTicks % MOB_SPAWN_WAVE_TIME == 0 && !worldObj.isRemote) {
            for(int i = 0; i < 3 + worldObj.rand.nextInt(2); i++) {
              EntityLiving entity = null;
              switch(worldObj.rand.nextInt(2)) {
              case 0 : {
                entity = new EntityZombie(worldObj);
                if(worldObj.rand.nextInt(12) == 0)
                  entity = new EntityWitch(worldObj);
               
                break;
              }
              case 1 : {
                entity = new EntitySkeleton(worldObj);
                ((EntitySkeleton) entity).setCurrentItemOrArmor(0, new ItemStack(Items.bow));
                if(worldObj.rand.nextInt(8) == 0) {
                  ((EntitySkeleton) entity).setSkeletonType(1);
                  ((EntitySkeleton) entity).setCurrentItemOrArmor(0, new ItemStack(Items.stone_sword));
                }
                break;
              }
              case 3 : {
                for(int j = 0; j < 1 + worldObj.rand.nextInt(5); j++) {
                  EntityPixie pixie = new EntityPixie(worldObj);
                  pixie.setProps(players.get(rand.nextInt(players.size())), this, 1, 8);
                  pixie.setPosition(posX + width / 2, posY + 2, posZ + width / 2);
                  worldObj.spawnEntityInWorld(pixie);
                }
              }
              }
             
              if(entity != null) {
                range = 6F;
                entity.setPosition(posX + 0.5 + Math.random() * range - range / 2, posY - 1, posZ + 0.5 + Math.random() * range - range / 2);
                worldObj.spawnEntityInWorld(entity);
              }
            }
          }

          setMobSpawnTicks(mobTicks - 1);
          setTPDelay(10);
        } else if(getTPDelay() > 0 && !worldObj.isRemote) {
          if(invul > 0)
            setInvulTime(invul - 1);
         
          setTPDelay(getTPDelay() - 1);
          if(getTPDelay() == 0 && getHealth() > 0) {
            int tries = 0;
            while(!teleportRandomly() && tries < 50)
              tries++;
            if(tries >= 50)
              teleportTo(source.posX + 0.5, source.posY + 1.6, source.posZ + 0.5);

            if(spawnLandmines)
              for(int i = 0; i < 6; i++) {
                int x = source.posX - 10 + rand.nextInt(20);
                int z = source.posZ - 10 + rand.nextInt(20);
                int y = worldObj.getTopSolidOrLiquidBlock(x, z);

                EntityMagicLandmine landmine = new EntityMagicLandmine(worldObj);
                landmine.setPosition(x + 0.5, y, z + 0.5);
                landmine.summoner = this;
                worldObj.spawnEntityInWorld(landmine);
              }

            for(int i = 0; i < (spawnPixies ? worldObj.rand.nextInt(3) : 1); i++) {
              EntityPixie pixie = new EntityPixie(worldObj);
              pixie.setProps(players.get(rand.nextInt(players.size())), this, 1, 8);
              pixie.setPosition(posX + width / 2, posY + 2, posZ + width / 2);
              worldObj.spawnEntityInWorld(pixie);
            }

            setTPDelay(dying ? 40 : 60);
            spawnLandmines = true;
            spawnPixies = false;
          }
        }
      } else {
        range = 3F;
        players = worldObj.getEntitiesWithinAABB(EntityPlayer.class, AxisAlignedBB.getBoundingBox(posX - range, posY - range, posZ - range, posX + range, posY + range, posZ + range));
        if(!players.isEmpty())
          damageEntity(DamageSource.causePlayerDamage(players.get(0)), 0);
      }
    }
  }

  // EntityEnderman code below ============================================================================

  protected boolean teleportRandomly() {
    double d0 = posX + (rand.nextDouble() - 0.5D) * 64.0D;
    double d1 = posY + (rand.nextInt(64) - 32);
    double d2 = posZ + (rand.nextDouble() - 0.5D) * 64.0D;
    return teleportTo(d0, d1, d2);
  }

  protected boolean teleportTo(double par1, double par3, double par5) {
    double d3 = posX;
    double d4 = posY;
    double d5 = posZ;
    posX = par1;
    posY = par3;
    posZ = par5;
    boolean flag = false;
    int i = MathHelper.floor_double(posX);
    int j = MathHelper.floor_double(posY);
    int k = MathHelper.floor_double(posZ);

    if(worldObj.blockExists(i, j, k)) {
      boolean flag1 = false;

      while(!flag1 && j > 0) {
        Block block = worldObj.getBlock(i, j - 1, k);

        if(block.getMaterial().blocksMovement())
          flag1 = true;
        else {
          --posY;
          --j;
        }
      }

      if(flag1) {
        setPosition(posX, posY, posZ);

        if(worldObj.getCollidingBoundingBoxes(this, boundingBox).isEmpty() && !worldObj.isAnyLiquid(boundingBox))
          flag = true;

        // Prevent out of bounds teleporting
        ChunkCoordinates source = getSource();
        if(vazkii.botania.common.core.helper.MathHelper.pointDistanceSpace(posX, posY, posZ, source.posX, source.posY, source.posZ) > 12)
          flag = false;
      }
    }

    if (!flag) {
      setPosition(d3, d4, d5);
      return false;
    } else  {
      short short1 = 128;

      for(int l = 0; l < short1; ++l)  {
        double d6 = l / (short1 - 1.0D);
        float f = (rand.nextFloat() - 0.5F) * 0.2F;
        float f1 = (rand.nextFloat() - 0.5F) * 0.2F;
        float f2 = (rand.nextFloat() - 0.5F) * 0.2F;
        double d7 = d3 + (posX - d3) * d6 + (rand.nextDouble() - 0.5D) * width * 2.0D;
        double d8 = d4 + (posY - d4) * d6 + rand.nextDouble() * height;
        double d9 = d5 + (posZ - d5) * d6 + (rand.nextDouble() - 0.5D) * width * 2.0D;
        worldObj.spawnParticle("portal", d7, d8, d9, f, f1, f2);
      }

      worldObj.playSoundEffect(d3, d4, d5, "mob.endermen.portal", 1.0F, 1.0F);
      playSound("mob.endermen.portal", 1.0F, 1.0F);
      return true;
    }
  }

  @Override
  @SideOnly(Side.CLIENT)
  public ResourceLocation getBossBarTexture() {
    return BossBarHandler.defaultBossBar;
  }

  @SideOnly(Side.CLIENT)
  private static Rectangle barRect;
  @SideOnly(Side.CLIENT)
  private static Rectangle hpBarRect;

  @Override
  @SideOnly(Side.CLIENT)
  public Rectangle getBossBarTextureRect() {
    if(barRect == null)
      barRect = new Rectangle(0, 0, 185, 15);
    return barRect;
  }

  @Override
  @SideOnly(Side.CLIENT)
  public Rectangle getBossBarHPTextureRect() {
    if(hpBarRect == null)
      hpBarRect = new Rectangle(0, barRect.y + barRect.height, 181, 7);
    return hpBarRect;
  }

  @Override
  @SideOnly(Side.CLIENT)
  public void bossBarRenderCallback() {
    // NO-OP
  }

  @Override
  @SideOnly(Side.CLIENT)
  public int getBossBarShaderProgram(boolean background) {
    return background ? 0 : ShaderHelper.dopplegangerBar;
  }

  @SideOnly(Side.CLIENT)
  private ShaderCallback shaderCallback;

  @Override
  @SideOnly(Side.CLIENT)
  public ShaderCallback getBossBarShaderCallback(boolean background, int shader) {
    if(shaderCallback == null)
      shaderCallback = new ShaderCallback() {

      @Override
      public void call(int shader) {
        int grainIntensityUniform = ARBShaderObjects.glGetUniformLocationARB(shader, "grainIntensity");
        int hpFractUniform = ARBShaderObjects.glGetUniformLocationARB(shader, "hpFract");

        float time = getInvulTime();
        float grainIntensity = time > 20 ? 1F : time / 20F;

        ARBShaderObjects.glUniform1fARB(grainIntensityUniform, grainIntensity);
        ARBShaderObjects.glUniform1fARB(hpFractUniform, (float) getHealth() / (float) getMaxHealth());
      }

    };

    return background ? null : shaderCallback;
  }
}
TOP

Related Classes of vazkii.botania.common.entity.EntityDoppleganger

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.