/** Copyright (C) <2015> <coolAlias> This file is part of coolAlias' Zelda Sword Skills Minecraft Mod; as such, you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package zeldaswordskills.entity.projectile; import net.minecraft.block.Block; import net.minecraft.block.BlockButtonWood; import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.monster.EntityEnderman; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; import net.minecraft.util.BlockPos; import net.minecraft.util.DamageSource; import net.minecraft.util.EntityDamageSourceIndirect; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.MathHelper; import net.minecraft.util.MovingObjectPosition; import net.minecraft.world.World; import zeldaswordskills.api.damage.DamageUtils.DamageSourceBaseIndirect; import zeldaswordskills.api.entity.CustomExplosion; import zeldaswordskills.ref.Sounds; import zeldaswordskills.util.WorldUtils; public class EntitySeedShot extends EntityMobThrowable { public static enum SeedType { NONE(0.0F, EnumParticleTypes.CRIT), BOMB(3.0F, EnumParticleTypes.EXPLOSION_LARGE), COCOA(1.25F, EnumParticleTypes.CRIT), DEKU(1.5F, EnumParticleTypes.EXPLOSION_LARGE), GRASS(1.0F, EnumParticleTypes.CRIT), MELON(1.25F, EnumParticleTypes.CRIT), NETHERWART(1.5F, EnumParticleTypes.CRIT), PUMPKIN(1.25F, EnumParticleTypes.CRIT); private final float damage; /** Particle to spawn upon impact */ public final EnumParticleTypes particle; private SeedType(float damage, EnumParticleTypes particle) { this.damage = damage; this.particle = particle; } /** Returns the base damage for this seed type */ public float getDamage() { return damage; } }; /** Watchable object index for critical and seed's type */ protected static final int CRITICAL_INDEX = 22, SEEDTYPE_INDEX = 23; /** Knockback strength, if any */ private int knockback = 0; public EntitySeedShot(World world) { super(world); setGravityVelocity(0.05F); } public EntitySeedShot(World world, EntityLivingBase entity, float velocity) { this(world, entity, velocity, 1, 0F); } public EntitySeedShot(World world, EntityLivingBase shooter, EntityLivingBase target, float velocity, float wobble) { super(world, shooter, target, velocity, wobble); setGravityVelocity(0.05F); } /** * @param n the nth shot fired; all shots after the 1st will vary the trajectory * by the spread given, while the first shot will have a true course */ public EntitySeedShot(World world, EntityLivingBase entity, float velocity, int n, float spread) { super(world, entity); setGravityVelocity(0.05F); if (n > 1) { setLocationAndAngles(entity.posX, entity.posY + (double) entity.getEyeHeight(), entity.posZ, entity.rotationYaw, entity.rotationPitch); float rotFactor = (float)(n / 2) * spread; rotationYaw += rotFactor * (n % 2 == 0 ? 1 : -1); posX -= (double)(MathHelper.cos(rotationYaw / 180.0F * (float) Math.PI) * 0.16F); posY -= 0.10000000149011612D; posZ -= (double)(MathHelper.sin(rotationYaw / 180.0F * (float) Math.PI) * 0.16F); setPosition(posX, posY, posZ); float f = 0.4F; motionX = (double)(-MathHelper.sin(rotationYaw / 180.0F * (float)Math.PI) * MathHelper.cos(rotationPitch / 180.0F * (float) Math.PI) * f); motionZ = (double)(MathHelper.cos(rotationYaw / 180.0F * (float)Math.PI) * MathHelper.cos(rotationPitch / 180.0F * (float) Math.PI) * f); motionY = (double)(-MathHelper.sin((rotationPitch + getInaccuracy()) / 180.0F * (float)Math.PI) * f); } setThrowableHeading(motionX, motionY, motionZ, velocity * 1.5F, 1.0F); } public EntitySeedShot(World world, double x, double y, double z) { super(world, x, y, z); setGravityVelocity(0.05F); } @Override public void entityInit() { super.entityInit(); // only needed if spawning trailing particles on client side: dataWatcher.addObject(CRITICAL_INDEX, Byte.valueOf((byte) 0)); // only needed if rendering differently for each type: dataWatcher.addObject(SEEDTYPE_INDEX, SeedType.GRASS.ordinal()); } /** Whether the seed has a stream of critical hit particles flying behind it */ public void setIsCritical(boolean isCrit) { dataWatcher.updateObject(CRITICAL_INDEX, Byte.valueOf((byte)(isCrit ? 1 : 0))); } /** Whether the seed has a stream of critical hit particles flying behind it */ public boolean getIsCritical() { return dataWatcher.getWatchableObjectByte(CRITICAL_INDEX) > 0; } /** Set the seed's type */ public EntitySeedShot setType(SeedType type) { dataWatcher.updateObject(SEEDTYPE_INDEX, type.ordinal()); return this; } /** Get the seed's type */ public SeedType getType() { return SeedType.values()[dataWatcher.getWatchableObjectInt(SEEDTYPE_INDEX) % SeedType.values().length]; } /** * Returns the damage source caused by this seed type */ public DamageSource getDamageSource() { switch(getType()) { case BOMB: return new DamageSourceBaseIndirect("slingshot", this, getThrower()).setExplosion().setProjectile(); case DEKU: return new DamageSourceBaseIndirect("slingshot", this, getThrower()).setStunDamage(80, 2, true).setProjectile(); case NETHERWART: return new EntityDamageSourceIndirect("slingshot", this, getThrower()).setFireDamage().setProjectile(); default: return new EntityDamageSourceIndirect("slingshot", this, getThrower()).setProjectile(); } } /** Sets the amount of knockback the arrow applies when it hits a mob. */ public void setKnockback(int value) { knockback = value; } /** Returns the amount of knockback the arrow applies when it hits a mob */ public int getKnockback() { return knockback; } @Override protected boolean canTriggerWalking() { return false; } @Override protected void onImpact(MovingObjectPosition mop) { if (mop.entityHit != null) { if (isBurning() && !(mop.entityHit instanceof EntityEnderman)) { mop.entityHit.setFire(5); } if (getType() == SeedType.BOMB) { CustomExplosion.createExplosion(new EntityBomb(worldObj, getThrower()), worldObj, posX, posY, posZ, 3.0F, SeedType.BOMB.getDamage(), false); setDead(); } else if (mop.entityHit.attackEntityFrom(getDamageSource(), calculateDamage())) { playSound(Sounds.DAMAGE_SUCCESSFUL_HIT, 1.0F, 1.2F / (rand.nextFloat() * 0.2F + 0.9F)); if (knockback > 0) { float f = MathHelper.sqrt_double(motionX * motionX + motionZ * motionZ); if (f > 0.0F) { double d = (double) knockback * 0.6000000238418579D / (double) f; mop.entityHit.addVelocity(motionX * d, 0.1D, motionZ * d); } } if (mop.entityHit instanceof EntityLivingBase) { EntityLivingBase entity = (EntityLivingBase) mop.entityHit; switch(getType()) { case COCOA: entity.addPotionEffect(new PotionEffect(Potion.weakness.id,100,0)); break; case PUMPKIN: entity.addPotionEffect(new PotionEffect(Potion.moveSlowdown.id,100,0)); break; default: } if (getThrower() instanceof EntityLivingBase) { EnchantmentHelper.applyThornEnchantments((EntityLivingBase) mop.entityHit, getThrower()); EnchantmentHelper.applyArthropodEnchantments((EntityLivingBase) getThrower(), mop.entityHit); } } if (!(mop.entityHit instanceof EntityEnderman)) { setDead(); } } else { motionX *= -0.10000000149011612D; motionY *= -0.10000000149011612D; motionZ *= -0.10000000149011612D; rotationYaw += 180.0F; prevRotationYaw += 180.0F; } } else { BlockPos pos = mop.getBlockPos(); IBlockState state = worldObj.getBlockState(pos); Block block = state.getBlock(); if (block.getMaterial() != Material.air) { block.onEntityCollidedWithBlock(worldObj, pos, this); } boolean flag = block.isSideSolid(worldObj, pos, mop.sideHit); if (getType() == SeedType.BOMB && flag) { double dx = mop.sideHit == EnumFacing.WEST ? -0.5D : mop.sideHit == EnumFacing.EAST ? 0.5D : 0.0D; double dy = mop.sideHit == EnumFacing.DOWN ? -0.5D : mop.sideHit == EnumFacing.UP ? 0.5D : 0.0D; double dz = mop.sideHit == EnumFacing.NORTH ? -0.5D : mop.sideHit == EnumFacing.SOUTH ? 0.5D : 0.0D; if (!worldObj.isRemote) { CustomExplosion.createExplosion(new EntityBomb(worldObj, getThrower()), worldObj, posX + dx, posY + dy, posZ + dz, 3.0F, SeedType.BOMB.getDamage(), false); } } else if (block instanceof BlockButtonWood) { WorldUtils.activateButton(worldObj, state, pos); flag = true; } if (flag) { playSound(Sounds.DAMAGE_SUCCESSFUL_HIT, 0.3F, 1.2F / (rand.nextFloat() * 0.2F + 0.9F)); setDead(); } } // Only spawn particles if it hit something for sure if (!isEntityAlive()) { EnumParticleTypes particle = getType().particle; for (int i = 0; i < 4; ++i) { worldObj.spawnParticle(particle, posX - motionX * (double) i / 4.0D, posY - motionY * (double) i / 4.0D, posZ - motionZ * (double) i / 4.0D, motionX, motionY + 0.2D, motionZ); } } } /** * Calculates and returns damage based on velocity */ protected float calculateDamage() { float f = MathHelper.sqrt_double(motionX * motionX + motionY * motionY + motionZ * motionZ); float dmg = f * getDamage(); if (getIsCritical()) { dmg += (rand.nextFloat() * (getDamage() / 4.0F)) + 0.25F; } return dmg; } @Override public void writeEntityToNBT(NBTTagCompound compound) { super.writeEntityToNBT(compound); compound.setBoolean("isCritical", getIsCritical()); compound.setByte("seedType", (byte) getType().ordinal()); compound.setInteger("knockback", knockback); } @Override public void readEntityFromNBT(NBTTagCompound compound) { super.readEntityFromNBT(compound); setIsCritical(compound.getBoolean("isCritical")); setType(SeedType.values()[compound.getByte("seedType") % SeedType.values().length]); knockback = compound.getInteger("knockback"); } }