private bool TryPlace(Block block, int dx, int dy, int dz) { IBlockAccessor blockAccess = entity.World.BlockAccessor; BlockPos pos = entity.ServerPos.XYZ.AsBlockPos.Add(dx, dy, dz); if (blockAccess.GetLiquidBlock(pos).IsLiquid()) { return(false); } if (!blockAccess.GetBlock(pos).IsReplacableBy(block)) { return(false); } pos.Y--; if (blockAccess.GetSolidBlock(pos.X, pos.Y, pos.Z).CanAttachBlockAt(blockAccess, block, pos, BlockFacing.UP)) { pos.Y++; blockAccess.SetBlock(block.BlockId, pos); // Instantly despawn the block again if it expired already BlockEntityTransient betran = blockAccess.GetBlockEntity(pos) as BlockEntityTransient; betran?.SetPlaceTime(entity.World.Calendar.TotalHours); if (betran?.IsDueTransition() == true) { blockAccess.SetBlock(0, pos); } return(true); } return(false); }
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"); }