diff --git a/mcl_comparators/init.lua b/mcl_comparators/init.lua index 3517e09..419ccb6 100644 --- a/mcl_comparators/init.lua +++ b/mcl_comparators/init.lua @@ -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=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(4−7, 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(9−5, 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 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 diff --git a/mesecons/internal.lua b/mesecons/internal.lua index 378f493..6ccde07 100644 --- a/mesecons/internal.lua +++ b/mesecons/internal.lua @@ -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)