diff --git a/hyperloop/elevator.lua b/hyperloop/elevator.lua index bf13343..719fe3d 100644 --- a/hyperloop/elevator.lua +++ b/hyperloop/elevator.lua @@ -212,8 +212,12 @@ minetest.register_node("hyperloop:shaft2", { light_source = 2, sunlight_propagates = true, is_ground_content = false, + ----- To be unbreakable ----- + on_blast = function() end, + on_destruct = function () end, + can_dig = function() return false end, diggable = false, - groups = {cracky = 1, not_in_creative_inventory=1}, + groups = {cracky = 1, not_in_creative_inventory=1, unbreakable=1}, sounds = default.node_sound_metal_defaults(), }) @@ -255,8 +259,12 @@ minetest.register_node("hyperloop:shaftA2", { light_source = 2, sunlight_propagates = true, is_ground_content = false, + ----- To be unbreakable ----- + on_blast = function() end, + on_destruct = function () end, + can_dig = function() return false end, diggable = false, - groups = {cracky = 1, not_in_creative_inventory=1}, + groups = {cracky = 1, not_in_creative_inventory=1, unbreakable=1}, sounds = default.node_sound_metal_defaults(), }) diff --git a/hyperloop/tube.lua b/hyperloop/tube.lua index 94e2f43..b1ad8b5 100644 --- a/hyperloop/tube.lua +++ b/hyperloop/tube.lua @@ -173,8 +173,12 @@ minetest.register_node("hyperloop:tubeS2", { light_source = 2, sunlight_propagates = true, is_ground_content = false, + ----- To be unbreakable ----- + on_blast = function() end, + on_destruct = function () end, + can_dig = function() return false end, diggable = false, - groups = {cracky = 1, not_in_creative_inventory=1}, + groups = {cracky = 1, not_in_creative_inventory=1, unbreakable=1}, sounds = default.node_sound_metal_defaults(), }) @@ -263,8 +267,12 @@ minetest.register_node("hyperloop:tubeA2", { light_source = 2, sunlight_propagates = true, is_ground_content = false, + ----- To be unbreakable ----- + on_blast = function() end, + on_destruct = function () end, + can_dig = function() return false end, diggable = false, - groups = {cracky = 1, not_in_creative_inventory=1}, + groups = {cracky = 1, not_in_creative_inventory=1, unbreakable=1}, sounds = default.node_sound_metal_defaults(), }) diff --git a/minecart/README.md b/minecart/README.md index f2f8747..622ebaf 100644 --- a/minecart/README.md +++ b/minecart/README.md @@ -44,6 +44,7 @@ The mod features are: - Ingame documentation (German and English), based on the mod "doc" - API to register carts from other mods - chat command '/mycart ' to output cart state and location +- Command interface for Techage (Lua and ICTA) and for Beduino Controllers Technical Background @@ -118,6 +119,45 @@ The "No speed limit" sign can be used to remove the speed limit. The speed limit signs must be placed next to the track so that they can be read from the cart. This allows different speeds in each direction of travel. +## Command Interface + +### Techage ICTA Controller + +The ICTA Controller support the conditions: + +- "read cart state" (function returns "stopped" or "running") +- "read cart location" (function returns the distance or the station/buffer name) + +See help page of the ICTA controller block. + +### Techage Lua Controller + +The Lua controller support the functions: + +- `$cart_state(num)` (function returns "stopped" or "running") +- `$cart_location(num)` (function returns the distance or the station/buffer name) + +See help page of the Lua controller block. + +### Cart Terminal + +The Cart Terminal has a Techage command interface with the commands: + +| Command | Data | Description | +| ---------- | ---------- | ------------------------------------------------------- | +| `state` | \ | Returns `unknown`, `stopped`, or `running` | +| `distance` | \ | Returns the distance from the cart to the Cart Terminal | + +### Beduino Controller + +The Cart Terminal has a Beduino command interface with the commands: + +| Command | Topic | Data | Response | Description | +| -------- | ----- | --------- | ---------- | ------------------------------------------------------- | +| State | 129 | [cart-id] | [state] | Returns 0 = UNKNOWN, 1 = STOPPED, 2 = RUNNING | +| Distance | 130 | [cart-id] | [distance] | Returns the distance from the cart to the Cart Terminal | + + Migration to v2 --------------- @@ -153,3 +193,4 @@ History Speed limit signs and cart terminal added 2021-09-02 V2.01 Chat command /stopcart added 2021-10-18 V2.02 Cart reproduction bug fixed +2023-01-04 V2.03 Techage and Beduino command interface added \ No newline at end of file diff --git a/minecart/beduino.lua b/minecart/beduino.lua new file mode 100644 index 0000000..9ecd2fa --- /dev/null +++ b/minecart/beduino.lua @@ -0,0 +1,93 @@ +--[[ + + Minecart + ======== + + Copyright (C) 2019-2023 Joachim Stolberg + + MIT + See license.txt for more information + +]]-- + +local minecart_lib = [[ +// Minecart library for reading status and distance +// from running carts. To do this, a cart terminal +// must be connected to an I/O Module. + +import "lib/techage.c" + +var payload[1]; +var resp[1]; + + +// Read cart state. +// Parameters: +// - ip_port: IOM port to the Cart Terminal +// - card_id: Cart number +// Function returns: +// - 0 for unknown/missing +// - 1 for stopped +// - 2 for running +func mc_get_state(io_port, cart_id) { + var sts; + + payload[0] = cart_id; + request_data(io_port, 129, payload, resp); + if(sts == 0) { + return resp[0]; + } + return 0; +} + +// Read cart distance. +// Parameters: +// - ip_port: IOM port to the Cart Terminal +// - card_id: Cart number +// Function returns the distance between +// Cart Terminal and cart in meter. +func mc_get_distance(io_port, cart_id) { + var sts; + + payload[0] = cart_id; + request_data(io_port, 130, payload, resp); + if(sts == 0) { + return resp[0]; + } + return 0; +} +]] + +local minecart_demo = [[ +import "sys/stdio.asm" +import "sys/os.c" +import "lib/minecart.c" + +func init() { + setstdout(1); // use terminal window for stdout + putstr("### Minecart Demo ###\n"); +} + +func cart_state(io_port, cart_id) { + putstr("Cart #"); + putnum(cart_id); + putstr(": state = "); + putnum(mc_get_state(io_port, cart_id)); + putstr(", distance = "); + putnum(mc_get_distance(io_port, cart_id)); + putstr("\n"); +} + +func loop() { + // Adapt IO port and cart ID to your needs + cart_state(0, 1); + sleep(20); +} +]] + +minetest.register_on_mods_loaded(function() + if minetest.global_exists("vm16") and minetest.global_exists("beduino") then + vm16.register_ro_file("beduino", "lib/minecart.c", minecart_lib) + vm16.register_ro_file("beduino", "demo/minecart.c", minecart_demo) + end +end) \ No newline at end of file diff --git a/minecart/hopperlib.lua b/minecart/hopperlib.lua index 6f2a583..d04ad05 100644 --- a/minecart/hopperlib.lua +++ b/minecart/hopperlib.lua @@ -94,8 +94,8 @@ function minecart.untake_items(pos, param2, stack) local def = RegisteredInventories[node.name] local inv = minetest.get_inventory({type="node", pos=npos}) - if def and inv and def.put_listname then - return inv:add_item(def.put_listname, stack) + if def and inv and def.take_listname then + return inv:add_item(def.take_listname, stack) elseif def and def.untake_item then return def.untake_item(npos, stack) else diff --git a/minecart/init.lua b/minecart/init.lua index 77b140d..e30d189 100644 --- a/minecart/init.lua +++ b/minecart/init.lua @@ -3,7 +3,7 @@ Minecart ======== - Copyright (C) 2019-2021 Joachim Stolberg + Copyright (C) 2019-2023 Joachim Stolberg MIT See license.txt for more information @@ -13,7 +13,7 @@ minecart = {} -- Version for compatibility checks, see readme.md/history -minecart.version = 2.02 +minecart.version = 2.03 minecart.hopper_enabled = minetest.settings:get_bool("minecart_hopper_enabled") ~= false minecart.teleport_enabled = minetest.settings:get_bool("minecart_teleport_enabled") == true @@ -39,6 +39,7 @@ dofile(MP .. "/protection.lua") dofile(MP .. "/signs.lua") dofile(MP .. "/terminal.lua") dofile(MP .. "/pusher.lua") +dofile(MP .. "/beduino.lua") if minecart.hopper_enabled then dofile(MP .. "/hopper.lua") diff --git a/minecart/monitoring.lua b/minecart/monitoring.lua index 82e3b03..c3e0242 100644 --- a/minecart/monitoring.lua +++ b/minecart/monitoring.lua @@ -3,7 +3,7 @@ Minecart ======== - Copyright (C) 2019-2021 Joachim Stolberg + Copyright (C) 2019-2023 Joachim Stolberg MIT See license.txt for more information @@ -90,7 +90,22 @@ local function get_cart_state_and_loc(name, userID, query_pos) end end return "unknown", 0, "unknown" -end +end + +-- Return the cart distance to the query_pos. +local function get_cart_distance(name, userID, query_pos) + if tCartsOnRail[name] and tCartsOnRail[name][userID] then + local cart = tCartsOnRail[name][userID] + if cart.last_pos or cart.pos then + if cart.objID == 0 then -- stopped + return math.floor(vector.distance(cart.pos or cart.last_pos, query_pos)) + else + return math.floor(vector.distance(cart.last_pos or cart.pos, query_pos)) + end + end + end + return 0 +end local function get_cart_info(owner, userID, query_pos) local state, loc, name = get_cart_state_and_loc(owner, userID, query_pos) @@ -318,6 +333,10 @@ function minecart.cmnd_cart_location(name, userID, query_pos) return loc end +function minecart.cmnd_cart_distance(name, userID, query_pos) + return get_cart_distance(name, userID, query_pos) +end + function minecart.get_cart_list(pos, name) local userIDs = {} local carts = {} diff --git a/minecart/terminal.lua b/minecart/terminal.lua index 60420de..64e4b71 100644 --- a/minecart/terminal.lua +++ b/minecart/terminal.lua @@ -3,7 +3,7 @@ Minecart ======== - Copyright (C) 2019-2021 Joachim Stolberg + Copyright (C) 2019-2023 Joachim Stolberg MIT See license.txt for more information @@ -59,6 +59,11 @@ minetest.register_node("minecart:terminal", { local meta = M(pos) meta:set_string("owner", placer:get_player_name()) meta:set_string("formspec", formspec(pos, "")) + if minetest.global_exists("techage") then + local number = techage.add_node(pos, "minecart:terminal") + meta:set_string("node_number", number) + meta:set_string("infotext", "Cart Terminal " .. number) + end minetest.get_node_timer(pos):start(2) end, @@ -88,3 +93,42 @@ minetest.register_craft({ {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"}, }, }) + +minetest.register_on_mods_loaded(function() + if minetest.global_exists("techage") then + techage.register_node({"minecart:terminal"}, { + on_recv_message = function(pos, src, topic, payload) + local number = tonumber(payload) + if number then + local owner = M(pos):get_string("owner") + if topic == "state" then + return minecart.cmnd_cart_state(owner, number) + elseif topic == "distance" then + return minecart.cmnd_cart_distance(owner, number, pos) + else + return "unsupported" + end + end + end, + on_beduino_receive_cmnd = function(pos, src, topic, payload) + return 2 -- unknown or invalid topic + end, + on_beduino_request_data = function(pos, src, topic, payload) + if topic == 128 then + return 0, "minecart:terminal" + elseif topic == 129 then -- state + local owner = M(pos):get_string("owner") + local STATE = {unknown = 0, stopped = 1, running = 2} + local state = STATE[minecart.cmnd_cart_state(owner, payload[1])] or 0 + return 0, {state} + elseif topic == 130 then -- distance + local owner = M(pos):get_string("owner") + local dist = minecart.cmnd_cart_distance(owner, payload[1], pos) + return 0, {dist} + else + return 2, "" -- topic is unknown or invalid + end + end, + }) + end +end) \ No newline at end of file diff --git a/signs_bot/cmd_move.lua b/signs_bot/cmd_move.lua index df51d01..abd0497 100644 --- a/signs_bot/cmd_move.lua +++ b/signs_bot/cmd_move.lua @@ -268,9 +268,11 @@ signs_bot.register_botcommand("fall_down", { local sts, pos3 = minetest.line_of_sight(pos1, pos2) if sts == false then sts, _ = minetest.spawn_falling_node(mem.robot_pos) + mem.stored_node = get_node_lvm(pos3) + minetest.swap_node(pos3, {name="air"}) if sts then mem.bot_falling = 2 - mem.robot_pos = {x=pos3.x, y=pos3.y+1, z=pos3.z} + mem.robot_pos = {x=pos3.x, y=pos3.y, z=pos3.z} return signs_bot.BUSY end end diff --git a/signs_bot/nodes.lua b/signs_bot/nodes.lua index c08c86e..83983a9 100644 --- a/signs_bot/nodes.lua +++ b/signs_bot/nodes.lua @@ -52,7 +52,7 @@ end if farming.mod == "redo" then local fp_grows = function(def, step) local crop = def.crop .. "_" .. step - local node = minetest.registered_nodes[crop] + local node = minetest.registered_nodes[crop] if node then fp(def.seed, def.crop .. "_1", crop, def.trellis) return node.groups and node.groups.growing @@ -63,7 +63,9 @@ if farming.mod == "redo" then -- everything except cocoa (these can only be placed on jungletree) if name ~= "farming:cocoa_beans" then local step = def.steps - while fp_grows(def, step) do step = step + 1 end + if step then + while fp_grows(def, step) do step = step + 1 end + end end end end diff --git a/techage/README.md b/techage/README.md index 1e6fcd3..fc4d2ef 100644 --- a/techage/README.md +++ b/techage/README.md @@ -89,6 +89,19 @@ Available worlds will be converted to 'lsqlite3', but there is no way back, so: ### History +**2023-02-04 V1.10** +- Improve flycontroller +- Remove handover for movecontroller +- Rename "techage:signal_lamp" to "techage:color_lamp" +- Rename "techage:signal_lamp2" to "techage:color_lamp2" +- Add countdown mode to TA4 Detector +- Adapt to new beduino and minecart versions +- Improve manuals +- flycontroller/movecontroller: Allow moving blocks through unloaded areas +- playerdetector: Add wrench menu to configure search radius +- Default furnace: Don't use items filled from the top as fuel +- Many further improvements and bug fixes from joe7575 and Niklp09 + **2022-09-03 V1.09** - Change the way items are pushed - Add "Flow Limiter" mode to TA4 pump and TA4 pusher diff --git a/techage/basic_machines/forceload.lua b/techage/basic_machines/forceload.lua index adecd64..3d7d039 100644 --- a/techage/basic_machines/forceload.lua +++ b/techage/basic_machines/forceload.lua @@ -266,6 +266,11 @@ if techage.max_num_forceload_blocks > 0 then output = "techage:forceloadtile", recipe = {"techage:forceload"}, }) + minetest.register_craft({ + type = "shapeless", + output = "techage:forceload", + recipe = {"techage:forceloadtile"}, + }) end minetest.register_on_joinplayer(function(player) diff --git a/techage/basic_machines/grinder.lua b/techage/basic_machines/grinder.lua index a3cc579..77bbe8d 100644 --- a/techage/basic_machines/grinder.lua +++ b/techage/basic_machines/grinder.lua @@ -17,7 +17,7 @@ local M = minetest.get_meta local S = techage.S -- Consumer Related Data -local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end +local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer or {} end local STANDBY_TICKS = 3 local COUNTDOWN_TICKS = 4 diff --git a/techage/basic_machines/pusher.lua b/techage/basic_machines/pusher.lua index 70883b7..b706d80 100644 --- a/techage/basic_machines/pusher.lua +++ b/techage/basic_machines/pusher.lua @@ -341,7 +341,7 @@ local tubing = { CRD(pos).State:stop(pos, nvm) config_item(pos, payload) return 0 - elseif topic == 68 then -- Set push limit + elseif topic == 68 or topic == 20 then -- Set push limit local nvm = techage.get_nvm(pos) CRD(pos).State:stop(pos, nvm) set_limit(pos, nvm, payload[1]) diff --git a/techage/basic_machines/quarry.lua b/techage/basic_machines/quarry.lua index 14a4796..b244222 100644 --- a/techage/basic_machines/quarry.lua +++ b/techage/basic_machines/quarry.lua @@ -196,6 +196,9 @@ local function quarry_task(pos, crd, nvm) pos1.y = y_curr pos2.y = y_curr + -- Restarting the server can detach the coroutine data. + -- Therefore, read nvm again. + nvm = techage.get_nvm(pos) nvm.level = y_first - y_curr if minetest.is_area_protected(pos1, pos2, owner, 5) then @@ -382,6 +385,11 @@ local tubing = { end, on_node_load = function(pos) CRD(pos).State:on_node_load(pos) + local nvm = techage.get_nvm(pos) + if nvm.techage_state == techage.RUNNING then + stop_sound(pos) + play_sound(pos) + end end, } diff --git a/techage/basic_machines/ta4_chest.lua b/techage/basic_machines/ta4_chest.lua index eb91de0..b3011c9 100644 --- a/techage/basic_machines/ta4_chest.lua +++ b/techage/basic_machines/ta4_chest.lua @@ -346,6 +346,20 @@ local function count_number_of_chests(pos) M(pos):set_int("stacksize", STACK_SIZE * cnt) end +local function dummy_chest_behind(pos, node) + local dir = techage.side_to_outdir("B", node.param2) + local pos1 = tubelib2.get_pos(pos, dir) + node = techage.get_node_lvm(pos1) + return node.name == "techage:ta4_chest_dummy" +end + +local function part_of_a_chain(pos, node) + local dir = techage.side_to_outdir("F", node.param2) + local pos1 = tubelib2.get_pos(pos, dir) + node = techage.get_node_lvm(pos1) + return node.name == "techage:ta4_chest_dummy" or node.name == "techage:ta4_chest" +end + local function search_chest_in_front(pos, node) local dir = techage.side_to_outdir("F", node.param2) local pos1 = tubelib2.get_pos(pos, dir) @@ -529,6 +543,10 @@ minetest.register_node("techage:ta4_chest", { after_place_node = function(pos, placer) local node = minetest.get_node(pos) + if dummy_chest_behind(pos, node) then + minetest.remove_node(pos) + return true + end if search_chest_in_front(pos, node) then node.name = "techage:ta4_chest_dummy" minetest.swap_node(pos, node) @@ -667,6 +685,18 @@ techage.register_node({"techage:ta4_chest_dummy"}, { end }) +minetest.register_lbm({ + label = "Repair Dummy Chests", + name = "techage:chest_dummy", + nodenames = {"techage:ta4_chest_dummy"}, + run_at_every_load = true, + action = function(pos, node) + if not part_of_a_chain(pos, node) then + minetest.swap_node(pos, {name = "techage:ta4_chest", param2 = node.param2}) + end + end, +}) + minetest.register_craft({ type = "shapeless", output = "techage:ta4_chest", diff --git a/techage/basis/fly_lib.lua b/techage/basis/fly_lib.lua index 61107d1..6cfcf28 100644 --- a/techage/basis/fly_lib.lua +++ b/techage/basis/fly_lib.lua @@ -3,7 +3,7 @@ TechAge ======= - Copyright (C) 2020-2022 Joachim Stolberg + Copyright (C) 2020-2023 Joachim Stolberg AGPL v3 See LICENSE.txt for more information @@ -30,26 +30,6 @@ local function lvect_add_vec(lvect1, offs) return lvect2 end -local function lvect_add(lvect1, lvect2) - if not lvect1 or not lvect2 then return end - - local lvect3 = {} - for i, v in ipairs(lvect1) do - lvect3[#lvect3 + 1] = vector.add(v, lvect2[i]) - end - return lvect3 -end - -local function lvect_subtract(lvect1, lvect2) - if not lvect1 or not lvect2 then return end - - local lvect3 = {} - for i, v in ipairs(lvect1) do - lvect3[#lvect3 + 1] = vector.subtract(v, lvect2[i]) - end - return lvect3 -end - -- yaw in radiant local function rotate(v, yaw) local sinyaw = math.sin(yaw) @@ -61,7 +41,6 @@ local function set_node(item) local dest_pos = item.dest_pos local name = item.name or "air" local param2 = item.param2 or 0 - local metadata = item.metadata or {} local nvm = techage.get_nvm(item.base_pos) local node = techage.get_node_lvm(dest_pos) local ndef1 = minetest.registered_nodes[name] @@ -100,7 +79,7 @@ local function push(item) queue[last] = item end -local function pop(nvm, time) +local function pop() if first > last then return end local item = queue[first] queue[first] = nil -- to allow garbage collection @@ -408,6 +387,9 @@ local function entity_to_node(pos, obj) end end +-- Create a node entitiy. +-- * base_pos is controller block related +-- * start_pos and dest_pos are entity positions local function node_to_entity(base_pos, start_pos, dest_pos) local meta = M(start_pos) local node, metadata @@ -464,9 +446,9 @@ local function determine_dir(pos1, pos2) return {x=0, y=0, z=0} end -local function move_entity(obj, dest_pos, dir, is_corner) +local function move_entity(obj, next_pos, dir, is_corner) local self = obj:get_luaentity() - self.dest_pos = dest_pos + self.next_pos = next_pos self.dir = dir if is_corner then local vel = vector.multiply(dir, math.min(CORNER_SPEED, self.max_speed)) @@ -477,7 +459,7 @@ local function move_entity(obj, dest_pos, dir, is_corner) end local function moveon_entity(obj, self, pos1) - local pos2 = next_path_pos(pos1, self.lpath, self.path_idx) + local pos2 = next_path_pos(pos1, self.lmove, self.path_idx) if pos2 then self.path_idx = self.path_idx + 1 local dir = determine_dir(pos1, pos2) @@ -486,43 +468,6 @@ local function moveon_entity(obj, self, pos1) end end --- Handover the entity to the next movecontroller -local function handover_to(obj, self, pos1) - if self.handover then - local info = techage.get_node_info(self.handover) - if info and info.name == "techage:ta4_movecontroller" then - local meta = M(info.pos) - if self.move2to1 then - self.handover = meta:contains("handoverA") and meta:get_string("handoverA") or nil - else - self.handover = meta:contains("handoverB") and meta:get_string("handoverB") or nil - end - - self.lpath = flylib.to_path(meta:get_string("path")) - if pos1 and self.lpath then - self.path_idx = 2 - if self.move2to1 then - self.lpath[1] = vector.multiply(self.lpath[1], - 1) - end - local pos2 = next_path_pos(pos1, self.lpath, 1) - local dir = determine_dir(pos1, pos2) - if not self.handover then - local nvm = techage.get_nvm(info.pos) - nvm.lpos1 = nvm.lpos1 or {} - if self.move2to1 then - nvm.lpos1[self.pos1_idx] = pos2 - - else - nvm.lpos1[self.pos1_idx] = pos1 - end - end - move_entity(obj, pos2, dir) - return true - end - end - end -end - minetest.register_entity("techage:move_item", { initial_properties = { pointable = true, @@ -538,62 +483,47 @@ minetest.register_entity("techage:move_item", { on_step = function(self, dtime, moveresult) local stop_obj = function(obj, self) - local dest_pos = self.dest_pos - obj:move_to(self.dest_pos, true) + local next_pos = self.next_pos + obj:move_to(self.next_pos, true) obj:set_acceleration({x=0, y=0, z=0}) obj:set_velocity({x=0, y=0, z=0}) - self.dest_pos = nil + self.next_pos = nil self.old_dist = nil - return dest_pos + return next_pos end - if self.dest_pos then + if self.next_pos then local obj = self.object local pos = obj:get_pos() - local dist = vector.distance(pos, self.dest_pos) + local dist = vector.distance(pos, self.next_pos) local speed = calc_speed(obj:get_velocity()) self.old_dist = self.old_dist or dist - -- Landing - if self.lpath and self.lpath[self.path_idx] then + if self.lmove and self.lmove[self.path_idx] then if dist < 1 or dist > self.old_dist then - local dest_pos = stop_obj(obj, self) - if not moveon_entity(obj, self, dest_pos) then - minetest.after(0.5, entity_to_node, dest_pos, obj) + -- change of direction + local next_pos = stop_obj(obj, self) + if not moveon_entity(obj, self, next_pos) then + minetest.after(0.5, entity_to_node, next_pos, obj) end return end - elseif self.handover and dist < 0.2 or dist > self.old_dist then - local dest_pos = stop_obj(obj, self) - if not handover_to(obj, self, dest_pos) then - minetest.after(0.5, entity_to_node, dest_pos, obj) - end + elseif dist < 0.05 or dist > self.old_dist then + -- Landing + local next_pos = stop_obj(obj, self) + local dest_pos = self.item.dest_pos or next_pos + minetest.after(0.5, entity_to_node, dest_pos, obj) return - else - if dist < 0.05 or dist > self.old_dist then - local dest_pos = stop_obj(obj, self) - minetest.after(0.5, entity_to_node, dest_pos, obj) - return - end end self.old_dist = dist -- Braking or limit max speed - if self.handover then - if speed > (dist * 4) or speed > self.max_speed then - speed = math.min(speed, math.max(dist * 4, MIN_SPEED)) - local vel = vector.multiply(self.dir,speed) - obj:set_velocity(vel) - obj:set_acceleration({x=0, y=0, z=0}) - end - else - if speed > (dist * 2) or speed > self.max_speed then - speed = math.min(speed, math.max(dist * 2, MIN_SPEED)) - local vel = vector.multiply(self.dir,speed) - obj:set_velocity(vel) - obj:set_acceleration({x=0, y=0, z=0}) - end + if speed > (dist * 2) or speed > self.max_speed then + speed = math.min(speed, math.max(dist * 2, MIN_SPEED)) + local vel = vector.multiply(self.dir,speed) + obj:set_velocity(vel) + obj:set_acceleration({x=0, y=0, z=0}) end monitoring_trigger_entity(self.item) @@ -618,30 +548,34 @@ local function is_simple_node(pos) return not techage.is_air_like(node.name) and techage.can_dig_node(node.name, ndef) end -local function move_node(pos, pos1_idx, start_pos, lpath, max_speed, height, move2to1, handover, cpos) - local pos2 = next_path_pos(start_pos, lpath, 1) +-- Move node from 'pos1' to the destination, calculated by means of 'lmove' +-- * pos and meta are controller block related +-- * lmove is the movement as a list of `moves` +-- * height is move block height as value between 0 and 1 and used to calculate the offset +-- for the attached object (player). +local function move_node(pos, meta, pos1, lmove, max_speed, height) + local pos2 = next_path_pos(pos1, lmove, 1) + local offs = dest_offset(lmove) + local dest_pos = vector.add(pos1, offs) -- optional for non-player objects - local yoffs = M(pos):get_float("offset") + local yoffs = meta:get_float("offset") if pos2 then - local dir = determine_dir(start_pos, pos2) - local obj = node_to_entity(pos, start_pos, pos2) + local dir = determine_dir(pos1, pos2) + local obj = node_to_entity(pos, pos1, dest_pos) if obj then local offs = {x=0, y=height or 1, z=0} - attach_objects(start_pos, offs, obj, yoffs) + attach_objects(pos1, offs, obj, yoffs) if dir.y == 0 then if (dir.x ~= 0 and dir.z == 0) or (dir.x == 0 and dir.z ~= 0) then - attach_objects(start_pos, dir, obj, yoffs) + attach_objects(pos1, dir, obj, yoffs) end end local self = obj:get_luaentity() self.path_idx = 2 - self.pos1_idx = pos1_idx - self.lpath = lpath + self.lmove = lmove self.max_speed = max_speed - self.move2to1 = move2to1 - self.handover = handover self.yoffs = yoffs move_entity(obj, pos2, dir) return true @@ -651,14 +585,20 @@ local function move_node(pos, pos1_idx, start_pos, lpath, max_speed, height, mov end end -local function move_nodes(pos, meta, nvm, lpath, max_speed, height, move2to1, handover) +-- Move the nodes from nvm.lpos1 to nvm.lpos2 +-- * nvm.lpos1 is a list of nodes +-- * lmove is the movement as a list of `moves` +-- * pos, meta, and nvm are controller block related +--- height is move block height as value between 0 and 1 and used to calculate the offset +-- for the attached object (player). +local function multi_move_nodes(pos, meta, nvm, lmove, max_speed, height, move2to1) local owner = meta:get_string("owner") - techage.counting_add(owner, #lpath, #nvm.lpos1 * #lpath) + techage.counting_add(owner, #lmove, #nvm.lpos1 * #lmove) for idx = 1, #nvm.lpos1 do local pos1 = nvm.lpos1[idx] local pos2 = nvm.lpos2[idx] - --print("move_nodes", idx, P2S(pos1), P2S(pos2)) + --print("multi_move_nodes", idx, P2S(pos1), P2S(pos2)) if move2to1 then pos1, pos2 = pos2, pos1 @@ -666,7 +606,7 @@ local function move_nodes(pos, meta, nvm, lpath, max_speed, height, move2to1, ha 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 - if move_node(pos, idx, pos1, lpath, max_speed, height, move2to1, handover) == false then + if move_node(pos, meta, pos1, lmove, max_speed, height) == false then meta:set_string("status", S("No valid node at the start position")) return false end @@ -691,21 +631,27 @@ local function move_nodes(pos, meta, nvm, lpath, max_speed, height, move2to1, ha return true end --- Move nodes from lpos1 by the given x/y/z 'line' -local function move_nodes2(pos, meta, lpos1, line, max_speed, height) +-- Move the nodes from lpos1 to lpos2. +-- * lpos1 is a list of nodes +-- * lpos2 = lpos1 + move +-- * pos and meta are controller block related +-- * height is move block height as value between 0 and 1 and used to calculate the offset +-- for the attached object (player). +local function move_nodes(pos, meta, lpos1, move, max_speed, height) local owner = meta:get_string("owner") + lpos1 = lpos1 or {} techage.counting_add(owner, #lpos1) local lpos2 = {} for idx = 1, #lpos1 do local pos1 = lpos1[idx] - local pos2 = vector.add(lpos1[idx], line) + local pos2 = vector.add(lpos1[idx], move) lpos2[idx] = pos2 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, idx, pos1, {line}, max_speed, height, false, false) + move_node(pos, meta, pos1, {move}, max_speed, height) else if not is_simple_node(pos1) then meta:set_string("status", S("No valid node at the start position")) @@ -728,13 +674,14 @@ local function move_nodes2(pos, meta, lpos1, line, max_speed, height) return true, lpos2 end +-- move2to1 is the direction and is true for 'from pos2 to pos1' +-- Move path and other data is stored as meta data of pos function flylib.move_to_other_pos(pos, move2to1) local meta = M(pos) local nvm = techage.get_nvm(pos) - local lpath, err = flylib.to_path(meta:get_string("path")) or {} + local lmove, err = flylib.to_path(meta:get_string("path")) or {} local max_speed = meta:contains("max_speed") and meta:get_int("max_speed") or MAX_SPEED local height = meta:contains("height") and meta:get_float("height") or 1 - local handover if err or nvm.running then return false end @@ -742,33 +689,28 @@ function flylib.move_to_other_pos(pos, move2to1) max_speed = techage.in_range(max_speed, MIN_SPEED, MAX_SPEED) nvm.lpos1 = nvm.lpos1 or {} - local offs = dest_offset(lpath) + local offs = dest_offset(lmove) if move2to1 then - lpath = reverse_path(lpath) + lmove = reverse_path(lmove) end -- calc destination positions nvm.lpos2 = lvect_add_vec(nvm.lpos1, offs) - if move2to1 then - handover = meta:contains("handoverA") and meta:get_string("handoverA") or nil - else - handover = meta:contains("handoverB") and meta:get_string("handoverB") or nil - end - nvm.running = move_nodes(pos, meta, nvm, lpath, max_speed, height, move2to1, handover) + nvm.running = multi_move_nodes(pos, meta, nvm, lmove, max_speed, height, move2to1) nvm.moveBA = nvm.running and not move2to1 return nvm.running end -function flylib.move_to(pos, line) +-- `move` the movement as a vector +function flylib.move_to(pos, move) local meta = M(pos) local nvm = techage.get_nvm(pos) local height = techage.in_range(meta:contains("height") and meta:get_float("height") or 1, 0, 1) local max_speed = meta:contains("max_speed") and meta:get_int("max_speed") or MAX_SPEED - local resp if nvm.running then return false end - nvm.running, nvm.lastpos = move_nodes2(pos, meta, nvm.lastpos or nvm.lpos1, line, max_speed, height) + nvm.running, nvm.lastpos = move_nodes(pos, meta, nvm.lastpos or nvm.lpos1, move, max_speed, height) return nvm.running end @@ -782,27 +724,28 @@ function flylib.reset_move(pos) if nvm.lpos1 and nvm.lpos1[1] then local move = vector.subtract(nvm.lpos1[1], (nvm.lastpos or nvm.lpos1)[1]) - local resp - nvm.running, nvm.lastpos = move_nodes2(pos, meta, nvm.lastpos or nvm.lpos1, move, max_speed, height) + nvm.running, nvm.lastpos = move_nodes(pos, meta, nvm.lastpos or nvm.lpos1, move, max_speed, height) return nvm.running end return false end +-- pos is the controller block pos +-- lpos is a list of node positions to be moved -- rot is one of "l", "r", "2l", "2r" --- cpos is the center pos (optional) -function flylib.rotate_nodes(pos, posses1, rot) +function flylib.rotate_nodes(pos, lpos, rot) local meta = M(pos) local owner = meta:get_string("owner") + -- cpos is the center pos local cpos = meta:contains("center") and flylib.to_vector(meta:get_string("center")) - local posses2 = techage.rotate_around_center(posses1, rot, cpos) + local lpos2 = techage.rotate_around_center(lpos, rot, cpos) local param2 local nodes2 = {} - techage.counting_add(owner, #posses1 * 2) + techage.counting_add(owner, #lpos * 2) - for i, pos1 in ipairs(posses1) do + for i, pos1 in ipairs(lpos) do local node = techage.get_node_lvm(pos1) if rot == "l" then param2 = techage.param2_turn_right(node.param2) @@ -813,7 +756,7 @@ function flylib.rotate_nodes(pos, posses1, rot) end if not minetest.is_protected(pos1, owner) and is_simple_node(pos1) then minetest.remove_node(pos1) - nodes2[#nodes2 + 1] = {pos = posses2[i], name = node.name, param2 = param2} + nodes2[#nodes2 + 1] = {pos = lpos2[i], name = node.name, param2 = param2} end end for _,item in ipairs(nodes2) do @@ -821,7 +764,7 @@ function flylib.rotate_nodes(pos, posses1, rot) minetest.add_node(item.pos, {name = item.name, param2 = item.param2}) end end - return posses2 + return lpos2 end function flylib.exchange_node(pos, name, param2) diff --git a/techage/basis/node_states.lua b/techage/basis/node_states.lua index 6febacf..ac7373f 100644 --- a/techage/basis/node_states.lua +++ b/techage/basis/node_states.lua @@ -3,7 +3,7 @@ TechAge ======= - Copyright (C) 2019-2022 Joachim Stolberg + Copyright (C) 2019-2023 Joachim Stolberg AGPL v3 See LICENSE.txt for more information @@ -520,6 +520,20 @@ function NodeStates:on_beduino_request_data(pos, topic, payload) end end +function NodeStates.get_beduino_state(pos) + local node = minetest.get_node(pos) + local nvm = techage.get_nvm(pos) + if node.name == "ignore" then -- unloaded node? + return 0, {techage.UNLOADED} + elseif nvm.techage_state == RUNNING then + local ttl = (nvm.last_active or 0) + MAX_CYCLE_TIME + if ttl < minetest.get_gametime() then + return 0, {techage.INACTIVE} + end + end + return 0, {nvm.techage_state or STOPPED} +end + -- restart timer function NodeStates:on_node_load(pos) local nvm = techage.get_nvm(pos) diff --git a/techage/beduino/kv_store.lua b/techage/beduino/kv_store.lua index 81424c2..9f565b7 100644 --- a/techage/beduino/kv_store.lua +++ b/techage/beduino/kv_store.lua @@ -55,6 +55,6 @@ minetest.register_on_mods_loaded(function() beduino.lib.register_SystemHandler(0x140, ta_kv_init) beduino.lib.register_SystemHandler(0x141, ta_kv_add) beduino.lib.register_SystemHandler(0x142, ta_kv_get) - vm16.register_ro_file("beduino", "ta_kvstore.c", kvstore_c) + vm16.register_ro_file("beduino", "lib/ta_kvstore.c", kvstore_c) end end) diff --git a/techage/digtron/battery.lua b/techage/digtron/battery.lua index 515c314..5e5b6c0 100644 --- a/techage/digtron/battery.lua +++ b/techage/digtron/battery.lua @@ -30,7 +30,7 @@ local TOTAL_MAX = INV_SIZE * FUEL_STACK_MAX local function count_coal(metadata) local total = 0 - for _,stack in pairs(metadata.inventory.fuel) do + for _,stack in pairs(metadata.inventory.fuel or {}) do total = total + stack:get_count() end return total diff --git a/techage/doc/items.lua b/techage/doc/items.lua index 9d85470..fa08dcb 100644 --- a/techage/doc/items.lua +++ b/techage/doc/items.lua @@ -115,7 +115,7 @@ techage.Items = { ta3_sequencer = "techage:ta3_sequencer", ta3_timer = "techage:ta3_timer", ta3_terminal = "techage:terminal2", - ta3_signallamp = "techage:signal_lamp_off", + ta3_colorlamp = "techage:color_lamp_off", ta3_doorblock = "techage:doorblock20", ta3_programmer = "techage:programmer", ta3_doorcontroller = "techage:ta3_doorcontroller", diff --git a/techage/doc/manual_DE.lua b/techage/doc/manual_DE.lua index 5b06703..99c276f 100644 --- a/techage/doc/manual_DE.lua +++ b/techage/doc/manual_DE.lua @@ -113,7 +113,7 @@ techage.manual_DE.aTitel = { "3,TA3 Sequenzer / Sequencer", "3,TA3 Timer", "3,TA3 Terminal", - "3,TechAge Signallampe / Signal Lamp", + "3,TechAge Farblampe / Color Lamp", "3,Tür/Tor Blöcke / Door/Gate Blocks", "3,TA3 Tür Controller / Door Controller", "3,TA3 Tür Controller II / Door Controller II", @@ -1133,7 +1133,7 @@ techage.manual_DE.aText = { "\n".. "\n".. "\n", - "Die Signallampe kann mit 'on'/'off' Kommando ein- bzw. ausgeschaltet werden. Diese Lampe braucht keinen Strom und\n".. + "Die Farblampe kann mit 'on'/'off' Kommando ein- bzw. ausgeschaltet werden. Diese Lampe braucht keinen Strom und\n".. "kann mit der Spritzpistole aus der Mod \"Unified Dyes\" und über Lua/Beduino Kommandos eingefärbt werden.\n".. "\n".. "Mit dem Chat-Kommando '/ta_color' wird die Farbpalette mit den Werten für die Lua/Beduino Kommandos angezeigt und mit '/ta_send color ' kann die Farbe geändert werden.\n".. @@ -2282,7 +2282,7 @@ techage.manual_DE.aItemName = { "ta3_sequencer", "ta3_timer", "ta3_terminal", - "ta3_signallamp", + "ta3_colorlamp", "ta3_doorblock", "ta3_doorcontroller", "ta3_doorcontroller", diff --git a/techage/doc/manual_EN.lua b/techage/doc/manual_EN.lua index b10047a..7766052 100644 --- a/techage/doc/manual_EN.lua +++ b/techage/doc/manual_EN.lua @@ -113,7 +113,7 @@ techage.manual_EN.aTitel = { "3,TA3 Sequencer", "3,TA3 Timer", "3,TA3 Terminal", - "3,TechAge Signal Lamp", + "3,TechAge Color Lamp", "3,Door/Gate Blocks", "3,TA3 Door Controller", "3,TA3 Door Controller II", @@ -2286,7 +2286,7 @@ techage.manual_EN.aItemName = { "ta3_sequencer", "ta3_timer", "ta3_terminal", - "ta3_signallamp", + "ta3_colorlamp", "ta3_doorblock", "ta3_doorcontroller", "ta3_doorcontroller", diff --git a/techage/fusion_reactor/controller.lua b/techage/fusion_reactor/controller.lua index 653602d..cc6f9e5 100644 --- a/techage/fusion_reactor/controller.lua +++ b/techage/fusion_reactor/controller.lua @@ -217,7 +217,6 @@ minetest.register_node("techage:ta5_fr_controller_pas", { on_timer = node_timer, after_dig_node = after_dig_node, on_receive_fields = on_receive_fields, - drawtype = "nodebox", paramtype2 = "facedir", groups = {choppy=2, cracky=2, crumbly=2}, is_ground_content = false, @@ -257,7 +256,6 @@ minetest.register_node("techage:ta5_fr_controller_act", { on_timer = node_timer, after_dig_node = after_dig_node, on_receive_fields = on_receive_fields, - drawtype = "nodebox", paramtype2 = "facedir", groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1}, drop = "", diff --git a/techage/fusion_reactor/magnet.lua b/techage/fusion_reactor/magnet.lua index 66889cf..9427577 100644 --- a/techage/fusion_reactor/magnet.lua +++ b/techage/fusion_reactor/magnet.lua @@ -54,7 +54,6 @@ minetest.register_node("techage:ta5_magnet1", { Cable:after_dig_node(pos) techage.del_mem(pos) end, - drawtype = "nodebox", paramtype2 = "facedir", groups = {choppy=2, cracky=2, crumbly=2}, is_ground_content = false, @@ -88,7 +87,6 @@ minetest.register_node("techage:ta5_magnet2", { Cable:after_dig_node(pos) techage.del_mem(pos) end, - drawtype = "nodebox", paramtype2 = "facedir", groups = {choppy=2, cracky=2, crumbly=2}, is_ground_content = false, diff --git a/techage/init.lua b/techage/init.lua index a17e3ba..445c829 100644 --- a/techage/init.lua +++ b/techage/init.lua @@ -3,7 +3,7 @@ TechAge ======= - Copyright (C) 2019-2022 Joachim Stolberg + Copyright (C) 2019-2023 Joachim Stolberg AGPL v3 See LICENSE.txt for more information @@ -13,7 +13,7 @@ techage = {} -- Version for compatibility checks, see readme.md/history -techage.version = 1.08 +techage.version = 1.10 if minetest.global_exists("tubelib") then minetest.log("error", "[techage] Techage can't be used together with the mod tubelib!") @@ -27,8 +27,8 @@ elseif minetest.global_exists("techpack") then elseif minetest.global_exists("tubelib2") and tubelib2.version < 2.2 then minetest.log("error", "[techage] Techage requires tubelib2 version 2.2 or newer!") return -elseif minetest.global_exists("minecart") and minecart.version < 1.08 then - minetest.log("error", "[techage] Techage requires minecart version 1.08 or newer!") +elseif minetest.global_exists("minecart") and minecart.version < 2.03 then + minetest.log("error", "[techage] Techage requires minecart version 2.03 or newer!") return elseif minetest.global_exists("lcdlib") and lcdlib.version < 1.01 then minetest.log("error", "[techage] Techage requires lcdlib version 1.01 or newer!") diff --git a/techage/items/cement.lua b/techage/items/cement.lua index ccb8b6f..8d9d2fe 100644 --- a/techage/items/cement.lua +++ b/techage/items/cement.lua @@ -43,6 +43,13 @@ else sounds = default.node_sound_stone_defaults(), }) + -- Needs to be a techage recipe, not to overwrite the clay/bakedclay recipe + techage.furnace.register_recipe({ + output = "techage:cement_block", + recipe = { + "default:clay", + }, + }) techage.add_grinder_recipe({input="techage:cement_block", output="techage:cement_powder"}) techage.add_grinder_recipe({input="bakedclay:white", output="techage:cement_powder"}) end diff --git a/techage/items/silicon.lua b/techage/items/silicon.lua index 95aeea6..5c89005 100644 --- a/techage/items/silicon.lua +++ b/techage/items/silicon.lua @@ -30,16 +30,16 @@ if minetest.global_exists("mesecon") then }, time = 6, }) -else - techage.furnace.register_recipe({ - output = "techage:ta4_silicon_wafer 16", - recipe = { - "basic_materials:silicon", - "basic_materials:silicon", - "basic_materials:silicon", - "techage:baborium_ingot" - }, - time = 6, - }) end +techage.furnace.register_recipe({ + output = "techage:ta4_silicon_wafer 16", + recipe = { + "basic_materials:silicon", + "basic_materials:silicon", + "basic_materials:silicon", + "techage:baborium_ingot" + }, + time = 6, +}) + diff --git a/techage/lamps/growlight.lua b/techage/lamps/growlight.lua index f4b1ac3..119bcb0 100644 --- a/techage/lamps/growlight.lua +++ b/techage/lamps/growlight.lua @@ -171,11 +171,26 @@ minetest.register_craft({ }, }) +local function contains(table, element) + for _, value in pairs(table) do + if value == element then + return true + end + end + return false +end + function techage.register_flower(name) + if contains(Flowers, name) then + return + end Flowers[#Flowers+1] = name end function techage.register_plant(name) + if contains(Plants, name) then + return + end Plants[name] = true end @@ -201,4 +216,5 @@ minetest.after(1, function() end end end + -- print(dump(Flowers)) end) diff --git a/techage/liquids/pump.lua b/techage/liquids/pump.lua index 2346099..f62d336 100644 --- a/techage/liquids/pump.lua +++ b/techage/liquids/pump.lua @@ -343,7 +343,7 @@ techage.register_node({"techage:t4_pump", "techage:t4_pump_on"}, { end end, on_beduino_receive_cmnd = function(pos, src, topic, payload) - if topic == 69 and payload then -- Set pump limit + if (topic == 69 or topic == 21) and payload then -- Set pump limit local nvm = techage.get_nvm(pos) State4:stop(pos, nvm) if payload[1] > 0 then diff --git a/techage/logic/button.lua b/techage/logic/button.lua index 0d9b82f..f1f2e8c 100644 --- a/techage/logic/button.lua +++ b/techage/logic/button.lua @@ -3,7 +3,7 @@ TechAge ======= - Copyright (C) 2017-2020 Joachim Stolberg + Copyright (C) 2017-2023 Joachim Stolberg AGPL v3 See LICENSE.txt for more information @@ -394,11 +394,16 @@ minetest.register_craft({ }, }) +techage.register_node({"techage:ta3_button_off", "techage:ta3_button_on"}, {}) + techage.register_node({ "techage:ta4_button_off", "techage:ta4_button_on", }, { on_recv_message = function(pos, src, topic, payload) - if topic == "name" then + if 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 @@ -409,7 +414,10 @@ techage.register_node({ end end, on_beduino_request_data = function(pos, src, topic, payload) - if topic == 144 then -- Player Name + 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 diff --git a/techage/logic/button_2x.lua b/techage/logic/button_2x.lua index c79f2bd..e38244d 100644 --- a/techage/logic/button_2x.lua +++ b/techage/logic/button_2x.lua @@ -244,6 +244,8 @@ minetest.register_node("techage:ta4_button_2x", { sounds = default.node_sound_glass_defaults(), }) +techage.register_node({"techage:ta4_button_2x"}, {}) + minetest.register_craft({ output = "techage:ta4_button_2x", recipe = { @@ -252,3 +254,12 @@ minetest.register_craft({ {"", "", ""}, }, }) + +minetest.register_craft({ + output = "techage:ta4_button_off 2", + recipe = { + {"", "", ""}, + {"", "techage:ta4_button_2x", ""}, + {"", "", ""}, + }, +}) diff --git a/techage/logic/button_4x.lua b/techage/logic/button_4x.lua index 9dbb892..5a73c9a 100644 --- a/techage/logic/button_4x.lua +++ b/techage/logic/button_4x.lua @@ -300,6 +300,8 @@ minetest.register_node("techage:ta4_button_4x", { sounds = default.node_sound_glass_defaults(), }) +techage.register_node({"techage:ta4_button_4x"}, {}) + minetest.register_craft({ output = "techage:ta4_button_4x", recipe = { @@ -308,3 +310,12 @@ minetest.register_craft({ {"", "", ""}, }, }) + +minetest.register_craft({ + output = "techage:ta4_button_off 4", + recipe = { + {"", "", ""}, + {"", "techage:ta4_button_4x", ""}, + {"", "", ""}, + }, +}) diff --git a/techage/logic/detector.lua b/techage/logic/detector.lua index 937b49a..daf790c 100644 --- a/techage/logic/detector.lua +++ b/techage/logic/detector.lua @@ -3,7 +3,7 @@ TechAge ======= - Copyright (C) 2017-2022 Joachim Stolberg + Copyright (C) 2017-2023 Joachim Stolberg AGPL v3 See LICENSE.txt for more information @@ -21,7 +21,7 @@ local logic = techage.logic local BLOCKING_TIME = 8 -- seconds local ON_TIME = 1 -local WRENCH_MENU = { +local WRENCH_MENU3 = { { type = "dropdown", choices = "1,2,4,6,8,12,16", @@ -47,6 +47,46 @@ local WRENCH_MENU = { } } +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() @@ -122,6 +162,17 @@ local function after_dig_node(pos, oldnode, oldmetadata, digger) 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 Detector"), @@ -139,7 +190,7 @@ minetest.register_node("techage:ta3_detector_off", { on_receive_fields = on_receive_fields, techage_set_numbers = techage_set_numbers, after_dig_node = after_dig_node, - ta3_formspec = WRENCH_MENU, + ta3_formspec = WRENCH_MENU3, on_rotate = screwdriver.disallow, paramtype = "light", @@ -167,7 +218,7 @@ minetest.register_node("techage:ta3_detector_on", { on_rotate = screwdriver.disallow, techage_set_numbers = techage_set_numbers, after_dig_node = after_dig_node, - ta3_formspec = WRENCH_MENU, + ta3_formspec = WRENCH_MENU3, paramtype2 = "facedir", groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1}, @@ -192,7 +243,8 @@ minetest.register_node("techage:ta4_detector_off", { on_receive_fields = on_receive_fields, techage_set_numbers = techage_set_numbers, after_dig_node = after_dig_node, - ta3_formspec = WRENCH_MENU, + ta4_formspec = WRENCH_MENU4, + ta_after_formspec = ta_after_formspec, on_rotate = screwdriver.disallow, paramtype = "light", @@ -220,7 +272,8 @@ minetest.register_node("techage:ta4_detector_on", { on_rotate = screwdriver.disallow, techage_set_numbers = techage_set_numbers, after_dig_node = after_dig_node, - ta3_formspec = WRENCH_MENU, + ta4_formspec = WRENCH_MENU4, + ta_after_formspec = ta_after_formspec, paramtype2 = "facedir", groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1}, @@ -268,13 +321,22 @@ techage.register_node({"techage:ta4_detector_off", "techage:ta4_detector_on"}, { 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 - switch_on(pos) local nvm = techage.get_nvm(pos) - if leftover == true then - nvm.counter = (nvm.counter or 0) + stack:get_count() - else - nvm.counter = (nvm.counter or 0) + stack:get_count() - leftover:get_count() + 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) + switch_on(pos) + end + elseif nvm.countdown == nil then + switch_on(pos) + end + nvm.counter = (nvm.counter or 0) + num_moved end return leftover end @@ -286,9 +348,15 @@ techage.register_node({"techage:ta4_detector_off", "techage:ta4_detector_on"}, { 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" @@ -298,6 +366,12 @@ techage.register_node({"techage:ta4_detector_off", "techage:ta4_detector_on"}, { 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 diff --git a/techage/logic/signallamp.lua b/techage/logic/signallamp.lua index 485fc83..ba0e7ba 100644 --- a/techage/logic/signallamp.lua +++ b/techage/logic/signallamp.lua @@ -3,7 +3,7 @@ TechAge ======= - Copyright (C) 2022 Joachim Stolberg + Copyright (C) 2022-2023 Joachim Stolberg AGPL v3 See LICENSE.txt for more information @@ -104,7 +104,6 @@ local function register_signallamp(name, description, tiles_off, tiles_on, node_ paramtype = "light", paramtype2 = "color", - --palette = "techage_palette256.png", 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}, @@ -148,7 +147,7 @@ local function register_signallamp(name, description, tiles_off, tiles_on, node_ local node = techage.get_node_lvm(pos) switch_off(pos, node) return 0 - elseif topic == 70 then + elseif topic == 70 or topic == 22 then local node = techage.get_node_lvm(pos) switch_on(pos, node, nil, payload[1]) return 0 @@ -196,8 +195,8 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end) -register_signallamp("techage:signal_lamp", - S("TechAge Signal Lamp"), +register_signallamp("techage:color_lamp", + S("TechAge Color Lamp"), {"techage_signal_lamp.png^[colorize:#000000:80"}, {"techage_signal_lamp.png"}, { @@ -209,8 +208,8 @@ register_signallamp("techage:signal_lamp", } ) -register_signallamp("techage:signal_lamp2", - S("TechAge Signal Lamp 2 "), +register_signallamp("techage:color_lamp2", + S("TechAge Color Lamp 2"), {"techage_signallamp2.png^[colorize:#000000:80"}, {"techage_signallamp2.png"} ) @@ -232,3 +231,8 @@ minetest.register_craft({ {"", "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") diff --git a/techage/manuals/manual_ta3_DE.md b/techage/manuals/manual_ta3_DE.md index 975b34a..f62b3f3 100644 --- a/techage/manuals/manual_ta3_DE.md +++ b/techage/manuals/manual_ta3_DE.md @@ -628,14 +628,14 @@ Im privaten Modus (private) kann das Terminal nur von Spielern verwendet werden, [ta3_terminal|image] -### TechAge Signallampe / Signal Lamp +### TechAge Farblampe / Color Lamp -Die Signallampe kann mit `on`/`off` Kommando ein- bzw. ausgeschaltet werden. Diese Lampe braucht keinen Strom und +Die Farblampe kann mit `on`/`off` Kommando ein- bzw. ausgeschaltet werden. Diese Lampe braucht keinen Strom und kann mit der Spritzpistole aus der Mod "Unified Dyes" und über Lua/Beduino Kommandos eingefärbt werden. Mit dem Chat-Kommando `/ta_color` wird die Farbpalette mit den Werten für die Lua/Beduino Kommandos angezeigt und mit `/ta_send color ` kann die Farbe geändert werden. -[ta3_signallamp|image] +[ta3_colorlamp|image] ### Tür/Tor Blöcke / Door/Gate Blocks diff --git a/techage/manuals/manual_ta3_EN.md b/techage/manuals/manual_ta3_EN.md index 713bd71..b440e5f 100644 --- a/techage/manuals/manual_ta3_EN.md +++ b/techage/manuals/manual_ta3_EN.md @@ -627,13 +627,13 @@ In public mode, all players can use the preconfigured keys. [ta3_terminal|image] -### TechAge Signal Lamp +### TechAge Color Lamp The signal lamp can be switched on or off with the `on` / `off` command. This lamp does not need electricity and can be colored with the airbrush tool from the mod Unified Dyes" and via Lua/Beduino commands. With the chat command `/ta_color` the color palette with the values for the Lua/Beduino commands is displayed and with `/ta_send color ` the color can be changed. -[ta3_signallamp|image] +[ta3_colorlamp|image] ### Door/Gate Blocks diff --git a/techage/manuals/ta4_lua_controller_EN.md b/techage/manuals/ta4_lua_controller_EN.md index 11b9e71..2c937b3 100644 --- a/techage/manuals/ta4_lua_controller_EN.md +++ b/techage/manuals/ta4_lua_controller_EN.md @@ -363,6 +363,7 @@ Please note, that this is not a technical distinction, only a logical. | "state" | one of: "running", "stopped", "blocked", "standby", "fault", or "unloaded" | Techage machine state, used by many machines | | "state" | one of: "red", "amber", "green", "off" | Signal Tower state | | "state" | one of: "empty", "loaded", "full" | State of a chest or Sensor Chest | +| "state" | one of: "on", "off" | State of a TA4 Button | | "fuel" | number | fuel value of a fuel consuming block | | "depth" | number | Read the current depth value of a quarry block (1..80) | | "load" | number | Read the load value in percent (0..100) of a tank, silo, accu, or battery block, or from the Signs Bot Box. Silo and tank return two values: The percentage value and the absolute value in units.
Example: percent, absolute = $send_cmnd("223", "load") | @@ -396,7 +397,8 @@ Please note, that this is not a technical distinction, only a logical. | "port" | string
`=on/off` | Enable/disable a Distributor filter slot..
Example: `"yellow=on"`
colors: red, green, blue, yellow | | "config" | "\ \" | Configure a Distributor filter slot, like: "red default:dirt dye:blue" | | "text" | text string | Text to be used for the Sensor Chest menu | -| "reset" | nil | Reset the item counter of the TA4 Item Detector block | +| "reset" | nil | Reset item and countdown counters of the TA4 Item Detector block | +| "countdown" | number | Set countdown counter of the TA4 Item Detector block to the given value and
start countdown mode. | | "limit" | number | Configure a TA4 Pusher with the number of items that are allowed to be pushed ("flow limiter" mode)
limit = 0 turns off the "flow limiter" mode | | "limit" | number | Configure a TA4 Pump with the number of liquid units that are allowed to be pumped ("flow limiter" mode)
limit = 0 turns off the "flow limiter" mode | | "config" | item string | Configure the TA4 pusher.
Example: `wool:blue` | @@ -417,7 +419,7 @@ Please note, that this is not a technical distinction, only a logical. | "stop" | nil | Stop command for the TA4 Sequencer. | | "gain" | volume | Set volume of the sound block (`volume` is a value between 0 and 1.0) | | "sound" | index | Select sound sample of the sound block | -| "color" | \ | Set the color of the TechAge Signal Lamp and TechAge Signal Lamp 2 (color = 0..255) | +| "color" | \ | Set the color of the TechAge Color Lamp and TechAge Color Lamp 2 (color = 0..255) | ### Server and Terminal Functions diff --git a/techage/manuals/ta_iom.md b/techage/manuals/ta_iom.md deleted file mode 100644 index 7d6d9c7..0000000 --- a/techage/manuals/ta_iom.md +++ /dev/null @@ -1,85 +0,0 @@ -# Techage/Beduino I/O Module - -I/O modules support the following functions: - -### event - -Every signal that is sent to an I/O module triggers an event on the controller. -Events can be queried using the `event()` function. -If the function returns the value `1`, one or more signals have been received. -Calling `event()` resets the event flag. - -```c -event() -``` - -### read - -Read a value from a remote techage block. - -- *port* is the I/O module port number -- *cmnd* is the command, like `IO_STATE` (see example code "ta_cmnd.c") - -```c -read(port, cmnd) -``` - -### send_cmnd - -Send a command to a techage block (see [commands](https://github.com/joe7575/beduino/blob/main/BEPs/bep-005_ta_cmnd.md)). - -- *port* is the I/O module port number -- *topic* is a number from the list of [Beduino commands](https://github.com/joe7575/beduino/blob/main/BEPs/bep-005_ta_cmnd.md) -- *payload* is an array or a string with additional information, depending on the command. If no additional commands are required, "" can be used. - -```c -send_cmnd(port, topic, payload) -``` - -### request_data - -Request information from a techage block (see [commands](https://github.com/joe7575/beduino/blob/main/BEPs/bep-005_ta_cmnd.md)). - -- *port* is the I/O module port number -- *topic* is a number from the list of [Beduino commands](https://github.com/joe7575/beduino/blob/main/BEPs/bep-005_ta_cmnd.md) -- *payload* is an array or a string with additional information, depending on the command. If no additional commands are required, "" can be used. -- *resp* is an array for the response data. The array must be defined large enough to hold the response data. - -```c -request_data(port, topic, payload, resp) -``` - -## Functions for TA4 Display and TA4 Display XL - -### clear_screen - -Clear the display. - -- *port* is the I/O module port number - -```c -clear_screen(port) -``` - -### append_line - -Add a new line to the display. -- *port* is the I/O module port number -- *text* is the text for one line - -```c -append_line(port, text) -``` - - -### write_line - -Overwrite a text line with the given string. - -- *port* is the I/O module port number -- *row* ist the display line/row (1-5) -- *text* is the text for one line - -```c -write_line(port, row, text) -``` diff --git a/techage/manuals/ta_kvstore.md b/techage/manuals/ta_kvstore.md index ddee165..bfdbca8 100644 --- a/techage/manuals/ta_kvstore.md +++ b/techage/manuals/ta_kvstore.md @@ -5,8 +5,8 @@ The key/value store simplifies the handling/comparison of strings. The following example shows the use of the Key/Value Store, here to check the names from the Player Detector: ```c -import "ta_kvstore.c" -import "ta_iom.c" +import "lib/ta_kvstore.c" +import "lib/ta_iom.c" var s[16]; diff --git a/techage/manuals/toc_DE.md b/techage/manuals/toc_DE.md index 808b9b3..4ede925 100644 --- a/techage/manuals/toc_DE.md +++ b/techage/manuals/toc_DE.md @@ -112,7 +112,7 @@ - [TA3 Sequenzer / Sequencer](./manual_ta3_DE.md#ta3-sequenzer--sequencer) - [TA3 Timer](./manual_ta3_DE.md#ta3-timer) - [TA3 Terminal](./manual_ta3_DE.md#ta3-terminal) - - [TechAge Signallampe / Signal Lamp](./manual_ta3_DE.md#techage-signallampe--signal-lamp) + - [TechAge Farblampe / Color Lamp](./manual_ta3_DE.md#techage-farblampe--color-lamp) - [Tür/Tor Blöcke / Door/Gate Blocks](./manual_ta3_DE.md#türtor-blöcke--doorgate-blocks) - [TA3 Tür Controller / Door Controller](./manual_ta3_DE.md#ta3-tür-controller--door-controller) - [TA3 Tür Controller II / Door Controller II](./manual_ta3_DE.md#ta3-tür-controller-ii--door-controller-ii) diff --git a/techage/manuals/toc_EN.md b/techage/manuals/toc_EN.md index fc0e3a2..fdd0398 100644 --- a/techage/manuals/toc_EN.md +++ b/techage/manuals/toc_EN.md @@ -112,7 +112,7 @@ - [TA3 Sequencer](./manual_ta3_EN.md#ta3-sequencer) - [TA3 Timer](./manual_ta3_EN.md#ta3-timer) - [TA3 Terminal](./manual_ta3_EN.md#ta3-terminal) - - [TechAge Signal Lamp](./manual_ta3_EN.md#techage-signal-lamp) + - [TechAge Color Lamp](./manual_ta3_EN.md#techage-color-lamp) - [Door/Gate Blocks](./manual_ta3_EN.md#doorgate-blocks) - [TA3 Door Controller](./manual_ta3_EN.md#ta3-door-controller) - [TA3 Door Controller II](./manual_ta3_EN.md#ta3-door-controller-ii) diff --git a/techage/move_controller/flycontroller.lua b/techage/move_controller/flycontroller.lua index 6e56c49..9f1c7cc 100644 --- a/techage/move_controller/flycontroller.lua +++ b/techage/move_controller/flycontroller.lua @@ -3,12 +3,12 @@ TechAge ======= - Copyright (C) 2020-2022 Joachim Stolberg + Copyright (C) 2020-2023 Joachim Stolberg AGPL v3 See LICENSE.txt for more information - TA4 Move Controller + TA5 Fly Controller ]]-- @@ -42,6 +42,13 @@ local WRENCH_MENU = { tooltip = S("Value in the range of 0.0 to 1.0"), default = "1.0", }, + { + type = "float", + name = "offset", + label = S("Object offset"), + tooltip = S("Y-offset for non-player objects like vehicles (-0.5 to 0.5)"), + default = "0.0", + }, } local function formspec(nvm, meta) @@ -165,8 +172,6 @@ minetest.register_node("techage:ta5_flycontroller", { elseif fields.moveAB then meta:set_string("status", "") if fly.move_to_other_pos(pos, false) then - nvm.moveBA = true - nvm.running = true meta:set_string("formspec", formspec(nvm, meta)) local name = player:get_player_name() mark.stop(name) @@ -175,8 +180,6 @@ minetest.register_node("techage:ta5_flycontroller", { elseif fields.moveBA then meta:set_string("status", "") if fly.move_to_other_pos(pos, true) then - nvm.moveBA = false - nvm.running = true meta:set_string("formspec", formspec(nvm, meta)) local name = player:get_player_name() mark.stop(name) @@ -185,8 +188,6 @@ minetest.register_node("techage:ta5_flycontroller", { elseif fields.move then meta:set_string("status", "") if fly.move_to_other_pos(pos, nvm.moveBA) then - nvm.moveBA = nvm.moveBA == false - nvm.running = true meta:set_string("formspec", formspec(nvm, meta)) local name = player:get_player_name() mark.stop(name) @@ -219,17 +220,11 @@ techage.register_node({"techage:ta5_flycontroller"}, { elseif topic == "state" then return nvm.running and "running" or "stopped" elseif topic == "a2b" then - nvm.moveBA = true - nvm.running = true return fly.move_to_other_pos(pos, false) elseif topic == "b2a" then - nvm.moveBA = false - nvm.running = true return fly.move_to_other_pos(pos, true) elseif topic == "move" then - nvm.moveBA = nvm.moveBA == false - nvm.running = true - return fly.move_to_other_pos(pos, nvm.moveBA == false) + return fly.move_to_other_pos(pos, nvm.moveBA) end return false end, @@ -237,17 +232,11 @@ techage.register_node({"techage:ta5_flycontroller"}, { local nvm = techage.get_nvm(pos) if topic == 11 then if payload[1] == 1 then - nvm.moveBA = true - nvm.running = true return fly.move_to_other_pos(pos, false) and 0 or 3 elseif payload[1] == 2 then - nvm.moveBA = false - nvm.running = true return fly.move_to_other_pos(pos, true) and 0 or 3 elseif payload[1] == 3 then - nvm.moveBA = nvm.moveBA == false - nvm.running = true - return fly.move_to_other_pos(pos, nvm.moveBA == false) and 0 or 3 + return fly.move_to_other_pos(pos, nvm.moveBA) and 0 or 3 end else return 2 @@ -260,6 +249,10 @@ techage.register_node({"techage:ta5_flycontroller"}, { end return 2, "" end, + on_node_load = function(pos, node) + local nvm = techage.get_nvm(pos) + nvm.running = false + end, }) minetest.register_craft({ diff --git a/techage/move_controller/movecontroller.lua b/techage/move_controller/movecontroller.lua index caf29af..2c25fb1 100644 --- a/techage/move_controller/movecontroller.lua +++ b/techage/move_controller/movecontroller.lua @@ -34,22 +34,6 @@ local WRENCH_MENU = { tooltip = S("Maximum speed for moving blocks"), default = "8", }, - { - type = "number", - name = "handoverB", - label = S("Handover to B"), - tooltip = S("Number of the next movecontroller"), - default = "", - check = techage.check_number, - }, - { - type = "number", - name = "handoverA", - label = S("Handover to A"), - tooltip = S("Number of the previous movecontroller"), - default = "", - check = techage.check_number, - }, { type = "float", name = "height", diff --git a/techage/move_controller/soundblock.lua b/techage/move_controller/soundblock.lua index bafcbb2..7743f0f 100644 --- a/techage/move_controller/soundblock.lua +++ b/techage/move_controller/soundblock.lua @@ -116,8 +116,9 @@ techage.register_node({"techage:ta3_soundblock"}, { end end, on_beduino_receive_cmnd = function(pos, src, topic, payload) + print("ta3_soundblock", topic, payload[1], payload[2]) if topic == 1 then - if payload[1] == 0 then + if payload[1] == 1 then play_predefined_sound(pos) return 0 end diff --git a/techage/power/formspecs.lua b/techage/power/formspecs.lua index 985901b..a982637 100644 --- a/techage/power/formspecs.lua +++ b/techage/power/formspecs.lua @@ -28,6 +28,7 @@ techage.power = {} -- Helper function ------------------------------------------------------------------------------- local function round(val) + val = tonumber(val) or 0 if val > 100 then return math.floor(val + 0.5) elseif val > 10 then diff --git a/techage/ta3_power/tiny_generator.lua b/techage/ta3_power/tiny_generator.lua index a6d31a9..17ee65d 100644 --- a/techage/ta3_power/tiny_generator.lua +++ b/techage/ta3_power/tiny_generator.lua @@ -225,8 +225,8 @@ minetest.register_node("techage:tiny_generator", { meta:set_string("liquid_name", nvm.liquid.name) meta:set_int("liquid_amount", nvm.liquid.amount) meta:set_string("description", S("TA3 Tiny Power Generator") .. " (fuel: " .. - tostring(nvm.liquid and nvm.liquid.amount or 0) .. "/" .. - tostring(fuel.CAPACITY) .. ")") + tostring(nvm.liquid and nvm.liquid.amount or 0) .. "/" .. + tostring(fuel.CAPACITY) .. ")") end end, diff --git a/techage/tools/repairkit.lua b/techage/tools/repairkit.lua index fab6f38..1ef971e 100644 --- a/techage/tools/repairkit.lua +++ b/techage/tools/repairkit.lua @@ -222,13 +222,29 @@ local function on_place(itemstack, placer, pointed_thing) end end +local function repair(itemstack, placer, pointed_thing) + if pointed_thing.type == "node" then + local pos = pointed_thing.under + if not placer or minetest.is_protected(pos, placer:get_player_name()) then + return + end + local number = techage.get_node_number(pos) + if number and not techage.get_node_info(number) then + techage.repair_number(pos) + minetest.chat_send_player(placer:get_player_name(), "Node repaired!") + itemstack:add_wear(65636/200) + return itemstack + end + end +end + minetest.register_tool("techage:repairkit", { description = S("TechAge Repair Kit"), inventory_image = "techage_repairkit.png", wield_image = "techage_repairkit.png^[transformR270", groups = {cracky=1, book=1}, - --on_use = repair, - --on_place = repair, + on_use = repair, + on_place = repair, node_placement_prediction = "", stack_max = 1, }) @@ -254,3 +270,12 @@ minetest.register_craft({ {"default:steel_ingot", "", ""}, }, }) + +--minetest.register_craft({ +-- output = "techage:repairkit", +-- recipe = { +-- {"", "", ""}, +-- {"", "techage:end_wrench", ""}, +-- {"", "", ""}, +-- }, +--}) diff --git a/unified_inventory/README.md b/unified_inventory/README.md index 5d23f79..8c64ab1 100644 --- a/unified_inventory/README.md +++ b/unified_inventory/README.md @@ -102,3 +102,16 @@ Other files from Wikimedia Commons: RealBadAngel: (CC-BY-4.0) * Everything else. + + +## Sounds + + * [`bell.ogg`](https://freesound.org/people/bennstir/sounds/81072/) by bennstir, CC 4.0 + * [`electricity.ogg`](https://freesound.org/people/Halleck/sounds/19486/) by Halleck, CC 4.0 (cut) + * [`pageflip1.ogg`](https://freesound.org/people/themfish/sounds/45823/) by themfish, CC 4.0 (cut, slowed down) + * `pageflip2.ogg` (derived from `pageflip1.ogg`) + * [`trash.ogg`](https://freesound.org/people/OwlStorm/sounds/151231/) by OwlStorm, CC 0 (speed up) + * [`trash_all.ogg`](https://freesound.org/people/abel_K/sounds/68280/) by abel_K, Sampling Plus 1.0 (speed up) + * [`ui_click.ogg`](https://freesound.org/people/lartti/sounds/527569/) by lartti, CC 0 (cut) + * [`ui_morning.ogg`](https://freesound.org/people/InspectorJ/sounds/439472/) by InspectorJ, CC 4.0 + * [`ui_owl.ogg`](https://freesound.org/people/manda_g/sounds/54987/) by manda_g, Sampling Plus 1.0 (cut) diff --git a/unified_inventory/api.lua b/unified_inventory/api.lua index e4b67fa..0437391 100644 --- a/unified_inventory/api.lua +++ b/unified_inventory/api.lua @@ -145,50 +145,18 @@ minetest.after(0.01, function() end end - -- Step 1: group-indexed lookup table for items - local spec_matcher = {} - for _, name in ipairs(ui.items_list) do - -- we only need to care about groups, exact items are handled separately - for group, value in pairs(minetest.registered_items[name].groups) do - if value and value ~= 0 then - if not spec_matcher[group] then - spec_matcher[group] = {} - end - spec_matcher[group][name] = true - end - end - end + -- Step 1: Initialize cache for looking up groups + unified_inventory.init_matching_cache() -- Step 2: Find all matching items for the given spec (groups) - local function get_matching_spec_items(specname) - if specname:sub(1,6) ~= "group:" then - return { [specname] = true } - end + local get_matching_spec_items = unified_inventory.get_matching_items - local accepted = {} - for i, group in ipairs(specname:sub(7):split(",")) do - if i == 1 then - -- First step: Copy all possible item names in this group - for name, _ in pairs(spec_matcher[group] or {}) do - accepted[name] = true - end - else - -- Perform filtering - if spec_matcher[group] then - for name, _ in pairs(accepted) do - accepted[name] = spec_matcher[group][name] - end - else - -- No matching items - return {} - end - end - end - return accepted - end - - for _, recipes in pairs(ui.crafts_for.recipe) do + for outputitemname, recipes in pairs(ui.crafts_for.recipe) do -- List of crafts that return this item string (variable "_") + + -- Problem: The group cache must be initialized after all mods finished loading + -- thus, invalid recipes might be indexed. Hence perform filtering with `new_recipe_list` + local new_recipe_list = {} for _, recipe in ipairs(recipes) do local ingredient_items = {} for _, spec in pairs(recipe.items) do @@ -204,7 +172,14 @@ minetest.after(0.01, function() end table.insert(ui.crafts_for.usage[name], recipe) end + + if next(ingredient_items) then + -- There's at least one known ingredient: mark as good recipe + -- PS: What whatll be done about partially incomplete recipes? + table.insert(new_recipe_list, recipe) + end end + ui.crafts_for.recipe[outputitemname] = new_recipe_list end for _, callback in ipairs(ui.initialized_callbacks) do diff --git a/unified_inventory/bags.lua b/unified_inventory/bags.lua index 876b1a5..464b65b 100644 --- a/unified_inventory/bags.lua +++ b/unified_inventory/bags.lua @@ -10,25 +10,26 @@ local F = minetest.formspec_escape local ui = unified_inventory ui.register_page("bags", { - get_formspec = function(player) + get_formspec = function(player, perplayer_formspec) local player_name = player:get_player_name() - return { formspec = table.concat({ - ui.style_full.standard_inv_bg, - ui.single_slot(0.925, 1.5), - ui.single_slot(3.425, 1.5), - ui.single_slot(5.925, 1.5), - ui.single_slot(8.425, 1.5), - "label["..ui.style_full.form_header_x..","..ui.style_full.form_header_y..";" .. F(S("Bags")) .. "]", - "button[0.6125,2.75;1.875,0.75;bag1;" .. F(S("Bag @1", 1)) .. "]", - "button[3.1125,2.75;1.875,0.75;bag2;" .. F(S("Bag @1", 2)) .. "]", - "button[5.6125,2.75;1.875,0.75;bag3;" .. F(S("Bag @1", 3)) .. "]", - "button[8.1125,2.75;1.875,0.75;bag4;" .. F(S("Bag @1", 4)) .. "]", + local std_inv_x = perplayer_formspec.std_inv_x + local formspec = { + perplayer_formspec.standard_inv_bg, + "label[", perplayer_formspec.form_header_x, ",", + perplayer_formspec.form_header_y, ";", F(S("Bags")), "]", "listcolors[#00000000;#00000000]", - "list[detached:" .. F(player_name) .. "_bags;bag1;1.075,1.65;1,1;]", - "list[detached:" .. F(player_name) .. "_bags;bag2;3.575,1.65;1,1;]", - "list[detached:" .. F(player_name) .. "_bags;bag3;6.075,1.65;1,1;]", - "list[detached:" .. F(player_name) .. "_bags;bag4;8.575,1.65;1,1;]" - }) } + } + + for i = 1, 4 do + local x = std_inv_x + i * 2.5 + formspec[#formspec + 1] = ui.single_slot(x - 1.875, 1.5) + formspec[#formspec + 1] = string.format("list[detached:%s_bags;bag%i;%.3f,1.65;1,1;]", + F(player_name), i, x - 1.725) + formspec[#formspec + 1] = string.format("button[%.4f,2.75;1.875,0.75;bag%i;%s]", + x - 2.1875, i, F(S("Bag @1", i))) + end + + return { formspec = table.concat(formspec) } end, }) @@ -36,7 +37,6 @@ ui.register_button("bags", { type = "image", image = "ui_bags_icon.png", tooltip = S("Bags"), - hide_lite=true }) local function get_player_bag_stack(player, i) @@ -48,23 +48,38 @@ end for bag_i = 1, 4 do ui.register_page("bag" .. bag_i, { - get_formspec = function(player) + get_formspec = function(player, perplayer_formspec) local stack = get_player_bag_stack(player, bag_i) local image = stack:get_definition().inventory_image local slots = stack:get_definition().groups.bagslots + local std_inv_x = perplayer_formspec.std_inv_x + local lite_mode = perplayer_formspec.is_lite_mode + + local bag_inv_y, header_x, header_y = 1.5, 0.3, 0.65 + if lite_mode then + bag_inv_y = 0.5 + header_x = perplayer_formspec.form_header_x + header_y = perplayer_formspec.form_header_y + end local formspec = { - ui.style_full.standard_inv_bg, - ui.make_inv_img_grid(0.3, 1.5, 8, slots/8), - "image[9.2,0.4;1,1;" .. image .. "]", - "label[0.3,0.65;" .. F(S("Bag @1", bag_i)) .. "]", + perplayer_formspec.standard_inv_bg, + ui.make_inv_img_grid(std_inv_x, bag_inv_y, 8, slots/8), + "label[", header_x, ",", header_y, ";", F(S("Bag @1", bag_i)), "]", "listcolors[#00000000;#00000000]", "listring[current_player;main]", string.format("list[current_player;bag%icontents;%f,%f;8,3;]", - bag_i, 0.3 + ui.list_img_offset, 1.5 + ui.list_img_offset), - "listring[current_name;bag" .. bag_i .. "contents]", + bag_i, std_inv_x + ui.list_img_offset, bag_inv_y + ui.list_img_offset), + "listring[current_name;bag", bag_i, "contents]", } + + if lite_mode then + return { formspec = table.concat(formspec) } + end + local n = #formspec + 1 + formspec[n] = "image[" .. std_inv_x + 8.9 .. ",0.4;1,1;" .. image .. "]" + n = n + 1 local player_name = player:get_player_name() -- For if statement. if ui.trash_enabled diff --git a/unified_inventory/callbacks.lua b/unified_inventory/callbacks.lua index fa6d03a..52cb710 100644 --- a/unified_inventory/callbacks.lua +++ b/unified_inventory/callbacks.lua @@ -57,30 +57,47 @@ end) local function apply_new_filter(player, search_text, new_dir) local player_name = player:get_player_name() - minetest.sound_play("click", {to_player=player_name, gain = 0.1}) + minetest.sound_play("ui_click", {to_player=player_name, gain = 0.1}) ui.apply_filter(player, search_text, new_dir) ui.current_searchbox[player_name] = search_text ui.set_inventory_formspec(player, ui.current_page[player_name]) end -minetest.register_on_player_receive_fields(function(player, formname, fields) +-- Search box handling +local function receive_fields_searchbox(player, formname, fields) local player_name = player:get_player_name() - local ui_peruser,draw_lite_mode = unified_inventory.get_per_player_formspec(player_name) + -- always take new search text, even if not searching on it yet + if fields.searchbox and fields.searchbox ~= ui.current_searchbox[player_name] then + ui.current_searchbox[player_name] = fields.searchbox + end + if fields.searchbutton + or fields.key_enter_field == "searchbox" then + + if ui.current_searchbox[player_name] ~= ui.activefilter[player_name] then + ui.apply_filter(player, ui.current_searchbox[player_name], "nochange") + ui.set_inventory_formspec(player, ui.current_page[player_name]) + minetest.sound_play("paperflip2", + {to_player=player_name, gain = 1.0}) + end + elseif fields.searchresetbutton then + if ui.activefilter[player_name] ~= "" then + apply_new_filter(player, "", "nochange") + end + end +end + +minetest.register_on_player_receive_fields(function(player, formname, fields) if formname ~= "" then return end - -- always take new search text, even if not searching on it yet - local dirty_search_filter = false + receive_fields_searchbox(player, formname, fields) - if fields.searchbox - and fields.searchbox ~= unified_inventory.current_searchbox[player_name] then - unified_inventory.current_searchbox[player_name] = fields.searchbox - dirty_search_filter = true - end + local player_name = player:get_player_name() + local ui_peruser,draw_lite_mode = unified_inventory.get_per_player_formspec(player_name) local clicked_category for name, value in pairs(fields) do @@ -114,7 +131,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) for i, def in pairs(unified_inventory.buttons) do if fields[def.name] then def.action(player) - minetest.sound_play("click", + minetest.sound_play("ui_click", {to_player=player_name, gain = 0.1}) return end @@ -179,7 +196,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end end if clicked_item then - minetest.sound_play("click", + minetest.sound_play("ui_click", {to_player=player_name, gain = 0.1}) local page = unified_inventory.current_page[player_name] local player_creative = unified_inventory.is_creative(player_name) @@ -201,25 +218,11 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end end - if fields.searchbutton - or fields.key_enter_field == "searchbox" then - if dirty_search_filter then - ui.apply_filter(player, ui.current_searchbox[player_name], "nochange") - ui.set_inventory_formspec(player, ui.current_page[player_name]) - minetest.sound_play("paperflip2", - {to_player=player_name, gain = 1.0}) - end - elseif fields.searchresetbutton then - if ui.activefilter[player_name] ~= "" then - apply_new_filter(player, "", "nochange") - end - end - -- alternate buttons if not (fields.alternate or fields.alternate_prev) then return end - minetest.sound_play("click", + minetest.sound_play("ui_click", {to_player=player_name, gain = 0.1}) local item_name = unified_inventory.current_item[player_name] if not item_name then diff --git a/unified_inventory/doc/mod_api.txt b/unified_inventory/doc/mod_api.txt index 99fa638..45eb432 100644 --- a/unified_inventory/doc/mod_api.txt +++ b/unified_inventory/doc/mod_api.txt @@ -24,7 +24,9 @@ Grouped by use-case, afterwards sorted alphabetically. Callbacks --------- -Register a callback that will be run whenever a craft is registered via unified_inventory.register_craft: +Register a callback that will be run whenever a craft is registered via unified_inventory.register_craft. +This callback is run before any recipe ingredients checks, hence it is also executed on recipes that are +purged after all mods finished loading. unified_inventory.register_on_craft_registered( function (item_name, options) diff --git a/unified_inventory/group.lua b/unified_inventory/group.lua index 2bc8e2f..1483b3c 100644 --- a/unified_inventory/group.lua +++ b/unified_inventory/group.lua @@ -1,4 +1,5 @@ local S = minetest.get_translator("unified_inventory") +local ui = unified_inventory function unified_inventory.extract_groupnames(groupname) local specname = ItemStack(groupname):get_name() @@ -26,6 +27,7 @@ end -- It may be a comma-separated list of group names. This is really a -- "group:..." ingredient specification, minus the "group:" prefix. +-- TODO Replace this with the more efficient spec matcher (below) local function compute_group_item(group_name_list) local group_names = group_name_list:split(",") local candidate_items = {} @@ -84,3 +86,61 @@ function unified_inventory.get_group_item(group_name) return group_item_cache[group_name] end + +--[[ +This is for filtering known items by groups +e.g. find all items that match "group:flower,yellow" (flower AND yellow groups) +]] +local spec_matcher = {} +function unified_inventory.init_matching_cache() + for _, name in ipairs(ui.items_list) do + -- we only need to care about groups, exact items are handled separately + for group, value in pairs(minetest.registered_items[name].groups) do + if value and value ~= 0 then + if not spec_matcher[group] then + spec_matcher[group] = {} + end + spec_matcher[group][name] = true + end + end + end +end + +--[[ +Retrieves all matching items + +Arguments: + specname (string): Item name or group(s) to filter + +Output: + { + matchingitem1 = true, + ... + } +]] +function unified_inventory.get_matching_items(specname) + if specname:sub(1,6) ~= "group:" then + return { [specname] = true } + end + + local accepted = {} + for i, group in ipairs(specname:sub(7):split(",")) do + if i == 1 then + -- First step: Copy all possible item names in this group + for name, _ in pairs(spec_matcher[group] or {}) do + accepted[name] = true + end + else + -- Perform filtering + if spec_matcher[group] then + for name, _ in pairs(accepted) do + accepted[name] = spec_matcher[group][name] + end + else + -- No matching items + return {} + end + end + end + return accepted +end diff --git a/unified_inventory/internal.lua b/unified_inventory/internal.lua index 45db6b3..0903d86 100644 --- a/unified_inventory/internal.lua +++ b/unified_inventory/internal.lua @@ -274,9 +274,11 @@ local function formspec_add_item_browser(player, formspec, ui_peruser) end end end - formspec[n] = string.format("label[%f,%f;%s: %s]", - ui_peruser.page_buttons_x + ui_peruser.btn_spc * (ui_peruser.is_lite_mode and 1 or 2), - ui_peruser.page_buttons_y + 0.1 + ui_peruser.btn_spc * 2, + formspec[n] = "style[page_number;content_offset=0]" + formspec[n + 1] = string.format("image_button[%f,%f;%f,0.4;;page_number;%s: %s;false;false;]", + ui_peruser.page_buttons_x, + ui_peruser.page_buttons_y + ui_peruser.btn_spc * 2 - 0.1, + ui_peruser.btn_spc * (bn - 1) + ui_peruser.btn_size, F(S("Page")), S("@1 of @2",page2,pagemax)) end diff --git a/unified_inventory/match_craft.lua b/unified_inventory/match_craft.lua index 2dd40b0..b2d18ec 100644 --- a/unified_inventory/match_craft.lua +++ b/unified_inventory/match_craft.lua @@ -126,25 +126,18 @@ Example output: } --]] function unified_inventory.find_usable_items(inv_items, craft_items) - local get_group = minetest.get_item_group local result = {} for craft_item in pairs(craft_items) do - local group = craft_item:match("^group:(.+)") - local found = {} + -- may specify group:type1,type2 + local items = unified_inventory.get_matching_items(craft_item) - if group ~= nil then - for inv_item in pairs(inv_items) do - if get_group(inv_item, group) > 0 then - found[inv_item] = true - end - end - else - if inv_items[craft_item] ~= nil then - found[craft_item] = true + local found = {} + for itemname, _ in pairs(items) do + if inv_items[itemname] then + found[itemname] = true end end - result[craft_item] = found end diff --git a/unified_inventory/register.lua b/unified_inventory/register.lua index 1f72bfe..225a861 100644 --- a/unified_inventory/register.lua +++ b/unified_inventory/register.lua @@ -98,7 +98,7 @@ ui.register_button("misc_set_day", { action = function(player) local player_name = player:get_player_name() if minetest.check_player_privs(player_name, {settime=true}) then - minetest.sound_play("birds", + minetest.sound_play("ui_morning", {to_player=player_name, gain = 1.0}) minetest.set_timeofday((6000 % 24000) / 24000) minetest.chat_send_player(player_name, @@ -122,7 +122,7 @@ ui.register_button("misc_set_night", { action = function(player) local player_name = player:get_player_name() if minetest.check_player_privs(player_name, {settime=true}) then - minetest.sound_play("owl", + minetest.sound_play("ui_owl", {to_player=player_name, gain = 1.0}) minetest.set_timeofday((21000 % 24000) / 24000) minetest.chat_send_player(player_name, @@ -183,14 +183,14 @@ ui.register_page("craft", { local n=#formspec+1 if ui.trash_enabled or ui.is_creative(player_name) or minetest.get_player_privs(player_name).give then - formspec[n] = string.format("label[%f,%f;%s]", craftx + 6.45, crafty + 2.4, F(S("Trash:"))) + formspec[n] = string.format("label[%f,%f;%s]", craftx + 6.35, crafty + 2.3, F(S("Trash:"))) formspec[n+1] = ui.make_trash_slot(craftx + 6.25, crafty + 2.5) n=n + 2 end if ui.is_creative(player_name) then formspec[n] = ui.single_slot(craftx - 2.5, crafty + 2.5) - formspec[n+1] = string.format("label[%f,%f;%s]", craftx - 2.3, crafty + 2.4,F(S("Refill:"))) + formspec[n+1] = string.format("label[%f,%f;%s]", craftx - 2.4, crafty + 2.3, F(S("Refill:"))) formspec[n+2] = string.format("list[detached:%srefill;main;%f,%f;1,1;]", F(player_name), craftx - 2.5 + ui.list_img_offset, crafty + 2.5 + ui.list_img_offset) end diff --git a/unified_inventory/sounds/birds.ogg b/unified_inventory/sounds/birds.ogg deleted file mode 100644 index 4a93395..0000000 Binary files a/unified_inventory/sounds/birds.ogg and /dev/null differ diff --git a/unified_inventory/sounds/owl.ogg b/unified_inventory/sounds/owl.ogg deleted file mode 100644 index f30d0b3..0000000 Binary files a/unified_inventory/sounds/owl.ogg and /dev/null differ diff --git a/unified_inventory/sounds/ui_click.ogg b/unified_inventory/sounds/ui_click.ogg new file mode 100644 index 0000000..0c9fe31 Binary files /dev/null and b/unified_inventory/sounds/ui_click.ogg differ diff --git a/unified_inventory/sounds/ui_morning.ogg b/unified_inventory/sounds/ui_morning.ogg new file mode 100644 index 0000000..7fb8c22 Binary files /dev/null and b/unified_inventory/sounds/ui_morning.ogg differ diff --git a/unified_inventory/sounds/ui_owl.ogg b/unified_inventory/sounds/ui_owl.ogg new file mode 100644 index 0000000..a1433f9 Binary files /dev/null and b/unified_inventory/sounds/ui_owl.ogg differ