260 lines
7.6 KiB
Lua
260 lines
7.6 KiB
Lua
-- mesecon_controller.lua
|
|
-- Mesecon-interfaceable Operation Panel alternative
|
|
-- Looks like a Mesecon Luacontroller
|
|
|
|
-- Luacontroller Adapted Code
|
|
-- From Mesecons mod https://mesecons.net/
|
|
-- (c) Jeija and Contributors
|
|
|
|
local BASENAME = "advtrains_luaautomation:mesecon_controller"
|
|
|
|
local rules = {
|
|
a = {x = -1, y = 0, z = 0, name="A"},
|
|
b = {x = 0, y = 0, z = 1, name="B"},
|
|
c = {x = 1, y = 0, z = 0, name="C"},
|
|
d = {x = 0, y = 0, z = -1, name="D"},
|
|
}
|
|
|
|
local function generate_name(ports)
|
|
local d = ports.d and 1 or 0
|
|
local c = ports.c and 1 or 0
|
|
local b = ports.b and 1 or 0
|
|
local a = ports.a and 1 or 0
|
|
return BASENAME..d..c..b..a
|
|
end
|
|
|
|
|
|
local function set_port(pos, rule, state)
|
|
if state then
|
|
mesecon.receptor_on(pos, {rule})
|
|
else
|
|
mesecon.receptor_off(pos, {rule})
|
|
end
|
|
end
|
|
|
|
local function clean_port_states(ports)
|
|
ports.a = ports.a and true or false
|
|
ports.b = ports.b and true or false
|
|
ports.c = ports.c and true or false
|
|
ports.d = ports.d and true or false
|
|
end
|
|
|
|
-- Local table for storing which Mesecons off events should be ignored
|
|
-- Indexed by hex encoded position
|
|
local ignored_off_events = {}
|
|
|
|
local function set_port_states(pos, ports)
|
|
local node = advtrains.ndb.get_node(pos)
|
|
local name = node.name
|
|
clean_port_states(ports)
|
|
local vports = minetest.registered_nodes[name].virtual_portstates
|
|
local new_name = generate_name(ports)
|
|
|
|
if name ~= new_name and vports then
|
|
-- Problem:
|
|
-- We need to place the new node first so that when turning
|
|
-- off some port, it won't stay on because the rules indicate
|
|
-- there is an onstate output port there.
|
|
-- When turning the output off then, it will however cause feedback
|
|
-- so that the luacontroller will receive an "off" event by turning
|
|
-- its output off.
|
|
-- Solution / Workaround:
|
|
-- Remember which output was turned off and ignore next "off" event.
|
|
local ph=minetest.pos_to_string(pos)
|
|
local railtbl = atlatc.active.nodes[ph]
|
|
if not railtbl then return end
|
|
|
|
local ign = railtbl.ignored_off_events or {}
|
|
if ports.a and not vports.a and not mesecon.is_powered(pos, rules.a) then ign.A = true end
|
|
if ports.b and not vports.b and not mesecon.is_powered(pos, rules.b) then ign.B = true end
|
|
if ports.c and not vports.c and not mesecon.is_powered(pos, rules.c) then ign.C = true end
|
|
if ports.d and not vports.d and not mesecon.is_powered(pos, rules.d) then ign.D = true end
|
|
railtbl.ignored_off_events = ign
|
|
|
|
advtrains.ndb.swap_node(pos, {name = new_name, param2 = node.param2})
|
|
|
|
-- Apply mesecon state only if node loaded
|
|
-- If node is not loaded, mesecon update will occur on next load via on_updated_from_nodedb
|
|
if advtrains.is_node_loaded(pos) then
|
|
if ports.a ~= vports.a then set_port(pos, rules.a, ports.a) end
|
|
if ports.b ~= vports.b then set_port(pos, rules.b, ports.b) end
|
|
if ports.c ~= vports.c then set_port(pos, rules.c, ports.c) end
|
|
if ports.d ~= vports.d then set_port(pos, rules.d, ports.d) end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function on_updated_from_nodedb(pos, newnode, oldnode)
|
|
-- Switch appropriate Mesecon receptors depending on the node change
|
|
local vports = minetest.registered_nodes[oldnode.name].virtual_portstates
|
|
local ports = minetest.registered_nodes[newnode.name].virtual_portstates
|
|
if ports.a ~= vports.a then set_port(pos, rules.a, ports.a) end
|
|
if ports.b ~= vports.b then set_port(pos, rules.b, ports.b) end
|
|
if ports.c ~= vports.c then set_port(pos, rules.c, ports.c) end
|
|
if ports.d ~= vports.d then set_port(pos, rules.d, ports.d) end
|
|
end
|
|
|
|
local function ignore_offevent(pos, rule)
|
|
local ph=minetest.pos_to_string(pos)
|
|
local railtbl = atlatc.active.nodes[ph]
|
|
if not railtbl then return nil end
|
|
local ign = railtbl.ignored_off_events
|
|
if ign and ign[rule.name] then
|
|
ign[rule.name] = nil
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
local valid_ports = {a=true, b=true, c=true, d=true}
|
|
|
|
local function fire_event(pos, evtdata)
|
|
local customfct={
|
|
set_mesecon_outputs = function(states)
|
|
assertt(states, "table")
|
|
set_port_states(pos, states)
|
|
end,
|
|
get_mesecon_input = function(port)
|
|
local portl = string.lower(port)
|
|
if not valid_ports[portl] then
|
|
error("get_mesecon_input: Invalid port (expected a,b,c,d)")
|
|
end
|
|
if mesecon.is_powered(pos, rules[portl]) then
|
|
return true
|
|
end
|
|
return false
|
|
end,
|
|
}
|
|
atlatc.active.run_in_env(pos, evtdata, customfct, true)
|
|
|
|
end
|
|
|
|
local output_rules = {}
|
|
local input_rules = {}
|
|
|
|
local node_box = {
|
|
type = "fixed",
|
|
fixed = {
|
|
{-8/16, -8/16, -8/16, 8/16, -7/16, 8/16}, -- Bottom slab
|
|
{-5/16, -7/16, -5/16, 5/16, -6/16, 5/16}, -- Circuit board
|
|
{-3/16, -6/16, -3/16, 3/16, -5/16, 3/16}, -- IC
|
|
}
|
|
}
|
|
|
|
local selection_box = {
|
|
type = "fixed",
|
|
fixed = { -8/16, -8/16, -8/16, 8/16, -5/16, 8/16 },
|
|
}
|
|
|
|
for a = 0, 1 do -- 0 = off 1 = on
|
|
for b = 0, 1 do
|
|
for c = 0, 1 do
|
|
for d = 0, 1 do
|
|
local cid = tostring(d)..tostring(c)..tostring(b)..tostring(a)
|
|
local node_name = BASENAME..cid
|
|
local top = "atlatc_luacontroller_top.png"
|
|
if a == 1 then
|
|
top = top.."^atlatc_luacontroller_LED_A.png"
|
|
end
|
|
if b == 1 then
|
|
top = top.."^atlatc_luacontroller_LED_B.png"
|
|
end
|
|
if c == 1 then
|
|
top = top.."^atlatc_luacontroller_LED_C.png"
|
|
end
|
|
if d == 1 then
|
|
top = top.."^atlatc_luacontroller_LED_D.png"
|
|
end
|
|
|
|
local groups
|
|
if a + b + c + d ~= 0 then
|
|
groups = {dig_immediate=2, not_in_creative_inventory=1, save_in_at_nodedb=1}
|
|
else
|
|
groups = {dig_immediate=2, save_in_at_nodedb=1}
|
|
end
|
|
|
|
output_rules[cid] = {}
|
|
input_rules[cid] = {}
|
|
if a == 1 then table.insert(output_rules[cid], rules.a) end
|
|
if b == 1 then table.insert(output_rules[cid], rules.b) end
|
|
if c == 1 then table.insert(output_rules[cid], rules.c) end
|
|
if d == 1 then table.insert(output_rules[cid], rules.d) end
|
|
|
|
if a == 0 then table.insert( input_rules[cid], rules.a) end
|
|
if b == 0 then table.insert( input_rules[cid], rules.b) end
|
|
if c == 0 then table.insert( input_rules[cid], rules.c) end
|
|
if d == 0 then table.insert( input_rules[cid], rules.d) end
|
|
|
|
local mesecons = {
|
|
effector = {
|
|
rules = input_rules[cid],
|
|
action_change = function (pos, _, rule_name, new_state)
|
|
if new_state == "off" then
|
|
-- check for ignored off event on this node
|
|
if ignore_offevent(pos, rule_name) then
|
|
return
|
|
end
|
|
end
|
|
--Note: rule_name is not a *name* but actually the full rule table (position + name field)
|
|
--Event format consistent with Mesecons Luacontroller event
|
|
atlatc.interrupt.add(0, pos, {type=new_state, [new_state]=true, pin=rule_name})
|
|
end,
|
|
},
|
|
receptor = {
|
|
state = mesecon.state.on,
|
|
rules = output_rules[cid]
|
|
},
|
|
}
|
|
|
|
minetest.register_node(node_name, {
|
|
description = "LuaATC Mesecon Controller",
|
|
drawtype = "nodebox",
|
|
tiles = {
|
|
top,
|
|
"atlatc_luacontroller_bottom.png",
|
|
"atlatc_luacontroller_sides.png",
|
|
"atlatc_luacontroller_sides.png",
|
|
"atlatc_luacontroller_sides.png",
|
|
"atlatc_luacontroller_sides.png"
|
|
},
|
|
inventory_image = top,
|
|
paramtype = "light",
|
|
is_ground_content = false,
|
|
groups = groups,
|
|
drop = BASENAME.."0000",
|
|
sunlight_propagates = true,
|
|
selection_box = selection_box,
|
|
node_box = node_box,
|
|
mesecons = mesecons,
|
|
-- Virtual portstates are the ports that
|
|
-- the node shows as powered up (light up).
|
|
virtual_portstates = {
|
|
a = a == 1,
|
|
b = b == 1,
|
|
c = c == 1,
|
|
d = d == 1,
|
|
},
|
|
after_dig_node = function (pos, node, player)
|
|
mesecon.receptor_off(pos, output_rules)
|
|
atlatc.active.after_dig_node(pos, node, player)
|
|
end,
|
|
after_place_node = atlatc.active.after_place_node,
|
|
on_receive_fields = atlatc.active.on_receive_fields,
|
|
advtrains = {
|
|
on_updated_from_nodedb = on_updated_from_nodedb
|
|
},
|
|
luaautomation = {
|
|
fire_event=fire_event
|
|
},
|
|
digiline = {
|
|
receptor = {},
|
|
effector = {
|
|
action = atlatc.active.on_digiline_receive
|
|
},
|
|
},
|
|
})
|
|
end
|
|
end
|
|
end
|
|
end
|