forked from VoxeLibre/VoxeLibre
Implement comparator accurately and remove WIP, remove ABMs for comparators and make updates triggered instead of polled, changes to redstone power transmission
This commit is contained in:
parent
e970a5f414
commit
13888236d6
|
@ -1,4 +1,6 @@
|
|||
local S = minetest.get_translator(minetest.get_current_modname())
|
||||
mcl_comparators = {}
|
||||
local mod = mcl_comparators
|
||||
|
||||
-- Functions that get the input/output rules of the comparator
|
||||
|
||||
|
@ -10,7 +12,6 @@ local function comparator_get_output_rules(node)
|
|||
return rules
|
||||
end
|
||||
|
||||
|
||||
local function comparator_get_input_rules(node)
|
||||
local rules = {
|
||||
-- we rely on this order in update_self below
|
||||
|
@ -24,117 +25,73 @@ local function comparator_get_input_rules(node)
|
|||
return rules
|
||||
end
|
||||
|
||||
|
||||
-- Functions that are called after the delay time
|
||||
|
||||
local function comparator_turnon(params)
|
||||
local rules = comparator_get_output_rules(params.node)
|
||||
mesecon.receptor_on(params.pos, rules)
|
||||
end
|
||||
|
||||
|
||||
local function comparator_turnoff(params)
|
||||
local rules = comparator_get_output_rules(params.node)
|
||||
mesecon.receptor_off(params.pos, rules)
|
||||
end
|
||||
|
||||
|
||||
-- Functions that set the correct node type an schedule a turnon/off
|
||||
|
||||
local function comparator_activate(pos, node)
|
||||
local def = minetest.registered_nodes[node.name]
|
||||
local onstate = def.comparator_onstate
|
||||
if onstate then
|
||||
minetest.swap_node(pos, { name = onstate, param2 = node.param2 })
|
||||
end
|
||||
minetest.after(0.1, comparator_turnon , {pos = pos, node = node})
|
||||
end
|
||||
|
||||
|
||||
local function comparator_deactivate(pos, node)
|
||||
local def = minetest.registered_nodes[node.name]
|
||||
local offstate = def.comparator_offstate
|
||||
if offstate then
|
||||
minetest.swap_node(pos, { name = offstate, param2 = node.param2 })
|
||||
end
|
||||
minetest.after(0.1, comparator_turnoff, {pos = pos, node = node})
|
||||
end
|
||||
|
||||
|
||||
-- weather pos has an inventory that contains at least one item
|
||||
local function container_inventory_nonempty(pos)
|
||||
local invnode = minetest.get_node(pos)
|
||||
local invnodedef = minetest.registered_nodes[invnode.name]
|
||||
-- Ignore stale nodes
|
||||
if not invnodedef then return false 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
|
||||
|
||||
local inv = minetest.get_inventory({type="node", pos=pos})
|
||||
if not inv then return false end
|
||||
|
||||
for listname, _ in pairs(inv:get_lists()) do
|
||||
if not inv:is_empty(listname) then return true end
|
||||
end
|
||||
|
||||
return false
|
||||
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
|
||||
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
|
||||
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)
|
||||
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
|
||||
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
|
||||
for ri = 2,3 do
|
||||
if my_input_rules[ri] then
|
||||
side_state = mesecon.is_power_on(vector.add(pos, my_input_rules[ri]))
|
||||
end
|
||||
if side_state then break end
|
||||
end
|
||||
-- state is known to be true
|
||||
return not side_state
|
||||
end
|
||||
|
||||
|
||||
local POSSIBLE_COMPARATOR_POSITIONS = {
|
||||
vector.new( 1,0, 0),
|
||||
vector.new(-1,0, 0),
|
||||
vector.new( 0,0, 1),
|
||||
vector.new( 0,0,-1),
|
||||
}
|
||||
-- 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
|
||||
if new_state then
|
||||
comparator_activate(pos, node)
|
||||
else
|
||||
comparator_deactivate(pos, node)
|
||||
|
||||
-- Find the node we are pointing at
|
||||
local input_rules = comparator_get_input_rules(node);
|
||||
local back_rule = input_rules[1]
|
||||
local back_pos = vector.add(pos, back_rule)
|
||||
local back_node = minetest.get_node(back_pos)
|
||||
local back_nodedef = minetest.registered_nodes[back_node.name]
|
||||
|
||||
-- Get the comparator mode
|
||||
local mode = minetest.registered_nodes[node.name].comparator_mode
|
||||
|
||||
-- Get a comparator reading from the block at the back of the comparator
|
||||
local power_level = 0
|
||||
if back_nodedef and back_nodedef._mcl_comparators_get_reading then
|
||||
power_level = back_nodedef._mcl_comparators_get_reading(back_pos)
|
||||
end
|
||||
|
||||
-- Get the maximum side power level
|
||||
local side_power_level = 0
|
||||
for i=2,3 do
|
||||
local pl = vl_redstone.get_power_level(vector.add(pos,input_rules[i]))
|
||||
if pl > side_power_level then
|
||||
side_power_level = pl
|
||||
end
|
||||
end
|
||||
|
||||
-- Apply subtraction or comparison
|
||||
if mode == "sub" then
|
||||
power_level = power_level - side_power_level
|
||||
if power_level < 0 then power_level = 0 end
|
||||
elseif mode == "comp" then
|
||||
if side_power_level > power_level then
|
||||
power_level = 0
|
||||
end
|
||||
end
|
||||
|
||||
vl_redstone.set_power(pos, power_level)
|
||||
end
|
||||
|
||||
function mod.trigger_update(pos)
|
||||
-- try to find a comparator with the back rule leading to pos
|
||||
for i = 1,#POSSIBLE_COMPARATOR_POSITIONS do
|
||||
local candidate = vector.add(pos, POSSIBLE_COMPARATOR_POSITIONS[i])
|
||||
local node = minetest.get_node(candidate)
|
||||
if minetest.get_item_group(node.name,"mcl_comparator") ~= 0 then
|
||||
update_self(candidate, node)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function mod.read_inventory(inv, inv_name)
|
||||
local stacks = inv:get_list(inv_name)
|
||||
local count = 0
|
||||
for i=1,#stacks do
|
||||
count = count + ( stacks[i]:get_count() / stacks[i]:get_stack_max() )
|
||||
end
|
||||
return math.floor(count * 16 / 5)
|
||||
end
|
||||
|
||||
-- compute tile depending on state and mode
|
||||
local function get_tiles(state, mode)
|
||||
|
@ -215,6 +172,7 @@ local groups = {
|
|||
destroy_by_lava_flow = 1,
|
||||
dig_by_piston = 1,
|
||||
attached_node = 1,
|
||||
mcl_comparator = 1,
|
||||
}
|
||||
|
||||
local on_rotate
|
||||
|
@ -309,7 +267,7 @@ for _, mode in pairs{"comp", "sub"} do
|
|||
end
|
||||
|
||||
minetest.register_node(nodename, nodedef)
|
||||
mcl_wip.register_wip_item(nodename)
|
||||
--mcl_wip.register_wip_item(nodename)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -327,6 +285,7 @@ minetest.register_craft({
|
|||
}
|
||||
})
|
||||
|
||||
--[[
|
||||
-- Register active block handlers
|
||||
minetest.register_abm({
|
||||
label = "Comparator signal input check (comparator is off)",
|
||||
|
@ -352,7 +311,7 @@ minetest.register_abm({
|
|||
chance = 1,
|
||||
action = update_self,
|
||||
})
|
||||
|
||||
]]
|
||||
|
||||
-- Add entry aliases for the Help
|
||||
if minetest.get_modpath("doc") then
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
name = mcl_comparators
|
||||
depends = mcl_wip, mesecons, mcl_sounds
|
||||
depends = mesecons, mcl_sounds, vl_redstone
|
||||
optional_depends = doc, screwdriver
|
||||
|
|
|
@ -4,7 +4,6 @@ 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)
|
||||
|
@ -33,10 +32,10 @@ local function update_sink(pos)
|
|||
end
|
||||
end
|
||||
|
||||
local last_strength = meta:get_int(REDSTONE_POWER_META_LAST_STATE)
|
||||
local last_strength = meta:get_int(REDSTONE_POWER_META)
|
||||
|
||||
if last_strength ~= strength then
|
||||
print("Updating "..node.name.." at "..vector.to_string(pos).."("..tostring(last_strength).."->"..tostring(strength)..")")
|
||||
--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
|
||||
|
@ -54,7 +53,7 @@ local function update_sink(pos)
|
|||
-- 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)
|
||||
meta:set_int(REDSTONE_POWER_META, strength)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -90,34 +89,35 @@ local function get_positions_from_node_rules(pos, rules_type, list, powered)
|
|||
rules = POWERED_BLOCK_RULES
|
||||
end
|
||||
|
||||
print("rules="..dump(rules))
|
||||
--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]))
|
||||
--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)
|
||||
--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")
|
||||
vl_scheduler.register_function("vl_redstone:flow_power",function(task, source_pos, source_strength, distance)
|
||||
print("Flowing lv"..tostring(source_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)
|
||||
processed[source_pos_str] = true
|
||||
|
||||
-- Update the source node's redstone power
|
||||
local meta = minetest.get_meta(source_pos)
|
||||
meta:set_int(REDSTONE_POWER_META, strength)
|
||||
meta:set_int(REDSTONE_POWER_META, source_strength)
|
||||
|
||||
-- Get rules
|
||||
local list = {}
|
||||
|
@ -126,11 +126,11 @@ vl_scheduler.register_function("vl_redstone:flow_power",function(task, source_po
|
|||
|
||||
for i=1,distance do
|
||||
local next_list = {}
|
||||
local strength = strength - (i - 1)
|
||||
local strength = source_strength - (i - 1)
|
||||
if strength < 0 then strength = 0 end
|
||||
|
||||
for pos_str,dir in pairs(list) do
|
||||
print("Processing "..pos_str)
|
||||
--print("Processing "..pos_str)
|
||||
|
||||
if not processed[pos_str] then
|
||||
processed[pos_str] = true
|
||||
|
@ -140,7 +140,7 @@ vl_scheduler.register_function("vl_redstone:flow_power",function(task, source_po
|
|||
|
||||
-- Update node power directly
|
||||
meta:set_int(REDSTONE_POWER_META.."."..source_pos_str, strength)
|
||||
print("pos="..vector.to_string(pos)..", strength="..tostring(strength))
|
||||
--print("pos="..vector.to_string(pos)..", strength="..tostring(strength))
|
||||
|
||||
-- handle spread
|
||||
get_positions_from_node_rules(pos, "conductor", next_list, powered)
|
||||
|
@ -158,10 +158,23 @@ end)
|
|||
function vl_redstone.set_power(pos, strength)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local distance = meta:get_int(REDSTONE_POWER_META)
|
||||
|
||||
-- Don't perform an update if the power level is the same as before
|
||||
if distance == strength then return end
|
||||
|
||||
print("previous="..tostring(distance)..", new="..tostring(strength))
|
||||
|
||||
-- Make the update distance the maximum of the new strength and the old strength
|
||||
if distance < strength then
|
||||
distance = strength
|
||||
end
|
||||
|
||||
vl_scheduler.add_task(0, "vl_redstone:flow_power", 2, {pos, strength, distance})
|
||||
-- Schedule an update
|
||||
vl_scheduler.add_task(0, "vl_redstone:flow_power", 2, {pos, strength, distance + 1})
|
||||
end
|
||||
|
||||
function vl_redstone.get_power_level(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
return meta:get_int(REDSTONE_POWER_META)
|
||||
end
|
||||
|
||||
|
|
|
@ -283,14 +283,17 @@ local def_hopper = {
|
|||
end
|
||||
end,
|
||||
on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
mcl_comparators.trigger_update(pos)
|
||||
minetest.log("action", player:get_player_name() ..
|
||||
" moves stuff in mcl_hoppers at " .. minetest.pos_to_string(pos))
|
||||
end,
|
||||
on_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
mcl_comparators.trigger_update(pos)
|
||||
minetest.log("action", player:get_player_name() ..
|
||||
" moves stuff to mcl_hoppers at " .. minetest.pos_to_string(pos))
|
||||
end,
|
||||
on_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
mcl_comparators.trigger_update(pos)
|
||||
minetest.log("action", player:get_player_name() ..
|
||||
" takes stuff from mcl_hoppers at " .. minetest.pos_to_string(pos))
|
||||
end,
|
||||
|
@ -334,6 +337,11 @@ local def_hopper = {
|
|||
|
||||
return true
|
||||
end,
|
||||
_mcl_comparators_get_reading = function (pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return mcl_comparators.read_inventory(inv, "main")
|
||||
end,
|
||||
sounds = mcl_sounds.node_sound_metal_defaults(),
|
||||
|
||||
_mcl_blast_resistance = 4.8,
|
||||
|
@ -525,16 +533,19 @@ local def_hopper_side = {
|
|||
end
|
||||
end,
|
||||
on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
mcl_comparators.trigger_update(pos)
|
||||
minetest.log("action", player:get_player_name() ..
|
||||
" moves stuff in mcl_hoppers at " .. minetest.pos_to_string(pos))
|
||||
" moves stuff in mcl_hoppers at (1) " .. minetest.pos_to_string(pos))
|
||||
end,
|
||||
on_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
mcl_comparators.trigger_update(pos)
|
||||
minetest.log("action", player:get_player_name() ..
|
||||
" moves stuff to mcl_hoppers at " .. minetest.pos_to_string(pos))
|
||||
" moves stuff to mcl_hoppers at (2) " .. minetest.pos_to_string(pos))
|
||||
end,
|
||||
on_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
mcl_comparators.trigger_update(pos)
|
||||
minetest.log("action", player:get_player_name() ..
|
||||
" takes stuff from mcl_hoppers at " .. minetest.pos_to_string(pos))
|
||||
" takes stuff from mcl_hoppers at (3) " .. minetest.pos_to_string(pos))
|
||||
end,
|
||||
on_rotate = on_rotate,
|
||||
sounds = mcl_sounds.node_sound_metal_defaults(),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
name = mcl_hoppers
|
||||
description = It's just a clone of Minecraft hoppers, functions nearly identical to them minus mesecons making them stop and the way they're placed.
|
||||
depends = mcl_core, mcl_formspec, mcl_sounds, mcl_util, mcl_dye
|
||||
depends = mcl_core, mcl_formspec, mcl_sounds, mcl_util, mcl_dye, mcl_comparators
|
||||
optional_depends = doc, screwdriver
|
||||
|
|
Loading…
Reference in New Issue