voltage for comparator

comparison and subtraction mode
This commit is contained in:
Henry Behrendt 2021-08-22 19:16:46 +02:00
parent 974fb22aa2
commit 1da67202db
3 changed files with 181 additions and 31 deletions

View File

@ -29,9 +29,57 @@ end
local function comparator_turnon(params)
local rules = comparator_get_output_rules(params.node)
mesecon.receptor_on(params.pos, rules)
if params.lower_voltage then
-- nur den eigenen Strom rausnehmen
-- output verfolgen, solange er kleiner wird und auf 0 setzen
-- Call turnoff on all linking positions
for _, rule in ipairs(mesecon.flattenrules(rules)) do
local np = vector.add(params.pos, rule)
local rulenames = mesecon.rules_link_rule_all(params.pos, rule)
for _, rulename in ipairs(rulenames) do
comparator_removevoltage(np, rulename, params.old_voltage)
end
end
end
mesecon.receptor_on(params.pos, rules, params.voltage)
end
function comparator_removevoltage(pos, link, voltage)
local frontiers = {{pos = pos, link = link}}
local signals = {}
local rec_on = {}
local cur_vol = voltage+1
minetest.log("action", mesecon.postostring(pos) .. "-->" .. "comparator_removevoltage" .. " v="..cur_vol)
local depth = 1
while frontiers[1] do
local f = table.remove(frontiers, 1)
local node = mesecon.get_node_force(f.pos)
if node and mesecon.is_conductor_on(node, f.link) then
local meta = minetest.get_meta(f.pos)
local tmp_vol = (meta:get_string("mesecon_voltage")~="" and meta:get_int("mesecon_voltage") or 15)
if tmp_vol<cur_vol and tmp_vol>=1 then
--solange spannung niedriger wird
cur_vol = tmp_vol
meta:set_int("mesecon_voltage", 0)
local rules = mesecon.conductor_get_rules(node)
for _, r in pairs(mesecon.rule2meta(f.link, rules)) do
local np = vector.add(f.pos, r)
-- check neighbors
for _, l in pairs(mesecon.rules_link_rule_all(f.pos, r)) do
table.insert(frontiers, {pos = np, link = l})
end
end
end
end
depth = depth + 1
end
end
local function comparator_turnoff(params)
local rules = comparator_get_output_rules(params.node)
@ -41,14 +89,16 @@ end
-- Functions that set the correct node type an schedule a turnon/off
local function comparator_activate(pos, node)
local function comparator_activate(pos, node, voltage, lower_voltage, old_voltage)
minetest.log("action", mesecon.postostring(pos) .. "-->" .. "comparator_activate" .. " v="..voltage .. " lv=" .. tostring(lower_voltage))
local def = minetest.registered_nodes[node.name]
minetest.swap_node(pos, { name = def.comparator_onstate, param2 = node.param2 })
minetest.after(0.1, comparator_turnon , {pos = pos, node = node})
minetest.after(0.1, comparator_turnon , {pos = pos, node = node, voltage = voltage, lower_voltage=lower_voltage, old_voltage=old_voltage})
end
local function comparator_deactivate(pos, node)
minetest.log("action", mesecon.postostring(pos) .. "-->" .. "comparator_deactivate " .. mesecon.postostring(pos))
local def = minetest.registered_nodes[node.name]
minetest.swap_node(pos, { name = def.comparator_offstate, param2 = node.param2 })
minetest.after(0.1, comparator_turnoff, {pos = pos, node = node})
@ -57,72 +107,166 @@ end
-- weather pos has an inventory that contains at least one item
local function container_inventory_nonempty(pos)
-- https://minecraft.fandom.com/wiki/Redstone_Comparator#Measure_block_state
-- signal strength = floor(1 + ((sum of all slots' fullnesses) / (number of slots in container)) × 14)
-- fullness of a slot = number of items in slot / max stack size for this type of item
local invnode = minetest.get_node(pos)
local invnodedef = minetest.registered_nodes[invnode.name]
-- Ignore stale nodes
if not invnodedef then return false end
if not invnodedef then return 0 end
-- Only accept containers. When a container is dug, it's inventory
-- seems to stay. and we don't want to accept the inventory of an air
-- block
if not invnodedef.groups.container then return false end
if not invnodedef.groups.container then return 0 end
local inv = minetest.get_inventory({type="node", pos=pos})
if not inv then return false end
if not inv then return 0 end
--minetest.log("action", mesecon.postostring(pos) .. "-->" .. "container_inventory_nonempty.inv= " .. mesecon.tabletostring(inv:get_lists()))
local item_count = 0
local inv_space = 0
for listname, _ in pairs(inv:get_lists()) do
if not inv:is_empty(listname) then return true end
--minetest.log("action", mesecon.postostring(pos) .. "-->" .. "container_inventory_nonempty.inv.size= " .. inv:get_size(listname))
local stack = inv:get_stack(listname, 1)
--minetest.log("action", mesecon.postostring(pos) .. "-->" .. "container_inventory_nonempty.inv.stack1= " .. mesecon.tabletostring(stack))
--minetest.log("action", mesecon.postostring(pos) .. "-->" .. "container_inventory_nonempty.inv.stack1.max= " .. stack:get_stack_max())
--minetest.log("action", mesecon.postostring(pos) .. "-->" .. "container_inventory_nonempty.inv.stack1.count= " .. stack:get_count())
local st_size = inv:get_size(listname)
inv_space = inv_space + (64 * st_size)
for i = 1,st_size do
local stack = inv:get_stack(listname, i)
item_count = item_count + (stack:get_count() * 64 / stack:get_stack_max())
end
--if not inv:is_empty(listname) then return true, 15 end
end
--minetest.log("action", mesecon.postostring(pos) .. "-->" .. "container_inventory_nonempty.inv_space= " .. inv_space)
--minetest.log("action", mesecon.postostring(pos) .. "-->" .. "container_inventory_nonempty.item_count= " .. item_count)
if item_count>0 then
local voltage = math.floor(1 + (item_count/inv_space*14))
--minetest.log("action", mesecon.postostring(pos) .. "-->" .. "container_inventory_nonempty.voltage= " .. voltage)
return voltage
end
return false
return 0
end
-- weather pos has an constant signal output for the comparator
local function static_signal_output(pos)
local node = minetest.get_node(pos)
local g = minetest.get_item_group(node.name, "comparator_signal")
return g > 0
return g
end
-- whether the comparator should be on according to its inputs
local function comparator_desired_on(pos, node)
local my_input_rules = comparator_get_input_rules(node);
local back_rule = my_input_rules[1]
local state
local voltage = 0
if back_rule then
local back_pos = vector.add(pos, back_rule)
state = mesecon.is_power_on(back_pos) or container_inventory_nonempty(back_pos) or static_signal_output(back_pos)
local _, vo_back = mesecon.is_power_on(back_pos)
local vo_coin = container_inventory_nonempty(back_pos)
local vo_sso = static_signal_output(back_pos)
voltage = math.max(vo_back, vo_coin, vo_sso)
end
-- if back input if off, we don't need to check side inputs
if not state then return false end
-- without power levels, side inputs have no influence on output in compare
-- mode
--minetest.log("action", mesecon.postostring(pos) .. "-->" .. "comparator_desired_on1.voltage= " .. voltage .. "state="..tostring(state))
-- if back input is off, we don't need to check side inputs
if voltage<1 then return false, 0 end
local mode = minetest.registered_nodes[node.name].comparator_mode
if mode == "comp" then return state end
-- subtract mode, subtract max(side_inputs) from back input
local side_state = false
local side_voltage = 0
for ri = 2,3 do
if my_input_rules[ri] then
side_state = mesecon.is_power_on(vector.add(pos, my_input_rules[ri]))
local _, s_voltage = mesecon.is_power_on(vector.add(pos, my_input_rules[ri]))
side_voltage = math.max(side_voltage, s_voltage)
end
if side_state then break end
end
-- state is known to be true
return not side_state
--minetest.log("action", mesecon.postostring(pos) .. "-->" .. "comparator_desired_on2.voltage= " .. voltage .. "state="..tostring(state))
if mode == "comp" then
-- Comparators in comparison mode
if side_voltage > voltage then
--minetest.log("action", mesecon.postostring(pos) .. "-->" .. "comparator_desired_on3.voltage= " .. voltage .. "state="..tostring(state))
return false, 0
else
--minetest.log("action", mesecon.postostring(pos) .. "-->" .. "comparator_desired_on4.voltage= " .. voltage .. "state="..tostring(state))
return true, voltage
end
end
-- comparator in subtraction mode
voltage = math.max(voltage-side_voltage, 0)
if voltage>0 then
return true, voltage
else
return false, 0
end
end
--[[
Compare signal strength
Comparators in comparison mode.
A redstone comparator in comparison mode (front torch down and unpowered) compares its rear input to its two side inputs.
If either side input is greater than the rear input, the comparator output turns off. If neither side input is greater than
the rear input, the comparator outputs the same signal strength as its rear input.
The formula for calculating the output signal strength is as follows:
output = rear × [left rear AND right rear]
Subtract signal strength
A redstone comparator in subtraction mode (front torch up and powered) subtracts the signal strength of the higher side input from the signal strength of the rear input.
output = max(rear max(left, right), 0)
For example: if the signal strength is 6 at the left input, 7 at the right input and 4 at the rear, the output signal has a strength of max(4 max(6, 7), 0) = max(47, 0) = max(3, 0) = 0.
If the signal strength is 9 at the rear, 2 at the right input and 5 at the left input, the output signal has a strength of max(9 max(2, 5), 0) = max(95, 0) = 4.
--]]
-- update comparator state, if needed
local function update_self(pos, node)
node = node or minetest.get_node(pos)
local old_state = mesecon.is_receptor_on(node.name)
local new_state = comparator_desired_on(pos, node)
if new_state ~= old_state then
local meta = minetest.get_meta(pos)
local old_voltage = old_state and (meta:get_string("mesecon_voltage")~="" and meta:get_int("mesecon_voltage") or 15) or 0
local new_state, new_voltage = comparator_desired_on(pos, node)
if (new_state ~= old_state) or (old_voltage~=new_voltage) then
minetest.log("action", mesecon.postostring(pos) .. "-->" .. "update_self.ov=" .. old_voltage .. " nv="..new_voltage.." os="..tostring(old_state).." ns="..tostring(new_state))
if old_voltage~=new_voltage then meta:set_int("mesecon_voltage", new_voltage) end
if new_state then
comparator_activate(pos, node)
comparator_activate(pos, node, new_voltage, (new_voltage<old_voltage), old_voltage)
else
comparator_deactivate(pos, node)
end

View File

@ -112,7 +112,10 @@ mesecon.queue:add_function("receptor_off", function (pos, rules)
for _, rec in pairs(rec_on) do
--rules??? -> mesecon.receptor_get_rules(node)
local node = mesecon.get_node_force(rec)
mesecon.receptor_on(rec, mesecon.receptor_get_rules(node))
local meta = minetest.get_meta(rec)
local voltage = meta:get_string("mesecon_voltage")~="" and meta:get_int("mesecon_voltage") or 15
mesecon.receptor_on(rec, mesecon.receptor_get_rules(node), voltage)
end
end
end

View File

@ -373,9 +373,11 @@ end
function mesecon.is_power_on(pos, rulename)
local node = get_node_force(pos)
if node and (mesecon.is_conductor_on(node, rulename) or is_receptor_on(node.name)) then
return true
local meta = minetest.get_meta(pos)
voltage = meta:get_string("mesecon_voltage")~="" and meta:get_int("mesecon_voltage") or 15
return true, voltage
end
return false
return false, 0
end
function mesecon.is_power_off(pos, rulename)
@ -416,14 +418,15 @@ function mesecon.turnon(pos, link, voltage)
local old_v = (mesecon.is_conductor_off(node, f.link) and 0 or meta:get_int("mesecon_voltage"))
local v = math.max(f.voltage, old_v)
--wenn spannung geändert
--wenn gleiche spannung, aber muss ausgeschalten werden
--if v~=old_v or (v<1 and mesecon.is_conductor_on(node, f.link)) or (meta:get_int("mesecon_voltage")<0) then
if v~=old_v or (v<1 and mesecon.is_conductor_on(node, f.link)) then
--minetest.log("action", f.pos.x .. "/" .. f.pos.y .. "/" .. f.pos.z .. "-->" .. "turnon.f.voltage="..f.voltage.."<-->"..v)
if v>0 or (v<1 and mesecon.is_conductor_on(node, f.link)) then
--if v>0 or (v<1 and mesecon.is_conductor_on(node, f.link)) or (meta:get_int("mesecon_voltage")<0) then
if v>=1 or (v<1 and mesecon.is_conductor_on(node, f.link)) then
-- Call turnon on neighbors
for _, r in pairs(mesecon.rule2meta(f.link, rules)) do
local np = vector.add(f.pos, r)