package com.flansmod.common.guns; import io.netty.buffer.ByteBuf; import org.lwjgl.input.Mouse; import net.minecraft.entity.Entity; import net.minecraft.entity.monster.EntityMob; 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.util.AxisAlignedBB; import net.minecraft.util.DamageSource; import net.minecraft.util.MovingObjectPosition; import net.minecraft.util.Vec3; import net.minecraft.world.World; import net.minecraftforge.fml.client.FMLClientHandler; import net.minecraftforge.fml.common.network.ByteBufUtils; import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import com.flansmod.common.FlansMod; import com.flansmod.common.PlayerData; import com.flansmod.common.PlayerHandler; import com.flansmod.common.network.PacketAAGunAngles; import com.flansmod.common.network.PacketMGFire; import com.flansmod.common.network.PacketPlaySound; import com.flansmod.common.teams.Team; import com.flansmod.common.teams.TeamsManager; import com.flansmod.common.vector.Vector3f; public class EntityAAGun extends Entity implements IEntityAdditionalSpawnData { private int sUpdateTime; private double sPosX; private double sPosY; private double sPosZ; private double sYaw; private double sPitch; private double field_9388_j; private double field_9387_k; private double field_9386_l; private int health; private int shootDelay; /** Gun angles */ public float gunYaw, gunPitch; /** Prev gun angles */ public float prevGunYaw, prevGunPitch; public float barrelRecoil[]; public AAGunType type; public Entity towedByEntity; public ItemStack[] ammo; // One per barrel public int reloadTimer; public int currentBarrel; // For cycling through firing each barrel public boolean mouseHeld; public boolean wasShooting; //Sentry stuff /** Stops the sentry shooting whoever placed it or their teammates */ public EntityPlayer placer = null; /** For getting the placer after a reload */ public String placerName = null; /** The sentry's current target */ public Entity target = null; /** How often to check for new targets */ public static final float targetAcquireInterval = 10; public int ticksSinceUsed = 0; private float yOffset; public EntityAAGun(World world) { super(world); preventEntitySpawning = true; setSize(2.0F, 2.0F); yOffset = 0F; gunYaw = 0; gunPitch = 0; shootDelay = 0; } public EntityAAGun(World world, AAGunType type1, double d, double d1, double d2, EntityPlayer p) { this(world); placer = p; placerName = p.getName(); type = type1; initType(); setPosition(d, d1, d2); } @Override public void setPosition(double d, double d1, double d2) { posX = d; posY = d1; posZ = d2; float f = width / 2.0F; float f1 = height; setEntityBoundingBox(AxisAlignedBB.fromBounds(d - f, (d1 - yOffset) + height, d2 - f, d + f, (d1 - yOffset) + height + f1, d2 + f)); } @Override public void setPositionAndRotation2(double d, double d1, double d2, float f, float f1, int i, boolean b) { sPosX = d; sPosY = d1; sPosZ = d2; sYaw = f; sPitch = f1; sUpdateTime = i; } public void initType() { health = type.health; barrelRecoil = new float[type.numBarrels]; ammo = new ItemStack[type.numBarrels]; } @Override protected void entityInit() { } @Override public void onCollideWithPlayer(EntityPlayer par1EntityPlayer) { } @Override public void applyEntityCollision(Entity entity) { //if(entity != riddenByEntity) //super.applyEntityCollision(entity); } @Override public AxisAlignedBB getCollisionBox(Entity entity) { return entity.getEntityBoundingBox(); } @Override public boolean canBePushed() { return false; } @Override public double getMountedYOffset() { return 0D; } public void setMouseHeld(boolean held) { mouseHeld = held; } @Override public boolean attackEntityFrom(DamageSource damagesource, float i) { if (damagesource.damageType.equals("player")) { Entity player = damagesource.getEntity(); if (player == riddenByEntity) { } else if(riddenByEntity != null) { return riddenByEntity.attackEntityFrom(damagesource, i); } else if(TeamsManager.canBreakGuns) { setDead(); } } else { setBeenAttacked(); health -= i; if (!worldObj.isRemote && health <= 0) setDead(); } return true; } public Vec3 rotate(double x, double y, double z) { double cosYaw = Math.cos(180F - gunYaw * 3.14159265F / 180F); double sinYaw = Math.sin(180F - gunYaw * 3.14159265F / 180F); double cosPitch = Math.cos(gunPitch * 3.14159265F / 180F); double sinPitch = Math.sin(gunPitch * 3.14159265F / 180F); double newX = x * cosYaw + (y * sinPitch + z * cosPitch) * sinYaw; double newY = y * cosPitch - z * sinPitch; double newZ = -x * sinYaw + (y * sinPitch + z * cosPitch) * cosYaw; return new Vec3(newX, newY, newZ); } @Override public boolean canBeCollidedWith() { return !isDead; } @Override public void onUpdate() { super.onUpdate(); prevGunYaw = gunYaw; prevGunPitch = gunPitch; ticksSinceUsed++; if(TeamsManager.aaLife > 0 && ticksSinceUsed > TeamsManager.aaLife * 20) { setDead(); } if (riddenByEntity != null) { ticksSinceUsed = 0; gunYaw = riddenByEntity.rotationYaw - 90; gunPitch = riddenByEntity.rotationPitch; } if (gunPitch > type.bottomViewLimit) gunPitch = type.bottomViewLimit; if (gunPitch < -type.topViewLimit) gunPitch = -type.topViewLimit; for (int i = 0; i < type.numBarrels; i++) barrelRecoil[i] *= 0.9F; if (shootDelay > 0) shootDelay--; // Sentry stuff if(isSentry()) { if(target != null && target.isDead) target = null; //Find a new target if we don't currently have one if(target == null && ticksExisted % targetAcquireInterval == 0) { target = getValidTarget(); } if(target != null) { double dX = target.posX - posX; double dY = target.posY - (posY + 1.5F); double dZ = target.posZ - posZ; double distanceToTarget = Math.sqrt(dX * dX + dY * dY + dZ * dZ); if(distanceToTarget > type.targetRange) target = null; else { float newYaw = 180F + (float)Math.atan2(dZ, dX) * 180F / 3.14159F; float newPitch = -(float)Math.atan2(dY, Math.sqrt(dX * dX + dZ * dZ)) * 180F / 3.14159F; float turnSpeed = 0.25F; gunYaw += (newYaw - gunYaw) * turnSpeed; gunPitch += (newPitch - gunPitch) * turnSpeed; } } } // apply gravity if (!onGround && !worldObj.isRemote) motionY -= 9.8D / 400D; // update motion motionX *= 0.5; motionZ *= 0.5; moveEntity(motionX, motionY, motionZ); if (worldObj.isRemote && riddenByEntity != null && riddenByEntity == FMLClientHandler.instance().getClient().thePlayer) { checkForShooting(); } if (worldObj.isRemote) { if (sUpdateTime > 0) { double d1 = posX + (sPosX - posX) / sUpdateTime; double d5 = posY + (sPosY - posY) / sUpdateTime; double d9 = posZ + (sPosZ - posZ) / sUpdateTime; double d12; for (d12 = sYaw - rotationYaw; d12 < -180D; d12 += 360D) { } for (; d12 >= 180D; d12 -= 360D) { } rotationYaw += d12 / sUpdateTime; rotationPitch += (sPitch - rotationPitch) / sUpdateTime; sUpdateTime--; setPosition(d1, d5, d9); setRotation(rotationYaw, rotationPitch); } return; } if (riddenByEntity != null && riddenByEntity.isDead) { riddenByEntity = null; } // Decrement the reload timer and reload if (reloadTimer > 0) reloadTimer--; //If it is 0 or less, go ahead and reload else { for (int i = 0; i < type.numBarrels; i++) { if (ammo[i] != null && ammo[i].getItemDamage() == ammo[i].getMaxDamage()) { ammo[i] = null; // Scrap metal output? } if (ammo[i] == null && riddenByEntity != null && riddenByEntity instanceof EntityPlayer) { int slot = findAmmo(((EntityPlayer) riddenByEntity)); if (slot >= 0) { ammo[i] = ((EntityPlayer) riddenByEntity).inventory.getStackInSlot(slot); if (!((EntityPlayer)riddenByEntity).capabilities.isCreativeMode) ((EntityPlayer) riddenByEntity).inventory.decrStackSize(slot, 1); reloadTimer = type.reloadTime; PacketPlaySound.sendSoundPacket(posX, posY, posZ, 50, dimension, type.reloadSound, true); } } } } if(!worldObj.isRemote && reloadTimer <= 0 && shootDelay <= 0) { if(mouseHeld && riddenByEntity != null && riddenByEntity instanceof EntityPlayer) { EntityPlayer player = (EntityPlayer)riddenByEntity; for(int j = 0; j < type.numBarrels; j++) { if(shootDelay <= 0 && ammo[j] != null && (!type.fireAlternately || type.fireAlternately && currentBarrel == j)) { // Fire BulletType bullet = BulletType.getBullet(ammo[j].getItem()); if (!((EntityPlayer)riddenByEntity).capabilities.isCreativeMode) ammo[j].damageItem(1, player); shootDelay = type.shootDelay; barrelRecoil[j] = type.recoil; Vec3 origin = rotate(type.barrelX[currentBarrel] / 16D - type.barrelZ[currentBarrel] / 16D, type.barrelY[currentBarrel] / 16D, type.barrelX[currentBarrel] / 16D + type.barrelZ[currentBarrel] / 16D).addVector(posX, posY, posZ); worldObj.spawnEntityInWorld(((ItemBullet)ammo[j].getItem()).getEntity(worldObj, origin, gunYaw + 90F, gunPitch, player, type.accuracy, type.damage, type)); PacketPlaySound.sendSoundPacket(posX, posY, posZ, 50, dimension, type.shootSound, true); } } currentBarrel = (currentBarrel + 1) % type.numBarrels; } else if(target != null) { for(int j = 0; j < type.numBarrels; j++) { int ammoSlot = j; if(type.shareAmmo) ammoSlot = 0; if(shootDelay <= 0 && ammo[ammoSlot] != null && (!type.fireAlternately || type.fireAlternately && currentBarrel == ammoSlot)) { // Fire BulletType bullet = BulletType.getBullet(ammo[ammoSlot].getItem()); ammo[ammoSlot].setItemDamage(ammo[ammoSlot].getItemDamage() + 1); shootDelay = type.shootDelay; barrelRecoil[ammoSlot] = type.recoil; Vec3 origin = rotate(type.barrelX[currentBarrel] / 16D - type.barrelZ[currentBarrel] / 16D, type.barrelY[currentBarrel] / 16D, type.barrelX[currentBarrel] / 16D + type.barrelZ[currentBarrel] / 16D).addVector(posX, posY + 1.5F, posZ); worldObj.spawnEntityInWorld(((ItemBullet)ammo[ammoSlot].getItem()).getEntity(worldObj, origin, gunYaw + 90F, gunPitch, placer, type.accuracy, type.damage, type)); PacketPlaySound.sendSoundPacket(posX, posY, posZ, 50, dimension, type.shootSound, true); } } currentBarrel = (currentBarrel + 1) % type.numBarrels; } } if(!worldObj.isRemote) { FlansMod.getPacketHandler().sendToAllAround(new PacketAAGunAngles(this), posX, posY, posZ, 50F, dimension); } } public boolean isSentry() { return type.targetMobs || type.targetPlayers; } public Entity getValidTarget() { if(worldObj.isRemote) return null; if(placer == null && placerName != null) placer = worldObj.getPlayerEntityByName(placerName); for(Object obj : worldObj.getEntitiesWithinAABBExcludingEntity(this, getEntityBoundingBox().expand(type.targetRange, type.targetRange, type.targetRange))) { Entity candidateEntity = (Entity)obj; if((type.targetMobs && candidateEntity instanceof EntityMob) || (type.targetPlayers && candidateEntity instanceof EntityPlayer)) { //Check that this entity is actually in range and visible if(candidateEntity.getDistanceToEntity(this) < type.targetRange) { if(candidateEntity instanceof EntityPlayer) { if(candidateEntity == placer || candidateEntity.getName().equals(placerName)) continue; if(TeamsManager.enabled && TeamsManager.getInstance().currentRound != null && placer != null) { PlayerData placerData = PlayerHandler.getPlayerData(placer, worldObj.isRemote ? Side.CLIENT : Side.SERVER); PlayerData candidateData = PlayerHandler.getPlayerData((EntityPlayer)candidateEntity, worldObj.isRemote ? Side.CLIENT : Side.SERVER); if(candidateData.team == Team.spectators || candidateData.team == null) continue; if(!TeamsManager.getInstance().currentRound.gametype.playerCanAttack((EntityPlayerMP)placer, placerData.team, (EntityPlayerMP)candidateEntity, candidateData.team)) continue; } } return candidateEntity; } } } return null; } @SideOnly(Side.CLIENT) private void checkForShooting() { //Send a packet! if(Mouse.isButtonDown(0) && !wasShooting && !FlansMod.proxy.isScreenOpen()) { FlansMod.getPacketHandler().sendToServer(new PacketMGFire(true)); wasShooting = true; } else if(!Mouse.isButtonDown(0) && wasShooting) { FlansMod.getPacketHandler().sendToServer(new PacketMGFire(false)); wasShooting = false; } } @Override public void setDead() { super.setDead(); // Drop gun if(worldObj.isRemote) return; dropItem(type.getItem(), 1); // Drop ammo boxes for (ItemStack stack : ammo) { if (stack != null) entityDropItem(stack, 0.5F); } } @Override public void updateRiderPosition() { if (riddenByEntity == null) { return; } double x = type.gunnerX / 16D; double y = type.gunnerY / 16D; double z = type.gunnerZ / 16D; double cosYaw = Math.cos((-gunYaw / 180D) * 3.1415926535897931D); double sinYaw = Math.sin((-gunYaw / 180D) * 3.1415926535897931D); double cosPitch = Math.cos((gunPitch / 180D) * 3.1415926535897931D); double sinPitch = Math.sin((gunPitch / 180D) * 3.1415926535897931D); double x2 = x * cosYaw + z * sinYaw; double z2 = -x * sinYaw + z * cosYaw; riddenByEntity.setPosition(posX + x2, posY + y, posZ + z2); } @Override protected void writeEntityToNBT(NBTTagCompound nbttagcompound) { nbttagcompound.setString("Type", type.shortName); nbttagcompound.setInteger("Health", health); nbttagcompound.setFloat("RotationYaw", rotationYaw); nbttagcompound.setFloat("RotationPitch", rotationPitch); for (int i = 0; i < type.numBarrels; i++) { if (ammo[i] != null) nbttagcompound.setTag("Ammo " + i, ammo[i].writeToNBT(new NBTTagCompound())); } nbttagcompound.setString("Placer", placer.getName()); } @Override protected void readEntityFromNBT(NBTTagCompound nbttagcompound) { type = AAGunType.getAAGun(nbttagcompound.getString("Type")); initType(); health = nbttagcompound.getInteger("Health"); rotationYaw = nbttagcompound.getFloat("RotationYaw"); rotationPitch = nbttagcompound.getFloat("RotationPitch"); for (int i = 0; i < type.numBarrels; i++) { ammo[i] = ItemStack.loadItemStackFromNBT(nbttagcompound.getCompoundTag("Ammo " + i)); } placerName = nbttagcompound.getString("Placer"); } @Override public boolean interactFirst(EntityPlayer entityplayer) //interact : change back when Forge updates { // Player right clicked on gun // Mount gun if (riddenByEntity != null && (riddenByEntity instanceof EntityPlayer) && riddenByEntity != entityplayer) { return true; } if (!worldObj.isRemote) { if (riddenByEntity == entityplayer) { entityplayer.mountEntity(null); return true; } if(!isSentry()) entityplayer.mountEntity(this); for (int i = 0; i < (type.shareAmmo ? 1 : type.numBarrels); i++) { if (ammo[i] == null) { int slot = findAmmo(entityplayer); if (slot >= 0) { ammo[i] = entityplayer.inventory.getStackInSlot(slot).copy(); ammo[i].stackSize = 1; if(!entityplayer.capabilities.isCreativeMode) entityplayer.inventory.decrStackSize(slot, 1); reloadTimer = type.reloadTime; worldObj.playSoundAtEntity(this, type.reloadSound, 1.0F, 1.0F / (rand.nextFloat() * 0.4F + 0.8F)); } } } } return true; } public int findAmmo(EntityPlayer player) { for (int i = 0; i < player.inventory.getSizeInventory(); i++) { ItemStack stack = player.inventory.getStackInSlot(i); if (type.isAmmo(stack)) { return i; } } return -1; } @Override public void writeSpawnData(ByteBuf data) { ByteBufUtils.writeUTF8String(data, type.shortName); } @Override public void readSpawnData(ByteBuf data) { try { type = AAGunType.getAAGun(ByteBufUtils.readUTF8String(data)); initType(); } catch(Exception e) { FlansMod.log("Failed to retreive AA gun type from server."); super.setDead(); e.printStackTrace(); } } @Override public boolean canRiderInteract() { return false; } @Override public ItemStack getPickedResult(MovingObjectPosition target) { ItemStack stack = new ItemStack(type.item, 1, 0); return stack; } }