mods that hopefully work for a TechAge-based pack
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
ICTA Controller - Action Registration
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
local logic = techage.logic
|
||||
|
||||
|
||||
|
||||
|
||||
-- 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 techage.icta_register_action(key, tData)
|
||||
table.insert(aActnTypes, key)
|
||||
table.insert(aActnTitles, tData.title)
|
||||
tData.__idx__ = #aActnTypes
|
||||
if kvRegisteredActn[key] ~= nil then
|
||||
print("[Techage] 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 techage.actn_formspec(row, kvSelect)
|
||||
return techage.submenu_generate_formspec(
|
||||
row, "actn", "Action type", aActnTypes, aActnTitles, kvRegisteredActn, kvSelect)
|
||||
end
|
||||
|
||||
-- evaluate the row action input
|
||||
-- and return new data
|
||||
function techage.actn_eval_input(kvSelect, fields)
|
||||
kvSelect = techage.submenu_eval_input(kvRegisteredActn, aActnTypes, aActnTitles, kvSelect, fields)
|
||||
return kvSelect
|
||||
end
|
||||
|
||||
|
||||
-- return the Lua code
|
||||
function techage.code_action(kvSelect, environ)
|
||||
if kvSelect and kvRegisteredActn[kvSelect.choice] then
|
||||
if techage.submenu_verify(environ.owner, kvRegisteredActn, kvSelect) then
|
||||
return kvRegisteredActn[kvSelect.choice].code(kvSelect, environ)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
techage.icta_register_action("default", {
|
||||
title = "",
|
||||
formspec = {},
|
||||
code = function(data, environ) return false, false end,
|
||||
button = function(data, environ) return "..." end,
|
||||
})
|
||||
|
||||
techage.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 = "Use a '*' character as reference to any\ncondition state",
|
||||
},
|
||||
},
|
||||
button = function(data, environ)
|
||||
return 'print("'..data.text:sub(1,12)..'")'
|
||||
end,
|
||||
code = function(data, environ)
|
||||
return function(env, output, idx)
|
||||
local text = string.gsub(data.text, "*", tostring(env.result[idx]))
|
||||
output(env.pos, text)
|
||||
end
|
||||
end,
|
||||
})
|
||||
@@ -0,0 +1,187 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2022 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
ICTA Controller - Battery
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
local BATTERY_CAPACITY = 10000000
|
||||
|
||||
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 number = meta:get_string("node_number")
|
||||
local percent = calc_percent(meta:get_int("content"))
|
||||
meta:set_string("infotext", S("Battery").." "..number..": "..percent.." %")
|
||||
if percent == 0 then
|
||||
local node = minetest.get_node(pos)
|
||||
node.name = "techage:ta4_battery_empty"
|
||||
minetest.swap_node(pos, node)
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
minetest.register_alias("techage:ta4_battery75", "techage:ta4_battery")
|
||||
minetest.register_alias("techage:ta4_battery50", "techage:ta4_battery")
|
||||
minetest.register_alias("techage:ta4_battery25", "techage:ta4_battery")
|
||||
|
||||
minetest.register_node("techage:ta4_battery", {
|
||||
description = S("Battery"),
|
||||
inventory_image = 'techage_battery_inventory.png',
|
||||
wield_image = 'techage_battery_inventory.png',
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_smartline.png",
|
||||
"techage_smartline.png",
|
||||
"techage_smartline.png",
|
||||
"techage_smartline.png",
|
||||
"techage_smartline.png",
|
||||
"techage_smartline.png^techage_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, itemstack)
|
||||
local content = BATTERY_CAPACITY
|
||||
if itemstack then
|
||||
local stack_meta = itemstack:get_meta()
|
||||
if stack_meta then
|
||||
-- This ensures that dug batteries of the old system are considered full.
|
||||
local string_content = stack_meta:get_string("content")
|
||||
if string_content ~= "" then
|
||||
-- Batteries dug in the new system are handled correctly.
|
||||
content = techage.in_range(stack_meta:get_int("content"), 0, BATTERY_CAPACITY)
|
||||
end
|
||||
end
|
||||
end
|
||||
M(pos):set_int("content", content)
|
||||
local number = techage.add_node(pos, "techage:ta4_battery")
|
||||
M(pos):set_string("node_number", number)
|
||||
on_timer(pos, 1)
|
||||
minetest.get_node_timer(pos):start(30)
|
||||
end,
|
||||
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
techage.remove_node(pos, oldnode, oldmetadata)
|
||||
end,
|
||||
|
||||
on_timer = on_timer,
|
||||
|
||||
preserve_metadata = function(pos, oldnode, oldmetadata, drops)
|
||||
local content = M(pos):get_int("content")
|
||||
|
||||
local meta = drops[1]:get_meta()
|
||||
meta:set_int("content", content)
|
||||
local percent = calc_percent(content)
|
||||
local text = S("Battery").." ("..percent.." %)"
|
||||
meta:set_string("description", text)
|
||||
end,
|
||||
|
||||
paramtype = "light",
|
||||
use_texture_alpha = techage.CLIP,
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
groups = {choppy=1, cracky=1, crumbly=1},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_node("techage:ta4_battery_empty", {
|
||||
description = S("Battery"),
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_smartline.png",
|
||||
"techage_smartline.png",
|
||||
"techage_smartline.png",
|
||||
"techage_smartline.png",
|
||||
"techage_smartline.png",
|
||||
"techage_smartline.png^techage_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",
|
||||
use_texture_alpha = techage.CLIP,
|
||||
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 = "techage:ta4_battery 2",
|
||||
recipe = {
|
||||
{"", "moreores:silver_ingot", ""},
|
||||
{"", "default:copper_ingot", ""},
|
||||
{"", "moreores:silver_ingot", ""},
|
||||
}
|
||||
})
|
||||
else
|
||||
minetest.register_craft({
|
||||
output = "techage:ta4_battery 2",
|
||||
recipe = {
|
||||
{"", "default:tin_ingot", ""},
|
||||
{"", "default:copper_ingot", ""},
|
||||
{"", "default:tin_ingot", ""},
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
techage.register_node({"techage:ta4_battery"}, {
|
||||
on_node_load = function(pos)
|
||||
minetest.get_node_timer(pos):start(30)
|
||||
end,
|
||||
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
if topic == "load" then
|
||||
local meta = minetest.get_meta(pos)
|
||||
return calc_percent(meta:get_int("content"))
|
||||
else
|
||||
return "unsupported"
|
||||
end
|
||||
end,
|
||||
on_beduino_request_data = function(pos, src, topic, payload)
|
||||
if topic == 134 then
|
||||
local meta = minetest.get_meta(pos)
|
||||
return 0, {calc_percent(meta:get_int("content"))}
|
||||
else
|
||||
return 2, ""
|
||||
end
|
||||
end,
|
||||
})
|
||||
@@ -0,0 +1,958 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
ICTA Controller - Register all controller commands
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
local logic = techage.logic
|
||||
|
||||
function techage.compare(op1, op2, method)
|
||||
if method == "is" then
|
||||
return op1 == op2
|
||||
elseif method == "is not" then
|
||||
return op1 ~= op2
|
||||
elseif method == "greater" then
|
||||
return op1 > op2
|
||||
elseif method == "less" then
|
||||
return op1 < op2
|
||||
end
|
||||
end
|
||||
|
||||
function techage.fmt_number(num)
|
||||
local mtch = num:match('^(%d+).*')
|
||||
if mtch and num ~= mtch then
|
||||
return mtch.."..."
|
||||
end
|
||||
return num
|
||||
end
|
||||
|
||||
|
||||
techage.icta_register_condition("initial", {
|
||||
title = "initial",
|
||||
formspec = {
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "Condition is true only after\ncontroller start.",
|
||||
},
|
||||
},
|
||||
-- Return two chunks of executable Lua code for the controller, according:
|
||||
-- return <read condition>, <expected result>
|
||||
code = function(data, environ)
|
||||
local condition = function(env, idx)
|
||||
return env.ticks
|
||||
end
|
||||
local result = function(val)
|
||||
return val == 1
|
||||
end
|
||||
return condition, result
|
||||
end,
|
||||
button = function(data, environ) return "Initial after start" end,
|
||||
})
|
||||
|
||||
techage.icta_register_condition("true", {
|
||||
title = "true",
|
||||
formspec = {
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "Condition is always true.",
|
||||
},
|
||||
},
|
||||
code = function(data, environ)
|
||||
local condition = function(env, idx)
|
||||
return true
|
||||
end
|
||||
local result = function(val)
|
||||
return val == true
|
||||
end
|
||||
return condition, result
|
||||
end,
|
||||
button = function(data, environ) return "true" end,
|
||||
})
|
||||
|
||||
techage.icta_register_condition("condition", {
|
||||
title = "condition",
|
||||
formspec = {
|
||||
{
|
||||
type = "textlist",
|
||||
name = "condition",
|
||||
label = "condition row number",
|
||||
choices = "1,2,3,4,5,6,7,8",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "operand",
|
||||
label = "condition",
|
||||
choices = "was true, was not true",
|
||||
default = "was true",
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "Used to execute two or more\nactions based on one condition.",
|
||||
},
|
||||
},
|
||||
code = function(data, environ)
|
||||
local condition = function(env, idx)
|
||||
local index = data.condition:byte(-1) - 0x30
|
||||
return env.condition[index]
|
||||
end
|
||||
local result = function(val)
|
||||
return val == (data.operand == "was true")
|
||||
end
|
||||
return condition, result
|
||||
end,
|
||||
button = function(data, environ) return "cond("..data.condition:sub(-1,-1)..","..data.operand..")" end,
|
||||
})
|
||||
|
||||
techage.icta_register_condition("input", {
|
||||
title = "inputs",
|
||||
formspec = {
|
||||
{
|
||||
type = "digits",
|
||||
name = "number",
|
||||
label = "block number",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "operand",
|
||||
choices = "is,is not",
|
||||
default = "is",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "value",
|
||||
choices = "on,off,invalid",
|
||||
default = "on",
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "An input is only available,\nif a block sends on/off\ncommands to the controller.",
|
||||
},
|
||||
},
|
||||
button = function(data, environ) -- default button label
|
||||
return 'inp('..techage.fmt_number(data.number)..','..data.operand.." "..data.value..')'
|
||||
end,
|
||||
code = function(data, environ)
|
||||
local condition = function(env, idx)
|
||||
return env.input[data.number]
|
||||
end
|
||||
local result = function(val)
|
||||
return techage.compare(val, data.value or "invalid", data.operand)
|
||||
end
|
||||
return condition, result
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_condition("state", {
|
||||
title = "read block state",
|
||||
formspec = {
|
||||
{
|
||||
type = "number",
|
||||
name = "number",
|
||||
label = "block number",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "operand",
|
||||
label = "",
|
||||
choices = "is,is not",
|
||||
default = "is",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "value",
|
||||
label = "",
|
||||
choices = "stopped,running,standby,blocked,nopower,fault,unloaded,invalid,on,off,empty,loaded,loading,full",
|
||||
default = "stopped",
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "Read the state of a TA3/TA4 machine.\n",
|
||||
},
|
||||
},
|
||||
button = function(data, environ) -- default button label
|
||||
return 'sts('..techage.fmt_number(data.number)..","..data.operand..' '..data.value..')'
|
||||
end,
|
||||
code = function(data, environ)
|
||||
local condition = function(env, idx)
|
||||
return techage.send_single(environ.number, data.number, "state")
|
||||
end
|
||||
local result = function(val)
|
||||
return techage.compare(val, data.value or "invalid", data.operand)
|
||||
end
|
||||
return condition, result
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_condition("fuel", {
|
||||
title = "read amount of fuel",
|
||||
formspec = {
|
||||
{
|
||||
type = "number",
|
||||
name = "number",
|
||||
label = "block number",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "operand",
|
||||
label = "",
|
||||
choices = "greater,less",
|
||||
default = "greater",
|
||||
},
|
||||
{
|
||||
type = "digits",
|
||||
name = "value",
|
||||
label = "than",
|
||||
default = ""
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "Read and evaluate the fuel value\nof a fuel consuming block.",
|
||||
},
|
||||
},
|
||||
button = function(data, environ)
|
||||
return 'fuel('..techage.fmt_number(data.number)..","..data.operand..' '..data.value..')'
|
||||
end,
|
||||
code = function(data, environ)
|
||||
local condition = function(env, idx)
|
||||
return techage.send_single(environ.number, data.number, "fuel")
|
||||
end
|
||||
local result = function(val)
|
||||
return techage.compare(val, tonumber(data.value) or 0, data.operand)
|
||||
end
|
||||
return condition, result
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_condition("load", {
|
||||
title = "read power/liquid load",
|
||||
formspec = {
|
||||
{
|
||||
type = "number",
|
||||
name = "number",
|
||||
label = "block number",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "operand",
|
||||
label = "",
|
||||
choices = "greater,less",
|
||||
default = "greater",
|
||||
},
|
||||
{
|
||||
type = "digits",
|
||||
name = "value",
|
||||
label = "than",
|
||||
default = ""
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "Read and evaluate the load (0..100)\nof a tank/storage block.",
|
||||
},
|
||||
},
|
||||
button = function(data, environ)
|
||||
return 'load('..techage.fmt_number(data.number)..","..data.operand..' '..data.value..')'
|
||||
end,
|
||||
code = function(data, environ)
|
||||
local condition = function(env, idx)
|
||||
return techage.send_single(environ.number, data.number, "load")
|
||||
end
|
||||
local result = function(val)
|
||||
return techage.compare(val, tonumber(data.value) or 0, data.operand)
|
||||
end
|
||||
return condition, result
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_condition("depth", {
|
||||
title = "read quarry depth",
|
||||
formspec = {
|
||||
{
|
||||
type = "number",
|
||||
name = "number",
|
||||
label = "quarry number",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "operand",
|
||||
label = "",
|
||||
choices = "greater,less",
|
||||
default = "greater",
|
||||
},
|
||||
{
|
||||
type = "digits",
|
||||
name = "value",
|
||||
label = "than",
|
||||
default = ""
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "Read and evaluate the current\ndepth of a quarry block.",
|
||||
},
|
||||
},
|
||||
button = function(data, environ)
|
||||
return 'depth('..techage.fmt_number(data.number)..","..data.operand..' '..data.value..')'
|
||||
end,
|
||||
code = function(data, environ)
|
||||
local condition = function(env, idx)
|
||||
return techage.send_single(environ.number, data.number, "depth")
|
||||
end
|
||||
local result = function(val)
|
||||
return techage.compare(val, tonumber(data.value) or 0, data.operand)
|
||||
end
|
||||
return condition, result
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_condition("delivered", {
|
||||
title = "read delivered power",
|
||||
formspec = {
|
||||
{
|
||||
type = "number",
|
||||
name = "number",
|
||||
label = "block number",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "operand",
|
||||
label = "",
|
||||
choices = "greater,less",
|
||||
default = "greater",
|
||||
},
|
||||
{
|
||||
type = "digits",
|
||||
name = "value",
|
||||
label = "than",
|
||||
default = ""
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "Read and evaluate the delivered\npower of a generator block.\nPower consuming blocks like accus\ncould also provide a negative value.",
|
||||
},
|
||||
},
|
||||
button = function(data, environ)
|
||||
return 'deliv('..techage.fmt_number(data.number)..","..data.operand..' '..data.value..')'
|
||||
end,
|
||||
code = function(data, environ)
|
||||
local condition = function(env, idx)
|
||||
return techage.send_single(environ.number, data.number, "delivered")
|
||||
end
|
||||
local result = function(val)
|
||||
return techage.compare(val, tonumber(data.value) or 0, data.operand)
|
||||
end
|
||||
return condition, result
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_condition("chest", {
|
||||
title = "read chest state",
|
||||
formspec = {
|
||||
{
|
||||
type = "number",
|
||||
name = "number",
|
||||
label = "chest number",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "operand",
|
||||
label = "",
|
||||
choices = "is,is not",
|
||||
default = "is",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "value",
|
||||
label = "",
|
||||
choices = "empty,loaded,full,invalid",
|
||||
default = "empty",
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "Read the state from a Techage chest\n"..
|
||||
"and other similar blocks.",
|
||||
},
|
||||
},
|
||||
button = function(data, environ) -- default button label
|
||||
return 'chest('..techage.fmt_number(data.number)..","..data.operand..' '..data.value..')'
|
||||
end,
|
||||
code = function(data, environ)
|
||||
local condition = function(env, idx)
|
||||
return techage.send_single(environ.number, data.number, "state")
|
||||
end
|
||||
local result = function(val)
|
||||
return techage.compare(val, data.value or "invalid", data.operand)
|
||||
end
|
||||
return condition, result
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_condition("signaltower", {
|
||||
title = "read Signal Tower state",
|
||||
formspec = {
|
||||
{
|
||||
type = "number",
|
||||
name = "number",
|
||||
label = "Signal Tower number",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "operand",
|
||||
choices = "is,is not",
|
||||
default = "is",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "value",
|
||||
choices = "off,green,amber,red,invalid",
|
||||
default = "off",
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "Read the color state\nof a Signal Tower.",
|
||||
},
|
||||
},
|
||||
button = function(data, environ) -- default button label
|
||||
return 'tower('..techage.fmt_number(data.number)..","..data.operand..' '..data.value..')'
|
||||
end,
|
||||
code = function(data, environ)
|
||||
local condition = function(env, idx)
|
||||
return techage.send_single(environ.number, data.number, "state")
|
||||
end
|
||||
local result = function(val)
|
||||
return techage.compare(val, data.value or "invalid", data.operand)
|
||||
end
|
||||
return condition, result
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_action("signaltower", {
|
||||
title = "TA4 Signal Tower",
|
||||
formspec = {
|
||||
{
|
||||
type = "numbers",
|
||||
name = "number",
|
||||
label = "Signal Tower number",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "value",
|
||||
label = "lamp color",
|
||||
choices = "off,green,amber,red",
|
||||
default = "red",
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "Turn on/off a Signal Tower lamp.",
|
||||
},
|
||||
},
|
||||
button = function(data, environ)
|
||||
return 'tower('..techage.fmt_number(data.number)..","..data.value..')'
|
||||
end,
|
||||
code = function(data, environ)
|
||||
return function(env, output, idx)
|
||||
techage.send_multi(environ.number, data.number, data.value)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_action("signallamp", {
|
||||
title = "TA4 Signal Lamp",
|
||||
formspec = {
|
||||
{
|
||||
type = "numbers",
|
||||
name = "number",
|
||||
label = "Signal Tower number",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "payload",
|
||||
label = "lamp number",
|
||||
choices = "1,2,3,4",
|
||||
default = "1",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "value",
|
||||
label = "lamp color",
|
||||
choices = "off,green,amber,red",
|
||||
default = "red",
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "Turn on/off a Signal Tower lamp.",
|
||||
},
|
||||
},
|
||||
button = function(data, environ)
|
||||
return 'tower('..techage.fmt_number(data.number)..","..data.payload..","..data.value..')'
|
||||
end,
|
||||
code = function(data, environ)
|
||||
return function(env, output, idx)
|
||||
techage.send_multi(environ.number, data.number, data.value, tonumber(data.payload))
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_action("switch", {
|
||||
title = "turn block on/off",
|
||||
formspec = {
|
||||
{
|
||||
type = "numbers",
|
||||
name = "number",
|
||||
label = "block number(s)",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "value",
|
||||
label = "state",
|
||||
choices = "on,off",
|
||||
default = "on",
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "Used for lamps, machines, gates,...",
|
||||
},
|
||||
},
|
||||
button = function(data, environ)
|
||||
return 'turn('..techage.fmt_number(data.number)..","..data.value..')'
|
||||
end,
|
||||
code = function(data, environ)
|
||||
return function(env, output, idx)
|
||||
techage.send_multi(environ.number, data.number, data.value)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_action("display", {
|
||||
title = "Display: overwrite one line",
|
||||
formspec = {
|
||||
{
|
||||
type = "numbers",
|
||||
name = "number",
|
||||
label = "Display number",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "row",
|
||||
label = "Display line",
|
||||
choices = "1,2,3,4,5",
|
||||
default = "1",
|
||||
},
|
||||
{
|
||||
type = "ascii",
|
||||
name = "text",
|
||||
label = "text",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "Use a '*' character as reference\nto any condition result",
|
||||
},
|
||||
},
|
||||
code = function(data, environ)
|
||||
return function(env, output, idx)
|
||||
local text = string.gsub(data.text, "*", tostring(env.result[idx]))
|
||||
local payload = safer_lua.Store()
|
||||
payload.set("row", data.row)
|
||||
payload.set("str", text)
|
||||
techage.send_multi(environ.number, data.number, "set", payload)
|
||||
end
|
||||
end,
|
||||
button = function(data, environ)
|
||||
return "lcd("..techage.fmt_number(data.number)..","..data.row..',"'..data.text..'")'
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_action("cleardisplay", {
|
||||
title = "Display: Clear screen",
|
||||
formspec = {
|
||||
{
|
||||
type = "number",
|
||||
name = "number",
|
||||
label = "Display number",
|
||||
default = "",
|
||||
},
|
||||
},
|
||||
code = function(data, environ)
|
||||
return function(env, output, idx)
|
||||
techage.send_multi(environ.number, data.number, "clear")
|
||||
end
|
||||
end,
|
||||
button = function(data, environ)
|
||||
return "clear lcd("..techage.fmt_number(data.number)..")"
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_action("chat", {
|
||||
title = "send chat message",
|
||||
formspec = {
|
||||
{
|
||||
type = "ascii",
|
||||
name = "text",
|
||||
label = "message",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "The chat message is send to the\nController owner, only.",
|
||||
},
|
||||
},
|
||||
code = function(data, environ)
|
||||
return function(env, output, idx)
|
||||
minetest.chat_send_player(environ.owner, "[TA4 ICTA Controller] "..data.text)
|
||||
end
|
||||
end,
|
||||
button = function(data, environ)
|
||||
return 'chat("'..data.text:sub(1,12)..'")'
|
||||
end,
|
||||
})
|
||||
|
||||
function techage.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
|
||||
|
||||
techage.icta_register_action("door", {
|
||||
title = "open/close door",
|
||||
formspec = {
|
||||
{
|
||||
type = "digits",
|
||||
name = "pos",
|
||||
label = "door position like: 123,7,-1200",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "door_state",
|
||||
label = "door state",
|
||||
choices = "open,close",
|
||||
default = "open",
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "For standard doors like the Steel Doors.\n"..
|
||||
"Use the Techage Info Tool to\neasily determine a door position.",
|
||||
},
|
||||
},
|
||||
code = function(data, environ)
|
||||
return function(env, output, idx)
|
||||
techage.icta_door_toggle(data.pos, environ.owner, data.door_state)
|
||||
end
|
||||
end,
|
||||
button = function(data, environ)
|
||||
return 'door("'..data.pos..'",'..data.door_state..")"
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_action("move", {
|
||||
title = "TA4 Move Controller",
|
||||
formspec = {
|
||||
{
|
||||
type = "number",
|
||||
name = "number",
|
||||
label = "block number",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "cmnd",
|
||||
label = "command",
|
||||
choices = "a2b,b2a,move",
|
||||
default = "a2b",
|
||||
},
|
||||
},
|
||||
button = function(data, environ) -- default button label
|
||||
return 'move('..techage.fmt_number(data.number)..","..data.cmnd..')'
|
||||
end,
|
||||
code = function(data, environ)
|
||||
return function(env, output, idx)
|
||||
return techage.send_single(environ.number, data.number, data.cmnd)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_action("turn", {
|
||||
title = "TA4 Turn Controller",
|
||||
formspec = {
|
||||
{
|
||||
type = "number",
|
||||
name = "number",
|
||||
label = "block number",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "cmnd",
|
||||
label = "command",
|
||||
choices = "left,right,uturn",
|
||||
default = "left",
|
||||
},
|
||||
},
|
||||
button = function(data, environ) -- default button label
|
||||
return 'move('..techage.fmt_number(data.number)..","..data.cmnd..')'
|
||||
end,
|
||||
code = function(data, environ)
|
||||
return function(env, output, idx)
|
||||
return techage.send_single(environ.number, data.number, data.cmnd)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_action("goto", {
|
||||
title = "TA4 Sequencer",
|
||||
formspec = {
|
||||
{
|
||||
type = "number",
|
||||
name = "number",
|
||||
label = "block number",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "cmnd",
|
||||
label = "command",
|
||||
choices = "goto,stop",
|
||||
default = "left",
|
||||
},
|
||||
{
|
||||
type = "number",
|
||||
name = "slot",
|
||||
label = "time slot",
|
||||
default = "1",
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "The 'stop' command needs no time slot.",
|
||||
},
|
||||
},
|
||||
button = function(data, environ) -- default button label
|
||||
return data.cmnd..'('..techage.fmt_number(data.number)..","..data.slot..')'
|
||||
end,
|
||||
code = function(data, environ)
|
||||
return function(env, output, idx)
|
||||
return techage.send_single(environ.number, data.number, data.cmnd, tonumber(data.slot or "1") or 1)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
function techage.icta_player_detect(own_num, number, name)
|
||||
local state = techage.send_single(own_num, number, "name", nil)
|
||||
if state ~= "" then
|
||||
if name == "*" or string.find(name, state) then
|
||||
return state
|
||||
end
|
||||
elseif name == "-" then
|
||||
return state
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
techage.icta_register_condition("playerdetector", {
|
||||
title = "read Player Detector",
|
||||
formspec = {
|
||||
{
|
||||
type = "number",
|
||||
name = "number",
|
||||
label = "Player Detector number",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "ascii",
|
||||
name = "name",
|
||||
label = "player name(s) or * for all",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "Read and check the name\nof a Player Detector.\nUse a '*' character for all player names.\n Use a '-' character for no player.",
|
||||
},
|
||||
},
|
||||
|
||||
code = function(data, environ)
|
||||
local condition = function(env, idx)
|
||||
return techage.icta_player_detect(environ.number, data.number, data.name)
|
||||
end
|
||||
local result = function(val)
|
||||
return val ~= nil
|
||||
end
|
||||
return condition, result
|
||||
end,
|
||||
button = function(data, environ)
|
||||
return "detector("..techage.fmt_number(data.number)..","..data.name:sub(1,8)..")"
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_action("set_filter", {
|
||||
title = "turn Distributor filter on/off",
|
||||
formspec = {
|
||||
{
|
||||
type = "number",
|
||||
name = "number",
|
||||
label = "distri number",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "color",
|
||||
label = "filter port",
|
||||
choices = "red,green,blue,yellow",
|
||||
default = "red",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "value",
|
||||
label = "state",
|
||||
choices = "on,off",
|
||||
default = "on",
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "turn Distributor filter port on/off\n",
|
||||
},
|
||||
},
|
||||
button = function(data, environ)
|
||||
return 'turn('..techage.fmt_number(data.number)..","..data.color..","..data.value..')'
|
||||
end,
|
||||
code = function(data, environ)
|
||||
return function(env, output, idx)
|
||||
local payload = data.color.."="..data.value
|
||||
techage.send_single(environ.number, data.number, "port", payload)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_condition("get_filter", {
|
||||
title = "read state of a Distributor filter slot",
|
||||
formspec = {
|
||||
{
|
||||
type = "number",
|
||||
name = "number",
|
||||
label = "distri number",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "color",
|
||||
label = "filter port",
|
||||
choices = "red,green,blue,yellow",
|
||||
default = "red",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "operand",
|
||||
choices = "is,is not",
|
||||
default = "is",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "value",
|
||||
label = "state",
|
||||
choices = "on,off",
|
||||
default = "off",
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "Read state of a Distributor filter slot.\n",
|
||||
},
|
||||
},
|
||||
button = function(data, environ) -- default button label
|
||||
return 'fltr('..techage.fmt_number(data.number)..","..data.color..' '..data.operand..' '..data.value..')'
|
||||
end,
|
||||
code = function(data, environ)
|
||||
local condition = function(env, idx)
|
||||
return techage.send_single(environ.number, data.number, "port", data.color)
|
||||
end
|
||||
local result = function(val)
|
||||
return techage.compare(val, data.value or "invalid", data.operand)
|
||||
end
|
||||
return condition, result
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_action("exchange", {
|
||||
title = "place/remove a block via the Door Controller II",
|
||||
formspec = {
|
||||
{
|
||||
type = "number",
|
||||
name = "number",
|
||||
label = "number",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "number",
|
||||
name = "slot",
|
||||
label = "slot no",
|
||||
default = "1",
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "place/remove a block via\nthe Door Controller II\n",
|
||||
},
|
||||
},
|
||||
button = function(data, environ)
|
||||
return 'exch('..techage.fmt_number(data.number)..","..data.slot..')'
|
||||
end,
|
||||
code = function(data, environ)
|
||||
return function(env, output, idx)
|
||||
local payload = data.slot
|
||||
techage.send_single(environ.number, data.number, "exchange", payload)
|
||||
end
|
||||
end,
|
||||
})
|
||||
@@ -0,0 +1,84 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
ICTA Controller - Condition Registration
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
local logic = techage.logic
|
||||
|
||||
-- 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 techage.icta_register_condition(key, tData)
|
||||
table.insert(aCondTypes, key)
|
||||
table.insert(aCondTitles, tData.title)
|
||||
if kvRegisteredCond[key] ~= nil then
|
||||
print("[Techage] 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 techage.cond_formspec(row, kvSelect)
|
||||
return techage.submenu_generate_formspec(
|
||||
row, "cond", "Condition type", aCondTypes, aCondTitles, kvRegisteredCond, kvSelect)
|
||||
end
|
||||
|
||||
-- evaluate the row condition input
|
||||
-- and return new data
|
||||
function techage.cond_eval_input(kvSelect, fields)
|
||||
kvSelect = techage.submenu_eval_input(kvRegisteredCond, aCondTypes, aCondTitles, kvSelect, fields)
|
||||
return kvSelect
|
||||
end
|
||||
|
||||
-- return the Lua code
|
||||
function techage.code_condition(kvSelect, environ)
|
||||
if kvSelect and kvRegisteredCond[kvSelect.choice] then
|
||||
if techage.submenu_verify(environ.owner, kvRegisteredCond, kvSelect) then
|
||||
return kvRegisteredCond[kvSelect.choice].code(kvSelect, environ)
|
||||
end
|
||||
end
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
techage.icta_register_condition("default", {
|
||||
title = "",
|
||||
formspec = {},
|
||||
code = function(data, environ)
|
||||
local condition = function(env, idx)
|
||||
return false
|
||||
end
|
||||
local result = function(val)
|
||||
return false
|
||||
end
|
||||
return condition, result
|
||||
end,
|
||||
button = function(data, environ) return "..." end,
|
||||
})
|
||||
@@ -0,0 +1,472 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2022 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
ICTA Controller
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
local logic = techage.logic
|
||||
|
||||
--
|
||||
-- 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 (see help)"
|
||||
local Cache = {}
|
||||
local FS_DATA = gen_table(techage.NUM_RULES, {})
|
||||
|
||||
|
||||
local function output(pos, text, flush_buffer)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if not flush_buffer then
|
||||
text = meta:get_string("output") .. "\n" .. (text or "")
|
||||
text = text:sub(-500,-1)
|
||||
end
|
||||
meta:set_string("output", text)
|
||||
meta:set_string("formspec", techage.formspecOutput(meta))
|
||||
end
|
||||
|
||||
-- cyclic execution (cycle, cond, result, after, actn)
|
||||
local function TemplCyc(cycle, cond, result, after, actn, idx)
|
||||
return function(env, output)
|
||||
if env.blocked[idx] == false and env.ticks % cycle == 0 then
|
||||
env.result[idx] = cond(env, idx)
|
||||
env.blocked[idx] = result(env.result[idx])
|
||||
if env.blocked[idx] then
|
||||
env.timer[idx] = env.ticks + after
|
||||
end
|
||||
env.condition[idx] = env.blocked[idx]
|
||||
else
|
||||
env.condition[idx] = false
|
||||
end
|
||||
if env.blocked[idx] and env.timer[idx] == env.ticks then
|
||||
actn(env, output, idx)
|
||||
env.blocked[idx] = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- event based execution
|
||||
local function TemplEvt(cond, result, after, actn, idx)
|
||||
return function(env, output)
|
||||
if env.blocked[idx] == false and env.event then
|
||||
env.result[idx] = cond(env, idx)
|
||||
env.blocked[idx] = result(env.result[idx])
|
||||
if env.blocked[idx] then
|
||||
env.timer[idx] = env.ticks + after
|
||||
end
|
||||
env.condition[idx] = env.blocked[idx]
|
||||
else
|
||||
env.condition[idx] = false
|
||||
end
|
||||
if env.blocked[idx] and env.timer[idx] == env.ticks then
|
||||
actn(env, output, idx)
|
||||
env.blocked[idx] = false
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
local tbl = {}
|
||||
for idx = 1,techage.NUM_RULES do
|
||||
local cycle = integer(fs_data[idx].cycle, 0, 1000)
|
||||
local cond, result = techage.code_condition(fs_data[idx].cond, environ)
|
||||
local after = integer(fs_data[idx].after, 0, 1000)
|
||||
local actn = techage.code_action(fs_data[idx].actn, environ)
|
||||
-- valid rule?
|
||||
if cycle and cond and after and actn then
|
||||
-- add rule number
|
||||
local f
|
||||
if cycle == 0 then -- event
|
||||
f = TemplEvt(cond, result, after, actn, idx)
|
||||
else -- cyclic
|
||||
f = TemplCyc(cycle, cond, result, after, actn, idx)
|
||||
end
|
||||
-- add to list of rules
|
||||
tbl[#tbl+1] = f
|
||||
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 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 functions = generate(pos, meta, gen_environ)
|
||||
Cache[number] = {
|
||||
code = functions,
|
||||
env = runtime_environ(pos),
|
||||
}
|
||||
return true
|
||||
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
|
||||
for _,func in ipairs(code) do
|
||||
local res, err = pcall(func, env, output)
|
||||
if not res then
|
||||
output(pos, err)
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
local function battery(pos)
|
||||
local battery_pos = minetest.find_node_near(pos, 1, {"techage:ta4_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", techage.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", techage.RUNNING)
|
||||
minetest.get_node_timer(pos):start(1)
|
||||
local fs_data = minetest.deserialize(meta:get_string("fs_data")) or FS_DATA
|
||||
meta:set_string("formspec", techage.formspecRules(meta, fs_data, sOUTPUT))
|
||||
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", techage.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", techage.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", techage.STOPPED)
|
||||
minetest.get_node_timer(pos):stop()
|
||||
meta:set_string("infotext", "Controller "..number..": No battery")
|
||||
meta:set_string("formspec", techage.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
|
||||
if player:get_player_name() ~= owner then
|
||||
return
|
||||
end
|
||||
|
||||
--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
|
||||
local fs_data = minetest.deserialize(meta:get_string("fs_data")) or FS_DATA
|
||||
local output = techage.edit_command(fs_data, fields.cmnd)
|
||||
stop_controller(pos, meta)
|
||||
meta:set_string("formspec", techage.formspecRules(meta, fs_data, output))
|
||||
meta:set_string("fs_data", minetest.serialize(fs_data))
|
||||
end
|
||||
if fields._type_ == "main" then
|
||||
techage.store_main_form_data(meta, fields)
|
||||
local key = techage.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", techage.formspecSubMenu(meta, key))
|
||||
end
|
||||
elseif fields._col_ == "cond" then
|
||||
techage.cond_formspec_update(meta, fields)
|
||||
elseif fields._col_ == "actn" then
|
||||
techage.actn_formspec_update(meta, fields)
|
||||
end
|
||||
if fields._exit_ == "ok" then -- exit from sub-menu?
|
||||
if fields._button_ then
|
||||
techage.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", techage.formspecHelp(evt.value))
|
||||
end
|
||||
if fields.update then
|
||||
meta:set_string("formspec", techage.formspecOutput(meta))
|
||||
elseif fields.clear then
|
||||
meta:set_string("output", "<press update>")
|
||||
meta:set_string("formspec", techage.formspecOutput(meta))
|
||||
elseif fields.list then
|
||||
local fs_data = minetest.deserialize(meta:get_string("fs_data")) or FS_DATA
|
||||
local s = techage.listing(fs_data)
|
||||
output(pos, s, true)
|
||||
elseif fields.tab == "1" then
|
||||
local fs_data = minetest.deserialize(meta:get_string("fs_data")) or FS_DATA
|
||||
meta:set_string("formspec", techage.formspecRules(meta, fs_data, sOUTPUT))
|
||||
elseif fields.tab == "2" then
|
||||
meta:set_string("formspec", techage.formspecOutput(meta))
|
||||
elseif fields.tab == "3" then
|
||||
meta:set_string("formspec", techage.formspecNotes(meta))
|
||||
elseif fields.tab == "4" then
|
||||
meta:set_string("formspec", techage.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 ta4_controller at ".. minetest.pos_to_string(pos))
|
||||
elseif fields.stop == "Stop" then
|
||||
stop_controller(pos, meta)
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_node("techage:ta4_icta_controller", {
|
||||
description = "TA4 ICTA Controller",
|
||||
inventory_image = "techage_ta4_controller_inventory.png",
|
||||
wield_image = "techage_ta4_controller_inventory.png",
|
||||
stack_max = 1,
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_smartline.png",
|
||||
"techage_smartline.png",
|
||||
"techage_smartline.png",
|
||||
"techage_smartline.png",
|
||||
"techage_smartline.png",
|
||||
"techage_smartline.png^techage_ta4_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 = techage.add_node(pos, "techage:ta4_icta_controller")
|
||||
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", techage.STOPPED)
|
||||
meta:set_string("formspec", techage.formspecRules(meta, fs_data, sOUTPUT))
|
||||
meta:set_string("infotext", "TA4 ICTA Controller "..number..": stopped")
|
||||
end,
|
||||
|
||||
on_receive_fields = on_receive_fields,
|
||||
|
||||
after_dig_node = function(pos, oldnode, oldmetadata)
|
||||
techage.remove_node(pos, oldnode, oldmetadata)
|
||||
techage.del_mem(pos)
|
||||
end,
|
||||
|
||||
on_timer = on_timer,
|
||||
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
use_texture_alpha = techage.CLIP,
|
||||
groups = {choppy=1, cracky=1, crumbly=1},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
|
||||
minetest.register_craft({
|
||||
output = "techage:ta4_icta_controller",
|
||||
recipe = {
|
||||
{"basic_materials:plastic_sheet", "dye:blue", "basic_materials:plastic_sheet"},
|
||||
{"", "default:copper_ingot", ""},
|
||||
{"techage:ta4_wlanchip", "techage:ta4_ramchip", "techage:ta4_ramchip"},
|
||||
},
|
||||
})
|
||||
|
||||
-- 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
|
||||
local t = minetest.get_us_time()
|
||||
Cache[own_number].env.input[rmt_number] = val
|
||||
Cache[own_number].env.last_event = t
|
||||
-- only two events per second
|
||||
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
|
||||
|
||||
techage.register_node({"techage:ta4_icta_controller"}, {
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local number = meta:get_string("number")
|
||||
local state = meta:get_int("state")
|
||||
|
||||
if state == techage.RUNNING and topic == "on" then
|
||||
set_input(pos, number, src, topic)
|
||||
elseif state == techage.RUNNING and topic == "off" then
|
||||
set_input(pos, number, src, topic)
|
||||
elseif topic == "state" then
|
||||
local state = meta:get_int("state") or 0
|
||||
return techage.StateStrings[state] or "stopped"
|
||||
else
|
||||
return "unsupported"
|
||||
end
|
||||
end,
|
||||
on_beduino_receive_cmnd = function(pos, src, topic, payload)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local number = meta:get_string("number")
|
||||
local state = meta:get_int("state")
|
||||
|
||||
if state == techage.RUNNING and topic == 1 and payload[1] == 1 then
|
||||
set_input(pos, number, src, topic)
|
||||
elseif state == techage.RUNNING and topic == 1 and payload[1] == 0 then
|
||||
set_input(pos, number, src, topic)
|
||||
else
|
||||
return 2
|
||||
end
|
||||
return 0
|
||||
end,
|
||||
on_beduino_request_data = function(pos, src, topic, payload)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local number = meta:get_string("number")
|
||||
local state = meta:get_int("state")
|
||||
|
||||
if topic == 129 then
|
||||
local state = meta:get_int("state") or 0
|
||||
return 0, {state or techage.STOPPED}
|
||||
else
|
||||
return 2, ""
|
||||
end
|
||||
end,
|
||||
})
|
||||
@@ -0,0 +1,40 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
ICTA Controller - Formspec edit command
|
||||
|
||||
]]--
|
||||
|
||||
function techage.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, techage.NUM_RULES))
|
||||
pos2 = math.max(1, math.min(pos2, techage.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, techage.NUM_RULES))
|
||||
fs_data[pos1] = {}
|
||||
return "row "..pos1.." deleted"
|
||||
end
|
||||
return "Invalid command '"..text.."'"
|
||||
end
|
||||
@@ -0,0 +1,243 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
ICTA Controller - Formspec
|
||||
|
||||
]]--
|
||||
|
||||
techage.NUM_RULES = 8
|
||||
|
||||
local SIZE = "size[13,8]"
|
||||
|
||||
local sHELP = [[ICTA 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 machine is fault, blocked, standby,...
|
||||
|
||||
Actions are:
|
||||
- switch on/off lamps and 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, if
|
||||
the controller received a command from
|
||||
another blocks, such as buttons.
|
||||
|
||||
Actions can be delayed. 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.
|
||||
|
||||
The Manual in German:
|
||||
https://github.com/joe7575/techage/blob/master/manuals/ta4_icta_controller_DE.md
|
||||
|
||||
Or the same as PDF:
|
||||
https://github.com/joe7575/techage/blob/master/manuals/ta4_icta_controller_DE.pdf
|
||||
|
||||
]]
|
||||
|
||||
-- to simplify the search for a pressed main form button (condition/action)
|
||||
local lButtonKeys = {}
|
||||
|
||||
for idx = 1,techage.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 techage.formspecError(meta)
|
||||
local running = meta:get_int("state") == techage.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
|
||||
|
||||
function techage.listing(fs_data)
|
||||
local tbl = {}
|
||||
|
||||
for idx = 1,techage.NUM_RULES do
|
||||
tbl[#tbl+1] = idx.." ("..fs_data[idx].cycle.."s): IF "..button(fs_data[idx].cond)
|
||||
tbl[#tbl+1] = " THEN "..button(fs_data[idx].actn).." after "..fs_data[idx].after.."s\n"
|
||||
end
|
||||
return table.concat(tbl)
|
||||
end
|
||||
|
||||
local function formspec_rules(fs_data)
|
||||
local tbl = {"field[0,0;0,0;_type_;;main]"..
|
||||
"label[0.4,0;Cycle/s:]label[2.5,0;IF cond:]label[7,0;THEN action:]label[11.5,0;after/s:]"}
|
||||
|
||||
for idx = 1,techage.NUM_RULES do
|
||||
local ypos = idx * 0.75 - 0.4
|
||||
tbl[#tbl+1] = "label[0,"..(0.2+ypos)..";"..idx.."]"
|
||||
tbl[#tbl+1] = "field[0.7,"..(0.3+ypos)..";1.4,1;cycle"..idx..";;"..(fs_data[idx].cycle or "").."]"
|
||||
tbl[#tbl+1] = "button[1.9,"..ypos..";4.9,1;cond"..idx..";"..minetest.formspec_escape(button(fs_data[idx].cond)).."]"
|
||||
tbl[#tbl+1] = "button[6.8,"..ypos..";4.9,1;actn"..idx..";"..minetest.formspec_escape(button(fs_data[idx].actn)).."]"
|
||||
tbl[#tbl+1] = "field[12,"..(0.3+ypos)..";1.4,1;after"..idx..";;"..(fs_data[idx].after or "").."]"
|
||||
end
|
||||
return table.concat(tbl)
|
||||
end
|
||||
|
||||
function techage.store_main_form_data(meta, fields)
|
||||
local fs_data = minetest.deserialize(meta:get_string("fs_data"))
|
||||
for idx = 1,techage.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 techage.main_form_button_pressed(fields)
|
||||
for _,key in ipairs(lButtonKeys) do
|
||||
if fields[key] then
|
||||
return key
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function techage.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 techage.cond_formspec(row, fs_data[row].cond)
|
||||
else
|
||||
local row = tonumber(key:sub(5,5))
|
||||
return techage.actn_formspec(row, fs_data[row].actn)
|
||||
end
|
||||
end
|
||||
|
||||
function techage.formspec_button_update(meta, fields)
|
||||
local fs_data = minetest.deserialize(meta:get_string("fs_data"))
|
||||
local row = tonumber(fields._row_ or 1)
|
||||
if fields._col_ == "cond" then
|
||||
fs_data[row].cond = techage.cond_eval_input(fs_data[row].cond, fields)
|
||||
elseif fields._col_ == "actn" then
|
||||
fs_data[row].actn = techage.actn_eval_input(fs_data[row].actn, fields)
|
||||
end
|
||||
meta:set_string("fs_data", minetest.serialize(fs_data))
|
||||
end
|
||||
|
||||
function techage.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 = techage.cond_eval_input(fs_data[row].cond, fields)
|
||||
meta:set_string("formspec", techage.cond_formspec(row, fs_data[row].cond))
|
||||
meta:set_string("fs_data", minetest.serialize(fs_data))
|
||||
end
|
||||
|
||||
function techage.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 = techage.actn_eval_input(fs_data[row].actn, fields)
|
||||
meta:set_string("formspec", techage.actn_formspec(row, fs_data[row].actn))
|
||||
meta:set_string("fs_data", minetest.serialize(fs_data))
|
||||
end
|
||||
|
||||
|
||||
function techage.formspecRules(meta, fs_data, output)
|
||||
local running = meta:get_int("state") == techage.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 techage.formspecOutput(meta)
|
||||
local running = meta:get_int("state") == techage.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[5.5,7.5;1.8,1;list;List]"..
|
||||
"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 techage.formspecNotes(meta)
|
||||
local running = meta:get_int("state") == techage.RUNNING
|
||||
local cmnd = running and "stop;Stop" or "start;Start"
|
||||
local notes = meta:get_string("notes") or ""
|
||||
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 techage.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
|
||||
@@ -0,0 +1,158 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2022 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
ICTA Controller - Signal Tower
|
||||
|
||||
]]--
|
||||
|
||||
|
||||
local function switch_on(pos, node, color)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("state", color)
|
||||
node.name = "techage:ta4_signaltower_"..color
|
||||
minetest.swap_node(pos, node)
|
||||
end
|
||||
|
||||
local function switch_off(pos, node)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("state", "off")
|
||||
node.name = "techage:ta4_signaltower"
|
||||
minetest.swap_node(pos, node)
|
||||
end
|
||||
|
||||
minetest.register_node("techage:ta4_signaltower", {
|
||||
description = "TA4 Signal Tower",
|
||||
tiles = {
|
||||
'techage_signaltower_top.png',
|
||||
'techage_signaltower_top.png',
|
||||
'techage_signaltower.png',
|
||||
},
|
||||
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{ -5/32, -16/32, -5/32, 5/32, 16/32, 5/32},
|
||||
},
|
||||
},
|
||||
|
||||
after_place_node = function(pos, placer)
|
||||
local number = techage.add_node(pos, "techage:ta4_signaltower")
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("state", "off")
|
||||
meta:set_string("infotext", "TA4 Signal Tower "..number)
|
||||
end,
|
||||
|
||||
on_rightclick = function(pos, node, clicker)
|
||||
if not minetest.is_protected(pos, clicker:get_player_name()) then
|
||||
switch_on(pos, node, "green")
|
||||
end
|
||||
end,
|
||||
|
||||
after_dig_node = function(pos, oldnode, oldmetadata)
|
||||
techage.remove_node(pos, oldnode, oldmetadata)
|
||||
end,
|
||||
|
||||
paramtype = "light",
|
||||
use_texture_alpha = techage.CLIP,
|
||||
light_source = 0,
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
groups = {cracky=2, crumbly=2},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_glass_defaults(),
|
||||
})
|
||||
|
||||
for _,color in ipairs({"green", "amber", "red"}) do
|
||||
minetest.register_node("techage:ta4_signaltower_"..color, {
|
||||
description = "TA4 Signal Tower",
|
||||
tiles = {
|
||||
'techage_signaltower_top.png',
|
||||
'techage_signaltower_top.png',
|
||||
'techage_signaltower_'..color..'.png',
|
||||
},
|
||||
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{ -5/32, -16/32, -5/32, 5/32, 16/32, 5/32},
|
||||
},
|
||||
},
|
||||
on_rightclick = function(pos, node, clicker)
|
||||
if not minetest.is_protected(pos, clicker:get_player_name()) then
|
||||
switch_off(pos, node)
|
||||
end
|
||||
end,
|
||||
|
||||
paramtype = "light",
|
||||
use_texture_alpha = techage.CLIP,
|
||||
light_source = 10,
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
groups = {crumbly=0, not_in_creative_inventory=1},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_glass_defaults(),
|
||||
drop = "techage:ta4_signaltower",
|
||||
})
|
||||
end
|
||||
|
||||
minetest.register_craft({
|
||||
output = "techage:ta4_signaltower",
|
||||
recipe = {
|
||||
{"dye:red", "default:copper_ingot", ""},
|
||||
{"dye:orange", "default:glass", ""},
|
||||
{"dye:green", "techage:ta4_wlanchip", ""},
|
||||
},
|
||||
})
|
||||
|
||||
techage.register_node({"techage:ta4_signaltower",
|
||||
"techage:ta4_signaltower_green",
|
||||
"techage:ta4_signaltower_amber",
|
||||
"techage:ta4_signaltower_red"}, {
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
local node = minetest.get_node(pos)
|
||||
if topic == "green" then
|
||||
switch_on(pos, node, "green")
|
||||
elseif topic == "amber" then
|
||||
switch_on(pos, node, "amber")
|
||||
elseif topic == "red" then
|
||||
switch_on(pos, node, "red")
|
||||
elseif topic == "off" then
|
||||
switch_off(pos, node)
|
||||
elseif topic == "state" then
|
||||
local meta = minetest.get_meta(pos)
|
||||
return meta:get_string("state")
|
||||
end
|
||||
end,
|
||||
on_beduino_receive_cmnd = function(pos, src, topic, payload)
|
||||
if topic == 2 then
|
||||
local color = ({"green", "amber", "red"})[payload[1]]
|
||||
local node = minetest.get_node(pos)
|
||||
if color then
|
||||
switch_on(pos, node, color)
|
||||
else
|
||||
switch_off(pos, node)
|
||||
end
|
||||
return 0
|
||||
else
|
||||
return 2 -- unknown or invalid topic
|
||||
end
|
||||
end,
|
||||
on_beduino_request_data = function(pos, src, topic, payload)
|
||||
if topic == 130 then
|
||||
local meta = minetest.get_meta(pos)
|
||||
local color = ({off = 0, green = 1, amber = 2, red = 3})[meta:get_string("state")] or 1
|
||||
return 0, {color}
|
||||
else
|
||||
return 2, "" -- unknown or invalid topic
|
||||
end
|
||||
end,
|
||||
})
|
||||
@@ -0,0 +1,213 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
ICTA Controller - Formspec
|
||||
|
||||
A sub-menu control to generate a formspec sting for conditions and actions
|
||||
]]--
|
||||
|
||||
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 offs = 1.4
|
||||
if kvDefinition[kvSelect.choice] then
|
||||
local lControls = kvDefinition[kvSelect.choice].formspec
|
||||
for _,elem in ipairs(lControls) do
|
||||
if elem.type == "label" then
|
||||
tbl[#tbl+1] = "label[0,"..offs..";Description:\n"..elem.label.."]"
|
||||
offs = offs + 0.4
|
||||
elseif 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 == "number" or elem.type == "digits" or elem.type == "letters"
|
||||
or elem.type == "ascii" then
|
||||
local val = kvSelect[elem.name] or elem.default
|
||||
tbl[#tbl+1] = "field[0.3,"..(offs+0.2)..";8,1;"..elem.name..";;"..val.."]"
|
||||
offs = offs + 0.9
|
||||
elseif elem.type == "textlist" then
|
||||
local l = elem.choices:split(",")
|
||||
local 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 + 0.9
|
||||
end
|
||||
end
|
||||
end
|
||||
return tbl
|
||||
end
|
||||
|
||||
local function default_data(kvDefinition, kvSelect)
|
||||
local lControls = kvDefinition[kvSelect.choice].formspec
|
||||
for _,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 _,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 == "number" 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 techage.submenu_verify(owner, kvDefinition, kvSelect)
|
||||
local error = false
|
||||
local lControls = kvDefinition[kvSelect.choice].formspec
|
||||
for _,elem in ipairs(lControls) do
|
||||
if elem.type == "numbers" then
|
||||
if not kvSelect[elem.name]:find("^[%d ]+$") then
|
||||
error = true
|
||||
end
|
||||
if not techage.check_numbers(kvSelect[elem.name], owner) then
|
||||
error = true
|
||||
end
|
||||
elseif elem.type == "number" then
|
||||
if not kvSelect[elem.name]:find("^[%d]+$") then
|
||||
error = true
|
||||
end
|
||||
if not techage.check_numbers(kvSelect[elem.name], owner) 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 techage.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..
|
||||
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_;Alternative 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 techage.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
|
||||
Reference in New Issue
Block a user