V1.13 Smartline Controller revised. Liquid Sampler node added. Release notes added

This commit is contained in:
Joachim Stolberg 2018-08-28 22:24:43 +02:00
parent 4c86e4e6b5
commit 3719bb6071
28 changed files with 2414 additions and 46 deletions

2
.gitignore vendored

@ -45,3 +45,5 @@ org.eclipse.*
*.lua.new *.lua.new
test_*.lua test_*.lua
shrink.py

@ -1,4 +1,4 @@
# TechPack V1.12 # TechPack V1.13
TechPack, a Mining, Crafting, & Farming Modpack for Minetest. TechPack, a Mining, Crafting, & Farming Modpack for Minetest.
@ -91,3 +91,6 @@ tubelib_addons1 optional: unified_inventory
- 2018-08-08 V1.10 * tubelib_addon3 with high performance nodes added - 2018-08-08 V1.10 * tubelib_addon3 with high performance nodes added
- 2018-08-13 V1.11 * Detector node added - 2018-08-13 V1.11 * Detector node added
- 2018-08-14 V1.12 * Teleporter node added - 2018-08-14 V1.12 * Teleporter node added
- 2018-08-28 V1.13 * Smartline Controller completely revised. Liquid Sampler added
See releasenotes.txt for further information

33
releasenotes.md Normal file

@ -0,0 +1,33 @@
# Release Notes of the ModPack TechPack [techpack]
## V1.13 Beta (2018-08-28)
### Additions
- A Liquid Sampler node is added. It is able to take all kind or renewable liquids (registered via bucket.register_liquid)
Currently only useful for recipes where a water-bucket is needed.
- Smartline has a new IF-THIS-THEN-THAT Controller V2 which should be much simpler to use.
It will replace the current one (V1).
Currently both are active, but if you dig a controller V1 it will be converted to a controller V2.
- The new controller needs batteries. Thus, Smartline has now its own battery node. The sl_controller.battery will not be
needed any more.
### Removals
- recipe for sl_controller/batteries removed.
- Recipe for Smartline controller V1 removed.
### Changes
- Quarry can no go direct from FAULT into RUNNING without reset the digging position
### Fixes
- bug in open/close door command for Minetest v0.4.17+ fixed

@ -10,6 +10,8 @@
battery.lua: battery.lua:
REPLACED BY SMARTLINE BATTERY !!!
]]-- ]]--
local function calc_percent(content) local function calc_percent(content)
@ -71,13 +73,13 @@ local function register_battery(ext, percent, nici)
local percent = calc_percent(tonumber(oldmetadata.fields.content)) local percent = calc_percent(tonumber(oldmetadata.fields.content))
local stack local stack
if percent > 95 then if percent > 95 then
stack = ItemStack("sl_controller:battery") stack = ItemStack("smartline:battery")
elseif percent > 75 then elseif percent > 75 then
stack = ItemStack("sl_controller:battery75") stack = ItemStack("smartline:battery75")
elseif percent > 50 then elseif percent > 50 then
stack = ItemStack("sl_controller:battery50") stack = ItemStack("smartline:battery50")
elseif percent > 25 then elseif percent > 25 then
stack = ItemStack("sl_controller:battery25") stack = ItemStack("smartline:battery25")
else else
return return
end end
@ -95,7 +97,7 @@ local function register_battery(ext, percent, nici)
}) })
end end
register_battery("", 1.0, 0) register_battery("", 1.0, 1)
register_battery("75", 0.75, 1) register_battery("75", 0.75, 1)
register_battery("50", 0.5, 1) register_battery("50", 0.5, 1)
register_battery("25", 0.25, 1) register_battery("25", 0.25, 1)
@ -135,22 +137,22 @@ minetest.register_node("sl_controller:battery_empty", {
}) })
if minetest.global_exists("moreores") then --if minetest.global_exists("moreores") then
minetest.register_craft({ -- minetest.register_craft({
output = "sl_controller:battery 2", -- output = "sl_controller:battery 2",
recipe = { -- recipe = {
{"", "moreores:silver_ingot", ""}, -- {"", "moreores:silver_ingot", ""},
{"", "default:copper_ingot", ""}, -- {"", "default:copper_ingot", ""},
{"", "moreores:silver_ingot", ""}, -- {"", "moreores:silver_ingot", ""},
} -- }
}) -- })
else --else
minetest.register_craft({ -- minetest.register_craft({
output = "sl_controller:battery 2", -- output = "sl_controller:battery 2",
recipe = { -- recipe = {
{"", "default:tin_ingot", ""}, -- {"", "default:tin_ingot", ""},
{"", "default:copper_ingot", ""}, -- {"", "default:copper_ingot", ""},
{"", "default:tin_ingot", ""}, -- {"", "default:tin_ingot", ""},
} -- }
}) -- })
end --end

@ -181,6 +181,7 @@ sl_controller.register_action("door", {
if door then if door then
local player = { local player = {
get_player_name = function() return self.meta.owner end, get_player_name = function() return self.meta.owner end,
is_player = function() return true end,
} }
if text == "open" then if text == "open" then
door:open(player) door:open(player)

@ -230,7 +230,7 @@ local function compile(pos, meta, number)
end end
local function battery(pos) local function battery(pos)
local battery_pos = minetest.find_node_near(pos, 1, {"sl_controller:battery"}) local battery_pos = minetest.find_node_near(pos, 1, {"sl_controller:battery", "smartline:battery"})
if battery_pos then if battery_pos then
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
meta:set_string("battery", minetest.pos_to_string(battery_pos)) meta:set_string("battery", minetest.pos_to_string(battery_pos))
@ -448,13 +448,13 @@ minetest.register_craft({
local function set_input(pos, number, input, val) local function set_input(pos, number, input, val)
if input then if input then
if Cache[number] and Cache[number].inputs then if Cache[number] and Cache[number].inputs then
Cache[number].inputs[input] = val
-- only one event per second -- only one event per second
local t = minetest.get_us_time() local t = minetest.get_us_time()
if not Cache[number].last_event or Cache[number].last_event < t then if not Cache[number].last_event or Cache[number].last_event < t then
Cache[number].inputs[input] = val
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
minetest.after(0.1, call_loop, pos, meta, -1) minetest.after(0.01, call_loop, pos, meta, -1)
Cache[number].last_event = t + 1000000 -- add one second Cache[number].last_event = t + 500000 -- add 500 ms
end end
end end
end end

@ -584,6 +584,7 @@ local function door_toggle(pos, owner, state)
if door then if door then
local player = { local player = {
get_player_name = function() return owner end, get_player_name = function() return owner end,
is_player = function() return true end,
} }
if state == "open" then if state == "open" then
door:open(player) door:open(player)

@ -10,6 +10,7 @@
controller.lua: controller.lua:
REPLACED BY SMARTLINE CONTROLLER2 !!!
]]-- ]]--
@ -63,7 +64,6 @@ The colors show, if conditions are true or false and
if actions were already executed or not. if actions were already executed or not.
It has a 'update' button to update the view. It has a 'update' button to update the view.
For more information, see: goo.gl/fF5ap6
]] ]]
local sOUTPUT = "Press 'help' for edit commands" local sOUTPUT = "Press 'help' for edit commands"
@ -117,8 +117,6 @@ local function exec_action(data, environ, number)
ActnRunTimeHandlers[data.__idx__](data, environ, number) ActnRunTimeHandlers[data.__idx__](data, environ, number)
end end
smartline = {}
-- --
-- API functions for condition/action registrations -- API functions for condition/action registrations
-- --
@ -845,9 +843,10 @@ minetest.register_node("smartline:controller", {
paramtype = "light", paramtype = "light",
sunlight_propagates = true, sunlight_propagates = true,
paramtype2 = "facedir", paramtype2 = "facedir",
groups = {choppy=1, cracky=1, crumbly=1}, groups = {choppy=1, cracky=1, crumbly=1, not_in_creative_inventory=1},
is_ground_content = false, is_ground_content = false,
sounds = default.node_sound_stone_defaults(), sounds = default.node_sound_stone_defaults(),
drop = "smartline:controller2",
}) })

105
smartline/icta/action.lua Normal file

@ -0,0 +1,105 @@
--[[
ICTA Controller
===============
Part of the SmartLine mod
Copyright (C) 2018 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
action.lua
]]--
local sl = smartline
--local mail_exists = minetest.get_modpath("mail") and mail ~= nil TODO
-- tables with all data from action registrations
local kvRegisteredActn = {}
-- list of keys for actions
local aActnTypes = {}
-- list of titles for actions
local aActnTitles = {}
--
-- API function for action registrations
--
function sl.icta_register_action(key, tData)
table.insert(aActnTypes, key)
table.insert(aActnTitles, tData.title)
tData.__idx__ = #aActnTypes
if kvRegisteredActn[key] ~= nil then
print("[SmartLine] Action registration error "..key)
return
end
kvRegisteredActn[key] = tData
for _,item in ipairs(tData.formspec) do
if item.type == "textlist" then
item.tChoices = string.split(item.choices, ",")
item.num_choices = #item.tChoices
end
end
end
-- return formspec string
function sl.actn_formspec(row, kvSelect)
return sl.submenu_generate_formspec(
row, "actn", "Action type", aActnTypes, aActnTitles, kvRegisteredActn, kvSelect)
end
-- evaluate the row action input
-- and return new data
function sl.actn_eval_input(kvSelect, fields)
kvSelect = sl.submenu_eval_input(kvRegisteredActn, aActnTypes, aActnTitles, kvSelect, fields)
return kvSelect
end
-- return the Lua code
function sl.code_action(kvSelect, environ)
if kvSelect and kvRegisteredActn[kvSelect.choice] then
if smartline.submenu_verify(kvRegisteredActn, kvSelect) then
return kvRegisteredActn[kvSelect.choice].code(kvSelect, environ)
end
end
return nil
end
sl.icta_register_action("default", {
title = "",
formspec = {},
code = function(data, environ) return false, false end,
button = function(data, environ) return "..." end,
})
sl.icta_register_action("print", {
title = "print to output window",
formspec = {
{
type = "ascii",
name = "text",
label = "Output the following text",
default = "",
},
{
type = "label",
name = "lbl",
label = "Hint: Use a '*' character as reference to any\ncondition state",
},
},
button = function(data, environ)
return 'print(...)'
end,
code = function(data, environ)
local s1 = 'local text = string.gsub("'..(smartline.escape(data.text))..'", "*", env.result[#])'
local s2 = 'output(env.pos, text)'
return s1.."\n\t"..s2
end,
})

123
smartline/icta/balancer.lua Normal file

@ -0,0 +1,123 @@
--[[
ICTA Controller
===============
Part of the SmartLine mod
Copyright (C) 2018 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
balancer.lua
]]--
local MAX_DIFF = 10
function smartline.balancer_condition(number1, number2, ratio1, ratio2, owner)
local cnt1 = tubelib.send_request(number1, "counter", nil) / ratio1
local cnt2 = tubelib.send_request(number2, "counter", nil) / ratio2
if cnt1 > cnt2 + MAX_DIFF then
tubelib.send_message(number1, owner, nil, "off", nil)
return number1
elseif cnt2 > cnt1 + MAX_DIFF then
tubelib.send_message(number2, owner, nil, "off", nil)
return number2
end
end
smartline.icta_register_condition("ratio", {
title = "Balancer ratio",
formspec = {
{
type = "numbers",
name = "number1",
label = "Pusher1",
default = "",
},
{
type = "digits",
name = "ratio1",
label = "Ratio1",
default = "",
},
{
type = "numbers",
name = "number2",
label = "Pusher2",
default = "",
},
{
type = "digits",
name = "ratio2",
label = "Ratio1",
default = "",
},
{
type = "label",
name = "lbl",
label = "Hint: Pusher1:Pusher2 shall have a\nitem counter ratio of Ratio1:Ratio2.",
},
},
-- Return two chunks of executable Lua code for the controller, according:
-- return <read condition>, <expected result>
code = function(data, environ)
local s = 'smartline.balancer_condition("%s", "%s", %s, %s, "%s")'
return s:format(data.number1, data.number2, data.ratio1, data.ratio2, environ.owner), '~= nil'
end,
button = function(data, environ)
return "ratio("..(data.ratio1 or "???").."/"..(data.ratio2 or "???")..")"
end,
})
smartline.icta_register_action("balancer", {
title = "Balancer Control",
formspec = {
{
type = "label",
name = "lbl",
label = "Hint: Stop one Pusher and start\nit again after 'after' seconds.",
},
},
button = function(data, environ)
return 'balancer()'
end,
code = function(data, environ)
local s = 'tubelib.send_message(env.result[#], "%s", nil, "on", nil)'
return string.format(s, data.number, environ.owner)
end,
})
smartline.icta_register_action("clearcounter", {
title = "Balancer clear counter",
formspec = {
{
type = "numbers",
name = "number1",
label = "Pusher1",
default = "",
},
{
type = "numbers",
name = "number2",
label = "Pusher2",
default = "",
},
{
type = "label",
name = "lbl",
label = "Hint: Clear both Pusher counters.",
},
},
button = function(data, environ)
return 'balancer()'
end,
code = function(data, environ)
local s = [[tubelib.send_message("%s", "%s", nil, "clear_counter", nil)
tubelib.send_message("%s", "%s", nil, "clear_counter", nil)]]
return string.format(s, data.number1, environ.owner, data.number2, environ.owner)
end,
})

158
smartline/icta/battery.lua Normal file

@ -0,0 +1,158 @@
--[[
ICTA Controller
===============
Copyright (C) 2018 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
battery.lua
]]--
local BATTERY_CAPACITY = 5000000
local function calc_percent(content)
local val = (BATTERY_CAPACITY - math.min(content or 0, BATTERY_CAPACITY))
return 100 - math.floor((val * 100.0 / BATTERY_CAPACITY))
end
local function on_timer(pos, elapsed)
local meta = minetest.get_meta(pos)
local percent = calc_percent(meta:get_int("content"))
meta:set_string("infotext", "Battery ("..percent.."%)")
if percent == 0 then
local node = minetest.get_node(pos)
node.name = "smartline:battery_empty"
minetest.swap_node(pos, node)
return false
end
return true
end
local function register_battery(ext, percent, nici)
minetest.register_node("smartline:battery"..ext, {
description = "Battery "..ext,
inventory_image = 'smartline_battery_inventory.png',
wield_image = 'smartline_battery_inventory.png',
tiles = {
-- up, down, right, left, back, front
"smartline.png",
"smartline.png",
"smartline.png",
"smartline.png",
"smartline.png",
"smartline.png^smartline_battery_green.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -6/32, -6/32, 14/32, 6/32, 6/32, 16/32},
},
},
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos)
meta:set_int("content", BATTERY_CAPACITY * percent)
local node = minetest.get_node(pos)
node.name = "smartline:battery"
minetest.swap_node(pos, node)
on_timer(pos, 1)
minetest.get_node_timer(pos):start(30)
end,
on_timer = on_timer,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
local percent = calc_percent(tonumber(oldmetadata.fields.content))
local stack
if percent > 95 then
stack = ItemStack("smartline:battery")
elseif percent > 75 then
stack = ItemStack("smartline:battery75")
elseif percent > 50 then
stack = ItemStack("smartline:battery50")
elseif percent > 25 then
stack = ItemStack("smartline:battery25")
else
return
end
local inv = minetest.get_inventory({type="player", name=digger:get_player_name()})
inv:add_item("main", stack)
end,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
groups = {choppy=1, cracky=1, crumbly=1, not_in_creative_inventory=nici},
drop = "",
is_ground_content = false,
sounds = default.node_sound_stone_defaults(),
})
end
register_battery("", 1.0, 0)
register_battery("75", 0.75, 1)
register_battery("50", 0.5, 1)
register_battery("25", 0.25, 1)
minetest.register_node("smartline:battery_empty", {
description = "Battery",
tiles = {
-- up, down, right, left, back, front
"smartline.png",
"smartline.png",
"smartline.png",
"smartline.png",
"smartline.png",
"smartline.png^smartline_battery_red.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -6/32, -6/32, 14/32, 6/32, 6/32, 16/32},
},
},
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos)
meta:set_int("content", 0)
end,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
groups = {choppy=1, cracky=1, crumbly=1, not_in_creative_inventory=1},
drop = "",
is_ground_content = false,
sounds = default.node_sound_stone_defaults(),
})
if minetest.global_exists("moreores") then
minetest.register_craft({
output = "smartline:battery 2",
recipe = {
{"", "moreores:silver_ingot", ""},
{"", "default:copper_ingot", ""},
{"", "moreores:silver_ingot", ""},
}
})
else
minetest.register_craft({
output = "smartline:battery 2",
recipe = {
{"", "default:tin_ingot", ""},
{"", "default:copper_ingot", ""},
{"", "default:tin_ingot", ""},
}
})
end

481
smartline/icta/commands.lua Normal file

@ -0,0 +1,481 @@
--[[
ICTA Controller
===============
Part of the SmartLine mod
Copyright (C) 2018 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
command.lua:
Register all controller commands
]]--
local sl = smartline
function sl.operand(s)
if s == "is" then
return "== "
else
return "~= "
end
end
-- '#' is used as placeholder for rule numbers and has to be escaped
function smartline.escape(s)
return s:gsub("#", '"..string.char(35).."')
end
smartline.icta_register_condition("once", {
title = "once",
formspec = {
{
type = "label",
name = "lbl",
label = "Hint: Once after start.",
},
},
-- Return two chunks of executable Lua code for the controller, according:
-- return <read condition>, <expected result>
code = function(data, environ)
return 'env.ticks', '== 1'
end,
button = function(data, environ) return "once" end,
})
smartline.icta_register_condition("true", {
title = "true",
formspec = {
{
type = "label",
name = "lbl",
label = "Hint: Condition is always true.",
},
},
code = function(data, environ)
return '"true"', '== "true"'
end,
button = function(data, environ) return "true" end,
})
smartline.icta_register_condition("condition", {
title = "Condition",
formspec = {
{
type = "textlist",
name = "condition",
label = "the action is executed, if",
choices = "condition 1,condition 2,condition 3,condition 4,condition 5,condition 6,condition 7,condition 8",
default = "",
},
{
type = "textlist",
name = "operand",
choices = "was true, was not true",
default = "was true",
},
{
type = "label",
name = "lbl",
label = "Hint: Execute two or several actions\nbased on one condition.",
},
},
code = function(data, environ)
local idx = data.condition:byte(-1) - 0x30
local expected_result = "== false"
if data.operand == "was true" then
expected_result = "== true"
end
return "env.condition["..idx.."]", expected_result
end,
button = function(data, environ) return data.condition or "???" end,
})
smartline.icta_register_condition("input", {
title = "inputs",
formspec = {
{
type = "digits",
name = "number",
label = "input from node with number",
default = "",
},
{
type = "textlist",
name = "operand",
choices = "is,is not",
default = "is",
},
{
type = "textlist",
name = "value",
choices = "on,off,false",
default = "on",
},
{
type = "label",
name = "lbl",
label = "Hint: An input is only available,\nif a nodes sends on/of\ncommands to the controller.",
},
},
button = function(data, environ) -- default button label
return 'input('..(data.number or "???")..')'
end,
code = function(data, environ)
return 'env.input["'..data.number..'"]',
sl.operand(data.operand)..'"'..data.value..'"'
end,
})
smartline.icta_register_condition("state", {
title = "node state request",
formspec = {
{
type = "digits",
name = "number",
label = "state from node with number",
default = "",
},
{
type = "textlist",
name = "operand",
label = "",
choices = "is,is not",
default = "is",
},
{
type = "textlist",
name = "value",
label = "",
choices = "stopped,running,standby,blocked,fault",
default = "stopped",
},
{
type = "label",
name = "lbl",
label = "Hint: Read the state from another node.\nWorks for Pusher, Harvester, Quarry,\nand others.",
},
},
button = function(data, environ) -- default button label
return 'state('..(data.number or "???")..')'
end,
code = function(data, environ)
return 'tubelib.send_request("'..data.number..'", "state", "")',
sl.operand(data.operand)..'"'..data.value..'"'
end,
})
smartline.icta_register_condition("fuel", {
title = "fuel state request",
formspec = {
{
type = "digits",
name = "number",
label = "fuel state from node with number",
default = "",
},
{
type = "textlist",
name = "operand",
label = "",
choices = "is,is not",
default = "is",
},
{
type = "textlist",
name = "value",
label = "",
choices = "full,loaded,empty",
default = "full"
},
{
type = "label",
name = "lbl",
label = "Hint: Read the fuel state from another node.\nWorks for Harvester and Quarry",
},
},
button = function(data, environ)
return 'fuel('..(data.number or "???")..')'
end,
code = function(data, environ)
return 'tubelib.send_request("'..data.number..'", "fuel", "")',
sl.operand(data.operand)..'"'..data.value..'"'
end,
})
smartline.icta_register_condition("signaltower", {
title = "Signal Tower state request",
formspec = {
{
type = "digits",
name = "number",
label = "state from Signal Tower with number",
default = "",
},
{
type = "textlist",
name = "operand",
label = "",
choices = "is,is not",
default = "is",
},
{
type = "textlist",
name = "value",
label = "",
choices = "off,green,amber,red",
default = "off",
},
{
type = "label",
name = "lbl",
label = "Hint: Read the state from a Signal Tower.",
},
},
button = function(data, environ) -- default button label
return 'tower('..(data.number or "???")..')'
end,
code = function(data, environ)
return 'tubelib.send_request("'..data.number..'", "state", "")',
sl.operand(data.operand)..'"'..data.value..'"'
end,
})
smartline.icta_register_action("signaltower", {
title = "Signal Tower command",
formspec = {
{
type = "numbers",
name = "number",
label = "set Signal Tower with number",
default = "",
},
{
type = "textlist",
name = "value",
label = "to color",
choices = "off,green,amber,red",
default = "red",
},
{
type = "label",
name = "lbl",
label = "Hint: Turn on a lamp from a Signal Tower.",
},
},
button = function(data, environ)
return 'tower('..(data.value or "???")..')'
end,
code = function(data, environ)
local s = 'tubelib.send_message("%s", "%s", nil, "%s", nil)'
return string.format(s, data.number, environ.owner, data.value)
end,
})
smartline.icta_register_action("switch", {
title = "node on/off command",
formspec = {
{
type = "numbers",
name = "number",
label = "set node with number",
default = "",
},
{
type = "textlist",
name = "value",
label = "to state",
choices = "on,off",
default = "on",
},
{
type = "label",
name = "lbl",
label = "Hint: Used for pushers, lamps, machines, gates,...",
},
},
button = function(data, environ)
return 'turn('..(data.value or "???")..')'
end,
code = function(data, environ)
local s = 'tubelib.send_message("%s", "%s", nil, "%s", nil)'
return string.format(s, data.number, environ.owner, data.value)
end,
})
smartline.icta_register_action("display", {
title = "Display: overwrite one line",
formspec = {
{
type = "numbers",
name = "number",
label = "output to Display with number",
default = "",
},
{
type = "textlist",
name = "row",
label = "Display line",
choices = "1,2,3,4,5,6,7,8,9",
default = "1",
},
{
type = "ascii",
name = "text",
label = "the following text",
default = "",
},
{
type = "label",
name = "lbl",
label = "Hint: Use a '*' character as reference to any\ncondition state",
},
},
code = function(data, environ)
local s1 = string.format('local text = string.gsub("%s", "*", env.result[#])', smartline.escape(data.text))
local s2 = string.format('local payload = {row = %s, str = text}', data.row)
local s3 = string.format('tubelib.send_message("%s", "%s", nil, "row", payload)', data.number, environ.owner)
return s1.."\n\t"..s2.."\n\t"..s3
end,
button = function(data, environ)
return "display("..(data.number or "???")..")"
end,
})
smartline.icta_register_action("cleardisplay", {
title = "Display: Clear screen",
formspec = {
{
type = "numbers",
name = "number",
label = "Display number",
default = "",
},
},
code = function(data, environ)
return 'tubelib.send_message("'..data.number..'", "'..environ.owner..'", nil, "clear", nil)'
end,
button = function(data, environ)
return "clear("..(data.number or "???")..")"
end,
})
smartline.icta_register_action("chat", {
title = "chat send",
formspec = {
{
type = "ascii",
name = "text",
label = "send the message",
default = "",
},
{
type = "label",
name = "lbl",
label = "Hint: The chat message is send to the\nController owner, only.",
},
},
code = function(data, environ)
return 'minetest.chat_send_player("'..environ.owner..'", "[SmartLine Controller] '..data.text..'")'
end,
button = function(data, environ)
return "chat(...)"
end,
})
function smartline.icta_door_toggle(pos, owner, state)
pos = minetest.string_to_pos("("..pos..")")
if pos then
local door = doors.get(pos)
if door then
local player = {
get_player_name = function() return owner end,
is_player = function() return true end,
}
if state == "open" then
door:open(player)
elseif state == "close" then
door:close(player)
end
end
end
end
smartline.icta_register_action("door", {
title = "doors open/close",
formspec = {
{
type = "digits",
name = "pos",
label = "door position like: 123,7,-1200",
default = "",
},
{
type = "textlist",
name = "door_state",
label = "set",
choices = "open,close",
default = "open",
},
{
type = "label",
name = "lbl1",
label = "For standard doors like the Steel Doors.",
},
{
type = "label",
name = "lbl2",
label = "Hint: Use the Tubelib Programmer to\ndetermine the door position.",
},
},
code = function(data, environ)
return 'smartline.icta_door_toggle("'..data.pos..'", "'..environ.owner..'", "'..data.door_state..'")'
end,
button = function(data, environ)
return "door("..(data.door_state or "???")..")"
end,
})
function smartline.icta_player_detect(number, name)
local state = tubelib.send_request(number, "name", nil)
if (name == "*" and state ~= "") or state == name then
return state
end
return nil
end
smartline.icta_register_condition("playerdetector", {
title = "Player Detector: name request",
formspec = {
{
type = "digits",
name = "number",
label = "name from player detector with number",
default = "",
},
{
type = "ascii",
name = "name",
label = "is",
default = "",
},
{
type = "label",
name = "lbl",
label = "Hint: Read and check the name\nfrom a Player Detector.\n Use a '*' character for all player names.",
},
},
code = function(data, environ)
return 'smartline.icta_player_detect("'..data.number..'", "'..data.name..'")', "~= nil"
end,
button = function(data, environ)
return "detector()"
end,
})

@ -0,0 +1,76 @@
--[[
ICTA Controller
===============
Part of the SmartLine mod
Copyright (C) 2018 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
condition.lua
]]--
local sl = smartline
-- tables with all data from condition registrations
local kvRegisteredCond = {}
-- list of keys for conditions
local aCondTypes = {}
-- list of titles for conditions
local aCondTitles = {}
--
-- API functions for condition registrations
--
function sl.icta_register_condition(key, tData)
table.insert(aCondTypes, key)
table.insert(aCondTitles, tData.title)
if kvRegisteredCond[key] ~= nil then
print("[SmartLine] Condition registration error "..key)
return
end
kvRegisteredCond[key] = tData
for _,item in ipairs(tData.formspec) do
if item.type == "textlist" then
item.tChoices = string.split(item.choices, ",")
item.num_choices = #item.tChoices
end
end
end
-- return formspec string
function sl.cond_formspec(row, kvSelect)
return sl.submenu_generate_formspec(
row, "cond", "Condition type", aCondTypes, aCondTitles, kvRegisteredCond, kvSelect)
end
-- evaluate the row condition input
-- and return new data
function sl.cond_eval_input(kvSelect, fields)
kvSelect = sl.submenu_eval_input(kvRegisteredCond, aCondTypes, aCondTitles, kvSelect, fields)
return kvSelect
end
-- return the Lua code
function sl.code_condition(kvSelect, environ)
if kvSelect and kvRegisteredCond[kvSelect.choice] then
if smartline.submenu_verify(kvRegisteredCond, kvSelect) then
return kvRegisteredCond[kvSelect.choice].code(kvSelect, environ)
end
end
return nil, nil
end
sl.icta_register_condition("default", {
title = "",
formspec = {},
code = function(data, environ) return false, false end,
button = function(data, environ) return "..." end,
})

@ -0,0 +1,463 @@
--[[
ICTA Controller
===============
Part of the SmartLine mod
Copyright (C) 2018 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
controller.lua
]]--
--
-- Helper functions
--
local function gen_table(size, val)
local tbl = {}
for idx = 1,size do
if type(val) == "table" then
tbl[idx] = table.copy(val)
else
tbl[idx] = val
end
end
return tbl
end
local function integer(s, min, max)
if s and s ~= "" and s:find("^%d+$") then
local num = tonumber(s)
if num < min then num = min end
if num > max then num = max end
return num
end
return min
end
local sOUTPUT = "Edit commands"
local Cache = {}
local FS_DATA = gen_table(smartline.NUM_RULES, {})
local function output(pos, text)
local meta = minetest.get_meta(pos)
text = meta:get_string("output") .. "\n" .. (text or "")
text = text:sub(-500,-1)
meta:set_string("output", text)
meta:set_string("formspec", smartline.formspecOutput(meta))
end
----------------- template -------------------------------
-- -- Rule 1
-- if env.blocked[1] == false and env.ticks % <cycle> == 0 then
-- env.result[1] = <check condition>
-- env.blocked[1] = env.result[1] <expected result>
-- if env.blocked[1] then
-- env.timer[1] = env.ticks + <after>
-- end
-- env.conditions[1] = env.blocked[1]
-- else
-- env.conditions[1] = false
-- end
-- if env.blocked[1] and env.timer[1] == env.ticks then
-- <action>
-- env.blocked[1] = false
-- end
-- cyclic execution
local TemplCyc = [[
-- Rule #
if env.blocked[#] == false and env.ticks %% %s == 0 then
env.result[#] = %s
env.blocked[#] = env.result[#] %s
if env.blocked[#] then
env.timer[#] = env.ticks + %s
end
env.condition[#] = env.blocked[#]
else
env.condition[#] = false
end
if env.blocked[#] and env.timer[#] == env.ticks then
%s
env.blocked[#] = false
end
]]
-- event based execution
local TemplEvt = [[
-- Rule #
if env.blocked[#] == false and env.event then
env.result[#] = %s
env.blocked[#] = env.result[#] %s
if env.blocked[#] then
env.timer[#] = env.ticks + %s
end
env.condition[#] = env.blocked[#]
else
env.condition[#] = false
end
if env.blocked[#] and env.timer[#] == env.ticks then
%s
env.blocked[#] = false
end
]]
-- generate the Lua code from the NUM_RULES rules
local function generate(pos, meta, environ)
local fs_data = minetest.deserialize(meta:get_string("fs_data")) or FS_DATA
-- chunks are compiled as vararg functions. Parameters are available via: local a, b, c = ...
local tbl = {"local env, output = ...\n"}
for idx = 1,smartline.NUM_RULES do
local cycle = integer(fs_data[idx].cycle, 0, 1000)
local cond, result = smartline.code_condition(fs_data[idx].cond, environ)
local after = integer(fs_data[idx].after, 0, 1000)
local actn = smartline.code_action(fs_data[idx].actn, environ)
-- valid rule?
if cycle and cond and after and actn then
-- add rule number
local s
if cycle == 0 then -- event?
s = string.format(TemplEvt, cond, result, after, actn)
else -- cyclic
s = string.format(TemplCyc, cycle, cond, result, after, actn)
end
-- add to list of rules
tbl[#tbl+1] = s:gsub("#", idx)
elseif cond ~= nil and actn == nil then
output(pos, "Error in action in rule "..idx)
elseif cond == nil and actn ~= nil then
output(pos, "Error in condition in rule "..idx)
end
end
return table.concat(tbl)
end
local function runtime_environ(pos)
return {
ticks = 0,
pos = pos,
timer = gen_table(8, 0),
blocked = gen_table(8, false),
result = gen_table(8, false),
condition = gen_table(8, false),
input = {}, -- node number is key
}
end
local function compile(pos, meta, number)
local gen_environ = {
meta = meta,
pos = pos,
number = number,
owner = meta:get_string("owner"),
}
local text = generate(pos, meta, gen_environ)
if text then
local code, err = loadstring(text)
if code then
Cache[number] = {
code = code,
env = runtime_environ(pos),
}
return true
else
output(pos, err)
return false
end
end
return false
end
local function execute(pos, number, event)
local code = Cache[number].code
local env = Cache[number].env
if event then
env.event = true
else
env.event = false
env.ticks = env.ticks + 1
end
local res, err = pcall(code, env, output)
if not res then
output(pos, err)
return false
end
return true
end
local function battery(pos)
local battery_pos = minetest.find_node_near(pos, 1, {"smartline:battery", "sl_controller:battery"})
if battery_pos then
local meta = minetest.get_meta(pos)
meta:set_string("battery", minetest.pos_to_string(battery_pos))
return true
end
return false
end
local function start_controller(pos, meta)
local number = meta:get_string("number")
if not battery(pos) then
meta:set_string("formspec", smartline.formspecError(meta))
return false
end
meta:set_string("output", "<press update>")
meta:set_int("cpu", 0)
if compile(pos, meta, number) then
meta:set_int("state", tubelib.RUNNING)
minetest.get_node_timer(pos):start(1)
meta:set_string("formspec", smartline.formspecOutput(meta))
meta:set_string("infotext", "Controller "..number..": running")
return true
end
return false
end
local function stop_controller(pos, meta)
local number = meta:get_string("number")
meta:set_int("state", tubelib.STOPPED)
minetest.get_node_timer(pos):stop()
meta:set_string("infotext", "Controller "..number..": stopped")
local fs_data = minetest.deserialize(meta:get_string("fs_data")) or FS_DATA
meta:set_string("formspec", smartline.formspecRules(meta, fs_data, sOUTPUT))
end
local function no_battery(pos)
local meta = minetest.get_meta(pos)
local number = meta:get_string("number")
meta:set_int("state", tubelib.STOPPED)
minetest.get_node_timer(pos):stop()
meta:set_string("infotext", "Controller "..number..": No battery")
meta:set_string("formspec", smartline.formspecError(meta))
end
local function update_battery(meta, cpu)
local pos = minetest.string_to_pos(meta:get_string("battery"))
if pos then
meta = minetest.get_meta(pos)
local content = meta:get_int("content") - cpu
if content <= 0 then
meta:set_int("content", 0)
return false
end
meta:set_int("content", content)
return true
end
end
local function on_timer(pos, elapsed)
local meta = minetest.get_meta(pos)
local t = minetest.get_us_time()
local number = meta:get_string("number")
if Cache[number] or compile(pos, meta, number) then
local res = execute(pos, number, elapsed == -1)
if res then
t = minetest.get_us_time() - t
if not update_battery(meta, t) then
no_battery(pos)
return false
end
end
--print("on_timer", t)
return res
end
return false
end
local function on_receive_fields(pos, formname, fields, player)
local meta = minetest.get_meta(pos)
local owner = meta:get_string("owner")
if not player or not player:is_player() then
return
end
local readonly = player:get_player_name() ~= owner
--print("fields", dump(fields))
if fields.quit then -- cancel button
return
end
if fields.notes then -- notes tab?
meta:set_string("notes", fields.notes)
end
if fields.go then
if not readonly then
local fs_data = minetest.deserialize(meta:get_string("fs_data")) or FS_DATA
local output = smartline.edit_command(fs_data, fields.cmnd)
stop_controller(pos, meta)
meta:set_string("formspec", smartline.formspecRules(meta, fs_data, output))
meta:set_string("fs_data", minetest.serialize(fs_data))
end
end
if fields._type_ == "main" then
if not readonly then
smartline.store_main_form_data(meta, fields)
end
local key = smartline.main_form_button_pressed(fields)
if key then
-- store data before going into sub-menu
meta:set_string("fs_old", meta:get_string("fs_data"))
meta:set_string("formspec", smartline.formspecSubMenu(meta, key))
end
elseif fields._col_ == "cond" then
smartline.cond_formspec_update(meta, fields)
elseif fields._col_ == "actn" then
smartline.actn_formspec_update(meta, fields)
end
if fields._exit_ == "ok" then -- exit from sub-menu?
if fields._button_ then
smartline.formspec_button_update(meta, fields)
end
-- simulate tab selection
fields.tab = "1"
elseif fields._cancel_ == "cancel" then -- abort from sub-menu?
-- restore old data
meta:set_string("fs_data", meta:get_string("fs_old"))
-- simulate tab selection
fields.tab = "1"
elseif fields.save == "Save" then -- abort from sub-menu?
-- store as old data
meta:set_string("fs_old", meta:get_string("fs_data"))
-- simulate tab selection
fields.tab = "1"
elseif fields.sb_help then
local evt = minetest.explode_scrollbar_event(fields.sb_help)
meta:set_string("formspec", smartline.formspecHelp(evt.value))
end
if fields.update then
meta:set_string("formspec", smartline.formspecOutput(meta))
elseif fields.clear then
meta:set_string("output", "<press update>")
meta:set_string("formspec", smartline.formspecOutput(meta))
elseif fields.tab == "1" then
local fs_data = minetest.deserialize(meta:get_string("fs_data")) or FS_DATA
meta:set_string("formspec", smartline.formspecRules(meta, fs_data, sOUTPUT))
elseif fields.tab == "2" then
meta:set_string("formspec", smartline.formspecOutput(meta))
elseif fields.tab == "3" then
meta:set_string("formspec", smartline.formspecNotes(meta))
elseif fields.tab == "4" then
meta:set_string("formspec", smartline.formspecHelp(1))
elseif fields.start == "Start" then
local environ = {
meta = meta,
pos = pos,
number = meta:get_string("number"),
owner = meta:get_string("owner"),
}
--print("CODE:", generate(pos, meta, environ))
start_controller(pos, meta)
minetest.log("action", player:get_player_name() ..
" starts the smartline_controller2 at ".. minetest.pos_to_string(pos))
elseif fields.stop == "Stop" then
stop_controller(pos, meta)
end
end
minetest.register_node("smartline:controller2", {
description = "SmartLine Controller",
inventory_image = "smartline_controller_inventory.png",
wield_image = "smartline_controller_inventory.png",
stack_max = 1,
tiles = {
-- up, down, right, left, back, front
"smartline.png",
"smartline.png",
"smartline.png",
"smartline.png",
"smartline.png",
"smartline.png^smartline_controller.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -6/32, -6/32, 14/32, 6/32, 6/32, 16/32},
},
},
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos)
local number = tubelib.add_node(pos, "smartline:controller2")
local fs_data = FS_DATA
meta:set_string("fs_data", minetest.serialize(fs_data))
meta:set_string("owner", placer:get_player_name())
meta:set_string("number", number)
meta:set_int("state", tubelib.STOPPED)
meta:set_string("formspec", smartline.formspecRules(meta, fs_data, sOUTPUT))
--meta:set_string("formspec", smartline.cond_formspec(1, 1, nil))
meta:set_string("infotext", "SmartLine Controller "..number..": stopped")
end,
on_receive_fields = on_receive_fields,
on_dig = function(pos, node, puncher, pointed_thing)
if minetest.is_protected(pos, puncher:get_player_name()) then
return
end
minetest.node_dig(pos, node, puncher, pointed_thing)
tubelib.remove_node(pos)
end,
on_timer = on_timer,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
groups = {choppy=1, cracky=1, crumbly=1},
is_ground_content = false,
sounds = default.node_sound_stone_defaults(),
})
--minetest.register_craft({
-- output = "smartline:controller2",
-- recipe = {
-- {"", "default:mese_crystal", ""},
-- {"dye:blue", "default:copper_ingot", "tubelib:wlanchip"},
-- {"", "default:mese_crystal", ""},
-- },
--})
-- write inputs from remote nodes
local function set_input(pos, own_number, rmt_number, val)
if rmt_number then
if Cache[own_number] and Cache[own_number].env.input then
Cache[own_number].env.input[rmt_number] = val
-- only two events per second
local t = minetest.get_us_time()
if not Cache[own_number].last_event or Cache[own_number].last_event < t then
minetest.after(0.01, on_timer, pos, -1)
Cache[own_number].last_event = t + 500000 -- add 500 ms
end
end
end
end
tubelib.register_node("smartline:controller2", {}, {
on_recv_message = function(pos, topic, payload)
local meta = minetest.get_meta(pos)
local number = meta:get_string("number")
if topic == "on" then
set_input(pos, number, payload, topic)
elseif topic == "off" then
set_input(pos, number, payload, topic)
elseif topic == "state" then
local state = meta:get_int("state")
return tubelib.statestring(state)
else
return "unsupported"
end
end,
})

40
smartline/icta/edit.lua Normal file

@ -0,0 +1,40 @@
--[[
ICTA Controller
===============
Copyright (C) 2018 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
edit.lua
]]--
function smartline.edit_command(fs_data, text)
local cmnd, pos1, pos2 = text:match('^(%S)%s(%d+)%s(%d+)$')
if pos2 == nil then
cmnd, pos1 = text:match('^(%S)%s(%d+)$')
end
if cmnd and pos1 and pos2 then
pos1 = math.max(1, math.min(pos1, smartline.NUM_RULES))
pos2 = math.max(1, math.min(pos2, smartline.NUM_RULES))
if cmnd == "x" then
local temp = fs_data[pos1]
fs_data[pos1] = fs_data[pos2]
fs_data[pos2] = temp
return "rows "..pos1.." and "..pos2.." exchanged"
end
if cmnd == "c" then
fs_data[pos2] = table.copy(fs_data[pos1])
return "row "..pos1.." copied to "..pos2
end
elseif cmnd == "d" and pos1 then
pos1 = math.max(1, math.min(pos1, smartline.NUM_RULES))
fs_data[pos1] = {}
return "row "..pos1.." deleted"
end
return "Invalid command '"..text.."'"
end

360
smartline/icta/formspec.lua Normal file

@ -0,0 +1,360 @@
--[[
SmartLine
=========
Part of the SmartLine mod
Copyright (C) 2018 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
formspec.lua:
controller formspecs
]]--
smartline.NUM_RULES = 8
local SIZE = "size[13,8]"
local sHELP = [[SmartLine Controller Help
Control other nodes by means of rules like:
IF <condition> THEN <action>
These rules allow to execute actions based on conditions.
Examples for conditions are:
- the Player Detector detects a player
- a button is pressed
- a node state is fault, blocked, standby,...
Actions are:
- switch on/off tubelib nodes, like lamps, machines
- send chat messages to the owner
- output a text message to the display
The controller executes all rules cyclically.
The cycle time for each rule is configurable
(1..1000 sec).
0 means, the rule will only be called, when
the controller received a command from
another node, like buttons.
Actions can be deleyed. Therefore, the
after value can be set (0..1000 sec).
Edit command examples:
- 'x 1 8' exchange rows 1 with row 8
- 'c 1 2' copy row 1 to 2
- 'd 3' delete row 3
The 'outp' tab is for debugging outputs via 'print'
The 'notes' tab for your notes.
The controller needs battery power to work.
The battery pack has to be placed near the
controller (1 node distance).
The needed battery power is directly dependent
on the CPU time the controller consumes.
For more information, see: goo.gl/fF5ap6
]]
-- to simplify the search for a pressed main form button (condition/action)
local lButtonKeys = {}
for idx = 1,smartline.NUM_RULES do
lButtonKeys[#lButtonKeys+1] = "cond"..idx
lButtonKeys[#lButtonKeys+1] = "actn"..idx
end
local function buttons(s)
return "button_exit[7.4,7.5;1.8,1;cancel;Cancel]"..
"button[9.3,7.5;1.8,1;save;Save]"..
"button[11.2,7.5;1.8,1;"..s.."]"
end
function smartline.formspecError(meta)
local running = meta:get_int("state") == tubelib.RUNNING
local cmnd = running and "stop;Stop" or "start;Start"
local init = meta:get_string("init")
init = minetest.formspec_escape(init)
return "size[4,3]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"label[0,0;No Battery?]"..
"button[1,2;1.8,1;start;Start]"
end
local function button(data)
if data then
return data.button
else
return "..."
end
end
local function formspec_rules(fs_data)
local tbl = {"field[0,0;0,0;_type_;;main]"..
"label[0.8,0;Cycle/s:]label[2.8,0;IF cond:]label[7,0;THEN action:]label[11.4,0;after/s:]"}
for idx = 1,smartline.NUM_RULES do
local ypos = idx * 0.75 - 0.4
tbl[#tbl+1] = "label[0,"..(0.2+ypos)..";"..idx.."]"
tbl[#tbl+1] = "field[0.9,"..(0.3+ypos)..";1.8,1;cycle"..idx..";;"..(fs_data[idx].cycle or "").."]"
tbl[#tbl+1] = "button[2.5,"..ypos..";4.3,1;cond"..idx..";"..button(fs_data[idx].cond).."]"
tbl[#tbl+1] = "button[6.8,"..ypos..";4.3,1;actn"..idx..";"..button(fs_data[idx].actn).."]"
tbl[#tbl+1] = "field[11.4,"..(0.3+ypos)..";1.8,1;after"..idx..";;"..(fs_data[idx].after or "").."]"
end
return table.concat(tbl)
end
function smartline.store_main_form_data(meta, fields)
local fs_data = minetest.deserialize(meta:get_string("fs_data"))
for idx = 1,smartline.NUM_RULES do
fs_data[idx].cycle = fields["cycle"..idx] or ""
fs_data[idx].after = fields["after"..idx] or "0"
end
meta:set_string("fs_data", minetest.serialize(fs_data))
end
function smartline.main_form_button_pressed(fields)
for _,key in ipairs(lButtonKeys) do
if fields[key] then
return key
end
end
return nil
end
function smartline.formspecSubMenu(meta, key)
local fs_data = minetest.deserialize(meta:get_string("fs_data"))
if key:sub(1,4) == "cond" then
local row = tonumber(key:sub(5,5))
return smartline.cond_formspec(row, fs_data[row].cond)
else
local row = tonumber(key:sub(5,5))
return smartline.actn_formspec(row, fs_data[row].actn)
end
end
function smartline.formspec_button_update(meta, fields)
local fs_data = minetest.deserialize(meta:get_string("fs_data"))
local row = tonumber(fields._row_ or 1)
print("row", row)
if fields._col_ == "cond" then
fs_data[row].cond = smartline.cond_eval_input(fs_data[row].cond, fields)
elseif fields._col_ == "actn" then
fs_data[row].actn = smartline.actn_eval_input(fs_data[row].actn, fields)
end
meta:set_string("fs_data", minetest.serialize(fs_data))
end
function smartline.cond_formspec_update(meta, fields)
local fs_data = minetest.deserialize(meta:get_string("fs_data"))
local row = tonumber(fields._row_ or 1)
fs_data[row].cond = smartline.cond_eval_input(fs_data[row].cond, fields)
meta:set_string("formspec", smartline.cond_formspec(row, fs_data[row].cond))
meta:set_string("fs_data", minetest.serialize(fs_data))
end
function smartline.actn_formspec_update(meta, fields)
local fs_data = minetest.deserialize(meta:get_string("fs_data"))
local row = tonumber(fields._row_ or 1)
fs_data[row].actn = smartline.actn_eval_input(fs_data[row].actn, fields)
meta:set_string("formspec", smartline.actn_formspec(row, fs_data[row].actn))
meta:set_string("fs_data", minetest.serialize(fs_data))
end
function smartline.formspecRules(meta, fs_data, output)
local running = meta:get_int("state") == tubelib.RUNNING
local cmnd = running and "stop;Stop" or "start;Start"
local init = meta:get_string("init")
init = minetest.formspec_escape(init)
return SIZE..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"tabheader[0,0;tab;rules,outp,notes,help;1;;true]"..
formspec_rules(fs_data)..
"label[0.2,7.0;"..output.."]"..
"field[0.3,7.8;4,1;cmnd;;<cmnd>]"..
"button[4.0,7.5;1.5,1;go;GO]"..
buttons(cmnd)
end
function smartline.formspecOutput(meta)
local running = meta:get_int("state") == tubelib.RUNNING
local cmnd = running and "stop;Stop" or "start;Start"
local output = meta:get_string("output")
output = minetest.formspec_escape(output)
return SIZE..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"tabheader[0,0;tab;rules,outp,notes,help;2;;true]"..
"textarea[0.3,0.2;13,8.3;output;Output:;"..output.."]"..
"button[7.4,7.5;1.8,1;clear;Clear]"..
"button[9.3,7.5;1.8,1;update;Update]"..
"button[11.2,7.5;1.8,1;"..cmnd.."]"
end
function smartline.formspecNotes(meta)
local running = meta:get_int("state") == tubelib.RUNNING
local cmnd = running and "stop;Stop" or "start;Start"
local notes = meta:get_string("notes")
if notes == "" then notes = "<space for your notes>" end
notes = minetest.formspec_escape(notes)
return SIZE..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"tabheader[0,0;tab;rules,outp,notes,help;3;;true]"..
"textarea[0.3,0.2;13,8.3;notes;Notepad:;"..notes.."]"..
buttons(cmnd)
end
function smartline.formspecHelp(offs)
return SIZE..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"tabheader[0,0;tab;rules,outp,notes,help;4;;true]"..
"field[0,0;0,0;_type_;;help]"..
"label[0,"..(-offs/50)..";"..sHELP.."]"..
--"label[0.2,0;test]"..
"scrollbar[12,1;0.5,7;vertical;sb_help;"..offs.."]"
end
--local function my_on_receive_fields(pos, formname, fields, player)
-- local meta = minetest.get_meta(pos)
-- local owner = meta:get_string("owner")
-- local state = meta:get_int("state")
-- if not player or not player:is_player() then
-- return
-- end
-- local fs_data = minetest.deserialize(meta:get_string("fs_data")) or {}
-- local output = ""
-- local readonly = player:get_player_name() ~= owner
-- print("fields", dump(fields))
-- -- FIRST: test if command entered?
-- if fields.ok then
-- if not readonly then
-- output = edit_command(fs_data, fields.cmnd)
-- smartline.stop_controller(pos, fs_data)
-- meta:set_string("formspec", formspec_main(tubelib.STOPPED, fs_data, output))
-- meta:set_string("fs_data", minetest.serialize(fs_data))
-- end
-- -- SECOND: eval none edit events (events based in __type__)?
-- elseif fields.help then
-- meta:set_string("formspec", formspec_help(1))
---- elseif fields.state then
---- meta:set_string("formspec", formspec_state(meta, fs_data))
---- elseif fields.update then
---- meta:set_string("formspec", formspec_state(meta, fs_data))
-- elseif fields._cancel_ then
-- fs_data = minetest.deserialize(meta:get_string("fs_old"))
-- meta:set_string("formspec", formspec_main(state, fs_data, sOUTPUT))
-- elseif fields.close then
-- meta:set_string("formspec", formspec_main(state, fs_data, sOUTPUT))
---- elseif fields.sb_help then
---- local evt = minetest.explode_scrollbar_event(fields.sb_help)
---- if evt.type == "CHG" then
---- meta:set_string("formspec", formspec_help(evt.value))
---- end
---- elseif fields.button then
---- if not readonly then
---- local number = meta:get_string("number")
---- local state = meta:get_int("state")
---- if state == tubelib.RUNNING then
---- smartline.stop_controller(pos, fs_data)
---- meta:set_string("formspec", formspec_main(tubelib.STOPPED, fs_data, sOUTPUT))
---- else
---- formspec2runtime_rule(number, owner, fs_data)
---- start_controller(pos, number, fs_data)
---- meta:set_string("formspec", formspec_main(tubelib.RUNNING, fs_data, sOUTPUT))
---- end
---- end
---- -- THIRD: evaluate edit events from sub-menus
-- elseif fields._col_ == "cond" then
-- local row = tonumber(fields._row_ or 1)
-- fs_data["cond"..row] = smartline.cond_eval_input(fs_data["cond"..row], fields)
-- meta:set_string("formspec", smartline.cond_formspec(row, fs_data["cond"..row]))
-- meta:set_string("fs_data", minetest.serialize(fs_data))
-- elseif fields._type_ == "main" then
-- fs_data = eval_formspec_main(meta, fs_data, fields, readonly)
-- meta:set_string("fs_data", minetest.serialize(fs_data))
---- elseif fields._type_ == "label" then
---- fs_data = eval_formspec_label(meta, fs_data, fields, readonly)
---- meta:set_string("fs_data", minetest.serialize(fs_data))
---- elseif fields._type_ == "cond" then
---- fs_data = eval_formspec_cond(meta, fs_data, fields, readonly)
---- meta:set_string("fs_data", minetest.serialize(fs_data))
---- elseif fields._type_ == "oprnd" then
---- fs_data = eval_formspec_oprnd(meta, fs_data, fields, readonly)
---- meta:set_string("fs_data", minetest.serialize(fs_data))
---- elseif fields._type_ == "actn" then
---- fs_data = eval_formspec_actn(meta, fs_data, fields, readonly)
---- meta:set_string("fs_data", minetest.serialize(fs_data))
---- elseif fields._type_ == "help" then
---- meta:set_string("formspec", formspec_main(state, fs_data, sOUTPUT))
---- elseif fields._type_ == "state" then
---- meta:set_string("formspec", formspec_main(state, fs_data, sOUTPUT))
-- end
-- -- FOURTH: back to main menu
-- if fields._exit_ then
-- meta:set_string("formspec", formspec_main(state, fs_data, sOUTPUT))
-- end
--end
--function smartline.on_receive_fields(pos, formname, fields, player)
-- local meta = minetest.get_meta(pos)
-- local owner = meta:get_string("owner")
-- if not player or not player:is_player() then
-- return
-- end
-- local readonly = player:get_player_name() ~= owner
-- print("fields", dump(fields))
-- if fields.cancel == nil then
-- if fields.rules then
-- --meta:set_string("rules", fields.rules)
-- meta:set_string("formspec", smartline.formspecRules(meta))
-- elseif fields.notes then
-- meta:set_string("notes", fields.notes)
-- meta:set_string("formspec", formspecNotes(meta))
-- end
-- end
-- if fields.update then
-- meta:set_string("formspec", formspecOutput(meta))
-- elseif fields.clear then
-- meta:set_string("output", "<press update>")
-- meta:set_string("formspec", formspecOutput(meta))
-- elseif fields.tab == "1" then
-- meta:set_string("formspec", smartline.formspecRules(meta))
-- elseif fields.tab == "2" then
-- meta:set_string("formspec", formspecOutput(meta))
-- elseif fields.tab == "3" then
-- meta:set_string("formspec", formspecNotes(meta))
-- elseif fields.tab == "4" then
-- meta:set_string("formspec", formspecHelp(1))
-- elseif fields.start == "Start" then
-- start_controller(pos)
-- minetest.log("action", player:get_player_name() ..
-- " starts the sl_controller at ".. minetest.pos_to_string(pos))
-- elseif fields.stop == "Stop" then
-- stop_controller(pos)
-- end
--end

197
smartline/icta/submenu.lua Normal file

@ -0,0 +1,197 @@
--[[
ICTA Controller
===============
Part of the SmartLine mod
Copyright (C) 2018 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
submenu.lua
A sub-menu control to generate a formspec sting for conditions and actions
]]--
local sl = smartline
local function index(list, x)
for idx, v in ipairs(list) do
if v == x then return idx end
end
return nil
end
-- generate the choice dependent part of the form
local function add_controls_to_table(tbl, kvDefinition, kvSelect)
local val = ""
local offs = 1.4
if kvDefinition[kvSelect.choice] then
local lControls = kvDefinition[kvSelect.choice].formspec
for idx,elem in ipairs(lControls) do
if elem.label and elem.label ~= "" then
tbl[#tbl+1] = "label[0,"..offs..";"..elem.label.."]"
offs = offs + 0.4
end
if elem.type == "numbers" or elem.type == "digits" or elem.type == "letters"
or elem.type == "ascii" then
val = kvSelect[elem.name] or elem.default
tbl[#tbl+1] = "field[0.3,"..(offs+0.2)..";8,1;"..elem.name..";;"..val.."]"
offs = offs + 1
elseif elem.type == "textlist" then
local l = elem.choices:split(",")
val = index(l, kvSelect[elem.name]) or elem.default
tbl[#tbl+1] = "dropdown[0.0,"..(offs)..";8.5,1.4;"..elem.name..";"..elem.choices..";"..val.."]"
offs = offs + 1
end
end
end
return tbl
end
local function default_data(kvDefinition, kvSelect)
local lControls = kvDefinition[kvSelect.choice].formspec
for idx,elem in ipairs(lControls) do
kvSelect[elem.name] = elem.default
end
kvSelect.button = kvDefinition[kvSelect.choice].button(kvSelect)
return kvSelect
end
-- Copy field/formspec data to the table kvSelect
-- kvDefinition: submenu formspec definition
-- kvSelect: form data
-- fields: formspec input
local function field_to_kvSelect(kvDefinition, kvSelect, fields)
local error = false
local lControls = kvDefinition[kvSelect.choice].formspec
for idx,elem in ipairs(lControls) do
if elem.type == "numbers" then
if fields[elem.name] then
if fields[elem.name]:find("^[%d ]+$") then
kvSelect[elem.name] = fields[elem.name]
else
kvSelect[elem.name] = elem.default
error = true
end
end
elseif elem.type == "digits" then -- including positions
if fields[elem.name] then
if fields[elem.name]:find("^[+%%-,%d]+$") then
kvSelect[elem.name] = fields[elem.name]
else
kvSelect[elem.name] = elem.default
error = true
end
end
elseif elem.type == "letters" then
if fields[elem.name] then
if fields[elem.name]:find("^[+-]?%a+$") then
kvSelect[elem.name] = fields[elem.name]
else
kvSelect[elem.name] = elem.default
error = true
end
end
elseif elem.type == "ascii" then
if fields[elem.name] then
kvSelect[elem.name] = fields[elem.name]
end
elseif elem.type == "textlist" then
if fields[elem.name] ~= nil then
kvSelect[elem.name] = fields[elem.name]
end
end
end
-- store user input of button text
if fields._button_ then
kvSelect._button_ = fields._button_
end
-- select button text
if error then
kvSelect.button = "invalid"
elseif kvSelect._button_ and kvSelect._button_ ~= "" then
kvSelect.button = kvSelect._button_
else
kvSelect.button = kvDefinition[kvSelect.choice].button(kvSelect)
end
return kvSelect
end
function smartline.submenu_verify(kvDefinition, kvSelect)
local error = false
local lControls = kvDefinition[kvSelect.choice].formspec
for idx,elem in ipairs(lControls) do
if elem.type == "numbers" then
if not kvSelect[elem.name]:find("^[%d ]+$") then
error = true
end
elseif elem.type == "digits" then -- including positions
if not kvSelect[elem.name]:find("^[+%%-,%d]+$") then
error = true
end
elseif elem.type == "letters" then
if not kvSelect[elem.name]:find("^[+-]?%a+$") then
error = true
end
elseif elem.type == "ascii" then
if kvSelect[elem.name] == "" or kvSelect[elem.name] == nil then
error = true
end
elseif elem.type == "textlist" then
if kvSelect[elem.name] == "" or kvSelect[elem.name] == nil then
error = true
end
end
end
return (error == false)
end
-- generate a formspec string from the given control definition
-- row, col: numbers to identify the control
-- title: Title text for the control
-- lKeys: list of keywords of selected choices according to fields
-- lChoice: list of possible choices for the control
-- kvDefinition: definitions of the choice dependent controls
-- kvSelect: data of the last selected item {choice, number, value, ...}
function smartline.submenu_generate_formspec(row, col, title, lKeys, lChoice, kvDefinition, kvSelect)
if kvSelect == nil or next(kvSelect) == nil then
kvSelect = {choice = "default"}
end
local tbl = {"size[8.2,9]"..
--default.gui_bg.. TODO
--default.gui_bg_img..
--default.gui_slots..
"field[0,0;0,0;_row_;;"..row.."]"..
"field[0,0;0,0;_col_;;"..col.."]"}
local sChoice = table.concat(lChoice, ",")
local idx = index(lKeys, kvSelect.choice) or 1
tbl[#tbl+1] = "label[0,0;"..title..":]"
tbl[#tbl+1] = "dropdown[0,0.5;8.5,1;choice;"..sChoice..";"..idx.."]"
tbl = add_controls_to_table(tbl, kvDefinition, kvSelect)
tbl[#tbl+1] = "field[0.2,8.7;4,1;_button_;Button text;"..(kvSelect._button_ or "").."]"
tbl[#tbl+1] = "button[4,8.4;2,1;_cancel_;cancel]"
tbl[#tbl+1] = "button[6,8.4;2,1;_exit_;ok]"
return table.concat(tbl)
end
-- return the selected and configured menu item based on user inputs (fields)
function smartline.submenu_eval_input(kvDefinition, lKeys, lChoice, kvSelect, fields)
-- determine selected choice
if fields.choice then
-- load with default values
local idx = index(lChoice, fields.choice) or 1
kvSelect = {choice = lKeys[idx]}
kvSelect = default_data(kvDefinition, kvSelect)
kvSelect = field_to_kvSelect(kvDefinition, kvSelect, fields)
else
-- add real data
kvSelect = field_to_kvSelect(kvDefinition, kvSelect, fields)
end
return kvSelect
end

@ -10,15 +10,29 @@
]]-- ]]--
smartline = {}
local MP = minetest.get_modpath("smartline")
if minetest.get_modpath("display_lib") and display_lib ~= nil and if minetest.get_modpath("display_lib") and display_lib ~= nil and
minetest.get_modpath("font_lib") and font_lib ~= nil then minetest.get_modpath("font_lib") and font_lib ~= nil then
dofile(minetest.get_modpath("smartline") .. "/display.lua") dofile(MP.."/display.lua")
end end
dofile(minetest.get_modpath("smartline") .. "/button.lua") dofile(MP.."/button.lua")
dofile(minetest.get_modpath("smartline") .. "/signaltower.lua") dofile(MP.."/signaltower.lua")
dofile(minetest.get_modpath("smartline") .. "/playerdetector.lua") dofile(MP.."/playerdetector.lua")
dofile(minetest.get_modpath("smartline") .. "/sequencer.lua") dofile(MP.."/sequencer.lua")
dofile(minetest.get_modpath("smartline") .. "/timer.lua") dofile(MP.."/timer.lua")
dofile(minetest.get_modpath("smartline") .. "/repeater.lua") dofile(MP.."/repeater.lua")
dofile(minetest.get_modpath("smartline") .. "/controller.lua") dofile(MP.."/controller.lua")
dofile(minetest.get_modpath("smartline") .. "/commands.lua") dofile(MP.."/commands.lua")
-- ICTA Controller
dofile(MP.."/icta/submenu.lua")
dofile(MP.."/icta/condition.lua")
dofile(MP.."/icta/action.lua")
dofile(MP.."/icta/formspec.lua")
dofile(MP.."/icta/controller.lua")
dofile(MP.."/icta/commands.lua")
dofile(MP.."/icta/edit.lua")
dofile(MP.."/icta/battery.lua")
dofile(MP.."/icta/balancer.lua")

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 B

@ -1,5 +1,6 @@
tubelib tubelib
default default
bucket
stairs? stairs?
moreores? moreores?
farming? farming?

@ -21,3 +21,4 @@ dofile(minetest.get_modpath("tubelib_addons1") .. '/funnel.lua')
dofile(minetest.get_modpath("tubelib_addons1") .. "/pusher_fast.lua") dofile(minetest.get_modpath("tubelib_addons1") .. "/pusher_fast.lua")
dofile(minetest.get_modpath("tubelib_addons1") .. "/detector.lua") dofile(minetest.get_modpath("tubelib_addons1") .. "/detector.lua")
dofile(minetest.get_modpath("tubelib_addons1") .. '/chest.lua') dofile(minetest.get_modpath("tubelib_addons1") .. '/chest.lua')
dofile(minetest.get_modpath("tubelib_addons1") .. '/liquidsampler.lua')

@ -0,0 +1,308 @@
--[[
Tubelib Addons 1
================
Copyright (C) 2017,2018 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
liquidsampler.lua
]]--
local CYCLE_TIME = 8
local function get_pos(pos, facedir, side)
local offs = {F=0, R=1, B=2, L=3, D=4, U=5}
local dst_pos = table.copy(pos)
facedir = (facedir + offs[side]) % 4
local dir = minetest.facedir_to_dir(facedir)
return vector.add(dst_pos, dir)
end
local function test_liquid(node)
local liquiddef = bucket.liquids[node.name]
if liquiddef ~= nil and liquiddef.itemname ~= nil and
node.name == liquiddef.source then
return liquiddef.itemname
end
end
local function sample_liquid(pos, meta)
local giving_back = test_liquid(minetest.get_node(pos))
if giving_back then
local inv = meta:get_inventory()
if inv:room_for_item("dst", ItemStack(giving_back)) and
inv:contains_item("src", ItemStack("bucket:bucket_empty")) then
minetest.remove_node(pos)
inv:remove_item("src", ItemStack("bucket:bucket_empty"))
inv:add_item("dst", ItemStack(giving_back))
return true -- success
else
return nil -- standby
end
else
return false -- fault
end
end
local function formspec(meta, state)
return "size[9,8.5]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"list[context;src;0,0;1,4;]"..
"image[0,0;1,1;bucket.png]"..
"image[1,1;1,1;tubelib_gui_arrow.png]"..
"image_button[1,3;1,1;".. tubelib.state_button(state) ..";button;]"..
"list[context;dst;2,0;7,4;]"..
"list[current_player;main;0.5,4.5;8,4;]"..
"listring[current_player;main]"..
"listring[context;src]" ..
"listring[current_player;main]"..
"listring[context;dst]" ..
"listring[current_player;main]"
end
local function switch_on(pos, node)
local meta = minetest.get_meta(pos)
local number = meta:get_string("number")
meta:set_int("running", tubelib.STATE_RUNNING)
meta:set_string("infotext", "Liquid Sampler "..number..": running")
meta:set_string("formspec", formspec(meta, tubelib.RUNNING))
node.name = "tubelib_addons1:liquidsampler_active"
minetest.swap_node(pos, node)
minetest.get_node_timer(pos):start(CYCLE_TIME)
return false
end
local function switch_off(pos, node)
local meta = minetest.get_meta(pos)
local number = meta:get_string("number")
meta:set_int("running", tubelib.STATE_STOPPED)
meta:set_string("infotext", "Liquid Sampler "..number..": stopped")
meta:set_string("formspec", formspec(meta, tubelib.STOPPED))
node.name = "tubelib_addons1:liquidsampler"
minetest.swap_node(pos, node)
minetest.get_node_timer(pos):stop()
return false
end
local function goto_fault(pos, node)
local meta = minetest.get_meta(pos)
local number = meta:get_string("number")
meta:set_int("running", tubelib.STATE_FAULT)
meta:set_string("infotext", "Liquid Sampler "..number..": fault")
meta:set_string("formspec", formspec(meta, tubelib.FAULT))
node.name = "tubelib_addons1:liquidsampler"
minetest.swap_node(pos, node)
minetest.get_node_timer(pos):start(20)
return false
end
local function goto_standby(pos, node)
local meta = minetest.get_meta(pos)
local number = meta:get_string("number")
meta:set_int("running", tubelib.STATE_STANDBY)
meta:set_string("infotext", "Liquid Sampler "..number..": standby")
meta:set_string("formspec", formspec(meta, tubelib.STANDBY))
node.name = "tubelib_addons1:liquidsampler"
minetest.swap_node(pos, node)
minetest.get_node_timer(pos):start(20)
return false
end
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return stack:get_count()
end
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return stack:get_count()
end
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return count
end
local function on_receive_fields(pos, formname, fields, sender)
if minetest.is_protected(pos, sender:get_player_name()) then
return
end
local meta = minetest.get_meta(pos)
local node = minetest.get_node(pos)
local running = meta:get_int("running")
if fields.button ~= nil then
if running == tubelib.STATE_RUNNING then
switch_off(pos, node)
meta:set_int("running", tubelib.STATE_STOPPED)
else
meta:set_int("running", tubelib.STATE_RUNNING)
switch_on(pos, node)
end
end
end
local function keep_running(pos, elapsed)
local meta = minetest.get_meta(pos)
local running = meta:get_int("running")
local water_pos = minetest.string_to_pos(meta:get_string("water_pos"))
local res = sample_liquid(water_pos, meta)
if res == nil then
local node = minetest.get_node(pos)
return goto_standby(pos, node)
elseif res == true then
if running <= 0 then
local node = minetest.get_node(pos)
return switch_on(pos, node)
end
elseif res == false then
if running > 0 then
local node = minetest.get_node(pos)
return goto_fault(pos, node)
end
end
meta:set_int("running", running)
return true
end
minetest.register_node("tubelib_addons1:liquidsampler", {
description = "Liquid Sampler",
tiles = {
-- up, down, right, left, back, front
'tubelib_front.png',
'tubelib_front.png',
'tubelib_addons1_liquidsampler.png',
'tubelib_addons1_liquidsampler_passive.png',
'tubelib_addons1_liquidsampler.png',
'tubelib_addons1_liquidsampler.png',
},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
inv:set_size("src", 4)
inv:set_size("dst", 28)
end,
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos)
meta:set_string("player_name", placer:get_player_name())
local number = tubelib.add_node(pos, "tubelib_addons1:liquidsampler")
meta:set_string("number", number)
local node = minetest.get_node(pos)
local water_pos = get_pos(pos, node.param2, "L")
meta:set_string("water_pos", minetest.pos_to_string(water_pos))
switch_off(pos, node)
end,
on_receive_fields = on_receive_fields,
after_dig_node = function(pos)
tubelib.remove_node(pos)
end,
on_timer = keep_running,
on_rotate = screwdriver.disallow,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
})
minetest.register_node("tubelib_addons1:liquidsampler_active", {
description = "Liquid Sampler",
tiles = {
-- up, down, right, left, back, front
'tubelib_front.png',
'tubelib_front.png',
'tubelib_addons1_liquidsampler.png',
{
image = "tubelib_addons1_liquidsampler_active.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 2,
},
},
'tubelib_addons1_liquidsampler.png',
'tubelib_addons1_liquidsampler.png',
},
on_receive_fields = on_receive_fields,
on_timer = keep_running,
on_rotate = screwdriver.disallow,
after_dig_node = function(pos)
tubelib.remove_node(pos)
end,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
groups = {crumbly=0, not_in_creative_inventory=1},
is_ground_content = false,
drop = "tubelib_addons1:liquidsampler",
sounds = default.node_sound_wood_defaults(),
})
minetest.register_craft({
output = "tubelib_addons1:liquidsampler",
recipe = {
{"group:wood", "default:steel_ingot", "group:wood"},
{"default:mese_crystal", "bucket:bucket_empty", "tubelib:tube1"},
{"group:wood", "default:steel_ingot", "group:wood"},
},
})
--------------------------------------------------------------- tubelib
tubelib.register_node("tubelib_addons1:liquidsampler", {"tubelib_addons1:liquidsampler_active"}, {
on_pull_item = function(pos, side)
local meta = minetest.get_meta(pos)
return tubelib.get_item(meta, "dst")
end,
on_push_item = function(pos, side, item)
local meta = minetest.get_meta(pos)
minetest.get_node_timer(pos):start(CYCLE_TIME)
return tubelib.put_item(meta, "src", item)
end,
on_unpull_item = function(pos, side, item)
local meta = minetest.get_meta(pos)
return tubelib.put_item(meta, "dst", item)
end,
on_recv_message = function(pos, topic, payload)
local node = minetest.get_node(pos)
if topic == "on" then
return switch_on(pos, node)
elseif topic == "off" then
return switch_off(pos, node)
elseif topic == "state" then
local meta = minetest.get_meta(pos)
local running = meta:get_int("running") or tubelib.STATE_STOPPED
return tubelib.statestring(running)
else
return "not supported"
end
end,
})
--------------------------------------------------------------- tubelib

@ -275,7 +275,7 @@ local function on_receive_fields(pos, formname, fields, player)
local running = meta:get_int("running") or STOP_STATE local running = meta:get_int("running") or STOP_STATE
if fields.button ~= nil then if fields.button ~= nil then
if running > STOP_STATE or running == FAULT_STATE then if running > STOP_STATE then
stop_the_machine(pos) stop_the_machine(pos)
else else
start_the_machine(pos) start_the_machine(pos)

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 B