From 5e3e7344fa43f354fa18425c11aa31587391a26d Mon Sep 17 00:00:00 2001 From: Joachim Stolberg Date: Sun, 5 Jan 2025 20:41:28 +0100 Subject: [PATCH] Add TA4 water remover device --- basic_machines/waterremover.lua | 351 ++++++++++++++++++++++++++++++++ basis/fly_lib.lua | 26 ++- basis/submenu.lua | 88 ++++++-- doc/items.lua | 1 + doc/manual_ta4_DE.lua | 17 ++ doc/manual_ta4_EN.lua | 16 ++ init.lua | 1 + locale/techage.de.tr | 17 +- locale/techage.fr.tr | 17 +- locale/techage.ru.tr | 17 +- locale/template.txt | 17 +- logic/terminal.lua | 29 +-- manuals/manual_ta4_DE.md | 16 ++ manuals/manual_ta4_EN.md | 15 ++ manuals/nanobasic.md | 2 + sounds/techage_scoopwater.ogg | Bin 0 -> 19734 bytes 16 files changed, 592 insertions(+), 38 deletions(-) create mode 100644 basic_machines/waterremover.lua create mode 100644 sounds/techage_scoopwater.ogg diff --git a/basic_machines/waterremover.lua b/basic_machines/waterremover.lua new file mode 100644 index 0000000..84be53e --- /dev/null +++ b/basic_machines/waterremover.lua @@ -0,0 +1,351 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019-2025 Joachim Stolberg + + AGPL v3 + See LICENSE.txt for more information + + Water Remover + + The Water Remover removes water from an area of ​​up to 21 x 21 x 80 m. + It is mainly used to drain caves. The water remover is placed at the highest + point of the cave and removes the water from the cave to the lowest point. + It digs one water block every two seconds. + +]]-- + +-- for lazy programmers +local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local S2P = minetest.string_to_pos +local M = minetest.get_meta +local NDEF = function(pos) return minetest.registered_nodes[techage.get_node_lvm(pos).name] or {} end +-- Consumer Related Data +local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end + +local S = techage.S +local Pipe = techage.LiquidPipe +local liquid = networks.liquid +local menu = techage.menu + +local TITLE = S("Water Remover") +local CYCLE_TIME = 2 +local STANDBY_TICKS = 4 +local COUNTDOWN_TICKS = 4 +local Side2Facedir = {F=0, R=1, B=2, L=3, D=4, U=5} + +local MENU = { + { + type = "dropdown", + choices = "9x9,11x11,13x13,15x15,17x17,19x19,21x21", + name = "area_size", + label = S("Area size"), + tooltip = S("Area where the water is to be removed"), + default = "9", + values = {9,11,13,15,17,19,21}, + }, + { + type = "numbers", + name = "area_depth", + label = S("Area depth"), + tooltip = S("Depth of the area where the water is to be removed (1-80)"), + default = "10", + check = function(value, player_name) return tonumber(value) >= 1 and tonumber(value) <= 80 end, + }, + { + type = "output", + name = "curr_depth", + label = S("Current depth"), + tooltip = S("Current working depth of the water remover"), + }, +} + +local WaterNodes = { + "default:water_source", + "default:river_water_source", + -- Add more water nodes here +} + +core.register_node("techage:air", { + description = "Techage Air", + inventory_image = "air.png", + wield_image = "air.png", + drawtype = "airlike", + paramtype = "light", + sunlight_propagates = true, + walkable = false, + pointable = false, + diggable = false, + buildable_to = true, + floodable = false, -- This is important! + air_equivalent = true, + drop = "", + groups = {not_in_creative_inventory=1}, +}) + +local function formspec(self, pos, nvm) + return "size[8,4.4]" .. + "box[0,-0.1;7.8,0.5;#c6e8ff]" .. + "label[3.0,-0.1;" .. minetest.colorize( "#000000", TITLE) .. "]" .. + "image[6.4,0.8;1,1;" .. techage.get_power_image(pos, nvm) .. "]" .. + "image_button[6.4,2.2;1,1;".. self:get_state_button_image(nvm) .. ";state_button;]" .. + "tooltip[6.4,2.2;1,1;" .. self:get_state_tooltip(nvm) .. "]" .. + menu.generate_formspec_container(pos, NDEF(pos), MENU, 0.0, 5.8) +end + +local function play_sound(pos) + minetest.sound_play("techage_scoopwater", { + pos = pos, + gain = 1.5, + max_hear_distance = 15}) +end + +local function on_node_state_change(pos, old_state, new_state) + local mem = techage.get_mem(pos) + local owner = M(pos):get_string("owner") + mem.co = nil + techage.unmark_position(owner) +end + +local function get_pos(pos, facedir, side, steps) + facedir = (facedir + Side2Facedir[side]) % 4 + local dir = vector.multiply(minetest.facedir_to_dir(facedir), steps or 1) + return vector.add(pos, dir) +end + +local function get_dig_pos(pos, xoffs, zoffs) + return {x = pos.x + xoffs - 1, y = pos.y, z = pos.z + zoffs - 1} +end + +-- pos is the quarry pos +local function get_corner_positions(pos, facedir, hole_diameter) + local _pos = get_pos(pos, facedir, "L") + local pos1 = get_pos(_pos, facedir, "F", math.floor((hole_diameter - 1) / 2)) + local pos2 = get_pos(_pos, facedir, "B", math.floor((hole_diameter - 1) / 2)) + pos2 = get_pos(pos2, facedir, "L", hole_diameter - 1) + if pos1.x > pos2.x then pos1.x, pos2.x = pos2.x, pos1.x end + if pos1.y > pos2.y then pos1.y, pos2.y = pos2.y, pos1.y end + if pos1.z > pos2.z then pos1.z, pos2.z = pos2.z, pos1.z end + return pos1, pos2 +end + +local function is_water(pos1, pos2) + return #minetest.find_nodes_in_area(pos1, pos2, WaterNodes) > 0 +end + +local function mark_area(pos1, pos2, owner) + pos1.y = pos1.y + 0.2 + techage.mark_cube(owner, pos1, pos2, TITLE, "#FF0000", 40) + pos1.y = pos1.y - 0.2 +end + +local function dig_water_node(mypos, dig_pos) + local outdir = M(mypos):get_int("outdir") + local node = techage.get_node_lvm(dig_pos) + if node.name == "default:water_source" then + minetest.swap_node(dig_pos, {name = "techage:air", param2 = 0}) + local leftover = liquid.put(mypos, Pipe, outdir, "techage:water", 1) + if leftover and leftover > 0 then + return "tank full" + end + return "ok" + end + return "no_water" +end + +local function drain_task(pos, crd, nvm) + nvm.area_depth = M(pos):get_int("area_depth") + nvm.area_size = M(pos):get_int("area_size") + local y_first = pos.y + local y_last = y_first - nvm.area_depth + 1 + local facedir = minetest.get_node(pos).param2 + local owner = M(pos):get_string("owner") + local cnt = 0 + + local pos1, pos2 = get_corner_positions(pos, facedir, nvm.area_size) + nvm.level = 1 + --print("drain_task", nvm.area_depth, nvm.area_size, y_first, y_last, M(pos):get_int("area_depth")) + for y_curr = y_first, y_last, -1 do + 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 + 1 + + if minetest.is_area_protected(pos1, pos2, owner, 5) then + crd.State:fault(pos, nvm, S("area is protected")) + return + end + + if is_water(pos1, pos2) then + mark_area(pos1, pos2, owner) + M(pos):set_string("curr_depth", nvm.level) + coroutine.yield() + + for zoffs = 1, nvm.area_size do + for xoffs = 1, nvm.area_size do + local qpos = get_dig_pos(pos1, xoffs, zoffs) + while true do + local res = dig_water_node(pos, qpos) + if res == "tank full" then + crd.State:blocked(pos, nvm, S("tank full")) + techage.unmark_position(owner) + coroutine.yield() + elseif res == "no_water" then + break + else + crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS) + if cnt % 4 == 0 then + play_sound(pos) + end + cnt = cnt + 1 + coroutine.yield() + break + end + end + end + coroutine.yield() + end + techage.unmark_position(owner) + end + end + M(pos):set_string("curr_depth", nvm.level) + crd.State:stop(pos, nvm, S("finished")) +end + +local function keep_running(pos, elapsed) + local mem = techage.get_mem(pos) + if not mem.co then + mem.co = coroutine.create(drain_task) + end + + local nvm = techage.get_nvm(pos) + local crd = CRD(pos) + local _, err = coroutine.resume(mem.co, pos, crd, nvm) + if err then + minetest.log("error", "[" .. TITLE .. "] at pos " .. minetest.pos_to_string(pos) .. " " .. err) + end +end + +local function on_rightclick(pos, node, clicker) + local nvm = techage.get_nvm(pos) + techage.set_activeformspec(pos, clicker) + M(pos):set_string("formspec", formspec(CRD(pos).State, pos, nvm)) +end + +local function on_receive_fields(pos, formname, fields, player) + if minetest.is_protected(pos, player:get_player_name()) then + return + end + + local nvm = techage.get_nvm(pos) + if menu.eval_input(pos, MENU, fields) then + M(pos):set_string("formspec", formspec(CRD(pos).State, pos, nvm)) + else + CRD(pos).State:state_button_event(pos, nvm, fields) + end +end + +local function after_dig_node(pos, oldnode, oldmetadata, digger) + Pipe:after_dig_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) + techage.del_mem(pos) +end + +local tiles = {} +-- '#' will be replaced by the stage number +tiles.pas = { + -- up, down, right, left, back, front + "techage_filling_ta#.png^techage_frame_ta#_top.png", + "techage_filling_ta#.png^techage_frame_ta#.png", + "techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_hole_pipe.png", + "techage_filling_ta#.png^techage_frame_ta#.png^techage_quarry_left.png", + "techage_filling_ta#.png^techage_appl_liquidsampler.png^techage_frame_ta#.png", + "techage_filling_ta#.png^techage_appl_liquidsampler.png^techage_frame_ta#.png", +} +tiles.act = { + -- up, down, right, left, back, front + "techage_filling_ta#.png^techage_frame_ta#_top.png", + "techage_filling_ta#.png^techage_frame_ta#.png", + "techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_hole_pipe.png", + { + name = "techage_frame14_ta#.png^techage_quarry_left14.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 2.0, + }, + }, + "techage_filling_ta#.png^techage_appl_liquidsampler.png^techage_frame_ta#.png", + "techage_filling_ta#.png^techage_appl_liquidsampler.png^techage_frame_ta#.png", +} + +local tubing = { + on_recv_message = function(pos, src, topic, payload) + if topic == "depth" then + local nvm = techage.get_nvm(pos) + return nvm.level or 0 + else + return CRD(pos).State:on_receive_message(pos, topic, payload) + end + end, + on_beduino_receive_cmnd = function(pos, src, topic, payload) + return CRD(pos).State:on_beduino_receive_cmnd(pos, topic, payload) + end, + on_beduino_request_data = function(pos, src, topic, payload) + if topic == 153 then -- Current Depth + local nvm = techage.get_nvm(pos) + return 0, {nvm.level or 0} + else + return CRD(pos).State:on_beduino_request_data(pos, topic, payload) + end + end, + on_node_load = function(pos) + CRD(pos).State:on_node_load(pos) + end, +} + +local _, _, node_name_ta4 = + techage.register_consumer("waterremover", TITLE, tiles, { + drawtype = "normal", + cycle_time = CYCLE_TIME, + standby_ticks = STANDBY_TICKS, + formspec = formspec, + tubing = tubing, + on_state_change = on_node_state_change, + after_place_node = function(pos, placer) + M(pos):set_string("owner", placer:get_player_name()) + M(pos):set_int("outdir", networks.side_to_outdir(pos, "R")) + M(pos):set_int("area_depth", 10) + M(pos):set_int("area_size", 9) + Pipe:after_place_node(pos) + end, + node_timer = keep_running, + on_receive_fields = on_receive_fields, + on_rightclick = on_rightclick, + after_dig_node = after_dig_node, + groups = {choppy=2, cracky=2, crumbly=2}, + sounds = default.node_sound_wood_defaults(), + num_items = {0,0,0,1}, + power_consumption = {0,0,0,10}, + } +) + +liquid.register_nodes({ + "techage:ta4_waterremover_pas", "techage:ta4_waterremover_act", +}, Pipe, "pump", {"R"}, {}) + +minetest.register_craft({ + output = node_name_ta4, + recipe = { + {"", "default:mese_crystal", ""}, + {"", "techage:ta3_liquidsampler_pas", ""}, + {"", "techage:ta4_wlanchip", ""}, + }, +}) diff --git a/basis/fly_lib.lua b/basis/fly_lib.lua index ed1840b..e24dfc1 100644 --- a/basis/fly_lib.lua +++ b/basis/fly_lib.lua @@ -571,6 +571,20 @@ local function is_simple_node(pos) end end +local function is_air(pos) + local node = techage.get_node_lvm(pos) + return node.name == "air" +end + +local function hidden_node(pos) + local meta = M(pos) + if meta:contains("ta_move_block") then + local node = minetest.deserialize(meta:get_string("ta_move_block")) + meta:set_string("ta_move_block", "") + return node + end +end + -- 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` @@ -640,7 +654,15 @@ local function multi_move_nodes(pos, meta, nvm, lmove, max_speed, height, move2t if is_simple_node(pos1) and is_valid_dest(pos2) 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 + else + running = true + end + elseif is_air(pos1) then + -- Try to recover the node + local node = hidden_node(pos2) + if node then + minetest.set_node(pos1, node) + running = move_node(pos, meta, pos1, lmove, max_speed, height) end else if not is_simple_node(pos1) then @@ -650,7 +672,6 @@ local function multi_move_nodes(pos, meta, nvm, lmove, max_speed, height, move2t meta:set_string("status", S("No valid destination position")) minetest.chat_send_player(owner, " [techage] " .. S("No valid destination position") .. " at " .. P2S(pos2)) end - return false end else if minetest.is_protected(pos1, owner) then @@ -660,7 +681,6 @@ local function multi_move_nodes(pos, meta, nvm, lmove, max_speed, height, move2t meta:set_string("status", S("Destination position is protected")) minetest.chat_send_player(owner, " [techage] " .. S("Destination position is protected") .. " at " .. P2S(pos2)) end - return false end running = true end diff --git a/basis/submenu.lua b/basis/submenu.lua index 46ce5da..8b30afc 100644 --- a/basis/submenu.lua +++ b/basis/submenu.lua @@ -3,7 +3,7 @@ TechAge ======= - Copyright (C) 2019-2022 Joachim Stolberg + Copyright (C) 2019-2025 Joachim Stolberg AGPL v3 See LICENSE.txt for more information @@ -38,9 +38,15 @@ end -- generate the formspec string to be placed into a container frame -local function generate_formspec_substring(pos, meta, form_def, player_name) +local function generate_formspec_substring(pos, meta, form_def, player_name, width, no_note) local tbl = {} local player_inv_needed = false + width = width or "10" + local xpos1 = tostring(tonumber(width) / 2 - 0.25) + local xpos2 = tostring(tonumber(width) / 2) + local xpos3 = tostring(tonumber(width) / 2 + 0.3) + local xpos4 = tostring(tonumber(width) / 2 + 0.5) + local xsize = tostring(tonumber(width) / 2) if meta and form_def then local nvm = techage.get_nvm(pos) @@ -56,9 +62,9 @@ local function generate_formspec_substring(pos, meta, form_def, player_name) val = meta:get_int(elem.name) end if nvm.running or techage.is_running(nvm) then - tbl[#tbl+1] = "label[4.75," .. offs .. ";" .. val .. "]" + tbl[#tbl+1] = "label[" .. xpos1 .. "," .. offs .. ";" .. val .. "]" else - tbl[#tbl+1] = "field[5," .. (offs+0.2) .. ";5.3,1;" .. elem.name .. ";;" .. val .. "]" + tbl[#tbl+1] = "field[" .. xpos2 .. "," .. (offs+0.2) .. ";" .. xpos3 .. ",1;" .. elem.name .. ";;" .. val .. "]" end elseif elem.type == "numbers" then local val = elem.default @@ -66,9 +72,9 @@ local function generate_formspec_substring(pos, meta, form_def, player_name) val = meta:get_string(elem.name) end if nvm.running or techage.is_running(nvm) then - tbl[#tbl+1] = "label[4.75," .. offs .. ";" .. val .. "]" + tbl[#tbl+1] = "label[" .. xpos1 .. "," .. offs .. ";" .. val .. "]" else - tbl[#tbl+1] = "field[5," .. (offs+0.2) .. ";5.3,1;" .. elem.name .. ";;" .. val .. "]" + tbl[#tbl+1] = "field[" .. xpos2 .. "," .. (offs+0.2) .. ";" .. xpos3 .. ",1;" .. elem.name .. ";;" .. val .. "]" end elseif elem.type == "float" then local val = elem.default @@ -76,9 +82,9 @@ local function generate_formspec_substring(pos, meta, form_def, player_name) val = tonumber(meta:get_string(elem.name)) or 0 end if nvm.running or techage.is_running(nvm) then - tbl[#tbl+1] = "label[4.75," .. offs .. ";" .. val .. "]" + tbl[#tbl+1] = "label[" .. xpos1 .. "," .. offs .. ";" .. val .. "]" else - tbl[#tbl+1] = "field[5," .. (offs+0.2) .. ";5.3,1;" .. elem.name .. ";;" .. val .. "]" + tbl[#tbl+1] = "field[" .. xpos2 .. "," .. (offs+0.2) .. ";" .. xpos3 .. ",1;" .. elem.name .. ";;" .. val .. "]" end elseif elem.type == "ascii" then local val = elem.default @@ -86,25 +92,30 @@ local function generate_formspec_substring(pos, meta, form_def, player_name) val = meta:get_string(elem.name) end if nvm.running or techage.is_running(nvm) then - tbl[#tbl+1] = "label[4.75," .. offs .. ";" .. minetest.formspec_escape(val) .. "]" + tbl[#tbl+1] = "label[" .. xpos1 .. "," .. offs .. ";" .. minetest.formspec_escape(val) .. "]" else - tbl[#tbl+1] = "field[5," .. (offs+0.2) .. ";5.3,1;" .. elem.name .. ";;" .. minetest.formspec_escape(val) .. "]" + tbl[#tbl+1] = "field[" .. xpos2 .. "," .. (offs+0.2) .. ";" .. xpos3 .. ",1;" .. elem.name .. ";;" .. minetest.formspec_escape(val) .. "]" end elseif elem.type == "const" then - tbl[#tbl+1] = "label[4.75," .. offs .. ";" .. elem.value .. "]" + tbl[#tbl+1] = "label[" .. xpos1 .. "," .. offs .. ";" .. elem.value .. "]" elseif elem.type == "output" then local val = nvm[elem.name] or meta:get_string(elem.name) or "" if tonumber(val) then val = techage.round(val) end - tbl[#tbl+1] = "label[4.75," .. offs .. ";" .. val .. "]" + tbl[#tbl+1] = "label[" .. xpos1 .. "," .. offs .. ";" .. val .. "]" elseif elem.type == "dropdown" then if nvm.running or techage.is_running(nvm) then local val = elem.default or "" if meta:contains(elem.name) then val = meta:get_string(elem.name) or "" + if elem.values then + local idx = index(elem.values, val) or 1 + local l = elem.choices:split(",") + val = l[idx] + end end - tbl[#tbl+1] = "label[4.75," .. offs .. ";" .. val .. "]" + tbl[#tbl+1] = "label[" .. xpos1 .. "," .. offs .. ";" .. val .. "]" elseif elem.on_dropdown then -- block provides a specific list of choice elements local val = elem.default if meta:contains(elem.name) then @@ -113,7 +124,7 @@ local function generate_formspec_substring(pos, meta, form_def, player_name) local choices = elem.on_dropdown(pos) local l = choices:split(",") local idx = index(l, val) or 1 - tbl[#tbl+1] = "dropdown[4.72," .. (offs) .. ";5.5,1.4;" .. elem.name .. ";" .. choices .. ";" .. idx .. "]" + tbl[#tbl+1] = "dropdown[" .. xpos1 .. "," .. (offs) .. ";" .. xpos4 .. ",1.4;" .. elem.name .. ";" .. choices .. ";" .. idx .. "]" else local val = elem.default if meta:contains(elem.name) then @@ -126,19 +137,19 @@ local function generate_formspec_substring(pos, meta, form_def, player_name) local l = elem.choices:split(",") idx = index(l, val) or 1 end - tbl[#tbl+1] = "dropdown[4.72," .. (offs) .. ";5.5,1.4;" .. elem.name .. ";" .. elem.choices .. ";" .. idx .. "]" + tbl[#tbl+1] = "dropdown[" .. xpos1 .. "," .. (offs) .. ";" .. xpos4 .. ",1.4;" .. elem.name .. ";" .. elem.choices .. ";" .. idx .. "]" end elseif elem.type == "items" then -- inventory if elem.size then - tbl[#tbl+1] = "list[detached:" .. minetest.formspec_escape(player_name) .. "_techage_wrench_menu;cfg;4.75," .. offs .. ";" .. elem.size .. ",1;]" + tbl[#tbl+1] = "list[detached:" .. minetest.formspec_escape(player_name) .. "_techage_wrench_menu;cfg;" .. xpos1 .. "," .. offs .. ";" .. elem.size .. ",1;]" else - tbl[#tbl+1] = "list[detached:" .. minetest.formspec_escape(player_name) .. "_techage_wrench_menu;cfg;4.75," .. offs .. ";" .. elem.width .. "," .. elem.height .. ";]" + tbl[#tbl+1] = "list[detached:" .. minetest.formspec_escape(player_name) .. "_techage_wrench_menu;cfg;" .. xpos1 .. "," .. offs .. ";" .. elem.width .. "," .. elem.height .. ";]" end player_inv_needed = true end end - if nvm.running or techage.is_running(nvm) then - local offs = #form_def * 0.9 - 0.2 + if not no_note and (nvm.running or techage.is_running(nvm)) then + local offs = #form_def * 0.9 - 0.3 tbl[#tbl+1] = "label[0," .. offs .. ";" .. S("Note: You can't change any values while the block is running!") .. "]" end end @@ -304,6 +315,45 @@ function techage.menu.generate_formspec(pos, ndef, form_def, player_name) return "" end +function techage.menu.generate_formspec_container(pos, ndef, form_def, ypos, width) + local meta = minetest.get_meta(pos) + local number = techage.get_node_number(pos) or "-" + local mem = techage.get_mem(pos) + mem.star = ((mem.star or 0) + 1) % 2 + local star = mem.star == 1 and " *" or " " + local bttn_width = (width - 0.3) / 3 + if meta and number and ndef and form_def then + local _, text = generate_formspec_substring(pos, meta, form_def, "", width, true) + local yoffs = math.min(#form_def, 8) * 0.9 + 0.7 + local buttons = "button[0.1," .. yoffs .. ";" .. bttn_width .. ",1;refresh;" .. S("Refresh") .. star .. "]" .. + "button_exit[" .. (bttn_width + 0.2) .. "," .. yoffs .. ";" .. bttn_width .. ",1;cancel;" .. S("Cancel") .. "]" .. + "button[" .. (2 * bttn_width + 0.3) .. "," .. yoffs .. ";" .. bttn_width .. ",1;save;" .. S("Save") .. "]" + + + if #form_def > 8 then + local size = (#form_def * 10) - 60 + return "container[0," .. ypos .. "]" .. + "box[0,0;" .. width .. "," .. (yoffs + 0.8) .. ";#395c74]".. + "scrollbaroptions[max=" .. size .. "]" .. + "scrollbar[9.4,0.6;0.4,7.7;vertical;wrenchmenu;]" .. + "scroll_container[0,1;12,9;wrenchmenu;vertical;]" .. + text .. + "scroll_container_end[]" .. + buttons .. + "container_end[]" + else + return "container[0," .. ypos .. "]" .. + "container[0,1]" .. + "box[-0.1,-0.3;" .. (width + 0.2) .. "," .. (yoffs + 0.3) .. ";#395c74]".. + text .. + "container_end[]" .. + buttons .. + "container_end[]" + end + end + return "" +end + function techage.menu.eval_input(pos, form_def, fields, player_name) if fields.save or fields.key_enter_field then local meta = minetest.get_meta(pos) diff --git a/doc/items.lua b/doc/items.lua index b63bf99..a315474 100644 --- a/doc/items.lua +++ b/doc/items.lua @@ -210,6 +210,7 @@ local items = { ta4_chargedetector = "techage:ta4_chargedetector_off", ta4_gaze_sensor = "techage:ta4_gaze_sensor_off", ta4_nodedetector = "techage:ta4_nodedetector_off", + ta4_waterremover = "techage:ta4_waterremover_pas", ---------------------------- techage_ta5 = "techage:ta5_fr_nucleus", ta5_flycontroller = "techage:ta5_flycontroller", diff --git a/doc/manual_ta4_DE.lua b/doc/manual_ta4_DE.lua index 91f58d5..e87f00c 100644 --- a/doc/manual_ta4_DE.lua +++ b/doc/manual_ta4_DE.lua @@ -89,6 +89,7 @@ return { "3,TA4 Kiessieb / Gravel Sieve", "3,TA4 Mühle / Grinder", "3,TA4 Steinbrecher / Quarry", + "3,TA4 Wasserentferner / Water Remover", "3,TA4 Elektronikfabrik / Electronic Fab", "3,TA4 Injektor / Injector", "3,TA4 Recycler", @@ -837,6 +838,20 @@ return { "\n".. "\n".. "\n", + "Der Wasserentferner entfernt Wasser aus einer Fläche von bis zu 21 x 21 x 80 m.\n".. + "Der Hauptzweck ist die Entwässerung von Höhlen. Er kann aber auch verwendet werden\\, um ein Loch ins Meer zu „bohren“.\n".. + "\n".. + "Der Wasserentferner benötigt Strom und eine Rohrverbindung zu einem Flüssigkeitstank.\n".. + "\n".. + "Der Wasserentferner wird am höchsten Punkt der Höhle platziert und entfernt das Wasser\n".. + "aus der Höhle zum tiefsten Punkt. Der Wasserentferner gräbt alle zwei Sekunden einen Wasserblock. \n".. + "Das Gerät benötigt 10 Ku Strom.\n".. + "\n".. + "Technisch gesehen ersetzt der Wasserentferner die Wasserblöcke durch einen speziellen Luftblock\\,\n".. + "der nicht sichtbar und nicht begehbar ist\\, aber verhindert\\, dass das Wasser zurückfließt.\n".. + "\n".. + "\n".. + "\n", "Die Funktion entspricht der von TA2\\, nur werden hier verschiedene Chips produziert.\n".. "Die Verarbeitungsleistung beträgt ein Chip alle 6 s. Der Block benötigt hierfür 12 ku Strom.\n".. "\n".. @@ -951,6 +966,7 @@ return { "ta4_gravelsieve", "ta4_grinder", "ta4_quarry", + "ta4_waterremover", "ta4_electronicfab", "ta4_injector", "ta4_recycler", @@ -1048,5 +1064,6 @@ return { "", "", "", + "", } } \ No newline at end of file diff --git a/doc/manual_ta4_EN.lua b/doc/manual_ta4_EN.lua index ea1c55e..fb58c28 100644 --- a/doc/manual_ta4_EN.lua +++ b/doc/manual_ta4_EN.lua @@ -89,6 +89,7 @@ return { "3,TA4 Gravel Sieve", "3,TA4 Grinder", "3,TA4 Quarry", + "3,TA4 Water Remover", "3,TA4 Electronic Fab", "3,TA4 Injector", "3,TA4 Recycler", @@ -838,6 +839,19 @@ return { "\n".. "\n".. "\n", + "The Water Remover removes water from an area of up to 21 x 21 x 80 m. The main\n".. + "purpose is to drain caves. But it can also be used to \"drill\" a hole into the sea.\n".. + "\n".. + "The Water Remover needs electricity and a pipe connection to a liquid tank. The\n".. + "Water Remover is placed at the highest point of the cave and removes the water\n".. + "from the cave to the lowest point. The Water Remover digs one water block every\n".. + "two seconds. The device requires 10 ku of electricity.\n".. + "\n".. + "Technically\\, the Water Remover replaces the water blocks with a special air block\n".. + "that is not visible and not walkable but prevents the water from flowing back.\n".. + "\n".. + "\n".. + "\n", "The function corresponds to that of TA2\\, only different chips are produced here.\n".. "The processing power is one chip every 6 s. The block requires 12 ku of electricity for this.\n".. "\n".. @@ -953,6 +967,7 @@ return { "ta4_gravelsieve", "ta4_grinder", "ta4_quarry", + "ta4_waterremover", "ta4_electronicfab", "ta4_injector", "ta4_recycler", @@ -1050,5 +1065,6 @@ return { "", "", "", + "", } } \ No newline at end of file diff --git a/init.lua b/init.lua index c2a766e..bb59229 100644 --- a/init.lua +++ b/init.lua @@ -199,6 +199,7 @@ dofile(MP.."/basic_machines/recycler.lua") dofile(MP.."/basic_machines/concentrator.lua") dofile(MP.."/basic_machines/recipeblock.lua") dofile(MP.."/basic_machines/ta5_chest.lua") +dofile(MP.."/basic_machines/waterremover.lua") -- Liquids II dofile(MP.."/liquids/tank.lua") diff --git a/locale/techage.de.tr b/locale/techage.de.tr index 269c379..28e6c18 100644 --- a/locale/techage.de.tr +++ b/locale/techage.de.tr @@ -1172,9 +1172,13 @@ Hole size=Lochgröße Quarry=Steinbrecher Start level=Startebene Start level @= 0@nmeans the same level@nas the quarry is placed=Startebene @= 0@nbedeutet gleiche Ebene@nwie der Steinbrecher +inventory full=Inventar ist voll + +### quarry.lua ### +### waterremover.lua ### + area is protected=Bereich ist geschützt finished=fertig -inventory full=Inventar ist voll ### reboiler.lua ### @@ -1582,6 +1586,17 @@ TA1 Watermill=TA1 Wasssermühle TA4 Water Pump=Wasserpumpe Water Pump=Wasserpumpe +### waterremover.lua ### + +Area depth=Flächentiefe +Area size=Flächengröße +Area where the water is to be removed=Fläche, aus der das Wasser entfernt werden soll +Current depth=Aktuelle Tiefe +Current working depth of the water remover=Aktuelle Arbeitstiefe des Wasserentferners +Depth of the area where the water is to be removed (1-80)=Tiefe der Fläche, aus der das Wasser entfernt werden soll (1-80) +Water Remover=Wasserentferner +tank full=Tank voll + ### windturbine_lib.lua ### Here is not enough water (41x41 m)!=Hier ist nicht genug Wasser (41x41 m)! diff --git a/locale/techage.fr.tr b/locale/techage.fr.tr index b30985e..a4a4883 100644 --- a/locale/techage.fr.tr +++ b/locale/techage.fr.tr @@ -1172,9 +1172,13 @@ Hole size= Quarry= Start level= Start level @= 0@nmeans the same level@nas the quarry is placed= +inventory full= + +### quarry.lua ### +### waterremover.lua ### + area is protected= finished= -inventory full= ### reboiler.lua ### @@ -1582,6 +1586,17 @@ TA1 Watermill= TA4 Water Pump=TA4 Pompe à eau Water Pump=Pompe à eau +### waterremover.lua ### + +Area depth= +Area size= +Area where the water is to be removed= +Current depth= +Current working depth of the water remover= +Depth of the area where the water is to be removed (1-80)= +Water Remover= +tank full= + ### windturbine_lib.lua ### Here is not enough water (41x41 m)!=Ici, il n'y a pas assez d'eau (41x41 m)! diff --git a/locale/techage.ru.tr b/locale/techage.ru.tr index 0cfb05a..5eae3d5 100644 --- a/locale/techage.ru.tr +++ b/locale/techage.ru.tr @@ -1172,9 +1172,13 @@ Hole size=Глубина ямы Quarry=Карьер Start level=Начальный высота Start level @= 0@nmeans the same level@nas the quarry is placed=Начальная высота @= 0@nозначает ту же высоту, что имеется у карьера +inventory full=инвентарь заполнен + +### quarry.lua ### +### waterremover.lua ### + area is protected=зона защищена finished=готово -inventory full=инвентарь заполнен ### reboiler.lua ### @@ -1582,6 +1586,17 @@ TA1 Watermill=TA1 Водяное колесо TA4 Water Pump=TA4 Водяной насос Water Pump=Водяной насос +### waterremover.lua ### + +Area depth= +Area size= +Area where the water is to be removed= +Current depth= +Current working depth of the water remover= +Depth of the area where the water is to be removed (1-80)= +Water Remover= +tank full= + ### windturbine_lib.lua ### Here is not enough water (41x41 m)!=Здесь недостаточно воды (41x41 м)! diff --git a/locale/template.txt b/locale/template.txt index 4ee8999..3847e97 100644 --- a/locale/template.txt +++ b/locale/template.txt @@ -1172,9 +1172,13 @@ Hole size= Quarry= Start level= Start level @= 0@nmeans the same level@nas the quarry is placed= +inventory full= + +### quarry.lua ### +### waterremover.lua ### + area is protected= finished= -inventory full= ### reboiler.lua ### @@ -1582,6 +1586,17 @@ TA1 Watermill= TA4 Water Pump= Water Pump= +### waterremover.lua ### + +Area depth= +Area size= +Area where the water is to be removed= +Current depth= +Current working depth of the water remover= +Depth of the area where the water is to be removed (1-80)= +Water Remover= +tank full= + ### windturbine_lib.lua ### Here is not enough water (41x41 m)!= diff --git a/logic/terminal.lua b/logic/terminal.lua index 1f7ed88..fd69683 100644 --- a/logic/terminal.lua +++ b/logic/terminal.lua @@ -49,6 +49,7 @@ are possible.]] local SYNTAX_ERR = S("Syntax error, try help") local WRENCH_MENU = { + [0] = {"techage:terminal2"}, --valid_nodes { type = "dropdown", choices = "all players,me", @@ -76,7 +77,7 @@ local function get_string(meta, num, default) return s end -local function formspec2(meta) +local function formspec2(pos, meta) local output = meta:get_string("output") local command = meta:get_string("command") output = minetest.formspec_escape(output) @@ -90,9 +91,13 @@ local function formspec2(meta) local bttn_text7 = get_string(meta, 7, "User7") local bttn_text8 = get_string(meta, 8, "User8") local bttn_text9 = get_string(meta, 9, "User9") + local wrench_image = "" + if minetest.get_node(pos).name == "techage:terminal2" then + wrench_image = techage.wrench_image(9.6, -0.2) + end return "size[10,8.5]".. --"style_type[table,field;font=mono]".. - techage.wrench_image(9.6, -0.2) .. + wrench_image .. "button[0,-0.2;3.3,1;bttn1;"..bttn_text1.."]button[3.3,-0.2;3.3,1;bttn2;"..bttn_text2.."]button[6.6,-0.2;3.2,1;bttn3;"..bttn_text3.."]".. "button[0,0.6;3.3,1;bttn4;"..bttn_text4.."]button[3.3,0.6;3.3,1;bttn5;"..bttn_text5.."]button[6.6,0.6;3.4,1;bttn6;"..bttn_text6.."]".. "button[0,1.4;3.3,1;bttn7;"..bttn_text7.."]button[3.3,1.4;3.3,1;bttn8;"..bttn_text8.."]button[6.6,1.4;3.4,1;bttn9;"..bttn_text9.."]".. @@ -107,14 +112,14 @@ local function output(pos, text) text = meta:get_string("output") .. "\n" .. (text or "") text = text:sub(-1000,-1) meta:set_string("output", text) - meta:set_string("formspec", formspec2(meta)) + meta:set_string("formspec", formspec2(pos, meta)) end local function append(pos, text) local meta = minetest.get_meta(pos) text = meta:get_string("output") .. (text or "") meta:set_string("output", text) - meta:set_string("formspec", formspec2(meta)) + meta:set_string("formspec", formspec2(pos, meta)) end local function get_line_text(pos, num) @@ -188,7 +193,7 @@ local function command(pos, command, player, is_ta4) if cmnd == "clear" then meta:set_string("output", "") - meta:set_string("formspec", formspec2(meta)) + meta:set_string("formspec", formspec2(pos, meta)) elseif cmnd == "" then output(pos, "$") elseif cmnd == "help" then @@ -255,7 +260,7 @@ local function command(pos, command, player, is_ta4) if bttn_num and label and cmnd then meta:set_string("bttn_text"..bttn_num, label) meta:set_string("bttn_cmnd"..bttn_num, cmnd) - meta:set_string("formspec", formspec2(meta)) + meta:set_string("formspec", formspec2(pos, meta)) return end @@ -303,7 +308,7 @@ local function register_terminal(name, description, tiles, node_box, selection_b local meta = minetest.get_meta(pos) meta:set_string("node_number", number) meta:set_string("command", S("commands like: help")) - meta:set_string("formspec", formspec2(meta)) + meta:set_string("formspec", formspec2(pos, meta)) if placer then meta:set_string("owner", placer:get_player_name()) end @@ -320,22 +325,22 @@ local function register_terminal(name, description, tiles, node_box, selection_b if evt.type == "DCL" then local s = get_line_text(pos, evt.row) meta:set_string("command", s) - meta:set_string("formspec", formspec2(meta)) + meta:set_string("formspec", formspec2(pos, meta)) return elseif (fields.ok or fields.key_enter_field) and fields.cmnd then local is_ta4 = string.find(description, "TA4") command(pos, fields.cmnd, player:get_player_name(), is_ta4) techage.historybuffer_add(pos, fields.cmnd) meta:set_string("command", "") - meta:set_string("formspec", formspec2(meta)) + meta:set_string("formspec", formspec2(pos, meta)) return elseif fields.key_up then meta:set_string("command", techage.historybuffer_priv(pos)) - meta:set_string("formspec", formspec2(meta)) + meta:set_string("formspec", formspec2(pos, meta)) return elseif fields.key_down then meta:set_string("command", techage.historybuffer_next(pos)) - meta:set_string("formspec", formspec2(meta)) + meta:set_string("formspec", formspec2(pos, meta)) return end end @@ -354,7 +359,7 @@ local function register_terminal(name, description, tiles, node_box, selection_b end, ta_after_formspec = function(pos, fields, playername) - if fields.save then + if fields.save and minetest.get_node(pos).name == "techage:terminal2" then if M(pos):get_string("opmode") == "basic" then if minetest.global_exists("nanobasic") then local node = techage.get_node_lvm(pos) diff --git a/manuals/manual_ta4_DE.md b/manuals/manual_ta4_DE.md index 6212744..a06cde0 100644 --- a/manuals/manual_ta4_DE.md +++ b/manuals/manual_ta4_DE.md @@ -994,6 +994,22 @@ Die maximale Tiefe beträgt 80 Meter. Der Steinbrecher benötigt 14 ku Strom. [ta4_quarry|image] +### TA4 Wasserentferner / Water Remover + +Der Wasserentferner entfernt Wasser aus einer Fläche von bis zu 21 x 21 x 80 m. +Der Hauptzweck ist die Entwässerung von Höhlen. Er kann aber auch verwendet werden, um ein Loch ins Meer zu „bohren“. + +Der Wasserentferner benötigt Strom und eine Rohrverbindung zu einem Flüssigkeitstank. + +Der Wasserentferner wird am höchsten Punkt der Höhle platziert und entfernt das Wasser +aus der Höhle zum tiefsten Punkt. Der Wasserentferner gräbt alle zwei Sekunden einen Wasserblock. +Das Gerät benötigt 10 Ku Strom. + +Technisch gesehen ersetzt der Wasserentferner die Wasserblöcke durch einen speziellen Luftblock, +der nicht sichtbar und nicht begehbar ist, aber verhindert, dass das Wasser zurückfließt. + +[ta4_waterremover|image] + ### TA4 Elektronikfabrik / Electronic Fab Die Funktion entspricht der von TA2, nur werden hier verschiedene Chips produziert. diff --git a/manuals/manual_ta4_EN.md b/manuals/manual_ta4_EN.md index 5791b73..cfee4c2 100644 --- a/manuals/manual_ta4_EN.md +++ b/manuals/manual_ta4_EN.md @@ -986,6 +986,21 @@ The maximum depth is 80 meters. The quarry requires 14 ku of electricity. [ta4_quarry|image] +### TA4 Water Remover + +The Water Remover removes water from an area of up to 21 x 21 x 80 m. The main +purpose is to drain caves. But it can also be used to "drill" a hole into the sea. + +The Water Remover needs electricity and a pipe connection to a liquid tank. The +Water Remover is placed at the highest point of the cave and removes the water +from the cave to the lowest point. The Water Remover digs one water block every +two seconds. The device requires 10 ku of electricity. + +Technically, the Water Remover replaces the water blocks with a special air block +that is not visible and not walkable but prevents the water from flowing back. + +[ta4_waterremover|image] + ### TA4 Electronic Fab The function corresponds to that of TA2, only different chips are produced here. diff --git a/manuals/nanobasic.md b/manuals/nanobasic.md index 58770e1..544d281 100644 --- a/manuals/nanobasic.md +++ b/manuals/nanobasic.md @@ -1055,6 +1055,7 @@ SLEEP(seconds) ``` The SLEEP function is used to pause program execution for a specified number of seconds. +If the number of seconds is 0, the program pauses one time slice (0.2 seconds). ### SPC @@ -1528,6 +1529,7 @@ corresponds to the error from previous chapter. | TA4 Pusher Counter | 150 | - | number | Read the number of pushed items for a TA4 Pusher in "flow limiter" mode | | TA4 Pump Counter | 151 | - | number | Read the number of pumped liquid units for a TA4 Pump in "flow limiter" mode | | Multi Button State | 152 | num | state | Read the button state (TA4 2x Button, TA4 4x Button)
`num` is the button number (1..4), `state`: 0 = "off", 1 = "on" | +| Water Remover Depth | 153 | - | depth | Current depth value of a remover (1..80) | ### CMD$ Commands with Response as String Value diff --git a/sounds/techage_scoopwater.ogg b/sounds/techage_scoopwater.ogg new file mode 100644 index 0000000000000000000000000000000000000000..43113d9991e2e3c62d219714572e620e51fc67a0 GIT binary patch literal 19734 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 Vcd?R`-bIb