public bool RayIntersectsBlockSelectionBox(BlockPos pos, BlockFilter filter) { if (filter?.Invoke(pos, blockSelectionTester.GetBlock(pos)) == false) { return(false); } Cuboidf[] selectionBoxes = blockSelectionTester.GetBlockIntersectionBoxes(pos); if (selectionBoxes == null) { return(false); } bool intersects = false; for (int i = 0; i < selectionBoxes.Length; i++) { tmpCuboidd.Set(selectionBoxes[i]).Translate(pos.X, pos.Y, pos.Z); if (RayIntersectsWithCuboid(tmpCuboidd, ref hitOnBlockFaceTmp, ref hitPositionTmp)) { if (intersects && hitPosition.SquareDistanceTo(ray.origin) <= hitPositionTmp.SquareDistanceTo(ray.origin)) { continue; } hitOnSelectionBox = i; intersects = true; hitOnBlockFace = hitOnBlockFaceTmp; hitPosition.Set(hitPositionTmp); } } return(intersects); }
public bool RayIntersectsBlockSelectionBox(BlockPos pos, BlockFilter filter) { if (filter?.Invoke(pos, blockSelectionTester.GetBlock(pos)) == false) { return(false); } Cuboidf[] selectionBoxes = blockSelectionTester.GetBlockIntersectionBoxes(pos); if (selectionBoxes == null) { return(false); } bool intersects = false; bool wasDecor = false; for (int i = 0; i < selectionBoxes.Length; i++) { tmpCuboidd.Set(selectionBoxes[i]).Translate(pos.X, pos.Y, pos.Z); if (RayIntersectsWithCuboid(tmpCuboidd, ref hitOnBlockFaceTmp, ref hitPositionTmp)) { bool isDecor = selectionBoxes[i] is DecorSelectionBox; if (intersects && (!wasDecor || isDecor) && hitPosition.SquareDistanceTo(ray.origin) <= hitPositionTmp.SquareDistanceTo(ray.origin)) { continue; } hitOnSelectionBox = i; intersects = true; wasDecor = isDecor; hitOnBlockFace = hitOnBlockFaceTmp; hitPosition.Set(hitPositionTmp); } } if (intersects && selectionBoxes[hitOnSelectionBox] is DecorSelectionBox dsb) { Vec3i posAdjust = dsb.PosAdjust; if (posAdjust != null) { pos.Add(posAdjust); } } return(intersects); }
/// <summary> /// Tests given cuboidf collides with the terrain. By default also checks if the cuboid is merely touching the terrain, set alsoCheckTouch to disable that. /// </summary> /// <param name="blockAccessor"></param> /// <param name="entityBoxRel"></param> /// <param name="pos"></param> /// <param name="alsoCheckTouch"></param> /// <returns></returns> public bool GetCollidingCollisionBox(IBlockAccessor blockAccessor, Cuboidf entityBoxRel, Vec3d pos, ref Cuboidd intoCubuid, bool alsoCheckTouch = true) { BlockPos blockPos = new BlockPos(); Vec3d blockPosVec = new Vec3d(); Cuboidd entityBox = entityBoxRel.ToDouble().Translate(pos); int minX = (int)(entityBoxRel.X1 + pos.X); int minY = (int)(entityBoxRel.Y1 + pos.Y - 1); // -1 for the extra high collision box of fences int minZ = (int)(entityBoxRel.Z1 + pos.Z); int maxX = (int)Math.Ceiling(entityBoxRel.X2 + pos.X); int maxY = (int)Math.Ceiling(entityBoxRel.Y2 + pos.Y); int maxZ = (int)Math.Ceiling(entityBoxRel.Z2 + pos.Z); for (int y = minY; y <= maxY; y++) { for (int x = minX; x <= maxX; x++) { for (int z = minZ; z <= maxZ; z++) { Block block = blockAccessor.GetBlock(x, y, z); blockPos.Set(x, y, z); blockPosVec.Set(x, y, z); Cuboidf[] collisionBoxes = block.GetCollisionBoxes(blockAccessor, blockPos); for (int i = 0; collisionBoxes != null && i < collisionBoxes.Length; i++) { Cuboidf collBox = collisionBoxes[i]; if (collBox == null) { continue; } bool colliding = alsoCheckTouch ? entityBox.IntersectsOrTouches(collBox, blockPosVec) : entityBox.Intersects(collBox, blockPosVec); if (colliding) { intoCubuid.Set(collBox).Translate(blockPos); return(true); } } } } } return(false); }
public Block GetCollidingBlock(IBlockAccessor blockAccessor, Cuboidf entityBoxRel, Vec3d pos, bool alsoCheckTouch = true) { Cuboidd entityBox = tmpBox.Set(entityBoxRel).Translate(pos); int minX = (int)(entityBoxRel.X1 + pos.X); int minY = (int)(entityBoxRel.Y1 + pos.Y - 1); // -1 for the extra high collision box of fences int minZ = (int)(entityBoxRel.Z1 + pos.Z); int maxX = (int)(entityBoxRel.X2 + pos.X); int maxY = (int)(entityBoxRel.Y2 + pos.Y); int maxZ = (int)(entityBoxRel.Z2 + pos.Z); for (int y = minY; y <= maxY; y++) { for (int x = minX; x <= maxX; x++) { for (int z = minZ; z <= maxZ; z++) { Block block = blockAccessor.GetBlock(x, y, z); blockPos.Set(x, y, z); blockPosVec.Set(x, y, z); Cuboidf[] collisionBoxes = block.GetCollisionBoxes(blockAccessor, blockPos); for (int i = 0; collisionBoxes != null && i < collisionBoxes.Length; i++) { Cuboidf collBox = collisionBoxes[i]; if (collBox == null) { continue; } bool colliding = alsoCheckTouch ? entityBox.IntersectsOrTouches(collBox, blockPosVec) : entityBox.Intersects(collBox, blockPosVec); if (colliding) { return(block); } } } } } return(null); }
public void ApplyTerrainCollision(Entity entity, EntityPos entitypos, float dtFac, ref Vec3d outposition, float stepHeight = 1) { IWorldAccessor worldaccess = entity.World; Vec3d pos = entitypos.XYZ; // Full stop when already inside a collisionbox, but allow a small margin of error // Disabled. Causes too many issues /*(if (blockWhenInside && IsColliding(worldaccess.BlockAccessor, entity.CollisionBox, pos, false)) * { * entity.CollidedVertically = true; * entity.CollidedVertically = true; * outposition.Set(pos); * return; * }*/ // entitypos.Motion.X = GameMath.Clamp(entitypos.Motion.X, -1, 1); // entitypos.Motion.Y = GameMath.Clamp(entitypos.Motion.Y, -1, 1); // entitypos.Motion.Z = GameMath.Clamp(entitypos.Motion.Z, -1, 1); tmpPositionVec.Set(pos); entityBox.Set( entity.CollisionBox.X1 + tmpPositionVec.X, entity.CollisionBox.Y1 + tmpPositionVec.Y, entity.CollisionBox.Z1 + tmpPositionVec.Z, entity.CollisionBox.X2 + tmpPositionVec.X, entity.CollisionBox.Y2 + tmpPositionVec.Y, entity.CollisionBox.Z2 + tmpPositionVec.Z ); CollisionBoxList.Clear(); outposition.Set(tmpPositionVec.X + entitypos.Motion.X * dtFac, tmpPositionVec.Y + entitypos.Motion.Y * dtFac, tmpPositionVec.Z + entitypos.Motion.Z * dtFac); minPos.Set( (int)(entityBox.X1 + Math.Min(0, entitypos.Motion.X * dtFac)), (int)(entityBox.Y1 + Math.Min(0, entitypos.Motion.Y * dtFac) - 1), // -1 for the extra high collision box of fences (int)(entityBox.Z1 + Math.Min(0, entitypos.Motion.Z * dtFac)) ); double y2 = Math.Max(entityBox.Y1 + stepHeight, entityBox.Y2); maxPos.Set( (int)(entityBox.X2 + Math.Max(0, entitypos.Motion.X * dtFac)), (int)(y2 + Math.Max(0, entitypos.Motion.Y * dtFac)), (int)(entityBox.Z2 + Math.Max(0, entitypos.Motion.Z * dtFac)) ); worldaccess.BlockAccessor.WalkBlocks(minPos, maxPos, (block, bpos) => { Cuboidf[] collisionBoxes = block.GetCollisionBoxes(worldaccess.BlockAccessor, bpos); for (int i = 0; collisionBoxes != null && i < collisionBoxes.Length; i++) { CollisionBoxList.Add(collisionBoxes[i], bpos, block); } }, true); tmpPosDelta.Set(entitypos.Motion.X * dtFac, entitypos.Motion.Y * dtFac, entitypos.Motion.Z * dtFac); // Y - Collision (Vertical) bool collided = false; for (int i = 0; i < CollisionBoxList.Count; i++) { tmpPosDelta.Y = CollisionBoxList.cuboids[i].pushOutY(entityBox, tmpPosDelta.Y, ref pushDirection); if (pushDirection == EnumPushDirection.None) { continue; } collided = true; CollisionBoxList.blocks[i].OnEntityCollide( worldaccess, entity, CollisionBoxList.positions[i], pushDirection == EnumPushDirection.Negative ? BlockFacing.UP : BlockFacing.DOWN, tmpPosDelta, !entity.CollidedVertically ); } entity.CollidedVertically = collided; entityBox.Translate(0, tmpPosDelta.Y, 0); var horizontalBlocked = false; var entityBoxMovedXZ = entityBox.OffsetCopy(tmpPosDelta.X, 0, tmpPosDelta.Z); foreach (var cuboid in CollisionBoxList.cuboids) { if (cuboid.Intersects(entityBoxMovedXZ)) { horizontalBlocked = true; break; } } // No collisions for the entity found when testing horizontally, so skip this. // This allows entities to move around corners without falling down on a certain axis. collided = false; if (horizontalBlocked) { // X - Collision (Horizontal) for (int i = 0; i < CollisionBoxList.Count; i++) { tmpPosDelta.X = CollisionBoxList.cuboids[i].pushOutX(entityBox, tmpPosDelta.X, ref pushDirection); if (pushDirection == EnumPushDirection.None) { continue; } collided = true; CollisionBoxList.blocks[i].OnEntityCollide( worldaccess, entity, CollisionBoxList.positions[i], pushDirection == EnumPushDirection.Negative ? BlockFacing.EAST : BlockFacing.WEST, tmpPosDelta, !entity.CollidedHorizontally ); } entityBox.Translate(tmpPosDelta.X, 0, 0); // Z - Collision (Horizontal) for (int i = 0; i < CollisionBoxList.Count; i++) { tmpPosDelta.Z = CollisionBoxList.cuboids[i].pushOutZ(entityBox, tmpPosDelta.Z, ref pushDirection); if (pushDirection == EnumPushDirection.None) { continue; } collided = true; CollisionBoxList.blocks[i].OnEntityCollide( worldaccess, entity, CollisionBoxList.positions[i], pushDirection == EnumPushDirection.Negative ? BlockFacing.SOUTH : BlockFacing.NORTH, tmpPosDelta, !entity.CollidedHorizontally ); } } entity.CollidedHorizontally = collided; //fix for player on ladder clipping into block above issue (caused by the .CollisionBox not always having height precisely 1.85) if (entity.CollidedVertically && tmpPosDelta.Y > 0) { tmpPosDelta.Y -= entity.LadderFixDelta; } outposition.Set(tmpPositionVec.X + tmpPosDelta.X, tmpPositionVec.Y + tmpPosDelta.Y, tmpPositionVec.Z + tmpPosDelta.Z); }