// TODO: There's a lot of code replication here, perhaps it can be consolidated /// <summary> /// Performs terrain collision tests and adjusts the X-axis velocity accordingly /// </summary> /// <returns>True if the entity collides with the terrain</returns> private bool AdjustVelocityX(IAABBEntity entity, World world, out Vector3 collision, out Vector3 collisionDirection) { collision = Vector3.Zero; collisionDirection = Vector3.Zero; if (entity.Velocity.X == 0) return false; // Do some enviornment guessing to improve speed int minY = (int)entity.Position.Y - (entity.Position.Y < 0 ? 1 : 0); int maxY = (int)(entity.Position.Y + entity.Size.Width) - (entity.Position.Y < 0 ? 1 : 0); int minZ = (int)entity.Position.Z - (entity.Position.Z < 0 ? 1 : 0); int maxZ = (int)(entity.Position.Z + entity.Size.Depth) - (entity.Position.Z < 0 ? 1 : 0); int minX, maxX; // Expand bounding box to include area to be tested if (entity.Velocity.X < 0) { TempBoundingBox = new BoundingBox( new Vector3(entity.BoundingBox.Min.X + entity.Velocity.X, entity.BoundingBox.Min.Y, entity.BoundingBox.Min.Z) - (entity.Size / 2), new Vector3(entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Z) - (entity.Size / 2)); maxX = (int)(TempBoundingBox.Max.X); minX = (int)(TempBoundingBox.Min.X + entity.Velocity.X); } else { TempBoundingBox = new BoundingBox(entity.BoundingBox.Min - (entity.Size / 2), new Vector3( entity.BoundingBox.Max.X + entity.Velocity.X, entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Z) - (entity.Size / 2)); minX = (int)(entity.BoundingBox.Min.X); maxX = (int)(entity.BoundingBox.Max.X + entity.Velocity.X); } // Do terrain checks double? collisionPoint = null; BoundingBox blockBox; for (int x = minX; x <= maxX; x++) { for (int y = minY; y <= maxY; y++) { for (int z = minZ; z <= maxZ; z++) { var position = new Vector3(x, y, z); var blockId = world.GetBlockId(position); var boundingBox = Block.GetBoundingBox(blockId); if (boundingBox == null) continue; blockBox = new BoundingBox(boundingBox.Value.Min + position, boundingBox.Value.Max + position); if (TempBoundingBox.Intersects(blockBox)) { if (entity.Velocity.X < 0) { if (!collisionPoint.HasValue) collisionPoint = blockBox.Max.X; else if (collisionPoint.Value < blockBox.Max.X) collisionPoint = blockBox.Max.X; } else { if (!collisionPoint.HasValue) collisionPoint = blockBox.Min.X; else if (collisionPoint.Value > blockBox.Min.X) collisionPoint = blockBox.Min.X; } collision = position; } } } } if (collisionPoint != null) { if (entity.Velocity.X < 0) { entity.Velocity = new Vector3( entity.Velocity.X - (TempBoundingBox.Min.X - collisionPoint.Value), entity.Velocity.Y, entity.Velocity.Z); collisionDirection = Vector3.Left; } else if (entity.Velocity.X > 0) { entity.Velocity = new Vector3( entity.Velocity.X - (TempBoundingBox.Max.X - collisionPoint.Value), entity.Velocity.Y, entity.Velocity.Z); collisionDirection = Vector3.Right; } return true; } return false; }
/// <summary> /// Performs terrain collision tests and adjusts the Y-axis velocity accordingly /// </summary> /// <returns>True if the entity collides with the terrain</returns> private bool AdjustVelocityY(IAABBEntity entity, World world, out Vector3 collision, out Vector3 collisionDirection) { collision = Vector3.Zero; collisionDirection = Vector3.Zero; if (entity.Velocity.Y == 0) return false; // Do some enviornment guessing to improve speed int minX = (int)entity.Position.X - (entity.Position.X < 0 ? 1 : 0); int maxX = (int)(entity.Position.X + entity.Size.Width) - (entity.Position.X < 0 ? 1 : 0); int minZ = (int)entity.Position.Z - (entity.Position.Z < 0 ? 1 : 0); int maxZ = (int)(entity.Position.Z + entity.Size.Depth) - (entity.Position.Z < 0 ? 1 : 0); int minY, maxY; // Expand bounding box to include area to be tested if (entity.Velocity.Y < 0) { TempBoundingBox = new BoundingBox( new Vector3(entity.BoundingBox.Min.X, entity.BoundingBox.Min.Y + entity.Velocity.Y, entity.BoundingBox.Min.Z) - (entity.Size / 2), new Vector3(entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Z) - (entity.Size / 2)); maxY = (int)(TempBoundingBox.Max.Y); minY = (int)(TempBoundingBox.Min.Y + entity.Velocity.Y); } else { TempBoundingBox = new BoundingBox(entity.BoundingBox.Min - (entity.Size / 2), new Vector3( entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y + entity.Velocity.Y, entity.BoundingBox.Max.Z) - (entity.Size / 2)); minY = (int)(entity.BoundingBox.Min.Y); maxY = (int)(entity.BoundingBox.Max.Y + entity.Velocity.Y); } // Clamp Y into map boundaries if (minY < 0) minY = 0; if (minY >= World.Height) minY = World.Height - 1; if (maxY < 0) maxY = 0; if (maxY >= World.Height) maxY = World.Height - 1; // Do terrain checks double? collisionPoint = null; BoundingBox blockBox; for (int x = minX; x <= maxX; x++) { for (int y = minY; y <= maxY; y++) { for (int z = minZ; z <= maxZ; z++) { var position = new Vector3(x, y, z); var blockId = world.GetBlockId(position); var boundingBox = Block.GetBoundingBox(blockId); if (boundingBox == null) continue; blockBox = new BoundingBox(boundingBox.Value.Min + position, boundingBox.Value.Max + position); if (TempBoundingBox.Intersects(blockBox)) { if (entity.Velocity.Y < 0) { if (!collisionPoint.HasValue) collisionPoint = blockBox.Max.Y; else if (collisionPoint.Value < blockBox.Max.Y) collisionPoint = blockBox.Max.Y; } else { if (!collisionPoint.HasValue) collisionPoint = blockBox.Min.Y; else if (collisionPoint.Value > blockBox.Min.Y) collisionPoint = blockBox.Min.Y; } collision = position; } } } } if (collisionPoint != null) { if (entity.Velocity.Y < 0) { // TODO: Do block event //var block = world.GetBlock(collision); //block.OnBlockWalkedOn(world, collision, this); entity.Velocity = new Vector3(entity.Velocity.X, entity.Velocity.Y + (collisionPoint.Value - TempBoundingBox.Min.Y), entity.Velocity.Z); collisionDirection = Vector3.Down; } else if (entity.Velocity.Y > 0) { entity.Velocity = new Vector3(entity.Velocity.X, entity.Velocity.Y - (TempBoundingBox.Max.Y - collisionPoint.Value), entity.Velocity.Z); collisionDirection = Vector3.Up; } return true; } return false; }
internal static void DefaultBlockUpdateHandler(BlockDescriptor block, World world, Coordinates3D updatedBlock) { var logic = GetLogicDescriptor(block); var support = logic.GetSupportDirection(block, world, updatedBlock); var supportingBlock = updatedBlock; switch (support) { case SupportDirection.Down: supportingBlock = updatedBlock + Coordinates3D.Down; break; case SupportDirection.Up: supportingBlock = updatedBlock + Coordinates3D.Up; break; case SupportDirection.East: supportingBlock = updatedBlock + Coordinates3D.East; break; case SupportDirection.West: supportingBlock = updatedBlock + Coordinates3D.West; break; case SupportDirection.North: supportingBlock = updatedBlock + Coordinates3D.North; break; case SupportDirection.South: supportingBlock = updatedBlock + Coordinates3D.South; break; default: return; } if (!World.IsValidPosition(supportingBlock)) return; if (world.GetBlockId(supportingBlock) == 0) // TODO: Air isn't the only thing that can't support some blocks OnBlockMined(block, world, updatedBlock, null); // TODO: Consider using a seperate delegate for blocks destroyed through non-player actions }
public static void OnItemUsedOnBlock(ItemDescriptor item, World world, Coordinates3D clickedBlock, Coordinates3D clickedSide, Coordinates3D cursorPosition) { if (world.GetBlockId(clickedBlock + clickedSide) == 0) world.SetBlockId(clickedBlock + clickedSide, CakeBlock.BlockId); }
internal static void DefaultBlockPlacedHandler(BlockDescriptor block, World world, Coordinates3D clickedBlock, Coordinates3D clickedSide, Coordinates3D cursorPosition) { if (world.GetBlockId(clickedBlock + clickedSide) == 0) // TODO: There are more situations than just air when a block can be overwritten { world.SetBlockId(clickedBlock + clickedSide, block.Id); world.SetMetadata(clickedBlock + clickedSide, block.Metadata); } }