package micdoodle8.mods.galacticraft.core.entities.player; import micdoodle8.mods.galacticraft.api.prefab.entity.EntitySpaceshipBase; import micdoodle8.mods.galacticraft.api.world.IZeroGDimension; import micdoodle8.mods.galacticraft.core.Constants; import micdoodle8.mods.galacticraft.core.GCBlocks; import micdoodle8.mods.galacticraft.core.TransformerHooks; import micdoodle8.mods.galacticraft.core.dimension.SpinManager; import micdoodle8.mods.galacticraft.core.dimension.WorldProviderSpaceStation; import micdoodle8.mods.galacticraft.core.entities.EntityLanderBase; import micdoodle8.mods.galacticraft.core.util.ConfigManagerCore; import micdoodle8.mods.galacticraft.core.util.GCLog; import net.minecraft.block.Block; import net.minecraft.block.BlockLiquid; import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityFlying; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.item.EntityFallingBlock; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.BlockPos; import net.minecraft.util.MathHelper; import net.minecraft.world.World; import net.minecraft.world.WorldProvider; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; public class FreefallHandler { private double pPrevMotionX; public double pPrevMotionY; private double pPrevMotionZ; private float jetpackBoost; private double pPrevdY; public boolean sneakLast; public boolean onWall; public int pjumpticks = 0; private GCPlayerStatsClient stats; public FreefallHandler(GCPlayerStatsClient statsClientCapability) { stats = statsClientCapability; } public boolean testFreefall(EntityPlayer player) { //Test whether feet are on a block, also stops the login glitch int playerFeetOnY = (int) (player.getEntityBoundingBox().minY - 0.01D); int xx = MathHelper.floor_double(player.posX); int zz = MathHelper.floor_double(player.posZ); BlockPos pos = new BlockPos(xx, playerFeetOnY, zz); IBlockState state = player.worldObj.getBlockState(pos); Block b = state.getBlock(); if (b.getMaterial() != Material.air && !(b instanceof BlockLiquid)) { double blockYmax = playerFeetOnY + b.getBlockBoundsMaxY(); if (player.getEntityBoundingBox().minY - blockYmax < 0.01D && player.getEntityBoundingBox().minY - blockYmax > -0.5D) { player.onGround = true; if (player.getEntityBoundingBox().minY - blockYmax > 0D) { player.posY -= player.getEntityBoundingBox().minY - blockYmax; player.setEntityBoundingBox(player.getEntityBoundingBox().offset(0, blockYmax - player.getEntityBoundingBox().minY, 0)); } else if (b.canCollideCheck(player.worldObj.getBlockState(new BlockPos(xx, playerFeetOnY, zz)), false)) { AxisAlignedBB collisionBox = b.getCollisionBoundingBox(player.worldObj, new BlockPos(xx, playerFeetOnY, zz), state); if (collisionBox != null && collisionBox.intersectsWith(player.getEntityBoundingBox())) { player.posY -= player.getEntityBoundingBox().minY - blockYmax; player.setEntityBoundingBox(player.getEntityBoundingBox().offset(0, blockYmax - player.getEntityBoundingBox().minY, 0)); } } return false; } } return true; } @SideOnly(Side.CLIENT) private boolean testFreefall(EntityPlayerSP p, boolean flag) { World world = p.worldObj; WorldProvider worldProvider = world.provider; if (!(worldProvider instanceof IZeroGDimension)) { return false; } if (this.pjumpticks > 0 || (stats.isSsOnGroundLast() && p.movementInput.jump)) { return false; } if (p.ridingEntity != null) { Entity e = p.ridingEntity; if (e instanceof EntitySpaceshipBase) { return ((EntitySpaceshipBase) e).getLaunched(); } if (e instanceof EntityLanderBase) { return false; } //TODO: should check whether lander has landed (whatever that means) //TODO: could check other ridden entities - every entity should have its own freefall check :( } //This is an "on the ground" check if (!flag) { return false; } else { float rY = p.rotationYaw % 360F; double zreach = 0D; double xreach = 0D; if (rY < 80F || rY > 280F) { zreach = 0.2D; } if (rY < 170F && rY > 10F) { xreach = 0.2D; } if (rY < 260F && rY > 100F) { zreach = -0.2D; } if (rY < 350F && rY > 190F) { xreach = -0.2D; } AxisAlignedBB playerReach = p.getEntityBoundingBox().addCoord(xreach, 0, zreach); boolean checkBlockWithinReach; if (worldProvider instanceof WorldProviderSpaceStation) { SpinManager spinManager = ((WorldProviderSpaceStation) worldProvider).getSpinManager(); checkBlockWithinReach = playerReach.maxX >= spinManager.ssBoundsMinX && playerReach.minX <= spinManager.ssBoundsMaxX && playerReach.maxY >= spinManager.ssBoundsMinY && playerReach.minY <= spinManager.ssBoundsMaxY && playerReach.maxZ >= spinManager.ssBoundsMinZ && playerReach.minZ <= spinManager.ssBoundsMaxZ; //Player is somewhere within the space station boundaries } else { checkBlockWithinReach = true; } if (checkBlockWithinReach) { //Check if the player's bounding box is in the same block coordinates as any non-vacuum block (including torches etc) //If so, it's assumed the player has something close enough to grab onto, so is not in freefall //Note: breatheable air here means the player is definitely not in freefall int xm = MathHelper.floor_double(playerReach.minX); int xx = MathHelper.floor_double(playerReach.maxX); int ym = MathHelper.floor_double(playerReach.minY); int yy = MathHelper.floor_double(playerReach.maxY); int zm = MathHelper.floor_double(playerReach.minZ); int zz = MathHelper.floor_double(playerReach.maxZ); for (int x = xm; x <= xx; x++) { for (int y = ym; y <= yy; y++) { for (int z = zm; z <= zz; z++) { //Blocks.air is hard vacuum - we want to check for that, here Block b = world.getBlockState(new BlockPos(x, y, z)).getBlock(); if (Blocks.air != b && GCBlocks.brightAir != b) { this.onWall = true; return false; } } } } } } /* if (freefall) { //If that check didn't produce a result, see if the player is inside the walls //TODO: could apply special weightless movement here like Coriolis force - the player is inside the walls, not touching them, and in a vacuum int quadrant = 0; double xd = p.posX - this.spinCentreX; double zd = p.posZ - this.spinCentreZ; if (xd<0) { if (xd<-Math.abs(zd)) { quadrant = 2; } else quadrant = (zd<0) ? 3 : 1; } else if (xd>Math.abs(zd)) { quadrant = 0; } else quadrant = (zd<0) ? 3 : 1; int ymin = MathHelper.floor_double(p.boundingBox.minY)-1; int ymax = MathHelper.floor_double(p.boundingBox.maxY); int xmin, xmax, zmin, zmax; switch (quadrant) { case 0: xmin = MathHelper.floor_double(p.boundingBox.maxX); xmax = this.ssBoundsMaxX - 1; zmin = MathHelper.floor_double(p.boundingBox.minZ)-1; zmax = MathHelper.floor_double(p.boundingBox.maxZ)+1; break; case 1: xmin = MathHelper.floor_double(p.boundingBox.minX)-1; xmax = MathHelper.floor_double(p.boundingBox.maxX)+1; zmin = MathHelper.floor_double(p.boundingBox.maxZ); zmax = this.ssBoundsMaxZ - 1; break; case 2: zmin = MathHelper.floor_double(p.boundingBox.minZ)-1; zmax = MathHelper.floor_double(p.boundingBox.maxZ)+1; xmin = this.ssBoundsMinX; xmax = MathHelper.floor_double(p.boundingBox.minX); break; case 3: default: xmin = MathHelper.floor_double(p.boundingBox.minX)-1; xmax = MathHelper.floor_double(p.boundingBox.maxX)+1; zmin = this.ssBoundsMinZ; zmax = MathHelper.floor_double(p.boundingBox.minZ); break; } //This block search could cost a lot of CPU (but client side) - maybe optimise later BLOCKCHECK0: for(int x = xmin; x <= xmax; x++) for (int z = zmin; z <= zmax; z++) for (int y = ymin; y <= ymax; y++) if (Blocks.air != this.worldProvider.worldObj.getBlock(x, y, z)) { freefall = false; break BLOCKCHECK0; } }*/ this.onWall = false; return true; } @SideOnly(Side.CLIENT) public void setupFreefallPre(EntityPlayerSP p) { double dY = p.motionY - pPrevMotionY; jetpackBoost = 0F; pPrevdY = dY; pPrevMotionX = p.motionX; pPrevMotionY = p.motionY; pPrevMotionZ = p.motionZ; } @SideOnly(Side.CLIENT) public void freefallMotion(EntityPlayerSP p) { boolean jetpackUsed = false; double dX = p.motionX - pPrevMotionX; double dY = p.motionY - pPrevMotionY; double dZ = p.motionZ - pPrevMotionZ; double posOffsetX = -p.motionX; double posOffsetY = - p.motionY; if (posOffsetY == - TransformerHooks.getGravityForEntity(p)) posOffsetY = 0; double posOffsetZ = -p.motionZ; //if (p.capabilities.isFlying) ///Undo whatever vanilla tried to do to our y motion if (dY < 0D && p.motionY != 0.0D) { p.motionY = pPrevMotionY; } else if (dY > 0.01D && stats.isInFreefallLast()) { //Impulse upwards - it's probably a jetpack from another mod if (dX < 0.01D && dZ < 0.01D) { float pitch = p.rotationPitch / Constants.RADIANS_TO_DEGREES; jetpackBoost = (float) dY * MathHelper.cos(pitch) * 0.1F; float factor = 1 + MathHelper.sin(pitch) / 5; p.motionY -= dY * factor; jetpackUsed = true; } else { p.motionY -= dY / 2; } } p.motionX -= dX; // p.motionY -= dY; //Enabling this will disable jetpacks p.motionZ -= dZ; if (p.movementInput.moveForward != 0) { p.motionX -= p.movementInput.moveForward * MathHelper.sin(p.rotationYaw / Constants.RADIANS_TO_DEGREES) / (ConfigManagerCore.hardMode ? 600F : 200F); p.motionZ += p.movementInput.moveForward * MathHelper.cos(p.rotationYaw / Constants.RADIANS_TO_DEGREES) / (ConfigManagerCore.hardMode ? 600F : 200F); } if (jetpackBoost != 0) { p.motionX -= jetpackBoost * MathHelper.sin(p.rotationYaw / Constants.RADIANS_TO_DEGREES); p.motionZ += jetpackBoost * MathHelper.cos(p.rotationYaw / Constants.RADIANS_TO_DEGREES); } if (p.movementInput.sneak) { if (!sneakLast) { // posOffsetY += 0.0268; sneakLast = true; } p.motionY -= ConfigManagerCore.hardMode ? 0.002D : 0.0032D; } else if (sneakLast) { sneakLast = false; // posOffsetY -= 0.0268; } if (!jetpackUsed && p.movementInput.jump) { p.motionY += ConfigManagerCore.hardMode ? 0.002D : 0.0032D; } float speedLimit = ConfigManagerCore.hardMode ? 0.9F : 0.7F; if (p.motionX > speedLimit) { p.motionX = speedLimit; } if (p.motionX < -speedLimit) { p.motionX = -speedLimit; } if (p.motionY > speedLimit) { p.motionY = speedLimit; } if (p.motionY < -speedLimit) { p.motionY = -speedLimit; } if (p.motionZ > speedLimit) { p.motionZ = speedLimit; } if (p.motionZ < -speedLimit) { p.motionZ = -speedLimit; } pPrevMotionX = p.motionX; pPrevMotionY = p.motionY; pPrevMotionZ = p.motionZ; p.moveEntity(p.motionX + posOffsetX, p.motionY + posOffsetY, p.motionZ + posOffsetZ); } /* double dyaw = p.rotationYaw - p.prevRotationYaw; p.rotationYaw -= dyaw * 0.8D; double dyawh = p.rotationYawHead - p.prevRotationYawHead; p.rotationYawHead -= dyawh * 0.8D; while (p.rotationYaw > 360F) { p.rotationYaw -= 360F; } while (p.rotationYaw < 0F) { p.rotationYaw += 360F; } while (p.rotationYawHead > 360F) { p.rotationYawHead -= 360F; } while (p.rotationYawHead < 0F) { p.rotationYawHead += 360F; } */ public void updateFreefall(EntityPlayer p) { pPrevMotionX = p.motionX; pPrevMotionY = p.motionY; pPrevMotionZ = p.motionZ; } @SideOnly(Side.CLIENT) public void preVanillaMotion(EntityPlayerSP p) { this.setupFreefallPre(p); stats.setSsOnGroundLast(p.onGround); } @SideOnly(Side.CLIENT) public void postVanillaMotion(EntityPlayerSP p) { World world = p.worldObj; WorldProvider worldProvider = world.provider; if (!(worldProvider instanceof IZeroGDimension)) { return; } boolean freefall = stats.isInFreefall(); freefall = this.testFreefall(p, freefall); stats.setInFreefall(freefall); stats.setInFreefallFirstCheck(true); SpinManager spinManager = null; if (worldProvider instanceof WorldProviderSpaceStation) { spinManager = ((WorldProviderSpaceStation) worldProvider).getSpinManager(); } boolean doCentrifugal = spinManager != null; if (freefall) { this.pjumpticks = 0; //Reverse effects of deceleration p.motionX /= 0.91F; p.motionZ /= 0.91F; p.motionY /= 0.9800000190734863D; if (spinManager != null) { doCentrifugal = spinManager.updatePlayerForSpin(p, 1F); } //Do freefall motion if (!p.capabilities.isCreativeMode) { this.freefallMotion(p); } else { p.capabilities.isFlying = true; //Half the normal acceleration in Creative mode double dx = p.motionX - this.pPrevMotionX; double dy = p.motionY - this.pPrevMotionY; double dz = p.motionZ - this.pPrevMotionZ; p.motionX -= dx / 2; p.motionY -= dy / 2; p.motionZ -= dz / 2; if (p.motionX > 1.2F) { p.motionX = 1.2F; } if (p.motionX < -1.2F) { p.motionX = -1.2F; } if (p.motionY > 0.7F) { p.motionY = 0.7F; } if (p.motionY < -0.7F) { p.motionY = -0.7F; } if (p.motionZ > 1.2F) { p.motionZ = 1.2F; } if (p.motionZ < -1.2F) { p.motionZ = -1.2F; } } //TODO: Think about endless drift? //Player may run out of oxygen - that will kill the player eventually if can't get back to SS //Could auto-kill + respawn the player if floats too far away (config option whether to lose items or not) //But we want players to be able to enjoy the view of the spinning space station from the outside //Arm and leg movements could start tumbling the player? } else //Not freefall - within arm's length of something or jumping { double dy = p.motionY - this.pPrevMotionY; //if (p.motionY < 0 && this.pPrevMotionY >= 0) p.posY -= p.motionY; //if (p.motionY != 0) p.motionY = this.pPrevMotionY; if (p.movementInput.jump) { if ((p.onGround || stats.isSsOnGroundLast()) && !p.capabilities.isCreativeMode) { if (this.pjumpticks < 25) this.pjumpticks++; p.motionY -= dy; } else { p.motionY += 0.015D; if (this.pjumpticks == 0) { p.motionY -= dy; } } } else if (this.pjumpticks > 0) { p.motionY += 0.0145D * this.pjumpticks; this.pjumpticks = 0; } else if (p.movementInput.sneak) { if (!p.onGround) { p.motionY -= 0.015D; } this.pjumpticks = 0; } } //Artificial gravity if (doCentrifugal && !p.onGround) { spinManager.applyCentrifugalForce(p); } this.pPrevMotionX = p.motionX; this.pPrevMotionY = p.motionY; this.pPrevMotionZ = p.motionZ; } /** * Used for non-player entities in ZeroGDimensions */ public static boolean testEntityFreefall(World worldObj, AxisAlignedBB entityBoundingBox) { //Check if the entity's bounding box is in the same block coordinates as any non-vacuum block (including torches etc) //If so, it's assumed the entity has something close enough to catch onto, so is not in freefall //Note: breatheable air here means the entity is definitely not in freefall int xmx = MathHelper.floor_double(entityBoundingBox.maxX + 0.2D); int ym = MathHelper.floor_double(entityBoundingBox.minY - 0.1D); int yy = MathHelper.floor_double(entityBoundingBox.maxY + 0.1D); int zm = MathHelper.floor_double(entityBoundingBox.minZ - 0.2D); int zz = MathHelper.floor_double(entityBoundingBox.maxZ + 0.2D); if (ym < 0) ym = 0; if (yy > 255) yy = 255; for (int x = MathHelper.floor_double(entityBoundingBox.minX - 0.2D); x <= xmx; x++) { for (int z = zm; z <= zz; z++) { if (!worldObj.isBlockLoaded(new BlockPos(x, 0, z), false)) continue; for (int y = ym; y <= yy; y++) { if (Blocks.air != worldObj.getBlockState(new BlockPos(x, y, z)).getBlock()) { return false; } } } } return true; } /** * Call this on every freefalling non-player entity in a dimension * either at the end of the world tick (ideal) or else during the * start of the next world tick (e.g. during updateWeather()) * * May require iteration through the world's loadedEntityList * See SpinManager.updateSpin() for an example * @param e */ public static void tickFreefallEntity(Entity e) { if (e.worldObj.provider instanceof IZeroGDimension) ((IZeroGDimension)e.worldObj.provider).setInFreefall(e); //Undo deceleration applied at the end of the previous tick boolean warnLog = false; if (e instanceof EntityLivingBase) { e.motionX /= (double)0.91F; //0.91F; e.motionZ /= (double)0.91F; //0.91F; e.motionY /= (e instanceof EntityFlying) ? 0.91F : 0.9800000190734863D; if (e.motionX > 10D) { warnLog = true; e.motionX = 10D; } else if (e.motionX < -10D) { warnLog = true; e.motionX = -10D; } if (e.motionY > 10D) { warnLog = true; e.motionY = 10D; } else if (e.motionY < -10D) { warnLog = true; e.motionY = -10D; } if (e.motionZ > 10D) { warnLog = true; e.motionZ = 10D; } else if (e.motionZ < -10D) { warnLog = true; e.motionZ = -10D; } } else if (e instanceof EntityFallingBlock) { e.motionY /= 0.9800000190734863D; //e.motionY += 0.03999999910593033D; //e.posY += 0.03999999910593033D; //e.lastTickPosY += 0.03999999910593033D; if (e.motionY > 10D) { warnLog = true; e.motionY = 10D; } else if (e.motionY < -10D) { warnLog = true; e.motionY = -10D; } } else { e.motionX /= 0.9800000190734863D; e.motionY /= 0.9800000190734863D; e.motionZ /= 0.9800000190734863D; if (e.motionX > 10D) { warnLog = true; e.motionX = 10D; } else if (e.motionX < -10D) { warnLog = true; e.motionX = -10D; } if (e.motionY > 10D) { warnLog = true; e.motionY = 10D; } else if (e.motionY < -10D) { warnLog = true; e.motionY = -10D; } if (e.motionZ > 10D) { warnLog = true; e.motionZ = 10D; } else if (e.motionZ < -10D) { warnLog = true; e.motionZ = -10D; } } if (warnLog) GCLog.debug(e.getName() + " moving too fast"); } }