/** * 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 the * Botania License: http://botaniamod.net/license.php * * File Created @ [Jan 25, 2015, 6:47:35 PM (GMT)] */ package vazkii.botania.common.entity; import javax.annotation.Nonnull; import net.minecraft.block.Block; import net.minecraft.block.BlockBush; import net.minecraft.block.BlockLeaves; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.projectile.EntityThrowable; import net.minecraft.init.MobEffects; 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.potion.PotionEffect; import net.minecraft.util.DamageSource; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.math.RayTraceResult; import net.minecraft.world.World; import vazkii.botania.common.core.helper.Vector3; import vazkii.botania.common.item.ModItems; public class EntityThornChakram extends EntityThrowable { private static final DataParameter<Integer> BOUNCES = EntityDataManager.createKey(EntityThornChakram.class, DataSerializers.VARINT); private static final DataParameter<Boolean> FLARE = EntityDataManager.createKey(EntityThornChakram.class, DataSerializers.BOOLEAN); private static final DataParameter<Integer> RETURN_TO = EntityDataManager.createKey(EntityThornChakram.class, DataSerializers.VARINT); private static final int MAX_BOUNCES = 16; private boolean bounced = false; private ItemStack stack = ItemStack.EMPTY; public EntityThornChakram(World world) { super(world); } public EntityThornChakram(World world, EntityLivingBase e, ItemStack stack) { super(world, e); this.stack = stack.copy(); } @Override protected void entityInit() { super.entityInit(); dataManager.register(BOUNCES, 0); dataManager.register(FLARE, false); dataManager.register(RETURN_TO, -1); } @Override public void onUpdate() { // Standard motion double mx = motionX; double my = motionY; double mz = motionZ; super.onUpdate(); if(!bounced) { // Reset the drag applied by super motionX = mx; motionY = my; motionZ = mz; } bounced = false; // Returning motion if(isReturning()) { Entity thrower = getThrower(); if(thrower != null) { Vector3 motion = Vector3.fromEntityCenter(thrower).subtract(Vector3.fromEntityCenter(this)).normalize(); motionX = motion.x; motionY = motion.y; motionZ = motion.z; } } // Client FX if(world.isRemote && isFire()) { double r = 0.1; double m = 0.1; for(int i = 0; i < 3; i++) world.spawnParticle(EnumParticleTypes.FLAME, posX + r * (Math.random() - 0.5), posY + r * (Math.random() - 0.5), posZ + r * (Math.random() - 0.5), m * (Math.random() - 0.5), m * (Math.random() - 0.5), m * (Math.random() - 0.5)); } // Server state control if(!world.isRemote && (getTimesBounced() >= MAX_BOUNCES || ticksExisted > 60)) { EntityLivingBase thrower = getThrower(); if(thrower == null) { dropAndKill(); } else { setEntityToReturnTo(thrower.getEntityId()); if(getDistanceSqToEntity(thrower) < 2) dropAndKill(); } } } private void dropAndKill() { ItemStack stack = getItemStack(); EntityItem item = new EntityItem(world, posX, posY, posZ, stack); world.spawnEntity(item); setDead(); } private ItemStack getItemStack() { return !stack.isEmpty() ? stack.copy() : new ItemStack(ModItems.thornChakram, 1, isFire() ? 1 : 0); } @Override protected void onImpact(@Nonnull RayTraceResult pos) { if(isReturning()) return; switch (pos.typeOfHit) { case BLOCK: { Block block = world.getBlockState(pos.getBlockPos()).getBlock(); if(block instanceof BlockBush || block instanceof BlockLeaves) return; int bounces = getTimesBounced(); if(bounces < MAX_BOUNCES) { Vector3 currentMovementVec = new Vector3(motionX, motionY, motionZ); EnumFacing dir = pos.sideHit; Vector3 normalVector = new Vector3(dir.getFrontOffsetX(), dir.getFrontOffsetY(), dir.getFrontOffsetZ()).normalize(); Vector3 movementVec = normalVector.multiply(-2 * currentMovementVec.dotProduct(normalVector)).add(currentMovementVec); motionX = movementVec.x; motionY = movementVec.y; motionZ = movementVec.z; bounced = true; if(!world.isRemote) setTimesBounced(getTimesBounced() + 1); } break; } case ENTITY: { if(!world.isRemote && pos.entityHit != null && pos.entityHit instanceof EntityLivingBase && pos.entityHit != getThrower()) { EntityLivingBase thrower = getThrower(); pos.entityHit.attackEntityFrom(thrower != null ? thrower instanceof EntityPlayer ? DamageSource.causeThrownDamage(this, thrower) : DamageSource.causeMobDamage(thrower) : DamageSource.GENERIC, 12); if(isFire()) pos.entityHit.setFire(5); else if(world.rand.nextInt(3) == 0) ((EntityLivingBase) pos.entityHit).addPotionEffect(new PotionEffect(MobEffects.POISON, 60, 0)); } break; } default: break; } } @Override protected float getGravityVelocity() { return 0F; } private int getTimesBounced() { return dataManager.get(BOUNCES); } private void setTimesBounced(int times) { dataManager.set(BOUNCES, times); } public boolean isFire() { return dataManager.get(FLARE); } public void setFire(boolean fire) { dataManager.set(FLARE, fire); } private boolean isReturning() { return getEntityToReturnTo() > -1; } private int getEntityToReturnTo() { return dataManager.get(RETURN_TO); } private void setEntityToReturnTo(int entityID) { dataManager.set(RETURN_TO, entityID); } @Override public void writeEntityToNBT(NBTTagCompound compound) { super.writeEntityToNBT(compound); if(!stack.isEmpty()) { compound.setTag("fly_stack", stack.writeToNBT(new NBTTagCompound())); } compound.setBoolean("flare", isFire()); } @Override public void readEntityFromNBT(NBTTagCompound compound) { super.readEntityFromNBT(compound); if(compound.hasKey("fly_stack")) { stack = new ItemStack(compound.getCompoundTag("fly_stack")); } setFire(compound.getBoolean("flare")); } }