package crazypants.enderio.machine.killera; import java.util.List; import java.util.Set; import java.util.UUID; import net.minecraft.command.IEntitySelector; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.item.EntityXPOrb; import net.minecraft.entity.monster.EntityZombie; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.item.ItemSword; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.server.MinecraftServer; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.Vec3; import net.minecraft.world.WorldServer; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.util.FakePlayer; import net.minecraftforge.common.util.ForgeDirection; import net.minecraftforge.event.entity.living.ZombieEvent.SummonAidEvent; import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidContainerRegistry; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidTank; import net.minecraftforge.fluids.FluidTankInfo; import net.minecraftforge.fluids.IFluidHandler; import com.enderio.core.api.common.util.ITankAccess; import com.enderio.core.client.render.BoundingBox; import com.enderio.core.common.util.BlockCoord; import com.enderio.core.common.util.FluidUtil; import com.enderio.core.common.util.ForgeDirectionOffsets; import com.enderio.core.common.vecmath.Vector3d; import com.google.common.collect.Sets; import com.mojang.authlib.GameProfile; import cpw.mods.fml.common.eventhandler.SubscribeEvent; import cpw.mods.fml.common.eventhandler.Event.Result; import crazypants.enderio.EnderIO; import crazypants.enderio.ModObject; import crazypants.enderio.config.Config; import crazypants.enderio.machine.AbstractMachineEntity; import crazypants.enderio.machine.FakePlayerEIO; import crazypants.enderio.machine.SlotDefinition; import crazypants.enderio.machine.generator.zombie.IHasNutrientTank; import crazypants.enderio.machine.generator.zombie.PacketNutrientTank; import crazypants.enderio.machine.wireless.WirelessChargedLocation; import crazypants.enderio.network.PacketHandler; import crazypants.enderio.tool.SmartTank; import crazypants.enderio.xp.ExperienceContainer; import crazypants.enderio.xp.IHaveExperience; import crazypants.enderio.xp.PacketExperianceContainer; import crazypants.enderio.xp.XpUtil; public class TileKillerJoe extends AbstractMachineEntity implements IFluidHandler, IEntitySelector, IHaveExperience, ITankAccess, IHasNutrientTank { public static class ZombieCache { private Set<EntityZombie> cache = Sets.newHashSet(); @SubscribeEvent public void onSummonAid(SummonAidEvent event) { if (!cache.isEmpty() && cache.remove(event.getSummoner())) { event.setResult(Result.DENY); } } } public static ZombieCache zCache; private static final int IO_MB_TICK = 250; protected AxisAlignedBB killBounds; private int[] frontFaceAndSides; protected AxisAlignedBB hooverBounds; protected FakePlayer attackera; protected WirelessChargedLocation chargedLocation; final SmartTank fuelTank = new SmartTank(EnderIO.fluidNutrientDistillation, FluidContainerRegistry.BUCKET_VOLUME * 2); int lastFluidLevelUpdate; private boolean tanksDirty; private boolean isSwingInProgress; private int swingProgressInt; private float swingProgress; private float prevSwingProgress; private final ExperienceContainer xpCon = new ExperienceContainer(XpUtil.getExperienceForLevel(Config.killerJoeMaxXpLevel)); private boolean hadSword; public TileKillerJoe() { super(new SlotDefinition(1, 0, 0)); if (zCache == null) { zCache = new ZombieCache(); MinecraftForge.EVENT_BUS.register(zCache); } } @Override public String getMachineName() { return ModObject.blockKillerJoe.unlocalisedName; } @Override protected boolean isMachineItemValidForSlot(int i, ItemStack itemstack) { if(itemstack == null) { return false; } return itemstack.getItem() instanceof ItemSword; } @Override public boolean isActive() { return false; } @Override public void doUpdate() { updateArmSwingProgress(); hooverXP(); if (!worldObj.isRemote) { getAttackera().onUpdate(); if(inventory[0] != null != hadSword) { worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); hadSword = inventory[0] != null; } } super.doUpdate(); } @Override public ExperienceContainer getContainer() { return xpCon; } private static final int[] slots = new int[1]; @Override public int[] getAccessibleSlotsFromSide(int var1) { return slots; } @Override public boolean canExtractItem(int slot, ItemStack itemstack, int side) { if(isSideDisabled(side)) { return false; } if(inventory[slot] == null || inventory[slot].stackSize < itemstack.stackSize) { return false; } return itemstack.getItem() == inventory[slot].getItem(); } @SuppressWarnings("unchecked") @Override protected boolean processTasks(boolean redstoneCheckPassed) { if(!shouldDoWorkThisTick(10)) { return false; } if(tanksDirty) { PacketHandler.sendToAllAround(new PacketNutrientTank(this), this); tanksDirty = false; } if(xpCon.isDirty()) { PacketHandler.sendToAllAround(new PacketExperianceContainer(this), this); xpCon.setDirty(false); } if(!redstoneCheckPassed) { return false; } if(fuelTank.getFluidAmount() < getActivationAmount()) { return false; } if(getStackInSlot(0) == null) { return false; } List<EntityLivingBase> entsInBounds = worldObj.getEntitiesWithinAABB(EntityLivingBase.class, getKillBounds()); if(!entsInBounds.isEmpty()) { for (EntityLivingBase ent : entsInBounds) { if(!ent.isDead && ent.deathTime <= 0 && !ent.isEntityInvulnerable() && ent.hurtResistantTime == 0) { if(ent instanceof EntityPlayer && ((EntityPlayer) ent).capabilities.disableDamage) { continue; //Ignore players in creative, can't damage them; } boolean togglePvp = false; if (ent instanceof EntityPlayer && !MinecraftServer.getServer().isPVPEnabled()) { if (Config.killerPvPoffDisablesSwing) { continue; } else if (Config.killerPvPoffIsIgnored) { togglePvp = true; } } if(Config.killerJoeMustSee && !canJoeSee(ent)) { continue; } if(ent instanceof EntityZombie) { zCache.cache.add((EntityZombie) ent); } FakePlayer fakee = getAttackera(); fakee.setCurrentItemOrArmor(0, getStackInSlot(0)); try { if (togglePvp) { MinecraftServer.getServer().setAllowPvp(true); } fakee.attackTargetEntityWithCurrentItem(ent); } finally { if (togglePvp) { MinecraftServer.getServer().setAllowPvp(false); } } useNutrient(); swingWeapon(); if(getStackInSlot(0).stackSize <= 0 || fakee.getCurrentEquippedItem() == null) { setInventorySlotContents(0, null); } return false; } } } return false; } int getActivationAmount() { return (int) (fuelTank.getCapacity() * 0.7f); } private boolean canJoeSee(EntityLivingBase ent) { Vec3 entPos = Vec3.createVectorHelper(ent.posX, ent.posY + (double) ent.getEyeHeight(), ent.posZ); for (int facing : frontFaceAndSides) { if(this.worldObj.rayTraceBlocks( Vec3.createVectorHelper(this.xCoord + faceMidPoints[facing][0], this.yCoord + faceMidPoints[facing][1], this.zCoord + faceMidPoints[facing][2]), entPos) == null) return true; } return false; } @Override public void setFacing(short facing) { super.setFacing(facing); frontFaceAndSides = new int[] { this.facing, ForgeDirection.ROTATION_MATRIX[0][this.facing], ForgeDirection.ROTATION_MATRIX[1][this.facing] }; } private static final double[][] faceMidPoints = new double[][] { { 0.5D, 0.0D, 0.5D }, { 0.5D, 1.0D, 0.5D }, { 0.5D, 0.5D, 0.0D }, { 0.5D, 0.5D, 1.0D }, { 0.0D, 0.5D, 0.5D }, { 1.0D, 0.5D, 0.5D } }; //------------------------------- XP public ExperienceContainer getXpContainer() { return xpCon; } private void hooverXP() { double maxDist = Config.killerJoeHooverXpLength; List<EntityXPOrb> xp = worldObj.selectEntitiesWithinAABB(EntityXPOrb.class, getHooverBounds(), this); for (EntityXPOrb entity : xp) { double xDist = (xCoord + 0.5D - entity.posX); double yDist = (yCoord + 0.5D - entity.posY); double zDist = (zCoord + 0.5D - entity.posZ); double totalDistance = Math.sqrt(xDist * xDist + yDist * yDist + zDist * zDist); if(totalDistance < 1.5) { hooverXP(entity); } else { double d = 1 - (Math.max(0.1, totalDistance) / maxDist); double speed = 0.01 + (d * 0.02); entity.motionX += xDist / totalDistance * speed; entity.motionZ += zDist / totalDistance * speed; entity.motionY += yDist / totalDistance * speed; if(yDist > 0.5) { entity.motionY = 0.12; } } } } private void hooverXP(EntityXPOrb entity) { if(!worldObj.isRemote) { if(!entity.isDead) { xpCon.addExperience(entity.getXpValue()); entity.setDead(); } } } @Override public boolean isEntityApplicable(Entity arg0) { return true; } //------------------------------- Weapon stuffs void swingWeapon() { if(getStackInSlot(0) == null) { return; } if(!isSwingInProgress || swingProgressInt >= getArmSwingAnimationEnd() / 2 || swingProgressInt < 0) { swingProgressInt = -1; isSwingInProgress = true; if(worldObj instanceof WorldServer) { PacketHandler.sendToAllAround(new PacketSwing(this), this); } } } float getSwingProgress(float p_70678_1_) { float f1 = swingProgress - prevSwingProgress; if(f1 < 0.0F) { ++f1; } return prevSwingProgress + f1 * p_70678_1_; } private void updateArmSwingProgress() { prevSwingProgress = swingProgress; int i = getArmSwingAnimationEnd(); if(isSwingInProgress) { ++swingProgressInt; if(swingProgressInt >= i) { swingProgressInt = 0; isSwingInProgress = false; } } else { swingProgressInt = 0; } swingProgress = (float) swingProgressInt / (float) i; } private int getArmSwingAnimationEnd() { return 6; } FakePlayer getAttackera() { if(attackera == null) { attackera = new Attackera(); } return attackera; } WirelessChargedLocation getChargedLocation() { if(chargedLocation == null) { chargedLocation = new WirelessChargedLocation(this); } return chargedLocation; } private AxisAlignedBB getKillBounds() { if(killBounds == null) { BoundingBox bb = new BoundingBox(getLocation()); Vector3d min = bb.getMin(); Vector3d max = bb.getMax(); max.y += Config.killerJoeAttackHeight; min.y -= Config.killerJoeAttackHeight; ForgeDirection facingDir = ForgeDirection.getOrientation(facing); if(ForgeDirectionOffsets.isPositiveOffset(facingDir)) { max.add(ForgeDirectionOffsets.offsetScaled(facingDir, Config.killerJoeAttackLength)); min.add(ForgeDirectionOffsets.forDir(facingDir)); } else { min.add(ForgeDirectionOffsets.offsetScaled(facingDir, Config.killerJoeAttackLength)); max.add(ForgeDirectionOffsets.forDir(facingDir)); } if(facingDir.offsetX == 0) { min.x -= Config.killerJoeAttackWidth; max.x += Config.killerJoeAttackWidth; } else { min.z -= Config.killerJoeAttackWidth; max.z += Config.killerJoeAttackWidth; } killBounds = AxisAlignedBB.getBoundingBox(min.x, min.y, min.z, max.x, max.y, max.z); } return killBounds; } private AxisAlignedBB getHooverBounds() { if(hooverBounds == null) { BoundingBox bb = new BoundingBox(getLocation()); Vector3d min = bb.getMin(); Vector3d max = bb.getMax(); max.y += Config.killerJoeAttackHeight; min.y -= Config.killerJoeAttackHeight; ForgeDirection facingDir = ForgeDirection.getOrientation(facing); if(ForgeDirectionOffsets.isPositiveOffset(facingDir)) { max.add(ForgeDirectionOffsets.offsetScaled(facingDir, Config.killerJoeHooverXpLength)); min.add(ForgeDirectionOffsets.forDir(facingDir)); } else { min.add(ForgeDirectionOffsets.offsetScaled(facingDir, Config.killerJoeHooverXpLength)); max.add(ForgeDirectionOffsets.forDir(facingDir)); } if(facingDir.offsetX == 0) { min.x -= Config.killerJoeHooverXpWidth * 2; max.x += Config.killerJoeHooverXpWidth * 2; } else { min.z -= Config.killerJoeHooverXpWidth * 2; max.z += Config.killerJoeHooverXpWidth * 2; } hooverBounds = AxisAlignedBB.getBoundingBox(min.x, min.y, min.z, max.x, max.y, max.z); } return hooverBounds; } //------------------------------- Fluid Stuff private void useNutrient() { fuelTank.drain(Config.killerJoeNutrientUsePerAttackMb, true); tanksDirty = true; } @Override protected boolean doPull(ForgeDirection dir) { boolean res = super.doPull(dir); // BlockCoord loc = getLocation().getLocation(dir); // IFluidHandler target = FluidUtil.getFluidHandler(worldObj, loc); // if(target != null) { // FluidTankInfo[] infos = target.getTankInfo(dir.getOpposite()); // if(infos != null) { // for (FluidTankInfo info : infos) { // if(info.fluid != null && info.fluid.amount > 0) { // if(canFill(dir, info.fluid.getFluid())) { // FluidStack canPull = info.fluid.copy(); // canPull.amount = Math.min(IO_MB_TICK, canPull.amount); // FluidStack drained = target.drain(dir.getOpposite(), canPull, false); // if(drained != null && drained.amount > 0) { // int filled = fill(dir, drained, false); // if(filled > 0) { // drained = target.drain(dir.getOpposite(), filled, true); // fill(dir, drained, true); // return res; // } // } // } // } // } // } // } FluidUtil.doPull(this, dir, IO_MB_TICK); return res; } @Override protected boolean doPush(ForgeDirection dir) { boolean res = super.doPush(dir); BlockCoord loc = getLocation().getLocation(dir); IFluidHandler target = FluidUtil.getFluidHandler(worldObj, loc); if(target != null) { FluidStack canDrain = drain(dir, IO_MB_TICK, false); if(canDrain != null && canDrain.amount > 0) { int drained = target.fill(dir.getOpposite(), canDrain, true); if(drained > 0) { drain(dir, drained, true); } } } return res; } @Override public int fill(ForgeDirection from, FluidStack resource, boolean doFill) { int res = fuelTank.fill(resource, doFill); if(res > 0 && doFill) { tanksDirty = true; } return res; } @Override public boolean canFill(ForgeDirection from, Fluid fluid) { return fuelTank.canFill(fluid); } @Override public FluidTankInfo[] getTankInfo(ForgeDirection from) { return new FluidTankInfo[] { fuelTank.getInfo() }; } @Override public FluidStack drain(ForgeDirection from, FluidStack resource, boolean doDrain) { return xpCon.drain(from, resource, doDrain); } @Override public FluidStack drain(ForgeDirection from, int maxDrain, boolean doDrain) { return xpCon.drain(from, maxDrain, doDrain); } @Override public boolean canDrain(ForgeDirection from, Fluid fluid) { return xpCon.canDrain(from, fluid); } //------------------------------- Save / Load @Override public void readCommon(NBTTagCompound nbtRoot) { super.readCommon(nbtRoot); fuelTank.readCommon("fuelTank", nbtRoot); xpCon.readFromNBT(nbtRoot); } @Override public void writeCommon(NBTTagCompound nbtRoot) { super.writeCommon(nbtRoot); fuelTank.writeCommon("fuelTank", nbtRoot); xpCon.writeToNBT(nbtRoot); } private static final UUID uuid = UUID.fromString("3baa66fa-a69a-11e4-89d3-123b93f75cba"); private static final GameProfile DUMMY_PROFILE = new GameProfile(uuid, "[EioKillera]"); private class Attackera extends FakePlayerEIO { ItemStack prevWeapon; public Attackera() { super(getWorldObj(), getLocation(), DUMMY_PROFILE); } @Override public void onUpdate() { setCurrentItemOrArmor(0, getStackInSlot(0)); ItemStack prev = prevWeapon; ItemStack cur = getCurrentEquippedItem(); if(!ItemStack.areItemStacksEqual(cur, prev)) { if(prev != null) { getAttributeMap().removeAttributeModifiers(prev.getAttributeModifiers()); } if(cur != null) { getAttributeMap().applyAttributeModifiers(cur.getAttributeModifiers()); } prevWeapon = cur == null ? null : cur.copy(); } getChargedLocation().chargeItems(inventory.mainInventory); } } @Override public FluidTank getInputTank(FluidStack forFluidType) { if (forFluidType != null && forFluidType.getFluid() == EnderIO.fluidNutrientDistillation) { return fuelTank; } /* * if (forFluidType != null && forFluidType.getFluid() == * EnderIO.fluidXpJuice) { return xpCon; } */ return null; } @Override public FluidTank[] getOutputTanks() { return new FluidTank[] { xpCon /* , fuelTank */}; } @Override public void setTanksDirty() { tanksDirty = true; } @Override public SmartTank getNutrientTank() { return fuelTank; } }