ICTA controller added
@ -24,7 +24,8 @@ local CYCLE_TIME = 6
|
||||
|
||||
local recipes = techage.recipes
|
||||
|
||||
local RecipeType = {
[2] = "ta2_electronic_fab",
|
||||
local RecipeType = {
|
||||
[2] = "ta2_electronic_fab",
|
||||
[3] = "ta3_electronic_fab",
|
||||
[4] = "ta4_electronic_fab",
|
||||
}
|
||||
@ -239,22 +240,6 @@ minetest.register_craft({
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craftitem("techage:vacuum_tube", {
|
||||
description = S("TA3 Vacuum Tube"),
|
||||
inventory_image = "techage_vacuum_tube.png",
|
||||
})
|
||||
|
||||
minetest.register_craftitem("techage:ta4_wlanchip", {
|
||||
description = S("TA4 WLAN Chip"),
|
||||
inventory_image = "techage_wlanchip.png",
|
||||
})
|
||||
|
||||
minetest.register_craftitem("techage:wlanchip", {
|
||||
description = S("WLAN Chip"),
|
||||
inventory_image = "techage_wlanchip.png",
|
||||
})
|
||||
|
||||
|
||||
if minetest.global_exists("unified_inventory") then
|
||||
unified_inventory.register_craft_type("ta2_electronic_fab", {
|
||||
description = S("TA2 Ele Fab"),
|
||||
@ -270,18 +255,3 @@ if minetest.global_exists("unified_inventory") then
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
recipes.add("ta2_electronic_fab", {
|
||||
output = "techage:vacuum_tube 2",
|
||||
input = {"default:glass 1", "basic_materials:copper_wire 1", "basic_materials:plastic_sheet 1", "techage:usmium_nuggets 1"}
|
||||
})
|
||||
|
||||
recipes.add("ta3_electronic_fab", {
|
||||
output = "techage:vacuum_tube 2",
|
||||
input = {"default:glass 1", "basic_materials:copper_wire 1", "basic_materials:plastic_sheet 1", "techage:usmium_nuggets 1"}
|
||||
})
|
||||
|
||||
recipes.add("ta3_electronic_fab", {
|
||||
output = "techage:ta4_wlanchip 8",
|
||||
input = {"default:mese_crystal 1", "default:copper_ingot 1", "default:gold_ingot 1", "techage:ta4_silicon_wafer 1"}
|
||||
})
|
||||
|
@ -339,6 +339,7 @@ function techage.check_numbers(numbers, placer_name)
|
||||
end
|
||||
|
||||
function techage.send_multi(src, numbers, topic, payload)
|
||||
--print("send_multi", src, numbers, topic)
|
||||
for _,num in ipairs(string_split(numbers, " ")) do
|
||||
if Number2Pos[num] and Number2Pos[num].name then
|
||||
local data = Number2Pos[num]
|
||||
@ -350,6 +351,7 @@ function techage.send_multi(src, numbers, topic, payload)
|
||||
end
|
||||
|
||||
function techage.send_single(src, number, topic, payload)
|
||||
--print("send_single", src, number, topic)
|
||||
if Number2Pos[number] and Number2Pos[number].name then
|
||||
local data = Number2Pos[number]
|
||||
if data.pos and NodeDef[data.name] and NodeDef[data.name].on_recv_message then
|
||||
|
107
icta_controller/action.lua
Normal file
@ -0,0 +1,107 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
GPL 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(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)
|
||||
local s1 = 'local text = string.gsub("'..(techage.icta_escape(data.text))..'", "*", env.result[#])'
|
||||
local s2 = 'output(env.pos, text)'
|
||||
return s1.."\n\t"..s2
|
||||
end,
|
||||
})
|
||||
|
167
icta_controller/battery.lua
Normal file
@ -0,0 +1,167 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
ICTA Controller - Battery
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
local logic = techage.logic
|
||||
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", S("Battery").." ("..percent.."%)")
|
||||
if percent == 0 then
|
||||
local node = minetest.get_node(pos)
|
||||
node.name = "techage:battery_empty"
|
||||
minetest.swap_node(pos, node)
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function register_battery(ext, percent, nici)
|
||||
minetest.register_node("techage:battery"..ext, {
|
||||
description = S("Battery").." "..ext,
|
||||
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)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_int("content", BATTERY_CAPACITY * percent)
|
||||
local node = minetest.get_node(pos)
|
||||
node.name = "techage: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("techage:battery")
|
||||
elseif percent > 75 then
|
||||
stack = ItemStack("techage:battery75")
|
||||
elseif percent > 50 then
|
||||
stack = ItemStack("techage:battery50")
|
||||
elseif percent > 25 then
|
||||
stack = ItemStack("techage: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("techage: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",
|
||||
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:battery 2",
|
||||
recipe = {
|
||||
{"", "moreores:silver_ingot", ""},
|
||||
{"", "default:copper_ingot", ""},
|
||||
{"", "moreores:silver_ingot", ""},
|
||||
}
|
||||
})
|
||||
else
|
||||
minetest.register_craft({
|
||||
output = "techage:battery 2",
|
||||
recipe = {
|
||||
{"", "default:tin_ingot", ""},
|
||||
{"", "default:copper_ingot", ""},
|
||||
{"", "default:tin_ingot", ""},
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
techage.register_node({"techage:battery", "techage:battery25", "techage:battery50", "techage:battery75"},
|
||||
{
|
||||
on_node_load = function(pos)
|
||||
minetest.get_node_timer(pos):start(30)
|
||||
end,
|
||||
})
|
618
icta_controller/commands.lua
Normal file
@ -0,0 +1,618 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
GPL 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
|
||||
|
||||
local function send_single_string(environ, number, topic, payload)
|
||||
payload = payload or "nil"
|
||||
local s = 'techage.send_single("%s", "%s", "%s", %s)'
|
||||
return string.format(s, environ.number, number, topic, payload)
|
||||
end
|
||||
|
||||
local function send_multi_string(environ, numbers, topic, payload)
|
||||
payload = payload or "nil"
|
||||
local s = 'techage.send_multi("%s", "%s", "%s", %s)'
|
||||
return string.format(s, environ.number, numbers, topic, payload)
|
||||
end
|
||||
|
||||
function techage.operand(s)
|
||||
if s == "is" then
|
||||
return "== "
|
||||
elseif s == "is not" then
|
||||
return "~= "
|
||||
elseif s == "greater" then
|
||||
return "> "
|
||||
elseif s == "less" then
|
||||
return "< "
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function techage.fmt_number(num)
|
||||
local mtch = num:match('^(%d+).*')
|
||||
if mtch and num ~= mtch then
|
||||
return mtch.."..."
|
||||
end
|
||||
return num
|
||||
end
|
||||
|
||||
-- '#' is used as placeholder for rule numbers and has to be escaped
|
||||
function techage.icta_escape(s)
|
||||
s = tostring(s)
|
||||
return s:gsub("#", '"..string.char(35).."')
|
||||
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)
|
||||
return 'env.ticks', '== 1'
|
||||
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)
|
||||
return '"true"', '== "true"'
|
||||
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 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 "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)
|
||||
return 'env.input["'..data.number..'"]',
|
||||
techage.operand(data.operand)..'"'..data.value..'"'
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_condition("state", {
|
||||
title = "block state request",
|
||||
formspec = {
|
||||
{
|
||||
type = "digits",
|
||||
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",
|
||||
default = "stopped",
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "Read the state from 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)
|
||||
return send_single_string(environ, data.number, "state"),
|
||||
techage.operand(data.operand)..'"'..data.value..'"'
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_condition("fuel", {
|
||||
title = "fuel request",
|
||||
formspec = {
|
||||
{
|
||||
type = "digits",
|
||||
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\nfrom a fuel consuming block.",
|
||||
},
|
||||
},
|
||||
button = function(data, environ)
|
||||
return 'fuel('..techage.fmt_number(data.number)..","..data.operand..' '..data.value..')'
|
||||
end,
|
||||
code = function(data, environ)
|
||||
return send_single_string(environ, data.number, "fuel"),
|
||||
techage.operand(data.operand)..tonumber(data.value)
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_condition("load", {
|
||||
title = "load request",
|
||||
formspec = {
|
||||
{
|
||||
type = "digits",
|
||||
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)\nfrom a tank/storage block.",
|
||||
},
|
||||
},
|
||||
button = function(data, environ)
|
||||
return 'load('..techage.fmt_number(data.number)..","..data.operand..' '..data.value..')'
|
||||
end,
|
||||
code = function(data, environ)
|
||||
return send_single_string(environ, data.number, "load"),
|
||||
techage.operand(data.operand)..tonumber(data.value)
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_condition("chest", {
|
||||
title = "chest state request",
|
||||
formspec = {
|
||||
{
|
||||
type = "digits",
|
||||
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,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)
|
||||
return send_single_string(environ, data.number, "state"),
|
||||
techage.operand(data.operand)..'"'..data.value..'"'
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_condition("signaltower", {
|
||||
title = "Signal Tower state request",
|
||||
formspec = {
|
||||
{
|
||||
type = "digits",
|
||||
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\nfrom 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)
|
||||
return send_single_string(environ, data.number, "state"),
|
||||
techage.operand(data.operand)..'"'..data.value..'"'
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_action("signaltower", {
|
||||
title = "Signal Tower command",
|
||||
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 send_multi_string(environ, data.number, data.value)
|
||||
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 send_multi_string(environ, data.number, data.value)
|
||||
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,6,7,8,9",
|
||||
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)
|
||||
local s1 = string.format('local text = string.gsub("%s", "*", tostring(env.result[#]))', techage.icta_escape(data.text))
|
||||
local s2 = string.format('local payload = {row = %s, str = text}', data.row)
|
||||
local s3 = send_multi_string(environ, data.number, "set", "payload")
|
||||
return s1.."\n\t"..s2.."\n\t"..s3
|
||||
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 = "numbers",
|
||||
name = "number",
|
||||
label = "Display number",
|
||||
default = "",
|
||||
},
|
||||
},
|
||||
code = function(data, environ)
|
||||
return send_multi_string(environ, data.number, "clear")
|
||||
end,
|
||||
button = function(data, environ)
|
||||
return "clear lcd("..techage.fmt_number(data.number)..")"
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_action("chat", {
|
||||
title = "chat send",
|
||||
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 'minetest.chat_send_player("'..environ.owner..'", "[TA4 ICTA Controller] '..data.text..' ")'
|
||||
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 = "doors open/close",
|
||||
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 Programmer to\neasily determine a door position.",
|
||||
},
|
||||
},
|
||||
code = function(data, environ)
|
||||
return 'techage.icta_door_toggle("'..data.pos..'", "'..environ.owner..'", "'..data.door_state..'")'
|
||||
end,
|
||||
button = function(data, environ)
|
||||
return 'door("'..data.pos..'",'..data.door_state..")"
|
||||
end,
|
||||
})
|
||||
|
||||
function techage.icta_player_detect(own_num, number, name)
|
||||
local state = techage.send_single(own_num, number, "name", nil)
|
||||
print("state="..state.."< name="..name.."<")
|
||||
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 = "Player Detector name request",
|
||||
formspec = {
|
||||
{
|
||||
type = "digits",
|
||||
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\nfrom a Player Detector.\nUse a '*' character for all player names.\n Use a '-' character for no player.",
|
||||
},
|
||||
},
|
||||
|
||||
code = function(data, environ)
|
||||
return 'techage.icta_player_detect("'..environ.number..'", "'..data.number..'", "'..data.name..'")', "~= nil"
|
||||
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 = "numbers",
|
||||
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)
|
||||
local payload = '{slot = "'..data.color..'", val = "'..data.value..'"}'
|
||||
return send_multi_string(environ, data.number, "filter", payload)
|
||||
end,
|
||||
})
|
||||
|
77
icta_controller/condition.lua
Normal file
@ -0,0 +1,77 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
GPL 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(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) return false, false end,
|
||||
button = function(data, environ) return "..." end,
|
||||
})
|
||||
|
512
icta_controller/controller.lua
Normal file
@ -0,0 +1,512 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
GPL 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
|
||||
|
||||
----------------- 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
|
||||
|
||||
-- -- Callback variant
|
||||
-- if env.blocked[1] == false and env.ticks % <cycle> == 0 then
|
||||
-- env.result[1], env.blocked[1] = <callback>
|
||||
-- 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 (cycle, cond, result, after, actn)
|
||||
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
|
||||
]]
|
||||
|
||||
-- event based execution of callback function
|
||||
local TemplEvtClbk = [[
|
||||
-- Rule #
|
||||
if env.blocked[#] == false and env.event then
|
||||
env.result[#], env.blocked[#] = %s(env, %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,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 s
|
||||
if cycle == 0 then -- event?
|
||||
if result then
|
||||
s = string.format(TemplEvt, cond, result, after, actn)
|
||||
else -- callback function
|
||||
local data = dump(fs_data[idx].cond)
|
||||
s = string.format(TemplEvtClbk, cond, data, after, actn)
|
||||
end
|
||||
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, {"techage: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_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_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("formspec", techage.cond_formspec(1, 1, nil))
|
||||
meta:set_string("infotext", "TA4 ICTA 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)
|
||||
techage.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 = "techage:ta4_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_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")
|
||||
return techage.statestring(state)
|
||||
else
|
||||
return "unsupported"
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
171
icta_controller/display.lua
Normal file
@ -0,0 +1,171 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
ICTA Controller - Display
|
||||
|
||||
]]--
|
||||
|
||||
|
||||
lcdlib.register_display_entity("techage:display_entity")
|
||||
|
||||
local function display_update(pos, objref)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local text = meta:get_string("text") or ""
|
||||
text = string.gsub(text, "|", " \n")
|
||||
local texture = lcdlib.make_multiline_texture(
|
||||
"default", text,
|
||||
--120, 120, 9, "top", "#000")
|
||||
70, 70, 5, "top", "#000")
|
||||
objref:set_properties({ textures = {texture},
|
||||
visual_size = {x=0.94, y=0.94} })
|
||||
end
|
||||
|
||||
local function on_timer(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
-- check if display is loaded and a player in front of the display
|
||||
if node.name == "techage:display" then
|
||||
local dir = minetest.facedir_to_dir((node.param2 + 2) % 4)
|
||||
local pos2 = vector.add(pos, vector.multiply(dir, 6))
|
||||
for _, obj in pairs(minetest.get_objects_inside_radius(pos2, 6)) do
|
||||
if obj:is_player() then
|
||||
lcdlib.update_entities(pos)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local lcd_box = {
|
||||
type = "wallmounted",
|
||||
wall_top = {-8/16, 15/32, -8/16, 8/16, 8/16, 8/16}
|
||||
}
|
||||
|
||||
minetest.register_node("techage:display", {
|
||||
description = "TA4 Display",
|
||||
inventory_image = 'techage_display_inventory.png',
|
||||
tiles = {"techage_display.png"},
|
||||
drawtype = "nodebox",
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "wallmounted",
|
||||
node_box = lcd_box,
|
||||
selection_box = lcd_box,
|
||||
light_source = 6,
|
||||
|
||||
display_entities = {
|
||||
["techage:display_entity"] = { depth = 0.42,
|
||||
on_display_update = display_update},
|
||||
},
|
||||
|
||||
after_place_node = function(pos, placer)
|
||||
local number = techage.add_node(pos, "techage:display")
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("number", number)
|
||||
meta:set_string("text", "My\nTechage\nTA4\nDisplay\nNo: "..number)
|
||||
meta:set_int("startscreen", 1)
|
||||
lcdlib.update_entities(pos)
|
||||
end,
|
||||
|
||||
after_dig_node = function(pos)
|
||||
techage.remove_node(pos)
|
||||
end,
|
||||
|
||||
on_timer = on_timer,
|
||||
on_place = lcdlib.on_place,
|
||||
on_construct = lcdlib.on_construct,
|
||||
on_destruct = lcdlib.on_destruct,
|
||||
on_rotate = lcdlib.on_rotate,
|
||||
groups = {cracky=2, crumbly=2},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_glass_defaults(),
|
||||
})
|
||||
|
||||
|
||||
minetest.register_craft({
|
||||
output = "techage:display",
|
||||
recipe = {
|
||||
{"", "", ""},
|
||||
{"default:glass", "dye:green", "techage:ta4_wlanchip"},
|
||||
{"", "default:copper_ingot", ""},
|
||||
},
|
||||
})
|
||||
|
||||
local function add_line(meta, payload)
|
||||
local text = meta:get_string("text")
|
||||
local rows
|
||||
if meta:get_int("startscreen") == 1 then
|
||||
rows = {}
|
||||
meta:set_int("startscreen", 0)
|
||||
else
|
||||
rows = string.split(text, "|")
|
||||
end
|
||||
if #rows > 8 then
|
||||
table.remove(rows, 1)
|
||||
end
|
||||
table.insert(rows, payload)
|
||||
text = table.concat(rows, "|")
|
||||
meta:set_string("text", text)
|
||||
end
|
||||
|
||||
local function write_row(meta, payload)
|
||||
local text = meta:get_string("text")
|
||||
if type(payload) == "table" then
|
||||
local row = tonumber(payload.row) or 0
|
||||
if row > 9 then row = 9 end
|
||||
local str = payload.str or "oops"
|
||||
if row == 0 then
|
||||
meta:set_string("infotext", str)
|
||||
return
|
||||
end
|
||||
local rows
|
||||
if meta:get_int("startscreen") == 1 then
|
||||
rows = {}
|
||||
meta:set_int("startscreen", 0)
|
||||
else
|
||||
rows = string.split(text, "|")
|
||||
end
|
||||
if #rows < 9 then
|
||||
for i = #rows, 9 do
|
||||
table.insert(rows, " ")
|
||||
end
|
||||
end
|
||||
rows[row] = str
|
||||
text = table.concat(rows, "|")
|
||||
meta:set_string("text", text)
|
||||
end
|
||||
end
|
||||
|
||||
techage.register_node({"techage:display"}, {
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
local node = minetest.get_node(pos)
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
if topic == "add" then -- add one line and scroll if necessary
|
||||
local meta = minetest.get_meta(pos)
|
||||
add_line(meta, payload)
|
||||
if not timer:is_started() then
|
||||
timer:start(1)
|
||||
end
|
||||
elseif topic == "set" then -- overwrite the given row
|
||||
local meta = minetest.get_meta(pos)
|
||||
write_row(meta, payload)
|
||||
if not timer:is_started() then
|
||||
timer:start(1)
|
||||
end
|
||||
elseif topic == "clear" then -- clear the screen
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("text", "")
|
||||
if not timer:is_started() then
|
||||
timer:start(1)
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
40
icta_controller/edit.lua
Normal file
@ -0,0 +1,40 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
GPL 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
|
236
icta_controller/formspec.lua
Normal file
@ -0,0 +1,236 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
GPL 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.
|
||||
]]
|
||||
|
||||
-- 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
|
133
icta_controller/signaltower.lua
Normal file
@ -0,0 +1,133 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
GPL 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: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:signaltower"
|
||||
minetest.swap_node(pos, node)
|
||||
end
|
||||
|
||||
minetest.register_node("techage: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: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)
|
||||
techage.remove_node(pos)
|
||||
end,
|
||||
|
||||
paramtype = "light",
|
||||
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: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",
|
||||
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:signaltower",
|
||||
})
|
||||
end
|
||||
|
||||
minetest.register_craft({
|
||||
output = "techage:signaltower",
|
||||
recipe = {
|
||||
{"dye:red", "default:copper_ingot", ""},
|
||||
{"dye:orange", "default:glass", ""},
|
||||
{"dye:green", "techage:ta4_wlanchip", ""},
|
||||
},
|
||||
})
|
||||
|
||||
techage.register_node({"techage:signaltower",
|
||||
"techage:signaltower_green",
|
||||
"techage:signaltower_amber",
|
||||
"techage: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,
|
||||
})
|
132
icta_controller/stopwatch.lua
Normal file
@ -0,0 +1,132 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
ICTA Controller - Stopwatch
|
||||
|
||||
Start/stop the watch with an on/off commands.
|
||||
The player name clicking the stop is stored in addition.
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
local logic = techage.logic
|
||||
|
||||
local function retrieve_clicker_name(number)
|
||||
local pos = techage.get_node_info(number).pos
|
||||
if pos then
|
||||
local meta = minetest.get_meta(pos)
|
||||
return meta:get_string("clicker_name") or "<unknown>"
|
||||
end
|
||||
return "<error>"
|
||||
end
|
||||
|
||||
-- env = {
|
||||
-- event = <bool>,
|
||||
-- last_event = <number> -- last event time
|
||||
-- ticks = <number,
|
||||
-- pos = <pos>,
|
||||
-- timer = gen_table(8, 0),
|
||||
-- blocked = gen_table(8, false),
|
||||
-- result = gen_table(8, false),
|
||||
-- condition = gen_table(8, false),
|
||||
-- input = <table>, -- node number is key
|
||||
-- number = <number>,
|
||||
-- owner = <string>,
|
||||
-- },
|
||||
--
|
||||
-- return cond_result, trigger_action
|
||||
function techage.stopwatch(env, data)
|
||||
if env.input[data.number] == "on" then
|
||||
env.time = env.last_event
|
||||
if not env.highscore then
|
||||
env.highscore = 99999
|
||||
end
|
||||
return nil, false
|
||||
else
|
||||
local time = (env.last_event - env.time) / 1000000
|
||||
local name = retrieve_clicker_name(data.number)
|
||||
env.highscore = math.min(time, env.highscore)
|
||||
local s1 = string.format("%2.1f s", time)
|
||||
local s2 = string.format("%2.1f s", env.highscore)
|
||||
env.stopwatch_result = {s1, s2, name}
|
||||
return nil, true
|
||||
end
|
||||
end
|
||||
|
||||
techage.icta_register_condition("stopwatch", {
|
||||
title = "stopwatch",
|
||||
formspec = {
|
||||
{
|
||||
type = "numbers",
|
||||
name = "number",
|
||||
label = "Switch number",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "Hint: Stop the time between switching on\nand switching off of the connected switch.",
|
||||
},
|
||||
},
|
||||
code = function(data, environ)
|
||||
return "techage.stopwatch"
|
||||
end,
|
||||
button = function(data, environ)
|
||||
return 'stopwatch('..sl.fmt_number(data.number)..')'
|
||||
end,
|
||||
})
|
||||
|
||||
techage.icta_register_action("stopwatch", {
|
||||
title = "stopwatch",
|
||||
formspec = {
|
||||
{
|
||||
type = "numbers",
|
||||
name = "number",
|
||||
label = "Display 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 = "label",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "textlist",
|
||||
name = "type",
|
||||
label = "type",
|
||||
choices = "time,highscore,name",
|
||||
default = "time",
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "Hint: Display number for the output\nof time, highscore and player name.",
|
||||
},
|
||||
},
|
||||
button = function(data, environ)
|
||||
return "lcd("..sl.fmt_number(data.number)..","..data.row..","..data.type..')'
|
||||
end,
|
||||
code = function(data, environ)
|
||||
local idx = ({time=1, highscore= 2, name=3})[data.type]
|
||||
local s1 = string.format('local payload = {row = %s, str = "%s "..env.stopwatch_result['..idx..']}', data.row, techage.escape(data.text))
|
||||
local s2 = string.format('techage.send_multi("%s", "%s", "row", payload)', environ.number, data.number)
|
||||
return s1.."\n\t"..s2
|
||||
end,
|
||||
})
|
196
icta_controller/submenu.lua
Normal file
@ -0,0 +1,196 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
GPL 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 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.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 == "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 + 0.9
|
||||
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 + 0.9
|
||||
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 techage.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 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
|
||||
|
14
init.lua
@ -209,6 +209,19 @@ else
|
||||
dofile(MP.."/hydrogen/electrolyzer.lua")
|
||||
dofile(MP.."/hydrogen/fuelcell.lua")
|
||||
|
||||
-- ICTA Controller
|
||||
dofile(MP.."/icta_controller/submenu.lua")
|
||||
dofile(MP.."/icta_controller/condition.lua")
|
||||
dofile(MP.."/icta_controller/action.lua")
|
||||
dofile(MP.."/icta_controller/formspec.lua")
|
||||
dofile(MP.."/icta_controller/controller.lua")
|
||||
dofile(MP.."/icta_controller/commands.lua")
|
||||
dofile(MP.."/icta_controller/edit.lua")
|
||||
dofile(MP.."/icta_controller/battery.lua")
|
||||
--dofile(MP.."/icta_controller/stopwatch.lua")
|
||||
dofile(MP.."/icta_controller/display.lua")
|
||||
dofile(MP.."/icta_controller/signaltower.lua")
|
||||
|
||||
-- Items
|
||||
dofile(MP.."/items/barrel.lua")
|
||||
dofile(MP.."/items/baborium.lua")
|
||||
@ -224,6 +237,7 @@ else
|
||||
dofile(MP.."/items/aluminium.lua")
|
||||
dofile(MP.."/items/plastic.lua")
|
||||
dofile(MP.."/items/hydrogen.lua")
|
||||
dofile(MP.."/items/electronic.lua")
|
||||
|
||||
if techage.basalt_stone_enabled then
|
||||
dofile(MP.."/items/basalt.lua")
|
||||
|
56
items/electronic.lua
Normal file
@ -0,0 +1,56 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Bauxite
|
||||
|
||||
]]--
|
||||
|
||||
local S = techage.S
|
||||
|
||||
minetest.register_craftitem("techage:vacuum_tube", {
|
||||
description = S("TA3 Vacuum Tube"),
|
||||
inventory_image = "techage_vacuum_tube.png",
|
||||
})
|
||||
|
||||
minetest.register_craftitem("techage:ta4_wlanchip", {
|
||||
description = S("TA4 WLAN Chip"),
|
||||
inventory_image = "techage_wlanchip.png",
|
||||
})
|
||||
|
||||
minetest.register_craftitem("techage:wlanchip", {
|
||||
description = S("WLAN Chip"),
|
||||
inventory_image = "techage_wlanchip.png",
|
||||
})
|
||||
|
||||
minetest.register_craftitem("techage:ta4_ramchip", {
|
||||
description = S("TA4 RAM Chip"),
|
||||
inventory_image = "techage_ramchip.png",
|
||||
})
|
||||
|
||||
|
||||
techage.recipes.add("ta2_electronic_fab", {
|
||||
output = "techage:vacuum_tube 2",
|
||||
input = {"default:glass 1", "basic_materials:copper_wire 1", "basic_materials:plastic_sheet 1", "techage:usmium_nuggets 1"}
|
||||
})
|
||||
|
||||
techage.recipes.add("ta3_electronic_fab", {
|
||||
output = "techage:vacuum_tube 2",
|
||||
input = {"default:glass 1", "basic_materials:copper_wire 1", "basic_materials:plastic_sheet 1", "techage:usmium_nuggets 1"}
|
||||
})
|
||||
|
||||
techage.recipes.add("ta3_electronic_fab", {
|
||||
output = "techage:ta4_wlanchip 8",
|
||||
input = {"default:mese_crystal 1", "default:copper_ingot 1", "default:gold_ingot 1", "techage:ta4_silicon_wafer 1"}
|
||||
})
|
||||
|
||||
techage.recipes.add("ta3_electronic_fab", {
|
||||
output = "techage:ta4_ramchip 8",
|
||||
input = {"default:mese_crystal 1", "default:gold_ingot 1", "default:copper_ingot 1", "techage:ta4_silicon_wafer 1"}
|
||||
})
|
@ -188,7 +188,7 @@ minetest.register_craft({
|
||||
})
|
||||
|
||||
techage.register_node({"techage:ta3_playerdetector_off", "techage:ta3_playerdetector_on"}, {
|
||||
on_recv_message = function(pos, topic, payload)
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
if topic == "name" then
|
||||
local nvm = techage.get_nvm(pos)
|
||||
return nvm.player_name or ""
|
||||
|
2
mod.conf
@ -1,4 +1,4 @@
|
||||
name = techage
|
||||
depends = default,tubelib2,basic_materials,bucket,stairs,screwdriver,minecart
|
||||
depends = default,doors,tubelib2,basic_materials,bucket,stairs,screwdriver,minecart,lcdlib
|
||||
optional_depends = unified_inventory,wielded_light,unifieddyes
|
||||
description = Techage, go through 4 tech ages in search of wealth and power!
|
BIN
textures/techage_battery_green.png
Normal file
After Width: | Height: | Size: 322 B |
BIN
textures/techage_battery_inventory.png
Normal file
After Width: | Height: | Size: 260 B |
BIN
textures/techage_battery_red.png
Normal file
After Width: | Height: | Size: 322 B |
BIN
textures/techage_display.png
Normal file
After Width: | Height: | Size: 246 B |
BIN
textures/techage_display_inventory.png
Normal file
After Width: | Height: | Size: 241 B |
BIN
textures/techage_ramchip.png
Normal file
After Width: | Height: | Size: 811 B |
BIN
textures/techage_signaltower.png
Normal file
After Width: | Height: | Size: 486 B |
BIN
textures/techage_signaltower_amber.png
Normal file
After Width: | Height: | Size: 462 B |
BIN
textures/techage_signaltower_green.png
Normal file
After Width: | Height: | Size: 478 B |
BIN
textures/techage_signaltower_red.png
Normal file
After Width: | Height: | Size: 467 B |
BIN
textures/techage_signaltower_top.png
Normal file
After Width: | Height: | Size: 193 B |
BIN
textures/techage_smartline.png
Normal file
After Width: | Height: | Size: 320 B |
BIN
textures/techage_ta4_controller.png
Normal file
After Width: | Height: | Size: 556 B |
BIN
textures/techage_ta4_controller_inventory.png
Normal file
After Width: | Height: | Size: 835 B |