local modname = minetest.get_current_modname() local modpath = minetest.get_modpath(modname) vl_redstone = {} local mod = vl_redstone local REDSTONE_POWER_META = modname .. ".power" local REDSTONE_POWER_META_LAST_STATE = modname .. ".last-power" local REDSTONE_POWER_META_SOURCE = REDSTONE_POWER_META.."." local function update_sink(pos) local node = minetest.get_node(pos) local nodedef = minetest.registered_nodes[node.name] -- Only do this processing of signal sinks if not nodedef.mesecons then return end local sink = nodedef.mesecons.effector if not sink then return end local meta = minetest.get_meta(pos) -- Calculate the maximum power feeding into this node local strength = 0 local meta_tbl = meta:to_table() for k,v in pairs(meta_tbl.fields) do if string.sub(k,1,#REDSTONE_POWER_META_SOURCE) == REDSTONE_POWER_META_SOURCE then --local source_pos_str = string.sub(k,#REDSTONE_POWER_META_SOURCE+1) --print("\tsource_pos_str="..source_pos_str) local source_strength = tonumber(v) --print("\tstrength="..source_strength) if source_strength > strength then strength = source_strength end end end local last_strength = meta:get_int(REDSTONE_POWER_META_LAST_STATE) if last_strength ~= strength then print("Updating "..node.name.." at "..vector.to_string(pos).."("..tostring(last_strength).."->"..tostring(strength)..")") -- Inform the node of changes if strength > 0 then -- Handle activation if sink.action_on then mcl_util.call_safe(nil, sink.action_on, {pos, node}) end else -- Handle deactivation if sink.action_off then mcl_util.call_safe(nil, sink.action_off, {pos, node}) end end -- TODO: handle signal level change notification -- Update the state as the last thing in case there is a crash in the above code meta:set_int(REDSTONE_POWER_META_LAST_STATE, strength) end end local POWERED_BLOCK_RULES = { vector.new( 1, 0, 0), vector.new(-1, 0, 0), vector.new( 0, 1, 0), vector.new( 0,-1, 0), vector.new( 0, 0, 1), vector.new( 0, 0,-1), } local function get_positions_from_node_rules(pos, rules_type, list, powered) list = list or {} local node = minetest.get_node(pos) local nodedef = minetest.registered_nodes[node.name] local rules if nodedef.mesecons then -- Get mesecons rules if not nodedef.mesecons[rules_type] then minetest.log("info","Node "..node.name.." has no mesecons."..rules_type.." rules") return list end rules = nodedef.mesecons[rules_type].rules if type(rules) == "function" then rules = rules(node) end else -- The only blocks that don't support mesecon that propagate power are solid blocks that -- are powered by another device. Mesecons calls this 'spread' if not powered[vector.to_string(pos)] then return list end if minetest.get_item_group(node.name,"solid") == 0 then return list end rules = POWERED_BLOCK_RULES end print("rules="..dump(rules)) -- Convert to absolute positions for i=1,#rules do local next_pos = vector.add(pos, rules[i]) local next_pos_str = vector.to_string(next_pos) print("\tnext: "..next_pos_str..", prev="..tostring(list[next_pos_str])) list[next_pos_str] = true -- Power solid blocks if rules[i].spread then powered[next_pos_str] = true print("powering "..next_pos_str) end end return list end vl_scheduler.register_function("vl_redstone:flow_power",function(task, source_pos, strength, distance) print("Flowing lv"..tostring(strength).." power from "..vector.to_string(source_pos).." for "..tostring(distance).." blocks") local processed = {} local powered = {} local source_pos_str = vector.to_string(source_pos) -- Update the source node's redstone power local meta = minetest.get_meta(source_pos) meta:set_int(REDSTONE_POWER_META, strength) -- Get rules local list = {} get_positions_from_node_rules(source_pos, "receptor", list, powered) --print("initial list="..dump(list)) for i=1,distance do local next_list = {} local strength = strength - (i - 1) if strength < 0 then strength = 0 end for pos_str,dir in pairs(list) do print("Processing "..pos_str) if not processed[pos_str] then processed[pos_str] = true local pos = vector.from_string(pos_str) local meta = minetest.get_meta(pos) -- Update node power directly meta:set_int(REDSTONE_POWER_META.."."..source_pos_str, strength) print("pos="..vector.to_string(pos)..", strength="..tostring(strength)) -- handle spread get_positions_from_node_rules(pos, "conductor", next_list, powered) -- Update the position update_sink(pos) end end -- Continue onto the next set of nodes to process list = next_list end end) function vl_redstone.set_power(pos, strength) local meta = minetest.get_meta(pos) local distance = meta:get_int(REDSTONE_POWER_META) if distance < strength then distance = strength end vl_scheduler.add_task(0, "vl_redstone:flow_power", 2, {pos, strength, distance}) end