package mekanism.common.entity; import io.netty.buffer.ByteBuf; import java.util.UUID; import mekanism.api.Coord4D; import mekanism.api.EnumColor; import mekanism.api.Pos3D; import mekanism.common.MekanismSounds; import net.minecraft.client.Minecraft; import net.minecraft.client.particle.Particle; import net.minecraft.client.particle.ParticleRedstone; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; 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.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; public class EntityBalloon extends Entity implements IEntityAdditionalSpawnData { public EnumColor color = EnumColor.DARK_BLUE; public Coord4D latched; public EntityLivingBase latchedEntity; /* server-only */ public boolean hasCachedEntity; public UUID cachedEntityUUID; private static final DataParameter<Byte> IS_LATCHED = EntityDataManager.<Byte>createKey(EntityBalloon.class, DataSerializers.BYTE); private static final DataParameter<Integer> LATCHED_X = EntityDataManager.<Integer>createKey(EntityBalloon.class, DataSerializers.VARINT); private static final DataParameter<Integer> LATCHED_Y = EntityDataManager.<Integer>createKey(EntityBalloon.class, DataSerializers.VARINT); private static final DataParameter<Integer> LATCHED_Z = EntityDataManager.<Integer>createKey(EntityBalloon.class, DataSerializers.VARINT); private static final DataParameter<Integer> LATCHED_ID = EntityDataManager.<Integer>createKey(EntityBalloon.class, DataSerializers.VARINT); public EntityBalloon(World world) { super(world); ignoreFrustumCheck = true; preventEntitySpawning = true; setPosition(posX + 0.5F, posY + 3F, posZ + 0.5F); setSize(0.25F, 0.25F); motionY = 0.04; dataManager.register(IS_LATCHED, (byte)0); dataManager.register(LATCHED_X, 0); dataManager.register(LATCHED_Y, 0); dataManager.register(LATCHED_Z, 0); dataManager.register(LATCHED_ID, -1); } public EntityBalloon(World world, double x, double y, double z, EnumColor c) { this(world); setPosition(x + 0.5F, y + 3F, z + 0.5F); prevPosX = posX; prevPosY = posY; prevPosZ = posZ; color = c; } public EntityBalloon(EntityLivingBase entity, EnumColor c) { this(entity.worldObj); latchedEntity = entity; setPosition(latchedEntity.posX, latchedEntity.posY + latchedEntity.height + 1.7F, latchedEntity.posZ); prevPosX = posX; prevPosY = posY; prevPosZ = posZ; color = c; dataManager.set(IS_LATCHED, (byte)2); dataManager.set(LATCHED_ID, entity.getEntityId()); } public EntityBalloon(World world, Coord4D obj, EnumColor c) { this(world); latched = obj; setPosition(latched.xCoord + 0.5F, latched.yCoord + 1.9F, latched.zCoord + 0.5F); prevPosX = posX; prevPosY = posY; prevPosZ = posZ; color = c; dataManager.set(IS_LATCHED, (byte)1); dataManager.set(LATCHED_X, latched != null ? latched.xCoord : 0); /* Latched X */ dataManager.set(LATCHED_Y, latched != null ? latched.yCoord : 0); /* Latched Y */ dataManager.set(LATCHED_Z, latched != null ? latched.zCoord : 0); /* Latched Z */ } @Override public void onUpdate() { prevPosX = posX; prevPosY = posY; prevPosZ = posZ; if(posY > 255) { pop(); return; } if(worldObj.isRemote) { if(dataManager.get(IS_LATCHED) == 1) { latched = new Coord4D((int)dataManager.get(LATCHED_X), (int)dataManager.get(LATCHED_Y), (int)dataManager.get(LATCHED_Z), worldObj.provider.getDimension()); } else { latched = null; } if(dataManager.get(IS_LATCHED) == 2) { latchedEntity = (EntityLivingBase)worldObj.getEntityByID(dataManager.get(LATCHED_ID)); } else { latchedEntity = null; } } else { if(hasCachedEntity) { findCachedEntity(); cachedEntityUUID = null; hasCachedEntity = false; } if(ticksExisted == 1) { dataManager.set(IS_LATCHED, new Byte(latched != null ? (byte)1 : (latchedEntity != null ? (byte)2 : (byte)0))); dataManager.set(LATCHED_X, new Integer(latched != null ? latched.xCoord : 0)); dataManager.set(LATCHED_Y, new Integer(latched != null ? latched.yCoord : 0)); dataManager.set(LATCHED_Z, new Integer(latched != null ? latched.zCoord : 0)); dataManager.set(LATCHED_ID, new Integer(latchedEntity != null ? latchedEntity.getEntityId() : -1)); } } if(!worldObj.isRemote) { if(latched != null && (latched.exists(worldObj) && latched.isAirBlock(worldObj))) { latched = null; dataManager.set(IS_LATCHED, (byte)0); } if(latchedEntity != null && (latchedEntity.getHealth() <= 0 || latchedEntity.isDead || !worldObj.loadedEntityList.contains(latchedEntity))) { latchedEntity = null; dataManager.set(IS_LATCHED, (byte)0); } } if(!isLatched()) { motionY = Math.min(motionY*1.02F, 0.2F); moveEntity(motionX, motionY, motionZ); motionX *= 0.98; motionZ *= 0.98; if(onGround) { motionX *= 0.7; motionZ *= 0.7; } if(motionY == 0) { motionY = 0.04; } } else if(latched != null) { motionX = 0; motionY = 0; motionZ = 0; } else if(latchedEntity != null && latchedEntity.getHealth() > 0) { int floor = getFloor(latchedEntity); if(latchedEntity.posY-(floor+1) < -0.1) { latchedEntity.motionY = Math.max(0.04, latchedEntity.motionY*1.015); } else if(latchedEntity.posY-(floor+1) > 0.1) { latchedEntity.motionY = Math.min(-0.04, latchedEntity.motionY*1.015); } else { latchedEntity.motionY = 0; } setPosition(latchedEntity.posX, latchedEntity.posY + getAddedHeight(), latchedEntity.posZ); } } public double getAddedHeight() { return latchedEntity.height + 0.8; } private int getFloor(EntityLivingBase entity) { BlockPos pos = new BlockPos(entity); for(BlockPos posi = pos; posi.getY() > 0; posi = posi.down()) { if(posi.getY() < 256 && !worldObj.isAirBlock(posi)) { return posi.getY()+1+(entity instanceof EntityPlayer ? 1 : 0); } } return -1; } private void findCachedEntity() { for(Object obj : worldObj.loadedEntityList) { if(obj instanceof EntityLivingBase) { EntityLivingBase entity = (EntityLivingBase)obj; if(entity.getUniqueID().equals(cachedEntityUUID)) { latchedEntity = entity; } } } } private void pop() { playSound(MekanismSounds.POP, 1, 1); if(worldObj.isRemote) { for(int i = 0; i < 10; i++) { try { doParticle(); } catch(Throwable t) {} } } setDead(); } @SideOnly(Side.CLIENT) private void doParticle() { Pos3D pos = new Pos3D(posX + (rand.nextFloat()*.6 - 0.3), posY + (rand.nextFloat()*.6 - 0.3), posZ + (rand.nextFloat()*.6 - 0.3)); Particle fx = new ParticleRedstone.Factory().getEntityFX(0, worldObj, pos.xCoord, pos.yCoord, pos.zCoord, 0, 0, 0); fx.setRBGColorF(color.getColor(0), color.getColor(1), color.getColor(2)); Minecraft.getMinecraft().effectRenderer.addEffect(fx); } @Override public boolean canBePushed() { return latched == null; } @Override public boolean canBeCollidedWith() { return !isDead; } @Override protected boolean canTriggerWalking() { return false; } @Override protected void entityInit() {} @Override protected void readEntityFromNBT(NBTTagCompound nbtTags) { color = EnumColor.values()[nbtTags.getInteger("color")]; if(nbtTags.hasKey("latched")) { latched = Coord4D.read(nbtTags.getCompoundTag("latched")); } if(nbtTags.hasKey("idMost")) { hasCachedEntity = true; cachedEntityUUID = new UUID(nbtTags.getLong("idMost"), nbtTags.getLong("idLeast")); } } @Override protected void writeEntityToNBT(NBTTagCompound nbtTags) { nbtTags.setInteger("color", color.ordinal()); if(latched != null) { nbtTags.setTag("latched", latched.write(new NBTTagCompound())); } if(latchedEntity != null) { nbtTags.setLong("idMost", latchedEntity.getUniqueID().getMostSignificantBits()); nbtTags.setLong("idLeast", latchedEntity.getUniqueID().getLeastSignificantBits()); } } @Override public boolean hitByEntity(Entity entity) { pop(); return true; } @Override public void writeSpawnData(ByteBuf data) { data.writeDouble(posX); data.writeDouble(posY); data.writeDouble(posZ); data.writeInt(color.ordinal()); if(latched != null) { data.writeByte((byte)1); latched.write(data); } else if(latchedEntity != null) { data.writeByte((byte)2); data.writeInt(latchedEntity.getEntityId()); } else { data.writeByte((byte)0); } } @Override public void readSpawnData(ByteBuf data) { setPosition(data.readDouble(), data.readDouble(), data.readDouble()); color = EnumColor.values()[data.readInt()]; byte type = data.readByte(); if(type == 1) { latched = Coord4D.read(data); } else if(type == 2) { latchedEntity = (EntityLivingBase)worldObj.getEntityByID(data.readInt()); } else { latched = null; } } @Override public void setDead() { super.setDead(); if(latchedEntity != null) { latchedEntity.isAirBorne = false; } } @Override public boolean isInRangeToRenderDist(double dist) { return dist <= 64; } @Override public boolean isInRangeToRender3d(double p_145770_1_, double p_145770_3_, double p_145770_5_) { return true; } @Override public boolean attackEntityFrom(DamageSource dmgSource, float damage) { if(isEntityInvulnerable(dmgSource)) { return false; } else { setBeenAttacked(); if(dmgSource != DamageSource.magic && dmgSource != DamageSource.drown && dmgSource != DamageSource.fall) { pop(); return true; } return false; } } public boolean isLatched() { if(!worldObj.isRemote) { return latched != null || latchedEntity != null; } else { return dataManager.get(IS_LATCHED) > 0; } } public boolean isLatchedToEntity() { return dataManager.get(IS_LATCHED) == 2 && latchedEntity != null; } }