569 lines
20 KiB
Lua
569 lines
20 KiB
Lua
-- Laser "physics". This file contains the code that propagates
|
|
-- the lasers, updates the laser nodes and the map.
|
|
|
|
|
|
-- Max. number of steps in the laser travel algorithm
|
|
local MAX_LASER_ITERATIONS = 10000
|
|
|
|
-- TODO: Don't use these variables, instead transport it between functions
|
|
local burning_cache = {}
|
|
local destroy_cache = {}
|
|
|
|
-- This propagates a *single laser* by a *single step* and checks what
|
|
-- to do with the next node. This will either add the laser, stop the
|
|
-- laser propagation (due to collision), or do a special event in case we hit a
|
|
-- laser block (mirror, crystal, etc.).
|
|
-- This function only works on VoxelManip data!
|
|
-- Parameters:
|
|
-- * `pos`: start position
|
|
-- * `dir`: laser direction
|
|
-- * `colorcode`: laser colorcode
|
|
-- * `varea`: VoxelArea for the VoxelManip workable area
|
|
-- * `vdata`: VoxelManip data table gotten with `get_data`
|
|
-- * `vdata_p2`: VoxelManip data table, but for param2
|
|
--
|
|
-- Returns a list of the *next* laser positions and direction, each entry is in format:
|
|
-- { pos, dir, colorcode }
|
|
-- This indicates where to spawn the next lasers (pos), where they are headed
|
|
-- towards (dir) and what color they are (colorcode).
|
|
-- Some laser blocks may spawn multiple lasers (crystal, transmissive mirror),
|
|
-- this is why it must be a list.
|
|
-- If the laser terminates (e.g. due to collision), returns false instead.
|
|
function lzr_laser.add_laser(pos, dir, colorcode, varea, vdata, vdata_p2)
|
|
-- 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
|
|
local vi = varea:indexp(pos)
|
|
local content_id = vdata[vi]
|
|
local param2 = vdata_p2[vi]
|
|
local nodename = minetest.get_name_from_content_id(content_id)
|
|
local ld = minetest.get_item_group(nodename, "laser_destroys")
|
|
-- Laser through air or destroyable block
|
|
if content_id == minetest.CONTENT_AIR or ld == 1 then
|
|
local dirs = lzr_laser.vector_and_color_to_dirs(dir, colorcode)
|
|
local dirstring = lzr_laser.dirs_to_dirstring(dirs)
|
|
vdata[vi] = minetest.get_content_id("lzr_laser:laser_"..dirstring)
|
|
if ld == 1 then
|
|
table.insert(destroy_cache, {pos=pos, nodename=nodename})
|
|
end
|
|
-- Just advance straight ahead
|
|
pos = vector.add(pos, dir)
|
|
return {{pos, dir, colorcode}}
|
|
-- Burning block
|
|
elseif ld == 2 then
|
|
local def = minetest.registered_nodes[nodename]
|
|
local active = def._lzr_active
|
|
if active then
|
|
-- Only burn in-game (for editor convenience)
|
|
if lzr_gamestate.get_state() == lzr_gamestate.LEVEL then
|
|
vdata[vi] = minetest.get_content_id(active)
|
|
table.insert(burning_cache, pos)
|
|
end
|
|
else
|
|
minetest.log("error", "[lzr_laser] Node definition of "..nodename.." has laser_destroys=2 but no _lzr_active")
|
|
end
|
|
-- Laser collides
|
|
return false
|
|
-- Laser through laser (laser intersection)
|
|
elseif minetest.get_item_group(nodename, "laser") > 0 then
|
|
local laser_group = minetest.get_item_group(nodename, "laser")
|
|
local dirstring_old = lzr_laser.laser_group_to_dirstring(laser_group)
|
|
|
|
local dirs_new = lzr_laser.vector_and_color_to_dirs(dir, colorcode)
|
|
local dirstring_new = lzr_laser.dirs_to_dirstring(dirs_new)
|
|
|
|
local place_dirstring = lzr_laser.dirstring_or(dirstring_old, dirstring_new)
|
|
vdata[vi] = minetest.get_content_id("lzr_laser:laser_"..place_dirstring)
|
|
-- Advance straight ahead
|
|
pos = vector.add(pos, dir)
|
|
return {{pos, dir, colorcode}}
|
|
-- Laser through skull
|
|
elseif minetest.get_item_group(nodename, "skull_shy") > 0 or minetest.get_item_group(nodename, "skull_cursed") > 0 then
|
|
local def = minetest.registered_nodes[nodename]
|
|
local active = def._lzr_active
|
|
if active then
|
|
-- Activate skull node
|
|
vdata[vi] = minetest.get_content_id(def._lzr_active)
|
|
end
|
|
pos = vector.add(pos, dir)
|
|
return {{pos, dir, colorcode}}
|
|
-- Mirror laser
|
|
elseif minetest.get_item_group(nodename, "mirror") > 0 then
|
|
local mirror_dir = lzr_laser.get_mirrored_laser_dir(nodename, param2, dir)
|
|
if mirror_dir then
|
|
local def = minetest.registered_nodes[nodename]
|
|
local active = def._lzr_active
|
|
if active then
|
|
-- Activate mirror node and mix color
|
|
local old_colorcode = minetest.get_item_group(nodename, "laser_block_color")
|
|
local new_colorcode = bit.bor(colorcode, old_colorcode)
|
|
vdata[vi] = minetest.get_content_id(active.."_"..new_colorcode)
|
|
end
|
|
-- Set new pos and dir after calculating mirror direction
|
|
pos = vector.add(pos, mirror_dir)
|
|
dir = mirror_dir
|
|
return {{pos, dir, colorcode}}
|
|
else
|
|
return false
|
|
end
|
|
-- Mirror and split laser
|
|
elseif minetest.get_item_group(nodename, "transmissive_mirror") > 0 then
|
|
local mirror_dir, mirror_side = lzr_laser.get_mirrored_laser_dir(nodename, param2, dir)
|
|
if mirror_dir then
|
|
local def = minetest.registered_nodes[nodename]
|
|
local state = def._lzr_transmissive_mirror_state
|
|
if not state then
|
|
minetest.log("error", "[lzr_laser] Transmissive mirror node '"..nodename.."' does not have _lzr_transmissive_mirror_state!")
|
|
return false
|
|
end
|
|
-- Combine transmissive mirror state
|
|
if mirror_side == true then
|
|
-- Combine current state with 0X (X = incoming colorcode)
|
|
state = lzr_laser.dirstring_or(state, "0"..colorcode)
|
|
else
|
|
-- Combine current state with X0 (X = incoming colorcode)
|
|
state = lzr_laser.dirstring_or(state, colorcode.."0")
|
|
end
|
|
|
|
local is_takable = minetest.get_item_group(nodename, "takable") ~= 0
|
|
local active
|
|
if is_takable then
|
|
active = "lzr_laser:transmissive_mirror_"..state.."_takable"
|
|
else
|
|
active = "lzr_laser:transmissive_mirror_"..state
|
|
end
|
|
|
|
-- Set new transmissive node state
|
|
vdata[vi] = minetest.get_content_id(active)
|
|
|
|
-- Report new laser positions and directions.
|
|
-- This always will report 2 new lasers.
|
|
-- The laser goes right through ...
|
|
local pos_straight = vector.add(pos, dir)
|
|
local dir_straight = dir
|
|
-- ... and it is also deflected
|
|
local pos_mirrored = vector.add(pos, mirror_dir)
|
|
local dir_mirrored = mirror_dir
|
|
return {
|
|
-- The laser that went straight through
|
|
{pos_straight, dir_straight, colorcode},
|
|
-- The mirrored laser
|
|
{pos_mirrored, dir_mirrored, colorcode}
|
|
}
|
|
else
|
|
-- Laser came from the wrong side so it ends here
|
|
return false
|
|
end
|
|
-- Crystal: Spread laser to all directions
|
|
elseif minetest.get_item_group(nodename, "crystal") > 0 then
|
|
local def = minetest.registered_nodes[nodename]
|
|
local active = def._lzr_active
|
|
if minetest.get_item_group(nodename, "laser_block_color") == colorcode then
|
|
return false
|
|
end
|
|
|
|
if active then
|
|
-- Activate node and mix color
|
|
local old_colorcode = minetest.get_item_group(nodename, "laser_block_color")
|
|
local new_colorcode = bit.bor(colorcode, old_colorcode)
|
|
vdata[vi] = minetest.get_content_id(active.."_"..new_colorcode)
|
|
end
|
|
-- Set dirs to spread laser towards
|
|
local dirs = {
|
|
vector.new(0, -1, 0),
|
|
vector.new(0, 1, 0),
|
|
vector.new(-1, 0, 0),
|
|
vector.new(1, 0, 0),
|
|
vector.new(0, 0, -1),
|
|
vector.new(0, 0, 1),
|
|
}
|
|
-- Don't spread to the direction we came from!
|
|
local fromdir = vector.multiply(dir, -1)
|
|
for d=1, #dirs do
|
|
if vector.equals(dirs[d], fromdir) 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], colorcode })
|
|
end
|
|
return output
|
|
-- Detector
|
|
elseif minetest.get_item_group(nodename, "detector") > 0 then
|
|
local detected = lzr_laser.check_detector(pos, nodename, dir, colorcode)
|
|
if detected then
|
|
local def = minetest.registered_nodes[nodename]
|
|
local active = def._lzr_active
|
|
if active then
|
|
-- Activate node
|
|
vdata[vi] = minetest.get_content_id(active)
|
|
end
|
|
end
|
|
-- Laser ends here
|
|
return false
|
|
-- Hollow barrel
|
|
elseif minetest.get_item_group(nodename, "hollow_barrel") > 0 then
|
|
-- Laser can through the hollow part
|
|
local axis = lzr_laser.get_barrel_axis(param2)
|
|
if (dir.x ~= 0 and axis == "x") or
|
|
(dir.y ~= 0 and axis == "y") or
|
|
(dir.z ~= 0 and axis == "z") then
|
|
local def = minetest.registered_nodes[nodename]
|
|
local active = def._lzr_active
|
|
if active then
|
|
-- Combine color
|
|
local old_colorcode = minetest.get_item_group(nodename, "laser_block_color")
|
|
local new_colorcode = bit.bor(colorcode, old_colorcode)
|
|
-- Activate barrel node
|
|
vdata[vi] = minetest.get_content_id(active.."_"..new_colorcode)
|
|
end
|
|
pos = vector.add(pos, dir)
|
|
return {{pos, dir, colorcode}}
|
|
else
|
|
return false
|
|
end
|
|
-- Pane
|
|
elseif minetest.get_item_group(nodename, "pane") > 0 then
|
|
-- Laser can through the pane
|
|
local axis = lzr_laser.get_pane_axis(param2)
|
|
if (dir.x ~= 0 and axis == "x") or
|
|
(dir.y ~= 0 and axis == "y") or
|
|
(dir.z ~= 0 and axis == "z") then
|
|
local def = minetest.registered_nodes[nodename]
|
|
local active = def._lzr_active
|
|
if active then
|
|
-- Combine color
|
|
local old_colorcode = minetest.get_item_group(nodename, "laser_block_color")
|
|
local new_colorcode = bit.bor(colorcode, old_colorcode)
|
|
-- Activate pane node
|
|
vdata[vi] = minetest.get_content_id(active.."_"..new_colorcode)
|
|
end
|
|
pos = vector.add(pos, dir)
|
|
return {{pos, dir, colorcode}}
|
|
else
|
|
return false
|
|
end
|
|
-- An open empty chest, or a slab
|
|
elseif minetest.get_item_group(nodename, "chest_open") > 0 or minetest.get_item_group(nodename, "slab") == 1 then
|
|
-- Laser can go into it from above
|
|
-- (this also works for slab because it cannot rotate)
|
|
if dir.y < 0 then
|
|
local def = minetest.registered_nodes[nodename]
|
|
local active = def._lzr_active
|
|
if active then
|
|
-- Laser beam from above goes into chest/slab
|
|
vdata[vi] = minetest.get_content_id(active.."_"..colorcode)
|
|
end
|
|
end
|
|
-- Laser ends here
|
|
return false
|
|
|
|
-- Palm leaves
|
|
elseif minetest.get_item_group(nodename, "palm_leaves") > 0 then
|
|
-- Laser can go into it from below
|
|
if dir.y > 0 then
|
|
local def = minetest.registered_nodes[nodename]
|
|
local active = def._lzr_active
|
|
if active then
|
|
-- Laser beam from below
|
|
vdata[vi] = minetest.get_content_id(active.."_"..colorcode)
|
|
end
|
|
end
|
|
-- Laser ends here
|
|
return false
|
|
|
|
-- Anything else terminates the laser
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
-- Emit a laser from an emitter and starts laser propagation.
|
|
-- * `pos`: position of emitter
|
|
-- * `colorcode`: laser colorcode
|
|
-- * `varea`, `vdata`, `vdata_p2`: See `lzr_laser.add_laser`
|
|
function lzr_laser.emit_laser(pos, colorcode, varea, vdata, vdata_p2)
|
|
local vi = varea:indexp(pos)
|
|
local content_id = vdata[vi]
|
|
local nodename = minetest.get_name_from_content_id(content_id)
|
|
if minetest.get_item_group(nodename, "emitter") == 0 then
|
|
minetest.log("error", "[lzr_laser] lzr_laser.emit_laser was called at invalid pos!")
|
|
return false
|
|
end
|
|
local param2 = vdata_p2[vi]
|
|
|
|
local dir = minetest.facedir_to_dir(param2)
|
|
dir = vector.multiply(dir, -1)
|
|
local i_pos = vector.add(pos, dir)
|
|
lzr_laser.travel_laser(i_pos, dir, colorcode, varea, vdata, vdata_p2)
|
|
|
|
end
|
|
|
|
-- Spawns and propagates a single laser from `pos` step-by-step
|
|
-- until either all laser beams that have been created during
|
|
-- travel have terminated, or the algorithm took too many
|
|
-- iterations (which is probably an error pointing to an infinite
|
|
-- laser loop; or the map is just very complex).
|
|
-- * `pos`: position of laser
|
|
-- * `dir`: direction towards the laser will travel to initially
|
|
-- * `colorcode`: laser colorcode
|
|
-- * `varea`, `vdata`, `vdata_p2`: See `lzr_laser.add_laser`
|
|
function lzr_laser.travel_laser(pos, dir, colorcode, varea, vdata, vdata_p2)
|
|
local i_pos = table.copy(pos)
|
|
local cond = true
|
|
-- This is a list of all currently "travelling lasers" that
|
|
-- spawned from the initial laser. Each travelling laser
|
|
-- has a position, direction and colorcode.
|
|
-- This list initializes with a single position, from where we start.
|
|
-- Each time add_laser is called, the travelling laser is removed, but
|
|
-- any new travelling lasers returned from add_laser will be added to the
|
|
-- list.
|
|
-- This essentially is a depth-first search.
|
|
local next_lasers = {{i_pos, dir, colorcode}}
|
|
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
|
|
|
|
-- Get next laser and propagate it by one step
|
|
local next_laser = next_lasers[1]
|
|
local add_laser_result = lzr_laser.add_laser(next_laser[1], next_laser[2], next_laser[3], varea, vdata, vdata_p2)
|
|
|
|
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
|
|
-- When the table is empty, this means all travelling lasers have terminated. Success!
|
|
if #next_lasers == 0 then
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Remove all lasers in the given area and disable all laser blocks
|
|
-- (e.g. mirrors, detectors)
|
|
-- * pos1: Minimum position of area
|
|
-- * pos2: Maximum position of area
|
|
-- * ignore_emitters: If true, won't disable emitters
|
|
-- * varea, vdata: See lzr_laser.add_laser
|
|
function lzr_laser.clear_lasers_in_area(pos1, pos2, ignore_emitters, varea, vdata)
|
|
for vi=1, #vdata do
|
|
local cid = vdata[vi]
|
|
local nodename = minetest.get_name_from_content_id(cid)
|
|
if minetest.get_item_group(nodename, "laser") ~= 0 then
|
|
vdata[vi] = minetest.CONTENT_AIR
|
|
elseif minetest.get_item_group(nodename, "laser_block") ~= 0 then
|
|
local def = minetest.registered_nodes[nodename]
|
|
local is_ignored_emitter = false
|
|
if ignore_emitters ~= true then
|
|
is_ignored_emitter = minetest.get_item_group(nodename, "emitter") > 0
|
|
end
|
|
if def and not is_ignored_emitter then
|
|
local inactive = def._lzr_inactive
|
|
if inactive then
|
|
vdata[vi] = minetest.get_content_id(inactive)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Emit lasers from all *active* emitters in area.
|
|
-- * pos1: Minimum position of area
|
|
-- * pos2: Maximum position of area
|
|
-- * ignore_emitters
|
|
-- * varea, vdata, vdata_p2: See lzr_laser.add_laser
|
|
function lzr_laser.emit_lasers_in_area(pos1, pos2, varea, vdata, vdata_p2)
|
|
local emitters = minetest.find_nodes_in_area(pos1, pos2, {"group:emitter"})
|
|
for e=1, #emitters do
|
|
local epos = emitters[e]
|
|
local vi = varea:indexp(epos)
|
|
local emitter_cid = vdata[vi]
|
|
local emittername = minetest.get_name_from_content_id(emitter_cid)
|
|
local is_active = minetest.get_item_group(emittername, "emitter") == 2
|
|
local colorcode = minetest.get_item_group(emittername, "emitter_color")
|
|
if is_active and colorcode ~= 0 then
|
|
lzr_laser.emit_laser(emitters[e], colorcode, varea, vdata, vdata_p2)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Returns true if all detectors in area between pos1 and pos2 are active.
|
|
-- If there are no detectors, returns true.
|
|
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
|
|
|
|
-- Returns true if there are no unclaimed treasures remaining in area
|
|
function lzr_laser.check_treasures_in_area(pos1, pos2)
|
|
local closed_chests = minetest.find_nodes_in_area(pos1, pos2, {"group:chest_closed"})
|
|
return #closed_chests > 0
|
|
end
|
|
|
|
-- Returns the number of treasures found in current level in area
|
|
function lzr_laser.count_found_treasures(pos1, pos2)
|
|
return #minetest.find_nodes_in_area(pos1, pos2, {"group:chest_open_treasure"})
|
|
end
|
|
|
|
-- Returns true if player has no detectors in inventory
|
|
-- * player: Player ObjectRef
|
|
-- * detector_placed: Set to true if the player has *just* placed a detector (default: false)
|
|
function lzr_laser.check_inventory_detectors(player, detector_placed)
|
|
local inv = player:get_inventory()
|
|
local count = 0
|
|
for i=1, inv:get_size("main") do
|
|
local item = inv:get_stack("main", i)
|
|
if minetest.get_item_group(item:get_name(), "detector") ~= 0 then
|
|
count = count + item:get_count()
|
|
if (detector_placed and count > 1) or (not detector_placed) then
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
-- Returns true if ALL detectors (both in level and in inventory) are active
|
|
-- * detector_placed: Set to true if the player has *just* placed a detector (default: false)
|
|
function lzr_laser.check_all_detectors(detector_placed)
|
|
local cond_area = lzr_laser.check_detectors_in_area(lzr_globals.PLAYFIELD_START, lzr_globals.PLAYFIELD_END)
|
|
local cond_inventory = true
|
|
local player = minetest.get_player_by_name("singleplayer")
|
|
if player then
|
|
cond_inventory = lzr_laser.check_inventory_detectors(player, detector_placed)
|
|
end
|
|
return cond_area and cond_inventory
|
|
end
|
|
|
|
-- Returns true if current level is won
|
|
function lzr_laser.check_level_won()
|
|
return not lzr_laser.check_treasures_in_area(lzr_globals.PLAYFIELD_START, lzr_globals.PLAYFIELD_END)
|
|
end
|
|
|
|
-- Returns a table of all detector states in the given area,
|
|
-- indexed by the VoxelArea index of a VoxelManip and the value
|
|
-- being either true for active, false for inactive and nil
|
|
-- for any position without a detector.
|
|
local function get_detector_states_in_area(pos1, pos2, varea, vdata)
|
|
local states = {}
|
|
for vi=1, #vdata do
|
|
local cid = vdata[vi]
|
|
local nodename = minetest.get_name_from_content_id(cid)
|
|
local dstate = minetest.get_item_group(nodename, "detector")
|
|
if dstate == 1 then
|
|
states[vi] = false
|
|
elseif dstate == 2 then
|
|
states[vi] = true
|
|
end
|
|
end
|
|
return states
|
|
end
|
|
|
|
-- Completely recalculate all lasers
|
|
function lzr_laser.full_laser_update(pos1, pos2)
|
|
local benchmark_time_1 = minetest.get_us_time()
|
|
|
|
burning_cache = {}
|
|
destroy_cache = {}
|
|
|
|
local vmanip = minetest.get_voxel_manip(pos1, pos2)
|
|
local vpos1, vpos2 = vmanip:get_emerged_area()
|
|
local varea = VoxelArea:new({MinEdge = vpos1, MaxEdge = vpos2})
|
|
local vdata = vmanip:get_data()
|
|
local vdata_p2 = vmanip:get_param2_data()
|
|
|
|
-- Remember the old state of the map and the state of
|
|
-- detectors, then compare it to the new detector state.
|
|
-- Used for the detector sound effect.
|
|
local detector_states_old = get_detector_states_in_area(pos1, pos2, varea, vdata)
|
|
|
|
-- << THE MAIN LASER UPDATE HAPPENS HERE >>
|
|
-- The update is simple: We first remove all lasers, then
|
|
-- re-emit everything. This *COULD* be made more efficient
|
|
-- by only recalculating the lasers at the points where
|
|
-- they got changed tho.
|
|
lzr_laser.clear_lasers_in_area(pos1, pos2, nil, varea, vdata)
|
|
lzr_laser.emit_lasers_in_area(pos1, pos2, varea, vdata, vdata_p2)
|
|
|
|
-- Required for the detector sound effect.
|
|
local detector_states_new = get_detector_states_in_area(pos1, pos2, varea, vdata)
|
|
|
|
-- Write laser changes to map
|
|
vmanip:set_data(vdata)
|
|
vmanip:set_param2_data(vdata_p2)
|
|
vmanip:write_to_map()
|
|
|
|
-- Post-map update changes for stuff that the VManip can't do
|
|
|
|
-- Play detector sound for all detectors that have changed their state
|
|
for vindex, state_new in pairs(detector_states_new) do
|
|
local state_old = detector_states_old[vindex]
|
|
local pos = varea:position(vindex)
|
|
if state_new == true and state_old == false then
|
|
minetest.sound_play({name="lzr_laser_detector_activate", gain=0.7}, {pos=pos}, true)
|
|
elseif state_new == false and state_old == true then
|
|
minetest.sound_play({name="lzr_laser_detector_deactivate", gain=0.7}, {pos=pos}, true)
|
|
end
|
|
end
|
|
|
|
-- Trigger node burning for nodes burned by laser
|
|
for b=1, #burning_cache do
|
|
local node = minetest.get_node(burning_cache[b])
|
|
local def = minetest.registered_nodes[node.name]
|
|
if def and def.on_construct then
|
|
def.on_construct(burning_cache[b])
|
|
end
|
|
end
|
|
burning_cache = {}
|
|
-- Trigger animation and sound effect for nodes destroyed by laser
|
|
for d=1, #destroy_cache do
|
|
local dpos = destroy_cache[d].pos
|
|
local nodename = destroy_cache[d].nodename
|
|
local def = minetest.registered_nodes[nodename]
|
|
if def and def.sounds and def.sounds.dug then
|
|
minetest.sound_play(def.sounds.dug, {pos=dpos}, true)
|
|
end
|
|
minetest.add_particlespawner({
|
|
amount = 12,
|
|
time = 0.001,
|
|
minpos = vector.subtract(dpos, vector.new(0.5, 0.5, 0.5)),
|
|
maxpos = vector.add(dpos, vector.new(0.5, 0.5, 0.5)),
|
|
minvel = vector.new(-0.5, -0.2, -0.5),
|
|
maxvel = vector.new(0.5, 0.2, 0.5),
|
|
minacc = vector.new(0, -lzr_globals.GRAVITY, 0),
|
|
maxacc = vector.new(0, -lzr_globals.GRAVITY, 0),
|
|
minsize = 0.8,
|
|
maxsize = 0.8,
|
|
minexptime = 0.5,
|
|
maxexptime = 0.55,
|
|
node = {name=nodename},
|
|
})
|
|
end
|
|
destroy_cache = {}
|
|
|
|
-- Print benchmark time
|
|
local benchmark_time_2 = minetest.get_us_time()
|
|
local diff = benchmark_time_2 - benchmark_time_1
|
|
minetest.log("info", "[lzr_laser] lzr_laser.full_laser_update took "..diff.." µs.")
|
|
end
|