/// <summary>Get the height of the lowest ground position we can stand at at the given XZ coordinates, in some given Y range.</summary> /// <returns>The resulting height, or WorldPhysics.MaximumHeight if it did not fit</returns> public static int LowestValidGroundHeight(ref CharacterPhysicsInfo info, WorldPhysics world, int x, int z, int startY, int endY, bool staticOnly = false) { int startX = x + info.startX; int endX = x + info.endX; if (endY > WorldPhysics.MaximumHeight) { endY = WorldPhysics.MaximumHeight; } // Repeatedly test the walkable height upwards, until it returns somewhere we are confirmed to fit int tryingHeight = startY; int resultingHeight; do { resultingHeight = world.GetGroundHeightInXRange(startX, endX, z, tryingHeight, tryingHeight + info.height, info.owner, staticOnly); if (resultingHeight <= startY) { return(startY); } if (resultingHeight == tryingHeight) { break; } tryingHeight = resultingHeight; }while(resultingHeight < endY); if (resultingHeight == tryingHeight && resultingHeight >= startY && resultingHeight < endY) { return(resultingHeight); } return(WorldPhysics.MaximumHeight); // <- Failed }
public static void TryMoveVertical(ref CharacterPhysicsInfo info, WorldPhysics world, int deltaY, ref Position position, bool staticOnly = false) { // // Move on Y axis first... // if (deltaY < 0) { int groundHeight = GroundHeight(ref info, world, position, staticOnly); if (position.Y > groundHeight) // Don't move down further if already below ground { int newY = position.Y + deltaY; if (newY < groundHeight) // Don't go below ground { position.Y = groundHeight; } else { position.Y = newY; } } } else if (deltaY > 0) { int startY = position.Y + info.height; int endY = startY + deltaY; endY = world.GetCeilingHeightInXRange(position.X + info.startX, position.X + info.endX, position.Z, startY, endY, info.owner, staticOnly); position.Y = endY - info.height; } }
/// <summary>Get the height of the topmost ground at a given position. Used for generating navigation data. (Eventually replace with a multi-layer heightmap.)</summary> public static int TopmostGroundHeight(ref CharacterPhysicsInfo info, WorldPhysics world, int x, int z, bool staticOnly = false) { int startX = x + info.startX; int endX = x + info.endX; // Hack: If we slip in just under the ceiling, we can get the result for the next-highest floor // This is a hack because it depends on knowledge of what GetGroundHeightInXRange is doing int minCeiling = WorldPhysics.MaximumHeight + info.height; if (world.levelCeiling != null) { for (int xx = startX; xx < endX; xx++) { int c = world.levelCeiling[xx, z]; if (c == 0) { continue; } if (c < minCeiling) { minCeiling = c; } } } return(world.GetGroundHeightInXRange(startX, endX, z, minCeiling - info.height, minCeiling, info.owner, staticOnly)); }
public static int GroundHeight(ref CharacterPhysicsInfo info, WorldPhysics world, Position position, bool staticOnly = false) { int startX = position.X + info.startX; int endX = position.X + info.endX; return(world.GetGroundHeightInXRange(startX, endX, position.Z, position.Y, position.Y + info.height, info.owner, staticOnly)); }
private static bool MoveStep(ref CharacterPhysicsInfo info, WorldPhysics world, int deltaX, int deltaZ, ref bool groundSnap, ref Position position, bool staticOnly = false) { int groundHeight = GroundHeight(ref info, world, new Position(position.X + deltaX, position.Y, position.Z + deltaZ), staticOnly); // NOTE: This ensures that we cannot get kicked up later on in the method, when we set to groundHeight if (position.Y + info.stairStepMaxHeight < groundHeight) { return(false); // Cannot move up that high } position.X += deltaX; position.Z += deltaZ; if (position.Y < groundHeight) { // TODO: BUG: We need to know if we're going to hit the ceiling by moving upwards here! // Do not go below ground position.Y = groundHeight; } else if (groundSnap) { if (position.Y < groundHeight + info.stairStepMaxHeight) { // Snap to the ground if we are nearby position.Y = groundHeight; } else { // We came "off" the ground, stop trying to snap to it groundSnap = false; } } return(true); }
public static bool TryMoveHorizontalDidHitWall(ref CharacterPhysicsInfo info, WorldPhysics world, int deltaX, int deltaZ, bool groundSnap, ref Position position, bool staticOnly = false) { bool hitWall = false; // // Sweep on XZ plane (slow but safe)... // while (deltaX > 0) { deltaX--; if (!MoveStep(ref info, world, 1, 0, ref groundSnap, ref position, staticOnly)) { hitWall = true; goto doneX; } } while (deltaX < 0) { deltaX++; if (!MoveStep(ref info, world, -1, 0, ref groundSnap, ref position, staticOnly)) { hitWall = true; goto doneX; } } doneX: while (deltaZ > 0) { deltaZ--; if (!MoveStep(ref info, world, 0, 1, ref groundSnap, ref position, staticOnly)) { hitWall = true; goto doneZ; } } while (deltaZ < 0) { deltaZ++; if (!MoveStep(ref info, world, 0, -1, ref groundSnap, ref position, staticOnly)) { hitWall = true; goto doneZ; } } doneZ: return(hitWall); }
/// <summary>Get the height of the level walkable heightmap at this position, if it is flat, otherwise WorldPhysics.MaximumHeight</summary> public static int LevelOnlyFlatGround(ref CharacterPhysicsInfo info, WorldPhysics world, int x, int z) { if (world.levelHeightmap == null) { return(WorldPhysics.MaximumHeight); } int startX = x + info.startX; int endX = x + info.endX; int h = world.levelHeightmap[startX, z]; for (int xx = startX + 1; xx < endX; xx++) { int c = world.levelHeightmap[xx, z]; if (c != h) { return(WorldPhysics.MaximumHeight); } } return(h); }
/// <summary>Determine whether a position is on (or inside) the ground at a given position.</summary> public static bool OnGround(ref CharacterPhysicsInfo info, WorldPhysics world, Position position) { return(position.Y <= GroundHeight(ref info, world, position)); }
public static bool DoLemmingsMotion(ref CharacterPhysicsInfo info, WorldPhysics world, int inputX, int inputZ, int deltaX, int deltaZ, bool groundSnap, bool wallSlide, ref Position position) { Debug.Assert(Math.Abs(inputX) <= 1 && Math.Abs(inputZ) <= 1); bool resultX = true, resultZ = true; // WARNING: Delicate copy-paste code ahead (for each direction: X+, X-, Z+, Z-) while (deltaX > 0) { if (MoveStep(ref info, world, 1, 0, ref groundSnap, ref position)) { deltaX--; } else if (wallSlide) // Move failed, try wall-slide { if (inputX > 0 && inputZ <= 0 && MoveStep(ref info, world, 1, -1, ref groundSnap, ref position)) { deltaX--; if (deltaZ < 0) { deltaZ++; } else if (deltaX > 0) { deltaX--; } } else if (inputX > 0 && inputZ >= 0 && MoveStep(ref info, world, 1, 1, ref groundSnap, ref position)) { deltaX--; if (deltaZ > 0) { deltaZ--; } else if (deltaX > 0) { deltaX--; } } else { resultX = false; goto doneX; // Failed to move on the X axis } } else { resultX = false; goto doneX; // Failed to move on the X axis } } while (deltaX < 0) { if (MoveStep(ref info, world, -1, 0, ref groundSnap, ref position)) { deltaX++; } else if (wallSlide) // Move failed, try wall-slide { if (inputX < 0 && inputZ <= 0 && MoveStep(ref info, world, -1, -1, ref groundSnap, ref position)) { deltaX++; if (deltaZ < 0) { deltaZ++; } else if (deltaX < 0) { deltaX++; } } else if (inputX < 0 && inputZ >= 0 && MoveStep(ref info, world, -1, 1, ref groundSnap, ref position)) { deltaX++; if (deltaZ > 0) { deltaZ--; } else if (deltaX < 0) { deltaX++; } } else { resultX = false; goto doneX; // Failed to move on the X axis } } else { resultX = false; goto doneX; // Failed to move on the X axis } } doneX: while (deltaZ > 0) { if (MoveStep(ref info, world, 0, 1, ref groundSnap, ref position)) { deltaZ--; } else if (wallSlide) // Move failed, try wall-slide { if (inputZ > 0 && inputX <= 0 && MoveStep(ref info, world, -1, 1, ref groundSnap, ref position)) { deltaZ--; if (deltaX < 0) { deltaX++; } else if (deltaZ > 0) { deltaZ--; } } else if (inputZ > 0 && inputX >= 0 && MoveStep(ref info, world, 1, 1, ref groundSnap, ref position)) { deltaZ--; if (deltaX > 0) { deltaX--; } else if (deltaZ > 0) { deltaZ--; } } else { resultZ = false; goto doneZ; // Failed to move on the Z axis } } else { resultZ = false; goto doneZ; // Failed to move on the Z axis } } while (deltaZ < 0) { if (MoveStep(ref info, world, 0, -1, ref groundSnap, ref position)) { deltaZ++; } else if (wallSlide) // Move failed, try wall-slide { if (inputZ < 0 && inputX <= 0 && MoveStep(ref info, world, -1, -1, ref groundSnap, ref position)) { deltaZ++; if (deltaX < 0) { deltaX++; } else if (deltaZ < 0) { deltaZ++; } } else if (inputZ < 0 && inputX >= 0 && MoveStep(ref info, world, 1, -1, ref groundSnap, ref position)) { deltaZ++; if (deltaX > 0) { deltaX--; } else if (deltaZ < 0) { deltaZ++; } } else { resultZ = false; goto doneZ; // Failed to move on the Z axis } } else { resultZ = false; goto doneZ; // Failed to move on the Z axis } } doneZ: return((resultX && inputX != 0) || (resultZ && inputZ != 0) || (inputX == 0 && inputZ == 0)); }
public static void TryMove(ref CharacterPhysicsInfo info, WorldPhysics world, Position delta, bool groundSnap, ref Position position, bool staticOnly = false) { TryMoveVertical(ref info, world, delta.Y, ref position, staticOnly); TryMoveHorizontalDidHitWall(ref info, world, delta.X, delta.Z, groundSnap, ref position, staticOnly); }