package com.flansmod.apocalypse.common.entity; import java.util.Calendar; import com.flansmod.common.FlansMod; import com.flansmod.common.PlayerHandler; import com.flansmod.common.guns.AttachmentType; import com.flansmod.common.guns.EnumFireMode; import com.flansmod.common.guns.GunType; import com.flansmod.common.guns.InventoryHelper; import com.flansmod.common.guns.ItemGun; import com.flansmod.common.guns.ItemShootable; import com.flansmod.common.guns.ShootableType; import com.flansmod.common.network.PacketPlaySound; import com.flansmod.common.network.PacketReload; import com.flansmod.common.vector.Vector3f; import com.google.common.base.Predicate; import net.minecraft.enchantment.Enchantment; import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.IEntityLivingData; import net.minecraft.entity.IRangedAttackMob; import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.entity.ai.EntityAIArrowAttack; import net.minecraft.entity.ai.EntityAIAvoidEntity; import net.minecraft.entity.ai.EntityAIFleeSun; import net.minecraft.entity.ai.EntityAIHurtByTarget; import net.minecraft.entity.ai.EntityAILookIdle; import net.minecraft.entity.ai.EntityAINearestAttackableTarget; import net.minecraft.entity.ai.EntityAIRestrictSun; import net.minecraft.entity.ai.EntityAISwimming; import net.minecraft.entity.ai.EntityAIWander; import net.minecraft.entity.ai.EntityAIWatchClosest; import net.minecraft.entity.monster.EntityIronGolem; import net.minecraft.entity.monster.EntityMob; import net.minecraft.entity.passive.EntityWolf; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.projectile.EntityArrow; import net.minecraft.init.Blocks; import net.minecraft.init.Items; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.util.BlockPos; import net.minecraft.world.DifficultyInstance; import net.minecraft.world.EnumDifficulty; import net.minecraft.world.EnumSkyBlock; import net.minecraft.world.World; import net.minecraft.world.WorldProviderHell; public class EntityFlansModShooter extends EntityMob implements IRangedAttackMob { private EntityAIArrowAttack aiArrowAttack = new EntityAIArrowAttack(this, 1.0D, 20, 1, 70.0F); public ItemStack[] ammoStacks; public float shootDelay = 0; public float minigunSpeed = 0.0F; public int loopedSoundDelay = 0; public boolean reloading = false; public boolean shouldPlayWarmupSound = true; private int soundDelay = 0; public EntityFlansModShooter(World world) { super(world); ammoStacks = new ItemStack[0]; tasks.addTask(1, new EntityAISwimming(this)); tasks.addTask(4, new EntityAIWander(this, 1.0D)); tasks.addTask(6, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F)); tasks.addTask(6, new EntityAILookIdle(this)); targetTasks.addTask(1, new EntityAIHurtByTarget(this, false, new Class[0])); targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityPlayer.class, true)); targetTasks.addTask(3, new EntityAINearestAttackableTarget(this, EntityIronGolem.class, true)); if (world != null && !world.isRemote) { tasks.addTask(4, this.aiArrowAttack); } renderDistanceWeight = 200D; } @Override public void onUpdate() { super.onUpdate(); if(shootDelay > 0) shootDelay--; } @Override protected void applyEntityAttributes() { super.applyEntityAttributes(); this.getEntityAttribute( SharedMonsterAttributes.followRange).setBaseValue(80D); this.getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(0.25D); } @Override public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, IEntityLivingData data) { data = super.onInitialSpawn(difficulty, data); this.tasks.addTask(4, this.aiArrowAttack); this.setEquipmentBasedOnDifficulty(difficulty); this.setEnchantmentBasedOnDifficulty(difficulty); this.setCanPickUpLoot(this.rand.nextFloat() < 0.55F * difficulty.getClampedAdditionalDifficulty()); return data; } @Override public void attackEntityWithRangedAttack(EntityLivingBase entity, float range) { ItemStack stack = getHeldItem(); if(stack != null && stack.getItem() instanceof ItemGun) { ItemGun item = (ItemGun)stack.getItem(); GunType type = item.GetType(); boolean shouldShoot = false; switch(type.mode) { case MINIGUN: shouldShoot = minigunSpeed >= type.minigunStartSpeed && shootDelay <= 0; break; case BURST: case FULLAUTO: case SEMIAUTO: shouldShoot = shootDelay <= 0; break; } if(type.useLoopingSounds && loopedSoundDelay <= 0 && minigunSpeed > 0.1F && !reloading) { loopedSoundDelay = shouldPlayWarmupSound ? type.warmupSoundLength : type.loopedSoundLength; PacketPlaySound.sendSoundPacket(posX, posY, posZ, FlansMod.soundRange, dimension, shouldPlayWarmupSound ? type.warmupSound : type.loopedSound, false); shouldPlayWarmupSound = false; } if(shouldShoot) { //player.inventory.setInventorySlotContents(player.inventory.currentItem, tryToShoot(itemstack, type, world, player, false)); int damage = 0; //Check all gun's slots for a valid bullet to shoot int bulletID = 0; ItemStack bulletStack = null; for(; bulletID < type.numAmmoItemsInGun; bulletID++) { ItemStack checkingStack = item.getBulletItemStack(stack, bulletID); if(checkingStack != null && checkingStack.getItem() != null && checkingStack.getItemDamage() < checkingStack.getMaxDamage()) { bulletStack = checkingStack; break; } } //If no bullet stack was found, reload if(bulletStack == null) { if(reload(stack, type, worldObj, this, false, false)) { //Set player shoot delay to be the reload delay //Set both gun delays to avoid reloading two guns at once shootDelay = (int)type.getReloadTime(stack); reloading = true; //Play reload sound if(type.reloadSound != null) PacketPlaySound.sendSoundPacket(posX, posY, posZ, FlansMod.soundRange, dimension, type.reloadSound, true); } } //A bullet stack was found, so try shooting with it else if(bulletStack.getItem() instanceof ItemShootable) { //Shoot shoot(stack, type, worldObj, bulletStack, this, false, entity); //Damage the bullet item damage = bulletStack.getItemDamage() + 1; bulletStack.setItemDamage(damage); //Update the stack in the gun item.setBulletItemStack(stack, bulletStack, bulletID); } switch(type.mode) { case FULLAUTO: case MINIGUN : shootDelay = type.GetShootDelay(stack); break; case SEMIAUTO: shootDelay = 2 * type.shootDelay; break; case BURST: shootDelay = (damage % 3 == 0 ? 3 * shootDelay : shootDelay); break; } } } } /** Reload method. Called automatically when firing with an empty clip */ public boolean reload(ItemStack gunStack, GunType gunType, World world, Entity entity, boolean creative, boolean forceReload) { ItemGun item = ((ItemGun)gunType.item); //Deployable guns cannot be reloaded in the inventory if(gunType.deployable) return false; //If you cannot reload half way through a clip, reject the player for trying to do so if(forceReload && !gunType.canForceReload) return false; //For playing sounds afterwards boolean reloadedSomething = false; //Check each ammo slot, one at a time for(int i = 0; i < gunType.numAmmoItemsInGun; i++) { //Get the stack in the slot ItemStack bulletStack = item.getBulletItemStack(gunStack, i); //If there is no magazine, if the magazine is empty or if this is a forced reload if(bulletStack == null || bulletStack.getItemDamage() == bulletStack.getMaxDamage() || forceReload) { //Iterate over all inventory slots and find the magazine / bullet item with the most bullets int bestSlot = -1; int bulletsInBestSlot = 0; for (int j = 0; j < ammoStacks.length; j++) { ItemStack searchingStack = ammoStacks[j]; if (searchingStack != null && searchingStack.getItem() instanceof ItemShootable && gunType.isAmmo(((ItemShootable)(searchingStack.getItem())).type)) { int bulletsInThisSlot = searchingStack.getMaxDamage() - searchingStack.getItemDamage(); if(bulletsInThisSlot > bulletsInBestSlot) { bestSlot = j; bulletsInBestSlot = bulletsInThisSlot; } } } //If there was a valid non-empty magazine / bullet item somewhere in the inventory, load it if(bestSlot != -1) { ItemStack newBulletStack = ammoStacks[bestSlot]; ShootableType newBulletType = ((ItemShootable)newBulletStack.getItem()).type; //Unload the old magazine (Drop an item if it is required and the player is not in creative mode) if(bulletStack != null && bulletStack.getItem() instanceof ItemShootable && ((ItemShootable)bulletStack.getItem()).type.dropItemOnReload != null && !creative) item.dropItem(world, this, ((ItemShootable)bulletStack.getItem()).type.dropItemOnReload); //Load the new magazine ItemStack stackToLoad = newBulletStack.copy(); stackToLoad.stackSize = 1; item.setBulletItemStack(gunStack, stackToLoad, i); //Remove the magazine from the inventory if(!creative) newBulletStack.stackSize--; if(newBulletStack.stackSize <= 0) newBulletStack = null; ammoStacks[bestSlot] = newBulletStack; //Tell the sound player that we reloaded something reloadedSomething = true; } } } return reloadedSomething; } /** Method for shooting to avoid repeated code */ private void shoot(ItemStack stack, GunType gunType, World world, ItemStack bulletStack, Entity entity, boolean left, EntityLivingBase target) { ItemGun item = (ItemGun)gunType.item; ShootableType bullet = ((ItemShootable)bulletStack.getItem()).type; // Play a sound if the previous sound has finished if (soundDelay <= 0 && gunType.shootSound != null) { AttachmentType barrel = gunType.getBarrel(stack); boolean silenced = barrel != null && barrel.silencer; //world.playSoundAtEntity(entityplayer, type.shootSound, 10F, type.distortSound ? 1.0F / (world.rand.nextFloat() * 0.4F + 0.8F) : 1.0F); PacketPlaySound.sendSoundPacket(posX, posY, posZ, FlansMod.soundRange, dimension, gunType.shootSound, gunType.distortSound, silenced); soundDelay = gunType.shootSoundLength; } if (!world.isRemote) { float inaccuracy = 0.5F; // Spawn the bullet entities for (int k = 0; k < gunType.numBullets * bullet.numBullets; k++) { Vector3f origin = new Vector3f(posX, posY + getEyeHeight(), posZ); Vector3f direction = new Vector3f(target.posX - posX, (target.posY + target.getEyeHeight()) - (posY + getEyeHeight()), target.posZ - posZ).normalise(null); Vector3f.add(direction, new Vector3f(rand.nextFloat() * direction.x * inaccuracy, rand.nextFloat() * direction.y * inaccuracy, rand.nextFloat() * direction.z * inaccuracy), direction); ItemShootable shootableItem = (ItemShootable)bulletStack.getItem(); shootableItem.Shoot(worldObj, origin, direction, gunType.getDamage(stack), gunType.getSpread(stack), gunType.getBulletSpeed(stack), gunType, this); } // Drop item on shooting if bullet requires it if(bullet.dropItemOnShoot != null) item.dropItem(world, this, bullet.dropItemOnShoot); // Drop item on shooting if gun requires it if(gunType.dropItemOnShoot != null) item.dropItem(world, this, gunType.dropItemOnShoot); } shootDelay = gunType.GetShootDelay(stack); } @Override protected boolean canDespawn() { return false; } @Override protected boolean isValidLightLevel() { return true; } @Override public boolean getCanSpawnHere() { return this.worldObj.getDifficulty() != EnumDifficulty.PEACEFUL; } }