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"); }
public void TickEntityPhysics(EntityPos pos, EntityControls controls, float dt) { FrameProfilerUtil profiler = entity.World.FrameProfiler; profiler.Mark("init"); float dtFac = 60 * dt; // This seems to make creatures clip into the terrain. Le sigh. // Since we currently only really need it for when the creature is dead, let's just use it only there // Also running animations for all nearby living entities is pretty CPU intensive so // the AnimationManager also just ticks if the entity is in view or dead if (!entity.Alive) { AdjustCollisionBoxToAnimation(dtFac); } if (controls.TriesToMove && entity.OnGround && !entity.Swimming) { pos.Motion.Add(windForce); } foreach (EntityLocomotion locomotor in Locomotors) { if (locomotor.Applicable(entity, pos, controls)) { locomotor.Apply(dt, entity, pos, controls); } } profiler.Mark("locomotors"); int knockbackState = entity.Attributes.GetInt("dmgkb"); if (knockbackState > 0) { float acc = entity.Attributes.GetFloat("dmgkbAccum") + dt; entity.Attributes.SetFloat("dmgkbAccum", acc); if (knockbackState == 1) { float str = 1 * 30 * dt * (entity.OnGround ? 1 : 0.5f); pos.Motion.X += entity.WatchedAttributes.GetDouble("kbdirX") * str; pos.Motion.Y += entity.WatchedAttributes.GetDouble("kbdirY") * str; pos.Motion.Z += entity.WatchedAttributes.GetDouble("kbdirZ") * str; entity.Attributes.SetInt("dmgkb", 2); } if (acc > 2 / 30f) { entity.Attributes.SetInt("dmgkb", 0); entity.Attributes.SetFloat("dmgkbAccum", 0); float str = 0.5f * 30 * dt; pos.Motion.X -= entity.WatchedAttributes.GetDouble("kbdirX") * str; pos.Motion.Y -= entity.WatchedAttributes.GetDouble("kbdirY") * str; pos.Motion.Z -= entity.WatchedAttributes.GetDouble("kbdirZ") * str; } } EntityAgent agent = entity as EntityAgent; if (agent?.MountedOn != null) { pos.SetFrom(agent.MountedOn.MountPosition); pos.Motion.X = 0; pos.Motion.Y = 0; pos.Motion.Z = 0; return; } profiler.Mark("knockback-and-mountedcheck"); profiler.Enter("collision"); if (pos.Motion.LengthSq() > 100D) { pos.Motion.X = GameMath.Clamp(pos.Motion.X, -10, 10); pos.Motion.Y = GameMath.Clamp(pos.Motion.Y, -10, 10); pos.Motion.Z = GameMath.Clamp(pos.Motion.Z, -10, 10); } if (!controls.NoClip) { DisplaceWithBlockCollision(pos, controls, dt); } else { pos.X += pos.Motion.X * dt * 60f; pos.Y += pos.Motion.Y * dt * 60f; pos.Z += pos.Motion.Z * dt * 60f; entity.Swimming = false; entity.FeetInLiquid = false; entity.OnGround = false; } profiler.Leave(); // Shake the player violently when falling at high speeds /*if (movedy < -50) * { * pos.X += (rand.NextDouble() - 0.5) / 5 * (-movedy / 50f); * pos.Z += (rand.NextDouble() - 0.5) / 5 * (-movedy / 50f); * } */ //return result; }