123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 |
- local function raycast_with_pointabilities(start_pos, end_pos, pointabilities)
- local ray = core.raycast(start_pos, end_pos, nil, nil, pointabilities)
- for hit in ray do
- if hit.type == "node" then
- return hit.under
- end
- end
- return nil
- end
- local function test_raycast_pointabilities(player, pos1)
- local pos2 = pos1:offset(0, 0, 1)
- local pos3 = pos1:offset(0, 0, 2)
- local oldnode1 = core.get_node(pos1)
- local oldnode2 = core.get_node(pos2)
- local oldnode3 = core.get_node(pos3)
- core.swap_node(pos1, {name = "air"})
- core.swap_node(pos2, {name = "testnodes:not_pointable"})
- core.swap_node(pos3, {name = "testnodes:pointable"})
- local p = nil
- assert(raycast_with_pointabilities(pos1, pos3, p) == pos3)
- p = core.registered_items["testtools:blocked_pointing_staff"].pointabilities
- assert(raycast_with_pointabilities(pos1, pos3, p) == nil)
- p = core.registered_items["testtools:ultimate_pointing_staff"].pointabilities
- assert(raycast_with_pointabilities(pos1, pos3, p) == pos2)
- core.swap_node(pos1, oldnode1)
- core.swap_node(pos2, oldnode2)
- core.swap_node(pos3, oldnode3)
- end
- unittests.register("test_raycast_pointabilities", test_raycast_pointabilities, {map=true})
- local function test_raycast_noskip(_, pos)
- local function random_point_in_area(min, max)
- local extents = max - min
- local v = extents:multiply(vector.new(
- math.random(),
- math.random(),
- math.random()
- ))
- return min + v
- end
- -- FIXME a variation of this unit test fails in an edge case.
- -- This is because Luanti does not handle perfectly diagonal raycasts correctly:
- -- Perfect diagonals collide with neither "outside" face and may thus "pierce" nodes.
- -- Enable the following code to reproduce:
- if 0 == 1 then
- pos = vector.new(6, 32, -3)
- math.randomseed(1596190898)
- function random_point_in_area(min, max)
- return min:combine(max, math.random)
- end
- end
- local function cuboid_minmax(extent)
- return pos:offset(-extent, -extent, -extent),
- pos:offset(extent, extent, extent)
- end
- -- Carve out a 3x3x3 dirt cuboid in a larger air cuboid
- local r = 8
- local min, max = cuboid_minmax(r + 1)
- local vm = core.get_voxel_manip(min, max)
- local old_data = vm:get_data()
- local data = vm:get_data()
- local emin, emax = vm:get_emerged_area()
- local va = VoxelArea:new({MinEdge = emin, MaxEdge = emax})
- for index in va:iterp(min, max) do
- data[index] = core.CONTENT_AIR
- end
- for index in va:iterp(cuboid_minmax(1)) do
- data[index] = core.get_content_id("basenodes:dirt")
- end
- vm:set_data(data)
- vm:write_to_map()
- -- Raycast many times from outside the cuboid
- for _ = 1, 100 do
- local ray_start
- repeat
- ray_start = random_point_in_area(cuboid_minmax(r))
- until not ray_start:in_area(cuboid_minmax(1.501))
- -- Pick a random position inside the dirt
- local ray_end = random_point_in_area(cuboid_minmax(1.499))
- -- The first pointed thing should have only air "in front" of it,
- -- or a dirt node got falsely skipped.
- local pt = core.raycast(ray_start, ray_end, false, false):next()
- if pt then
- assert(core.get_node(pt.above).name == "air")
- end
- end
- vm:set_data(old_data)
- vm:write_to_map()
- end
- unittests.register("test_raycast_noskip", test_raycast_noskip, {map = true, random = true})
|