From 5425e10e91d4e0397f7cb56ca419c769759df244 Mon Sep 17 00:00:00 2001 From: Joachim Stolberg Date: Fri, 3 Jan 2025 16:38:21 +0100 Subject: [PATCH] Improve door and fly controller --- basis/fly_lib.lua | 32 +- basis/mark_lib.lua | 28 +- doc/manual_ta3_DE.lua | 4 +- doc/manual_ta3_EN.lua | 4 +- locale/techage.de.tr | 4 +- locale/techage.fr.tr | 1 + locale/techage.ru.tr | 1 + locale/template.txt | 1 + logic/basic_terminal.lua | 24 +- logic/sequencer2.lua | 35 ++- manuals/manual_ta3_DE.md | 4 +- manuals/manual_ta3_EN.md | 4 +- manuals/nanobasic.md | 1 + manuals/ta3_terminal.md | 2 + move_controller/doorcontroller2.lua | 452 ++++++++-------------------- move_controller/flycontroller.lua | 1 + move_controller/movecontroller.lua | 29 +- 17 files changed, 259 insertions(+), 368 deletions(-) diff --git a/basis/fly_lib.lua b/basis/fly_lib.lua index b3babb6..9370e79 100644 --- a/basis/fly_lib.lua +++ b/basis/fly_lib.lua @@ -60,6 +60,7 @@ local function set_node(item, playername) minetest.set_node(dest_pos, {name=name, param2=param2}) meta:from_table(item.metadata or {}) meta:set_string("ta_move_block", "") + -- Protect the doors from being opened by hand meta:set_int("ta_door_locked", 1) end return @@ -623,6 +624,7 @@ end -- for the attached object (player). local function multi_move_nodes(pos, meta, nvm, lmove, max_speed, height, move2to1) local owner = meta:get_string("owner") + local running = false techage.counting_add(owner, #lmove, #nvm.lpos1 * #lmove) for idx = 1, #nvm.lpos1 do @@ -660,9 +662,12 @@ local function multi_move_nodes(pos, meta, nvm, lmove, max_speed, height, move2t end return false end + running = true end - meta:set_string("status", S("Running")) - return true + if running then + meta:set_string("status", S("Running")) + end + return running end -- Move the nodes from lpos1 to lpos2. @@ -681,11 +686,12 @@ local function move_nodes(pos, meta, lpos1, move, max_speed, height) local pos1 = lpos1[idx] local pos2 = vector.add(lpos1[idx], move) - lpos2[idx] = pos2 + lpos2[idx] = pos1 if not minetest.is_protected(pos1, owner) and not minetest.is_protected(pos2, owner) then if is_simple_node(pos1) and is_valid_dest(pos2) then move_node(pos, meta, pos1, {move}, max_speed, height) + lpos2[idx] = pos2 else if not is_simple_node(pos1) then meta:set_string("status", S("No valid node at the start position")) @@ -829,7 +835,7 @@ function flylib.move_to_other_pos(pos, move2to1) return nvm.running end --- `move` the movement as a vector +-- `move` is the movement as a vector function flylib.move_to(pos, move) local meta = M(pos) local nvm = techage.get_nvm(pos) @@ -857,11 +863,19 @@ function flylib.reset_move(pos) if nvm.running then return false end if meta:get_string("teleport_mode") == "enable" then return false end - if nvm.lpos1 and nvm.lpos1[1] then - local move = vector.subtract(nvm.lpos1[1], (nvm.lastpos or nvm.lpos1)[1]) - - nvm.running, nvm.lastpos = move_nodes(pos, meta, nvm.lastpos or nvm.lpos1, move, max_speed, height) - return nvm.running + if nvm.moveBA then -- A/B mode has no nvm.lastpos + if nvm.lpos2 and nvm.lpos2[1] then + local move = vector.subtract(nvm.lpos2[1], nvm.lpos1[1]) + nvm.running, nvm.lastpos = move_nodes(pos, meta, nvm.lpos2, move, max_speed, height) + return nvm.running + end + else + if nvm.lpos1 and nvm.lpos1[1] then + local move = vector.subtract(nvm.lpos1[1], (nvm.lastpos or nvm.lpos2)[1]) + local lpos = nvm.lastpos or nvm.lpos1 + nvm.running, nvm.lastpos = move_nodes(pos, meta, lpos, move, max_speed, height) + return nvm.running + end end return false end diff --git a/basis/mark_lib.lua b/basis/mark_lib.lua index 40dee56..dc38e41 100644 --- a/basis/mark_lib.lua +++ b/basis/mark_lib.lua @@ -26,7 +26,7 @@ local function unmark_position(name, pos) item.entity:remove() table.remove(MarkedNodes[name], idx) CurrentPos = pos - return + return true end end end @@ -57,10 +57,15 @@ end function marker.get_poslist(name) local idx = 0 local lst = {} + local hashlist = {} for _,item in ipairs(MarkedNodes[name] or {}) do - table.insert(lst, item.pos) - idx = idx + 1 - if idx >= MAX_NUM then break end + local hash = minetest.hash_node_position(item.pos) + if not hashlist[hash] then + table.insert(lst, item.pos) + hashlist[hash] = true + idx = idx + 1 + if idx >= MAX_NUM then break end + end end return lst end @@ -87,6 +92,16 @@ function marker.stop(name) MaxNumber[name] = nil end +function marker.mark_positions(name, lpos, ttl) + for _,pos in ipairs(lpos or {}) do + local entity = minetest.add_entity(pos, "techage:block_marker") + if entity ~= nil then + entity:get_luaentity().player_name = name + entity:get_luaentity().ttl = ttl + end + end +end + minetest.register_on_leaveplayer(function(ObjectRef, timed_out) if ObjectRef and ObjectRef:is_player() then local name = ObjectRef:get_player_name() @@ -110,19 +125,20 @@ minetest.register_entity(":techage:block_marker", { visual_size = {x=1.1, y=1.1}, collisionbox = {-0.55,-0.55,-0.55, 0.55,0.55,0.55}, glow = 8, + static_save = false, }, on_step = function(self, dtime) self.ttl = (self.ttl or 2400) - 1 if self.ttl <= 0 then local pos = self.object:get_pos() - unmark_position(self.player_name, pos) + if not unmark_position(self.player_name, pos) then self.object:remove() end end end, on_punch = function(self, hitter) local pos = self.object:get_pos() local name = hitter:get_player_name() if name == self.player_name then - unmark_position(name, pos) + if not unmark_position(self.player_name, pos) then self.object:remove() end end end, }) diff --git a/doc/manual_ta3_DE.lua b/doc/manual_ta3_DE.lua index 9748d82..b6f75e2 100644 --- a/doc/manual_ta3_DE.lua +++ b/doc/manual_ta3_DE.lua @@ -593,7 +593,7 @@ return { "\n", "Der Tür Controller II kann alle Arten von Blöcken entfernen und wieder setzen. Um den Tür Controller II anzulernen\\, muss der \"Aufzeichnen\" Button gedrückt werden. Dann müssen alle Blöcke angeklickt werden\\, die Teil der Tür / des Tores sein sollen. Danach muss der \"Fertig\" Button gedrückt werden. Es können bis zu 16 Blöcke ausgewählt werden. Die entfernten Blöcke werden im Inventar des Controllers gespeichert.\n".. "\n".. - " Über die Tasten \"Entfernen\" bzw. \"Setzen\" kann die Funktion des Controllers von Hand getestet werden.\n".. + " Über die Taste \"Austauschen\" kann die Funktion des Controllers von Hand getestet werden.\n".. "\n".. "Wird ein 'on' / 'off' Kommando an den Tür Controller II gesendet\\, entfernt bzw. setzt er die Blöcke ebenfalls.\n".. "\n".. @@ -607,6 +607,8 @@ return { "\n".. "Die Slot-Nummer des Inventars (1 .. 16) muss in allen drei Fällen als payload übergeben werden.\n".. "\n".. + "Mit '$send_cmnd(node_number\\, \"reset\")' wird der Tür Controller zurückgesetzt. \n".. + "\n".. "Damit lassen sich auch ausfahrbare Treppen und ähnliches simulieren.\n".. "\n".. "\n".. diff --git a/doc/manual_ta3_EN.lua b/doc/manual_ta3_EN.lua index 53b0af4..470edcd 100644 --- a/doc/manual_ta3_EN.lua +++ b/doc/manual_ta3_EN.lua @@ -592,7 +592,7 @@ return { "\n".. "\n".. "\n", - "The Door Controller II can remove and set all types of blocks. To teach in the Door Controller II\\, the \"Record\" button must be pressed. Then all blocks that should be part of the door / gate must be clicked. Then the \"Done\" button must be pressed. Up to 16 blocks can be selected. The removed blocks are saved in the controller's inventory. The function of the controller can be tested manually using the \"Remove\" or \"Set\" buttons. If an 'on' /'off' command is sent to the Door Controller II\\, it removes or sets the blocks as well.\n".. + "The Door Controller II can remove and set all types of blocks. To teach in the Door Controller II\\, the \"Record\" button must be pressed. Then all blocks that should be part of the door / gate must be clicked. Then the \"Done\" button must be pressed. Up to 16 blocks can be selected. The removed blocks are saved in the controller's inventory. The function of the controller can be tested manually using the \"Exchange\" button. If an 'on' /'off' command is sent to the Door Controller II\\, it removes or sets the blocks as well.\n".. "\n".. "With '$send_cmnd(node_number\\, \"exchange\"\\, 2)' individual blocks can be set\\, removed or replaced by other blocks from the inventory. \n".. "\n".. @@ -604,6 +604,8 @@ return { "\n".. "The slot number of the inventory (1 .. 16) must be passed as payload in all three cases.\n".. "\n".. + "With '$send_cmnd(node_number\\, \"reset\")' the door controller is reset.\n".. + "\n".. "This can also be used to simulate extendable stairs and the like. \n".. "\n".. "\n".. diff --git a/locale/techage.de.tr b/locale/techage.de.tr index 9638e2d..269c379 100644 --- a/locale/techage.de.tr +++ b/locale/techage.de.tr @@ -424,6 +424,7 @@ Recording...=Aufzeichnung... ### movecontroller.lua ### Reset=Rücksetzen +Show positions=Positionen anzeigen ### drillbox.lua ### @@ -1601,6 +1602,3 @@ Remove detector=Entferne Detektor TA4 Collider Detector Worker=TA4 Collider Detektor Worker [TA4] Detector is being built!=[TA4] Detektor wird gebaut! [TA4] Detector is being removed!=[TA4] Detektor wird entfernt! - - -##### not used anymore ##### diff --git a/locale/techage.fr.tr b/locale/techage.fr.tr index e8e21e5..b30985e 100644 --- a/locale/techage.fr.tr +++ b/locale/techage.fr.tr @@ -424,6 +424,7 @@ Recording...=Enregistrement... ### movecontroller.lua ### Reset= +Show positions= ### drillbox.lua ### diff --git a/locale/techage.ru.tr b/locale/techage.ru.tr index 666e736..0cfb05a 100644 --- a/locale/techage.ru.tr +++ b/locale/techage.ru.tr @@ -424,6 +424,7 @@ Recording...=Запись... ### movecontroller.lua ### Reset=Переустановить +Show positions= ### drillbox.lua ### diff --git a/locale/template.txt b/locale/template.txt index 33d597e..4ee8999 100644 --- a/locale/template.txt +++ b/locale/template.txt @@ -424,6 +424,7 @@ Recording...= ### movecontroller.lua ### Reset= +Show positions= ### drillbox.lua ### diff --git a/logic/basic_terminal.lua b/logic/basic_terminal.lua index 2b07fb9..f8c3c78 100644 --- a/logic/basic_terminal.lua +++ b/logic/basic_terminal.lua @@ -15,6 +15,7 @@ local M = minetest.get_meta local S = techage.S local SCREENSAVER_TIME = 60 * 5 +local CYCLE_TIME = 0.2 local Functions = {} local Actions = {} @@ -301,7 +302,8 @@ minetest.register_node("techage:basic_terminal", { on_timer = function(pos, elapsed) local nvm = techage.get_nvm(pos) --print("on_timer", nvm.status) - if (nvm.timeout or 0) > minetest.get_gametime() then + if nvm.ttl and nvm.ttl > 1 then + nvm.ttl = nvm.ttl - 1 return true end @@ -315,14 +317,14 @@ minetest.register_node("techage:basic_terminal", { nvm.status = "error" nvm.bttns = {"Edit", "", "", "", "", "Stop", "", ""} nvm.input = "" - nvm.timeout = 0 + 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.timeout = 0 + nvm.ttl = nil local text = nanobasic.get_screen_buffer(pos) M(pos):set_string("formspec", formspec(pos, text)) elseif res == nanobasic.NB_BREAK then @@ -331,7 +333,7 @@ minetest.register_node("techage:basic_terminal", { nvm.status = "break" nvm.bttns = {"", "", "", "", "", "Stop", "Continue", "List"} nvm.input = InputField - nvm.timeout = 0 + nvm.ttl = nil local text = nanobasic.get_screen_buffer(pos) M(pos):set_string("formspec", formspec(pos, text)) elseif res >= nanobasic.NB_XFUNC then @@ -472,7 +474,7 @@ end) register_ext_function("sleep", {nanobasic.NB_NUM}, nanobasic.NB_NONE, function(pos, nvm) local t = nanobasic.pop_num(pos) or 0 - nvm.timeout = minetest.get_gametime() + t + nvm.ttl = t / CYCLE_TIME local text = nanobasic.get_screen_buffer(pos) M(pos):set_string("formspec", formspec(pos, text)) return true @@ -700,8 +702,8 @@ register_action({"init", "edit", "stopped"}, "Run", function(pos, nvm, fields) nvm.input = "" nvm.variables = nanobasic.get_variable_list(pos) nvm.error_label_addr = nanobasic.get_label_address(pos, "65000") or 0 - nvm.timeout = 0 - minetest.get_node_timer(pos):start(0.2) + nvm.ttl = nil + minetest.get_node_timer(pos):start(CYCLE_TIME) return nanobasic.get_screen_buffer(pos) or "" else nvm.status = "error" @@ -715,7 +717,7 @@ register_action({"break"}, "Continue", function(pos, nvm, fields) nvm.status = "running" nvm.bttns = {"", "", "", "", "", "Stop", "", ""} nvm.input = "" - minetest.get_node_timer(pos):start(0.2) + minetest.get_node_timer(pos):start(CYCLE_TIME) return nanobasic.get_screen_buffer(pos) or "" end) @@ -798,7 +800,7 @@ register_action({"input_num"}, "Enter", function(pos, nvm, fields) nvm.status = "running" nvm.bttns = {"", "", "", "", "", "Stop", "", ""} nvm.input = "" - minetest.get_node_timer(pos):start(0.2) + minetest.get_node_timer(pos):start(CYCLE_TIME) return nanobasic.get_screen_buffer(pos) or "" end) @@ -808,7 +810,7 @@ register_action({"input_str"}, "Enter", function(pos, nvm, fields) nvm.status = "running" nvm.bttns = {"", "", "", "", "", "Stop", "", ""} nvm.input = "" - minetest.get_node_timer(pos):start(0.2) + minetest.get_node_timer(pos):start(CYCLE_TIME) return nanobasic.get_screen_buffer(pos) or "" end) @@ -818,7 +820,7 @@ techage.register_node({"techage:basic_terminal"}, { nanobasic.vm_restore(pos) local nvm = techage.get_nvm(pos) if nvm.status == "running" then - minetest.get_node_timer(pos):start(0.2) + minetest.get_node_timer(pos):start(CYCLE_TIME) end end, }) diff --git a/logic/sequencer2.lua b/logic/sequencer2.lua index 884ccbf..6473967 100644 --- a/logic/sequencer2.lua +++ b/logic/sequencer2.lua @@ -59,6 +59,8 @@ local WRENCH_MENU = { }, } +local delayed_start = false + local function cycle_time(pos) local mem = techage.get_mem(pos) if not mem.cycletime then @@ -208,6 +210,16 @@ local function restart_timer(pos, ticks) 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 @@ -329,7 +341,7 @@ local INFO = [[Commands: 'goto ', '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 + if (topic == "goto" or topic == "on") and not nvm.running and not delayed_start then local mem = techage.get_mem(pos) nvm.running = true mem.idx = tonumber(payload or 1) or 1 @@ -348,7 +360,7 @@ techage.register_node({"techage:ta4_sequencer"}, { 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 + if payload[1] ~= 0 and not nvm.running and not delayed_start then local mem = techage.get_mem(pos) nvm.running = true mem.idx = tonumber(payload or 1) or 1 @@ -364,4 +376,23 @@ techage.register_node({"techage:ta4_sequencer"}, { end return 2 end, + on_node_load = function(pos, node) + local nvm = techage.get_nvm(pos) + if nvm.running and delayed_start then + nvm.running = false + minetest.get_node_timer(pos):stop() + minetest.after(30, function(pos) + local nvm = techage.get_nvm(pos) + nvm.running = true + minetest.get_node_timer(pos):start(1) + end, pos) + end + end, }) + +core.register_on_mods_loaded(function() + delayed_start = true + minetest.after(30, function() + delayed_start = false + end) +end) diff --git a/manuals/manual_ta3_DE.md b/manuals/manual_ta3_DE.md index f08b317..5642c22 100644 --- a/manuals/manual_ta3_DE.md +++ b/manuals/manual_ta3_DE.md @@ -680,7 +680,7 @@ Der Tür Controller dient zur Ansteuerung der TA3 Tür/Tor Blöcke. Beim Tür Co Der Tür Controller II kann alle Arten von Blöcken entfernen und wieder setzen. Um den Tür Controller II anzulernen, muss der "Aufzeichnen" Button gedrückt werden. Dann müssen alle Blöcke angeklickt werden, die Teil der Tür / des Tores sein sollen. Danach muss der "Fertig" Button gedrückt werden. Es können bis zu 16 Blöcke ausgewählt werden. Die entfernten Blöcke werden im Inventar des Controllers gespeichert. - Über die Tasten "Entfernen" bzw. "Setzen" kann die Funktion des Controllers von Hand getestet werden. + Über die Taste "Austauschen" kann die Funktion des Controllers von Hand getestet werden. Wird ein `on` / `off` Kommando an den Tür Controller II gesendet, entfernt bzw. setzt er die Blöcke ebenfalls. @@ -694,6 +694,8 @@ Mit `$send_cmnd(node_number, "get", 2)` wird der Name des gesetzten Blocks zurü Die Slot-Nummer des Inventars (1 .. 16) muss in allen drei Fällen als payload übergeben werden. +Mit `$send_cmnd(node_number, "reset")` wird der Tür Controller zurückgesetzt. + Damit lassen sich auch ausfahrbare Treppen und ähnliches simulieren. [ta3_doorcontroller|image] diff --git a/manuals/manual_ta3_EN.md b/manuals/manual_ta3_EN.md index be7edb4..5f893bb 100644 --- a/manuals/manual_ta3_EN.md +++ b/manuals/manual_ta3_EN.md @@ -677,7 +677,7 @@ The door controller is used to control the TA3 door/gate blocks. With the door c ### TA3 Door Controller II -The Door Controller II can remove and set all types of blocks. To teach in the Door Controller II, the "Record" button must be pressed. Then all blocks that should be part of the door / gate must be clicked. Then the "Done" button must be pressed. Up to 16 blocks can be selected. The removed blocks are saved in the controller's inventory. The function of the controller can be tested manually using the "Remove" or "Set" buttons. If an `on` /`off` command is sent to the Door Controller II, it removes or sets the blocks as well. +The Door Controller II can remove and set all types of blocks. To teach in the Door Controller II, the "Record" button must be pressed. Then all blocks that should be part of the door / gate must be clicked. Then the "Done" button must be pressed. Up to 16 blocks can be selected. The removed blocks are saved in the controller's inventory. The function of the controller can be tested manually using the "Exchange" button. If an `on` /`off` command is sent to the Door Controller II, it removes or sets the blocks as well. With `$send_cmnd(node_number, "exchange", 2)` individual blocks can be set, removed or replaced by other blocks from the inventory. @@ -689,6 +689,8 @@ The name of the set block is returned with `$send_cmnd(node_number, "get", 2)`. The slot number of the inventory (1 .. 16) must be passed as payload in all three cases. +With `$send_cmnd(node_number, "reset")` the door controller is reset. + This can also be used to simulate extendable stairs and the like. [ta3_doorcontroller|image] diff --git a/manuals/nanobasic.md b/manuals/nanobasic.md index 7259767..58770e1 100644 --- a/manuals/nanobasic.md +++ b/manuals/nanobasic.md @@ -1465,6 +1465,7 @@ As payload data, these commands may require numeric values or a string value. | DC2 Exchange Block | 9 | 0, idx | TA3 Door Controller II (techage:ta3_doorcontroller2). Exchange a block
`idx` is the inventory slot number (1..n) | | DC2 Set Block | 9 | 1, idx | TA3 Door Controller II (techage:ta3_doorcontroller2). Set/add a block
`idx` is the inventory slot number (1..n) with the block to be set | | DC2 Dig Block | 9 | 2, idx | TA3 Door Controller II (techage:ta3_doorcontroller2). Dig/remove a block
`idx` is the empty inventory slot number (1..n) for the block | +| DC2 Reset | 9 | 3 | TA3 Door Controller II (techage:ta3_doorcontroller2). Reset the door controller | | Autocrafter | 10 | num, idx | Set the TA4 Autocrafter recipe with a recipe from a TA4 Recipe Block.
`num` is the TA4 Recipe Block number
`idx` is the number of the recipe in the TA4 Recipe Block | | Autocrafter | 11 | - | Move all items from input inventory to output inventory. Returns 1 if the input inventory was emptied in the process. Otherwise return 0 | | Move Contr. 1 | 11 | 1 | TA4 Move Controller command to move the block(s) from position A to B | diff --git a/manuals/ta3_terminal.md b/manuals/ta3_terminal.md index a9e7ab3..fe3d653 100644 --- a/manuals/ta3_terminal.md +++ b/manuals/ta3_terminal.md @@ -33,6 +33,8 @@ examples are written in lower case letters. ### Hello World +The following example prints "Hello World!" to the terminal. + ```basic 10 for i=1 to 10 20 print "Hello World!" diff --git a/move_controller/doorcontroller2.lua b/move_controller/doorcontroller2.lua index 7804314..c133a92 100644 --- a/move_controller/doorcontroller2.lua +++ b/move_controller/doorcontroller2.lua @@ -3,7 +3,7 @@ TechAge ======= - Copyright (C) 2020-2023 Joachim Stolberg + Copyright (C) 2020-2025 Joachim Stolberg AGPL v3 See LICENSE.txt for more information @@ -17,146 +17,74 @@ local M = minetest.get_meta local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end local S = techage.S +local MP = minetest.get_modpath("techage") +local mark = dofile(MP .. "/basis/mark_lib.lua") local logic = techage.logic local fly = techage.flylib local NUMSLOTS = 16 -local MarkedNodes = {} -- t[player] = {{entity, pos},...} -local CurrentPos -- to mark punched entities - -------------------------------------------------------------------------- -- helper functions -------------------------------------------------------------------------- -local function count_nodes(tbl, name) - if tbl[name] then - tbl[name] = tbl[name] + 1 - else - tbl[name] = 1 +local function get_positions(nvm) + local lpos = {} + for idx,cfg in ipairs(nvm.config) do + lpos[idx] = cfg.pos end -end - -local function take_node(tbl, name) - if tbl[name] and tbl[name] > 0 then - tbl[name] = tbl[name] - 1 - return true - end -end - -local function next_node(tbl) - return function(tbl) - local name, cnt = next(tbl) - if cnt and cnt > 0 then - cnt = cnt - 1 - if cnt == 0 then - tbl[name] = nil - else - tbl[name] = cnt - end - return name - end - end, tbl -end - -local function get_new_nodename(item) - local name = item:get_name() - if name == "" then - return "air" - end - return name + return lpos end local function get_node_name(nvm, slot) - nvm.pos_list = nvm.pos_list or {} - local pos = nvm.pos_list[slot] - if pos then - return techage.get_node_lvm(pos).name + nvm.config = nvm.config or {} + local cfg = nvm.config[slot] + if cfg then + return techage.get_node_lvm(cfg.pos).name end return "unknown" end -local function is_simple_node(name) - local ndef = minetest.registered_nodes[name] - return name ~= "air" and techage.can_dig_node(name, ndef) +local function is_simple_node(pos, name) + if not minecart.is_rail(pos, name) then + local ndef = minetest.registered_nodes[name] + return techage.can_dig_node(name, ndef) or minecart.is_cart(name) + end + return false end --------------------------------------------------------------------------- --- Marker --------------------------------------------------------------------------- -local function unmark_position(name, pos) - pos = vector.round(pos) - for idx,item in ipairs(MarkedNodes[name] or {}) do - if vector.equals(pos, item.pos) then - item.entity:remove() - table.remove(MarkedNodes[name], idx) - CurrentPos = pos - return - end +-- Slot Configuration { +-- pos, -- pos from the node in the inventory +-- param2, -- param2 from the node in the inventory +-- state, -- false = block is dug/slot is filled, true = block is set/slot is empty +-- } +local function gen_config(pos, pos_list) + local nvm = techage.get_nvm(pos) + nvm.config = {} + for idx,pos in ipairs(pos_list) do + local node = techage.get_node_lvm(pos) + nvm.config[idx] = {pos = pos, param2 = node.param2, state = true} end end -local function unmark_all(name) - for _,item in ipairs(MarkedNodes[name] or {}) do - item.entity:remove() +local function gen_config_initial(pos) + local inv = M(pos):get_inventory() + local item_list = inv:get_list("main") + local nvm = techage.get_nvm(pos) + nvm.config = {} + nvm.pos_list = nvm.pos_list or {} + nvm.param2_list = nvm.param2_list or {} + local len = #nvm.pos_list + for idx = 1, len do + -- The status is not yet known. It is assumed that the block is set (status = true) + -- if the inventory is empty. + local item = item_list[idx] + local state = (item and item:get_count() > 0) == false + nvm.config[idx] = {pos = nvm.pos_list[idx], param2 = nvm.param2_list[idx], state = state} end - MarkedNodes[name] = nil + nvm.pos_list = nil + nvm.param2_list = nil end -local function mark_position(name, pos) - MarkedNodes[name] = MarkedNodes[name] or {} - pos = vector.round(pos) - if not CurrentPos or not vector.equals(pos, CurrentPos) then -- entity not punched? - local entity = minetest.add_entity(pos, "techage:marker") - if entity ~= nil then - entity:get_luaentity().player_name = name - table.insert(MarkedNodes[name], {pos = pos, entity = entity}) - end - CurrentPos = nil - return true - end - CurrentPos = nil -end - -local function get_poslist(name) - local lst = {} - for _,item in ipairs(MarkedNodes[name] or {}) do - table.insert(lst, item.pos) - end - return lst -end - -minetest.register_entity(":techage:marker", { - initial_properties = { - visual = "cube", - textures = { - "techage_cube_mark.png", - "techage_cube_mark.png", - "techage_cube_mark.png", - "techage_cube_mark.png", - "techage_cube_mark.png", - "techage_cube_mark.png", - }, - physical = false, - visual_size = {x = 1.1, y = 1.1}, - collisionbox = {-0.55,-0.55,-0.55, 0.55,0.55,0.55}, - glow = 8, - }, - on_step = function(self, dtime) - self.ttl = (self.ttl or 2400) - 1 - if self.ttl <= 0 then - local pos = self.object:get_pos() - unmark_position(self.player_name, pos) - end - end, - on_punch = function(self, hitter) - local pos = self.object:get_pos() - local name = hitter:get_player_name() - if name == self.player_name then - unmark_position(name, pos) - end - end, -}) - -------------------------------------------------------------------------- -- formspec -------------------------------------------------------------------------- @@ -165,191 +93,90 @@ local function formspec1(nvm, meta) local play_sound = dump(nvm.play_sound or false) return "size[8,7]".. "tabheader[0,0;tab;"..S("Ctrl,Inv")..";1;;true]".. - "button[0.7,0.2;3,1;record;"..S("Record").."]".. - "button[4.3,0.2;3,1;ready;"..S("Done").."]".. - "button[0.7,1.2;3,1;reset;"..S("Reset").."]".. - "button[4.3,1.2;3,1;exchange;"..S("Exchange").."]".. - "checkbox[4.3,2.1;play_sound;"..S("with door sound")..";"..play_sound.."]".. - "label[0.5,2.3;"..status.."]".. + "button[0.7,0.0;3,1;record;"..S("Record").."]".. + "button[4.3,0.0;3,1;ready;"..S("Done").."]".. + "button[0.7,0.9;3,1;reset;"..S("Reset").."]".. + "button[4.3,0.9;3,1;exchange;"..S("Exchange").."]".. + "button[0.7,1.8;3,1;show;"..S("Show positions").."]".. + "checkbox[4.3,1.8;play_sound;"..S("with door sound")..";"..play_sound.."]".. + "label[0.5,2.8;"..status.."]".. "list[current_player;main;0,3.3;8,4;]" end -local function formspec2() +local function formspec2(nvm) + local lbls = {} + for idx,item in ipairs(nvm.config or {}) do + local x = ((idx-1) % 8) + 0.3 + local y = math.floor((idx-1) / 8) * 2.4 + if item.state then + lbls[idx] = "label[" .. x .."," .. y .. ";" .. idx .. "]" + else + lbls[idx] = "label[" .. x .."," .. y .. ";" .. idx .. " *]" + end + end return "size[8,7]".. "tabheader[0,0;tab;"..S("Ctrl,Inv")..";2;;true]".. - "label[0.3,0.0;1]".. - "label[7.3,0.0;8]".. - "label[0.3,2.4;9]".. - "label[7.3,2.4;16]".. + table.concat(lbls, "").. "list[context;main;0,0.5;8,2;]".. "list[current_player;main;0,3.3;8,4;]".. "listring[context;main]".. "listring[current_player;main]" end -local function play_sound(pos) - minetest.sound_play("techage_button", { - pos = pos, - gain = 1, - max_hear_distance = 15}) -end - --------------------------------------------------------------------------- --- Configuration --------------------------------------------------------------------------- --- Store the current state of inventory and placed nodes -local function store_config(pos, nvm) - local meta = M(pos) - local inv = meta:get_inventory() - local item_list = inv:get_list("main") - local nodes = {exp_nodes = {}, inv_nodes = {}} - - nvm.pos_list = nvm.pos_list or {} - nvm.param2_list = nvm.param2_list or {} - - for idx = 1, NUMSLOTS do - local pos = nvm.pos_list[idx] - - if pos then - local param2 = nvm.param2_list[idx] or 0 - local item = item_list[idx] - if item and item:get_count() > 0 then - nodes.inv_nodes[idx] = {name = item:get_name(), param2 = param2} - end - - local node = techage.get_node_lvm(pos) - if is_simple_node(node.name) or node.name == "air" then - nodes.exp_nodes[idx] = techage.get_node_lvm(pos) - end - end - end - meta:set_string("stored_config", minetest.serialize(nodes)) -end - --- Generate a table of currently available inventory and placed nodes -local function available_nodes(pos, nvm, item_list) - local nodes = {} - nvm.pos_list = nvm.pos_list or {} - - for idx = 1, NUMSLOTS do - local item = item_list[idx] - if item and item:get_count() > 0 then - count_nodes(nodes, item:get_name()) - end - - local pos = nvm.pos_list[idx] - if pos then - local node = techage.get_node_lvm(pos) - if is_simple_node(node.name) then - count_nodes(nodes, node.name) - end - end - end - return nodes -end - -local function restore_config(pos, nvm) - local meta = M(pos) - local inv = meta:get_inventory() - local item_list = inv:get_list("main") - local stock = available_nodes(pos, nvm, item_list) - local nodes = minetest.deserialize(meta:get_string("stored_config")) or {} - nvm.pos_list = nvm.pos_list or {} - - inv:set_list("main", {}) - item_list = inv:get_list("main") - - for idx, node in pairs(nodes.inv_nodes or {}) do - if take_node(stock, node.name) then - item_list[idx] = ItemStack(node.name) - end - end - inv:set_list("main", item_list) - - for idx, node in pairs(nodes.exp_nodes or {}) do - local pos = nvm.pos_list[idx] - if take_node(stock, node.name) then - local param2 = nvm.param2_list[idx] or 0 - fly.exchange_node(pos, node.name, param2) - nvm.expected_nodenames[idx] = node.name - else - fly.remove_node(pos) - nvm.expected_nodenames[idx] = "air" - end - end - - for name in next_node(stock) do - inv:add_item("main", ItemStack(name)) - end - - return true -end - -------------------------------------------------------------------------- -- Exchange nodes -------------------------------------------------------------------------- -local function exchange_node(pos, item, param2) - local node = minetest.get_node_or_nil(pos) - if node and (is_simple_node(node.name) or node.name == "air") then - if item and is_simple_node(item:get_name()) then - fly.exchange_node(pos, item:get_name(), param2) +local function exchange_node(cfg, item) + local node = techage.get_node_lvm(cfg.pos) + if is_simple_node(cfg.pos, node.name) then + local name = item:get_count() > 0 and item:get_name() or "air" + fly.exchange_node(cfg.pos, name, cfg.param2) + cfg.param2 = node.param2 + cfg.state = not cfg.state + if node.name ~= "air" then + return ItemStack(node.name) else - fly.remove_node(pos) - end - if not techage.is_air_like(node.name) then - return ItemStack(node.name), node.param2 - else - return ItemStack(), param2 + return ItemStack() end end - return item, param2 -end - -local function expected_node(pos, nvm, idx, force, new_nodename) - local expected_name = force and nvm.expected_nodenames[idx] or nil - if expected_name then - local node = techage.get_node_lvm(pos) - if expected_name == node.name then - nvm.expected_nodenames[idx] = new_nodename - return true - else - return false - end - end - nvm.expected_nodenames[idx] = new_nodename - return true + return item end local function exchange_nodes(pos, nvm, slot, force) - local meta = M(pos) - local inv = meta:get_inventory() - + local inv = M(pos):get_inventory() local item_list = inv:get_list("main") local res = false - nvm.pos_list = nvm.pos_list or {} - nvm.param2_list = nvm.param2_list or {} - nvm.expected_nodenames = nvm.expected_nodenames or {} + nvm.config = nvm.config or {} + local len = #nvm.config - for idx = (slot or 1), (slot or NUMSLOTS) do - local pos = nvm.pos_list[idx] + for idx = (slot or 1), (slot or len) do + local cfg = nvm.config[idx] local item = item_list[idx] - if pos then - if (force == nil) - or (force == "exch") - or (force == "dig" and item:get_count() == 0) - or (force == "set" and item:get_count() > 0) then - if expected_node(pos, nvm, idx, force, get_new_nodename(item)) then - item_list[idx], nvm.param2_list[idx] = exchange_node(pos, item, nvm.param2_list[idx]) - end - res = true - end + if (force == nil) + or (force == "exch") + or (force == "dig" and item:get_count() == 0) + or (force == "set" and item:get_count() > 0) then + item_list[idx] = exchange_node(cfg, item) + res = true end end - inv:set_list("main", item_list) return res end +local function reset_config(pos, nvm) + local inv = M(pos):get_inventory() + local item_list = inv:get_list("main") + + for idx, cfg in ipairs(nvm.config or {}) do + local item = item_list[idx] + if not cfg.state then + item_list[idx] = exchange_node(cfg, item) + end + end + inv:set_list("main", item_list) +end + local function show_nodes(pos) local nvm = techage.get_nvm(pos) if not nvm.is_on then @@ -417,45 +244,48 @@ minetest.register_node("techage:ta3_doorcontroller2", { local nvm = techage.get_nvm(pos) if fields.tab == "2" then - meta:set_string("formspec", formspec2(meta)) + meta:set_string("formspec", formspec2(nvm)) return elseif fields.tab == "1" then meta:set_string("formspec", formspec1(nvm, meta)) return elseif fields.record then - local inv = meta:get_inventory() - nvm.pos_list = {} + nvm.recording = true meta:set_string("status", S("Recording...")) local name = player:get_player_name() minetest.chat_send_player(name, S("Click on all the blocks that are part of the door/gate")) - nvm.expected_nodenames = {} - MarkedNodes[name] = {} + mark.unmark_all(name) + mark.start(name, NUMSLOTS) meta:set_string("stored_config", "") meta:set_string("formspec", formspec1(nvm, meta)) - elseif fields.ready then + elseif fields.ready and nvm.recording then + nvm.recording = false local name = player:get_player_name() - local pos_list = get_poslist(name) + local pos_list = mark.get_poslist(name) + gen_config(pos, pos_list) local text = #pos_list.." "..S("block positions are stored.") meta:set_string("status", text) - nvm.pos_list = pos_list - nvm.expected_nodenames = {} - unmark_all(name) + mark.unmark_all(name) + mark.stop(name) meta:set_string("stored_config", "") meta:set_string("formspec", formspec1(nvm, meta)) elseif fields.exchange then if exch_nodes(pos) then - store_config(pos, nvm) meta:set_string("status", S("Blocks exchanged")) meta:set_string("formspec", formspec1(nvm, meta)) local name = player:get_player_name() - MarkedNodes[name] = nil + mark.stop(name) end + elseif fields.show then + local name = player:get_player_name() + local lpos = get_positions(nvm) + mark.mark_positions(name, lpos, 300) elseif fields.reset then - restore_config(pos, nvm) + reset_config(pos, nvm) meta:set_string("status", S("Blocks reset")) meta:set_string("formspec", formspec1(nvm, meta)) local name = player:get_player_name() - MarkedNodes[name] = nil + mark.stop(name) elseif fields.play_sound then nvm.play_sound = fields.play_sound == "true" meta:set_string("formspec", formspec1(nvm, meta)) @@ -478,10 +308,16 @@ minetest.register_node("techage:ta3_doorcontroller2", { if minetest.is_protected(pos, player:get_player_name()) then return 0 end - if is_simple_node(stack:get_name()) then - return 1 + return 1 + end, + + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + if not clicker or minetest.is_protected(pos, clicker:get_player_name()) then + return end - return 0 + local meta = M(pos) + local nvm = techage.get_nvm(pos) + meta:set_string("formspec", formspec1(nvm, meta)) end, can_dig = function(pos, player) @@ -495,7 +331,7 @@ minetest.register_node("techage:ta3_doorcontroller2", { after_dig_node = function(pos, oldnode, oldmetadata, digger) local name = digger:get_player_name() - unmark_all(name) + mark.unmark_all(name) techage.remove_node(pos, oldnode, oldmetadata) end, @@ -525,7 +361,7 @@ techage.register_node({"techage:ta3_doorcontroller2"}, { return get_node_name(nvm, tonumber(payload)) elseif topic == "reset" then local nvm = techage.get_nvm(pos) - return restore_config(pos, nvm) + return reset_config(pos, nvm) end return false end, @@ -545,7 +381,7 @@ techage.register_node({"techage:ta3_doorcontroller2"}, { return exchange_nodes(pos, nvm, payload[2] or 1, "dig") and 0 or 3 elseif topic == 9 and payload[1] == 3 then -- reset local nvm = techage.get_nvm(pos) - return restore_config(pos, nvm) and 0 or 3 + return reset_config(pos, nvm) and 0 or 3 end return 2 end, @@ -557,23 +393,9 @@ techage.register_node({"techage:ta3_doorcontroller2"}, { return 2, "" end, on_node_load = function(pos) - local meta = M(pos) local nvm = techage.get_nvm(pos) - meta:set_string("status", "") - meta:set_string("formspec", formspec1(nvm, meta)) - local pos_list = minetest.deserialize(meta:get_string("pos_list")) - if pos_list then - nvm.pos_list = pos_list - meta:set_string("pos_list", "") - local inv = meta:get_inventory() - if inv:is_empty("main") then - nvm.is_on = true - end - end - local param2_list = minetest.deserialize(meta:get_string("param2_list")) - if param2_list then - nvm.param2_list = param2_list - meta:set_string("param2_list", "") + if nvm.config == nil then + gen_config_initial(pos) end end, }) @@ -584,20 +406,6 @@ minetest.register_craft({ recipe = {"techage:ta3_doorcontroller"}, }) -minetest.register_on_punchnode(function(pos, node, puncher, pointed_thing) - if puncher and puncher:is_player() then - local name = puncher:get_player_name() - - if not MarkedNodes[name] then - return - end - - if not minetest.is_protected(pointed_thing.under, name) then - mark_position(name, pointed_thing.under) - end - end -end) - local Doors = { "doors:door_steel", "doors:prison_door", diff --git a/move_controller/flycontroller.lua b/move_controller/flycontroller.lua index 70a21b3..caedeaf 100644 --- a/move_controller/flycontroller.lua +++ b/move_controller/flycontroller.lua @@ -258,6 +258,7 @@ techage.register_node({"techage:ta5_flycontroller"}, { end, on_node_load = function(pos, node) M(pos):set_string("status", "") + techage.get_nvm(pos).running = false end, }) diff --git a/move_controller/movecontroller.lua b/move_controller/movecontroller.lua index 5f7ce00..a701746 100644 --- a/move_controller/movecontroller.lua +++ b/move_controller/movecontroller.lua @@ -63,26 +63,29 @@ local function formspec(nvm, meta) local path = minetest.formspec_escape(meta:contains("path") and meta:get_string("path") or "0,3,0") local buttons if meta:get_string("opmode") == "move xyz" then - buttons = "field[0.4,2.5;3.8,1;path;" .. S("Move distance") .. ";" .. path .. "]" .. - "button_exit[4.1,2.2;3.8,1;move2;" .. S("Move") .. "]" .. - "button_exit[0.1,3.3;3.8,1;reset;" .. S("Reset") .. "]" + buttons = "field[0.4,2.3;3.8,1;path;" .. S("Move distance") .. ";" .. path .. "]" .. + "button_exit[4.1,2.0;3.8,1;move2;" .. S("Move") .. "]" .. + "button_exit[0.1,3.0;3.8,1;reset;" .. S("Reset") .. "]" .. + "button[4.1,3.0;3.8,1;show;" .. S("Show positions") .. "]" else - buttons = "field[0.4,2.5;3.8,1;path;" .. S("Move distance (A to B)") .. ";" .. path .. "]" .. - "button_exit[0.1,3.3;3.8,1;moveAB;" .. S("Move A-B") .. "]" .. - "button_exit[4.1,3.3;3.8,1;moveBA;" .. S("Move B-A") .. "]" .. - "button[4.1,2.2;3.8,1;store;" .. S("Store") .. "]" + buttons = "field[0.4,2.3;3.8,1;path;" .. S("Move distance (A to B)") .. ";" .. path .. "]" .. + "button_exit[0.1,3.0;3.8,1;moveAB;" .. S("Move A-B") .. "]" .. + "button_exit[4.1,3.0;3.8,1;moveBA;" .. S("Move B-A") .. "]" .. + "button[4.1,2.0;3.8,1;store;" .. S("Store") .. "]" .. + "button[0.1,4.0;3.8,1;show;" .. S("Show positions") .. "]" .. + "button_exit[4.1,4.0;3.8,1;reset;" .. S("Reset") .. "]" end - return "size[8,5]" .. + return "size[8,5.5]" .. default.gui_bg .. default.gui_bg_img .. default.gui_slots .. "box[0,-0.1;7.2,0.5;#c6e8ff]" .. "label[0.2,-0.1;" .. minetest.colorize( "#000000", S("TA4 Move Controller")) .. "]" .. techage.wrench_image(7.4, -0.05) .. - "button[0.1,0.8;3.8,1;record;" .. S("Record") .. "]" .. - "button[4.1,0.8;3.8,1;done;" .. S("Done") .. "]" .. + "button[0.1,0.7;3.8,1;record;" .. S("Record") .. "]" .. + "button[4.1,0.7;3.8,1;done;" .. S("Done") .. "]" .. buttons .. - "label[0.3,4.3;" .. status .. "]" + "label[0.3,5.0;" .. status .. "]" end minetest.register_node("techage:ta4_movecontroller", { @@ -175,6 +178,9 @@ minetest.register_node("techage:ta4_movecontroller", { end elseif fields.reset then fly.reset_move(pos) + elseif fields.show then + local name = player:get_player_name() + mark.mark_positions(name, nvm.lpos1, 300) end end, @@ -263,6 +269,7 @@ techage.register_node({"techage:ta4_movecontroller"}, { on_node_load = function(pos, node) M(pos):set_string("teleport_mode", "") -- delete not working (legacy) op mode M(pos):set_string("status", "") + techage.get_nvm(pos).running = false end, })