2024-03-01 17:12:13 +01:00
|
|
|
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
|
2024-09-04 19:15:39 +02:00
|
|
|
if hit.type == "node" then
|
|
|
|
return hit.under
|
|
|
|
end
|
2024-03-01 17:12:13 +01:00
|
|
|
end
|
2024-09-04 19:15:39 +02:00
|
|
|
return nil
|
2024-03-01 17:12:13 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
local function test_raycast_pointabilities(player, pos1)
|
2024-09-04 19:15:39 +02:00
|
|
|
local pos2 = pos1:offset(0, 0, 1)
|
|
|
|
local pos3 = pos1:offset(0, 0, 2)
|
2024-03-01 17:12:13 +01:00
|
|
|
|
2024-09-04 19:15:39 +02:00
|
|
|
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"})
|
2024-03-01 17:12:13 +01:00
|
|
|
|
2024-09-04 19:15:39 +02:00
|
|
|
local p = nil
|
|
|
|
assert(raycast_with_pointabilities(pos1, pos3, p) == pos3)
|
2024-03-01 17:12:13 +01:00
|
|
|
|
2024-09-04 19:15:39 +02:00
|
|
|
p = core.registered_items["testtools:blocked_pointing_staff"].pointabilities
|
|
|
|
assert(raycast_with_pointabilities(pos1, pos3, p) == nil)
|
2024-03-01 17:12:13 +01:00
|
|
|
|
2024-09-04 19:15:39 +02:00
|
|
|
p = core.registered_items["testtools:ultimate_pointing_staff"].pointabilities
|
|
|
|
assert(raycast_with_pointabilities(pos1, pos3, p) == pos2)
|
2024-03-01 17:12:13 +01:00
|
|
|
|
2024-09-04 19:15:39 +02:00
|
|
|
core.swap_node(pos1, oldnode1)
|
|
|
|
core.swap_node(pos2, oldnode2)
|
|
|
|
core.swap_node(pos3, oldnode3)
|
2024-03-01 17:12:13 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
unittests.register("test_raycast_pointabilities", test_raycast_pointabilities, {map=true})
|
2025-01-05 16:32:09 +01:00
|
|
|
|
|
|
|
local function test_raycast_noskip(_, pos)
|
2025-01-08 10:56:05 +01:00
|
|
|
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
|
|
|
|
|
2025-01-05 16:32:09 +01:00
|
|
|
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
|
2025-01-08 10:56:05 +01:00
|
|
|
ray_start = random_point_in_area(cuboid_minmax(r))
|
2025-01-05 16:32:09 +01:00
|
|
|
until not ray_start:in_area(cuboid_minmax(1.501))
|
|
|
|
-- Pick a random position inside the dirt
|
2025-01-08 10:56:05 +01:00
|
|
|
local ray_end = random_point_in_area(cuboid_minmax(1.499))
|
2025-01-05 16:32:09 +01:00
|
|
|
-- 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
|
|
|
|
|
2025-01-08 10:56:05 +01:00
|
|
|
unittests.register("test_raycast_noskip", test_raycast_noskip, {map = true, random = true})
|