2021-12-21 08:45:26 -08:00
|
|
|
-- Max. number of steps in the laser travel algorithm
|
2021-12-25 19:15:32 -08:00
|
|
|
local MAX_LASER_ITERATIONS = 10000
|
2021-12-21 08:45:26 -08:00
|
|
|
|
|
|
|
-- Add a laser node to pos with the direction `dir`.
|
|
|
|
-- Dir is a direction vector, and only one direction must be set
|
|
|
|
function lzr_laser.add_laser(pos, dir)
|
2021-12-27 19:31:46 -08:00
|
|
|
-- Check if laser is still within the playfield
|
|
|
|
if pos.x < lzr_globals.PLAYFIELD_START.x or pos.x > lzr_globals.PLAYFIELD_END.x or
|
|
|
|
pos.y < lzr_globals.PLAYFIELD_START.y or pos.y > lzr_globals.PLAYFIELD_END.y or
|
|
|
|
pos.z < lzr_globals.PLAYFIELD_START.z or pos.z > lzr_globals.PLAYFIELD_END.z then
|
|
|
|
return false
|
|
|
|
end
|
2021-12-21 08:45:26 -08:00
|
|
|
local node = minetest.get_node(pos)
|
|
|
|
-- Laser through air
|
|
|
|
if node.name == "air" then
|
|
|
|
local dirs = lzr_laser.vector_to_dirs(dir)
|
|
|
|
local dirstring = lzr_laser.dirs_to_dirstring(dirs)
|
|
|
|
minetest.set_node(pos, {name="lzr_laser:laser_"..dirstring})
|
2021-12-21 13:00:51 -08:00
|
|
|
pos = vector.add(pos, dir)
|
2021-12-27 07:36:31 -08:00
|
|
|
return {{pos, dir}}
|
2021-12-21 08:45:26 -08:00
|
|
|
-- Laser through laser (laser intersection)
|
|
|
|
elseif minetest.get_item_group(node.name, "laser") > 0 then
|
|
|
|
local dirnum = minetest.get_item_group(node.name, "laser")
|
2021-12-27 07:55:04 -08:00
|
|
|
local dirstring_old = lzr_laser.dec2bin(dirnum, 3)
|
2021-12-21 08:45:26 -08:00
|
|
|
|
|
|
|
local dirs_new = lzr_laser.vector_to_dirs(dir)
|
|
|
|
local dirstring_new = lzr_laser.dirs_to_dirstring(dirs_new)
|
|
|
|
|
|
|
|
local place_dirstring = lzr_laser.bitwise_or(dirstring_old, dirstring_new)
|
|
|
|
minetest.set_node(pos, {name="lzr_laser:laser_"..place_dirstring})
|
2021-12-21 13:00:51 -08:00
|
|
|
pos = vector.add(pos, dir)
|
2021-12-27 07:36:31 -08:00
|
|
|
return {{pos, dir}}
|
2021-12-21 13:00:51 -08:00
|
|
|
-- Mirror laser
|
|
|
|
elseif minetest.get_item_group(node.name, "mirror") > 0 then
|
|
|
|
local mirror_dir = lzr_laser.get_mirrored_laser_dir(pos, dir)
|
|
|
|
if mirror_dir then
|
2021-12-27 19:11:20 -08:00
|
|
|
local def = minetest.registered_nodes[node.name]
|
|
|
|
local active = def._lzr_active
|
2021-12-29 06:24:19 -08:00
|
|
|
if not active then
|
|
|
|
return false
|
|
|
|
end
|
2021-12-21 13:00:51 -08:00
|
|
|
-- Activate mirror node
|
2021-12-27 19:11:20 -08:00
|
|
|
minetest.set_node(pos, {name=def._lzr_active, param2 = node.param2})
|
2021-12-21 13:00:51 -08:00
|
|
|
-- Set new pos and dir
|
|
|
|
pos = vector.add(pos, mirror_dir)
|
|
|
|
dir = mirror_dir
|
2021-12-27 07:36:31 -08:00
|
|
|
return {{pos, dir}}
|
|
|
|
else
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
-- Mirror and split laser
|
|
|
|
elseif minetest.get_item_group(node.name, "transmissive_mirror") > 0 then
|
|
|
|
local mirror_dir = lzr_laser.get_mirrored_laser_dir(pos, dir)
|
|
|
|
if mirror_dir then
|
2021-12-27 19:11:20 -08:00
|
|
|
local def = minetest.registered_nodes[node.name]
|
|
|
|
local active = def._lzr_active
|
2021-12-29 06:24:19 -08:00
|
|
|
if not active then
|
|
|
|
return false
|
|
|
|
end
|
2021-12-27 07:36:31 -08:00
|
|
|
-- Activate mirror node
|
2021-12-27 19:11:20 -08:00
|
|
|
minetest.set_node(pos, {name=active, param2 = node.param2})
|
2021-12-27 07:36:31 -08:00
|
|
|
-- Set new pos and dir
|
|
|
|
local pos_straight = vector.add(pos, dir)
|
|
|
|
local dir_straight = dir
|
|
|
|
local pos_mirrored = vector.add(pos, mirror_dir)
|
|
|
|
local dir_mirrored = mirror_dir
|
|
|
|
return {{pos_straight, dir_straight}, {pos_mirrored, dir_mirrored}}
|
2021-12-21 13:00:51 -08:00
|
|
|
else
|
|
|
|
return false
|
|
|
|
end
|
2021-12-29 06:24:19 -08:00
|
|
|
-- Spread laser to all directions
|
|
|
|
elseif minetest.get_item_group(node.name, "crystal") > 0 then
|
|
|
|
local def = minetest.registered_nodes[node.name]
|
|
|
|
local active = def._lzr_active
|
|
|
|
if not active then
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
-- Activate node
|
|
|
|
minetest.set_node(pos, {name=active, param2 = node.param2})
|
|
|
|
-- Set dirs
|
|
|
|
local dirs = {
|
|
|
|
{ x = 0, y = -1, z = 0 },
|
|
|
|
{ x = 0, y = 1, z = 0 },
|
|
|
|
{ x = -1, y = 0, z = 0 },
|
|
|
|
{ x = 1, y = 0, z = 0 },
|
|
|
|
{ x = 0, y = 0, z = -1 },
|
|
|
|
{ x = 0, y = 0, z = 1 },
|
|
|
|
}
|
|
|
|
for d=1, #dirs do
|
|
|
|
if vector.equals(dirs[d], dirs) then
|
|
|
|
table.remove(dirs, d)
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
local output = {}
|
|
|
|
for d=1, #dirs do
|
|
|
|
table.insert(output, { vector.add(pos, dirs[d]), dirs[d] })
|
|
|
|
end
|
|
|
|
return output
|
2021-12-21 14:13:14 -08:00
|
|
|
-- Detector
|
|
|
|
elseif minetest.get_item_group(node.name, "detector") > 0 then
|
|
|
|
local detected = lzr_laser.check_detector(pos, dir)
|
|
|
|
if detected then
|
2021-12-27 19:11:20 -08:00
|
|
|
local def = minetest.registered_nodes[node.name]
|
|
|
|
local active = def._lzr_active
|
2021-12-21 14:13:14 -08:00
|
|
|
-- Activate node
|
2021-12-27 19:11:20 -08:00
|
|
|
minetest.set_node(pos, {name=active, param2 = node.param2})
|
2021-12-21 15:35:16 -08:00
|
|
|
local done = lzr_laser.check_detectors_in_area(lzr_globals.PLAYFIELD_START, lzr_globals.PLAYFIELD_END)
|
|
|
|
if done then
|
|
|
|
lzr_levels.next_level()
|
|
|
|
end
|
2021-12-21 14:13:14 -08:00
|
|
|
end
|
2021-12-21 14:18:11 -08:00
|
|
|
-- Laser ends here
|
|
|
|
return false
|
2021-12-21 08:45:26 -08:00
|
|
|
-- Anything else: fail
|
|
|
|
else
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function lzr_laser.emit_laser(pos)
|
|
|
|
local node = minetest.get_node(pos)
|
2021-12-21 11:23:15 -08:00
|
|
|
if minetest.get_item_group(node.name, "emitter") == 0 then
|
|
|
|
minetest.log("error", "[lzr_laser] lzr_laser.emit_laser was called at invalid pos!")
|
2021-12-21 08:45:26 -08:00
|
|
|
return false
|
|
|
|
end
|
|
|
|
local dir = minetest.facedir_to_dir(node.param2)
|
2021-12-21 11:23:15 -08:00
|
|
|
dir = vector.multiply(dir, -1)
|
2021-12-21 08:45:26 -08:00
|
|
|
local i_pos = vector.add(pos, dir)
|
2021-12-21 11:23:15 -08:00
|
|
|
lzr_laser.travel_laser(i_pos, dir)
|
2021-12-21 08:45:26 -08:00
|
|
|
end
|
|
|
|
|
|
|
|
function lzr_laser.travel_laser(pos, dir)
|
2021-12-21 11:23:15 -08:00
|
|
|
local i_pos = table.copy(pos)
|
2021-12-21 08:45:26 -08:00
|
|
|
local cond = true
|
2021-12-27 07:36:31 -08:00
|
|
|
local next_lasers = {{i_pos, dir}}
|
2021-12-21 08:45:26 -08:00
|
|
|
local i = 0
|
|
|
|
while cond do
|
|
|
|
i = i + 1
|
|
|
|
-- Halt execution for very long loops to prevent freezing the game
|
|
|
|
if i > MAX_LASER_ITERATIONS then
|
|
|
|
minetest.log("error", "[lzr_laser] lzr_laser.travel_laser aborted (too many iterations!)")
|
|
|
|
break
|
|
|
|
end
|
2021-12-27 07:36:31 -08:00
|
|
|
local next_laser = next_lasers[1]
|
|
|
|
local add_laser_result = lzr_laser.add_laser(next_laser[1], next_laser[2])
|
|
|
|
table.remove(next_lasers, 1)
|
|
|
|
if add_laser_result ~= false then
|
|
|
|
for a=1, #add_laser_result do
|
|
|
|
table.insert(next_lasers, add_laser_result[a])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if #next_lasers == 0 then
|
|
|
|
break
|
2021-12-21 13:00:51 -08:00
|
|
|
end
|
2021-12-21 08:45:26 -08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Remove all lasers in area and disable all laser blocks
|
2021-12-21 13:32:35 -08:00
|
|
|
function lzr_laser.clear_lasers_in_area(pos1, pos2, ignore_emitters)
|
2021-12-21 13:00:51 -08:00
|
|
|
-- Remove lasers
|
2021-12-21 08:45:26 -08:00
|
|
|
local lasers = minetest.find_nodes_in_area(pos1, pos2, {"group:laser"})
|
|
|
|
minetest.bulk_set_node(lasers, {name="air"})
|
2021-12-21 13:00:51 -08:00
|
|
|
|
|
|
|
-- Disable laser blocks (mirror, etc.)
|
2021-12-21 08:45:26 -08:00
|
|
|
local laser_blocks = minetest.find_nodes_in_area(pos1, pos2, {"group:laser_block"})
|
|
|
|
for b=1, #laser_blocks do
|
2021-12-21 13:00:51 -08:00
|
|
|
local block_pos = laser_blocks[b]
|
|
|
|
local block = minetest.get_node(block_pos)
|
2021-12-21 08:45:26 -08:00
|
|
|
local def = minetest.registered_nodes[block.name]
|
2021-12-21 13:32:35 -08:00
|
|
|
local is_ignored_emitter = false
|
|
|
|
if ignore_emitters ~= true then
|
|
|
|
is_ignored_emitter = minetest.get_item_group(block.name, "emitter") > 0
|
|
|
|
end
|
|
|
|
if def and not is_ignored_emitter then
|
2021-12-21 08:45:26 -08:00
|
|
|
local inactive = def._lzr_inactive
|
|
|
|
if inactive then
|
2021-12-21 13:00:51 -08:00
|
|
|
minetest.set_node(block_pos, {name=inactive, param2=block.param2})
|
2021-12-21 08:45:26 -08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2021-12-21 13:32:35 -08:00
|
|
|
|
|
|
|
-- Emit lasers from all active emitters
|
|
|
|
function lzr_laser.emit_lasers_in_area(pos1, pos2)
|
|
|
|
local emitters = minetest.find_nodes_in_area(pos1, pos2, {"group:emitter"})
|
|
|
|
for e=1, #emitters do
|
|
|
|
local epos = emitters[e]
|
|
|
|
local emitter = minetest.get_node(epos)
|
|
|
|
local is_active = minetest.get_item_group(emitter.name, "emitter") == 2
|
|
|
|
if is_active then
|
|
|
|
lzr_laser.emit_laser(emitters[e])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2021-12-21 13:36:28 -08:00
|
|
|
|
2021-12-21 14:18:11 -08:00
|
|
|
-- Return true if all detectors in area are on (including if no detector)
|
|
|
|
function lzr_laser.check_detectors_in_area(pos1, pos2)
|
|
|
|
local detectors = minetest.find_nodes_in_area(pos1, pos2, {"group:detector"})
|
|
|
|
for d=1, #detectors do
|
|
|
|
local dpos = detectors[d]
|
|
|
|
local detector = minetest.get_node(dpos)
|
|
|
|
local is_active = minetest.get_item_group(detector.name, "detector") == 2
|
|
|
|
if not is_active then
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
2021-12-21 13:36:28 -08:00
|
|
|
-- Completely recalculate all lasers
|
|
|
|
function lzr_laser.full_laser_update(pos1, pos2)
|
|
|
|
lzr_laser.clear_lasers_in_area(pos1, pos2)
|
|
|
|
lzr_laser.emit_lasers_in_area(pos1, pos2)
|
|
|
|
end
|