public void DisplaceWithBlockCollision(EntityPos pos, EntityControls controls, float dt) { IBlockAccessor blockAccess = entity.World.BlockAccessor; nextPosition.Set(pos.X + pos.Motion.X, pos.Y + pos.Motion.Y, pos.Z + pos.Motion.Z); bool falling = pos.Motion.Y < 0; bool feetInLiquidBefore = entity.FeetInLiquid; bool onGroundBefore = entity.OnGround; bool swimmingBefore = entity.Swimming; double prevYMotion = pos.Motion.Y; controls.IsClimbing = false; if (!onGroundBefore && entity.Properties.CanClimb == true) { int height = (int)Math.Ceiling(entity.CollisionBox.Y2); entityBox.Set(entity.CollisionBox).Translate(pos.X, pos.Y, pos.Z); for (int dy = 0; dy < height; dy++) { tmpPos.Set((int)pos.X, (int)pos.Y + dy, (int)pos.Z); Block nblock = blockAccess.GetBlock(tmpPos); if (!nblock.Climbable && !entity.Properties.CanClimbAnywhere) { continue; } Cuboidf[] collBoxes = nblock.GetCollisionBoxes(blockAccess, tmpPos); if (collBoxes == null) { continue; } for (int i = 0; i < collBoxes.Length; i++) { double dist = entityBox.ShortestDistanceFrom(collBoxes[i], tmpPos); controls.IsClimbing |= dist < entity.Properties.ClimbTouchDistance; if (controls.IsClimbing) { entity.ClimbingOnFace = null; break; } } } for (int i = 0; !controls.IsClimbing && i < BlockFacing.HORIZONTALS.Length; i++) { BlockFacing facing = BlockFacing.HORIZONTALS[i]; for (int dy = 0; dy < height; dy++) { tmpPos.Set((int)pos.X + facing.Normali.X, (int)pos.Y + dy, (int)pos.Z + facing.Normali.Z); Block nblock = blockAccess.GetBlock(tmpPos); if (!nblock.Climbable && !entity.Properties.CanClimbAnywhere) { continue; } Cuboidf[] collBoxes = nblock.GetCollisionBoxes(blockAccess, tmpPos); if (collBoxes == null) { continue; } for (int j = 0; j < collBoxes.Length; j++) { double dist = entityBox.ShortestDistanceFrom(collBoxes[j], tmpPos); controls.IsClimbing |= dist < entity.Properties.ClimbTouchDistance; if (controls.IsClimbing) { entity.ClimbingOnFace = facing; break; } } } } } if (controls.IsClimbing) { if (controls.WalkVector.Y == 0) { pos.Motion.Y = controls.Sneak ? Math.Max(-0.07, pos.Motion.Y - 0.07) : pos.Motion.Y; if (controls.Jump) { pos.Motion.Y = 0.04; } } nextPosition.Set(pos.X + pos.Motion.X, pos.Y + pos.Motion.Y, pos.Z + pos.Motion.Z); } collisionTester.ApplyTerrainCollision(entity, pos, ref outposition, !(entity is EntityPlayer)); bool isStepping = HandleSteppingOnBlocks(pos, controls); HandleSneaking(pos, controls, dt); if (blockAccess.IsNotTraversable((int)(pos.X + pos.Motion.X), (int)pos.Y, (int)pos.Z)) { outposition.X = pos.X; } if (blockAccess.IsNotTraversable((int)pos.X, (int)(pos.Y + pos.Motion.Y), (int)pos.Z)) { outposition.Y = pos.Y; } if (blockAccess.IsNotTraversable((int)pos.X, (int)pos.Y, (int)(pos.Z + pos.Motion.Z))) { outposition.Z = pos.Z; } pos.SetPos(outposition); // Set the players motion to zero if he collided. if ((nextPosition.X < outposition.X && pos.Motion.X < 0) || (nextPosition.X > outposition.X && pos.Motion.X > 0)) { pos.Motion.X = 0; } if ((nextPosition.Y < outposition.Y && pos.Motion.Y < 0) || (nextPosition.Y > outposition.Y && pos.Motion.Y > 0)) { pos.Motion.Y = 0; } if ((nextPosition.Z < outposition.Z && pos.Motion.Z < 0) || (nextPosition.Z > outposition.Z && pos.Motion.Z > 0)) { pos.Motion.Z = 0; } Block block = blockAccess.GetBlock((int)pos.X, (int)(pos.Y), (int)pos.Z); Block aboveblock = blockAccess.GetBlock((int)pos.X, (int)(pos.Y + 1), (int)pos.Z); Block middleBlock = blockAccess.GetBlock((int)pos.X, (int)(pos.Y + entity.CollisionBox.Y1 + entity.CollisionBox.Y2 * 0.66f), (int)pos.Z); entity.OnGround = (entity.CollidedVertically && falling && !controls.IsClimbing) || isStepping; entity.FeetInLiquid = block.IsLiquid() && ((block.LiquidLevel + (aboveblock.LiquidLevel > 0 ? 1 : 0)) / 8f >= pos.Y - (int)pos.Y); entity.Swimming = middleBlock.IsLiquid(); // Console.WriteLine(entity.World.Side + ": "+ entity.OnGround + " / " + pos.Y); if (!onGroundBefore && entity.OnGround) { entity.OnFallToGround(prevYMotion); } if ((!entity.Swimming && !feetInLiquidBefore && entity.FeetInLiquid) || (!entity.FeetInLiquid && !swimmingBefore && entity.Swimming)) { entity.OnCollideWithLiquid(); } if ((swimmingBefore && !entity.Swimming && !entity.FeetInLiquid) || (feetInLiquidBefore && !entity.FeetInLiquid && !entity.Swimming)) { entity.OnExitedLiquid(); } if (!falling || entity.OnGround || controls.IsClimbing) { entity.PositionBeforeFalling.Set(outposition); } Cuboidd testedEntityBox = collisionTester.entityBox; for (int y = (int)testedEntityBox.Y1; y <= (int)testedEntityBox.Y2; y++) { for (int x = (int)testedEntityBox.X1; x <= (int)testedEntityBox.X2; x++) { for (int z = (int)testedEntityBox.Z1; z <= (int)testedEntityBox.Z2; z++) { collisionTester.tmpPos.Set(x, y, z); collisionTester.tempCuboid.Set(x, y, z, x + 1, y + 1, z + 1); if (collisionTester.tempCuboid.IntersectsOrTouches(testedEntityBox)) { // Saves us a few cpu cycles if (x == (int)pos.X && y == (int)pos.Y && z == (int)pos.Z) { block.OnEntityInside(entity.World, entity, collisionTester.tmpPos); continue; } blockAccess.GetBlock(x, y, z).OnEntityInside(entity.World, entity, collisionTester.tmpPos); } } } } }
public void DisplaceWithBlockCollision(EntityPos pos, EntityControls controls, float dt) { IBlockAccessor blockAccess = entity.World.BlockAccessor; float dtFac = 60 * dt; moveDelta.Set(pos.Motion.X * dtFac, pos.Motion.Y * dtFac, pos.Motion.Z * dtFac); nextPosition.Set(pos.X + moveDelta.X, pos.Y + moveDelta.Y, pos.Z + moveDelta.Z); bool falling = pos.Motion.Y < 0; bool feetInLiquidBefore = entity.FeetInLiquid; bool onGroundBefore = entity.OnGround; bool swimmingBefore = entity.Swimming; double prevYMotion = pos.Motion.Y; controls.IsClimbing = false; if (!onGroundBefore && entity.Properties.CanClimb == true) { int height = (int)Math.Ceiling(entity.CollisionBox.Y2); entityBox.Set(entity.CollisionBox).Translate(pos.X, pos.Y, pos.Z); for (int dy = 0; dy < height; dy++) { tmpPos.Set((int)pos.X, (int)pos.Y + dy, (int)pos.Z); Block nblock = blockAccess.GetBlock(tmpPos); if (!nblock.Climbable && !entity.Properties.CanClimbAnywhere) { continue; } Cuboidf[] collBoxes = nblock.GetCollisionBoxes(blockAccess, tmpPos); if (collBoxes == null) { continue; } for (int i = 0; i < collBoxes.Length; i++) { double dist = entityBox.ShortestDistanceFrom(collBoxes[i], tmpPos); controls.IsClimbing |= dist < entity.Properties.ClimbTouchDistance; if (controls.IsClimbing) { entity.ClimbingOnFace = null; break; } } } for (int i = 0; !controls.IsClimbing && i < BlockFacing.HORIZONTALS.Length; i++) { BlockFacing facing = BlockFacing.HORIZONTALS[i]; for (int dy = 0; dy < height; dy++) { tmpPos.Set((int)pos.X + facing.Normali.X, (int)pos.Y + dy, (int)pos.Z + facing.Normali.Z); Block nblock = blockAccess.GetBlock(tmpPos); if (!nblock.Climbable && !(entity.Properties.CanClimbAnywhere && entity.Alive)) { continue; } Cuboidf[] collBoxes = nblock.GetCollisionBoxes(blockAccess, tmpPos); if (collBoxes == null) { continue; } for (int j = 0; j < collBoxes.Length; j++) { double dist = entityBox.ShortestDistanceFrom(collBoxes[j], tmpPos); controls.IsClimbing |= dist < entity.Properties.ClimbTouchDistance; if (controls.IsClimbing) { entity.ClimbingOnFace = facing; entity.ClimbingOnCollBox = collBoxes[j]; break; } } } } } if (controls.IsClimbing) { if (controls.WalkVector.Y == 0) { pos.Motion.Y = controls.Sneak ? Math.Max(-0.07, pos.Motion.Y - 0.07) : pos.Motion.Y; if (controls.Jump) { pos.Motion.Y = 0.035 * dt * 60f; } } // what was this for? it causes jitter //moveDelta.Y = pos.Motion.Y * dt * 60f; //nextPosition.Set(pos.X + moveDelta.X, pos.Y + moveDelta.Y, pos.Z + moveDelta.Z); } collisionTester.ApplyTerrainCollision(entity, pos, dtFac, ref outposition, stepHeight); if (!entity.Properties.CanClimbAnywhere) { controls.IsStepping = HandleSteppingOnBlocks(pos, moveDelta, dtFac, controls); } HandleSneaking(pos, controls, dt); if (entity.CollidedHorizontally && !controls.IsClimbing && !controls.IsStepping) { if (blockAccess.GetBlock((int)pos.X, (int)(pos.Y), (int)pos.Z).LiquidLevel >= 7 || (blockAccess.GetBlock((int)pos.X, (int)(pos.Y - 0.05), (int)pos.Z).LiquidLevel >= 7)) { pos.Motion.Y += 0.2 * dt; controls.IsStepping = true; } } if (blockAccess.IsNotTraversable((pos.X + pos.Motion.X * dt * 60f), pos.Y, pos.Z)) { outposition.X = pos.X; } if (blockAccess.IsNotTraversable(pos.X, (pos.Y + pos.Motion.Y * dt * 60f), pos.Z)) { outposition.Y = pos.Y; } if (blockAccess.IsNotTraversable(pos.X, pos.Y, (pos.Z + pos.Motion.Z * dt * 60f))) { outposition.Z = pos.Z; } pos.SetPos(outposition); // Set the motion to zero if he collided. if ((nextPosition.X < outposition.X && pos.Motion.X < 0) || (nextPosition.X > outposition.X && pos.Motion.X > 0)) { pos.Motion.X = 0; } if ((nextPosition.Y < outposition.Y && pos.Motion.Y < 0) || (nextPosition.Y > outposition.Y && pos.Motion.Y > 0)) { pos.Motion.Y = 0; } if ((nextPosition.Z < outposition.Z && pos.Motion.Z < 0) || (nextPosition.Z > outposition.Z && pos.Motion.Z > 0)) { pos.Motion.Z = 0; } float offX = entity.CollisionBox.X2 - entity.OriginCollisionBox.X2; float offZ = entity.CollisionBox.Z2 - entity.OriginCollisionBox.Z2; int posX = (int)(pos.X + offX); int posZ = (int)(pos.Z + offZ); Block block = blockAccess.GetBlock(posX, (int)(pos.Y), posZ); Block aboveblock = blockAccess.GetBlock(posX, (int)(pos.Y + 1), posZ); Block middleBlock = blockAccess.GetBlock(posX, (int)(pos.Y + entity.SwimmingOffsetY), posZ); entity.OnGround = (entity.CollidedVertically && falling && !controls.IsClimbing) || controls.IsStepping; entity.FeetInLiquid = block.IsLiquid() && ((block.LiquidLevel + (aboveblock.LiquidLevel > 0 ? 1 : 0)) / 8f >= pos.Y - (int)pos.Y); entity.InLava = block.LiquidCode == "lava"; entity.Swimming = middleBlock.IsLiquid(); if (!onGroundBefore && entity.OnGround) { entity.OnFallToGround(prevYMotion); } if ((!entity.Swimming && !feetInLiquidBefore && entity.FeetInLiquid) || (!entity.FeetInLiquid && !swimmingBefore && entity.Swimming)) { entity.OnCollideWithLiquid(); } if ((swimmingBefore && !entity.Swimming && !entity.FeetInLiquid) || (feetInLiquidBefore && !entity.FeetInLiquid && !entity.Swimming)) { entity.OnExitedLiquid(); } if (!falling || entity.OnGround || controls.IsClimbing) { entity.PositionBeforeFalling.Set(outposition); } Cuboidd testedEntityBox = collisionTester.entityBox; for (int y = (int)testedEntityBox.Y1; y <= (int)testedEntityBox.Y2; y++) { for (int x = (int)testedEntityBox.X1; x <= (int)testedEntityBox.X2; x++) { for (int z = (int)testedEntityBox.Z1; z <= (int)testedEntityBox.Z2; z++) { collisionTester.tmpPos.Set(x, y, z); collisionTester.tempCuboid.Set(x, y, z, x + 1, y + 1, z + 1); if (collisionTester.tempCuboid.IntersectsOrTouches(testedEntityBox)) { // Saves us a few cpu cycles if (x == (int)pos.X && y == (int)pos.Y && z == (int)pos.Z) { block.OnEntityInside(entity.World, entity, collisionTester.tmpPos); continue; } blockAccess.GetBlock(x, y, z).OnEntityInside(entity.World, entity, collisionTester.tmpPos); } } } } }
public void DisplaceWithBlockCollision(EntityPos pos, EntityControls controls, float dt) { IBlockAccessor blockAccess = entity.World.BlockAccessor; FrameProfilerUtil profiler = entity.World.FrameProfiler; float dtFac = 60 * dt; double prevYMotion = pos.Motion.Y; moveDelta.Set(pos.Motion.X * dtFac, prevYMotion * dtFac, pos.Motion.Z * dtFac); nextPosition.Set(pos.X + moveDelta.X, pos.Y + moveDelta.Y, pos.Z + moveDelta.Z); bool falling = prevYMotion < 0; bool feetInLiquidBefore = entity.FeetInLiquid; bool onGroundBefore = entity.OnGround; bool swimmingBefore = entity.Swimming; controls.IsClimbing = false; entity.ClimbingOnFace = null; entity.ClimbingIntoFace = null; if (/*!onGroundBefore &&*/ entity.Properties.CanClimb == true) { int height = (int)Math.Ceiling(entity.CollisionBox.Y2); entityBox.SetAndTranslate(entity.CollisionBox, pos.X, pos.Y, pos.Z); for (int dy = 0; dy < height; dy++) { tmpPos.Set((int)pos.X, (int)pos.Y + dy, (int)pos.Z); Block nblock = blockAccess.GetBlock(tmpPos); if (!nblock.Climbable && !entity.Properties.CanClimbAnywhere) { continue; } Cuboidf[] collBoxes = nblock.GetCollisionBoxes(blockAccess, tmpPos); if (collBoxes == null) { continue; } for (int i = 0; i < collBoxes.Length; i++) { double dist = entityBox.ShortestDistanceFrom(collBoxes[i], tmpPos); controls.IsClimbing |= dist < entity.Properties.ClimbTouchDistance; if (controls.IsClimbing) { entity.ClimbingOnFace = null; break; } } } if (controls.WalkVector.LengthSq() > 0.00001 && entity.Properties.CanClimbAnywhere && entity.Alive) { var walkIntoFace = BlockFacing.FromVector(controls.WalkVector.X, controls.WalkVector.Y, controls.WalkVector.Z); if (walkIntoFace != null) { tmpPos.Set((int)pos.X + walkIntoFace.Normali.X, (int)pos.Y + walkIntoFace.Normali.Y, (int)pos.Z + walkIntoFace.Normali.Z); Block nblock = blockAccess.GetBlock(tmpPos); Cuboidf[] icollBoxes = nblock.GetCollisionBoxes(blockAccess, tmpPos); entity.ClimbingIntoFace = (icollBoxes != null && icollBoxes.Length != 0) ? walkIntoFace : null; } } for (int i = 0; !controls.IsClimbing && i < BlockFacing.HORIZONTALS.Length; i++) { BlockFacing facing = BlockFacing.HORIZONTALS[i]; for (int dy = 0; dy < height; dy++) { tmpPos.Set((int)pos.X + facing.Normali.X, (int)pos.Y + dy, (int)pos.Z + facing.Normali.Z); Block nblock = blockAccess.GetBlock(tmpPos); if (!nblock.Climbable && !(entity.Properties.CanClimbAnywhere && entity.Alive)) { continue; } Cuboidf[] collBoxes = nblock.GetCollisionBoxes(blockAccess, tmpPos); if (collBoxes == null) { continue; } for (int j = 0; j < collBoxes.Length; j++) { double dist = entityBox.ShortestDistanceFrom(collBoxes[j], tmpPos); controls.IsClimbing |= dist < entity.Properties.ClimbTouchDistance; if (controls.IsClimbing) { entity.ClimbingOnFace = facing; entity.ClimbingOnCollBox = collBoxes[j]; break; } } } } } if (controls.IsClimbing) { if (controls.WalkVector.Y == 0) { pos.Motion.Y = controls.Sneak ? Math.Max(-0.07, pos.Motion.Y - 0.07) : pos.Motion.Y; if (controls.Jump) { pos.Motion.Y = 0.035 * dt * 60f; } } // what was this for? it causes jitter // moveDelta.Y = pos.Motion.Y * dt * 60f; // nextPosition.Set(pos.X + moveDelta.X, pos.Y + moveDelta.Y, pos.Z + moveDelta.Z); } profiler.Mark("prep"); collisionTester.ApplyTerrainCollision(entity, pos, dtFac, ref outposition, stepHeight); profiler.Mark("terraincollision"); if (!entity.Properties.CanClimbAnywhere) { if (smoothStepping) { controls.IsStepping = HandleSteppingOnBlocksSmooth(pos, moveDelta, dtFac, controls); } else { controls.IsStepping = HandleSteppingOnBlocks(pos, moveDelta, dtFac, controls); } } profiler.Mark("stepping-checks"); HandleSneaking(pos, controls, dt); if (entity.CollidedHorizontally && !controls.IsClimbing && !controls.IsStepping && entity.Properties.Habitat != EnumHabitat.Underwater) { if (blockAccess.GetBlock((int)pos.X, (int)(pos.Y + 0.5), (int)pos.Z).LiquidLevel >= 7 || blockAccess.GetBlock((int)pos.X, (int)(pos.Y), (int)pos.Z).LiquidLevel >= 7 || (blockAccess.GetBlock((int)pos.X, (int)(pos.Y - 0.05), (int)pos.Z).LiquidLevel >= 7)) { pos.Motion.Y += 0.2 * dt; controls.IsStepping = true; } else // attempt to prevent endless collisions { double absX = Math.Abs(pos.Motion.X); double absZ = Math.Abs(pos.Motion.Z); if (absX > absZ) { if (absZ < 0.001) { pos.Motion.Z += pos.Motion.Z < 0 ? -0.0025 : 0.0025; } } else { if (absX < 0.001) { pos.Motion.X += pos.Motion.X < 0 ? -0.0025 : 0.0025; } } } } if (outposition.X != pos.X && blockAccess.IsNotTraversable((pos.X + pos.Motion.X * dt * 60f), pos.Y, pos.Z)) { outposition.X = pos.X; } if (outposition.Y != pos.Y && blockAccess.IsNotTraversable(pos.X, (pos.Y + pos.Motion.Y * dt * 60f), pos.Z)) { outposition.Y = pos.Y; } if (outposition.Z != pos.Z && blockAccess.IsNotTraversable(pos.X, pos.Y, (pos.Z + pos.Motion.Z * dt * 60f))) { outposition.Z = pos.Z; } pos.SetPos(outposition); profiler.Mark("apply-motion"); // Set the motion to zero if he collided. if ((nextPosition.X < outposition.X && pos.Motion.X < 0) || (nextPosition.X > outposition.X && pos.Motion.X > 0)) { pos.Motion.X = 0; } if ((nextPosition.Y < outposition.Y && pos.Motion.Y < 0) || (nextPosition.Y > outposition.Y && pos.Motion.Y > 0)) { pos.Motion.Y = 0; } if ((nextPosition.Z < outposition.Z && pos.Motion.Z < 0) || (nextPosition.Z > outposition.Z && pos.Motion.Z > 0)) { pos.Motion.Z = 0; } float offX = entity.CollisionBox.X2 - entity.OriginCollisionBox.X2; float offZ = entity.CollisionBox.Z2 - entity.OriginCollisionBox.Z2; int posX = (int)(pos.X + offX); int posZ = (int)(pos.Z + offZ); Block block = blockAccess.GetBlock(posX, (int)(pos.Y), posZ); Block waterOrIce = blockAccess.GetLiquidBlock(posX, (int)(pos.Y), posZ); Block middleWOIBlock = blockAccess.GetLiquidBlock(posX, (int)(pos.Y + entity.SwimmingOffsetY), posZ); entity.OnGround = (entity.CollidedVertically && falling && !controls.IsClimbing) || controls.IsStepping; entity.FeetInLiquid = false; if (waterOrIce.IsLiquid()) { Block aboveblock = blockAccess.GetLiquidBlock(posX, (int)(pos.Y + 1), posZ); entity.FeetInLiquid = ((waterOrIce.LiquidLevel + (aboveblock.LiquidLevel > 0 ? 1 : 0)) / 8f >= pos.Y - (int)pos.Y); } entity.InLava = block.LiquidCode == "lava"; entity.Swimming = middleWOIBlock.IsLiquid(); if (!onGroundBefore && entity.OnGround) { entity.OnFallToGround(prevYMotion); } if ((!entity.Swimming && !feetInLiquidBefore && entity.FeetInLiquid) || (!entity.FeetInLiquid && !swimmingBefore && entity.Swimming)) { entity.OnCollideWithLiquid(); } if ((swimmingBefore && !entity.Swimming && !entity.FeetInLiquid) || (feetInLiquidBefore && !entity.FeetInLiquid && !entity.Swimming)) { entity.OnExitedLiquid(); } if (!falling || entity.OnGround || controls.IsClimbing) { entity.PositionBeforeFalling.Set(outposition); } profiler.Mark("apply-collisionandflags"); Cuboidd testedEntityBox = collisionTester.entityBox; int xMax = (int)testedEntityBox.X2; int yMax = (int)testedEntityBox.Y2; int zMax = (int)testedEntityBox.Z2; int zMin = (int)testedEntityBox.Z1; for (int y = (int)testedEntityBox.Y1; y <= yMax; y++) { for (int x = (int)testedEntityBox.X1; x <= xMax; x++) { for (int z = zMin; z <= zMax; z++) { collisionTester.tmpPos.Set(x, y, z); collisionTester.tempCuboid.Set(x, y, z, x + 1, y + 1, z + 1); if (collisionTester.tempCuboid.IntersectsOrTouches(testedEntityBox)) { // Saves us a few cpu cycles if (x == (int)pos.X && z == (int)pos.Z && y == (int)pos.Y) { block.OnEntityInside(entity.World, entity, collisionTester.tmpPos); continue; } blockAccess.GetBlock(x, y, z).OnEntityInside(entity.World, entity, collisionTester.tmpPos); } } } } profiler.Mark("trigger-insideblock"); }