mirror of
				https://github.com/luanti-org/luanti.git
				synced 2025-11-04 09:15:29 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			104 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			104 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
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})
 |