// Aquires DataLock (read) private bool RayCast(Vec3 from, Cell from_cell, Vec3 to, MovementFlag flags, bool test_2d, bool ignore_movement_cost, ref HashSet <Cell> ignored_cells) { using (new ReadLock(DataLock)) { if (to.IsEmpty) { return(false); } if (from_cell == null && !GetCellContaining(from, out from_cell, ignored_cells, false, flags, test_2d, 2)) { return(false); } if (test_2d ? from_cell.Contains2D(to) : from_cell.Contains(to, 2)) { return(true); } ignored_cells.Add(from_cell); Vec3 ray_dir = to - from; if (test_2d) { ray_dir.Normalize2D(); } else { ray_dir.Normalize(); } Vec3 ray_origin = new Vec3(from); // raycast through neighbours foreach (Cell.Neighbour neighbour in from_cell.Neighbours) { if (neighbour.cell.Disabled || (neighbour.connection_flags & flags) != flags || (!ignore_movement_cost && neighbour.cell.MovementCostMult > from_cell.MovementCostMult)) { continue; } Cell neighbour_cell = neighbour.cell; if (ignored_cells.Contains(neighbour_cell)) { continue; } Vec3 intersection = null; bool ray_test_result = test_2d ? neighbour_cell.AABB.RayTest2D(ray_origin, ray_dir, out intersection) : neighbour_cell.AABB.RayTest(ray_origin, ray_dir, out intersection); // ray intersects on connection plane if (ray_test_result) { AABB shared_aabb = test_2d ? from_cell.AABB.Intersect2D(neighbour_cell.AABB, true) : from_cell.AABB.Intersect(neighbour_cell.AABB, true); if (shared_aabb != null) { bool accepted = test_2d ? shared_aabb.Contains2D(intersection) : shared_aabb.Contains(intersection); if (accepted && RayCast(intersection, neighbour_cell, to, flags, test_2d, ignore_movement_cost, ref ignored_cells)) { return(true); } } } } return(false); } }