/*
* This file is part of Matter Overdrive
* Copyright (c) 2015., Simeon Radivoev, All rights reserved.
*
* Matter Overdrive is free software: 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.
*
* Matter Overdrive 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 Matter Overdrive. If not, see <http://www.gnu.org/licenses>.
*/
package matteroverdrive.entity.weapon;
import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import io.netty.buffer.ByteBuf;
import matteroverdrive.Reference;
import matteroverdrive.api.events.weapon.MOEventPlasmaBlotHit;
import matteroverdrive.api.gravity.IGravitationalAnomaly;
import matteroverdrive.api.gravity.IGravityEntity;
import matteroverdrive.api.weapon.WeaponShot;
import matteroverdrive.client.data.Color;
import matteroverdrive.client.sound.MOPositionedSound;
import matteroverdrive.fx.PhaserBoltRecoil;
import matteroverdrive.items.weapon.EnergyWeapon;
import net.minecraft.block.Block;
import net.minecraft.block.BlockTNT;
import net.minecraft.block.material.Material;
import net.minecraft.client.Minecraft;
import net.minecraft.client.particle.EntityExplodeFX;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.IProjectile;
import net.minecraft.entity.item.EntityTNTPrimed;
import net.minecraft.entity.monster.EntityEnderman;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.play.server.S2BPacketChangeGameState;
import net.minecraft.util.*;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.util.ForgeDirection;
import java.util.List;
/**
* Created by Simeon on 7/25/2015.
*/
public class PlasmaBolt extends Entity implements IProjectile, IGravityEntity, IEntityAdditionalSpawnData
{
private int blockX = -1;
private int blockY = -1;
private int blockZ = -1;
private int distanceTraveled;
private float damage;
public Entity shootingEntity;
private Block block;
private int life;
private int color;
private float fireDamageMultiply;
private ItemStack weapon;
private float renderSize = 2;
private float knockback;
public PlasmaBolt(World world)
{
super(world);
this.renderDistanceWeight = 10.0D;
this.setSize(0.5F, 0.5F);
}
public PlasmaBolt(World world, EntityLivingBase entityLivingBase, Vec3 position, Vec3 dir, WeaponShot shot,float speed) {
super(world);
rand.setSeed(shot.getSeed());
this.setEntityId(rand.nextInt(Integer.MAX_VALUE));
this.renderDistanceWeight = 10.0D;
this.shootingEntity = entityLivingBase;
this.setSize(0.5F, 0.5F);
this.setLocationAndAngles(position.xCoord, position.yCoord, position.zCoord, entityLivingBase.rotationYaw, entityLivingBase.rotationPitch);
this.motionX = dir.xCoord;
this.motionY = dir.yCoord;
this.motionZ = dir.zCoord;
this.yOffset = 0.0F;
this.life = shot.getRange();
this.damage = shot.getDamage();
this.color = shot.getColor();
this.setThrowableHeading(this.motionX, this.motionY, this.motionZ,speed * 1.5F, shot.getAccuracy());
}
@Override
public void setThrowableHeading(double x, double y, double z, float speed, float accuracy)
{
float dirLength = MathHelper.sqrt_double(x * x + y * y + z * z);
x /= (double)dirLength;
y /= (double)dirLength;
z /= (double)dirLength;
x += this.rand.nextGaussian() * 0.007499999832361937D * (double)accuracy;
y += this.rand.nextGaussian() * 0.007499999832361937D * (double)accuracy;
z += this.rand.nextGaussian() * 0.007499999832361937D * (double)accuracy;
x *= (double)speed;
y *= (double)speed;
z *= (double)speed;
this.motionX = x;
this.motionY = y;
this.motionZ = z;
float f3 = MathHelper.sqrt_double(x * x + z * z);
this.prevRotationYaw = this.rotationYaw = (float)(Math.atan2(x, z) * 180.0D / Math.PI);
this.prevRotationPitch = this.rotationPitch = (float)(Math.atan2(y, (double)f3) * 180.0D / Math.PI);
}
@Override
protected void entityInit()
{
}
public void simulateDelay(int delay)
{
if (delay > 0) {
double lastMotionX = motionX;
double lastMotionY = motionY;
double lastMotionZ = motionZ;
motionX *= delay;
motionY *= delay;
motionZ *= delay;
onUpdate();
motionX = lastMotionX;
motionY = lastMotionY;
motionZ = lastMotionZ;
}
}
@Override
public void onUpdate() {
super.onUpdate();
if (this.prevRotationPitch == 0.0F && this.prevRotationYaw == 0.0F) {
float f = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ);
this.prevRotationYaw = this.rotationYaw = (float) (Math.atan2(this.motionX, this.motionZ) * 180.0D / Math.PI);
this.prevRotationPitch = this.rotationPitch = (float) (Math.atan2(this.motionY, (double) f) * 180.0D / Math.PI);
}
Block block = this.worldObj.getBlock(this.blockX, this.blockY, this.blockZ);
if (block.getMaterial() != Material.air) {
block.setBlockBoundsBasedOnState(this.worldObj, this.blockX, this.blockY, this.blockZ);
AxisAlignedBB axisalignedbb = block.getCollisionBoundingBoxFromPool(this.worldObj, this.blockX, this.blockY, this.blockZ);
if (axisalignedbb != null && axisalignedbb.isVecInside(Vec3.createVectorHelper(this.posX, this.posY, this.posZ))) {
setDead();
}
}
if (this.distanceTraveled > this.life)
{
setDead();
return;
}
distanceTraveled+=Vec3.createVectorHelper(motionX,motionY,motionZ).lengthVector();
float motionLeway = 0.0f;
Vec3 vec31 = Vec3.createVectorHelper(this.posX - this.motionX*motionLeway, this.posY - this.motionY*motionLeway, this.posZ - this.motionZ*motionLeway);
Vec3 vec3 = Vec3.createVectorHelper(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ);
MovingObjectPosition movingobjectposition = this.worldObj.func_147447_a(vec31, vec3, false, true, false);
vec31 = Vec3.createVectorHelper(this.posX - this.motionX*motionLeway, this.posY - this.motionY*motionLeway, this.posZ - this.motionZ*motionLeway);
vec3 = Vec3.createVectorHelper(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ);
if (movingobjectposition != null) {
vec3 = Vec3.createVectorHelper(movingobjectposition.hitVec.xCoord, movingobjectposition.hitVec.yCoord, movingobjectposition.hitVec.zCoord);
}
Entity entity = null;
Vec3 hit = null;
List list = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.boundingBox.addCoord(this.motionX, this.motionY, this.motionZ).expand(1.0D, 1.0D, 1.0D));
double d0 = 0.0D;
int i;
float f1;
for (i = 0; i < list.size(); ++i) {
Entity entity1 = (Entity) list.get(i);
if (entity1 != null && entity1.canBeCollidedWith() && !entity1.isDead && entity1 instanceof EntityLivingBase && ((EntityLivingBase)entity1).deathTime == 0) {
if (this.shootingEntity != null)
{
if (this.shootingEntity instanceof EntityLivingBase)
{
if(!canAttackTeammate((EntityLivingBase) entity1,(EntityLivingBase)this.shootingEntity))
continue;
}
if (entity1 == this.shootingEntity || entity1 == this.shootingEntity.ridingEntity)
continue;
}
f1 = 0.4F;
AxisAlignedBB axisalignedbb1 = entity1.boundingBox.expand((double) f1, (double) f1, (double) f1);
MovingObjectPosition movingobjectposition1 = axisalignedbb1.calculateIntercept(vec31, vec3);
if (movingobjectposition1 != null)
{
double d1 = vec31.distanceTo(movingobjectposition1.hitVec);
if (d1 < d0 || d0 == 0.0D) {
entity = entity1;
hit = movingobjectposition1.hitVec;
d0 = d1;
}
}
}
}
if (entity != null && entity != this.shootingEntity) {
movingobjectposition = new MovingObjectPosition(entity,hit);
}
if (movingobjectposition != null && movingobjectposition.entityHit != null && movingobjectposition.entityHit instanceof EntityPlayer) {
EntityPlayer entityplayer = (EntityPlayer) movingobjectposition.entityHit;
if (entityplayer.capabilities.disableDamage || this.shootingEntity instanceof EntityPlayer && !((EntityPlayer) this.shootingEntity).canAttackPlayer(entityplayer)) {
movingobjectposition = null;
}
}
if (movingobjectposition != null) {
if (movingobjectposition.entityHit != null) {
DamageSource damagesource;
if (this.shootingEntity == null) {
damagesource = getDamageSource(this);
} else {
damagesource = getDamageSource(this.shootingEntity);
}
movingobjectposition.entityHit.hurtResistantTime = 0;
double lastMotionX = movingobjectposition.entityHit.motionX;
double lastMotionY = movingobjectposition.entityHit.motionY;
double lastMotionZ = movingobjectposition.entityHit.motionZ;
if (movingobjectposition.entityHit.attackEntityFrom(damagesource, this.damage))
{
movingobjectposition.entityHit.motionX = lastMotionX + (movingobjectposition.entityHit.motionX - lastMotionX) * knockback;
movingobjectposition.entityHit.motionY = lastMotionY + (movingobjectposition.entityHit.motionY - lastMotionY) * knockback;
movingobjectposition.entityHit.motionZ = lastMotionZ + (movingobjectposition.entityHit.motionZ - lastMotionZ) * knockback;
if (movingobjectposition.entityHit instanceof EntityLivingBase) {
EntityLivingBase entitylivingbase = (EntityLivingBase) movingobjectposition.entityHit;
if (this.shootingEntity != null && this.shootingEntity instanceof EntityLivingBase) {
EnchantmentHelper.func_151384_a(entitylivingbase, this.shootingEntity);
EnchantmentHelper.func_151385_b((EntityLivingBase) this.shootingEntity, entitylivingbase);
}
if (this.shootingEntity != null && movingobjectposition.entityHit != this.shootingEntity && movingobjectposition.entityHit instanceof EntityPlayer && this.shootingEntity instanceof EntityPlayerMP) {
((EntityPlayerMP) this.shootingEntity).playerNetServerHandler.sendPacket(new S2BPacketChangeGameState(6, 0.0F));
}
}
if (fireDamageMultiply > 0)
{
movingobjectposition.entityHit.setFire((int)(10 * fireDamageMultiply));
}
if (!(movingobjectposition.entityHit instanceof EntityEnderman))
{
this.setDead();
}
}
else
{
if (movingobjectposition.entityHit instanceof EntityLivingBase)
{
//client hit
this.setDead();
}
}
if (weapon != null && weapon.getItem() instanceof EnergyWeapon)
{
if (worldObj.isRemote) {
((EnergyWeapon) weapon.getItem()).onProjectileHit(movingobjectposition, weapon, worldObj, 5);
onHit(movingobjectposition);
}
MinecraftForge.EVENT_BUS.post(new MOEventPlasmaBlotHit(weapon,movingobjectposition,this,worldObj.isRemote ? Side.CLIENT : Side.SERVER));
}
} else {
this.blockX = movingobjectposition.blockX;
this.blockY = movingobjectposition.blockY;
this.blockZ = movingobjectposition.blockZ;
this.block = this.worldObj.getBlock(this.blockX, this.blockY, this.blockZ);
if (this.block.getMaterial() != Material.air) {
this.block.onEntityCollidedWithBlock(this.worldObj, this.blockX, this.blockY, this.blockZ, this);
if (this.block instanceof BlockTNT)
{
worldObj.setBlockToAir(blockX, blockY, blockZ);
EntityTNTPrimed entitytntprimed = new EntityTNTPrimed(worldObj, (double)((float)blockX + 0.5F), (double)((float)blockY + 0.5F), (double)((float)blockZ + 0.5F), shootingEntity instanceof EntityLivingBase ? (EntityLivingBase) shootingEntity : null);
entitytntprimed.fuse = 0;
worldObj.spawnEntityInWorld(entitytntprimed);
}
}
if (weapon != null && weapon.getItem() instanceof EnergyWeapon)
{
if (worldObj.isRemote) {
((EnergyWeapon) weapon.getItem()).onProjectileHit(movingobjectposition, weapon, worldObj, 5);
onHit(movingobjectposition);
}
MinecraftForge.EVENT_BUS.post(new MOEventPlasmaBlotHit(weapon,movingobjectposition,this,worldObj.isRemote ? Side.CLIENT : Side.SERVER));
}
this.setDead();
}
}
this.boundingBox.offset(this.motionX, this.motionY, this.motionZ);
this.posX = (this.boundingBox.minX + this.boundingBox.maxX) / 2.0D;
this.posY = this.boundingBox.minY + (double)this.yOffset - (double)this.ySize;
this.posZ = (this.boundingBox.minZ + this.boundingBox.maxZ) / 2.0D;
this.func_145775_I();
}
@SideOnly(Side.CLIENT)
protected void onHit(MovingObjectPosition hit) {
Vec3 sideHit;
if (hit.typeOfHit.equals(MovingObjectPosition.MovingObjectType.BLOCK)) {
sideHit = Vec3.createVectorHelper(ForgeDirection.getOrientation(hit.sideHit).offsetX, ForgeDirection.getOrientation(hit.sideHit).offsetY, ForgeDirection.getOrientation(hit.sideHit).offsetZ);
} else {
sideHit = Vec3.createVectorHelper(-motionX, -motionY, -motionZ);
}
Color c = new Color(color);
EntityExplodeFX explodeFX = new EntityExplodeFX(worldObj, hit.hitVec.xCoord, hit.hitVec.yCoord, hit.hitVec.zCoord, 0, 0, 0);
explodeFX.setRBGColorF(c.getFloatR(),c.getFloatG(),c.getFloatB());
Minecraft.getMinecraft().effectRenderer.addEffect(explodeFX);
if (rand.nextFloat() < 0.8f) {
int hitPraticles = Math.max(0, (int) (16*renderSize) - ((int) (8 * renderSize) * Minecraft.getMinecraft().gameSettings.particleSetting));
for (int i = 0; i < hitPraticles; i++) {
Minecraft.getMinecraft().effectRenderer.addEffect(new PhaserBoltRecoil(worldObj, hit.hitVec.xCoord, hit.hitVec.yCoord, hit.hitVec.zCoord, c, sideHit.xCoord * 30, sideHit.yCoord * 30, sideHit.zCoord * 30));
}
if (getRenderSize() > 0.5)
{
MOPositionedSound sizzleSound = new MOPositionedSound(new ResourceLocation(Reference.MOD_ID + ":" + "sizzle"), rand.nextFloat() * 0.2f + 0.4f, rand.nextFloat() * 0.6f + 0.7f);
sizzleSound.setPosition((float) hit.hitVec.xCoord, (float) hit.hitVec.yCoord, (float) hit.hitVec.zCoord);
Minecraft.getMinecraft().getSoundHandler().playSound(sizzleSound);
if (hit.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK)
{
MOPositionedSound ricochetSound = new MOPositionedSound(new ResourceLocation(Reference.MOD_ID + ":" + "laser_ricochet"), rand.nextFloat() * 0.2f + 0.6f, rand.nextFloat() * 0.2f + 1f);
ricochetSound.setPosition((float) hit.hitVec.xCoord, (float) hit.hitVec.yCoord, (float) hit.hitVec.zCoord);
Minecraft.getMinecraft().getSoundHandler().playSound(ricochetSound);
}
}
}
if (hit.typeOfHit.equals(MovingObjectPosition.MovingObjectType.ENTITY))
{
if (hit.entityHit instanceof EntityLivingBase)
{
for (int s = 0; s < Math.max(0,10 - (5 * Minecraft.getMinecraft().gameSettings.particleSetting)); s++) {
worldObj.spawnParticle("reddust", hit.hitVec.xCoord+rand.nextDouble()*0.4-0.2, hit.hitVec.yCoord+rand.nextDouble()*0.4-0.2, hit.hitVec.zCoord+rand.nextDouble()*0.4-0.2, 0,0,0);
}
}
}
}
private boolean canAttackTeammate(EntityLivingBase shooter,EntityLivingBase target)
{
if (shooter != null && target != null)
{
if (shooter.getTeam() != null && shooter.isOnSameTeam(target))
{
return shooter.getTeam().getAllowFriendlyFire();
}
return true;
}
return true;
}
public DamageSource getDamageSource(Entity shootingEntity)
{
EntityDamageSourceIndirect dmg = new EntityDamageSourceIndirect("plasmaBolt",this,shootingEntity);
dmg.setProjectile();
return dmg;
}
/**
* (abstract) Protected helper method to write subclass entity data to NBT.
*/
@Override
public void writeEntityToNBT(NBTTagCompound tagCompound)
{
tagCompound.setShort("xTile", (short) this.blockX);
tagCompound.setShort("yTile", (short) this.blockY);
tagCompound.setShort("zTile", (short) this.blockZ);
tagCompound.setFloat("damage", this.damage);
tagCompound.setInteger("distanceTraveled", this.distanceTraveled);
tagCompound.setInteger("life", this.life);
tagCompound.setInteger("color", this.color);
}
/**
* (abstract) Protected helper method to read subclass entity data from NBT.
*/
@Override
public void readEntityFromNBT(NBTTagCompound tagCompound)
{
this.blockX = tagCompound.getShort("xTile");
this.blockY = tagCompound.getShort("yTile");
this.blockZ = tagCompound.getShort("zTile");
if (tagCompound.hasKey("damage", 99))
{
this.damage = tagCompound.getFloat("damage");
}
if (tagCompound.hasKey("ticksInAir", 99))
{
this.distanceTraveled = tagCompound.getInteger("distanceTraveled");
}
if (tagCompound.hasKey("life", 99))
{
this.life = tagCompound.getInteger("life");
}
if (tagCompound.hasKey("color", 99))
{
this.color = tagCompound.getInteger("color");
}
}
/**
* Called by a player entity when they collide with an entity
*/
public void onCollideWithPlayer(EntityPlayer p_70100_1_)
{
}
/**
* returns if this entity triggers Block.onEntityWalking on the blocks they walk on. used for spiders and wolves to
* prevent them from trampling crops
*/
protected boolean canTriggerWalking()
{
return false;
}
@SideOnly(Side.CLIENT)
public float getShadowSize()
{
return 0.0F;
}
public void setDamage(float p_70239_1_)
{
this.damage = p_70239_1_;
}
public double getDamage()
{
return this.damage;
}
/**
* If returns false, the item will not inflict any damage against entities.
*/
public boolean canAttackWithItem()
{
return false;
}
public void setWeapon(ItemStack weapon)
{
this.weapon = weapon;
}
public int getColor()
{
return color;
}
public boolean shouldRenderInPass(int pass)
{
return pass == 1;
}
@Override
public boolean isAffectedByAnomaly(IGravitationalAnomaly anomaly) {
return false;
}
@Override
public void onEntityConsumed(IGravitationalAnomaly anomaly)
{
}
public void setFireDamageMultiply(float fiery)
{
this.fireDamageMultiply = fiery;
}
@Override
public void writeSpawnData(ByteBuf buffer)
{
buffer.writeFloat(damage);
buffer.writeInt(color);
buffer.writeFloat(fireDamageMultiply);
buffer.writeInt(life);
}
@Override
public void readSpawnData(ByteBuf additionalData)
{
damage = additionalData.readFloat();
color = additionalData.readInt();
fireDamageMultiply = additionalData.readFloat();
life = additionalData.readInt();
}
public float getLife()
{
return 1f-((float)distanceTraveled/(float)life);
}
public float setLife(float life)
{
return life;
}
public void setRenderSize(float size)
{
this.renderSize = size;
}
public float getRenderSize(){return renderSize;}
public void setKnockBack(float knockback)
{
this.knockback = knockback;
}
}