package openblocks.common.tileentity; import java.util.List; import net.minecraft.block.Block; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.Vec3; import openblocks.Config; import openmods.api.IAddAwareTile; import openmods.api.INeighbourAwareTile; import openmods.api.IPlacerAwareTile; import openmods.sync.SyncableByte; import openmods.sync.SyncableFloat; import openmods.tileentity.SyncedTileEntity; public class TileEntityFan extends SyncedTileEntity implements IPlacerAwareTile, INeighbourAwareTile, IAddAwareTile { private static final double CONE_HALF_APERTURE = 1.2 / 2.0; private SyncableFloat angle; private SyncableByte power; private float bladeRotation; private float bladeSpeed; public TileEntityFan() {} @Override protected void createSyncedFields() { angle = new SyncableFloat(); power = new SyncableByte(); } @Override public void updateEntity() { float redstonePower = power.get() / 15.0f; bladeSpeed = redstonePower; bladeRotation += redstonePower; final double maxForce = Config.fanForce * redstonePower; if (maxForce <= 0) return; @SuppressWarnings("unchecked") List<Entity> entities = worldObj.getEntitiesWithinAABB(Entity.class, getEntitySearchBoundingBox()); if (entities.isEmpty()) return; double angle = Math.toRadians(getAngle() - 90); final Vec3 blockPos = getConeApex(angle); final Vec3 basePos = getConeBaseCenter(angle); final Vec3 coneAxis = Vec3.createVectorHelper(basePos.xCoord - blockPos.xCoord, basePos.yCoord - blockPos.yCoord, basePos.zCoord - blockPos.zCoord); for (Entity entity : entities) { if (entity instanceof EntityPlayer && ((EntityPlayer)entity).capabilities.isCreativeMode) continue; Vec3 directionVec = Vec3.createVectorHelper( entity.posX - blockPos.xCoord, entity.posY - blockPos.yCoord, entity.posZ - blockPos.zCoord); if (isLyingInSphericalCone(coneAxis, directionVec, CONE_HALF_APERTURE)) { final double distToOrigin = directionVec.lengthVector(); final double force = (1.0 - distToOrigin / Config.fanRange) * maxForce; if (force <= 0) continue; Vec3 normal = directionVec.normalize(); entity.motionX += force * normal.xCoord; entity.motionZ += force * normal.zCoord; } } } private Vec3 getConeBaseCenter(double angle) { return Vec3.createVectorHelper( xCoord + (Math.cos(angle) * Config.fanRange), yCoord + 0.5, zCoord + (Math.sin(angle) * Config.fanRange)); } private Vec3 getConeApex(double angle) { return Vec3.createVectorHelper(xCoord + 0.5 - Math.cos(angle) * 1.1, yCoord + 0.5, zCoord + 0.5 - Math.sin(angle) * 1.1); } private AxisAlignedBB getEntitySearchBoundingBox() { AxisAlignedBB boundingBox = AxisAlignedBB.getBoundingBox(xCoord, yCoord - 2, zCoord, xCoord + 1, yCoord + 3, zCoord + 1); return boundingBox.expand(Config.fanRange, Config.fanRange, Config.fanRange); } private static boolean isLyingInSphericalCone(Vec3 coneAxis, Vec3 originToTarget, double halfAperture) { double angleToAxisCos = originToTarget.dotProduct(coneAxis) / originToTarget.lengthVector() / coneAxis.lengthVector(); return angleToAxisCos > Math.cos(halfAperture); } @Override public void onBlockPlacedBy(EntityLivingBase placer, ItemStack stack) { angle.set(placer.rotationYawHead); } public float getAngle() { return angle.get(); } public float getBladeRotation(float partialTickTime) { return bladeRotation + bladeSpeed * partialTickTime; } @Override public void onNeighbourChanged(Block block) { updateRedstone(); } @Override public void onAdded() { updateRedstone(); } private void updateRedstone() { if (!worldObj.isRemote) { int power = Config.redstoneActivatedFan? worldObj.getStrongestIndirectPower(xCoord, yCoord, zCoord) : 15; this.power.set((byte)power); sync(); } } }