diff options
| -rw-r--r-- | mesecons/init.lua | 16 | ||||
| -rw-r--r-- | mesecons/internal.lua | 132 | 
2 files changed, 53 insertions, 95 deletions
| diff --git a/mesecons/init.lua b/mesecons/init.lua index 944a86a..7da3583 100644 --- a/mesecons/init.lua +++ b/mesecons/init.lua @@ -101,8 +101,6 @@ function mesecon.receptor_on(pos, rules)  end  mesecon.queue:add_function("receptor_off", function (pos, rules) -	mesecon.vm_begin() -  	rules = rules or mesecon.rules.default  	-- if area (any of the rule targets) is not loaded, keep trying and call this again later @@ -118,15 +116,19 @@ mesecon.queue:add_function("receptor_off", function (pos, rules)  		local np = vector.add(pos, rule)  		local rulenames = mesecon.rules_link_rule_all(pos, rule)  		for _, rulename in ipairs(rulenames) do -			if not mesecon.connected_to_receptor(np, mesecon.invertRule(rule)) then -				mesecon.turnoff(np, rulename) +			mesecon.vm_begin() +			mesecon.changesignal(np, minetest.get_node(np), rulename, mesecon.state.off, 2) + +			-- Turnoff returns true if turnoff process was successful, no onstate receptor +			-- was found along the way. Commit changes that were made in voxelmanip. If turnoff +			-- returns true, an onstate receptor was found, abort voxelmanip transaction. +			if (mesecon.turnoff(np, rulename)) then +				mesecon.vm_commit()  			else -				mesecon.changesignal(np, minetest.get_node(np), rulename, mesecon.state.off, 2) +				mesecon.vm_abort()  			end  		end  	end - -	mesecon.vm_commit()  end)  function mesecon.receptor_off(pos, rules) diff --git a/mesecons/internal.lua b/mesecons/internal.lua index c125d3f..2a352f3 100644 --- a/mesecons/internal.lua +++ b/mesecons/internal.lua @@ -41,7 +41,6 @@  -- mesecon.turnoff(pos, link)				--> link is the input rule that caused calling turnoff, turns off every connected node, iterative  -- mesecon.connected_to_receptor(pos, link)		--> Returns true if pos is connected to a receptor directly or via conductors, iterative  -- mesecon.rules_link(output, input, dug_outputrules)	--> Returns true if outputposition + outputrules = inputposition and inputposition + inputrules = outputposition (if the two positions connect) --- mesecon.rules_link_anydir(outp., inp., d_outpr.)	--> Same as rules mesecon.rules_link but also returns true if output and input are swapped  -- mesecon.is_powered(pos)				--> Returns true if pos is powered by a receptor or a conductor  -- RULES ROTATION helpers @@ -371,6 +370,10 @@ function mesecon.is_power_off(pos, rulename)  	return false  end +-- Turn off an equipotential section starting at `pos`, which outputs in the direction of `link`. +-- Breadth-first search. Map is abstracted away in a voxelmanip. +-- Follow all all conductor paths replacing conductors that were already +-- looked at, activating / changing all effectors along the way.  function mesecon.turnon(pos, link)  	local frontiers = {{pos = pos, link = link}} @@ -384,7 +387,7 @@ function mesecon.turnon(pos, link)  		elseif mesecon.is_conductor_off(node, f.link) then  			local rules = mesecon.conductor_get_rules(node) -			-- call turnon on neighbors +			-- Call turnon on neighbors  			for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do  				local np = vector.add(f.pos, r)  				for _, l in ipairs(mesecon.rules_link_rule_all(f.pos, r)) do @@ -403,8 +406,25 @@ function mesecon.turnon(pos, link)  	end  end +-- Turn on an equipotential section starting at `pos`, which outputs in the direction of `link`. +-- Breadth-first search. Map is abstracted away in a voxelmanip. +-- Follow all all conductor paths replacing conductors that were already +-- looked at, deactivating / changing all effectors along the way. +-- In case an onstate receptor is discovered, abort the process by returning false, which will +-- cause `receptor_off` to discard all changes made in the voxelmanip. +-- Contrary to turnon, turnoff has to cache all change and deactivate signals so that they will only +-- be called in the very end when we can be sure that no conductor was found along the path. +-- +-- Signal table entry structure: +-- { +--	pos = position of effector, +--	node = node descriptor (name, param1 and param2), +--	link = link the effector is connected to, +--	depth = indicates order in which signals wire fired, higher is later +-- }  function mesecon.turnoff(pos, link)  	local frontiers = {{pos = pos, link = link}} +	local signals = {}  	local depth = 1  	while frontiers[1] do @@ -415,10 +435,19 @@ function mesecon.turnoff(pos, link)  			-- Area does not exist; do nothing  		elseif mesecon.is_conductor_on(node, f.link) then  			local rules = mesecon.conductor_get_rules(node) - -			-- call turnoff on neighbors  			for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do  				local np = vector.add(f.pos, r) + +				-- Check if an onstate receptor is connected. If that is the case, +				-- abort this turnoff process by returning false. `receptor_off` will +				-- discard all the changes that we made in the voxelmanip: +				for _, l in ipairs(mesecon.rules_link_rule_all_inverted(f.pos, r)) do +					if mesecon.is_receptor_on(mesecon.get_node_force(np).name) then +						return false +					end +				end + +				-- Call turnoff on neighbors  				for _, l in ipairs(mesecon.rules_link_rule_all(f.pos, r)) do  					table.insert(frontiers, {pos = np, link = l})  				end @@ -426,93 +455,24 @@ function mesecon.turnoff(pos, link)  			mesecon.swap_node_force(f.pos, mesecon.get_conductor_off(node, f.link))  		elseif mesecon.is_effector(node.name) then -			mesecon.changesignal(f.pos, node, f.link, mesecon.state.off, depth) -			if mesecon.is_effector_on(node.name) and not mesecon.is_powered(f.pos) then -				mesecon.deactivate(f.pos, node, f.link, depth) -			end +			table.insert(signals, { +				pos = f.pos, +				node = node, +				link = f.link, +				depth = depth +			})  		end  		depth = depth + 1  	end -end - -function mesecon.connected_to_receptor(pos, link) -	local node = mesecon.get_node_force(pos) -	if not node then return false end - -	-- Check if conductors around are connected -	local rules = mesecon.get_any_inputrules(node) -	if not rules then return false end -	for _, rule in ipairs(mesecon.rule2meta(link, rules)) do -		local links = mesecon.rules_link_rule_all_inverted(pos, rule) -		for _, l in ipairs(links) do -			local np = vector.add(pos, l) -			if mesecon.find_receptor_on(np, mesecon.invertRule(l)) then -				return true -			end +	for _, sig in ipairs(signals) do +		mesecon.changesignal(sig.pos, sig.node, sig.link, mesecon.state.off, sig.depth) +		if mesecon.is_effector_on(sig.node.name) and not mesecon.is_powered(sig.pos) then +			mesecon.deactivate(sig.pos, sig.node, sig.link, sig.depth)  		end  	end -	return false -end - -function mesecon.find_receptor_on(pos, link) -	local frontiers = {{pos = pos, link = link}} -	local checked = {} - -	-- List of positions that have been searched for onstate receptors -	local depth = 1 -	while frontiers[depth] do -		local f = frontiers[depth] -		local node = mesecon.get_node_force(f.pos) - -		if not node then return false end -		if mesecon.is_receptor_on(node.name) then return true end -		if mesecon.is_conductor_on(node, f.link) then -			local rules = mesecon.conductor_get_rules(node) - -			-- call turnoff on neighbors: normal rules -			for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do -				local np = vector.add(f.pos, r) - -				local links = mesecon.rules_link_rule_all_inverted(f.pos, r) -				for _, l in ipairs(links) do -					local checkedstring = np.x..np.y..np.z..l.x..l.y..l.z -					if not checked[checkedstring] then -						table.insert(frontiers, {pos = np, link = l}) -						checked[checkedstring] = true -					end -				end -			end - -		end -		depth = depth + 1 -	end -end - -function mesecon.rules_link(output, input, dug_outputrules) --output/input are positions (outputrules optional, used if node has been dug), second return value: the name of the affected input rule -	local outputnode = mesecon.get_node_force(output) -	local inputnode = mesecon.get_node_force(input) - -	local outputrules = dug_outputrules or mesecon.get_any_outputrules(outputnode) -	local inputrules = mesecon.get_any_inputrules(inputnode) -	if not outputrules or not inputrules then -		return -	end - -	for _, outputrule in ipairs(mesecon.flattenrules(outputrules)) do -		-- Check if output sends to input -		if vector.equals(vector.add(output, outputrule), input) then -			for _, inputrule in ipairs(mesecon.flattenrules(inputrules)) do -				-- Check if input accepts from output -				if  vector.equals(vector.add(input, inputrule), output) then -					return true, inputrule -				end -			end -		end -	end - -	return false +	return true  end  function mesecon.rules_link_rule_all(output, rule) @@ -552,10 +512,6 @@ function mesecon.rules_link_rule_all_inverted(input, rule)  	return rules  end -function mesecon.rules_link_anydir(pos1, pos2) -	return mesecon.rules_link(pos1, pos2) or mesecon.rules_link(pos2, pos1) -end -  function mesecon.is_powered(pos, rule)  	local node = mesecon.get_node_force(pos)  	local rules = mesecon.get_any_inputrules(node) | 
