mods that hopefully work for a TechAge-based pack

This commit is contained in:
tangent committed 2026-09-09 06:27:51 -06:00
1 parent 4e6b08d194
commit c27db2a537
6234 files changed
+375578

No files matched your search

@@ -0,0 +1,871 @@
--[[
Basic Terminal
==============
Copyright (C) 2018-2025 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
terminal.lua:
]]--
local M = minetest.get_meta
local S = techage.S
local SCREENSAVER_TIME = 60 * 5
local CYCLE_TIME = 0.1
local Functions = {}
local Actions = {}
local Buttons = {"Edit", "Save", "Renum", "Cancel", "Run", "Stop", "Continue", "List"}
local States = {"init", "edit", "stopped", "running", "error", "input_str", "input_num", "break"}
local ErrorStr = {
[1] = "Node not found",
[2] = "Command not supported",
[3] = "Command failed",
[4] = "Access denied",
[5] = "Wrong response type",
[6] = "Wrong number of parameters",
}
local InputField = "style_type[field;textcolor=#FFFFFF]" ..
"field[1.5,0.8;7.4,0.7;input;;]" ..
"field_close_on_enter[input;false]" ..
"button[9,0.8;1.5,0.7;Enter;Enter]"
local WRENCH_MENU = {
{
type = "dropdown",
choices = "all players,friends,me",
name = "public",
label = S("Access allowed for"),
tooltip = S("Friends are players for whom this area is not protected"),
default = "1",
values = {1,2,0}
},
{
type = "dropdown",
choices = "terminal,basic",
name = "opmode",
label = S("Operational mode"),
tooltip = S("Switch between TA3 terminal and BASIC computer"),
default = "terminal",
},
}
local function register_ext_function(name, param_types, return_type, func)
Functions[nanobasic.add_function(name, param_types, return_type)] = func
end
local function register_action(states, key, func)
for _,state in ipairs(states) do
Actions[state] = Actions[state] or {}
Actions[state][key] = func
end
end
local function font_size(nvm)
nvm.trm_text_size = nvm.trm_text_size or 0
return nvm.trm_text_size >= 0 and "+" .. nvm.trm_text_size or tostring(nvm.trm_text_size)
end
local function fs_output_window(nvm, x, y, name, text)
local font_size = font_size(nvm)
local fs = {
"container[", x, ",", y, "]",
"box[0,0;12,7.5;#000000]",
"style_type[textarea;font=mono;",
"textcolor=#FFFFFF;border=false;",
"font_size=", font_size, "]",
"textarea[0,0;12,7.5;" .. name .. ";;",
minetest.formspec_escape(text), "]",
"container_end[]"
}
return table.concat(fs, "")
end
local function fs_size_buttons(x, y)
local fs = {
"container[", x, ",", y, "]",
"button[0.0,0;0.6,0.6;larger;+]",
"button[0.6,0;0.6,0.6;smaller;-]",
"container_end[]"
}
return table.concat(fs, "")
end
local function key_rows(keys)
local t = {}
for i = 1, #keys do
local x = (i - 1) * 1.5
if keys[i] ~= "" then
t[#t+1] = "button[" .. x .. ",0;1.5,0.7;" .. keys[i] .. ";" .. keys[i] .. "]"
else
t[#t+1] = "button[" .. x .. ",0;1.5,0.7;;---]"
end
end
return table.concat(t, "")
end
local function input_panel(nvm, x, y)
local fs = {
"container[", x, ",", y, "]",
key_rows(nvm.bttns or Buttons),
nvm.input or "",
"container_end[]"
}
return table.concat(fs, "")
end
local function get_action(nvm, fields)
local keys = {"Edit", "Save", "Renum", "Cancel", "Run", "Stop", "Continue", "List", "Enter", "smaller", "larger"}
nvm.status = nvm.status or "init"
if nvm.status == "" then
nvm.status = "init"
end
--print("get_action", nvm.status, dump(fields))
for _,key in ipairs(keys) do
if fields[key] and Actions[nvm.status] and Actions[nvm.status][key] then
return Actions[nvm.status][key]
end
end
return function(pos, nvm, fields)
return
end
end
local function formspec(pos, text)
local nvm = techage.get_nvm(pos)
local name = nvm.status == "edit" and "code" or ""
return "formspec_version[4]" ..
"size[12.8,10.5]" ..
"label[0.5,0.6;Mode: " .. (nvm.status or "") .. "]" ..
fs_size_buttons(10.6, 0.1) ..
techage.wrench_image(11.9, 0.15) ..
fs_output_window(nvm, 0.4, 0.8, name, text or "") ..
input_panel(nvm, 0.4, 8.6)
end
local function poweron_message(pos)
local s = nanobasic.free_mem() or ""
local ver = nanobasic.version()
return "NanoBasic V" .. ver .. "\n" .. s .. "Ready.\n"
end
-- Lines have line numbers at the beginning, like: "10 PRINT "Hello World"
-- This function sorts the lines by the line numbers
local function sort_lines(pos, nvm, code)
local lines = {}
local keys = {}
for line in code:gmatch("[^\r\n]+") do
local num = tonumber(line:match("^%s*(%d+)"))
if num then
if lines[num] then
nanobasic.print(pos, "Line number " .. num .. " is already used\n")
return
end
lines[num] = line
keys[#keys + 1] = num
else
nanobasic.print(pos, "Line number missing in line '" .. line .. "'\n")
return
end
end
table.sort(keys)
local sorted = {}
for i,num in ipairs(keys) do
sorted[i] = lines[num]
end
return table.concat(sorted, "\n")
end
local function replace_all_goto_refs(lines, new_nums)
for num,line in pairs(lines) do
local goto_num = line:match("GOTO%s+(%d+)")
if goto_num then
local new_num = new_nums[goto_num]
if new_num then
lines[num] = line:gsub("GOTO%s+%d+", "GOTO " .. new_num)
end
end
goto_num = line:match("GOSUB%s+(%d+)")
if goto_num then
local new_num = new_nums[goto_num]
if new_num then
lines[num] = line:gsub("GOSUB%s+%d+", "GOSUB " .. new_num)
end
end
goto_num = line:match("goto%s+(%d+)")
if goto_num then
local new_num = new_nums[goto_num]
if new_num then
lines[num] = line:gsub("goto%s+%d+", "goto " .. new_num)
end
end
goto_num = line:match("gosub%s+(%d+)")
if goto_num then
local new_num = new_nums[goto_num]
if new_num then
lines[num] = line:gsub("gosub%s+%d+", "gosub " .. new_num)
end
end
end
end
local function renumber_lines(pos, nvm, code)
local lines = {}
local new_nums = {}
local num = 10
for line in code:gmatch("[^\r\n]+") do
local s = line:match("^%s*(%d+)")
if s and tonumber(s) < num then
lines[#lines + 1] = num .. line:sub(s:len() + 1)
new_nums[s] = num
num = num + 10
else
lines[#lines + 1] = line
num = tonumber(s) + 10
end
end
replace_all_goto_refs(lines, new_nums)
return table.concat(lines, "\n")
end
minetest.register_node("techage:basic_terminal", {
description = S("TA3 Terminal"),
tiles = {-- up, down, right, left, back, front
'techage_terminal2_top.png',
'techage_terminal2_side.png',
'techage_terminal2_side.png^[transformFX',
'techage_terminal2_side.png',
'techage_terminal2_back.png',
"techage_terminal2_front.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{-12/32, -16/32, -16/32, 12/32, -14/32, 16/32},
{-12/32, -14/32, -3/32, 12/32, 6/32, 16/32},
{-10/32, -12/32, 14/32, 10/32, 4/32, 18/32},
{-12/32, 4/32, -4/32, 12/32, 6/32, 16/32},
{-12/32, -16/32, -4/32, -10/32, 6/32, 16/32},
{ 10/32, -16/32, -4/32, 12/32, 6/32, 16/32},
{-12/32, -14/32, -4/32, 12/32, -12/32, 16/32},
},
},
selection_box = {
type = "fixed",
fixed = {
{-12/32, -16/32, -4/32, 12/32, 6/32, 16/32},
},
},
after_place_node = function(pos, placer)
local number = techage.add_node(pos, minetest.get_node(pos).name)
local nvm = techage.get_nvm(pos)
local meta = M(pos)
local text = poweron_message(pos)
nvm.trm_ttl = 0
nvm.status = "init"
nvm.bttns = Buttons
nvm.input = ""
meta:set_int("public", 0)
meta:set_string("formspec", formspec(pos, text))
if placer then
meta:set_string("owner", placer:get_player_name())
end
meta:set_string("infotext", S("TA3 Terminal"))
end,
on_receive_fields = function(pos, formname, fields, player)
local nvm = techage.get_nvm(pos)
local meta = M(pos)
local public = meta:get_int("public")
if public == 1 or
public == 2 and not minetest.is_protected(pos, player:get_player_name()) or
public == 0 and player:get_player_name() == meta:get_string("owner") then
fields.Enter = fields.Enter or fields.key_enter_field
local action = get_action(nvm, fields)
local text = action(pos, nvm, fields, player)
if text then
meta:set_string("formspec", formspec(pos, text))
end
end
end,
on_timer = function(pos, elapsed)
local nvm = techage.get_nvm(pos)
if nvm.ttl and nvm.ttl > 1 then
nvm.ttl = nvm.ttl - 1
return true
end
if nvm.status == "running" then
local res = nanobasic.run(pos, 100)
if res == nanobasic.NB_BUSY then
if techage.is_activeformspec(pos) then
local text = nanobasic.get_screen_buffer(pos)
M(pos):set_string("formspec", formspec(pos, text))
end
return true
elseif res == nanobasic.NB_ERROR then
nvm.status = "error"
nvm.bttns = {"Edit", "", "", "", "", "Stop", "", ""}
nvm.input = ""
nvm.ttl = nil
local text = nanobasic.get_screen_buffer(pos)
M(pos):set_string("formspec", formspec(pos, text))
elseif res == nanobasic.NB_END then
nvm.status = "stopped"
nvm.bttns = {"Edit", "", "", "", "Run", "Stop", "", ""}
nvm.input = ""
nvm.ttl = nil
local text = nanobasic.get_screen_buffer(pos)
M(pos):set_string("formspec", formspec(pos, text))
elseif res == nanobasic.NB_BREAK then
local lineno = nanobasic.pop_num(pos);
nanobasic.print(pos, string.format("Break in line %d\n", lineno));
nvm.status = "break"
nvm.bttns = {"", "", "", "", "", "Stop", "Continue", "List"}
nvm.input = InputField
nvm.ttl = nil
local text = nanobasic.get_screen_buffer(pos)
M(pos):set_string("formspec", formspec(pos, text))
elseif res >= nanobasic.NB_XFUNC then
return Functions[res] and Functions[res](pos, nvm) or false
else
print("res = ", res)
return false
end
end
return false
end,
on_rightclick = function(pos, node, clicker)
local nvm = techage.get_nvm(pos)
local text
nvm.trm_ttl = minetest.get_gametime() + SCREENSAVER_TIME
if nvm.status == "edit" then
text = M(pos):get_string("code")
else
text = nanobasic.get_screen_buffer(pos) or ""
end
techage.set_activeformspec(pos, clicker)
M(pos):set_string("formspec", formspec(pos, text))
end,
ta_after_formspec = function(pos, fields, playername)
if fields.save then
if M(pos):get_string("opmode") == "terminal" then
local node = techage.get_node_lvm(pos)
node.name = "techage:terminal2"
minetest.swap_node(pos, node)
local ndef = minetest.registered_nodes["techage:terminal2"]
ndef.after_place_node(pos)
end
end
end,
after_dig_node = function(pos, oldnode, oldmetadata)
techage.remove_node(pos, oldnode, oldmetadata)
nanobasic.destroy(pos)
end,
ta3_formspec = WRENCH_MENU,
drop = "techage:terminal2",
paramtype = "light",
use_texture_alpha = "clip",
sunlight_propagates = true,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1},
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
})
--
-- Register VM external/callback functions
--
local function get_params(pos)
local payload3 = nanobasic.pop_num(pos) or 0
if payload3 >= 0x8000000 then
payload3 = payload3 - 0x100000000
end
local payload2 = nanobasic.pop_num(pos) or 0
if payload2 >= 0x8000000 then
payload2 = payload2 - 0x100000000
end
local payload1 = nanobasic.pop_num(pos) or 0
if payload1 >= 0x8000000 then
payload1 = payload1 - 0x100000000
end
local cmnd = nanobasic.pop_num(pos)
local num = nanobasic.pop_num(pos) or 0
local owner = M(pos):get_string("owner")
local own_num = M(pos):get_string("node_number")
return owner, num, own_num, {payload1, payload2, payload3}
end
local function get_str_param(pos)
local payload3 = nanobasic.pop_num(pos) -- dummy value
local payload2 = nanobasic.pop_num(pos) -- dummy value
local payload1 = nanobasic.pop_str(pos) or ""
local cmnd = nanobasic.pop_num(pos)
local num = nanobasic.pop_num(pos) or 0
local owner = M(pos):get_string("owner")
local own_num = M(pos):get_string("node_number")
return owner, num, own_num, payload1
end
local function error_handling(pos, num, sts)
local nvm = techage.get_nvm(pos)
if sts > 0 and nvm.error_label_addr and nvm.error_label_addr > 0 then
local err = ErrorStr[sts] or "unknown error"
nanobasic.push_num(pos, num)
nanobasic.push_str(pos, err)
nanobasic.set_pc(pos, nvm.error_label_addr)
end
end
register_ext_function("input", {nanobasic.NB_STR}, nanobasic.NB_NUM, function(pos, nvm)
nvm.status = "input_num"
local s = nanobasic.pop_str(pos)
nanobasic.print(pos, s .. "? ")
nvm.bttns = {"", "", "", "", "", "Stop", "", ""}
nvm.input = InputField
local text = nanobasic.get_screen_buffer(pos) or ""
M(pos):set_string("formspec", formspec(pos, text))
return false -- stop execution
end)
register_ext_function("input$", {nanobasic.NB_STR}, nanobasic.NB_STR, function(pos, nvm)
nvm.status = "input_str"
local s = nanobasic.pop_str(pos)
nanobasic.print(pos, s .. "? ")
nvm.bttns = {"", "", "", "", "", "Stop", "", ""}
nvm.input = InputField
local text = nanobasic.get_screen_buffer(pos) or ""
M(pos):set_string("formspec", formspec(pos, text))
return false -- stop execution
end)
register_ext_function("sleep", {nanobasic.NB_NUM}, nanobasic.NB_NONE, function(pos, nvm)
local t = nanobasic.pop_num(pos) or 0
nvm.ttl = t / CYCLE_TIME
if techage.is_activeformspec(pos) then
local text = nanobasic.get_screen_buffer(pos)
M(pos):set_string("formspec", formspec(pos, text))
end
return true
end)
register_ext_function("time", {}, nanobasic.NB_NUM, function(pos, nvm)
nanobasic.push_num(pos, minetest.get_gametime() or 0)
return true
end)
register_ext_function("daytime", {}, nanobasic.NB_NUM, function(pos, nvm)
nanobasic.push_num(pos, math.floor(minetest.get_timeofday() * 1440) or 0)
return true
end)
register_ext_function("daytime$", {nanobasic.NB_NUM}, nanobasic.NB_STR, function(pos, nvm)
local british = nanobasic.pop_num(pos) or 0
local t = minetest.get_timeofday()
local h = math.floor(t * 24) % 24
local m = math.floor(t * 1440) % 60
if british == 1 then
if h < 12 then
nanobasic.push_str(pos, string.format("%02d:%02d am", h, m))
else
nanobasic.push_str(pos, string.format("%02d:%02d pm", h - 12, m))
end
else
nanobasic.push_str(pos, string.format("%02d:%02d", h, m))
end
return true
end)
register_ext_function("hold", {}, nanobasic.NB_NONE, function(pos, nvm)
local own_num = M(pos):get_string("node_number")
techage.cmnd_hold(own_num)
return true
end)
register_ext_function("release", {}, nanobasic.NB_NONE, function(pos, nvm)
local own_num = M(pos):get_string("node_number")
techage.cmnd_release(own_num)
return true
end)
-- num: cmd(num: node_num, num: cmnd, any: pyld1, num: pyld2, num: pyld3)
register_ext_function("cmd", {nanobasic.NB_NUM, nanobasic.NB_NUM, nanobasic.NB_ANY, nanobasic.NB_ANY, nanobasic.NB_ANY}, nanobasic.NB_NUM, function(pos, nvm)
local cmnd = nanobasic.peek_num(pos, 4) or 0
if cmnd < 64 then -- command with payload as number(s)
local owner, num, own_num, payload = get_params(pos)
if techage.not_protected(tostring(num), owner) then
techage.counting_add(owner, 1)
local sts, resp = techage.beduino_send_cmnd(own_num, num, cmnd, payload)
nanobasic.push_num(pos, sts)
error_handling(pos, num, sts)
else
nanobasic.push_num(pos, 4)
error_handling(pos, num, 4)
end
elseif cmnd < 128 then -- command with payload as string
local owner, num, own_num, payload = get_str_param(pos)
if techage.not_protected(tostring(num), owner) then
techage.counting_add(owner, 1)
local sts, resp = techage.beduino_send_cmnd(own_num, num, cmnd, payload)
nanobasic.push_num(pos, sts)
error_handling(pos, num, sts)
else
nanobasic.push_num(pos, 4)
error_handling(pos, num, 4)
end
elseif cmnd < 192 then -- request with payload as number(s) and result as number
local owner, num, own_num, payload = get_params(pos)
if techage.not_protected(tostring(num), owner) then
techage.counting_add(owner, 1)
local sts, resp = techage.beduino_request_data(own_num, num, cmnd, payload)
if type(resp) == "table" then
nanobasic.push_num(pos, resp[1] or 0)
else
nanobasic.push_num(pos, 5)
sts = 5
end
error_handling(pos, num, sts)
else
nanobasic.push_num(pos, 4)
error_handling(pos, num, 4)
end
else
local owner, num, own_num, payload = get_str_param(pos)
if techage.not_protected(tostring(num), owner) then
techage.counting_add(owner, 1)
local sts, resp = techage.beduino_request_data(own_num, num, cmnd, payload)
if type(resp) == "table" then
nanobasic.push_num(pos, resp[1] or 0)
else
nanobasic.push_num(pos, 5)
sts = 5
end
error_handling(pos, num, sts)
else
nanobasic.push_num(pos, 4)
error_handling(pos, num, 4)
end
end
return true
end)
-- str: cmd(num: node_num, num: cmnd, any: pyld1, any: pyld2, num: pyld3)
register_ext_function("cmd$", {nanobasic.NB_NUM, nanobasic.NB_NUM, nanobasic.NB_ANY, nanobasic.NB_ANY, nanobasic.NB_ANY}, nanobasic.NB_STR, function(pos, nvm)
local cmnd = nanobasic.peek_num(pos, 4) or 0
if cmnd >= 128 then -- request with payload as number(s) and result as string
local owner, num, own_num, payload = get_params(pos)
if techage.not_protected(tostring(num), owner) then
techage.counting_add(owner, 1)
local sts, resp = techage.beduino_request_data(own_num, num, cmnd, payload)
if type(resp) == "string" then
nanobasic.push_str(pos, resp)
else
nanobasic.push_str(pos, "")
sts = 5
end
error_handling(pos, num, sts)
else
nanobasic.push_str(pos, "")
error_handling(pos, num, 4)
end
end
return true
end)
-- none: chat(str: msg)
register_ext_function("chat", {nanobasic.NB_STR}, nanobasic.NB_NONE, function(pos, nvm)
local msg = nanobasic.pop_str(pos) or ""
local owner = M(pos):get_string("owner")
minetest.chat_send_player(owner, msg)
return true
end)
-- none: dputs(num: node_num, num: row, str: text)
register_ext_function("dputs", {nanobasic.NB_NUM, nanobasic.NB_NUM, nanobasic.NB_STR}, nanobasic.NB_NONE, function(pos, nvm)
local text = nanobasic.pop_str(pos) or ""
local row = nanobasic.pop_num(pos) or 0
local num = nanobasic.pop_num(pos) or 0
local own_num = M(pos):get_string("node_number")
local owner = M(pos):get_string("owner")
if techage.not_protected(tostring(num), owner) then
techage.counting_add(owner, 1)
if row == 0 then -- add line
techage.send_single(own_num, num, "add", text)
else
local payload = safer_lua.Store()
payload.set("row", row)
payload.set("str", text)
techage.send_single(own_num, num, "set", payload)
end
end
return true
end)
-- none: dclr(num: node_num)
register_ext_function("dclr", {nanobasic.NB_NUM}, nanobasic.NB_NONE, function(pos, nvm)
local num = nanobasic.pop_num(pos) or 0
local own_num = M(pos):get_string("node_number")
local owner = M(pos):get_string("owner")
if techage.not_protected(tostring(num), owner) then
techage.counting_add(owner, 1)
techage.send_single(own_num, num, "clear", nil)
end
return true
end)
-- none: door(str: node_pos, str: state)
register_ext_function("door", {nanobasic.NB_STR, nanobasic.NB_STR}, nanobasic.NB_NONE, function(pos, nvm)
local state = nanobasic.pop_str(pos) or ""
local spos = nanobasic.pop_str(pos) or 0
local doorpos = minetest.string_to_pos("(" .. spos .. ")")
local owner = M(pos):get_string("owner")
if pos then
local door = doors.get(doorpos)
if door then
techage.counting_add(owner, 1)
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
return true
end)
-- str: iname(str: item_name)
register_ext_function("iname$", {nanobasic.NB_STR}, nanobasic.NB_STR, function(pos, nvm)
local item_name = nanobasic.pop_str(pos) or ""
local item = minetest.registered_items[item_name]
if item and item.description then
local s = minetest.get_translated_string("en", item.description)
nanobasic.push_str(pos, s or "")
else
nanobasic.push_str(pos, "")
end
return true
end)
register_ext_function("reset", {}, nanobasic.NB_NONE, function(pos, nvm)
nanobasic.reset(pos)
return true
end)
--
-- Register user input actions: register_action(states, key, function)
--
register_action({"init", "stopped", "error", "break"}, "Edit", function(pos, nvm, fields)
nvm.status = "edit"
nvm.bttns = {"", "Save", "Renum", "Cancel", "Run", "", "", ""}
nvm.input = ""
return M(pos):get_string("code")
end)
register_action({"edit"}, "Save", function(pos, nvm, fields)
nanobasic.clear_screen(pos)
local code = sort_lines(pos, nvm, fields.code)
if code == nil then
nvm.status = "error"
nvm.bttns = {"Edit", "", "", "", "", "Stop", "", ""}
nvm.input = ""
M(pos):set_string("code", fields.code)
return nanobasic.get_screen_buffer(pos) or ""
end
M(pos):set_string("code", code)
return code
end)
register_action({"edit"}, "Renum", function(pos, nvm, fields)
nanobasic.clear_screen(pos)
local code = sort_lines(pos, nvm, fields.code)
if code == nil then
nvm.status = "error"
nvm.bttns = {"Edit", "", "", "", "", "Stop", "", ""}
nvm.input = ""
M(pos):set_string("code", fields.code)
return nanobasic.get_screen_buffer(pos) or ""
end
code = renumber_lines(pos, nvm, code)
M(pos):set_string("code", code)
return code
end)
register_action({"edit"}, "Cancel", function(pos, nvm, fields)
nvm.status = "stopped"
nvm.bttns = {"Edit", "", "", "", "Run", "Stop", "", ""}
nvm.input = ""
return nanobasic.get_screen_buffer(pos) or ""
end)
register_action({"init", "edit", "stopped"}, "Run", function(pos, nvm, fields)
if nvm.status == "edit" then
M(pos):set_string("code", fields.code)
end
local code = M(pos):get_string("code")
if nanobasic.create(pos, code) then
nvm.status = "running"
nvm.bttns = {"", "", "", "", "", "Stop", "", "List"}
nvm.input = ""
nvm.variables = nanobasic.get_variable_list(pos)
nvm.onload_label_addr = nanobasic.get_label_address(pos, "64000") or 0
nvm.error_label_addr = nanobasic.get_label_address(pos, "65000") or 0
nvm.ttl = nil
minetest.get_node_timer(pos):start(CYCLE_TIME)
return nanobasic.get_screen_buffer(pos) or ""
else
nvm.status = "error"
nvm.bttns = {"Edit", "", "", "", "", "Stop", "", ""}
nvm.input = ""
return nanobasic.get_screen_buffer(pos) or ""
end
end)
register_action({"break"}, "Continue", function(pos, nvm, fields)
nvm.status = "running"
nvm.bttns = {"", "", "", "", "", "Stop", "", "List"}
nvm.input = ""
minetest.get_node_timer(pos):start(CYCLE_TIME)
return nanobasic.get_screen_buffer(pos) or ""
end)
register_action({"break"}, "List", function(pos, nvm, fields)
nvm.status = "break"
nvm.bttns = {"", "", "", "", "", "Stop", "Continue", "List"}
nvm.input = InputField
return M(pos):get_string("code")
end)
register_action({"running"}, "List", function(pos, nvm, fields, player)
nvm.bttns = {"", "", "", "", "", "Stop", "Continue", ""}
nvm.input = ""
techage.reset_activeformspec(pos, player)
return M(pos):get_string("code")
end)
register_action({"running"}, "Continue", function(pos, nvm, fields, player)
nvm.bttns = {"", "", "", "", "", "Stop", "", "List"}
nvm.input = ""
techage.set_activeformspec(pos, player)
return nanobasic.get_screen_buffer(pos) or ""
end)
register_action({"break"}, "Enter", function(pos, nvm, fields)
nvm.status = "break"
nvm.bttns = {"", "", "", "", "", "Stop", "Continue", "List"}
nvm.input = InputField
local s = fields.input:lower()
local var_name, arr_idx = s:match('^(%w+)%s*,%s*([0-9]*)$')
if var_name == nil then
var_name, arr_idx = s, "0"
end
if nvm.variables[var_name] then
arr_idx = tonumber(arr_idx)
local var_type, var_idx = nvm.variables[var_name][1], nvm.variables[var_name][2]
local val = nanobasic.read_variable(pos, var_type, var_idx, arr_idx)
if var_type == nanobasic.NB_NUM then
nanobasic.print(pos, string.format("%s = %u\n", var_name, val));
elseif var_type == nanobasic.NB_STR then
nanobasic.print(pos, string.format("%s = \"%s\"\n", var_name, val));
else
nanobasic.print(pos, string.format("%s(%u) = %u\n", var_name, arr_idx, val));
end
else
nanobasic.print(pos, string.format("Variable '%s' is unknown\n", var_name));
end
return nanobasic.get_screen_buffer(pos) or ""
end)
register_action({"stopped", "init"}, "Stop", function(pos, nvm, fields)
nvm.status = "init"
nanobasic.print(pos, "\nProgram stopped.\n")
nvm.bttns = Buttons
nvm.input = ""
nanobasic.clear_screen(pos)
return poweron_message(pos)
end)
register_action({"running", "input_num", "input_str"}, "Stop", function(pos, nvm, fields)
nvm.status = "stopped"
nanobasic.print(pos, "\nProgram stopped.\n")
nvm.bttns = {"Edit", "", "", "", "Run", "Stop", "", ""}
nvm.input = ""
return nanobasic.get_screen_buffer(pos) or ""
end)
register_action({"break", "error"}, "Stop", function(pos, nvm, fields)
nvm.status = "stopped"
nanobasic.print(pos, "\nProgram stopped.\n")
nvm.bttns = {"Edit", "", "", "", "Run", "Stop", "", ""}
nvm.input = ""
return nanobasic.get_screen_buffer(pos) or ""
end)
register_action(States, "larger", function(pos, nvm, fields)
nvm.trm_text_size = math.min((nvm.trm_text_size or 0) + 1, 8)
return fields.code or nanobasic.get_screen_buffer(pos) or ""
end)
register_action(States, "smaller", function(pos, nvm, fields)
nvm.trm_text_size = math.max((nvm.trm_text_size or 0) - 1, -8)
return fields.code or nanobasic.get_screen_buffer(pos) or ""
end)
register_action(States, "quit", function(pos, nvm, fields)
nvm.trm_ttl = 0
return ""
end)
register_action({"input_num"}, "Enter", function(pos, nvm, fields)
nanobasic.print(pos, fields.input .. "\n")
nanobasic.push_num(pos, tonumber(fields.input) or 0)
nvm.status = "running"
nvm.bttns = {"", "", "", "", "", "Stop", "", ""}
nvm.input = ""
minetest.get_node_timer(pos):start(CYCLE_TIME)
return nanobasic.get_screen_buffer(pos) or ""
end)
register_action({"input_str"}, "Enter", function(pos, nvm, fields)
nanobasic.print(pos, fields.input .. "\n")
nanobasic.push_str(pos, fields.input)
nvm.status = "running"
nvm.bttns = {"", "", "", "", "", "Stop", "", ""}
nvm.input = ""
minetest.get_node_timer(pos):start(CYCLE_TIME)
return nanobasic.get_screen_buffer(pos) or ""
end)
techage.register_node({"techage:basic_terminal"}, {
on_node_load = function(pos)
nanobasic.vm_restore(pos)
local nvm = techage.get_nvm(pos)
if nvm.status == "running" then
if nvm.onload_label_addr and nvm.onload_label_addr > 0 then
nanobasic.set_pc(pos, nvm.onload_label_addr)
end
minetest.get_node_timer(pos):start(CYCLE_TIME)
end
end,
})
@@ -0,0 +1,516 @@
--[[
TechAge
=======
Copyright (C) 2017-2023 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA3 & TA4 Logic button
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local NDEF = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}) end
local logic = techage.logic
local WRENCH_MENU3 = {
{
type = "dropdown",
choices = "switch,on button,button 1s,button 2s,button 4s,button 8s,button 16s,button 32s",
name = "type",
label = S("Type"),
tooltip = S("Button or switch"),
default = "1",
},
{
type = "numbers",
name = "numbers",
label = S("Number"),
tooltip = S("Destination block number(s)"),
default = "",
check = techage.check_numbers,
},
{
type = "dropdown",
choices = "private,protected,public",
name = "access",
label = S("Access"),
tooltip = S("Button protection"),
default = "1",
},
}
local WRENCH_MENU4 = {
{
type = "dropdown",
choices = "switch,on button,button 1s,button 2s,button 4s,button 8s,button 16s,button 32s",
name = "type",
label = S("Type"),
tooltip = S("Button or switch"),
default = "1",
},
{
type = "numbers",
name = "numbers",
label = S("Number"),
tooltip = S("Destination block number(s)"),
default = "",
check = techage.check_numbers,
},
{
type = "ascii",
name = "command",
label = S("Command"),
tooltip = S("Command to be sent"),
default = "on",
},
{
type = "dropdown",
choices = "private,protected,public",
name = "access",
label = S("Access"),
tooltip = S("Button protection"),
default = "1",
},
{
type = "ascii",
name = "decription",
label = S("Infotext"),
tooltip = S("Change the block name (infotext)"),
default = "",
},
}
local function switch_on(pos, no_sound)
local cycle_time = M(pos):get_int("cycle_time")
local name = techage.get_node_lvm(pos).name
if name == "techage:ta3_button_off" then
logic.swap_node(pos, "techage:ta3_button_on")
elseif name == "techage:ta4_button_off" then
logic.swap_node(pos, "techage:ta4_button_on")
end
logic.guarded_action(pos, logic.send_cmnd, pos, "command", "on", cycle_time)
if not no_sound then
minetest.sound_play("techage_button", {
pos = pos,
gain = 0.5,
max_hear_distance = 5,
})
end
end
local function switch_off(pos, no_sound)
local name = techage.get_node_lvm(pos).name
if name == "techage:ta3_button_on" then
logic.swap_node(pos, "techage:ta3_button_off")
elseif name == "techage:ta4_button_on" then
logic.swap_node(pos, "techage:ta4_button_off")
end
local meta = M(pos)
if meta:get_string("off_command") ~= "true" and
(not meta:contains("command") or meta:get_string("command") == "on") then
logic.guarded_action(pos, logic.send_off, pos, M(pos))
end
if not no_sound then
minetest.sound_play("techage_button", {
pos = pos,
gain = 0.5,
max_hear_distance = 5,
})
end
end
local function formspec(meta)
local numbers = meta:get_string("numbers") or ""
local idx = meta:get_int("cycle_idx") or 0
if idx == 0 then idx = 1 end
local access_idx = meta:get_string("public") == "true" and 3 or meta:get_string("protected") == "true" and 2 or 1
return "size[7.5,6]"..
"dropdown[0.2,0;3;type;switch,on button,button 1s,button 2s,button 4s,button 8s,button 16s,button 32s;"..idx.."]"..
"field[0.5,2;7,1;numbers;"..S("Insert destination node number(s)")..";"..numbers.."]" ..
"label[0.2,3;"..S("Access:").."]"..
"dropdown[3,3;4;access;private,protected,public;"..access_idx.."]"..
"button_exit[2,4;3,1;exit;"..S("Save").."]"
end
local function store_fields_data(pos, fields)
local meta = M(pos)
meta:set_string("numbers", fields.numbers)
meta:set_string("off_command", "")
if fields.access == "protected" then
meta:set_string("protected", "true")
meta:set_string("public", "")
end
if fields.access == "public" then
meta:set_string("public", "true")
meta:set_string("protected", "")
end
if fields.access == "private" then
meta:set_string("public", "")
meta:set_string("protected", "")
end
local cycle_time = nil
if fields.type == "switch" then
meta:set_int("cycle_idx", 1)
cycle_time = 0
elseif fields.type == "on button" then
meta:set_int("cycle_idx", 2)
meta:set_string("off_command", "true")
cycle_time = 1
elseif fields.type == "button 1s" then
meta:set_int("cycle_idx", 3)
cycle_time = 1
elseif fields.type == "button 2s" then
meta:set_int("cycle_idx", 4)
cycle_time = 2
elseif fields.type == "button 4s" then
meta:set_int("cycle_idx", 5)
cycle_time = 4
elseif fields.type == "button 8s" then
meta:set_int("cycle_idx", 6)
cycle_time = 8
elseif fields.type == "button 16s" then
meta:set_int("cycle_idx", 7)
cycle_time = 16
elseif fields.type == "button 32s" then
meta:set_int("cycle_idx", 8)
cycle_time = 32
end
if cycle_time ~= nil then
meta:set_int("cycle_time", cycle_time)
end
meta:set_string("access", fields.access)
meta:set_string("type", fields.type)
end
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
if not techage.check_numbers(fields.numbers, player:get_player_name()) then
return
end
store_fields_data(pos, fields)
local meta = M(pos)
logic.infotext(meta, NDEF(pos).description)
if fields.exit then
meta:set_string("formspec", "")
meta:set_string("fixed" , "true")
else
meta:set_string("formspec", formspec(meta))
end
end
local function ta_after_formspec(pos, fields, playername)
store_fields_data(pos, fields)
local meta = M(pos)
if fields.decription and fields.decription ~= "" then
logic.infotext(meta, fields.decription)
else
logic.infotext(meta, NDEF(pos).description)
end
end
local function can_access(pos, player)
local meta = M(pos)
local public = meta:get_string("public") == "true"
local protected = meta:get_string("protected") == "true"
local owner = meta:get_string("owner")
local name = player:get_player_name()
return public or protected and not minetest.is_protected(pos, name) or owner == name
end
local function on_rightclick_on(pos, node, clicker)
local meta = M(pos)
local fixed = meta:get_string("fixed")
if fixed == "true" then
if can_access(pos, clicker) then
switch_on(pos)
local mem = techage.get_mem(pos)
mem.clicker = clicker and clicker:get_player_name()
mem.time = math.floor(minetest.get_us_time() / 100000)
end
end
end
local function on_rightclick_off(pos, node, clicker)
local meta = M(pos)
local numbers = meta:get_string("numbers")
local cycle_time = meta:get_int("cycle_time") or 0
if numbers ~= "" and numbers ~= nil and cycle_time == 0 then
if can_access(pos, clicker) then
switch_off(pos)
end
end
end
local function techage_set_numbers(pos, numbers, player_name)
local meta = M(pos)
local res = logic.set_numbers(pos, numbers, player_name, NDEF(pos).description)
meta:set_string("formspec", formspec(meta))
return res
end
local function after_dig_node(pos, oldnode, oldmetadata, digger)
techage.remove_node(pos, oldnode, oldmetadata)
end
minetest.register_node("techage:ta3_button_off", {
description = S("TA3 Button/Switch"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_button.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_button.png",
"techage_filling_ta3.png^techage_frame_ta3.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_button_off.png",
},
after_place_node = function(pos, placer)
local meta = M(pos)
logic.after_place_node(pos, placer, "techage:ta3_button_off", S("TA3 Button/Switch"))
logic.infotext(meta, S("TA3 Button/Switch"))
meta:set_string("formspec", formspec(meta))
meta:set_string("public", "false")
meta:set_int("cycle_time", 0)
end,
ta3_formspec = WRENCH_MENU3,
ta_after_formspec = ta_after_formspec,
on_receive_fields = on_receive_fields,
on_rightclick = on_rightclick_on,
techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node,
on_rotate = screwdriver.disallow,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
})
minetest.register_node("techage:ta3_button_on", {
description = ("TA3 Button/Switch"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_button.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_button.png",
"techage_filling_ta3.png^techage_frame_ta3.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_button_on.png",
},
ta3_formspec = WRENCH_MENU3,
ta_after_formspec = ta_after_formspec,
on_rightclick = on_rightclick_off,
on_timer = switch_off,
on_rotate = screwdriver.disallow,
techage_set_numbers = techage_set_numbers,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
drop = "techage:ta3_button_off",
})
minetest.register_node("techage:ta4_button_off", {
description = S("TA4 Button/Switch"),
inventory_image = "techage_smartline_button_inv.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_smartline_button_off.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 = M(pos)
logic.after_place_node(pos, placer, "techage:ta4_button_off", S("TA4 Button/Switch"))
logic.infotext(meta, S("TA4 Button/Switch"))
meta:set_string("formspec", formspec(meta))
meta:set_string("public", "false")
meta:set_int("cycle_time", 0)
end,
ta4_formspec = WRENCH_MENU4,
ta_after_formspec = ta_after_formspec,
on_receive_fields = on_receive_fields,
on_rightclick = on_rightclick_on,
techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node,
on_rotate = screwdriver.disallow,
paramtype = "light",
use_texture_alpha = techage.CLIP,
sunlight_propagates = true,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("techage:ta4_button_on", {
description = ("TA4 Button/Switch"),
inventory_image = "techage_smartline_button_inv.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_smartline_button_on.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -6/32, -6/32, 14/32, 6/32, 6/32, 16/32},
},
},
ta4_formspec = WRENCH_MENU4,
ta_after_formspec = ta_after_formspec,
on_rightclick = on_rightclick_off,
on_timer = switch_off,
on_rotate = screwdriver.disallow,
techage_set_numbers = techage_set_numbers,
paramtype = "light",
use_texture_alpha = techage.CLIP,
sunlight_propagates = true,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1},
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
drop = "techage:ta4_button_off",
})
minetest.register_craft({
output = "techage:ta3_button_off",
recipe = {
{"", "group:wood", ""},
{"default:glass", "techage:vacuum_tube", ""},
{"", "group:wood", ""},
},
})
minetest.register_craft({
output = "techage:ta4_button_off",
recipe = {
{"", "techage:aluminum", "dye:blue"},
{"", "default:glass", "techage:ta4_wlanchip"},
{"", "", ""},
},
})
techage.register_node({"techage:ta3_button_off", "techage:ta3_button_on"}, {
on_recv_message = function(pos, src, topic, payload)
if topic == "on" then
switch_on(pos, true)
return true
elseif topic == "off" then
switch_off(pos, true)
return true
elseif topic == "state" then
local name = techage.get_node_lvm(pos).name
return name == "techage:ta3_button_on" and "on" or "off"
else
return "unsupported"
end
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
if topic == 1 and payload[1] == 1 then
switch_on(pos, true)
return 0
elseif topic == 1 and payload[1] == 0 then
switch_off(pos, true)
return 0
else
return 2
end
end,
on_beduino_request_data = function(pos, src, topic, payload)
if topic == 131 then -- State
local name = techage.get_node_lvm(pos).name
return 0, name == "techage:ta3_button_on" and {1} or {0}
else
return 2, ""
end
end,
}
)
techage.register_node({
"techage:ta4_button_off", "techage:ta4_button_on",
}, {
on_recv_message = function(pos, src, topic, payload)
if topic == "on" then
switch_on(pos, true)
return true
elseif topic == "off" then
switch_off(pos, true)
return true
elseif topic == "state" then
local name = techage.get_node_lvm(pos).name
return name == "techage:ta4_button_on" and "on" or "off"
elseif topic == "name" then
local mem = techage.get_mem(pos)
return mem.clicker or ""
elseif topic == "time" then
local mem = techage.get_mem(pos)
return mem.time or 0
else
return "unsupported"
end
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
if topic == 1 and payload[1] == 1 then
switch_on(pos, true)
return 0
elseif topic == 1 and payload[1] == 0 then
switch_off(pos, true)
return 0
else
return 2
end
end,
on_beduino_request_data = function(pos, src, topic, payload)
if topic == 131 then -- State
local name = techage.get_node_lvm(pos).name
return 0, name == "techage:ta4_button_on" and {1} or {0}
elseif topic == 144 then -- Player Name
local mem = techage.get_mem(pos)
return 0, mem.clicker
elseif topic == 149 then --time
local mem = techage.get_mem(pos)
return 0, {mem.time or 0}
else
return 2, ""
end
end,
}
)
@@ -0,0 +1,308 @@
--[[
TechAge
=======
Copyright (C) 2017-2021 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA4 Logic twofold button
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local logic = techage.logic
local function get_button_num(pos, clicker, pointed_thing)
-- use the node behind the button to get better results
if clicker and pointed_thing then
local offs = vector.subtract(pointed_thing.under, pointed_thing.above)
pointed_thing.under = vector.add(pointed_thing.under, offs)
pointed_thing.above = vector.add(pointed_thing.above, offs)
local pos1 = minetest.pointed_thing_to_face_pos(clicker, pointed_thing)
local y = pos1.y - pos.y
if y > 0.03 then
return 1
elseif y < -0.03 then
return 2
end
end
end
local WRENCH_MENU = {
{
type = "dropdown",
choices = "button,switch",
name = "type",
label = S("Type"),
tooltip = S("Momentary button or on/off switch"),
default = "1",
},
{
type = "ascii",
name = "label1",
label = S("Label") .. " 1",
tooltip = S("Label for the button"),
default = "1",
},
{
type = "numbers",
name = "dest_number1",
label = S("Number") .. " 1",
tooltip = S("Destination block number"),
default = "",
check = techage.check_numbers,
},
{
type = "ascii",
name = "command1",
label = S("Command") .. " 1",
tooltip = S("Command to be sent (ignored for switches)"),
default = "1",
},
{
type = "ascii",
name = "label2",
label = S("Label") .. " 2",
tooltip = S("Label for the button"),
default = "1",
},
{
type = "numbers",
name = "dest_number2",
label = S("Number") .. " 2",
tooltip = S("Destination block number"),
default = "",
check = techage.check_numbers,
},
{
type = "ascii",
name = "command2",
label = S("Command") .. " 2",
tooltip = S("Command to be sent (ignored for switches)"),
default = "2",
},
{
type = "dropdown",
choices = "private,protected,public",
name = "access",
label = S("Access"),
tooltip = S("Button protection"),
default = "8",
},
}
local function send_cmnd(pos, num, cmd)
local meta = M(pos)
local own_num = meta:get_string("node_number")
local dest = meta:get_string("dest_number" .. num)
local command, payload = cmd, nil
if not cmd then
local s = meta:get_string("command" .. num)
command, payload = unpack(string.split(s, " ", false, 1))
end
local owner = meta:get_string("owner")
if techage.check_numbers(dest, owner) then
techage.send_multi(own_num, dest, command, payload)
end
end
local function button_update(pos, objref)
local meta = M(pos)
pos = vector.round(pos)
local nvm = techage.get_nvm(pos)
nvm.button = nvm.button or {}
local tbl = {" ", " ", meta:get_string("label1"), " ", meta:get_string("label2")}
local text = "< " .. table.concat(tbl, "\n< ")
local texture = lcdlib.make_multiline_texture("default", text, 96, 96, 7, "top", "#000", 6)
if nvm.button[1] then
texture = texture .. "^techage_smartline_button_4x_on2.png"
end
if nvm.button[2] then
texture = texture .. "^techage_smartline_button_4x_on3.png"
end
objref:set_properties({ textures = {texture}, visual_size = {x=1, y=1} })
end
local function switch_off(pos, num)
local nvm = techage.get_nvm(pos)
nvm.button = nvm.button or {}
nvm.button[num] = nil
lcdlib.update_entities(pos)
end
local function switch_on(pos, num)
local nvm = techage.get_nvm(pos)
nvm.button = nvm.button or {}
nvm.button[num] = true
lcdlib.update_entities(pos)
end
local lcd_box = {-8/16, -4/16, 7.75/16, 8/16, 4/16, 8/16}
local function can_access(pos, player)
local meta = M(pos)
local playername = player:get_player_name()
local access = meta:get_string("access")
local owner = meta:get_string("owner")
local protected = minetest.is_protected(pos, playername)
if access == "private" and playername ~= owner then
return false
elseif access == "protected" and protected then
return false
end
return true
end
minetest.register_node("techage:ta4_button_2x", {
description = S("TA4 2x Button"),
inventory_image = 'techage_smartline_button_2x.png',
tiles = {'techage_smartline_button_2x.png'},
drawtype = "nodebox",
paramtype = "light",
use_texture_alpha = "clip",
sunlight_propagates = true,
paramtype2 = "facedir",
node_box = {
type = "fixed",
fixed = lcd_box,
},
light_source = 6,
display_entities = {
["techage:display_entity"] = { depth = 0.48,
on_display_update = button_update},
},
after_place_node = function(pos, placer)
local number = techage.add_node(pos, "techage:ta4_button_2x")
local meta = minetest.get_meta(pos)
meta:set_string("node_number", number)
meta:set_string("owner", placer:get_player_name())
meta:set_string("infotext", "TA4 2x Button " .. number)
meta:set_string("type", "button")
meta:set_string("label1", "B1")
meta:set_string("label2", "B2")
lcdlib.update_entities(pos)
end,
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
if clicker and clicker:is_player() then
-- Check access settings
if not can_access(pos, clicker) then
return
end
local num = get_button_num(pos, clicker, pointed_thing)
if num then
local typ = M(pos):get_string("type")
if typ == "switch" then
local nvm = techage.get_nvm(pos)
nvm.button = nvm.button or {}
if nvm.button[num] then
switch_off(pos, num)
logic.guarded_action(pos, send_cmnd, pos, num, "off")
else
switch_on(pos, num)
logic.guarded_action(pos, send_cmnd, pos, num, "on")
end
else
switch_on(pos, num)
logic.guarded_action(pos, send_cmnd, pos, num)
minetest.after(0.5, switch_off, pos, num)
end
minetest.sound_play("techage_button", {
pos = pos,
gain = 0.5,
max_hear_distance = 5,
})
end
end
end,
ta_after_formspec = function(pos, fields, playername)
lcdlib.update_entities(pos)
end,
after_dig_node = function(pos, oldnode, oldmetadata)
techage.remove_node(pos, oldnode, oldmetadata)
end,
ta3_formspec = WRENCH_MENU,
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(),
})
techage.register_node({"techage:ta4_button_2x"}, {
on_recv_message = function(pos, src, topic, payload)
local num = math.min(tonumber(payload) or 0, 2)
if topic == "on" then
switch_on(pos, num)
logic.guarded_action(pos, send_cmnd, pos, num)
return true
elseif topic == "off" then
switch_off(pos, num)
return true
elseif topic == "state" then
local nvm = techage.get_nvm(pos)
nvm.button = nvm.button or {}
return nvm.button[num] == true
else
return "unsupported"
end
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
local num = math.min(payload[1], 2)
if topic == 23 and payload[2] == 1 then
switch_on(pos, num)
logic.guarded_action(pos, send_cmnd, pos, num)
return 0
elseif topic == 23 and payload[2] == 0 then
switch_off(pos, num)
return 0
else
return 2
end
end,
on_beduino_request_data = function(pos, src, topic, payload)
if topic == 152 then -- State
local num = math.min(payload[1], 2)
local nvm = techage.get_nvm(pos)
nvm.button = nvm.button or {}
return 0, nvm.button[num] and {1} or {0}
else
return 2, ""
end
end,
}
)
minetest.register_craft({
output = "techage:ta4_button_2x",
recipe = {
{"", "", ""},
{"", "techage:ta4_button_off", "techage:ta4_button_off"},
{"", "", ""},
},
})
minetest.register_craft({
output = "techage:ta4_button_off 2",
recipe = {
{"", "", ""},
{"", "techage:ta4_button_2x", ""},
{"", "", ""},
},
})
@@ -0,0 +1,364 @@
--[[
TechAge
=======
Copyright (C) 2017-2021 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA4 Logic fourfold button
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local logic = techage.logic
local function get_button_num(pos, clicker, pointed_thing)
-- use the node behind the button to get better results
if clicker and pointed_thing then
local offs = vector.subtract(pointed_thing.under, pointed_thing.above)
pointed_thing.under = vector.add(pointed_thing.under, offs)
pointed_thing.above = vector.add(pointed_thing.above, offs)
local pos1 = minetest.pointed_thing_to_face_pos(clicker, pointed_thing)
local y = pos1.y - pos.y
if y < -0.3 then
return 4
elseif y < -0.03 and y > -0.22 then
return 3
elseif y > 0.03 and y < 0.22 then
return 2
elseif y > 0.3 then
return 1
end
end
end
local WRENCH_MENU = {
{
type = "dropdown",
choices = "button,switch",
name = "type",
label = S("Type"),
tooltip = S("Momentary button or on/off switch"),
default = "1",
},
{
type = "ascii",
name = "label1",
label = S("Label") .. " 1",
tooltip = S("Label for the button"),
default = "1",
},
{
type = "numbers",
name = "dest_number1",
label = S("Number") .. " 1",
tooltip = S("Destination block number"),
default = "",
check = techage.check_numbers,
},
{
type = "ascii",
name = "command1",
label = S("Command") .. " 1",
tooltip = S("Command to be sent (ignored for switches)"),
default = "1",
},
{
type = "ascii",
name = "label2",
label = S("Label") .. " 2",
tooltip = S("Label for the button"),
default = "1",
},
{
type = "numbers",
name = "dest_number2",
label = S("Number") .. " 2",
tooltip = S("Destination block number"),
default = "",
check = techage.check_numbers,
},
{
type = "ascii",
name = "command2",
label = S("Command") .. " 2",
tooltip = S("Command to be sent (ignored for switches)"),
default = "2",
},
{
type = "ascii",
name = "label3",
label = S("Label") .. " 3",
tooltip = S("Label for the button"),
default = "1",
},
{
type = "numbers",
name = "dest_number3",
label = S("Number") .. " 3",
tooltip = S("Destination block number"),
default = "",
check = techage.check_numbers,
},
{
type = "ascii",
name = "command3",
label = S("Command") .. " 3",
tooltip = S("Command to be sent (ignored for switches)"),
default = "3",
},
{
type = "ascii",
name = "label4",
label = S("Label") .. " 4",
tooltip = S("Label for the button"),
default = "1",
},
{
type = "numbers",
name = "dest_number4",
label = S("Number") .. " 4",
tooltip = S("Destination block number"),
default = "",
check = techage.check_numbers,
},
{
type = "ascii",
name = "command4",
label = S("Command") .. " 4",
tooltip = S("Command to be sent (ignored for switches)"),
default = "4",
},
{
type = "dropdown",
choices = "private,protected,public",
name = "access",
label = S("Access"),
tooltip = S("Button protection"),
default = "8",
},
}
local function send_cmnd(pos, num, cmd)
local meta = M(pos)
local own_num = meta:get_string("node_number")
local dest = meta:get_string("dest_number" .. num)
local command, payload = cmd, nil
if not cmd then
local s = meta:get_string("command" .. num)
command, payload = unpack(string.split(s, " ", false, 1))
end
local owner = meta:get_string("owner")
if techage.check_numbers(dest, owner) then
techage.send_multi(own_num, dest, command, payload)
end
end
local function button_update(pos, objref)
local meta = M(pos)
pos = vector.round(pos)
local nvm = techage.get_nvm(pos)
nvm.button = nvm.button or {}
local tbl = {meta:get_string("label1"), " ", meta:get_string("label2"), " ", meta:get_string("label3"), " ", meta:get_string("label4")}
local text = "< " .. table.concat(tbl, "\n< ")
local texture = lcdlib.make_multiline_texture("default", text, 96, 96, 7, "top", "#000", 6)
if nvm.button[1] then
texture = texture .. "^techage_smartline_button_4x_on1.png"
end
if nvm.button[2] then
texture = texture .. "^techage_smartline_button_4x_on2.png"
end
if nvm.button[3] then
texture = texture .. "^techage_smartline_button_4x_on3.png"
end
if nvm.button[4] then
texture = texture .. "^techage_smartline_button_4x_on4.png"
end
objref:set_properties({ textures = {texture}, visual_size = {x=1, y=1} })
end
local function switch_off(pos, num)
local nvm = techage.get_nvm(pos)
nvm.button = nvm.button or {}
nvm.button[num] = nil
lcdlib.update_entities(pos)
end
local function switch_on(pos, num)
local nvm = techage.get_nvm(pos)
nvm.button = nvm.button or {}
nvm.button[num] = true
lcdlib.update_entities(pos)
end
local lcd_box = {-8/16, -8/16, 7.75/16, 8/16, 8/16, 8/16}
local function can_access(pos, player)
local meta = M(pos)
local playername = player:get_player_name()
local access = meta:get_string("access")
local owner = meta:get_string("owner")
local protected = minetest.is_protected(pos, playername)
if access == "private" and playername ~= owner then
return false
elseif access == "protected" and protected then
return false
end
return true
end
minetest.register_node("techage:ta4_button_4x", {
description = S("TA4 4x Button"),
inventory_image = 'techage_smartline_button_4x.png',
tiles = {'techage_smartline_button_4x.png'},
drawtype = "nodebox",
paramtype = "light",
use_texture_alpha = "clip",
sunlight_propagates = true,
paramtype2 = "facedir",
node_box = {
type = "fixed",
fixed = lcd_box,
},
light_source = 6,
display_entities = {
["techage:display_entity"] = { depth = 0.48,
on_display_update = button_update},
},
after_place_node = function(pos, placer)
local number = techage.add_node(pos, "techage:ta4_button_4x")
local meta = minetest.get_meta(pos)
meta:set_string("node_number", number)
meta:set_string("owner", placer:get_player_name())
meta:set_string("infotext", "TA4 4x Button " .. number)
meta:set_string("type", "button")
meta:set_string("label1", "B1")
meta:set_string("label2", "B2")
meta:set_string("label3", "B3")
meta:set_string("label4", "B4")
lcdlib.update_entities(pos)
end,
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
if clicker and clicker:is_player() then
-- Check access settings
if not can_access(pos, clicker) then
return
end
local num = get_button_num(pos, clicker, pointed_thing)
if num then
local typ = M(pos):get_string("type")
if typ == "switch" then
local nvm = techage.get_nvm(pos)
nvm.button = nvm.button or {}
if nvm.button[num] then
switch_off(pos, num)
logic.guarded_action(pos, send_cmnd, pos, num, "off")
else
switch_on(pos, num)
logic.guarded_action(pos, send_cmnd, pos, num, "on")
end
else
switch_on(pos, num)
logic.guarded_action(pos, send_cmnd, pos, num)
minetest.after(0.5, switch_off, pos, num)
end
minetest.sound_play("techage_button", {
pos = pos,
gain = 0.5,
max_hear_distance = 5,
})
end
end
end,
ta_after_formspec = function(pos, fields, playername)
lcdlib.update_entities(pos)
end,
after_dig_node = function(pos, oldnode, oldmetadata)
techage.remove_node(pos, oldnode, oldmetadata)
end,
ta3_formspec = WRENCH_MENU,
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(),
})
techage.register_node({"techage:ta4_button_4x"}, {
on_recv_message = function(pos, src, topic, payload)
local num = math.min(tonumber(payload) or 0, 4)
if topic == "on" then
switch_on(pos, num)
logic.guarded_action(pos, send_cmnd, pos, num)
return true
elseif topic == "off" then
switch_off(pos, num)
return true
elseif topic == "state" then
local nvm = techage.get_nvm(pos)
nvm.button = nvm.button or {}
return nvm.button[num] == true
else
return "unsupported"
end
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
local num = math.min(payload[1], 4)
if topic == 23 and payload[2] == 1 then
switch_on(pos, num)
logic.guarded_action(pos, send_cmnd, pos, num)
return 0
elseif topic == 23 and payload[2] == 0 then
switch_off(pos, num)
return 0
else
return 2
end
end,
on_beduino_request_data = function(pos, src, topic, payload)
if topic == 152 then -- State
local num = math.min(payload[1], 4)
local nvm = techage.get_nvm(pos)
nvm.button = nvm.button or {}
return 0, nvm.button[num] and {1} or {0}
else
return 2, ""
end
end,
}
)
minetest.register_craft({
output = "techage:ta4_button_4x",
recipe = {
{"", "techage:ta4_button_off", "techage:ta4_button_off"},
{"", "techage:ta4_button_off", "techage:ta4_button_off"},
{"", "", ""},
},
})
minetest.register_craft({
output = "techage:ta4_button_off 4",
recipe = {
{"", "", ""},
{"", "techage:ta4_button_4x", ""},
{"", "", ""},
},
})
@@ -0,0 +1,182 @@
--[[
TechAge
=======
Copyright (C) 2019-2022 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
Cart Detector/Starter
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local logic = techage.logic
local CYCLE_TIME = 2
local function switch_off(pos)
local node = minetest.get_node(pos)
if node.name == "techage:ta3_cartdetector_on" then
logic.swap_node(pos, "techage:ta3_cartdetector_off")
logic.send_off(pos, M(pos))
end
end
local function switch_on(pos)
logic.swap_node(pos, "techage:ta3_cartdetector_on")
if logic.send_on(pos, M(pos)) then
minetest.after(1, switch_off, pos)
end
end
local function node_timer(pos)
if minecart.is_cart_available(pos, nil, 1.5) then
switch_on(pos)
else
switch_off(pos)
end
return true
end
local function formspec(meta)
local numbers = meta:get_string("numbers") or ""
return "size[7.5,4]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"field[0.5,1;7,1;numbers;"..S("Insert destination node number(s)")..";"..numbers.."]" ..
"button_exit[2,2.2;3,1;accept;"..S("accept").."]"
end
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local meta = minetest.get_meta(pos)
if fields.accept then
if techage.check_numbers(fields.numbers, player:get_player_name()) then
meta:set_string("numbers", fields.numbers)
logic.infotext(M(pos), S("TA3 Cart Detector"))
end
meta:set_string("formspec", formspec(meta))
end
end
local function techage_set_numbers(pos, numbers, player_name)
local meta = M(pos)
local res = logic.set_numbers(pos, numbers, player_name, S("TA3 Cart Detector"))
meta:set_string("formspec", formspec(meta))
return res
end
local function after_dig_node(pos, oldnode, oldmetadata, digger)
techage.remove_node(pos, oldnode, oldmetadata)
end
minetest.register_node("techage:ta3_cartdetector_off", {
description = S("TA3 Cart Detector"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png^techage_appl_arrow.png^[transformR90",
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_cartdetector.png",
},
after_place_node = function(pos, placer)
local meta = M(pos)
logic.after_place_node(pos, placer, "techage:ta3_cartdetector_off", S("TA3 Player Detector"))
logic.infotext(meta, S("TA3 Cart Detector"))
meta:set_string("formspec", formspec(meta))
minetest.get_node_timer(pos):start(CYCLE_TIME)
end,
on_receive_fields = on_receive_fields,
on_timer = node_timer,
techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("techage:ta3_cartdetector_on", {
description = "TA3 Cart Detector",
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png^techage_appl_arrow.png^[transformR90",
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_cartdetector_on.png",
},
on_receive_fields = on_receive_fields,
on_timer = node_timer,
techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
drop = "techage:ta3_cartdetector_off"
})
minetest.register_craft({
output = "techage:ta3_cartdetector_off",
recipe = {
{"", "group:wood", "default:mese_crystal"},
{"", "default:copper_ingot", "techage:vacuum_tube"},
{"", "group:wood", "basic_materials:motor"},
},
})
techage.register_node({"techage:ta3_cartdetector_off", "techage:ta3_cartdetector_on"}, {
on_recv_message = function(pos, src, topic, payload)
if topic == "on" then
local node = minetest.get_node(pos)
local dir = minetest.facedir_to_dir(node.param2)
minecart.punch_cart(pos, nil, 1.6, dir)
elseif topic == "state" then
local node = techage.get_node_lvm(pos)
if node.name == "techage:ta3_cartdetector_on" then
return "on"
else
return "off"
end
else
return "unsupported"
end
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
if topic == 1 then
local node = minetest.get_node(pos)
local dir = minetest.facedir_to_dir(node.param2)
minecart.punch_cart(pos, nil, 1.6, dir)
return 0
else
return 2
end
end,
on_beduino_request_data = function(pos, src, topic, payload)
if topic == 142 then -- Binary State
local node = techage.get_node_lvm(pos)
if node.name == "techage:ta3_cartdetector_on" then
return 0, {1}
else
return 0, {0}
end
else
return 2, ""
end
end,
on_node_load = function(pos)
minetest.get_node_timer(pos):start(CYCLE_TIME)
end,
})
-- Register default cart in addition
minecart.tEntityNames["carts:cart"] = true
@@ -0,0 +1,232 @@
--[[
TechAge
=======
Copyright (C) 2019-2023 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA4 Energy storage charge detector
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local Cable = techage.ElectricCable
local power = networks.power
local logic = techage.logic
local CYCLE_TIME = 8
local DESCR = S("TA4 Energy Storage Charge Detector")
local WRENCH_MENU = {
{
type = "numbers",
name = "numbers",
label = S("Number"),
tooltip = S("Destination block number"),
default = "",
check = techage.check_numbers,
},
{
type = "dropdown",
choices = "10%,20%,30%,40%,50%,60%,70%,80%,90%",
name = "switch_point",
label = S("Switch point"),
tooltip = S("Storage charge level switch point"),
default = "50",
values = {10, 20, 30, 40, 50, 60, 70, 80, 90}
},
{
type = "ascii",
name = "command1",
label = '"<" ' .. S("Command"),
tooltip = S("Command to send when the energy storage charge\nlevel falls below the specified switch point"),
default = "off",
},
{
type = "ascii",
name = "command2",
label = '">" ' .. S("Command"),
tooltip = S("Command to send when the energy storage charge\nlevel rises above the specified switch point"),
default = "on",
},
}
local function switch_on(pos)
if logic.swap_node(pos, "techage:ta4_chargedetector_on") then
logic.send_cmnd(pos, "command2", "on")
end
end
local function switch_off(pos)
if logic.swap_node(pos, "techage:ta4_chargedetector_off") then
logic.send_cmnd(pos, "command1", "off")
end
end
local function switch_point(pos)
local mem = techage.get_mem(pos)
if not mem.switch_point then
mem.switch_point = tonumber(M(pos):get_string("switch_point")) or 50
end
return mem.switch_point
end
local function above_switch_point(pos)
local outdir = M(pos):get_int("outdir")
local value = networks.power.get_storage_percent(pos, Cable, outdir)
return value > switch_point(pos)
end
local function techage_set_numbers(pos, numbers, player_name)
local res = logic.set_numbers(pos, numbers, player_name, DESCR)
return res
end
local function after_dig_node(pos, oldnode, oldmetadata, digger)
Cable:after_dig_node(pos)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end
local function ta_after_formspec(pos, fields, playername)
local mem = techage.get_mem(pos)
mem.switch_point = nil
end
minetest.register_node("techage:ta4_chargedetector_off", {
description = DESCR,
inventory_image = 'techage_charge_detector_inv.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_charge_detector_off.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 = M(pos)
logic.after_place_node(pos, placer, "techage:ta4_chargedetector_off", DESCR)
logic.infotext(meta, DESCR)
M(pos):set_int("outdir", networks.side_to_outdir(pos, "B"))
Cable:after_place_node(pos)
minetest.get_node_timer(pos):start(CYCLE_TIME)
end,
on_timer = function (pos, elapsed)
if not above_switch_point(pos) then
switch_on(pos)
end
return true
end,
techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node,
ta_after_formspec = ta_after_formspec,
ta4_formspec = WRENCH_MENU,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("techage:ta4_chargedetector_on", {
description = DESCR,
inventory_image = 'techage_charge_detector_inv.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_charge_detector_on.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -6/32, -6/32, 14/32, 6/32, 6/32, 16/32},
},
},
on_timer = function (pos, elapsed)
if above_switch_point(pos) then
switch_off(pos)
end
return true
end,
techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node,
ta_after_formspec = ta_after_formspec,
ta4_formspec = WRENCH_MENU,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1},
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
drop = "techage:ta4_chargedetector_off"
})
minetest.register_craft({
output = "techage:ta4_chargedetector_off",
recipe = {
{"", "", ""},
{"basic_materials:plastic_sheet", "dye:blue", "techage:aluminum"},
{"techage:ta4_wlanchip", "techage:electric_cableS", "default:copper_ingot"},
},
})
techage.register_node({
"techage:ta4_chargedetector_off", "techage:ta4_chargedetector_on"
}, {
on_recv_message = function(pos, src, topic, payload)
if topic == "state" then
local node = techage.get_node_lvm(pos)
if node.name == "techage:ta4_chargedetector_on" then
return "on"
else
return "off"
end
else
return "unsupported"
end
end,
on_beduino_request_data = function(pos, src, topic, payload)
if topic == 142 then -- Binary State
local node = techage.get_node_lvm(pos)
if node.name == "techage:ta4_chargedetector_on" then
return 0, {1}
else
return 0, {0}
end
else
return 2, ""
end
end,
on_node_load = function(pos)
minetest.get_node_timer(pos):start(CYCLE_TIME)
end,
}
)
power.register_nodes({"techage:ta4_chargedetector_off", "techage:ta4_chargedetector_on"}, Cable, "con", {"B"})
@@ -0,0 +1,225 @@
--[[
TechAge
=======
Copyright (C) 2017-2022 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA4 Collector
Collects states from other nodes, acting as a state concentrator.
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local NDEF = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}) end
local logic = techage.logic
local CYCLE_TIME = 1
-- The numbering seems strange here, but I had to add the "running" state
-- afterwards without changing the behavior of the block.
local tStates = {stopped = 0, running = 1, standby = 2, blocked = 3, fault = 4, defect = 5}
local tDropdownPos = {["1 standby"] = 1, ["2 blocked"] = 2, ["3 fault"] = 3, ["4 defect"] = 4}
local lStates = {[0] = "stopped", "running", "standby", "blocked", "fault", "defect"}
local tDropdownPos = {["1 standby"] = 1, ["2 blocked"] = 2, ["3 nopower"] = 3, ["4 fault"] = 4}
local TaStates = {running = 1, blocked = 2, standby = 3, nopower = 4, fault = 5, stopped = 6}
local function formspec(nvm, meta)
nvm.poll_numbers = nvm.poll_numbers or {}
local poll_numbers = table.concat(nvm.poll_numbers, " ")
local event_number = meta:get_string("event_number")
local dropdown_pos = meta:get_int("dropdown_pos")
if dropdown_pos == 0 then dropdown_pos = 1 end
return "size[9,6]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"field[0.3,0.6;9,1;poll_numbers;"..S("Node numbers to read the states from")..":;"..poll_numbers.."]" ..
"field[0.3,2;9,1;event_number;"..S("Node number to send the events to")..":;"..event_number.."]" ..
"label[1.3,2.8;"..S("Send an event if state is equal or larger than")..":]"..
"dropdown[1.2,3.4;7,4;severity;1 standby,2 blocked,3 nopower,4 fault;"..dropdown_pos.."]"..
"button_exit[3,5;2,1;exit;Save]"
end
local function send_event(nvm, meta)
local event_number = meta:get_string("event_number")
if event_number ~= "" then
local severity = meta:get_int("dropdown_pos")
local own_number = meta:get_string("own_number")
if nvm.common_state > severity then
techage.send_multi(own_number, event_number, "on")
else
techage.send_multi(own_number, event_number, "off")
end
end
end
local function request_state(nvm, meta)
local number = nvm.poll_numbers and nvm.poll_numbers[nvm.idx]
if number then
local own_number = meta:get_string("own_number")
local state = techage.send_single(own_number, number, "state", nil)
if state then
state = tStates[state] or 0
nvm.common_state = math.max(nvm.common_state, state)
end
end
end
local function on_timer(pos,elapsed)
local nvm = techage.get_nvm(pos)
local meta = minetest.get_meta(pos)
nvm.idx = (nvm.idx or 0) + 1
nvm.common_state = nvm.common_state or 0
if not nvm.poll_numbers then
local own_number = meta:get_string("own_number")
meta:set_string("infotext", S("TA4 State Collector").." "..own_number..": stopped")
nvm.common_state = 0
nvm.idx = 1
return false
end
if nvm.idx > #nvm.poll_numbers then
nvm.idx = 1
if nvm.stored_state ~= nvm.common_state then
send_event(nvm, meta)
local own_number = meta:get_string("own_number")
meta:set_string("infotext", S("TA4 State Collector").." "..own_number..': "'..lStates[nvm.common_state]..'"')
nvm.stored_state = nvm.common_state
end
nvm.common_state = 0 -- reset for the next round
end
request_state(nvm, meta)
return true
end
minetest.register_node("techage:ta4_collector", {
description = S("TA4 State Collector"),
inventory_image = "techage_smartline_collector_inv.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_smartline_collector.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 nvm = techage.get_nvm(pos)
local meta = minetest.get_meta(pos)
local own_number = techage.add_node(pos, "techage:ta4_collector")
meta:set_string("own_number", own_number)
meta:set_string("formspec", formspec(nvm, meta))
meta:set_string("infotext", S("TA4 State Collector").." "..own_number)
meta:set_string("owner", placer:get_player_name())
end,
on_receive_fields = function(pos, formname, fields, player)
local meta = minetest.get_meta(pos)
local owner = meta:get_string("owner")
if owner ~= player:get_player_name() then
return
end
local nvm = techage.get_nvm(pos)
local timer = minetest.get_node_timer(pos)
local own_number = meta:get_string("own_number")
if fields.quit == "true" and fields.poll_numbers then
if techage.check_numbers(fields.event_number, player:get_player_name()) then
meta:set_string("event_number", fields.event_number)
end
if techage.check_numbers(fields.poll_numbers, player:get_player_name()) then
nvm.poll_numbers = string.split(fields.poll_numbers, " ")
nvm.idx = 0
if not timer:is_started() then
timer:start(CYCLE_TIME)
end
meta:set_string("infotext", S("TA4 State Collector").." "..own_number..": running")
else
if timer:is_started() then
timer:stop()
end
meta:set_string("infotext", S("TA4 State Collector").." "..own_number..": stopped")
nvm.common_state = 0
nvm.stored_state = 0
end
if fields.severity then
meta:set_int("dropdown_pos", tDropdownPos[fields.severity])
end
meta:set_string("formspec", formspec(nvm, meta))
end
end,
techage_set_numbers = function(pos, numbers, player_name)
local nvm = techage.get_nvm(pos)
local meta = M(pos)
local res = logic.set_numbers(pos, numbers, player_name, NDEF(pos).description)
meta:set_string("formspec", formspec(nvm, meta))
return res
end,
on_timer = on_timer,
after_dig_node = function(pos, oldnode, oldmetadata)
techage.remove_node(pos, oldnode, oldmetadata)
end,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
})
minetest.register_craft({
output = "techage:ta4_collector",
recipe = {
{"", "techage:aluminum", "dye:blue"},
{"", "default:mese_crystal_fragment", "techage:ta4_wlanchip"},
},
})
techage.register_node({"techage:ta4_collector"}, {
on_recv_message = function(pos, src, topic, payload)
if topic == "state" then
local nvm = techage.get_nvm(pos)
return lStates[nvm.stored_state or 0]
else
return "unsupported"
end
end,
on_beduino_request_data = function(pos, src, topic, payload)
if topic == 129 then
local nvm = techage.get_nvm(pos)
return 0, {TaStates[lStates[nvm.stored_state or 0]]}
else
return 2, ""
end
end,
on_node_load = function(pos)
minetest.get_node_timer(pos):start(CYCLE_TIME)
end,
})
@@ -0,0 +1,182 @@
--[[
TechAge
=======
Copyright (C) 2017-2023 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA3 Commannd Converter
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local DESCR = S("TA3 Command Converter")
local logic = techage.logic
local sDelay = "0,1,2,3,4,5,7,10,15,20,30,45,60"
local function switch_on(pos)
logic.swap_node(pos, "techage:ta3_command_converter_on")
logic.send_cmnd(pos, "command_on", "")
end
local function switch_off(pos)
logic.swap_node(pos, "techage:ta3_command_converter_off")
logic.send_cmnd(pos, "command_off", "")
end
local function formspec(meta)
local numbers = meta:get_string("numbers") or ""
local command_on = meta:get_string("command_on")
local command_off = meta:get_string("command_off")
local delay_on = techage.dropdown_index(sDelay, meta:get_string("delay_on"))
local delay_off = techage.dropdown_index(sDelay, meta:get_string("delay_off"))
return "formspec_version[4]size[10,6]"..
"box[0.2,0.2;9.6,0.6;#c6e8ff]" ..
"label[0.4,0.5;" .. minetest.colorize( "#000000", DESCR) .. "]" ..
"field[1.0,1.5;8,0.6;numbers;" .. S("Insert destination node number(s)") .. ";" .. numbers .. "]" ..
"label[0.6,2.7;" .. S("Receive") .. "]" ..
"label[2.8,2.7;" .. S("Sent command") .. "]" ..
"label[7.34,2.7;" .. S("Send delay (s)") .. "]" ..
"box[0.5,3.1;1.2,0.6;#888]" ..
"label[0.6,3.4;on]" ..
"field[2.7,3.1;3.5,0.6;command_on;;" .. command_on .. "]" ..
"dropdown[7.2,3.1;2,0.6;delay_on;" .. sDelay .. ";" .. delay_on .. ";false]" ..
"box[0.5,4.0;1.2,0.6;#888]" ..
"label[0.6,4.3;off]" ..
"field[2.7,4.0;3.5,0.6;command_off;;" .. command_off .. "]" ..
"dropdown[7.2,4.0;2,0.6;delay_off;" .. sDelay .. ";" .. delay_off .. ";false]" ..
"button_exit[3.5,5.2;3,0.6;exit;" .. S("Save") .. "]"
end
local function techage_set_numbers(pos, numbers, player_name)
local meta = M(pos)
local res = logic.set_numbers(pos, numbers, player_name, DESCR)
meta:set_string("formspec", formspec(meta))
return true --res
end
local function after_dig_node(pos, oldnode, oldmetadata, digger)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local meta = minetest.get_meta(pos)
if fields.exit and fields.exit ~= "" then
if techage.check_numbers(fields.numbers, player:get_player_name()) then
meta:set_string("numbers", fields.numbers)
logic.infotext(M(pos), DESCR)
end
meta:set_string("command_on", fields.command_on)
meta:set_string("command_off", fields.command_off)
meta:set_int("delay_on", fields.delay_on or 0)
meta:set_int("delay_off", fields.delay_off or 0)
meta:set_string("formspec", formspec(meta))
elseif fields.exit == "close" then
meta:set_string("formspec", formspec(meta))
end
end
minetest.register_node("techage:ta3_command_converter_off", {
description = DESCR,
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_command_converter.png",
},
after_place_node = function(pos, placer)
local meta = M(pos)
logic.after_place_node(pos, placer, "techage:ta3_command_converter_off", DESCR)
logic.infotext(meta, DESCR)
meta:set_string("formspec", formspec(meta))
end,
on_timer = function (pos, elapsed)
switch_on(pos)
end,
on_receive_fields = on_receive_fields,
techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
})
minetest.register_node("techage:ta3_command_converter_on", {
description = DESCR,
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_command_converter_on.png",
},
on_timer = function (pos, elapsed)
switch_off(pos)
end,
on_receive_fields = on_receive_fields,
techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
drop = "techage:ta3_command_converter_off"
})
minetest.register_craft({
output = "techage:ta3_command_converter_off",
recipe = {
{"", "group:wood", ""},
{"default:mese_crystal_fragment", "default:copper_ingot", "techage:vacuum_tube"},
{"", "group:wood", ""},
},
})
techage.register_node({
"techage:ta3_command_converter_off", "techage:ta3_command_converter_on"
}, {
on_recv_message = function(pos, src, topic, payload)
if topic == "on" then
local delay = M(pos):get_int("delay_on") or 0
if delay > 0 then
minetest.get_node_timer(pos):start(delay)
else
switch_on(pos)
end
return true
elseif topic == "off" then
local delay = M(pos):get_int("delay_off") or 0
if delay > 0 then
minetest.get_node_timer(pos):start(delay)
else
switch_off(pos)
end
return true
else
return "unsupported"
end
end,
}
)
@@ -0,0 +1,394 @@
--[[
TechAge
=======
Copyright (C) 2017-2023 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA3/TA4 Item detector
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local NDEF = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}) end
local logic = techage.logic
local BLOCKING_TIME = 8 -- seconds
local ON_TIME = 1
local WRENCH_MENU3 = {
{
type = "dropdown",
choices = "1,2,4,6,8,12,16",
name = "ontime",
label = S("On Time") .. " [s]",
tooltip = S("The time between the 'on' and 'off' commands."),
default = "1",
},
{
type = "dropdown",
choices = "2,4,6,8,12,16,20",
name = "blockingtime",
label = S("Blocking Time") .. " [s]",
tooltip = S("The time after the 'off' command\nuntil the next 'on' command is accepted."),
default = "8",
},
{
type = "items",
name = "config",
label = S("Configured Items"),
tooltip = S("Items which generate an 'on' command.\nIf empty, all passed items generate an 'on' command."),
size = 4,
}
}
local WRENCH_MENU4 = {
{
type = "dropdown",
choices = "1,2,4,6,8,12,16",
name = "ontime",
label = S("On Time") .. " [s]",
tooltip = S("The time between the 'on' and 'off' commands."),
default = "1",
},
{
type = "dropdown",
choices = "2,4,6,8,12,16,20",
name = "blockingtime",
label = S("Blocking Time") .. " [s]",
tooltip = S("The time after the 'off' command\nuntil the next 'on' command is accepted."),
default = "8",
},
{
type = "number",
name = "countdown",
label = S("Countdown"),
tooltip = S("Counts down the number of items passed through\nand only triggers an 'on' command when it reaches zero."),
default = "0",
},
{
type = "output",
name = "countdown",
label = S("Current countdown"),
tooltip = S("Current countdown value."),
default = "0",
},
{
type = "items",
name = "config",
label = S("Configured Items"),
tooltip = S("Items which generate an 'on' command.\nIf empty, all passed items generate an 'on' command."),
size = 4,
}
}
local function switch_on(pos)
local mem = techage.get_mem(pos)
local t = minetest.get_gametime()
if t > (mem.time or 0) then
local name = techage.get_node_lvm(pos).name
if name == "techage:ta3_detector_off" then
logic.swap_node(pos, "techage:ta3_detector_on")
else
logic.swap_node(pos, "techage:ta4_detector_on")
end
local meta = M(pos)
local on_time = math.max(meta:get_int("ontime"), ON_TIME)
local blocking_time = tonumber(meta:get_string("blockingtime")) or BLOCKING_TIME
logic.send_on(pos, meta, on_time)
mem.time = t + blocking_time + on_time
end
end
local function switch_off(pos)
local name = techage.get_node_lvm(pos).name
if name == "techage:ta3_detector_on" then
logic.swap_node(pos, "techage:ta3_detector_off")
else
logic.swap_node(pos, "techage:ta4_detector_off")
end
logic.send_off(pos, M(pos))
end
local function formspec(meta)
local numbers = meta:get_string("numbers") or ""
return "size[7.5,3]"..
techage.wrench_image(7, -0.1) ..
"field[0.5,1;7,1;numbers;"..S("Insert destination node number(s)")..";"..numbers.."]" ..
"button_exit[2,2;3,1;exit;"..S("Save").."]"
end
local function after_place_node3(pos, placer)
local meta = M(pos)
local inv = meta:get_inventory()
inv:set_size('cfg', 4)
logic.after_place_node(pos, placer, "techage:ta3_detector_off", S("TA3 Item Detector"))
logic.infotext(meta, S("TA3 Item Detector"))
meta:set_string("formspec", formspec(meta))
end
local function after_place_node4(pos, placer)
local meta = M(pos)
local inv = meta:get_inventory()
inv:set_size('cfg', 4)
logic.after_place_node(pos, placer, "techage:ta4_detector_off", S("TA4 Item Detector"))
logic.infotext(meta, S("TA4 Item Detector"))
meta:set_string("formspec", formspec(meta))
end
local function on_receive_fields(pos, formname, fields, player)
local meta = minetest.get_meta(pos)
if techage.check_numbers(fields.numbers, player:get_player_name()) then
meta:set_string("numbers", fields.numbers)
logic.infotext(M(pos), NDEF(pos).description)
meta:set_string("formspec", formspec(M(pos)))
end
end
local function techage_set_numbers(pos, numbers, player_name)
local meta = M(pos)
local res = logic.set_numbers(pos, numbers, player_name, NDEF(pos).description)
meta:set_string("formspec", formspec(meta))
return res
end
local function after_dig_node(pos, oldnode, oldmetadata, digger)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end
local function ta_after_formspec(pos, fields, playername)
if fields.save then
local nvm = techage.get_nvm(pos)
local val = M(pos):get_int("countdown") or 0
if val > 0 then
nvm.countdown = val
else
nvm.countdown = nil
end
end
end
minetest.register_node("techage:ta3_detector_off", {
description = S("TA3 Item Detector"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png^techage_appl_arrow.png",
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_outp.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_inp.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_detector.png^[transformFX",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_detector.png",
},
after_place_node = after_place_node3,
on_receive_fields = on_receive_fields,
techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node,
ta3_formspec = WRENCH_MENU3,
on_rotate = screwdriver.disallow,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
})
minetest.register_node("techage:ta3_detector_on", {
description = S("TA3 Item Detector"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png^techage_appl_arrow.png",
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_outp.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_inp.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_detector_on.png^[transformFX",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_detector_on.png",
},
on_timer = switch_off,
on_rotate = screwdriver.disallow,
techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node,
ta3_formspec = WRENCH_MENU3,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
drop = "techage:ta3_detector_off",
})
minetest.register_node("techage:ta4_detector_off", {
description = S("TA4 Item Detector"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta4.png^techage_frame_ta4_top.png^techage_appl_arrow.png",
"techage_filling_ta4.png^techage_frame_ta4_top.png",
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_outp.png",
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_inp.png",
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_detector.png^[transformFX",
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_detector.png",
},
after_place_node = after_place_node4,
on_receive_fields = on_receive_fields,
techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node,
ta4_formspec = WRENCH_MENU4,
ta_after_formspec = ta_after_formspec,
on_rotate = screwdriver.disallow,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("techage:ta4_detector_on", {
description = S("TA4 Item Detector"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta4.png^techage_frame_ta4_top.png^techage_appl_arrow.png",
"techage_filling_ta4.png^techage_frame_ta4_top.png",
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_outp.png",
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_inp.png",
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_detector_on.png^[transformFX",
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_detector_on.png",
},
on_timer = switch_off,
on_rotate = screwdriver.disallow,
techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node,
ta4_formspec = WRENCH_MENU4,
ta_after_formspec = ta_after_formspec,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1},
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
drop = "techage:ta4_detector_off",
})
minetest.register_craft({
output = "techage:ta3_detector_off",
recipe = {
{"", "group:wood", ""},
{"techage:tubeS", "techage:vacuum_tube", "techage:tubeS"},
{"", "group:wood", ""},
},
})
minetest.register_craft({
output = "techage:ta4_detector_off",
recipe = {
{"", "techage:ta3_detector_off", ""},
{"", "techage:ta4_wlanchip", ""},
{"", "", ""},
},
})
techage.register_node({"techage:ta3_detector_off", "techage:ta3_detector_on"}, {
on_push_item = function(pos, in_dir, stack)
local leftover = techage.safe_push_items(pos, in_dir, stack)
if leftover then
local inv = minetest.get_inventory({type = "node", pos = pos})
if not inv or inv:is_empty("cfg") or inv:contains_item("cfg", ItemStack(stack:get_name())) then
if leftover == true or leftover:get_count() ~= stack:get_count() then
switch_on(pos)
end
end
return leftover
end
return false
end,
is_pusher = true, -- is a pulling/pushing node
})
techage.register_node({"techage:ta4_detector_off", "techage:ta4_detector_on"}, {
on_push_item = function(pos, in_dir, stack)
local leftover = techage.safe_push_items(pos, in_dir, stack)
if leftover then
local inv = minetest.get_inventory({type = "node", pos = pos})
if not inv or inv:is_empty("cfg") or inv:contains_item("cfg", ItemStack(stack:get_name())) then
local nvm = techage.get_nvm(pos)
local num_moved = stack:get_count()
if leftover ~= true then
num_moved = num_moved - leftover:get_count()
end
if nvm.countdown and nvm.countdown > 0 then
nvm.countdown = nvm.countdown - num_moved
if nvm.countdown <= 0 then
M(pos):set_int("countdown", 0)
if leftover == true or leftover:get_count() ~= stack:get_count() then
switch_on(pos)
end
end
elseif nvm.countdown == nil then
if leftover == true or leftover:get_count() ~= stack:get_count() then
switch_on(pos)
end
end
nvm.counter = (nvm.counter or 0) + num_moved
end
return leftover
end
return false
end,
is_pusher = true, -- is a pulling/pushing node
on_recv_message = function(pos, src, topic, payload)
if topic == "count" then
local nvm = techage.get_nvm(pos)
return nvm.counter or 0
elseif topic == "countdown" then
local nvm = techage.get_nvm(pos)
nvm.countdown = tonumber(payload) or 0
M(pos):set_int("countdown", nvm.countdown)
return true
elseif topic == "reset" then
local nvm = techage.get_nvm(pos)
nvm.counter = 0
nvm.countdown = nil
return true
else
return "unsupported"
end
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
if topic == 6 then -- Detector Block Reset
local nvm = techage.get_nvm(pos)
nvm.counter = 0
nvm.countdown = nil
return 0
elseif topic == 5 then -- Detector Block Countdown
local nvm = techage.get_nvm(pos)
nvm.countdown = tonumber(payload[1]) or 0
M(pos):set_int("countdown", nvm.countdown)
return 0
else
return 2
end
end,
on_beduino_request_data = function(pos, src, topic, payload)
if topic == 139 then
local nvm = techage.get_nvm(pos)
return 0, {nvm.counter or 0}
else
return 2, ""
end
end,
})
@@ -0,0 +1,147 @@
--[[
TechAge
=======
Copyright (C) 2017-2023 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA3 Flip-flop
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local DESCR = S("TA3 Flip-Flop")
local logic = techage.logic
local function switch_on(pos)
logic.swap_node(pos, "techage:ta3_flipflop_on")
logic.send_on(pos, M(pos))
end
local function switch_off(pos)
logic.swap_node(pos, "techage:ta3_flipflop_off")
logic.send_off(pos, M(pos))
end
local function formspec(meta)
local numbers = meta:get_string("numbers") or ""
return "formspec_version[4]size[10,3.5]"..
"box[0.2,0.2;9.6,0.6;#c6e8ff]" ..
"label[0.4,0.5;" .. minetest.colorize( "#000000", DESCR) .. "]" ..
"field[1.0,1.5;8,0.6;numbers;" .. S("Insert destination node number(s)") .. ";" .. numbers .. "]" ..
"button_exit[3.5,2.7;3,0.6;exit;" .. S("Save") .. "]"
end
local function techage_set_numbers(pos, numbers, player_name)
local meta = M(pos)
local res = logic.set_numbers(pos, numbers, player_name, DESCR)
meta:set_string("formspec", formspec(meta))
return true --res
end
local function after_dig_node(pos, oldnode, oldmetadata, digger)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local meta = minetest.get_meta(pos)
if fields.exit and fields.exit ~= "" then
if techage.check_numbers(fields.numbers, player:get_player_name()) then
meta:set_string("numbers", fields.numbers)
logic.infotext(M(pos), DESCR)
end
meta:set_string("formspec", formspec(meta))
elseif fields.exit == "close" then
meta:set_string("formspec", formspec(meta))
end
end
minetest.register_node("techage:ta3_flipflop_off", {
description = DESCR,
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_flipflop.png",
},
after_place_node = function(pos, placer)
local meta = M(pos)
logic.after_place_node(pos, placer, "techage:ta3_flipflop_off", DESCR)
logic.infotext(meta, DESCR)
meta:set_string("formspec", formspec(meta))
end,
on_timer = function (pos, elapsed)
switch_on(pos)
end,
on_receive_fields = on_receive_fields,
techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
})
minetest.register_node("techage:ta3_flipflop_on", {
description = DESCR,
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_flipflop_on.png",
},
on_timer = function (pos, elapsed)
switch_off(pos)
end,
on_receive_fields = on_receive_fields,
techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
drop = "techage:ta3_flipflop_off"
})
minetest.register_craft({
output = "techage:ta3_flipflop_off",
recipe = {
{"", "group:wood", ""},
{"default:mese_crystal_fragment", "default:steel_ingot", "techage:vacuum_tube"},
{"", "group:wood", ""},
},
})
techage.register_node({
"techage:ta3_flipflop_off", "techage:ta3_flipflop_on"
}, {
on_recv_message = function(pos, src, topic, payload)
if topic == "on" then
minetest.get_node_timer(pos):start(0.1)
return true
elseif topic == "off" then
return true
else
return "unsupported"
end
end,
}
)
@@ -0,0 +1,244 @@
--[[
TechAge
=======
Copyright (C) 2017-2023 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA4 Gaze Sensor
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local logic = techage.logic
local CYCLE_TIME = 2
local DESCR = S("TA4 Gaze Sensor")
local MAX_PLAYER_DIST = 30
local WRENCH_MENU = {
{
type = "ascii",
name = "names",
label = S("Player Names"),
tooltip = S("Input the player name(s) separated by blanks.\nIf empty, only the owner is accepted."),
default = "",
},
{
type = "numbers",
name = "numbers",
label = S("Number"),
tooltip = S("Destination block number(s)"),
default = "",
check = techage.check_numbers,
},
{
type = "ascii",
name = "command1",
label = "On " .. S("Command"),
tooltip = S("Command to send when sensor is viewed"),
default = "on",
},
{
type = "ascii",
name = "command2",
label = "Off " .. S("Command"),
tooltip = S("Command to send when sensor is no longer viewed"),
default = "off",
},
}
local function switch_on(pos)
if logic.swap_node(pos, "techage:ta4_gaze_sensor_on") then
logic.send_cmnd(pos, "command1", "on")
end
end
local function switch_off(pos)
if logic.swap_node(pos, "techage:ta4_gaze_sensor_off") then
logic.send_cmnd(pos, "command2", "off")
end
end
local function get_players(pos)
local meta = minetest.get_meta(pos)
local names = meta:get_string("names") or ""
local t = string.split(names, " ") or {}
t[#t + 1] = meta:get_string("owner")
return t
end
local function player_focuses_block(pos, name, obj)
obj = obj or minetest.get_player_by_name(name)
if obj then
local player_pos = obj:get_pos()
player_pos.y = player_pos.y + 1.5
local dist = vector.distance(pos, player_pos)
if dist < MAX_PLAYER_DIST then
local dir = obj:get_look_dir()
if dir then
local vec1 = vector.multiply(dir, dist)
local pos1 = vector.round(vector.add(player_pos, vec1))
if vector.equals(pos, pos1) then
local item = obj:get_wielded_item()
if not item or item:get_name() ~= "techage:end_wrench" then
return true
end
end
end
end
end
return false
end
local function scan_for_player(pos)
local mem = techage.get_mem(pos)
mem.players = mem.players or get_players(pos)
if mem.players[1] == "***" then
for _, obj in ipairs(minetest.get_objects_inside_radius(pos, MAX_PLAYER_DIST)) do
if player_focuses_block(pos, "", obj) then
mem.player_name = obj:get_player_name()
return true
end
end
else
for _, name in ipairs(mem.players) do
if player_focuses_block(pos, name, nil) then
mem.player_name = name
return true
end
end
end
mem.player_name = ""
return false
end
local function ta_after_formspec(pos, fields, playername)
if fields.save then
local mem = techage.get_mem(pos)
mem.players = nil
end
end
minetest.register_node("techage:ta4_gaze_sensor_off", {
description = DESCR,
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta4.png^techage_frame_ta4_top.png",
"techage_filling_ta4.png^techage_frame_ta4_top.png",
"techage_filling_ta4.png^techage_frame_ta4_top.png^techage_appl_gaze_sensor.png",
},
after_place_node = function(pos, placer)
local meta = M(pos)
logic.after_place_node(pos, placer, "techage:ta4_gaze_sensor_off", DESCR)
logic.infotext(meta, DESCR)
minetest.get_node_timer(pos):start(CYCLE_TIME)
end,
on_timer = function (pos, elapsed)
if scan_for_player(pos) then
switch_on(pos)
end
return true
end,
techage_set_numbers = function(pos, numbers, player_name)
return logic.set_numbers(pos, numbers, player_name, DESCR)
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end,
ta4_formspec = WRENCH_MENU,
ta_after_formspec = ta_after_formspec,
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
})
minetest.register_node("techage:ta4_gaze_sensor_on", {
description = DESCR,
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta4.png^techage_frame_ta4_top.png",
"techage_filling_ta4.png^techage_frame_ta4_top.png",
"techage_filling_ta4.png^techage_frame_ta4_top.png^techage_appl_gaze_sensor_on.png",
},
on_timer = function (pos, elapsed)
if not scan_for_player(pos) then
switch_off(pos)
end
return true
end,
techage_set_numbers = function(pos, numbers, player_name)
return logic.set_numbers(pos, numbers, player_name, DESCR)
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end,
ta4_formspec = WRENCH_MENU,
ta_after_formspec = ta_after_formspec,
groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
drop = "techage:ta4_gaze_sensor_off"
})
minetest.register_craft({
output = "techage:ta4_gaze_sensor_off",
recipe = {
{"dye:blue", "techage:aluminum", "dye:blue"},
{"default:copper_ingot", "techage:ta4_wlanchip", "default:wood"},
},
})
techage.register_node({
"techage:ta4_gaze_sensor_off", "techage:ta4_gaze_sensor_on",
}, {
on_recv_message = function(pos, src, topic, payload)
if topic == "name" then
local mem = techage.get_mem(pos)
return mem.player_name or ""
elseif topic == "state" then
local node = techage.get_node_lvm(pos)
if node.name == "techage:ta4_gaze_sensor_on" then
return "on"
else
return "off"
end
else
return "unsupported"
end
end,
on_beduino_request_data = function(pos, src, topic, payload)
if topic == 144 then -- Player Name
local mem = techage.get_mem(pos)
return 0, mem.player_name or ""
elseif topic == 142 then -- Binary State
local node = techage.get_node_lvm(pos)
if node.name == "techage:ta4_gaze_sensor_on" then
return 0, {1}
else
return 0, {0}
end
else
return 2, ""
end
end,
on_node_load = function(pos)
minetest.get_node_timer(pos):start(CYCLE_TIME)
end,
}
)
+130
View File
@@ -0,0 +1,130 @@
--[[
TechAge
=======
Copyright (C) 2019 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
Logic library
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local S2P = minetest.string_to_pos
local P2S = minetest.pos_to_string
techage.logic = {}
local Face2Dir = {[0]=
{x=0, y=0, z=1},
{x=1, y=0, z=0},
{x=0, y=0, z=-1},
{x=-1, y=0, z=0},
{x=0, y=-1, z=0},
{x=0, y=1, z=0}
}
-- Determine the destination position based on the position,
-- the node param2, and a route table like : {0,0,3}
-- 0 = forward, 1 = right, 2 = backward, 3 = left
function techage.logic.dest_pos(pos, param2, route)
local p2 = param2
for _,dir in ipairs(route) do
p2 = (param2 + dir) % 4
pos = vector.add(pos, Face2Dir[p2])
end
return pos, p2
end
function techage.logic.swap_node(pos, name)
local node = techage.get_node_lvm(pos)
if node.name == name then
return false
end
node.name = name
minetest.swap_node(pos, node)
return true
end
function techage.logic.after_place_node(pos, placer, name, descr)
local meta = M(pos)
local own_num = techage.add_node(pos, name)
meta:set_string("node_number", own_num)
meta:set_string("owner", placer:get_player_name())
meta:set_string("infotext", descr.." -")
end
techage.recursion_guard = techage.recursion_guard or {}
function techage.logic.guarded_action(pos, cmnd, ...)
if not cmnd then
return
end
local arg = {...}
local own_num = M(pos):get_string("node_number")
if techage.recursion_guard[own_num] then
minetest.log("warning", "[techage] Button recursion detected at node_number=".. own_num .. " pos=" .. P2S(pos))
return -- recursion detected, do not execute
end
techage.recursion_guard[own_num] = true
local result = cmnd(unpack(arg))
techage.recursion_guard[own_num] = nil
return result
end
function techage.logic.send_on(pos, meta, time)
local own_num = meta:get_string("node_number") or ""
local numbers = meta:get_string("numbers") or ""
if time and time > 0 then
minetest.get_node_timer(pos):start(time)
end
techage.send_multi(own_num, numbers, "on")
return own_num == numbers
end
function techage.logic.send_cmnd(pos, ident, default, time)
local meta = M(pos)
local s = meta:contains(ident) and meta:get_string(ident) or default
local command, payload = unpack(string.split(s, " ", false, 1))
local own_num = meta:get_string("node_number") or ""
local numbers = meta:get_string("numbers") or ""
if time and time > 0 then
minetest.get_node_timer(pos):start(time)
end
if command and command ~= "" then
techage.send_multi(own_num, numbers, command, payload)
end
end
function techage.logic.send_off(pos, meta)
local own_num = meta:get_string("node_number") or ""
local numbers = meta:get_string("numbers") or ""
techage.send_multi(own_num, numbers, "off")
end
function techage.logic.infotext(meta, descr, text)
local own_num = meta:get_string("node_number") or ""
local numbers = meta:get_string("numbers") or ""
if numbers ~= "" then
meta:set_string("infotext", descr.." "..own_num..": "..S("connected with").." "..numbers)
elseif text then
meta:set_string("infotext", descr.." "..own_num..": "..text)
else
meta:set_string("infotext", descr.." "..own_num)
end
end
function techage.logic.set_numbers(pos, numbers, player_name, descr)
if techage.check_numbers(numbers, player_name) then
local meta = M(pos)
meta:set_string("numbers", numbers)
techage.logic.infotext(meta, descr)
return true
end
return false
end
@@ -0,0 +1,190 @@
--[[
TechAge
=======
Copyright (C) 2019-2022 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
Light Detector
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local logic = techage.logic
local CYCLE_TIME = 2
local function switch_off(pos)
local node = minetest.get_node(pos)
if node.name == "techage:ta3_lightdetector_on" then
logic.swap_node(pos, "techage:ta3_lightdetector_off")
logic.send_off(pos, M(pos))
end
end
local function switch_on(pos)
logic.swap_node(pos, "techage:ta3_lightdetector_on")
if logic.send_on(pos, M(pos)) then
minetest.after(1, switch_off, pos)
end
end
local function node_timer(pos)
local nvm = techage.get_nvm(pos)
local trigger = nvm.mode or 7
local pos_above = {x = pos.x, y = pos.y + 1, z = pos.z}
if minetest.get_node_light(pos_above, nil) == nil then
switch_off(pos)
return true
end
if minetest.get_node_light(pos_above, nil) > trigger then
switch_on(pos)
else
switch_off(pos)
end
return true
end
local function formspec(meta, nvm)
local numbers = meta:get_string("numbers") or ""
local dropdown_label = "1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15" -- Has to be a cleaner way of doing this, but it's just easier this way
return "size[7.5,4]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"field[0.5,1;7,1;numbers;"..S("Insert destination node number(s)")..";"..numbers.."]" ..
"label[0.2,1.6;"..S("Send signal if light level is above:").."]"..
"dropdown[0.2,2.1;7.3,1;mode;"..dropdown_label.."; "..(nvm.mode or 7).."]"..
"button_exit[2,3.2;3,1;accept;"..S("accept").."]"
end
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local meta = minetest.get_meta(pos)
local nvm = techage.get_nvm(pos)
if fields.accept then
nvm.mode = tonumber(fields.mode) or 7
if techage.check_numbers(fields.numbers, player:get_player_name()) then
meta:set_string("numbers", fields.numbers)
logic.infotext(M(pos), S("TA3 Light Detector"))
end
meta:set_string("formspec", formspec(meta, nvm))
end
end
local function techage_set_numbers(pos, numbers, player_name)
local meta = M(pos)
local res = logic.set_numbers(pos, numbers, player_name, S("TA3 Light Detector"))
local nvm = techage.get_nvm(pos)
meta:set_string("formspec", formspec(meta, nvm))
return res
end
local function after_dig_node(pos, oldnode, oldmetadata, digger)
techage.remove_node(pos, oldnode, oldmetadata)
end
minetest.register_node("techage:ta3_lightdetector_off", {
description = S("TA3 Light Detector"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png^techage_appl_lightdetector.png",
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3_top.png^techage_appl_arrow.png^[transformR90",
},
after_place_node = function(pos, placer)
local meta = M(pos)
local nvm = techage.get_nvm(pos)
logic.after_place_node(pos, placer, "techage:ta3_lightdetector_off", S("TA3 Light Detector"))
logic.infotext(meta, S("TA3 Light Detector"))
meta:set_string("formspec", formspec(meta, nvm))
minetest.get_node_timer(pos):start(CYCLE_TIME)
end,
on_receive_fields = on_receive_fields,
on_timer = node_timer,
techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("techage:ta3_lightdetector_on", {
description = "TA3 Light Detector (On)",
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png^techage_appl_lightdetector_on.png",
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3_top.png^techage_appl_arrow.png^[transformR90",
},
on_receive_fields = on_receive_fields,
on_timer = node_timer,
techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
drop = "techage:ta3_lightdetector_off"
})
minetest.register_craft({
output = "techage:ta3_lightdetector_off",
recipe = {
{"", "group:wood", "default:glass"},
{"", "default:copper_ingot", "techage:vacuum_tube"},
{"", "group:wood", "default:mese_crystal"},
},
})
techage.register_node({"techage:ta3_lightdetector_off", "techage:ta3_lightdetector_on"}, {
on_recv_message = function(pos, src, topic, payload)
if topic == "state" then
local node = techage.get_node_lvm(pos)
if node.name == "techage:ta3_lightdetector_on" then
return "on"
else
return "off"
end
elseif topic == "light_level" then -- Allow finding the specific light level
local pos_above = {x = pos.x, y = pos.y + 1, z = pos.z}
return minetest.get_node_light(pos_above, nil)
else
return "unsupported"
end
end,
on_beduino_request_data = function(pos, src, topic, payload)
if topic == 142 then -- Binary State
local node = techage.get_node_lvm(pos)
if node.name == "techage:ta3_lightdetector_on" then
return 0, {1}
else
return 0, {0}
end
elseif topic == 143 then -- Allow finding the specific light level
local pos_above = {x = pos.x, y = pos.y + 1, z = pos.z}
return 0, {minetest.get_node_light(pos_above, nil)}
else
return 2, ""
end
end,
on_node_load = function(pos)
minetest.get_node_timer(pos):start(CYCLE_TIME)
end,
})
@@ -0,0 +1,435 @@
--[[
TechAge
=======
Copyright (C) 2017-2022 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
Logic Block 2
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local logic = techage.logic
local NUM_RULES = 4
local HELP = S("Send an 'on'/'off' command if the\nexpression becomes true.\n") ..
S("\nRule:\n<output> = on/off if <input-expression> is true\n") ..
S("\n<output> is the block number to which the\ncommand should be sent.\n") ..
S("\n<input-expression> is a boolean expression\nwhere input numbers are evaluated.\n") ..
S("\nExamples:\n1234 == on\n1234 == off\n1234 == on and 2345 == off\n2345 ~= 3456\n") ..
S("\nValid operators:\nand or on off me == ~= ( )\n") ..
S("'~=' means: not equal\n") ..
S("'me' has to be used for the own block number.\n") ..
S("\nAll rules are checked with each received\ncommand.") ..
S("\nThe internal processing time for all\ncommands is 100 ms.")
local ValidSymbols = {
["me"] = true,
["and"] = true,
["or"] = true,
["on"] = true,
["off"] = true,
["=="] = true,
["~="] = true,
["("] = true,
[")"] = true,
}
local Dropdown = {
[""] = 1,
["on"] = 2,
["off"] = 3
}
local function check_expr(pos, expr)
local nvm = techage.get_nvm(pos)
local origin = expr
-- Add blanks for the syntax check
expr = expr:gsub("==", " == ")
expr = expr:gsub("~=", " ~= ")
expr = expr:gsub("%(", " ( ")
expr = expr:gsub("%)", " ) ")
-- First syntax check
local old_sym = "or" -- valid default value
for sym in expr:gmatch("[^%s]+") do
if not ValidSymbols[sym] and string.find(sym, '^[0-9]+$') == nil then
return "Unexpected symbol '"..sym.."'"
end
if string.find(sym, '^[0-9]+$') and sym == nvm.own_num then
return "Invalid node number '"..sym.."'"
end
-- function call check
if sym == "(" and (old_sym ~= "and" and old_sym ~= "or") then
return "Syntax error at '" .. sym .. "'"
end
old_sym = sym
end
-- Second syntax check
local code, _ = loadstring("return " .. expr)
if not code then
return "Syntax error in '" .. origin .. "'"
end
end
local function check_num(pos, num, player_name)
local nvm = techage.get_nvm(pos)
if num ~= "me" and (num == nvm.own_num or
not techage.check_numbers(num, player_name)) then
return "Invalid node number '"..num.."'"
end
end
local function debug(mem, text)
mem.debug = mem.debug or {}
if #mem.debug > 20 then
table.remove(mem.debug, 1)
end
local s = string.format("%.3f", techage.SystemTime) .. " s: " .. text
table.insert(mem.debug, s)
end
local function send(pos, num, val)
local nvm = techage.get_nvm(pos)
local mem = techage.get_mem(pos)
debug(mem, "(outp) " .. num .. " = " .. val)
if num == "me" then
nvm.outp_tbl = nvm.outp_tbl or {}
nvm.outp_tbl.me = val
-- set the input directly
nvm.inp_tbl = nvm.inp_tbl or {}
nvm.inp_tbl.me = val
else
nvm.outp_tbl = nvm.outp_tbl or {}
nvm.outp_tbl[num] = val
nvm.own_num = nvm.own_num or M(pos):get_string("node_number")
techage.send_single(nvm.own_num, num, val)
end
end
local function get_inputs(pos)
local nvm = techage.get_nvm(pos)
local mem = techage.get_mem(pos)
-- old data is needed for formspec 'input' values
nvm.old_inp_tbl = table.copy(nvm.inp_tbl or {})
for _, num in ipairs(mem.outp_num or {}) do
nvm.old_inp_tbl[num] = nvm.outp_tbl[num] or "off"
end
return nvm.old_inp_tbl
end
local function check_syntax(pos, line, owner, outp, expr)
local err = check_num(pos, outp, owner)
if not err then
err = check_expr(pos, expr)
if not err then
return true, "ok"
end
end
return false, "Error(" .. line .. "): " .. err
end
local function compile(nvm, str)
if str then
local code, _ = loadstring(str)
if code then
nvm.error = "ok"
return code
else
nvm.error = "Unknown compile error"
end
end
end
local function data(nvm)
local inp = {}
local outp = {}
for num, val in pairs(nvm.old_inp_tbl or {}) do
if num == nvm.own_num then num = "me" end
inp[#inp+1] = num .. " = " .. tostring(val)
end
for num, val in pairs(nvm.outp_tbl or {}) do
if num == nvm.own_num then num = "me" end
outp[#outp+1] = num .. " = " .. tostring(val)
end
return table.concat(inp, ", "), table.concat(outp, ", ")
end
local function get_code(pos, nvm, mem)
local meta = M(pos)
local tbl = {"local inputs = get_inputs(pos) or {}"}
local owner = M(pos):get_string("owner")
nvm.own_num = nvm.own_num or M(pos):get_string("node_number")
mem.outp_num = {}
for i = 1,NUM_RULES do
local outp = meta:get_string("outp" .. i)
local val = meta:get_string("val" .. i)
local expr = meta:get_string("expr" .. i)
if outp ~= "" and val ~= "" and expr ~= "" then
local res, err = check_syntax(pos, i, owner, outp, expr)
if res then
expr = string.gsub(expr, '([0-9]+)', 'inputs["%1"]')
expr = string.gsub(expr, 'me', 'inputs["me"]')
expr = string.gsub(expr, 'on', '"on"')
expr = string.gsub(expr, 'off', '"off"')
tbl[#tbl + 1] = "if "..expr.." then send(pos, '"..outp.."', '"..val.."') end"
table.insert(mem.outp_num, outp)
else
nvm.error = err
return
end
end
end
local str = table.concat(tbl, "\n")
local code = compile(nvm, str)
if code then
local env = {}
env.send = send
env.pos = pos
env.get_inputs = get_inputs
setfenv(code, env)
return code
end
end
local function execute(pos)
local nvm = techage.get_nvm(pos)
local mem = techage.get_mem(pos)
mem.code = mem.code or get_code(pos, nvm, mem)
if mem.code then
local res, _ = pcall(mem.code)
if not res then
nvm.error = "Unknown runtime error"
mem.code = nil
end
end
end
local function rules(meta)
local tbl = {}
tbl[#tbl + 1] = "label[-0.2,0;<outp>]"
tbl[#tbl + 1] = "label[1.4,0;=]"
tbl[#tbl + 1] = "label[1.8,0;<cmnd>]"
tbl[#tbl + 1] = "label[3.5,0;if]"
tbl[#tbl + 1] = "label[4.2,0;<inp expression> is true]"
for i = 1,NUM_RULES do
local y1 = (i * 0.9) - 0.1
local y2 = (i * 0.9) - 0.2
local y3 = (i * 0.9) - 0.3
local outp = meta:get_string("outp" .. i)
local val = meta:get_string("val" .. i)
local expr = meta:get_string("expr" .. i)
val = Dropdown[val] or 1
tbl[#tbl + 1] = "field[0," .. y1 .. ";1.6,1;outp" .. i ..";;" .. outp .. "]"
tbl[#tbl + 1] = "label[1.4," .. y2 .. ";=]"
tbl[#tbl + 1] = "dropdown[1.8," .. y3 .. ";1.6,1;val" .. i ..";,on,off;" .. val .. "]"
tbl[#tbl + 1] = "label[3.5," .. y2 .. ";if]"
tbl[#tbl + 1] = "field[4.2," .. y1 .. ";5.6,1;expr" .. i ..";;" .. expr .. "]"
end
return table.concat(tbl, "")
end
local function formspec(pos, meta)
local nvm = techage.get_nvm(pos)
local err = nvm.error or "ok"
err = minetest.formspec_escape(err)
nvm.io_tbl = nvm.io_tbl or {}
local inputs, outputs = data(nvm)
local bt = nvm.blocking_time or 1
return "size[10,8.2]" ..
"tabheader[0,0;tab;"..S("Rules") .. "," .. S("Help") .. "," .. S("Debug") .. ";1;;true]" ..
"container[0.4,0.1]" ..
rules(meta) ..
"container_end[]" ..
"label[0.2,4.4;" .. S("Blocking Time") .. "]"..
"field[4.6,4.5;2,1;bt;;" .. bt .. "]"..
"label[6.3,4.4;s]"..
"label[0,5.3;" .. S("Inputs") .. ":]" ..
"label[2,5.3;" .. inputs .."]" ..
"label[0,5.9;" .. S("Outputs") .. ":]" ..
"label[2,5.9;" .. outputs .."]" ..
"label[0,6.5;" .. S("Syntax") .. ":]" ..
"label[2,6.5;" .. err .. "]" ..
"button[1.5,7.5;3,1;update;" .. S("Update") .. "]" ..
"button[5.6,7.5;3,1;store;" .. S("Store") .. "]"
end
local function formspec_help()
return "size[10,8.2]" ..
"tabheader[0,0;tab;"..S("Rules") .. "," .. S("Help") .. "," .. S("Debug") .. ";2;;true]" ..
"textarea[0.3,0.3;9.9,8.5;;;"..minetest.formspec_escape(HELP).."]"
end
local function formspec_debug(mem)
mem.debug = mem.debug or {}
local s = table.concat(mem.debug, "\n")
return "size[10,8.2]" ..
"tabheader[0,0;tab;"..S("Rules") .. "," .. S("Help") .. "," .. S("Debug") .. ";3;;true]" ..
"textarea[0.3,0.3;9.9,8.5;;;"..minetest.formspec_escape(s).."]" ..
"button[1.5,7.5;3,1;update2;" .. S("Update") .. "]" ..
"button[5.6,7.5;3,1;clear;" .. S("Clear") .. "]"
end
minetest.register_node("techage:ta3_logic2", {
description = S("TA3 Logic Block"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_logic.png",
},
after_place_node = function(pos, placer)
local meta = M(pos)
local nvm = techage.get_nvm(pos)
logic.after_place_node(pos, placer, "techage:ta3_logic2", S("TA3 Logic Block"))
logic.infotext(meta, S("TA3 Logic Block"))
meta:set_string("formspec", formspec(pos, meta))
meta:set_string("owner", placer:get_player_name())
end,
on_receive_fields = function(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local meta = M(pos)
if fields.store then
for i = 1,NUM_RULES do
meta:set_string("outp" .. i, fields["outp" .. i] or "")
meta:set_string("val" .. i, fields["val" .. i] or "")
meta:set_string("expr" .. i, fields["expr" .. i] or "")
end
local nvm = techage.get_nvm(pos)
nvm.blocking_time = tonumber(fields.bt) or 0.1
nvm.inp_tbl = {me = "off"}
nvm.outp_tbl = {}
elseif fields.update2 then
local mem = techage.get_mem(pos)
meta:set_string("formspec", formspec_debug(mem))
elseif fields.clear then
local mem = techage.get_mem(pos)
mem.debug = {}
meta:set_string("formspec", formspec_debug(mem))
end
if fields.tab == "2" then
meta:set_string("formspec", formspec_help())
elseif fields.tab == "3" then
local mem = techage.get_mem(pos)
meta:set_string("formspec", formspec_debug(mem))
else
local nvm = techage.get_nvm(pos)
local mem = techage.get_mem(pos)
mem.code = nil
get_code(pos, nvm, mem)
meta:set_string("formspec", formspec(pos, meta))
end
end,
on_timer = function(pos)
execute(pos)
return false
end,
on_rightclick = function(pos, node, clicker)
if minetest.is_protected(pos, clicker:get_player_name()) then
return
end
local meta = M(pos)
local nvm = techage.get_nvm(pos)
meta:set_string("formspec", formspec(pos, meta))
end,
after_dig_node = function(pos, oldnode, oldmetadata)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
})
minetest.register_craft({
output = "techage:ta3_logic2",
recipe = {
{"", "group:wood", ""},
{"techage:vacuum_tube", "default:copper_ingot", "techage:vacuum_tube"},
{"", "group:wood", ""},
},
})
techage.register_node({"techage:ta3_logic2"}, {
on_recv_message = function(pos, src, topic, payload)
local nvm = techage.get_nvm(pos)
local mem = techage.get_mem(pos)
nvm.own_num = nvm.own_num or M(pos):get_string("node_number")
nvm.blocking_time = nvm.blocking_time or M(pos):get_float("blocking_time")
nvm.inp_tbl = nvm.inp_tbl or {}
if src ~= nvm.own_num then
if topic == "on" then
debug(mem, "(inp) " .. src .. " = on")
nvm.inp_tbl[src] = "on"
elseif topic == "off" then
debug(mem, "(inp) " .. src .. " = off")
nvm.inp_tbl[src] = "off"
else
debug(mem, "(inp) invalid command")
return "unsupported"
end
local t = math.max((mem.ttl or 0) - techage.SystemTime, 0.1)
minetest.get_node_timer(pos):start(t)
mem.ttl = techage.SystemTime + (nvm.blocking_time or 0)
end
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
local nvm = techage.get_nvm(pos)
local mem = techage.get_mem(pos)
nvm.own_num = nvm.own_num or M(pos):get_string("node_number")
nvm.blocking_time = nvm.blocking_time or M(pos):get_float("blocking_time")
nvm.inp_tbl = nvm.inp_tbl or {}
if src ~= nvm.own_num then
if topic == 1 and payload[1] == 1 then
debug(mem, "(inp) " .. src .. " = on")
nvm.inp_tbl[src] = "on"
return 0
elseif topic == 1 and payload[1] == 0 then
debug(mem, "(inp) " .. src .. " = off")
nvm.inp_tbl[src] = "off"
return 0
else
debug(mem, "(inp) invalid command")
return 2
end
local t = math.max((mem.ttl or 0) - techage.SystemTime, 0.1)
minetest.get_node_timer(pos):start(t)
mem.ttl = techage.SystemTime + (nvm.blocking_time or 0)
end
end,
})
@@ -0,0 +1,264 @@
--[[
TechAge
=======
Copyright (C) 2017-2022 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
Lua Logic Block (Deprecated and replaced by "techage:ta3_logic2")
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local logic = techage.logic
-- mem.inp_tbl = {
-- n001 = true, -- "on" received
-- n002 = false, -- "off" received
-- inp = true, -- last input
-- outp = false, -- last output
-- }
local ValidSymbols = {
["if"] = true,
["then"] = true,
["else"] = true,
["end"] = true,
["return"] = true,
["not"] = true,
["and"] = true,
["or"] = true,
["inp"] = true,
["outp"] = true,
["true"] = true,
["false"] = true,
["nil"] = true,
["=="] = true,
["~="] = true,
["("] = true,
[")"] = true,
}
local function check(expression)
for _, sym in ipairs(string.split(expression, " ")) do
if not ValidSymbols[sym] and string.find(sym, '^[n0-9]+$') == nil then
return false, "Error: Invalid symbol '"..sym.."'"
end
end
return true, "ok"
end
local function compile(nvm, expression)
local res, err = check(expression)
if res then
local code, err = loadstring(expression, "")
if code then
nvm.code = code
nvm.error = "ok"
else
nvm.code = nil
nvm.error = err
end
else
nvm.code = nil
nvm.error = err
end
end
local function get_code(pos, nvm)
local meta = M(pos)
local if_expr = meta:get_string("if_expr") or ""
local then_expr = meta:get_string("then_expr") or ""
local else_expr = meta:get_string("else_expr") or ""
local expr = "if "..if_expr.." then return "..then_expr.." else return "..else_expr.." end"
compile(nvm, expr)
return nvm.code
end
local function eval(pos, nvm)
nvm.code = nvm.code or get_code(pos, nvm)
if nvm.code then
setfenv(nvm.code, nvm.inp_tbl)
local res, sts = pcall(nvm.code)
if res then
nvm.error = "ok"
if sts == true and nvm.inp_tbl.outp ~= true then
nvm.inp_tbl.outp = sts
return "on"
elseif sts == false and nvm.inp_tbl.outp ~= false then
nvm.inp_tbl.outp = sts
return "off"
end
else
nvm.error = "Error: "..sts
end
end
end
local function data(nvm)
local tbl = {"inp = "..dump(nvm.inp_tbl.inp), "outp = "..dump(nvm.inp_tbl.outp)}
for k,v in pairs(nvm.inp_tbl) do
if k ~= "inp" and k ~= "outp" then
tbl[#tbl+1] = k.." = "..dump(v)
end
end
return table.concat(tbl, ", ")
end
local function formspec(pos, meta)
local nvm = techage.get_nvm(pos)
local numbers = meta:get_string("numbers") or ""
local if_expr = meta:get_string("if_expr") or ""
local then_expr = meta:get_string("then_expr") or ""
local else_expr = meta:get_string("else_expr") or ""
local err = nvm.error or "ok"
if err ~= "ok" then
err = string.sub(err, 15)
end
err = minetest.formspec_escape(err)
nvm.inp_tbl = nvm.inp_tbl or {inp = false, outp = false}
local data = data(nvm)
return "size[9,8]"..
"background[0,0;9,1.3;techage_formspec_bg.png]"..
"field[0.5,0.2;8.5,2;numbers;"..S("Insert destination node number(s)")..";"..numbers.."]" ..
"label[0,1.4;Variables: "..data.."]"..
"label[0,2;Valid symbols: not and or true false nil == ~= ( )]"..
"background[0,2.6;9,4;techage_formspec_bg.png]"..
"label[0.1,2.8;if]"..
"field[0.8,2.9;7,1;if_expr;;"..if_expr.."]" ..
"label[7.6,2.8;then]"..
"label[0.6,3.8;return]"..
"field[2,3.9;7,1;then_expr;;"..then_expr.."]" ..
"label[0.1,4.5;else]"..
"label[0.6,5.2;return]"..
"field[2,5.3;7,1;else_expr;;"..else_expr.."]" ..
"label[0.1,6;end]"..
"label[0,6.8;Result: "..err.."]"..
"button[2,7.3;2.5,1;update;"..S("Update").."]"..
"button[5,7.3;2.5,1;store;"..S("Store").."]"
end
minetest.register_node("techage:ta3_logic", {
description = S("TA3 Logic Block"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_logic.png",
},
after_place_node = function(pos, placer)
local meta = M(pos)
local nvm = techage.get_nvm(pos)
nvm.inp_tbl = {inp = false, outp = false}
logic.after_place_node(pos, placer, "techage:ta3_logic", S("TA3 Logic Block"))
logic.infotext(meta, S("TA3 Logic Block"))
meta:set_string("formspec", formspec(pos, meta))
end,
on_receive_fields = function(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local meta = M(pos)
local nvm = techage.get_nvm(pos)
if fields.numbers and fields.numbers ~= "" then
if techage.check_numbers(fields.numbers, player:get_player_name()) then
meta:set_string("numbers", fields.numbers)
logic.infotext(M(pos), S("TA3 Logic Block"))
end
end
if fields.if_expr and fields.if_expr ~= "" then
meta:set_string("if_expr", fields.if_expr)
end
if fields.then_expr and fields.then_expr ~= "" then
meta:set_string("then_expr", fields.then_expr)
end
if fields.else_expr and fields.else_expr ~= "" then
meta:set_string("else_expr", fields.else_expr)
end
if fields.store then
get_code(pos, nvm)
end
meta:set_string("formspec", formspec(pos, meta))
end,
on_timer = function(pos,elapsed)
local nvm = techage.get_nvm(pos)
local topic = eval(pos, nvm)
if topic then
local meta = M(pos)
local own_num = meta:get_string("node_number") or ""
local numbers = meta:get_string("numbers") or ""
techage.send_multi(own_num, numbers, topic)
end
return false
end,
techage_set_numbers = function(pos, numbers, player_name)
local meta = M(pos)
local res = logic.set_numbers(pos, numbers, player_name, S("TA3 Logic Block"))
meta:set_string("formspec", formspec(pos, meta))
return res
end,
after_dig_node = function(pos, oldnode, oldmetadata)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
drop = "techage:ta3_logic2",
sounds = default.node_sound_wood_defaults(),
})
-- Deprecated and replaced by "techage:ta3_logic2"
--minetest.register_craft({
-- output = "techage:ta3_logic",
-- recipe = {
-- {"", "group:wood", ""},
-- {"", "default:copper_ingot", "techage:vacuum_tube"},
-- {"", "group:wood", ""},
-- },
--})
techage.register_node({"techage:ta3_logic"}, {
on_recv_message = function(pos, src, topic, payload)
local nvm = techage.get_nvm(pos)
nvm.inp_tbl = nvm.inp_tbl or {outp = false}
if topic == "on" then
nvm.inp_tbl.inp = true
nvm.inp_tbl["n"..src] = true
elseif topic == "off" then
nvm.inp_tbl.inp = false
nvm.inp_tbl["n"..src] = false
else
return "unsupported"
end
minetest.get_node_timer(pos):start(0.1)
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
if topic == 1 and payload[1] == 1 then
nvm.inp_tbl.inp = true
nvm.inp_tbl["n"..src] = true
return 0
elseif topic == 1 and payload[1] == 0 then
nvm.inp_tbl.inp = false
nvm.inp_tbl["n"..src] = false
return 0
else
return 2
end
end,
on_node_load = function(pos)
end,
})
@@ -0,0 +1,116 @@
--[[
TechAge
=======
Copyright (C) 2017-2022 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA4 Mapblock Active Detector
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local logic = techage.logic
minetest.register_node("techage:ta4_mbadetector", {
description = "TA4 Mapblock Active Detector",
inventory_image = 'techage_smartline_mba_detector_inv.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_smartline_mba_detector.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 = M(pos)
logic.after_place_node(pos, placer, "techage:ta4_mbadetector", S("TA4 Mapblock Active Detector"))
logic.infotext(meta, S("TA4 Mapblock Active Detector"))
minetest.get_node_timer(pos):start(1)
end,
on_timer = function(pos, elapsed)
local mem = techage.get_mem(pos)
mem.gametime = minetest.get_gametime()
return true
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
})
minetest.register_craft({
output = "techage:ta4_mbadetector",
recipe = {
{"", "group:wood", "default:mese_crystal"},
{"", "techage:vacuum_tube", "default:copper_ingot"},
{"", "group:wood", ""},
},
})
techage.register_node({"techage:ta4_mbadetector"}, {
on_recv_message = function(pos, src, topic, payload)
if topic == "state" then
if minetest.compare_block_status then
if minetest.compare_block_status(pos, "active") then
return "on"
else
return "off"
end
else
local mem = techage.get_mem(pos)
local res = mem.gametime and mem.gametime > (minetest.get_gametime() - 2)
return res and "on" or "off"
end
else
return "unsupported"
end
end,
on_beduino_request_data = function(pos, src, topic, payload)
if topic == 142 then -- Binary State
if minetest.compare_block_status then
if minetest.compare_block_status(pos, "active") then
return 0, {1}
else
return 0, {0}
end
else
local mem = techage.get_mem(pos)
local res = mem.gametime and mem.gametime > (minetest.get_gametime() - 2)
return 0, {res and 1 or 0}
end
else
return 2, ""
end
end,
on_node_load = function(pos)
minetest.get_node_timer(pos):start(1)
end,
}
)
@@ -0,0 +1,199 @@
--[[
TechAge
=======
Copyright (C) 2017-2023 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
Mesecons converter
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local logic = techage.logic
local OVER_LOAD_MAX = 10
local CYCLE_TIME = 2
local function formspec(meta)
local numbers = meta:get_string("numbers") or ""
return "size[7.5,3]"..
"field[0.5,1;7,1;numbers;"..S("Insert destination node number(s)")..";"..numbers.."]" ..
"button_exit[2,2;3,1;exit;"..S("Save").."]"
end
local function send_message(pos, topic)
local meta = M(pos)
local mem = techage.get_mem(pos)
mem.overload_cnt = (mem.overload_cnt or 0) + 1
if mem.overload_cnt > OVER_LOAD_MAX then
logic.infotext(M(pos), S("TA3 Mesecons Converter"), "fault (overloaded)")
techage.logic.swap_node(pos, "techage:ta3_mesecons_converter")
minetest.get_node_timer(pos):stop()
return false
end
local own_num = meta:get_string("node_number")
local numbers = meta:get_string("numbers")
techage.send_multi(own_num, numbers, topic)
end
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local meta = M(pos)
if techage.check_numbers(fields.numbers, player:get_player_name()) then
meta:set_string("numbers", fields.numbers)
logic.infotext(M(pos), S("TA3 Mesecons Converter"))
meta:set_string("formspec", formspec(meta))
end
minetest.get_node_timer(pos):start(CYCLE_TIME)
end
local function on_timer(pos,elapsed)
local mem = techage.get_mem(pos)
mem.overload_cnt = 0
return true
end
local function techage_set_numbers(pos, numbers, player_name)
local meta = M(pos)
local res = logic.set_numbers(pos, numbers, player_name, S("TA3 Mesecons Converter"))
meta:set_string("formspec", formspec(meta))
return res
end
local function after_dig_node(pos, oldnode, oldmetadata)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
mesecon.on_dignode(pos, oldnode)
end
minetest.register_node("techage:ta3_mesecons_converter", {
description = S("TA3 Mesecons Converter"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_mesecons_converter.png",
},
after_place_node = function(pos, placer)
local meta = M(pos)
local mem = techage.get_mem(pos)
logic.after_place_node(pos, placer, "techage:ta3_mesecons_converter", S("TA3 Mesecons Converter"))
logic.infotext(meta, S("TA3 Mesecons Converter"))
meta:set_string("formspec", formspec(meta))
mem.overload_cnt = -OVER_LOAD_MAX -- to prevent overload after placing
minetest.get_node_timer(pos):start(CYCLE_TIME)
mesecon.on_placenode(pos, minetest.get_node(pos))
end,
on_receive_fields = on_receive_fields,
on_timer = on_timer,
techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node,
mesecons = {
receptor = {
state = mesecon.state.off,
rules = mesecon.rules.default,
},
effector = {
rules = mesecon.rules.default,
action_on = function(pos, node)
techage.logic.swap_node(pos, "techage:ta3_mesecons_converter_on")
send_message(pos, "on")
end,
action_off = function(pos, node)
end,
action_change = function(pos, node)
techage.logic.swap_node(pos, "techage:ta3_mesecons_converter_on")
send_message(pos, "on")
end,
}
},
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
})
minetest.register_node("techage:ta3_mesecons_converter_on", {
description = S("TA3 Mesecons Converter"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_mesecons_converter.png",
},
on_receive_fields = on_receive_fields,
on_timer = on_timer,
techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node,
mesecons = {
receptor = {
state = mesecon.state.off,
rules = mesecon.rules.default,
},
effector = {
rules = mesecon.rules.default,
action_on = function(pos, node)
end,
action_off = function(pos, node)
techage.logic.swap_node(pos, "techage:ta3_mesecons_converter")
send_message(pos, "off")
end,
action_change = function(pos, node)
techage.logic.swap_node(pos, "techage:ta3_mesecons_converter")
send_message(pos, "off")
end,
}
},
paramtype = "light",
light_source = 5,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
drop = "techage:ta3_mesecons_converter",
})
minetest.register_craft({
output = "techage:ta3_mesecons_converter",
recipe = {
{"techage:ta3_repeater", "mesecons:wire_00000000_off"},
},
})
techage.register_node({"techage:ta3_mesecons_converter", "techage:ta3_mesecons_converter_on"}, {
on_recv_message = function(pos, src, topic, payload)
local mem = techage.get_mem(pos)
mem.overload_cnt = (mem.overload_cnt or 0) + 1
if mem.overload_cnt > OVER_LOAD_MAX then
logic.infotext(M(pos), S("TA3 Mesecons Converter"), "fault (overloaded)")
minetest.get_node_timer(pos):stop()
return false
elseif topic == "on" then
techage.logic.swap_node(pos, "techage:ta3_mesecons_converter_on")
mesecon.receptor_on(pos, mesecon.rules.default)
elseif topic == "off" then
techage.logic.swap_node(pos, "techage:ta3_mesecons_converter")
mesecon.receptor_off(pos, mesecon.rules.default)
end
end,
on_node_load = function(pos)
minetest.get_node_timer(pos):start(CYCLE_TIME)
end,
})
@@ -0,0 +1,407 @@
--[[
TechAge
=======
Copyright (C) 2017-2023 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA3/TA4 Node Detector
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local MP = minetest.get_modpath("techage")
local mark = dofile(MP .. "/basis/mark_lib.lua")
local logic = techage.logic
local CYCLE_TIME = 2
local MAX_BLOCKS = 4
local DESCR3 = S("TA3 Node Detector")
local DESCR4 = S("TA4 Node Detector")
local function switch_on3(pos)
if logic.swap_node(pos, "techage:ta3_nodedetector_on") then
logic.send_on(pos, M(pos))
end
end
local function switch_off3(pos)
if logic.swap_node(pos, "techage:ta3_nodedetector_off") then
logic.send_off(pos, M(pos))
end
end
local function switch_on4(pos)
if logic.swap_node(pos, "techage:ta4_nodedetector_on") then
logic.send_on(pos, M(pos))
end
end
local function switch_off4(pos)
if logic.swap_node(pos, "techage:ta4_nodedetector_off") then
logic.send_off(pos, M(pos))
end
end
local DropdownValues = {
[S("added")] = 1,
[S("removed")] = 2,
[S("added or removed")] = 3,
}
local AirLikeBlocks = {"air"}
local kvAirLikeBlocks = {air = 1}
for i = 1,14 do
-- Add light blocks from the mod "wielded_light" to the air-like blocks
AirLikeBlocks[#AirLikeBlocks + 1] = "wielded_light:" .. i
kvAirLikeBlocks["wielded_light:" .. i] = 1
end
local function formspec3(meta, nvm)
local numbers = meta:get_string("numbers") or ""
local label = S("added")..","..S("removed")..","..S("added or removed")
return "size[7.5,4]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"field[0.5,0.6;7,1;numbers;"..S("Insert destination node number(s)")..";"..numbers.."]" ..
"label[0.2,1.6;"..S("Send signal if nodes have been:").."]"..
"dropdown[0.2,2.1;7.3,1;mode;"..label..";"..(nvm.mode or 3).."]"..
"button_exit[2,3.2;3,1;accept;"..S("accept").."]"
end
local function formspec4(meta, nvm)
local numbers = meta:get_string("numbers") or ""
local label = S("added")..","..S("removed")..","..S("added or removed")
return "size[7.5,6]"..
"box[0,-0.1;7.2,0.5;#c6e8ff]" ..
"label[0.2,-0.1;" .. minetest.colorize( "#000000", DESCR4) .. "]" ..
"button[0.1,0.6;3.6,1;record;" .. S("Record") .. "]" ..
"button[3.9,0.6;3.6,1;done;" .. S("Done") .. "]" ..
"field[0.5,2.6;7,1;numbers;"..S("Insert destination node number(s)")..";"..numbers.."]" ..
"label[0.2,3.6;"..S("Send signal if nodes have been:").."]"..
"dropdown[0.2,4.1;7.3,1;mode;"..label..";"..(nvm.mode or 3).."]"..
"button_exit[2,5.2;3,1;accept;"..S("accept").."]"
end
local function any_node_changed3(pos)
local nvm = techage.get_nvm(pos)
if not nvm.pos1 or not nvm.pos2 or not nvm.num then
local node = minetest.get_node(pos)
local param2 = (node.param2 + 2) % 4
nvm.pos1 = logic.dest_pos(pos, param2, {0})
nvm.pos2 = logic.dest_pos(pos, param2, {0,0,0})
nvm.num = #minetest.find_nodes_in_area(nvm.pos1, nvm.pos2, AirLikeBlocks)
return false
end
local num1 = #minetest.find_nodes_in_area(nvm.pos1, nvm.pos2, AirLikeBlocks)
local num2 = #minetest.find_nodes_in_area(nvm.pos1, nvm.pos2, {"ignore"})
if num2 == 0 and nvm.num ~= num1 then
if nvm.mode == 1 and num1 < nvm.num then
nvm.num = num1
return true
elseif nvm.mode == 2 and num1 > nvm.num then
nvm.num = num1
return true
elseif nvm.mode == 3 then
nvm.num = num1
return true
end
nvm.num = num1
end
return false
end
local function any_node_changed4(pos)
local nvm = techage.get_nvm(pos)
nvm.lpos = nvm.lpos or {}
local num = 0
for _,pos1 in ipairs(nvm.lpos) do
local name = minetest.get_node(pos1).name
if name == "ignore" then return false end
num = num + (kvAirLikeBlocks[name] or 0)
end
if not nvm.num then
nvm.num = num
elseif nvm.num ~= num then
if nvm.mode == 1 and num < nvm.num then
nvm.num = num
return true
elseif nvm.mode == 2 and num > nvm.num then
nvm.num = num
return true
elseif nvm.mode == 3 then
nvm.num = num
return true
end
nvm.num = num
end
return false
end
local function on_receive_fields3(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local nvm = techage.get_nvm(pos)
local meta = M(pos)
if fields.accept then
nvm.mode = DropdownValues[fields.mode] or 3
if techage.check_numbers(fields.numbers, player:get_player_name()) then
meta:set_string("numbers", fields.numbers)
logic.infotext(M(pos), DESCR3)
end
end
meta:set_string("formspec", formspec3(meta, nvm))
end
local function on_receive_fields4(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local name = player:get_player_name()
local nvm = techage.get_nvm(pos)
local meta = M(pos)
if fields.accept then
nvm.mode = DropdownValues[fields.mode] or 3
if techage.check_numbers(fields.numbers, name) then
meta:set_string("numbers", fields.numbers)
logic.infotext(M(pos), DESCR4)
end
mark.unmark_all(name)
mark.stop(name)
elseif fields.record then
nvm.lpos = {}
minetest.chat_send_player(name, "[techage] " .. S("Click on all blocks whose positions should be checked"))
mark.start(name, MAX_BLOCKS)
elseif fields.done then
local pos_list = mark.get_poslist(name)
minetest.chat_send_player(name, "[techage] " .. #pos_list.." "..S("block positions are stored."))
nvm.lpos = pos_list
mark.unmark_all(name)
mark.stop(name)
end
meta:set_string("formspec", formspec4(meta, nvm))
end
local function node_timer3(pos)
if any_node_changed3(pos)then
switch_on3(pos)
else
switch_off3(pos)
end
return true
end
minetest.register_node("techage:ta3_nodedetector_off", {
description = DESCR3,
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png^techage_appl_arrow.png^[transformR270",
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_nodedetector.png",
},
after_place_node = function(pos, placer)
local meta = M(pos)
local nvm = techage.get_nvm(pos)
logic.after_place_node(pos, placer, "techage:ta3_nodedetector_off", DESCR3)
logic.infotext(meta, DESCR3)
nvm.mode = 3 -- default mode
meta:set_string("formspec", formspec3(meta, nvm))
minetest.get_node_timer(pos):start(CYCLE_TIME)
any_node_changed3(pos)
end,
on_timer = node_timer3,
on_receive_fields = on_receive_fields3,
techage_set_numbers = function(pos, numbers, player_name)
local meta = M(pos)
local res = logic.set_numbers(pos, numbers, player_name, DESCR3)
meta:set_string("formspec", formspec3(meta, techage.get_nvm(pos)))
return res
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end,
on_rotate = screwdriver.disallow,
paramtype2 = "facedir",
is_ground_content = false,
groups = {choppy=2, cracky=2, crumbly=2},
sounds = default.node_sound_wood_defaults(),
})
minetest.register_node("techage:ta3_nodedetector_on", {
description = DESCR3,
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png^techage_appl_arrow.png^[transformR270",
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_nodedetector_on.png",
},
on_timer = node_timer3,
techage_set_numbers = function(pos, numbers, player_name)
local meta = M(pos)
local res = logic.set_numbers(pos, numbers, player_name, DESCR3)
meta:set_string("formspec", formspec3(meta, techage.get_nvm(pos)))
return res
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end,
on_rotate = screwdriver.disallow,
paramtype2 = "facedir",
is_ground_content = false,
drop = "techage:ta3_nodedetector_off",
groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory = 1},
sounds = default.node_sound_wood_defaults(),
})
local function node_timer4(pos)
if any_node_changed4(pos)then
switch_on4(pos)
else
switch_off4(pos)
end
return true
end
minetest.register_node("techage:ta4_nodedetector_off", {
description = DESCR4,
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta4.png^techage_frame_ta4_top.png^techage_appl_arrow.png^[transformR270",
"techage_filling_ta4.png^techage_frame_ta4_top.png",
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_nodedetector.png",
},
after_place_node = function(pos, placer)
local meta = M(pos)
local nvm = techage.get_nvm(pos)
logic.after_place_node(pos, placer, "techage:ta4_nodedetector_off", DESCR4)
logic.infotext(meta, DESCR4)
nvm.mode = 3 -- default mode
meta:set_string("formspec", formspec4(meta, nvm))
minetest.get_node_timer(pos):start(CYCLE_TIME)
end,
on_timer = node_timer4,
on_receive_fields = on_receive_fields4,
techage_set_numbers = function(pos, numbers, player_name)
local meta = M(pos)
local res = logic.set_numbers(pos, numbers, player_name, DESCR4)
meta:set_string("formspec", formspec4(meta, techage.get_nvm(pos)))
return res
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end,
on_rotate = screwdriver.disallow,
paramtype2 = "facedir",
is_ground_content = false,
groups = {choppy=2, cracky=2, crumbly=2},
sounds = default.node_sound_wood_defaults(),
})
minetest.register_node("techage:ta4_nodedetector_on", {
description = DESCR4,
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta4.png^techage_frame_ta4_top.png^techage_appl_arrow.png^[transformR270",
"techage_filling_ta4.png^techage_frame_ta4_top.png",
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_nodedetector_on.png",
},
on_timer = node_timer4,
techage_set_numbers = function(pos, numbers, player_name)
local meta = M(pos)
local res = logic.set_numbers(pos, numbers, player_name, DESCR4)
meta:set_string("formspec", formspec4(meta, techage.get_nvm(pos)))
return res
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end,
on_rotate = screwdriver.disallow,
paramtype2 = "facedir",
is_ground_content = false,
drop = "techage:ta4_nodedetector_off",
groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory = 1},
sounds = default.node_sound_wood_defaults(),
})
minetest.register_craft({
output = "techage:ta3_nodedetector_off",
recipe = {
{"", "group:wood", ""},
{"", "default:copper_ingot", "techage:vacuum_tube"},
{"", "group:wood", "default:mese_crystal"},
},
})
minetest.register_craft({
output = "techage:ta4_nodedetector_off",
recipe = {
{"", "dye:blue", ""},
{"", "techage:ta3_nodedetector_off", ""},
{"", "techage:ta4_wlanchip", ""},
},
})
techage.register_node({"techage:ta3_nodedetector_off", "techage:ta3_nodedetector_on",
"techage:ta4_nodedetector_off", "techage:ta4_nodedetector_on"}, {
on_recv_message = function(pos, src, topic, payload)
if topic == "state" then
local node = techage.get_node_lvm(pos)
if node.name == "techage:ta3_nodedetector_off" or node.name == "techage:ta4_nodedetector_off" then
return "off"
else
return "on"
end
else
return "unsupported"
end
end,
on_beduino_request_data = function(pos, src, topic, payload)
if topic == 142 then
local node = techage.get_node_lvm(pos)
if node.name == "techage:ta3_nodedetector_off" or node.name == "techage:ta4_nodedetector_off" then
return 0, {0}
else
return 0, {1}
end
else
return 2, ""
end
end,
on_node_load = function(pos)
minetest.get_node_timer(pos):start(CYCLE_TIME)
end,
})
@@ -0,0 +1,382 @@
--[[
TechAge
=======
Copyright (C) 2017-2022 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA3 & TA4 Player Detector
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local NDEF = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}) end
local logic = techage.logic
local CYCLE_TIME = 1
local WRENCH_MENU = {
{
type = "dropdown",
choices = "1,2,3,4,5,6,7,8",
name = "radius",
label = S("Radius"),
tooltip = S("Search radius"),
default = "4",
},
{
type = "numbers",
name = "numbers",
label = S("Number"),
tooltip = S("Destination block number"),
default = "",
check = techage.check_numbers,
},
{
type = "ascii",
name = "command1",
label = "On " .. S("Command"),
tooltip = S("Command to send when player is detected"),
default = "on",
},
{
type = "ascii",
name = "command2",
label = "Off " .. S("Command"),
tooltip = S("Command to send when player moves away"),
default = "off",
},
}
local function switch_on(pos, stage)
if logic.swap_node(pos, "techage:ta"..stage.."_playerdetector_on") then
logic.send_cmnd(pos, "command1", "on")
end
end
local function switch_off(pos, stage)
if logic.swap_node(pos, "techage:ta"..stage.."_playerdetector_off") then
logic.send_cmnd(pos, "command2", "off")
end
end
local function scan_for_player(pos)
local nvm = techage.get_nvm(pos)
local meta = minetest.get_meta(pos)
local names = meta:get_string("names") or ""
local radius = meta:contains("radius") and meta:get_int("radius") or 4
for _, object in pairs(minetest.get_objects_inside_radius(pos, radius)) do
if object:is_player() then
if names == "" then
nvm.player_name = object:get_player_name()
return true
end
for _,name in ipairs(string.split(names, " ")) do
if object:get_player_name() == name then
nvm.player_name = name
return true
end
end
end
end
nvm.player_name = nil
return false
end
local function formspec_help()
return "size[8,6]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"label[3,0;Player Detector Help]"..
"label[0,1;Input the number(s) of the destination node(s).\n"..
"Separate numbers via blanks, like '123 234'.\n\n"..
"Input the player name(s) separated by blanks,\nor empty for all players.]"..
"button_exit[3,5;2,1;exit;close]"
end
local function formspec(meta)
local numbers = meta:get_string("numbers") or ""
local names = meta:get_string("names") or ""
return "size[7,4]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"field[0.3,0.6;7,1;numbers;"..S("Insert destination node number(s)")..";"..numbers.."]" ..
"field[0.3,2;7,1;names;Insert player name(s) (optional):;"..names.."]" ..
"button[0.9,3;2.5,1;help;help]"..
"button_exit[3.5,3;2.5,1;exit;Save]"
end
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local meta = minetest.get_meta(pos)
if fields.exit == "Save" then
if techage.check_numbers(fields.numbers, player:get_player_name()) then
meta:set_string("numbers", fields.numbers)
logic.infotext(M(pos), NDEF(pos).description)
end
meta:set_string("names", fields.names)
meta:set_string("formspec", formspec(meta))
elseif fields.help ~= nil then
meta:set_string("formspec", formspec_help())
elseif fields.exit == "close" then
meta:set_string("formspec", formspec(meta))
end
end
minetest.register_node("techage:ta3_playerdetector_off", {
description = S("TA3 Player Detector"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_playerdetector.png",
},
after_place_node = function(pos, placer)
local meta = M(pos)
logic.after_place_node(pos, placer, "techage:ta3_playerdetector_off", S("TA3 Player Detector"))
logic.infotext(meta, S("TA3 Player Detector"))
meta:set_string("formspec", formspec(meta))
minetest.get_node_timer(pos):start(CYCLE_TIME)
end,
on_receive_fields = on_receive_fields,
on_timer = function (pos, elapsed)
if scan_for_player(pos) then
switch_on(pos, 3)
end
return true
end,
techage_set_numbers = function(pos, numbers, player_name)
local meta = M(pos)
local res = logic.set_numbers(pos, numbers, player_name, S("TA3 Player Detector"))
meta:set_string("formspec", formspec(meta))
return res
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
})
minetest.register_node("techage:ta3_playerdetector_on", {
description = "TA3 Player Detector",
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_playerdetector_on.png",
},
on_receive_fields = on_receive_fields,
on_timer = function (pos, elapsed)
if not scan_for_player(pos) then
switch_off(pos, 3)
end
return true
end,
techage_set_numbers = function(pos, numbers, player_name)
local meta = M(pos)
local res = logic.set_numbers(pos, numbers, player_name, S("TA3 Player Detector"))
meta:set_string("formspec", formspec(meta))
return res
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
drop = "techage:ta3_playerdetector_off"
})
minetest.register_node("techage:ta4_playerdetector_off", {
description = S("TA4 Player Detector"),
inventory_image = 'techage_smartline_detector_inv.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_smartline_detector.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 = M(pos)
logic.after_place_node(pos, placer, "techage:ta4_playerdetector_off", S("TA4 Player Detector"))
logic.infotext(meta, S("TA4 Player Detector"))
meta:set_string("formspec", formspec(meta))
minetest.get_node_timer(pos):start(CYCLE_TIME)
end,
on_receive_fields = on_receive_fields,
on_timer = function (pos, elapsed)
if scan_for_player(pos) then
switch_on(pos, 4)
end
return true
end,
techage_set_numbers = function(pos, numbers, player_name)
local meta = M(pos)
local res = logic.set_numbers(pos, numbers, player_name, S("TA4 Player Detector"))
meta:set_string("formspec", formspec(meta))
return res
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end,
ta4_formspec = WRENCH_MENU,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("techage:ta4_playerdetector_on", {
description = "TA4 Player Detector",
inventory_image = 'techage_smartline_detector_inv.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_smartline_detector_on.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -6/32, -6/32, 14/32, 6/32, 6/32, 16/32},
},
},
on_receive_fields = on_receive_fields,
on_timer = function (pos, elapsed)
if not scan_for_player(pos) then
switch_off(pos, 4)
end
return true
end,
techage_set_numbers = function(pos, numbers, player_name)
local meta = M(pos)
local res = logic.set_numbers(pos, numbers, player_name, S("TA4 Player Detector"))
meta:set_string("formspec", formspec(meta))
return res
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end,
ta4_formspec = WRENCH_MENU,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1},
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
drop = "techage:ta4_playerdetector_off"
})
minetest.register_craft({
output = "techage:ta3_playerdetector_off",
recipe = {
{"", "group:wood", "default:mese_crystal"},
{"", "default:copper_ingot", "techage:vacuum_tube"},
{"", "group:wood", ""},
},
})
minetest.register_craft({
output = "techage:ta4_playerdetector_off",
recipe = {
{"", "techage:aluminum", "dye:blue"},
{"", "default:copper_ingot", "techage:ta4_wlanchip"},
},
})
techage.register_node({
"techage:ta3_playerdetector_off", "techage:ta3_playerdetector_on",
"techage:ta4_playerdetector_off", "techage:ta4_playerdetector_on"
}, {
on_recv_message = function(pos, src, topic, payload)
if topic == "name" then
local nvm = techage.get_nvm(pos)
return nvm.player_name or ""
elseif topic == "state" then
local node = techage.get_node_lvm(pos)
if node.name == "techage:ta3_playerdetector_on" or
node.name == "techage:ta4_playerdetector_on" then
return "on"
else
return "off"
end
else
return "unsupported"
end
end,
on_beduino_request_data = function(pos, src, topic, payload)
if topic == 144 then -- Player Name
local nvm = techage.get_nvm(pos)
return 0, nvm.player_name or ""
elseif topic == 142 then -- Binary State
local node = techage.get_node_lvm(pos)
if node.name == "techage:ta3_playerdetector_on" or
node.name == "techage:ta4_playerdetector_on" then
return 0, {1}
else
return 0, {0}
end
else
return 2, ""
end
end,
on_node_load = function(pos)
minetest.get_node_timer(pos):start(CYCLE_TIME)
end,
}
)
@@ -0,0 +1,100 @@
--[[
TechAge
=======
Copyright (C) 2017-2020 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
Number programmer
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local function join_to_string(tbl)
local t = {}
for key,_ in pairs(tbl) do
t[#t + 1] = key
end
return table.concat(t, " ")
end
local function reset_programmer(itemstack, user, pointed_thing)
user:get_meta():set_string("techage_prog_numbers", "")
minetest.chat_send_player(user:get_player_name(), S("[TechAge Programmer] programmer reset"))
return itemstack
end
local function read_number(itemstack, user, pointed_thing)
local pos = pointed_thing.under
if pos then
local number = techage.get_node_number(pos)
if number then
local numbers = minetest.deserialize(user:get_meta():get_string("techage_prog_numbers")) or {}
techage.add_to_set(numbers, number)
user:get_meta():set_string("techage_prog_numbers", minetest.serialize(numbers))
minetest.chat_send_player(user:get_player_name(), S("[TechAge Programmer] number").." "..number.." read")
else
minetest.chat_send_player(user:get_player_name(), S("[TechAge Programmer] Unknown node on").." "..minetest.pos_to_string(pos))
end
else
return reset_programmer(itemstack, user, pointed_thing)
end
return itemstack
end
local function program_numbers(itemstack, placer, pointed_thing)
local pos = pointed_thing.under
if pos then
local meta = M(pos)
local numbers = minetest.deserialize(placer:get_meta():get_string("techage_prog_numbers")) or {}
placer:get_meta():set_string("techage_prog_numbers", "")
local player_name = placer:get_player_name()
if meta and meta:get_string("owner") ~= player_name then
minetest.chat_send_player(player_name, S("[TechAge Programmer] foreign or unknown node!"))
return itemstack
end
local text = table.concat(numbers, " ")
local ndef = minetest.registered_nodes[minetest.get_node(pos).name]
if ndef and ndef.techage_set_numbers then
local res = ndef.techage_set_numbers(pos, text, player_name)
if res == true then
minetest.chat_send_player(player_name, S("[TechAge Programmer] node programmed!"))
else
minetest.chat_send_player(player_name, S("[TechAge Programmer] Error: invalid numbers!"))
end
else
minetest.chat_send_player(player_name, S("[TechAge Programmer] Error: programmer not supported!"))
end
return itemstack
else
return reset_programmer(itemstack, placer, pointed_thing)
end
end
minetest.register_craftitem("techage:programmer", {
description = S("TechAge Programmer (right = read number, left = write numbers)"),
inventory_image = "techage_programmer.png",
stack_max = 1,
wield_image = "techage_programmer_wield.png",
groups = {cracky=1, book=1},
-- left mouse button = program
on_use = program_numbers,
on_secondary_use = reset_programmer,
-- right mouse button = read
on_place = read_number,
})
minetest.register_craft({
output = "techage:programmer",
recipe = {
{"", "default:steel_ingot", ""},
{"", "techage:ta4_wlanchip", ""},
{"", "dye:red", ""},
},
})
@@ -0,0 +1,132 @@
--[[
TechAge
=======
Copyright (C) 2017-2020 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
Signal Repeater
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local logic = techage.logic
local OVER_LOAD_MAX = 10
local CYCLE_TIME = 2
local function formspec(meta)
local numbers = meta:get_string("numbers") or ""
return "size[7.5,3]"..
"field[0.5,1;7,1;numbers;"..S("Insert destination node number(s)")..";"..numbers.."]" ..
"button_exit[2,2;3,1;exit;"..S("Save").."]"
end
minetest.register_node("techage:ta3_repeater", {
description = S("TA3 Repeater"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_repeater.png",
},
after_place_node = function(pos, placer)
local meta = M(pos)
local mem = techage.get_mem(pos)
logic.after_place_node(pos, placer, "techage:ta3_repeater", S("TA3 Repeater"))
logic.infotext(meta, S("TA3 Repeater"))
meta:set_string("formspec", formspec(meta))
mem.overload_cnt = 0
minetest.get_node_timer(pos):start(CYCLE_TIME)
end,
on_receive_fields = function(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local meta = M(pos)
if techage.check_numbers(fields.numbers, player:get_player_name()) then
meta:set_string("numbers", fields.numbers)
logic.infotext(M(pos), S("TA3 Repeater"))
meta:set_string("formspec", formspec(meta))
end
end,
on_timer = function(pos,elapsed)
local mem = techage.get_mem(pos)
mem.overload_cnt = 0
return true
end,
techage_set_numbers = function(pos, numbers, player_name)
local meta = M(pos)
local res = logic.set_numbers(pos, numbers, player_name, S("TA3 Repeater"))
meta:set_string("formspec", formspec(meta))
return res
end,
after_dig_node = function(pos, oldnode, oldmetadata)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
})
minetest.register_craft({
output = "techage:ta3_repeater",
recipe = {
{"", "group:wood", ""},
{"techage:vacuum_tube", "", "techage:vacuum_tube"},
{"", "group:wood", ""},
},
})
techage.register_node({"techage:ta3_repeater"}, {
on_recv_message = function(pos, src, topic, payload)
local mem = techage.get_mem(pos)
mem.overload_cnt = (mem.overload_cnt or 0) + 1
if mem.overload_cnt > OVER_LOAD_MAX then
logic.infotext(M(pos), S("TA3 Repeater"), "fault (overloaded)")
minetest.get_node_timer(pos):stop()
return false
else
local numbers = M(pos):get_string("numbers") or ""
techage.counting_start(M(pos):get_string("owner"))
techage.send_multi(src, numbers, topic, payload)
techage.counting_stop()
end
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
local mem = techage.get_mem(pos)
mem.overload_cnt = (mem.overload_cnt or 0) + 1
if mem.overload_cnt > OVER_LOAD_MAX then
logic.infotext(M(pos), S("TA3 Repeater"), "fault (overloaded)")
minetest.get_node_timer(pos):stop()
return 3
else
if topic == 1 then
local numbers = M(pos):get_string("numbers") or ""
techage.counting_start(M(pos):get_string("owner"))
techage.send_multi(src, numbers, payload[1] == 1 and "on" or "off")
techage.counting_stop()
return 0
end
end
return 2
end,
on_node_load = function(pos)
minetest.get_node_timer(pos):start(CYCLE_TIME)
end,
})
@@ -0,0 +1,309 @@
--[[
TechAge
=======
Copyright (C) 2017-2022 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA3 Sequencer
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local logic = techage.logic
local NUM_SLOTS = 8
local sAction = ",on,off"
local kvAction = {[""]=1, ["on"]=2, ["off"]=3}
local tAction = {nil, "on", "off"}
local function new_rules()
local tbl = {}
for idx = 1,NUM_SLOTS do
tbl[idx] = {offs = "", num = "", act = 1}
end
return tbl
end
local function formspec(state, rules, endless)
local tbl = {"size[8,9.2]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"label[0,0;Number(s)]label[2.1,0;Command]label[6.4,0;Offset/s]"}
for idx, rule in ipairs(rules or {}) do
tbl[#tbl+1] = "field[0.2,"..(-0.2+idx)..";2,1;num"..idx..";;"..(rule.num or "").."]"
tbl[#tbl+1] = "dropdown[2,"..(-0.4+idx)..";3.9,1;act"..idx..";"..sAction..";"..(rule.act or "").."]"
tbl[#tbl+1] = "field[6.2,"..(-0.2+idx)..";2,1;offs"..idx..";;"..(rule.offs or "").."]"
end
tbl[#tbl+1] = "checkbox[0,8.5;endless;Run endless;"..dump(endless).."]"
tbl[#tbl+1] = "button[2.2,8.5;1.5,1;help;help]"
tbl[#tbl+1] = "button[4.2,8.5;1.5,1;save;Save]"
tbl[#tbl+1] = "image_button[6.2,8.5;1,1;".. techage.state_button(state) ..";button;]"
return table.concat(tbl)
end
local function formspec_help()
return "size[8,9.2]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"label[2,0;TA3 Sequencer Help]"..
"label[0,1;Define a sequence of commands\nto control other machines.]"..
"label[0,2.2;Numbers(s) are the node numbers,\nthe command shall sent to.]"..
"label[0,3.4;The commands 'on'/'off' are used\n for machines and other nodes.]"..
"label[0,4.6;Offset is the time to the\nnext line in seconds (0.2 to 999).]"..
"label[0,5.8;If endless is set, the Sequencer\nrestarts again and again.]"..
"label[0,7;The command ' ' does nothing,\nonly consuming the offset time.]"..
"button[3,8;2,1;exit;close]"
end
local function stop_the_sequencer(pos)
local nvm = techage.get_nvm(pos)
local meta = M(pos)
nvm.running = false
nvm.endless = nvm.endless or false
nvm.rules = nvm.rules or new_rules()
logic.infotext(meta, S("TA3 Sequencer"), "stopped")
meta:set_string("formspec", formspec(techage.STOPPED, nvm.rules, nvm.endless))
minetest.get_node_timer(pos):stop()
return false
end
local function get_next_slot(idx, rules, endless)
idx = idx + 1
if idx <= #rules and rules[idx].offs ~= "" and rules[idx].num ~= "" then
return idx
elseif endless then
return 1
end
return nil
end
local function restart_timer(pos, time)
local timer = minetest.get_node_timer(pos)
if timer:is_started() then
timer:stop()
end
if type(time) == "number" then
time = math.max(time, 0.2)
timer:start(time)
end
end
local function check_rules(pos, elapsed)
local nvm = techage.get_nvm(pos)
local own_num = M(pos):get_string("node_number")
nvm.rules = nvm.rules or new_rules()
nvm.running = nvm.running or false
nvm.index = nvm.index or 1
nvm.endless = nvm.endless or false
techage.counting_start(M(pos):get_string("owner"))
while true do -- process all rules as long as offs == 0
local rule = nvm.rules[nvm.index]
local offs = tonumber(nvm.rules[nvm.index].offs or 1)
techage.send_multi(own_num, rule.num, tAction[rule.act])
nvm.index = get_next_slot(nvm.index, nvm.rules, nvm.endless)
if nvm.index ~= nil and offs ~= nil and nvm.running then
-- after the last rule a pause with 1 or more sec is required
if nvm.index == 1 and offs < 1 then
offs = 1
end
if offs > 0 then
-- we can't restart the timer within the function om_timer
minetest.after(0, restart_timer, pos, offs)
techage.counting_stop()
return false
end
else
techage.counting_stop()
return stop_the_sequencer(pos)
end
end
techage.counting_stop()
return false
end
local function start_the_sequencer(pos)
local nvm = techage.get_nvm(pos)
if not nvm.running then
local meta = M(pos)
nvm.running = true
nvm.endless = nvm.endless or false
nvm.rules = nvm.rules or new_rules()
logic.infotext(meta, S("TA3 Sequencer"), "running")
meta:set_string("formspec", formspec(techage.RUNNING, nvm.rules, nvm.endless))
minetest.get_node_timer(pos):start(0.1)
end
return false
end
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local meta = M(pos)
local nvm = techage.get_nvm(pos)
nvm.running = nvm.running or false
nvm.endless = nvm.endless or false
nvm.rules = nvm.rules or new_rules()
if fields.help ~= nil then
meta:set_string("formspec", formspec_help())
return
end
if fields.endless ~= nil then
nvm.endless = fields.endless == "true"
nvm.index = 1
end
if fields.exit ~= nil then
if nvm.running then
meta:set_string("formspec", formspec(techage.RUNNING, nvm.rules, nvm.endless))
else
meta:set_string("formspec", formspec(techage.STOPPED, nvm.rules, nvm.endless))
end
return
end
for idx = 1,NUM_SLOTS do
if fields["offs"..idx] ~= nil then
nvm.rules[idx].offs = tonumber(fields["offs"..idx]) or ""
end
if fields["num"..idx] ~= nil and
techage.check_numbers(fields["num"..idx], player:get_player_name()) then
nvm.rules[idx].num = fields["num"..idx]
end
if fields["act"..idx] ~= nil then
nvm.rules[idx].act = kvAction[fields["act"..idx]]
end
end
if fields.button ~= nil then
if nvm.running then
stop_the_sequencer(pos)
else
start_the_sequencer(pos)
end
elseif fields.num1 ~= nil then -- any other change?
stop_the_sequencer(pos)
else
if nvm.running then
meta:set_string("formspec", formspec(techage.RUNNING, nvm.rules, nvm.endless))
else
meta:set_string("formspec", formspec(techage.STOPPED, nvm.rules, nvm.endless))
end
end
end
minetest.register_node("techage:ta3_sequencer", {
description = S("TA3 Sequencer"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_sequencer.png",
},
after_place_node = function(pos, placer)
local meta = M(pos)
local nvm = techage.get_nvm(pos)
logic.after_place_node(pos, placer, "techage:ta3_sequencer", S("TA3 Sequencer"))
logic.infotext(meta, S("TA3 Sequencer"), S("stopped"))
nvm.rules = new_rules()
nvm.index = 1
nvm.running = false
nvm.endless = false
meta:set_string("formspec", formspec(techage.STOPPED, nvm.rules, nvm.endless))
end,
on_receive_fields = on_receive_fields,
can_dig = function(pos, puncher)
if minetest.is_protected(pos, puncher:get_player_name()) then
return false
end
local nvm = techage.get_nvm(pos)
return not nvm.running
end,
after_dig_node = function(pos, oldnode, oldmetadata)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end,
on_timer = check_rules,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_stone_defaults(),
})
minetest.register_craft({
output = "techage:ta3_sequencer",
recipe = {
{"group:wood", "group:wood", ""},
{"default:mese_crystal", "techage:vacuum_tube", ""},
{"group:wood", "group:wood", ""},
},
})
techage.register_node({"techage:ta3_sequencer"}, {
on_recv_message = function(pos, src, topic, payload)
if topic == "on" then
start_the_sequencer(pos)
elseif topic == "off" then
-- do not stop immediately
local nvm = techage.get_nvm(pos)
if not nvm.running then
nvm.endless = not (nvm.endless or false)
else
nvm.endless = false
end
elseif topic == "pause" then
stop_the_sequencer(pos)
else
return "unsupported"
end
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
local nvm = techage.get_nvm(pos)
if topic == 7 then -- TA3 Sequenzer
if payload[1] == 1 then
start_the_sequencer(pos)
return 0
elseif payload[1] == 0 then
-- do not stop immediately
local nvm = techage.get_nvm(pos)
if not nvm.running then
nvm.endless = not (nvm.endless or false)
else
nvm.endless = false
end
return 0
elseif payload[1] == 2 then
stop_the_sequencer(pos)
return 0
end
end
return 2
end,
on_node_load = function(pos)
local nvm = techage.get_nvm(pos)
if nvm.running then
minetest.get_node_timer(pos):start(1)
end
end,
})
@@ -0,0 +1,409 @@
--[[
TechAge
=======
Copyright (C) 2017-2025 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA4 Sequencer
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local logic = techage.logic
local HELP = S("Syntax:\n") ..
S("'[<num>] <command>'\n") ..
S("\n") ..
S("<num> is a number from 1 to 50000 and is\n") ..
S("the timeslot when the command is executed.\n") ..
S(" - 1 corresponds to 100 ms\n") ..
S(" - 50000 corresponds to 4 game days\n") ..
S("\n") ..
S("<command> is one of the following:\n") ..
S(" - 'send <node num> <cmnd>' (techage command)\n") ..
S(" - 'goto <num>' (jump to another line)\n") ..
S(" - 'stop' (stop the execution)\n") ..
S(" - 'nop' (do nothing)\n") ..
S("\n") ..
S("Example:\n") ..
" -- move controller commands\n" ..
" [1] send 1234 a2b\n" ..
" [30] send 1234 b2a\n" ..
" [60] goto 1 -- keep going"
local WRENCH_MENU = {
{
type = "dropdown",
choices = "100ms,200ms,500ms,1s,2s",
name = "cycletime",
label = S("Cycle time"),
tooltip = S("Timer cycle time (default: 100 ms)"),
default = "100ms",
values = {0.1, 0.2, 0.5, 1.0, 2.0}
},
{
type = "dropdown",
choices = "no,yes",
name = "ignore_off",
label = S("Ignore OFF command"),
tooltip = S("If 'yes' a received OFF command won't stop the sequencer"),
default = "no",
values = {0, 1}
},
{
type = "dropdown",
choices = "no,yes",
name = "reset",
label = S("Reset on reload"),
tooltip = S("Restart the sequencer when the map block is reloaded"),
default = "yes",
values = {0, 1}
},
{
type = "number",
name = "delay",
label = S("Delay on reload"),
tooltip = S("Delay in seconds for start of execution when map block is reloaded"),
default = "0",
},
}
local function cycle_time(pos)
local nvm = techage.get_nvm(pos)
if not nvm.cycletime then
nvm.cycletime = tonumber(M(pos):get_string("cycletime")) or 0.1
end
return nvm.cycletime
end
local function strsplit(text)
text = text:gsub("\r\n", "\n")
text = text:gsub("\r", "\n")
return string.split(text, "\n", true)
end
local function trim(s)
return (s:gsub("^%s*(.-)%s*$", "%1"))
end
local function command(s)
local num, cmd, pld = unpack(string.split(s, " ", false, 2))
if not num or not cmd then
return S("Invalid command!")
end
return {number = num, cmnd = cmd, payload = pld}
end
local function add_error(text, line_num)
local tbl = {}
for idx, line in ipairs(strsplit(text)) do
if idx == line_num and not string.find(line, '--<<== error') then
tbl[#tbl+1] = line.." --<<== error"
else
tbl[#tbl+1] = line
end
end
return table.concat(tbl, "\n")
end
local function exception(tRes, line, s)
if tRes then
tRes.line = line
tRes.error = s
end
end
local function compile(s, tRes)
local tCode = {}
local old_idx = 0
local start_idx
for i, line in ipairs(strsplit(s)) do
line = trim(line)
line = string.split(line, "--", true, 1)[1] or ""
if line ~= "" then
local idx, cmnd1, cmnd2 = unpack(string.split(line, " ", false, 2))
idx = tonumber(string.match(idx, "^%[(%d+)%]$"))
if not idx then
return exception(tRes, i, "Syntax error!")
end
if idx > 50000 then
return exception(tRes, i, "Order error!")
end
if idx <= old_idx then
return exception(tRes, i, "Order error!")
end
start_idx = start_idx or idx
if old_idx ~= 0 and tCode[old_idx] and not tCode[old_idx].next_idx then
tCode[old_idx].next_idx = idx
end
if cmnd1 == "send" then
local res = command(cmnd2)
if type(res) == "string" then
return exception(tRes, i, res)
end
tCode[idx] = res
elseif cmnd1 == "goto" then
tCode[idx] = {next_idx = tonumber(cmnd2) or 1}
elseif cmnd1 == "stop" then
tCode[idx] = false
elseif cmnd1 == nil or cmnd1 == "nop" then
tCode[idx] = {}
end
old_idx = idx
end
end
-- Returns:
-- {
-- start_idx = 1,
-- tCode = {
-- <idx> = {number = <number>, cmnd = <string>, payload = <data>, next_idx = <idx>},
-- ...
-- },
-- }
return {start_idx=start_idx, tCode=tCode}
end
local function check_syntax(meta)
local tRes = {}
local res = compile(meta:get_string("text"), tRes)
if not res then
meta:set_string("err_msg", tRes.error)
meta:set_string("text", add_error(meta:get_string("text"), tRes.line))
return false
else
meta:set_string("err_msg", "")
return true
end
end
local function formspec(nvm, meta)
local text = meta:get_string("text")
text = minetest.formspec_escape(text)
local bttn = nvm.running and ("stop;" .. S("Stop")) or ("start;" .. S("Start"))
local style = nvm.running and "style_type[textarea;font=mono;textcolor=#888888;border=false]" or
"style_type[textarea;font=mono;textcolor=#FFFFFF;border=false]"
local textarea = nvm.running and "textarea[0.3,0.2;10,8.3;;;"..text.."]" or
"textarea[0.3,0.2;10,8.3;text;;"..text.."]"
return "size[10,8]" ..
style ..
techage.wrench_image(9.3, -0.25) ..
"tabheader[0,0;tab;edit,help;1;;true]" ..
"label[0.1,-0.2;" .. S("Commands") .. ":]" ..
textarea ..
"background[0.1,0.3;9.8,7.0;techage_form_mask.png]" ..
"label[0.1,7.5;" .. meta:get_string("err_msg") .. "]" ..
"button_exit[3.4,7.5;2.2,1;cancel;" .. S("Cancel") .. "]" ..
"button[5.6,7.5;2.2,1;save;" .. S("Save") .. "]" ..
"button[7.8,7.5;2.2,1;" .. bttn .. "]"
end
local function formspec_help(meta)
local text = "" --minetest.formspec_escape("hepl")
return "size[10,8]"..
"style_type[textarea;font=mono;textcolor=#FFFFFF;border=false]"..
"tabheader[0,0;tab;edit,help;2;;true]"..
"textarea[0.3,0.3;10,9;;" .. S("Help") .. ":;"..minetest.formspec_escape(HELP).."]" ..
"background[0.1,0.3;9.8,8.0;techage_form_mask.png]"
end
local function restart_timer(pos, ticks)
local timer = minetest.get_node_timer(pos)
if timer:is_started() then
timer:stop()
end
timer:start(ticks * cycle_time(pos))
end
local function start_delayed(pos, sec)
local timer = minetest.get_node_timer(pos)
if timer:is_started() then
timer:stop()
end
local nvm = techage.get_nvm(pos)
nvm.running = true
timer:start(sec)
end
local function node_timer(pos, elapsed)
local nvm = techage.get_nvm(pos)
if nvm.running then
nvm.code = nvm.code or compile(M(pos):get_string("text"))
if nvm.code then
nvm.idx = nvm.idx or nvm.code.start_idx
local code = nvm.code.tCode[nvm.idx]
if code and code.cmnd then
local src = M(pos):get_string("node_number")
techage.counting_start(M(pos):get_string("owner"))
techage.send_single(src, code.number, code.cmnd, code.payload)
techage.counting_stop()
end
if code and code.next_idx then
local offs = code.next_idx - nvm.idx
minetest.after(0, restart_timer, pos, math.max(offs, 1))
nvm.idx = code.next_idx
else
nvm.running = false
local meta = M(pos)
meta:set_string("formspec", formspec(nvm, meta))
logic.infotext(meta, S("TA4 Sequencer"), S("stopped"))
end
end
end
return false
end
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local meta = M(pos)
local nvm = techage.get_nvm(pos)
nvm.running = nvm.running or false
if fields.stop then
nvm.running = false
minetest.get_node_timer(pos):stop()
logic.infotext(meta, S("TA4 Sequencer"), S("stopped"))
elseif not nvm.running then
if fields.tab == "2" then
meta:set_string("formspec", formspec_help(meta))
return
elseif fields.tab == "1" then
meta:set_string("formspec", formspec(nvm, meta))
return
end
if fields.save then
nvm.running = false
meta:set_string("text", fields.text or "")
nvm.code = nil
nvm.idx = nil
elseif fields.start then
if check_syntax(meta) then
nvm.running = true
meta:set_string("text", fields.text or "")
nvm.code = nil
nvm.idx = nil
nvm.cycletime = nil
restart_timer(pos, 1)
logic.infotext(meta, S("TA4 Sequencer"), S("running"))
end
end
end
meta:set_string("formspec", formspec(nvm, meta))
end
minetest.register_node("techage:ta4_sequencer", {
description = S("TA4 Sequencer"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta4.png^techage_frame_ta4_top.png",
"techage_filling_ta4.png^techage_frame_ta4_top.png",
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_sequencer.png",
},
after_place_node = function(pos, placer)
local meta = M(pos)
local nvm = techage.get_nvm(pos)
logic.after_place_node(pos, placer, "techage:ta4_sequencer", S("TA4 Sequencer"))
logic.infotext(meta, S("TA4 Sequencer"), S("stopped"))
nvm.running = false
meta:set_string("formspec", formspec(nvm, meta))
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 = node_timer,
ta4_formspec = WRENCH_MENU,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_stone_defaults(),
})
minetest.register_craft({
output = "techage:ta4_sequencer",
recipe = {
{"default:steel_ingot", "dye:blue", "default:steel_ingot"},
{"techage:ta4_ramchip", "default:mese_crystal", "techage:ta4_wlanchip"},
{"techage:aluminum", "group:wood", "techage:aluminum"},
},
})
local INFO = [[Commands: 'goto <num>', 'stop', 'on', 'off']]
techage.register_node({"techage:ta4_sequencer"}, {
on_recv_message = function(pos, src, topic, payload)
local nvm = techage.get_nvm(pos)
if (topic == "goto" or topic == "on") and not nvm.running then
nvm.running = true
nvm.idx = tonumber(payload or 1) or 1
restart_timer(pos, 1)
logic.infotext(M(pos), S("TA4 Sequencer"), S("running"))
elseif topic == "stop" or (topic == "off" and M(pos):get_int("ignore_off") == 0) then
nvm.running = false
minetest.get_node_timer(pos):stop()
logic.infotext(M(pos), S("TA4 Sequencer"), S("stopped"))
elseif topic == "info" then
return INFO
else
return "unsupported"
end
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
local nvm = techage.get_nvm(pos)
if topic == 13 then
if payload[1] ~= 0 and not nvm.running then
nvm.running = true
nvm.idx = tonumber(payload or 1) or 1
restart_timer(pos, 1)
logic.infotext(M(pos), S("TA4 Sequencer"), S("running"))
return 0
elseif payload[1] == 0 then
nvm.running = false
minetest.get_node_timer(pos):stop()
logic.infotext(M(pos), S("TA4 Sequencer"), S("stopped"))
return 0
end
end
return 2
end,
on_node_load = function(pos, node)
local nvm = techage.get_nvm(pos)
local meta = M(pos)
if not meta:contains("reset") then
meta:set_int("reset", 1)
end
if not meta:contains("delay") then
meta:set_int("delay", 1)
end
if nvm.running and meta:get_int("reset") == 1 then
nvm.code = nil
nvm.idx = nil
nvm.cycletime = nil
end
if nvm.running and meta:get_int("delay") > 0 then
restart_timer(pos, meta:get_int("delay") * 10)
else
restart_timer(pos, 1)
end
end,
})
@@ -0,0 +1,201 @@
--[[
TechAge
=======
Copyright (C) 2022-2023 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
Colored Signal Lamps (with unifieddyes support)
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local logic = techage.logic
local COLORED = minetest.get_modpath("unifieddyes") and minetest.global_exists("unifieddyes")
local LampsOff = {}
local LampsOn = {}
local function switch_on(pos, node, player, color)
if player and minetest.is_protected(pos, player:get_player_name()) then
return
end
color = tonumber(color) or node.param2
if LampsOff[node.name] then
node.name = LampsOff[node.name]
node.param2 = color
minetest.swap_node(pos, node)
elseif LampsOn[node.name] and color ~= node.param2 then
node.param2 = color
minetest.swap_node(pos, node)
end
end
local function switch_off(pos, node, player)
if player and minetest.is_protected(pos, player:get_player_name()) then
return
end
if LampsOn[node.name] then
node.name = LampsOn[node.name]
minetest.swap_node(pos, node)
end
end
local function register_signallamp(name, description, tiles_off, tiles_on, node_box)
LampsOff[name .. "_off"] = name .. "_on"
LampsOn[name .. "_on"] = name .. "_off"
minetest.register_node(name .. "_off", {
description = description,
tiles = tiles_off,
drawtype = node_box and "nodebox",
node_box = node_box,
after_place_node = function(pos, placer, itemstack, pointed_thing)
logic.after_place_node(pos, placer, name .. "_off", description)
logic.infotext(M(pos), description)
if COLORED then
unifieddyes.recolor_on_place(pos, placer, itemstack, pointed_thing)
else
local node = minetest.get_node(pos)
node.param2 = 35
minetest.swap_node(pos, node)
end
end,
on_rightclick = switch_on,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
techage.remove_node(pos, oldnode, oldmetadata)
if COLORED then
unifieddyes.after_dig_node(pos, oldnode, oldmetadata, digger)
end
end,
on_construct = COLORED and unifieddyes.on_construct or nil,
on_dig = COLORED and unifieddyes.on_dig or nil,
paramtype = "light",
paramtype2 = "color",
--palette = "techage_palette256.png",
palette = COLORED and "unifieddyes_palette_extended.png" or "techage_palette256.png",
place_param2 = 240,
sunlight_propagates = true,
sounds = default.node_sound_glass_defaults(),
groups = {choppy=2, cracky=1, ud_param2_colorable = 1},
is_ground_content = false,
drop = name .. "_off"
})
minetest.register_node(name .. "_on", {
description = description,
tiles = tiles_on,
drawtype = node_box and "nodebox",
node_box = node_box,
on_rightclick = switch_off,
paramtype = "light",
paramtype2 = "color",
palette = COLORED and "unifieddyes_palette_extended.png" or "techage_palette256.png",
groups = {choppy=2, cracky=1, not_in_creative_inventory=1, ud_param2_colorable = 1},
after_dig_node = function(pos, oldnode, oldmetadata, digger)
techage.remove_node(pos, oldnode, oldmetadata)
if COLORED then
unifieddyes.after_dig_node(pos, oldnode, oldmetadata, digger)
end
end,
on_dig = COLORED and unifieddyes.on_dig or nil,
light_source = 10,
is_ground_content = false,
drop = name .. "_off"
})
techage.register_node({name .. "_off", name .. "_on"}, {
on_recv_message = function(pos, src, topic, payload)
if topic == "on" then
local node = techage.get_node_lvm(pos)
switch_on(pos, node)
return true
elseif topic == "off" then
local node = techage.get_node_lvm(pos)
switch_off(pos, node)
return true
elseif topic == "color" then
local node = techage.get_node_lvm(pos)
switch_on(pos, node, nil, payload)
return true
else
return "unsupported"
end
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
if topic == 1 and payload[1] == 1 then
local node = techage.get_node_lvm(pos)
switch_on(pos, node)
return 0
elseif topic == 1 and payload[1] == 0 then
local node = techage.get_node_lvm(pos)
switch_off(pos, node)
return 0
elseif topic == 70 or topic == 22 then
local node = techage.get_node_lvm(pos)
switch_on(pos, node, nil, payload[1])
return 0
else
return 2
end
end,
})
end
register_signallamp("techage:color_lamp",
S("TechAge Color Lamp"),
{"techage_signal_lamp.png^[colorize:#000000:80"},
{"techage_signal_lamp.png"},
{
type = "fixed",
fixed = {
{-6/16, -6/16, -6/16, 6/16, 6/16, 6/16},
{-4/16, -10/16, -4/16, 4/16, -6/16, 4/16},
}
}
)
register_signallamp("techage:color_lamp2",
S("TechAge Color Lamp 2"),
{"techage_signallamp2.png^[colorize:#000000:80"},
{"techage_signallamp2.png"}
)
minetest.register_craft({
output = "techage:signal_lamp_off",
recipe = {
{"", "wool:white", ""},
{"", "default:torch", ""},
{"", "techage:vacuum_tube", ""},
},
})
minetest.register_craft({
output = "techage:signal_lamp2_off",
recipe = {
{"", "default:glass", ""},
{"", "default:torch", ""},
{"", "techage:vacuum_tube", ""},
},
})
minetest.register_alias("techage:signal_lamp_off", "techage:color_lamp_off")
minetest.register_alias("techage:signal_lamp2_off", "techage:color_lamp2_off")
minetest.register_alias("techage:signal_lamp_on", "techage:color_lamp_on")
minetest.register_alias("techage:signal_lamp2_on", "techage:color_lamp2_on")
@@ -0,0 +1,164 @@
--[[
TechAge
=======
Copyright (C) 2017-2022 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA4 Logic twofold signal lamp
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local OFF = 0
local GREEN = 1
local AMBER = 2
local RED = 3
local WRENCH_MENU = {
{
type = "ascii",
name = "label1",
label = S("Label") .. " 1",
tooltip = S("Label for the lamp"),
default = "1",
},
{
type = "ascii",
name = "label2",
label = S("Label") .. " 2",
tooltip = S("Label for the lamp"),
default = "2",
},
}
local function lamp_update(pos, objref)
local meta = M(pos)
pos = vector.round(pos)
local nvm = techage.get_nvm(pos)
nvm.lamp = nvm.lamp or {}
local tbl = {" ", " ", meta:get_string("label1"), " ", meta:get_string("label2")}
local text = "< " .. table.concat(tbl, "\n< ")
local texture = lcdlib.make_multiline_texture("default", text, 96, 96, 7, "top", "#000", 6)
if nvm.lamp[1] == RED then
texture = texture .. "^techage_smartline_signal_red2.png"
elseif nvm.lamp[1] == GREEN then
texture = texture .. "^techage_smartline_signal_green2.png"
elseif nvm.lamp[1] == AMBER then
texture = texture .. "^techage_smartline_signal_amber2.png"
end
if nvm.lamp[2] == RED then
texture = texture .. "^techage_smartline_signal_red3.png"
elseif nvm.lamp[2] == GREEN then
texture = texture .. "^techage_smartline_signal_green3.png"
elseif nvm.lamp[2] == AMBER then
texture = texture .. "^techage_smartline_signal_amber3.png"
end
objref:set_properties({ textures = {texture}, visual_size = {x=1, y=1} })
end
local lcd_box = {-8/16, -4/16, 7.75/16, 8/16, 4/16, 8/16}
minetest.register_node("techage:ta4_signallamp_2x", {
description = S("TA4 2x Signal Lamp"),
inventory_image = 'techage_smartline_signal_2x.png^techage_smartline_signal_green2.png^techage_smartline_signal_amber3.png',
tiles = {'techage_smartline_signal_2x.png'},
drawtype = "nodebox",
paramtype = "light",
use_texture_alpha = "clip",
sunlight_propagates = true,
paramtype2 = "facedir",
node_box = {
type = "fixed",
fixed = lcd_box,
},
light_source = 6,
display_entities = {
["techage:display_entity"] = { depth = 0.48,
on_display_update = lamp_update},
},
after_place_node = function(pos, placer)
local number = techage.add_node(pos, "techage:ta4_signallamp_2x")
local meta = minetest.get_meta(pos)
meta:set_string("node_number", number)
meta:set_string("owner", placer:get_player_name())
meta:set_string("infotext", S("TA4 2x Signal Lamp") .. " " .. number)
local nvm = techage.get_nvm(pos)
nvm.lamp = {}
lcdlib.update_entities(pos)
end,
after_dig_node = function(pos, oldnode, oldmetadata)
techage.remove_node(pos, oldnode, oldmetadata)
end,
ta_after_formspec = function(pos, fields, playername)
lcdlib.update_entities(pos)
end,
ta3_formspec = WRENCH_MENU,
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(),
})
techage.register_node({"techage:ta4_signallamp_2x"}, {
on_recv_message = function(pos, src, topic, payload)
local nvm = techage.get_nvm(pos)
nvm.lamp = nvm.lamp or {}
if topic == "green" then
local num = math.min(tonumber(payload) or 0, 2)
nvm.lamp[num] = GREEN
lcdlib.update_entities(pos)
elseif topic == "amber" then
local num = math.min(tonumber(payload) or 0, 2)
nvm.lamp[num] = AMBER
lcdlib.update_entities(pos)
elseif topic == "red" then
local num = math.min(tonumber(payload) or 0, 2)
nvm.lamp[num] = RED
lcdlib.update_entities(pos)
elseif topic == "off" then
local num = math.min(tonumber(payload) or 0, 2)
nvm.lamp[num] = OFF
lcdlib.update_entities(pos)
end
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
local nvm = techage.get_nvm(pos)
nvm.lamp = nvm.lamp or {}
if topic == 3 then -- Signal Lamp
local num = math.min(payload[1] or 1, 2)
local color = math.min(payload[2] or 0, 3)
nvm.lamp[num] = color
lcdlib.update_entities(pos)
return 0
else
return 2
end
end,
})
minetest.register_craft({
output = "techage:ta4_signallamp_2x",
recipe = {
{"", "techage:aluminum", "dye:blue"},
{"", "default:glass", "techage:ta4_wlanchip"},
{"", "techage:ta4_leds", "techage:ta4_leds"},
},
})
@@ -0,0 +1,195 @@
--[[
TechAge
=======
Copyright (C) 2017-2022 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA4 Logic fourfold signal lamp
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local OFF = 0
local GREEN = 1
local AMBER = 2
local RED = 3
local WRENCH_MENU = {
{
type = "ascii",
name = "label1",
label = S("Label") .. " 1",
tooltip = S("Label for the lamp"),
default = "1",
},
{
type = "ascii",
name = "label2",
label = S("Label") .. " 2",
tooltip = S("Label for the lamp"),
default = "2",
},
{
type = "ascii",
name = "label3",
label = S("Label") .. " 3",
tooltip = S("Label for the lamp"),
default = "3",
},
{
type = "ascii",
name = "label4",
label = S("Label") .. " 4",
tooltip = S("Label for the lamp"),
default = "4",
},
}
local function lamp_update(pos, objref)
local meta = M(pos)
pos = vector.round(pos)
local nvm = techage.get_nvm(pos)
nvm.lamp = nvm.lamp or {}
local tbl = {meta:get_string("label1"), " ", meta:get_string("label2"), " ", meta:get_string("label3"), " ", meta:get_string("label4")}
local text = "< " .. table.concat(tbl, "\n< ")
local texture = lcdlib.make_multiline_texture("default", text, 96, 96, 7, "top", "#000", 6)
if nvm.lamp[1] == RED then
texture = texture .. "^techage_smartline_signal_red1.png"
elseif nvm.lamp[1] == GREEN then
texture = texture .. "^techage_smartline_signal_green1.png"
elseif nvm.lamp[1] == AMBER then
texture = texture .. "^techage_smartline_signal_amber1.png"
end
if nvm.lamp[2] == RED then
texture = texture .. "^techage_smartline_signal_red2.png"
elseif nvm.lamp[2] == GREEN then
texture = texture .. "^techage_smartline_signal_green2.png"
elseif nvm.lamp[2] == AMBER then
texture = texture .. "^techage_smartline_signal_amber2.png"
end
if nvm.lamp[3] == RED then
texture = texture .. "^techage_smartline_signal_red3.png"
elseif nvm.lamp[3] == GREEN then
texture = texture .. "^techage_smartline_signal_green3.png"
elseif nvm.lamp[3] == AMBER then
texture = texture .. "^techage_smartline_signal_amber3.png"
end
if nvm.lamp[4] == RED then
texture = texture .. "^techage_smartline_signal_red4.png"
elseif nvm.lamp[4] == GREEN then
texture = texture .. "^techage_smartline_signal_green4.png"
elseif nvm.lamp[4] == AMBER then
texture = texture .. "^techage_smartline_signal_amber4.png"
end
objref:set_properties({ textures = {texture}, visual_size = {x=1, y=1} })
end
local lcd_box = {-8/16, -8/16, 7.75/16, 8/16, 8/16, 8/16}
minetest.register_node("techage:ta4_signallamp_4x", {
description = S("TA4 4x Signal Lamp"),
inventory_image = 'techage_smartline_signal_4x.png^techage_smartline_signal_off1.png^techage_smartline_signal_green2.png^techage_smartline_signal_amber3.png^techage_smartline_signal_red4.png',
tiles = {'techage_smartline_signal_4x.png'},
drawtype = "nodebox",
paramtype = "light",
use_texture_alpha = "clip",
sunlight_propagates = true,
paramtype2 = "facedir",
node_box = {
type = "fixed",
fixed = lcd_box,
},
light_source = 6,
display_entities = {
["techage:display_entity"] = { depth = 0.48,
on_display_update = lamp_update},
},
after_place_node = function(pos, placer)
local number = techage.add_node(pos, "techage:ta4_signallamp_4x")
local meta = minetest.get_meta(pos)
meta:set_string("node_number", number)
meta:set_string("owner", placer:get_player_name())
meta:set_string("infotext", S("TA4 4x Signal Lamp") .. " " .. number)
local nvm = techage.get_nvm(pos)
nvm.text = {"1", "", "2", "", "3", "", "4"}
nvm.lamp = {}
lcdlib.update_entities(pos)
end,
after_dig_node = function(pos, oldnode, oldmetadata)
techage.remove_node(pos, oldnode, oldmetadata)
end,
ta_after_formspec = function(pos, fields, playername)
lcdlib.update_entities(pos)
end,
ta3_formspec = WRENCH_MENU,
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(),
})
techage.register_node({"techage:ta4_signallamp_4x"}, {
on_recv_message = function(pos, src, topic, payload)
local nvm = techage.get_nvm(pos)
nvm.lamp = nvm.lamp or {}
if topic == "green" then
local num = math.min(tonumber(payload) or 0, 4)
nvm.lamp[num] = GREEN
lcdlib.update_entities(pos)
elseif topic == "amber" then
local num = math.min(tonumber(payload) or 0, 4)
nvm.lamp[num] = AMBER
lcdlib.update_entities(pos)
elseif topic == "red" then
local num = math.min(tonumber(payload) or 0, 4)
nvm.lamp[num] = RED
lcdlib.update_entities(pos)
elseif topic == "off" then
local num = math.min(tonumber(payload) or 0, 4)
nvm.lamp[num] = OFF
lcdlib.update_entities(pos)
end
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
local nvm = techage.get_nvm(pos)
nvm.lamp = nvm.lamp or {}
if topic == 3 then -- Signal Lamp
local num = math.min(payload[1] or 1, 4)
local color = math.min(payload[2] or 0, 3)
nvm.lamp[num] = color
lcdlib.update_entities(pos)
return 0
else
return 2
end
end,
})
minetest.register_craft({
output = "techage:ta4_signallamp_4x",
recipe = {
{"", "techage:ta4_signallamp_2x", ""},
{"", "techage:ta4_signallamp_2x", ""},
{"", "", ""},
},
})
@@ -0,0 +1,493 @@
--[[
Terminal
========
Copyright (C) 2018-2024 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
terminal.lua:
]]--
local M = minetest.get_meta
local S = techage.S
local HELP_TA3 = [[ #### TA3 Terminal ####
Send commands to machines and output the results.
Local commands:
- Clear screen with 'clear'
- Output this message with 'help'
- Switch to public use of buttons with 'pub'
- Switch to private use of buttons with 'priv'
- Output techage version with 'vers'
- Program a user button with
'set <button-num> <button-text> <command>'
Example: 'set 1 ON cmd 1234 on'
- send a command with 'cmd <num> <cmnd>'
Example: 'cmd 1234 on']]
local HELP_TA4 = [[ #### TA4 Terminal ####
Send commands to machines and output the results.
Local commands:
- Clear screen with 'clear'
- Output this message with 'help'
- Switch to public use of buttons with 'pub'
- Switch to private use of buttons with 'priv'
- Output techage version with 'vers'
- Program a user button with
'set <button-num> <button-text> <command>'
Example: 'set 1 ON cmd 1234 on'
- send a command with 'cmd <num> <cmnd>'
Example: 'cmd 1234 on'
- Connect to a machine with 'connect <num>'
If connected, compact commands like 'status'
are possible.]]
local SYNTAX_ERR = S("Syntax error, try help")
local WRENCH_MENU = {
[0] = {"techage:terminal2"}, --valid_nodes
{
type = "dropdown",
choices = "all players,me",
name = "public",
label = S("Access allowed for"),
tooltip = S("Friends are players for whom this area is not protected"),
default = "1",
values = {1,0}
},
{
type = "dropdown",
choices = "terminal,basic",
name = "opmode",
label = S("Operational mode"),
tooltip = S("Switch between TA3 terminal and BASIC computer"),
default = "terminal",
},
}
local function get_string(meta, num, default)
local s = meta:get_string("bttn_text"..num)
if not s or s == "" then
return default
end
return s
end
local function formspec2(pos, meta)
local output = meta:get_string("output")
local command = meta:get_string("command")
output = minetest.formspec_escape(output)
output = output:gsub("\n", ",")
local bttn_text1 = get_string(meta, 1, "User1")
local bttn_text2 = get_string(meta, 2, "User2")
local bttn_text3 = get_string(meta, 3, "User3")
local bttn_text4 = get_string(meta, 4, "User4")
local bttn_text5 = get_string(meta, 5, "User5")
local bttn_text6 = get_string(meta, 6, "User6")
local bttn_text7 = get_string(meta, 7, "User7")
local bttn_text8 = get_string(meta, 8, "User8")
local bttn_text9 = get_string(meta, 9, "User9")
local wrench_image = ""
if minetest.get_node(pos).name == "techage:terminal2" then
wrench_image = techage.wrench_image(9.6, -0.2)
end
return "size[10,8.5]"..
--"style_type[table,field;font=mono]"..
wrench_image ..
"button[0,-0.2;3.3,1;bttn1;"..bttn_text1.."]button[3.3,-0.2;3.3,1;bttn2;"..bttn_text2.."]button[6.6,-0.2;3.2,1;bttn3;"..bttn_text3.."]"..
"button[0,0.6;3.3,1;bttn4;"..bttn_text4.."]button[3.3,0.6;3.3,1;bttn5;"..bttn_text5.."]button[6.6,0.6;3.4,1;bttn6;"..bttn_text6.."]"..
"button[0,1.4;3.3,1;bttn7;"..bttn_text7.."]button[3.3,1.4;3.3,1;bttn8;"..bttn_text8.."]button[6.6,1.4;3.4,1;bttn9;"..bttn_text9.."]"..
"table[0,2.3;9.8,5.6;output;"..output..";200]"..
"field[0.4,8.2;7.6,1;cmnd;;"..command.."]" ..
"field_close_on_enter[cmnd;false]"..
"button[7.9,7.9;2,1;ok;"..S("Enter").."]"
end
local function output(pos, text)
local meta = minetest.get_meta(pos)
text = meta:get_string("output") .. "\n" .. (text or "")
text = text:sub(-1000,-1)
meta:set_string("output", text)
meta:set_string("formspec", formspec2(pos, meta))
end
local function append(pos, text)
local meta = minetest.get_meta(pos)
text = meta:get_string("output") .. (text or "")
meta:set_string("output", text)
meta:set_string("formspec", formspec2(pos, meta))
end
local function get_line_text(pos, num)
local meta = minetest.get_meta(pos)
local text = meta:get_string("output") or ""
local lines = string.split(text, "\n", true)
local line = lines[num] or ""
return line:gsub("^[%s$]*(.-)%s*$", "%1")
end
local function server_debug(pos, command, player)
local cmnd, payload = command:match('^pipe%s+([%w_]+)%s*(.*)$')
if cmnd then
if not minetest.check_player_privs(player, "server") then
output(pos, "server privs missing")
return
end
local resp = techage.transfer(
pos,
"B", -- outdir
cmnd, -- topic
payload, -- payload
techage.LiquidPipe, -- network
nil) -- valid nodes
output(pos, dump(resp))
return true
end
cmnd, payload = command:match('^axle%s+([%w_]+)%s*(.*)$')
if cmnd then
if not minetest.check_player_privs(player, "server") then
output(pos, "server privs missing")
return
end
local resp = techage.transfer(
pos,
"B", -- outdir
cmnd, -- topic
payload, -- payload
techage.TA1Axle, -- network
nil) -- valid nodes
output(pos, dump(resp))
return true
end
cmnd, payload = command:match('^vtube%s+([%w_]+)%s*(.*)$')
if cmnd then
if not minetest.check_player_privs(player, "server") then
output(pos, "server privs missing")
return
end
local resp = techage.transfer(
pos,
"B", -- outdir
cmnd, -- topic
payload, -- payload
techage.VTube, -- network
nil) -- valid nodes
output(pos, dump(resp))
return true
end
end
local function command(pos, command, player, is_ta4)
local meta = minetest.get_meta(pos)
local owner = meta:get_string("owner") or ""
command = command:sub(1,80)
command = string.trim(command)
local cmnd, data = command:match('^(%w+)%s*(.*)$')
if cmnd == "clear" then
meta:set_string("output", "")
meta:set_string("formspec", formspec2(pos, meta))
elseif cmnd == "" then
output(pos, "$")
elseif cmnd == "help" then
if is_ta4 then
output(pos, HELP_TA4)
else
output(pos, HELP_TA3)
end
elseif cmnd == "pub" then
meta:set_int("public", 1)
output(pos, "$ "..command)
output(pos, "Switched to public buttons!")
elseif cmnd == "priv" then
meta:set_int("public", 0)
output(pos, "$ "..command)
output(pos, "Switched to private buttons!")
elseif cmnd == "vers" then
output(pos, "$ "..command)
output(pos, "Techage version = " .. techage.version)
elseif cmnd == "connect" and data then
output(pos, "$ "..command)
if techage.not_protected(data, owner) then
local own_num = meta:get_string("node_number")
local resp = techage.send_single(own_num, data, cmnd)
if resp then
meta:set_string("connected_to", data)
output(pos, "Connected.")
else
meta:set_string("connected_to", "")
output(pos, "Not connected!")
end
else
output(pos, "Protection error!")
end
else
output(pos, "$ "..command)
local own_num = meta:get_string("node_number")
local connected_to = meta:contains("connected_to") and meta:get_string("connected_to")
local bttn_num, label, num, cmnd, payload
num, cmnd, payload = command:match('^cmd%s+([0-9]+)%s+(%w+)%s*(.*)$')
if num and cmnd then
if techage.not_protected(num, owner) then
local resp = techage.send_single(own_num, num, cmnd, payload)
if type(resp) == "string" then
output(pos, resp)
else
output(pos, dump(resp))
end
end
return
end
num, cmnd = command:match('^turn%s+([0-9]+)%s+([onf]+)$')
if num and (cmnd == "on" or cmnd == "off") then
if techage.not_protected(num, owner) then
local resp = techage.send_single(own_num, num, cmnd)
output(pos, dump(resp))
end
return
end
bttn_num, label, cmnd = command:match('^set%s+([1-9])%s+([%w_]+)%s+(.+)$')
if bttn_num and label and cmnd then
meta:set_string("bttn_text"..bttn_num, label)
meta:set_string("bttn_cmnd"..bttn_num, cmnd)
meta:set_string("formspec", formspec2(pos, meta))
return
end
if server_debug(pos, command, player) then
return
end
if connected_to then
local cmnd, payload = command:match('^(%w+)%s*(.*)$')
if cmnd then
local resp = techage.send_single(own_num, connected_to, cmnd, payload)
if resp ~= true then
if type(resp) == "string" then
output(pos, resp)
else
output(pos, dump(resp))
end
end
return
end
end
if command ~= "" then
output(pos, SYNTAX_ERR)
end
end
end
local function send_cmnd(pos, meta, num)
local cmnd = meta:get_string("bttn_cmnd"..num)
local owner = meta:get_string("owner") or ""
command(pos, cmnd, owner)
end
local function register_terminal(name, description, tiles, node_box, selection_box)
minetest.register_node("techage:"..name, {
description = description,
tiles = tiles,
drawtype = "nodebox",
node_box = node_box,
selection_box = selection_box,
after_place_node = function(pos, placer)
local number = techage.add_node(pos, minetest.get_node(pos).name)
local meta = minetest.get_meta(pos)
meta:set_string("node_number", number)
meta:set_string("command", S("commands like: help"))
meta:set_string("formspec", formspec2(pos, meta))
if placer then
meta:set_string("owner", placer:get_player_name())
end
meta:set_string("infotext", description .. " " .. number)
end,
on_receive_fields = function(pos, formname, fields, player)
local meta = minetest.get_meta(pos)
local public = meta:get_int("public") ~= 0
local protected = minetest.is_protected(pos, player:get_player_name())
if not protected then
local evt = minetest.explode_table_event(fields.output)
if evt.type == "DCL" then
local s = get_line_text(pos, evt.row)
meta:set_string("command", s)
meta:set_string("formspec", formspec2(pos, meta))
return
elseif (fields.ok or fields.key_enter_field) and fields.cmnd then
local is_ta4 = string.find(description, "TA4")
command(pos, fields.cmnd, player:get_player_name(), is_ta4)
techage.historybuffer_add(pos, fields.cmnd)
meta:set_string("command", "")
meta:set_string("formspec", formspec2(pos, meta))
return
elseif fields.key_up then
meta:set_string("command", techage.historybuffer_priv(pos))
meta:set_string("formspec", formspec2(pos, meta))
return
elseif fields.key_down then
meta:set_string("command", techage.historybuffer_next(pos))
meta:set_string("formspec", formspec2(pos, meta))
return
end
end
if public or not protected then
if fields.bttn1 then send_cmnd(pos, meta, 1)
elseif fields.bttn2 then send_cmnd(pos, meta, 2)
elseif fields.bttn3 then send_cmnd(pos, meta, 3)
elseif fields.bttn4 then send_cmnd(pos, meta, 4)
elseif fields.bttn5 then send_cmnd(pos, meta, 5)
elseif fields.bttn6 then send_cmnd(pos, meta, 6)
elseif fields.bttn7 then send_cmnd(pos, meta, 7)
elseif fields.bttn8 then send_cmnd(pos, meta, 8)
elseif fields.bttn9 then send_cmnd(pos, meta, 9)
end
end
end,
ta_after_formspec = function(pos, fields, playername)
if fields.save and minetest.get_node(pos).name == "techage:terminal2" then
if M(pos):get_string("opmode") == "basic" then
if minetest.global_exists("nanobasic") then
local node = techage.get_node_lvm(pos)
node.name = "techage:basic_terminal"
minetest.swap_node(pos, node)
local ndef = minetest.registered_nodes["techage:basic_terminal"]
ndef.after_place_node(pos)
else
output(pos, "\nBASIC is not supported. Talk to your server staff!")
end
end
end
end,
after_dig_node = function(pos, oldnode, oldmetadata)
techage.remove_node(pos, oldnode, oldmetadata)
end,
ta_preserve_nodedata = techage.preserve_nodedata,
ta_restore_nodedata = techage.restore_nodedata,
ta3_formspec = WRENCH_MENU,
paramtype = "light",
use_texture_alpha = techage.CLIP,
sunlight_propagates = true,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
})
end
register_terminal("terminal2", S("TA3 Terminal"), {
-- up, down, right, left, back, front
'techage_terminal2_top.png',
'techage_terminal2_side.png',
'techage_terminal2_side.png^[transformFX',
'techage_terminal2_side.png',
'techage_terminal2_back.png',
"techage_terminal2_front.png",
},
{
type = "fixed",
fixed = {
{-12/32, -16/32, -16/32, 12/32, -14/32, 16/32},
{-12/32, -14/32, -3/32, 12/32, 6/32, 16/32},
{-10/32, -12/32, 14/32, 10/32, 4/32, 18/32},
{-12/32, 4/32, -4/32, 12/32, 6/32, 16/32},
{-12/32, -16/32, -4/32, -10/32, 6/32, 16/32},
{ 10/32, -16/32, -4/32, 12/32, 6/32, 16/32},
{-12/32, -14/32, -4/32, 12/32, -12/32, 16/32},
},
},
{
type = "fixed",
fixed = {
{-12/32, -16/32, -4/32, 12/32, 6/32, 16/32},
},
}
)
register_terminal("terminal3", S("TA4 Terminal"), {
-- up, down, right, left, back, front
'techage_terminal1_top.png',
'techage_terminal1_bottom.png',
'techage_terminal1_side.png',
'techage_terminal1_side.png',
'techage_terminal1_bottom.png',
"techage_terminal1_front.png",
},
{
type = "fixed",
fixed = {
{-12/32, -16/32, -8/32, 12/32, -14/32, 12/32},
{-12/32, -14/32, 12/32, 12/32, 6/32, 14/32},
},
},
{
type = "fixed",
fixed = {
{-12/32, -16/32, -8/32, 12/32, -14/32, 12/32},
{-12/32, -14/32, 12/32, 12/32, 6/32, 14/32},
},
}
)
minetest.register_craft({
output = "techage:terminal2",
recipe = {
{"", "", ""},
{"techage:basalt_glass_thin", "techage:vacuum_tube", "default:copper_ingot"},
{"dye:grey", "default:steel_ingot", "techage:usmium_nuggets"},
},
})
minetest.register_craft({
output = "techage:terminal3",
recipe = {
{"techage:basalt_glass_thin", "", ""},
{"techage:ta4_leds", "", ""},
{"techage:aluminum", "techage:ta4_wlanchip", "techage:ta4_ramchip"},
},
})
techage.register_node({"techage:terminal2"}, {
on_recv_message = function(pos, src, topic, payload)
output(pos, "src="..src..", cmd="..dump(topic)..", data="..dump(payload))
return true
end,
on_node_load = function(pos)
local meta = M(pos)
local number = meta:get_string("number") or ""
if number ~= "" then
meta:set_string("node_number", number)
meta:set_string("number", "")
end
end,
})
techage.register_node({"techage:terminal3"}, {
on_recv_message = function(pos, src, topic, payload)
if topic == "text" then
output(pos, payload)
elseif topic == "append" then
append(pos, payload)
else
output(pos, "src="..src..", cmd="..dump(topic)..", data="..dump(payload))
end
return true
end,
})
@@ -0,0 +1,209 @@
--[[
TechAge
=======
Copyright (C) 2017-2019 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA3 Sequencer
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local logic = techage.logic
local CYCLE_TIME = 8
local tTime = {
["00:00"] = 1, ["02:00"] = 2, ["04:00"] = 3,
["06:00"] = 4, ["08:00"] = 5, ["10:00"] = 6,
["12:00"] = 7, ["14:00"] = 8, ["16:00"] = 9,
["18:00"] =10, ["20:00"] =11, ["22:00"] =12,
}
local sTime = "00:00,02:00,04:00,06:00,08:00,10:00,12:00,14:00,16:00,18:00,20:00,22:00"
local tAction = {
[""] = 1,
["on"] = 2,
["off"] = 3,
}
local sAction = ",on,off"
local function deserialize(meta, name)
local s = meta:get_string(name) or ""
if s ~= "" then
return minetest.deserialize(s)
end
end
local function formspec(events, numbers, actions)
return "size[8,8]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"label[0,0;Time]label[2.3,0;Number(s)]label[4.5,0;Command]"..
"dropdown[0,1;2,1;e1;"..sTime..";"..events[1].."]"..
"field[2.3,1.2;2,1;n1;;"..numbers[1].."]" ..
"dropdown[4.5,1;3,1;a1;"..sAction..";"..tAction[actions[1]].."]"..
"dropdown[0,2;2,1;e2;"..sTime..";"..events[2].."]"..
"field[2.3,2.2;2,1;n2;;"..numbers[2].."]" ..
"dropdown[4.5,2;3,1;a2;"..sAction..";"..tAction[actions[2]].."]"..
"dropdown[0,3;2,1;e3;"..sTime..";"..events[3].."]"..
"field[2.3,3.2;2,1;n3;;"..numbers[3].."]" ..
"dropdown[4.5,3;3,1;a3;"..sAction..";"..tAction[actions[3]].."]"..
"dropdown[0,4;2,1;e4;"..sTime..";"..events[4].."]"..
"field[2.3,4.2;2,1;n4;;"..numbers[4].."]" ..
"dropdown[4.5,4;3,1;a4;"..sAction..";"..tAction[actions[4]].."]"..
"dropdown[0,5;2,1;e5;"..sTime..";"..events[5].."]"..
"field[2.3,5.2;2,1;n5;;"..numbers[5].."]" ..
"dropdown[4.5,5;3,1;a5;"..sAction..";"..tAction[actions[5]].."]"..
"dropdown[0,6;2,1;e6;"..sTime..";"..events[6].."]"..
"field[2.3,6.2;2,1;n6;;"..numbers[6].."]" ..
"dropdown[4.5,6;3,1;a6;"..sAction..";"..tAction[actions[6]].."]"..
"button_exit[3,7;2,1;exit;close]"
end
local function check_rules(pos,elapsed)
local nvm = techage.get_nvm(pos)
nvm.done = nvm.done or {false,false,false,false,false,false}
local hour = math.floor(minetest.get_timeofday() * 24)
local meta = minetest.get_meta(pos)
local events = deserialize(meta, "events")
local numbers = deserialize(meta, "numbers")
local actions = deserialize(meta, "actions")
local number = meta:get_string("node_number")
if events and numbers and actions then
-- check all rules
for idx,act in ipairs(actions) do
if act ~= "" and numbers[idx] ~= "" then
local hr = (events[idx] - 1) * 2
if ((hour - hr) % 24) <= 4 then -- last 4 hours?
if nvm.done[idx] == false then -- not already executed?
techage.send_multi(number, numbers[idx], act)
nvm.done[idx] = true
end
else
nvm.done[idx] = false
end
end
end
-- prepare for the next day
if hour == 23 then
nvm.done = {false,false,false,false,false,false}
end
return true
end
return false
end
minetest.register_node("techage:ta3_timer", {
description = S("TA3 Timer"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3_top.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_timer.png",
},
after_place_node = function(pos, placer)
local meta = M(pos)
local nvm = techage.get_nvm(pos)
logic.after_place_node(pos, placer, "techage:ta3_timer", S("TA3 Timer"))
logic.infotext(meta, S("TA3 Timer"))
local events = {1,1,1,1,1,1}
local numbers = {"0000","","","","",""}
local actions = {"","","","","",""}
nvm.done = {false,false,false,false,false,false}
meta:set_string("events", minetest.serialize(events))
meta:set_string("numbers", minetest.serialize(numbers))
meta:set_string("actions", minetest.serialize(actions))
meta:set_string("formspec", formspec(events, numbers, actions))
minetest.get_node_timer(pos):start(CYCLE_TIME)
end,
on_receive_fields = function(pos, formname, fields, player)
local meta = minetest.get_meta(pos)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local events = minetest.deserialize(meta:get_string("events"))
for idx, evt in ipairs({fields.e1, fields.e2, fields.e3, fields.e4, fields.e5, fields.e6}) do
if evt ~= nil then
events[idx] = tTime[evt]
end
end
meta:set_string("events", minetest.serialize(events))
local numbers = minetest.deserialize(meta:get_string("numbers"))
for idx, num in ipairs({fields.n1, fields.n2, fields.n3, fields.n4, fields.n5, fields.n6}) do
if num ~= nil and techage.check_numbers(num, player:get_player_name()) then
numbers[idx] = num
end
end
meta:set_string("numbers", minetest.serialize(numbers))
local actions = minetest.deserialize(meta:get_string("actions"))
for idx, act in ipairs({fields.a1, fields.a2, fields.a3, fields.a4, fields.a5, fields.a6}) do
if act ~= nil then
actions[idx] = act
end
end
meta:set_string("actions", minetest.serialize(actions))
meta:set_string("formspec", formspec(events, numbers, actions))
local nvm = techage.get_nvm(pos)
nvm.done = {false,false,false,false,false,false}
end,
on_timer = check_rules,
after_dig_node = function(pos, oldnode, oldmetadata)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end,
paramtype = "light",
paramtype2 = "facedir",
sunlight_propagates = true,
sounds = default.node_sound_stone_defaults(),
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
})
minetest.register_craft({
output = "techage:ta3_timer",
recipe = {
{"group:wood", "group:wood", ""},
{"default:gold_ingot", "techage:vacuum_tube", ""},
{"group:wood", "group:wood", ""},
},
})
techage.register_node({"techage:ta3_timer"}, {
on_node_load = function(pos)
minetest.get_node_timer(pos):start(CYCLE_TIME)
-- check rules for just loaded areas
local nvm = techage.get_nvm(pos)
nvm.done = {false,false,false,false,false,false}
check_rules(pos,0)
end,
})
@@ -0,0 +1,540 @@
--[[
TechAge
=======
Copyright (C) 2019-2025 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
Traffic Lights
]]--
local S = techage.S
local M = minetest.get_meta
local P2S = minetest.pos_to_string
local TITLE = S("TA4 Traffic Light")
local
ConvertTo = {
off = {
["techage:ta4_trafficlight1_red"] = "techage:ta4_trafficlight1",
["techage:ta4_trafficlight1_amber"] = "techage:ta4_trafficlight1",
["techage:ta4_trafficlight1_green"] = "techage:ta4_trafficlight1",
["techage:ta4_trafficlight1B_red"] = "techage:ta4_trafficlight1B",
["techage:ta4_trafficlight1B_amber"] = "techage:ta4_trafficlight1B",
["techage:ta4_trafficlight1B_green"] = "techage:ta4_trafficlight1B",
["techage:ta4_trafficlight2_red"] = "techage:ta4_trafficlight2",
["techage:ta4_trafficlight2_amber"] = "techage:ta4_trafficlight2",
["techage:ta4_trafficlight2_green"] = "techage:ta4_trafficlight2",
["techage:ta4_trafficlight2B_red"] = "techage:ta4_trafficlight2B",
["techage:ta4_trafficlight2B_amber"] = "techage:ta4_trafficlight2B",
["techage:ta4_trafficlight2B_green"] = "techage:ta4_trafficlight2B",
},
green = {
["techage:ta4_trafficlight1_red"] = "techage:ta4_trafficlight1_green",
["techage:ta4_trafficlight1_amber"] = "techage:ta4_trafficlight1_green",
["techage:ta4_trafficlight1"] = "techage:ta4_trafficlight1_green",
["techage:ta4_trafficlight1B_red"] = "techage:ta4_trafficlight1B_green",
["techage:ta4_trafficlight1B_amber"] = "techage:ta4_trafficlight1B_green",
["techage:ta4_trafficlight1B"] = "techage:ta4_trafficlight1B_green",
["techage:ta4_trafficlight2_red"] = "techage:ta4_trafficlight2_green",
["techage:ta4_trafficlight2_amber"] = "techage:ta4_trafficlight2_green",
["techage:ta4_trafficlight2"] = "techage:ta4_trafficlight2_green",
["techage:ta4_trafficlight2B_red"] = "techage:ta4_trafficlight2B_green",
["techage:ta4_trafficlight2B_amber"] = "techage:ta4_trafficlight2B_green",
["techage:ta4_trafficlight2B"] = "techage:ta4_trafficlight2B_green",
},
amber = {
["techage:ta4_trafficlight1_red"] = "techage:ta4_trafficlight1_amber",
["techage:ta4_trafficlight1_green"] = "techage:ta4_trafficlight1_amber",
["techage:ta4_trafficlight1"] = "techage:ta4_trafficlight1_amber",
["techage:ta4_trafficlight1B_red"] = "techage:ta4_trafficlight1B_amber",
["techage:ta4_trafficlight1B_green"] = "techage:ta4_trafficlight1B_amber",
["techage:ta4_trafficlight1B"] = "techage:ta4_trafficlight1B_amber",
["techage:ta4_trafficlight2_red"] = "techage:ta4_trafficlight2_amber",
["techage:ta4_trafficlight2_green"] = "techage:ta4_trafficlight2_amber",
["techage:ta4_trafficlight2"] = "techage:ta4_trafficlight2_amber",
["techage:ta4_trafficlight2B_red"] = "techage:ta4_trafficlight2B_amber",
["techage:ta4_trafficlight2B_green"] = "techage:ta4_trafficlight2B_amber",
["techage:ta4_trafficlight2B"] = "techage:ta4_trafficlight2B_amber",
},
red = {
["techage:ta4_trafficlight1_amber"] = "techage:ta4_trafficlight1_red",
["techage:ta4_trafficlight1_green"] = "techage:ta4_trafficlight1_red",
["techage:ta4_trafficlight1"] = "techage:ta4_trafficlight1_red",
["techage:ta4_trafficlight1B_amber"] = "techage:ta4_trafficlight1B_red",
["techage:ta4_trafficlight1B_green"] = "techage:ta4_trafficlight1B_red",
["techage:ta4_trafficlight1B"] = "techage:ta4_trafficlight1B_red",
["techage:ta4_trafficlight2_amber"] = "techage:ta4_trafficlight2_red",
["techage:ta4_trafficlight2_green"] = "techage:ta4_trafficlight2_red",
["techage:ta4_trafficlight2"] = "techage:ta4_trafficlight2_red",
["techage:ta4_trafficlight2B_amber"] = "techage:ta4_trafficlight2B_red",
["techage:ta4_trafficlight2B_green"] = "techage:ta4_trafficlight2B_red",
["techage:ta4_trafficlight2B"] = "techage:ta4_trafficlight2B_red",
},
}
local SelectionBox = {
type = "fixed",
fixed = {-5/32, -15/32, 6/32, 5/32, 15/32, 24/32},
}
local function is_pole(pos)
local node = minetest.get_node(pos)
local dir = tubelib2.side_to_dir("B", node.param2)
local pos2 = tubelib2.get_pos(pos, dir)
local name = minetest.get_node(pos2).name
return name == "techage:trafficlight_connector" or name == "techage:trafficlight_pole"
end
local function switch_on(pos, color)
local node = techage.get_node_lvm(pos)
if ConvertTo[color][node.name] then
node.name = ConvertTo[color][node.name]
M(pos):set_string("state", color)
minetest.swap_node(pos, node)
end
end
local function switch_off(pos)
local node = techage.get_node_lvm(pos)
if ConvertTo["off"][node.name] then
node.name = ConvertTo["off"][node.name]
M(pos):set_string("state", "off")
minetest.swap_node(pos, node)
end
end
local function on_rightclick(pos, node, clicker)
if not minetest.is_protected(pos, clicker:get_player_name()) then
local state = M(pos):get_string("state")
if state == "off" then
switch_on(pos, "green")
else
switch_off(pos)
end
end
end
local function after_dig_node(pos, oldnode, oldmetadata)
techage.remove_node(pos, oldnode, oldmetadata)
end
minetest.register_node("techage:ta4_trafficlight1", {
description = TITLE,
tiles = {"techage_trafficlight1.png"},
drawtype = "mesh",
mesh = "techage_traffic_light.obj",
selection_box = SelectionBox,
after_place_node = function(pos, placer)
local number
if is_pole(pos) then
local node = minetest.get_node(pos)
node.name = "techage:ta4_trafficlight1B"
minetest.swap_node(pos, node)
number = techage.add_node(pos, "techage:ta4_trafficlight1B")
else
number = techage.add_node(pos, "techage:ta4_trafficlight1")
end
local meta = M(pos)
meta:set_string("state", "off")
meta:set_string("infotext", TITLE .. " " .. number)
end,
on_rightclick = on_rightclick,
after_dig_node = after_dig_node,
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
local tiles = {"techage_trafficlight1_" .. color .. '.png'}
minetest.register_node("techage:ta4_trafficlight1_" .. color, {
description = TITLE,
tiles = tiles,
drawtype = "mesh",
mesh = "techage_traffic_light.obj",
selection_box = SelectionBox,
on_rightclick = on_rightclick,
after_dig_node = after_dig_node,
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_trafficlight1",
})
end
minetest.register_node("techage:ta4_trafficlight1B", {
description = TITLE,
tiles = {"techage_trafficlight1.png"},
drawtype = "mesh",
mesh = "techage_traffic_lightB.obj",
selection_box = SelectionBox,
on_rightclick = on_rightclick,
after_dig_node = after_dig_node,
paramtype = "light",
use_texture_alpha = techage.CLIP,
light_source = 0,
sunlight_propagates = true,
paramtype2 = "facedir",
groups = {cracky=2, crumbly=2, not_in_creative_inventory=1},
is_ground_content = false,
sounds = default.node_sound_glass_defaults(),
drop = "techage:ta4_trafficlight1",
})
for _,color in ipairs({"green", "amber", "red"}) do
local tiles = {"techage_trafficlight1_" .. color .. '.png'}
minetest.register_node("techage:ta4_trafficlight1B_" .. color, {
description = TITLE,
tiles = tiles,
drawtype = "mesh",
mesh = "techage_traffic_lightB.obj",
selection_box = SelectionBox,
on_rightclick = on_rightclick,
after_dig_node = after_dig_node,
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_trafficlight1",
})
end
minetest.register_node("techage:ta4_trafficlight2", {
description = TITLE,
tiles = {"techage_trafficlight2.png"},
drawtype = "mesh",
mesh = "techage_traffic_light.obj",
selection_box = SelectionBox,
after_place_node = function(pos, placer)
local number
if is_pole(pos) then
local node = minetest.get_node(pos)
node.name = "techage:ta4_trafficlight2B"
minetest.swap_node(pos, node)
number = techage.add_node(pos, "techage:ta4_trafficlight2B")
else
number = techage.add_node(pos, "techage:ta4_trafficlight2")
end
local meta = M(pos)
meta:set_string("state", "off")
meta:set_string("infotext", TITLE .. " " .. number)
end,
on_rightclick = on_rightclick,
after_dig_node = after_dig_node,
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
local tiles = {"techage_trafficlight2_" .. color .. '.png'}
minetest.register_node("techage:ta4_trafficlight2_" .. color, {
description = TITLE,
tiles = tiles,
drawtype = "mesh",
mesh = "techage_traffic_light.obj",
selection_box = SelectionBox,
on_rightclick = on_rightclick,
after_dig_node = after_dig_node,
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_trafficlight2",
})
end
minetest.register_node("techage:ta4_trafficlight2B", {
description = TITLE,
tiles = {"techage_trafficlight2.png"},
drawtype = "mesh",
mesh = "techage_traffic_lightB.obj",
selection_box = SelectionBox,
on_rightclick = on_rightclick,
after_dig_node = after_dig_node,
paramtype = "light",
use_texture_alpha = techage.CLIP,
light_source = 0,
sunlight_propagates = true,
paramtype2 = "facedir",
groups = {cracky=2, crumbly=2, not_in_creative_inventory=1},
is_ground_content = false,
sounds = default.node_sound_glass_defaults(),
drop = "techage:ta4_trafficlight2",
})
for _,color in ipairs({"green", "amber", "red"}) do
local tiles = {"techage_trafficlight2_" .. color .. '.png'}
minetest.register_node("techage:ta4_trafficlight2B_" .. color, {
description = TITLE,
tiles = tiles,
drawtype = "mesh",
mesh = "techage_traffic_lightB.obj",
selection_box = SelectionBox,
on_rightclick = on_rightclick,
after_dig_node = after_dig_node,
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_trafficlight2",
})
end
minetest.register_craft({
output = "techage:ta4_trafficlight1",
recipe = {
{"", "dye:black", ""},
{"", "techage:ta4_signaltower", ""},
{"", "default:steel_ingot", ""},
},
})
minetest.register_craft({
output = "techage:ta4_trafficlight2",
recipe = {
{"", "dye:orange", ""},
{"", "techage:ta4_signaltower", ""},
{"", "default:steel_ingot", ""},
},
})
techage.register_node({
"techage:ta4_trafficlight1",
"techage:ta4_trafficlight1_green",
"techage:ta4_trafficlight1_amber",
"techage:ta4_trafficlight1_red",
"techage:ta4_trafficlight2",
"techage:ta4_trafficlight2_green",
"techage:ta4_trafficlight2_amber",
"techage:ta4_trafficlight2_red",
"techage:ta4_trafficlight1B",
"techage:ta4_trafficlight1B_green",
"techage:ta4_trafficlight1B_amber",
"techage:ta4_trafficlight1B_red",
"techage:ta4_trafficlight2B",
"techage:ta4_trafficlight2B_green",
"techage:ta4_trafficlight2B_amber",
"techage:ta4_trafficlight2B_red"}, {
on_recv_message = function(pos, src, topic, payload)
if topic == "green" then
switch_on(pos, "green")
elseif topic == "amber" then
switch_on(pos, "amber")
elseif topic == "red" then
switch_on(pos, "red")
elseif topic == "off" then
switch_off(pos)
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]]
if color then
switch_on(pos, color)
else
switch_off(pos)
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,
})
minetest.register_node("techage:trafficlight_pole", {
description = S("TA4 Traffic Light Pole"),
tiles = {
"techage_trafficlight_pole.png",
},
paramtype2 = "facedir", -- important!
drawtype = "nodebox",
node_box = {
type = "connected",
fixed = {{ -4/32, -16/32, -4/32, 4/32, 16/32, 4/32}},
connect_left = {{-16/32, 0/32, -3/32, 3/32, 6/32, 3/32}},
connect_right = {{ -3/32, 0/32, -3/32, 16/32, 6/32, 3/32}},
connect_back = {{ -3/32, 0/32, -3/32, 3/32, 6/32, 16/32}},
connect_front = {{ -3/32, 0/32, -16/32, 3/32, 6/32, 3/32}},
},
connects_to = {
"techage:trafficlight_arm",
"techage:trafficlight_connector",
"techage:ta4_trafficlight1",
"techage:ta4_trafficlight1B",
"techage:ta4_trafficlight1_red",
"techage:ta4_trafficlight1_amber",
"techage:ta4_trafficlight1_green",
"techage:ta4_trafficlight1B_red",
"techage:ta4_trafficlight1B_amber",
"techage:ta4_trafficlight1B_green",
"techage:ta4_trafficlight2",
"techage:ta4_trafficlight2B",
"techage:ta4_trafficlight2_red",
"techage:ta4_trafficlight2_amber",
"techage:ta4_trafficlight2_green",
"techage:ta4_trafficlight2B_red",
"techage:ta4_trafficlight2B_amber",
"techage:ta4_trafficlight2B_green",
},
paramtype = "light",
sunlight_propagates = true,
is_ground_content = false,
groups = {cracky=2, crumbly=2, choppy=2},
sounds = default.node_sound_defaults(),
})
minetest.register_node("techage:trafficlight_connector", {
description = S("TA4 Traffic Light Connector"),
tiles = {
"techage_trafficlight_pole.png",
},
paramtype2 = "facedir", -- important!
drawtype = "nodebox",
node_box = {
type = "connected",
fixed = {{ -4/32, -4/32, -4/32, 4/32, 10/32, 4/32}},
connect_left = {{-16/32, 0/32, -3/32, 3/32, 6/32, 3/32}},
connect_right = {{ -3/32, 0/32, -3/32, 16/32, 6/32, 3/32}},
connect_back = {{ -3/32, 0/32, -3/32, 3/32, 6/32, 16/32}},
connect_front = {{ -3/32, 0/32, -16/32, 3/32, 6/32, 3/32}},
},
connects_to = {
"techage:trafficlight_arm",
"techage:trafficlight_connector",
"techage:trafficlight_pole",
"techage:ta4_trafficlight1",
"techage:ta4_trafficlight1B",
"techage:ta4_trafficlight1_red",
"techage:ta4_trafficlight1_amber",
"techage:ta4_trafficlight1_green",
"techage:ta4_trafficlight1B_red",
"techage:ta4_trafficlight1B_amber",
"techage:ta4_trafficlight1B_green",
"techage:ta4_trafficlight2",
"techage:ta4_trafficlight2B",
"techage:ta4_trafficlight2_red",
"techage:ta4_trafficlight2_amber",
"techage:ta4_trafficlight2_green",
"techage:ta4_trafficlight2B_red",
"techage:ta4_trafficlight2B_amber",
"techage:ta4_trafficlight2B_green",
},
paramtype = "light",
sunlight_propagates = true,
is_ground_content = false,
groups = {cracky=2, crumbly=2, choppy=2},
sounds = default.node_sound_defaults(),
})
minetest.register_node("techage:trafficlight_arm", {
description = S("TA4 Traffic Light Arm"),
tiles = {
"techage_trafficlight_pole.png",
},
paramtype2 = "facedir", -- important!
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {{ -3/32, 0/32, -16/32, 3/32, 6/32, 16/32}},
},
paramtype = "light",
sunlight_propagates = true,
is_ground_content = false,
groups = {cracky=2, crumbly=2, choppy=2},
sounds = default.node_sound_defaults(),
})
minetest.register_craft({
output = "techage:trafficlight_pole",
recipe = {
{"", "basic_materials:steel_bar", ""},
{"", "basic_materials:steel_bar", "dye:dark_grey"},
{"", "basic_materials:steel_bar", ""},
},
})
minetest.register_craft({
output = "techage:trafficlight_arm",
recipe = {
{"", "dye:dark_grey", ""},
{"basic_materials:steel_bar", "basic_materials:steel_bar", "basic_materials:steel_bar"},
{"", "", ""},
},
})
minetest.register_craft({
output = "techage:trafficlight_connector",
recipe = {
{"dye:dark_grey", "basic_materials:steel_bar", ""},
{"basic_materials:steel_bar", "", ""},
{"", "", ""},
},
})