built on 31/12/2022 10:51:49

This commit is contained in:
Joachim Stolberg 2022-12-31 10:51:49 +01:00
parent 8321aff4c7
commit d734800e13
63 changed files with 761 additions and 567 deletions

View File

@ -323,6 +323,11 @@ local function door_command(floor_pos, facedir, cmnd, sound)
-- one step up -- one step up
local door_pos1 = hyperloop.new_pos(floor_pos, facedir, "1B", 0) local door_pos1 = hyperloop.new_pos(floor_pos, facedir, "1B", 0)
local door_pos2 = hyperloop.new_pos(floor_pos, facedir, "1B", 1) local door_pos2 = hyperloop.new_pos(floor_pos, facedir, "1B", 1)
local meta = M(floor_pos)
local owner = meta:contains("owner") and meta:get_string("owner")
if owner and (minetest.is_protected(door_pos1, owner) or minetest.is_protected(door_pos2, owner)) then
return
end
local node1 = minetest.get_node(door_pos1) local node1 = minetest.get_node(door_pos1)
local node2 = minetest.get_node(door_pos2) local node2 = minetest.get_node(door_pos2)
@ -455,6 +460,7 @@ minetest.register_node("hyperloop:elevator_bottom", {
"field[0.5,1.5;5,1;floor;"..S("Floor name")..";"..S("Base").."]" .. "field[0.5,1.5;5,1;floor;"..S("Floor name")..";"..S("Base").."]" ..
"button_exit[2,3;2,1;exit;"..S("Save").."]" "button_exit[2,3;2,1;exit;"..S("Save").."]"
meta:set_string("formspec", formspec) meta:set_string("formspec", formspec)
meta:set_string("owner", placer:get_player_name())
-- add upper part of the car -- add upper part of the car
pos = Shaft:get_pos(pos, 6) pos = Shaft:get_pos(pos, 6)

View File

@ -266,7 +266,7 @@ local function collect_network_nodes(pos, tlib2, outdir)
-- outdir corresponds to the indir coming from -- outdir corresponds to the indir coming from
connection_walk(pos, outdir, Flip[outdir], node, tlib2, function(pos, indir, node) connection_walk(pos, outdir, Flip[outdir], node, tlib2, function(pos, indir, node)
local ndef = net_def2(pos, node.name, netw_type) local ndef = net_def2(pos, node.name, netw_type)
if ndef then if ndef and ndef.ntype then
local ntype = ndef.ntype local ntype = ndef.ntype
if not netw[ntype] then netw[ntype] = {} end if not netw[ntype] then netw[ntype] = {} end
netw[ntype][#netw[ntype] + 1] = {pos = pos, indir = indir} netw[ntype][#netw[ntype] + 1] = {pos = pos, indir = indir}

View File

@ -102,7 +102,7 @@ local function format_error_str(str, label)
if s:find("function 'xpcall'") then if s:find("function 'xpcall'") then
break break
elseif s:find(".-%.lua:%d+:(.+)") then elseif s:find(".-%.lua:%d+:(.+)") then
local err = s:gsub(".-%.lua:%d+:%s*(.+)", "extern: %1") local err = s:gsub(".-%.lua:%d+:%s*(.+)", "%1")
table.insert(tbl, err) table.insert(tbl, err)
elseif s:find('%[string ".-"%]') then elseif s:find('%[string ".-"%]') then
local line, err = s:match('^%[string ".-"%]:(%d+): (.+)$') local line, err = s:match('^%[string ".-"%]:(%d+): (.+)$')
@ -134,6 +134,24 @@ local function compile(pos, text, label, err_clbk)
end end
end end
local function runtime_delimiter(code)
local time = minetest.get_us_time()
local timeout = function ()
if minetest.get_us_time() - time > safer_lua.MaxExeTime then
debug.sethook()
error("Runtime limit exceeded")
end
end
debug.sethook(timeout, "c")
code()
debug.sethook()
end
local function error_handler(...)
debug.sethook()
return debug.traceback(...)
end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Standard init/loop controller -- Standard init/loop controller
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -148,7 +166,7 @@ function safer_lua.init(pos, init, loop, environ, err_clbk)
env.S = {} env.S = {}
env.S = map(env.S, environ) env.S = map(env.S, environ)
setfenv(code, env) setfenv(code, env)
local res, err = xpcall(code, debug.traceback) local res, err = xpcall(runtime_delimiter, error_handler, code)
if not res then if not res then
err_clbk(pos, format_error(err, "init")) err_clbk(pos, format_error(err, "init"))
else else
@ -171,7 +189,7 @@ function safer_lua.run_loop(pos, elapsed, code, err_clbk)
env.event = false env.event = false
env.ticks = env.ticks + 1 env.ticks = env.ticks + 1
end end
local res, err = xpcall(code, debug.traceback) local res, err = xpcall(runtime_delimiter, error_handler, code)
if calc_used_mem_size(env) > safer_lua.MaxTableSize then if calc_used_mem_size(env) > safer_lua.MaxTableSize then
err_clbk(pos, "Error: Data memory limit exceeded") err_clbk(pos, "Error: Data memory limit exceeded")
return false return false
@ -188,7 +206,7 @@ end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
local function thread(pos, code, err_clbk) local function thread(pos, code, err_clbk)
while true do while true do
local res, err = xpcall(code, debug.traceback) local res, err = xpcall(runtime_delimiter, error_handler, code)
if not res then if not res then
err_clbk(pos, format_error(err, "loop")) err_clbk(pos, format_error(err, "loop"))
return false return false

View File

@ -8,7 +8,7 @@ A subset of the language Lua for safe and secure Lua sandboxes with:
- limited posibilities to call functions - limited posibilities to call functions
### License ### License
Copyright (C) 2018-2021 Joachim Stolberg Copyright (C) 2018-2022 Joachim Stolberg
Code: Licensed under the GNU LGPL version 2.1 or later. See LICENSE.txt Code: Licensed under the GNU LGPL version 2.1 or later. See LICENSE.txt
@ -19,3 +19,4 @@ none
- 2018-06-24 v0.01 * first draft - 2018-06-24 v0.01 * first draft
- 2020-03-14 v1.00 * extracted from TechPack and released - 2020-03-14 v1.00 * extracted from TechPack and released
- 2021-11-28 v1.01 * function `string.split2` added, `unpack` removed - 2021-11-28 v1.01 * function `string.split2` added, `unpack` removed
- 2022-12-22 v1.02 * Limit code execution time for recursive function calls (#3 by Thomas--S)

View File

@ -48,6 +48,7 @@ function signs_bot.move_robot(mem)
if node4.name == "signs_bot:robot_foot" then if node4.name == "signs_bot:robot_foot" then
minetest.swap_node(pos4, mem.stored_node or {name = "air"}) minetest.swap_node(pos4, mem.stored_node or {name = "air"})
end end
minetest.swap_node(pos3, {name = "air"})
minetest.remove_node(pos) minetest.remove_node(pos)
else else
minetest.swap_node(pos, mem.stored_node or {name = "air"}) minetest.swap_node(pos, mem.stored_node or {name = "air"})
@ -319,7 +320,7 @@ signs_bot.register_botcommand("stop", {
if mem.capa then if mem.capa then
mem.capa = mem.capa + 2 mem.capa = mem.capa + 2
end end
return signs_bot.DONE return signs_bot.BUSY
end, end,
}) })

View File

@ -168,6 +168,7 @@ signs_bot.register_botcommand("place_below", {
end, end,
cmnd = function(base_pos, mem, slot) cmnd = function(base_pos, mem, slot)
slot = tonumber(slot) or 0 slot = tonumber(slot) or 0
mem.stored_node = {name = "air"}
return place_item_below(base_pos, mem.robot_pos, mem.robot_param2, slot) return place_item_below(base_pos, mem.robot_pos, mem.robot_param2, slot)
end, end,
}) })

View File

@ -129,6 +129,7 @@ end
local function gen_string_cmnd(code, pc, num_param, script) local function gen_string_cmnd(code, pc, num_param, script)
local tokens = tokenizer(script) local tokens = tokenizer(script)
pc = math.min(pc, #tokens)
if num_param == 0 then if num_param == 0 then
return tokens[pc] return tokens[pc]
elseif num_param == 1 then elseif num_param == 1 then

View File

@ -109,17 +109,19 @@ techage.register_node({"default:furnace", "default:furnace_active"}, {
local inv = meta:get_inventory() local inv = meta:get_inventory()
return techage.get_items(pos, inv, "dst", num) return techage.get_items(pos, inv, "dst", num)
end, end,
on_push_item = function(pos, side, stack) on_push_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local inv = meta:get_inventory() local inv = meta:get_inventory()
minetest.get_node_timer(pos):start(1.0) minetest.get_node_timer(pos):start(1.0)
if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then if in_dir == 5 then
return techage.put_items(inv, "src", stack)
elseif minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then
return techage.put_items(inv, "fuel", stack) return techage.put_items(inv, "fuel", stack)
else else
return techage.put_items(inv, "src", stack) return techage.put_items(inv, "src", stack)
end end
end, end,
on_unpull_item = function(pos, side, stack) on_unpull_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local inv = meta:get_inventory() local inv = meta:get_inventory()
return techage.put_items(inv, "dst", stack) return techage.put_items(inv, "dst", stack)

View File

@ -431,4 +431,5 @@ if minetest.global_exists("farming") then
techage.add_grinder_recipe({input="farming:seed_rice 6", output="farming:rice_flour"}, true) techage.add_grinder_recipe({input="farming:seed_rice 6", output="farming:rice_flour"}, true)
techage.add_grinder_recipe({input="farming:oat 3", output="farming:flour"}, true) techage.add_grinder_recipe({input="farming:oat 3", output="farming:flour"}, true)
techage.add_grinder_recipe({input="farming:seed_oat 6", output="farming:flour"}, true) techage.add_grinder_recipe({input="farming:seed_oat 6", output="farming:flour"}, true)
techage.add_grinder_recipe({input="farming:seed_cotton 3", output="basic_materials:oil_extract"}, true)
end end

View File

@ -26,7 +26,7 @@ local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm
local S = techage.S local S = techage.S
local CYCLE_TIME = 3 local CYCLE_TIME = 4
local STANDBY_TICKS = 4 local STANDBY_TICKS = 4
local COUNTDOWN_TICKS = 4 local COUNTDOWN_TICKS = 4
@ -205,6 +205,7 @@ local function quarry_task(pos, crd, nvm)
if not is_air_level(pos1, pos2, nvm.hole_diameter) then if not is_air_level(pos1, pos2, nvm.hole_diameter) then
mark_area(pos1, pos2, owner) mark_area(pos1, pos2, owner)
M(pos):set_string("formspec", formspec(CRD(pos).State, pos, nvm))
coroutine.yield() coroutine.yield()
for zoffs = 1, nvm.hole_diameter do for zoffs = 1, nvm.hole_diameter do

View File

@ -613,10 +613,10 @@ techage.register_node({"techage:ta4_chest"}, {
on_recv_message = function(pos, src, topic, payload) on_recv_message = function(pos, src, topic, payload)
if topic == "count" then if topic == "count" then
local nvm = techage.get_nvm(pos) local nvm = techage.get_nvm(pos)
return get_count(nvm, tonumber(payload or 1) or 1) return get_count(nvm, tonumber(payload or 0) or 0)
elseif topic == "itemstring" then elseif topic == "itemstring" then
local nvm = techage.get_nvm(pos) local nvm = techage.get_nvm(pos)
return get_itemstring(nvm, tonumber(payload or 1) or 1) return get_itemstring(nvm, tonumber(payload or 0) or 0)
elseif topic == "state" then elseif topic == "state" then
local nvm = techage.get_nvm(pos) local nvm = techage.get_nvm(pos)
return inv_state(nvm) return inv_state(nvm)
@ -627,10 +627,10 @@ techage.register_node({"techage:ta4_chest"}, {
on_beduino_request_data = function(pos, src, topic, payload) on_beduino_request_data = function(pos, src, topic, payload)
if topic == 140 and payload[1] == 1 then -- Inventory Item Count if topic == 140 and payload[1] == 1 then -- Inventory Item Count
local nvm = techage.get_nvm(pos) local nvm = techage.get_nvm(pos)
return 0, {get_count(nvm, tonumber(payload[2] or 1) or 1)} return 0, {get_count(nvm, tonumber(payload[2] or 0) or 0)}
elseif topic == 140 and payload[1] == 2 then -- Inventory Item Name elseif topic == 140 and payload[1] == 2 then -- Inventory Item Name
local nvm = techage.get_nvm(pos) local nvm = techage.get_nvm(pos)
return 0, get_itemstring(nvm, tonumber(payload[2] or 1) or 1) return 0, get_itemstring(nvm, tonumber(payload[2] or 0) or 0)
elseif topic == 131 then -- Chest State elseif topic == 131 then -- Chest State
local nvm = techage.get_nvm(pos) local nvm = techage.get_nvm(pos)
return 0, {inv_state_num(nvm)} return 0, {inv_state_num(nvm)}

View File

@ -16,6 +16,7 @@
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
--local P = minetest.string_to_pos --local P = minetest.string_to_pos
--local M = minetest.get_meta --local M = minetest.get_meta
local has_mesecons = minetest.global_exists("mesecon")
local NodeInfoCache = {} local NodeInfoCache = {}
local NumbersToBeRecycled = {} local NumbersToBeRecycled = {}
@ -171,7 +172,7 @@ end)
techage.dug_node = {} techage.dug_node = {}
minetest.register_on_dignode(function(pos, oldnode, digger) minetest.register_on_dignode(function(pos, oldnode, digger)
if not digger then return end if not digger then return end
-- store pos for tools without own 'register_on_dignode' -- store the position of the dug block for tools like the TA1 hammer
techage.dug_node[digger:get_player_name()] = pos techage.dug_node[digger:get_player_name()] = pos
end) end)
@ -317,6 +318,13 @@ function techage.register_node(names, node_definition)
if node_definition.on_node_load then if node_definition.on_node_load then
register_lbm(names[1], names) register_lbm(names[1], names)
end end
-- register mvps stopper
if has_mesecons then
for _, name in ipairs(names) do
mesecon.register_mvps_stopper(name)
end
end
end end
------------------------------------------------------------------- -------------------------------------------------------------------
@ -331,6 +339,19 @@ function techage.not_protected(number, placer_name, clicker_name)
return false return false
end end
-- Check the given number value.
-- Returns true if the number is valid, point to real node and
-- and the node is not protected for the given player_name.
function techage.check_number(number, placer_name)
if number then
if not techage.not_protected(number, placer_name, nil) then
return false
end
return true
end
return false
end
-- Check the given list of numbers. -- Check the given list of numbers.
-- Returns true if number(s) is/are valid, point to real nodes and -- Returns true if number(s) is/are valid, point to real nodes and
-- and the nodes are not protected for the given player_name. -- and the nodes are not protected for the given player_name.

View File

@ -3,7 +3,7 @@
TechAge TechAge
======= =======
Copyright (C) 2020-2021 Joachim Stolberg Copyright (C) 2020-2022 Joachim Stolberg
AGPL v3 AGPL v3
See LICENSE.txt for more information See LICENSE.txt for more information
@ -57,6 +57,96 @@ local function rotate(v, yaw)
return {x = v.x * cosyaw - v.z * sinyaw, y = v.y, z = v.x * sinyaw + v.z * cosyaw} return {x = v.x * cosyaw - v.z * sinyaw, y = v.y, z = v.x * sinyaw + v.z * cosyaw}
end end
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]
local ndef2 = minetest.registered_nodes[node.name]
nvm.running = false
M(item.base_pos):set_string("status", S("Stopped"))
if ndef1 and ndef2 then
if ndef2.buildable_to then
local meta = M(dest_pos)
minetest.set_node(dest_pos, {name=name, param2=param2})
meta:from_table(item.metadata or {})
meta:set_string("ta_move_block", "")
meta:set_int("ta_door_locked", 1)
return
end
local meta = M(dest_pos)
if not meta:contains("ta_move_block") then
meta:set_string("ta_move_block", minetest.serialize({name=name, param2=param2}))
return
end
elseif ndef1 then
minetest.add_item(dest_pos, ItemStack(name))
end
end
-------------------------------------------------------------------------------
-- Entity monitoring
-------------------------------------------------------------------------------
local queue = {}
local first = 0
local last = -1
local function push(item)
last = last + 1
queue[last] = item
end
local function pop(nvm, time)
if first > last then return end
local item = queue[first]
queue[first] = nil -- to allow garbage collection
first = first + 1
return item
end
local function monitoring()
local num = last - first + 1
for _ = 1, num do
local item = pop()
if item.ttl >= techage.SystemTime then
-- still valud
push(item)
elseif item.ttl ~= 0 then
set_node(item)
end
end
minetest.after(1, monitoring)
end
minetest.after(1, monitoring)
minetest.register_on_shutdown(function()
local num = last - first + 1
for _ = 1, num do
local item = pop()
if item.ttl ~= 0 then
set_node(item)
end
end
end)
local function monitoring_add_entity(item)
item.ttl = techage.SystemTime + 1
push(item)
end
local function monitoring_del_entity(item)
-- Mark as timed out
item.ttl = 0
end
local function monitoring_trigger_entity(item)
item.ttl = techage.SystemTime + 1
end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- to_path function for the fly/move path -- to_path function for the fly/move path
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -310,41 +400,15 @@ end
local function entity_to_node(pos, obj) local function entity_to_node(pos, obj)
local self = obj:get_luaentity() local self = obj:get_luaentity()
if self then if self and self.item then
local name = self.item_name or "air"
local param2 = self.param2 or 0
local metadata = self.metadata or {}
detach_objects(pos, self) detach_objects(pos, self)
if self.base_pos then monitoring_del_entity(self.item)
local nvm = techage.get_nvm(self.base_pos)
nvm.running = nil
end
minetest.after(0.1, obj.remove, obj) minetest.after(0.1, obj.remove, obj)
local node = minetest.get_node(pos) set_node(self.item)
local ndef1 = minetest.registered_nodes[name]
local ndef2 = minetest.registered_nodes[node.name]
if ndef1 and ndef2 then
if ndef2.buildable_to then
local meta = M(pos)
minetest.set_node(pos, {name=name, param2=param2})
meta:from_table(metadata)
meta:set_string("ta_move_block", "")
meta:set_int("ta_door_locked", 1)
return
end
local meta = M(pos)
if not meta:contains("ta_move_block") then
meta:set_string("ta_move_block", minetest.serialize({name=name, param2=param2}))
return
end
--minetest.add_item(pos, ItemStack(name))
elseif ndef1 then
minetest.add_item(pos, ItemStack(name))
end
end end
end end
local function node_to_entity(start_pos) local function node_to_entity(base_pos, start_pos, dest_pos)
local meta = M(start_pos) local meta = M(start_pos)
local node, metadata local node, metadata
@ -356,7 +420,7 @@ local function node_to_entity(start_pos)
meta:set_string("ta_block_locked", "true") meta:set_string("ta_block_locked", "true")
elseif not meta:contains("ta_block_locked") then elseif not meta:contains("ta_block_locked") then
-- Block with other metadata -- Block with other metadata
node = minetest.get_node(start_pos) node = techage.get_node_lvm(start_pos)
metadata = meta:to_table() metadata = meta:to_table()
minetest.after(0.1, minetest.remove_node, start_pos) minetest.after(0.1, minetest.remove_node, start_pos)
else else
@ -371,9 +435,15 @@ local function node_to_entity(start_pos)
obj:set_armor_groups({immortal=1}) obj:set_armor_groups({immortal=1})
-- To be able to revert to node -- To be able to revert to node
self.item_name = node.name
self.param2 = node.param2 self.param2 = node.param2
self.metadata = metadata or {} self.item = {
name = node.name,
param2 = node.param2,
metadata = metadata or {},
dest_pos = dest_pos,
base_pos = base_pos,
}
monitoring_add_entity(self.item)
-- Prepare for attachments -- Prepare for attachments
self.players = {} self.players = {}
@ -436,7 +506,6 @@ local function handover_to(obj, self, pos1)
end end
local pos2 = next_path_pos(pos1, self.lpath, 1) local pos2 = next_path_pos(pos1, self.lpath, 1)
local dir = determine_dir(pos1, pos2) local dir = determine_dir(pos1, pos2)
--print("handover_to", P2S(pos1), P2S(pos2), P2S(dir), P2S(info.pos), meta:get_string("path"))
if not self.handover then if not self.handover then
local nvm = techage.get_nvm(info.pos) local nvm = techage.get_nvm(info.pos)
nvm.lpos1 = nvm.lpos1 or {} nvm.lpos1 = nvm.lpos1 or {}
@ -458,7 +527,7 @@ minetest.register_entity("techage:move_item", {
initial_properties = { initial_properties = {
pointable = true, pointable = true,
makes_footstep_sound = true, makes_footstep_sound = true,
static_save = true, static_save = false,
collide_with_objects = false, collide_with_objects = false,
physical = false, physical = false,
visual = "wielditem", visual = "wielditem",
@ -467,49 +536,6 @@ minetest.register_entity("techage:move_item", {
selectionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, selectionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
}, },
get_staticdata = function(self)
return minetest.serialize({
item_name = self.item_name,
param2 = self.param2,
metadata = self.metadata,
move2to1 = self.move2to1,
handover = self.handover,
path_idx = self.path_idx,
pos1_idx = self.pos1_idx,
lpath = self.lpath,
start_pos = self.start_pos,
base_pos = self.base_pos,
max_speed = self.max_speed,
dest_pos = self.dest_pos,
dir = self.dir,
respawn = true,
})
end,
on_activate = function(self, staticdata)
if staticdata then
local tbl = minetest.deserialize(staticdata) or {}
self.item_name = tbl.item_name or "air"
self.param2 = tbl.param2 or 0
self.metadata = tbl.metadata or {}
self.move2to1 = tbl.move2to1 or false
self.handover = tbl.handover
self.path_idx = tbl.path_idx or 1
self.pos1_idx = tbl.pos1_idx or 1
self.lpath = tbl.lpath or {}
self.max_speed = tbl.max_speed or MAX_SPEED
self.dest_pos = tbl.dest_pos or self.object:get_pos()
self.start_pos = tbl.start_pos or self.object:get_pos()
self.base_pos = tbl.base_pos
self.dir = tbl.dir or {x=0, y=0, z=0}
self.object:set_properties({wield_item = self.item})
--print("tbl.respawn", tbl.respawn)
if tbl.respawn then
entity_to_node(self.dest_pos, self.object)
end
end
end,
on_step = function(self, dtime, moveresult) on_step = function(self, dtime, moveresult)
local stop_obj = function(obj, self) local stop_obj = function(obj, self)
local dest_pos = self.dest_pos local dest_pos = self.dest_pos
@ -518,7 +544,6 @@ minetest.register_entity("techage:move_item", {
obj:set_velocity({x=0, y=0, z=0}) obj:set_velocity({x=0, y=0, z=0})
self.dest_pos = nil self.dest_pos = nil
self.old_dist = nil self.old_dist = nil
self.ttl = 2
return dest_pos return dest_pos
end end
@ -571,19 +596,13 @@ minetest.register_entity("techage:move_item", {
end end
end end
elseif self.ttl then monitoring_trigger_entity(self.item)
self.ttl = self.ttl - dtime
if self.ttl < 0 then
local obj = self.object
local pos = obj:get_pos()
entity_to_node(pos, obj)
end
end end
end, end,
}) })
local function is_valid_dest(pos) local function is_valid_dest(pos)
local node = minetest.get_node(pos) local node = techage.get_node_lvm(pos)
if techage.is_air_like(node.name) then if techage.is_air_like(node.name) then
return true return true
end end
@ -594,20 +613,19 @@ local function is_valid_dest(pos)
end end
local function is_simple_node(pos) local function is_simple_node(pos)
local node = minetest.get_node(pos) local node =techage.get_node_lvm(pos)
local ndef = minetest.registered_nodes[node.name] local ndef = minetest.registered_nodes[node.name]
return not techage.is_air_like(node.name) and techage.can_dig_node(node.name, ndef) return not techage.is_air_like(node.name) and techage.can_dig_node(node.name, ndef)
end end
local function move_node(pos, pos1_idx, start_pos, lpath, max_speed, height, move2to1, handover, cpos) 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) local pos2 = next_path_pos(start_pos, lpath, 1)
--print("move_node", P2S(pos), P2S(start_pos), lpath, max_speed, height, move2to1, P2S(pos2))
-- optional for non-player objects -- optional for non-player objects
local yoffs = M(pos):get_float("offset") local yoffs = M(pos):get_float("offset")
if pos2 then if pos2 then
local dir = determine_dir(start_pos, pos2) local dir = determine_dir(start_pos, pos2)
local obj = node_to_entity(start_pos) local obj = node_to_entity(pos, start_pos, pos2)
if obj then if obj then
local offs = {x=0, y=height or 1, z=0} local offs = {x=0, y=height or 1, z=0}
@ -622,40 +640,43 @@ local function move_node(pos, pos1_idx, start_pos, lpath, max_speed, height, mov
self.pos1_idx = pos1_idx self.pos1_idx = pos1_idx
self.lpath = lpath self.lpath = lpath
self.max_speed = max_speed self.max_speed = max_speed
self.start_pos = start_pos
self.base_pos = pos
self.move2to1 = move2to1 self.move2to1 = move2to1
self.handover = handover self.handover = handover
self.yoffs = yoffs self.yoffs = yoffs
--print("move_node", P2S(start_pos), P2S(pos2), P2S(dir), P2S(pos))
move_entity(obj, pos2, dir) move_entity(obj, pos2, dir)
return true
else
return false
end end
end end
end end
local function move_nodes(pos, meta, nvm, lpath, max_speed, height, move2to1, handover) local function move_nodes(pos, meta, nvm, lpath, max_speed, height, move2to1, handover)
--print("move_nodes", dump(nvm), dump(lpath), max_speed, height, move2to1, handover)
local owner = meta:get_string("owner") local owner = meta:get_string("owner")
techage.counting_add(owner, #nvm.lpos1 * #lpath) techage.counting_add(owner, #lpath, #nvm.lpos1 * #lpath)
for idx = 1, #nvm.lpos1 do for idx = 1, #nvm.lpos1 do
local pos1 = nvm.lpos1[idx] local pos1 = nvm.lpos1[idx]
local pos2 = nvm.lpos2[idx] local pos2 = nvm.lpos2[idx]
--print("move_nodes", idx, P2S(pos1), P2S(pos2))
if move2to1 then if move2to1 then
pos1, pos2 = pos2, pos1 pos1, pos2 = pos2, pos1
end end
--print("move_nodes", P2S(pos1), P2S(pos2))
if not minetest.is_protected(pos1, owner) and not minetest.is_protected(pos2, owner) then 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 is_simple_node(pos1) and is_valid_dest(pos2) then
move_node(pos, idx, pos1, lpath, max_speed, height, move2to1, handover) if move_node(pos, idx, pos1, lpath, max_speed, height, move2to1, handover) == false then
meta:set_string("status", S("No valid node at the start position"))
return false
end
else else
if not is_simple_node(pos1) then if not is_simple_node(pos1) then
meta:set_string("status", S("No valid node at the start position")) meta:set_string("status", S("No valid node at the start position"))
else else
meta:set_string("status", S("No valid destination position")) meta:set_string("status", S("No valid destination position"))
end end
return false
end end
else else
if minetest.is_protected(pos1, owner) then if minetest.is_protected(pos1, owner) then
@ -666,12 +687,12 @@ local function move_nodes(pos, meta, nvm, lpath, max_speed, height, move2to1, ha
return false return false
end end
end end
meta:set_string("status", S("Running"))
return true return true
end end
-- Move nodes from lpos1 by the given x/y/z 'line' -- Move nodes from lpos1 by the given x/y/z 'line'
local function move_nodes2(pos, meta, lpos1, line, max_speed, height) local function move_nodes2(pos, meta, lpos1, line, max_speed, height)
--print("move_nodes2", dump(lpos1), dump(line), max_speed, height)
local owner = meta:get_string("owner") local owner = meta:get_string("owner")
techage.counting_add(owner, #lpos1) techage.counting_add(owner, #lpos1)
@ -691,6 +712,7 @@ local function move_nodes2(pos, meta, lpos1, line, max_speed, height)
else else
meta:set_string("status", S("No valid destination position")) meta:set_string("status", S("No valid destination position"))
end end
return false, lpos1
end end
else else
if minetest.is_protected(pos1, owner) then if minetest.is_protected(pos1, owner) then
@ -702,7 +724,7 @@ local function move_nodes2(pos, meta, lpos1, line, max_speed, height)
end end
end end
meta:set_string("status", "") meta:set_string("status", S("Running"))
return true, lpos2 return true, lpos2
end end
@ -714,7 +736,7 @@ function flylib.move_to_other_pos(pos, move2to1)
local height = meta:contains("height") and meta:get_float("height") or 1 local height = meta:contains("height") and meta:get_float("height") or 1
local handover local handover
if err then return false end if err or nvm.running then return false end
height = techage.in_range(height, 0, 1) height = techage.in_range(height, 0, 1)
max_speed = techage.in_range(max_speed, MIN_SPEED, MAX_SPEED) max_speed = techage.in_range(max_speed, MIN_SPEED, MAX_SPEED)
@ -732,7 +754,9 @@ function flylib.move_to_other_pos(pos, move2to1)
else else
handover = meta:contains("handoverB") and meta:get_string("handoverB") or nil handover = meta:contains("handoverB") and meta:get_string("handoverB") or nil
end end
return move_nodes(pos, meta, nvm, lpath, max_speed, height, move2to1, handover) nvm.running = move_nodes(pos, meta, nvm, lpath, max_speed, height, move2to1, handover)
nvm.moveBA = nvm.running and not move2to1
return nvm.running
end end
function flylib.move_to(pos, line) function flylib.move_to(pos, line)
@ -742,8 +766,10 @@ function flylib.move_to(pos, line)
local max_speed = meta:contains("max_speed") and meta:get_int("max_speed") or MAX_SPEED local max_speed = meta:contains("max_speed") and meta:get_int("max_speed") or MAX_SPEED
local resp local resp
resp, nvm.lastpos = move_nodes2(pos, meta, nvm.lastpos or nvm.lpos1, line, max_speed, height) if nvm.running then return false end
return resp
nvm.running, nvm.lastpos = move_nodes2(pos, meta, nvm.lastpos or nvm.lpos1, line, max_speed, height)
return nvm.running
end end
function flylib.reset_move(pos) function flylib.reset_move(pos)
@ -751,11 +777,17 @@ function flylib.reset_move(pos)
local nvm = techage.get_nvm(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 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 max_speed = meta:contains("max_speed") and meta:get_int("max_speed") or MAX_SPEED
if nvm.running then return false end
if nvm.lpos1 and nvm.lpos1[1] then
local move = vector.subtract(nvm.lpos1[1], (nvm.lastpos or nvm.lpos1)[1]) local move = vector.subtract(nvm.lpos1[1], (nvm.lastpos or nvm.lpos1)[1])
local resp local resp
resp, nvm.lastpos = move_nodes2(pos, meta, nvm.lastpos or nvm.lpos1, move, max_speed, height) nvm.running, nvm.lastpos = move_nodes2(pos, meta, nvm.lastpos or nvm.lpos1, move, max_speed, height)
return resp return nvm.running
end
return false
end end
-- rot is one of "l", "r", "2l", "2r" -- rot is one of "l", "r", "2l", "2r"
@ -842,4 +874,4 @@ minetest.register_on_dieplayer(function(player)
end end
end) end)
return flylib techage.flylib = flylib

View File

@ -72,8 +72,11 @@ end
local api = {} local api = {}
function api.store_mapblock_data(key, mapblock_data) function api.store_mapblock_data(key, mapblock_data)
if use_marshal then if use_marshal and mapblock_data then
set_block(key, marshal.encode(mapblock_data)) local data = marshal.encode(mapblock_data)
if data then
set_block(key, data)
end
else else
set_block(key, minetest.serialize(mapblock_data)) set_block(key, minetest.serialize(mapblock_data))
end end

View File

@ -1,208 +0,0 @@
--[[
Techage
=======
Copyright (C) 2020-2021 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA4 Terminal
]]--
local M = minetest.get_meta
local S = techage.S
local STR_LEN = 80
local HELP = [[#### TA4 Terminal ####
Send commands to the connected machine
and output text messages from the
machine.
Commands can have up to 80 characters.
Local commands:
- clear = clear screen
- help = this message
- pub = switch to public use
- priv = switch to private use
- connect <num> = connect the machine
All other commands are machine dependent.
]]
local function get_string(meta, num, default)
local s = meta:get_string("bttn_text"..num)
if not s or s == "" then
return default
end
return s
end
local function formspec2(mem)
mem.command = mem.command or ""
mem.output = mem.output or ""
local output = minetest.formspec_escape(mem.output)
output = output:gsub("\n", ",")
local command = minetest.formspec_escape(mem.command)
local bttn_text1 = get_string(meta, 1, "User1")
local bttn_text2 = get_string(meta, 2, "User2")
local bttn_text3 = get_string(meta, 3, "User3")
local bttn_text4 = get_string(meta, 4, "User4")
local bttn_text5 = get_string(meta, 5, "User5")
local bttn_text6 = get_string(meta, 6, "User6")
local bttn_text7 = get_string(meta, 7, "User7")
local bttn_text8 = get_string(meta, 8, "User8")
local bttn_text9 = get_string(meta, 9, "User9")
return "size[10,8]"..
"style_type[table,field;font=mono]"..
"button[0,0;3.3,1;bttn1;"..bttn_text1.."]button[3.3,0;3.3,1;bttn2;"..bttn_text2.."]button[6.6,0;3.3,1;bttn3;"..bttn_text3.."]"..
"button[0,0.8;3.3,1;bttn4;"..bttn_text4.."]button[3.3,0.8;3.3,1;bttn5;"..bttn_text5.."]button[6.6,0.8;3.3,1;bttn6;"..bttn_text6.."]"..
"button[0,1.6;3.3,1;bttn7;"..bttn_text7.."]button[3.3,1.6;3.3,1;bttn8;"..bttn_text8.."]button[6.6,1.6;3.3,1;bttn9;"..bttn_text9.."]"..
"table[0,2.5;9.8,4.7;output;"..output..";200]"..
"field[0.4,7.7;7.6,1;cmnd;;"..mem.command.."]" ..
"field_close_on_enter[cmnd;false]"..
"button[7.9,7.4;2,1;enter;"..S("Enter").."]"
end
local function output(pos, text)
local mem = techage.get_mem(pos)
mem.output = mem.output .. "\n" .. (text or "")
mem.output = mem.output:sub(-500,-1)
M(pos):set_string("formspec", formspec2(mem))
end
local function command(pos, mem, player)
local meta = minetest.get_meta(pos)
local owner = meta:get_string("owner")
if mem.command == "clear" then
mem.output = ""
mem.command = ""
meta:set_string("formspec", formspec2(mem))
elseif mem.command == "" then
output(pos, ">")
mem.command = ""
meta:set_string("formspec", formspec2(mem))
elseif mem.command == "help" then
local meta = minetest.get_meta(pos)
mem.output = HELP
mem.command = ""
meta:set_string("formspec", formspec2(mem))
elseif mem.command == "pub" and owner == player then
meta:set_int("public", 1)
output(pos, "> "..mem.command)
mem.command = ""
output(pos, "Switched to public use!")
elseif mem.command == "priv" and owner == player then
meta:set_int("public", 0)
output(pos, "> "..mem.command)
mem.command = ""
output(pos, "Switched to private use!")
elseif meta:get_int("public") == 1 or owner == player then
if mem.command == "clear" then
mem.output =
mem.command = ""
meta:set_string("formspec", formspec2(mem))
end
end
end
minetest.register_node("techage:ta4_terminal", {
description = "TA4 Collider Terminal",
tiles = {
-- up, down, right, left, back, front
'techage_terminal1_top.png',
'techage_terminal1_bottom.png',
'techage_terminal1_side.png',
'techage_terminal1_side.png',
'techage_terminal1_bottom.png',
"techage_terminal1_front.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{-12/32, -16/32, -8/32, 12/32, -14/32, 12/32},
{-12/32, -14/32, 12/32, 12/32, 6/32, 14/32},
},
},
selection_box = {
type = "fixed",
fixed = {
{-12/32, -16/32, -8/32, 12/32, -14/32, 12/32},
{-12/32, -14/32, 12/32, 12/32, 6/32, 14/32},
},
},
after_place_node = function(pos, placer)
local number = techage.add_node(pos, minetest.get_node(pos).name)
local meta = minetest.get_meta(pos)
meta:set_string("formspec", formspec1())
meta:set_string("owner", placer:get_player_name())
meta:set_string("infotext", S("TA4 Collider Terminal") .. ": " .. S("not connected")
end,
on_receive_fields = function(pos, formname, fields, player)
local meta = minetest.get_meta(pos)
local mem = techage.get_mem(pos)
if fields.number and fields.number ~= "" then
local owner = meta:get_string("owner")
if techage.check_numbers(fields.number, owner) then
local own_number = meta:get_string("own_number")
if techage.send_single(own_number, fields.number, "connect") == true then
meta:set_string("number", fields.number)
meta:set_string("infotext", S("TA4 Collider Terminal") .. ": " .. S("connected with") .. " " .. fields.number)
meta:set_string("formspec", formspec2(mem))
end
end
elseif (fields.enter or fields.key_enter_field) and fields.cmnd then
mem.command = string.sub(fields.cmnd, 1, STR_LEN)
command(pos, mem, player:get_player_name())
elseif fields.key_up then
mem.command = pdp13.historybuffer_priv(pos)
meta:set_string("formspec", formspec2(mem))
elseif fields.key_down then
mem.command = pdp13.historybuffer_next(pos)
meta:set_string("formspec", formspec2(mem))
end
end,
after_dig_node = function(pos, oldnode, oldmetadata)
techage.remove_node(pos, oldnode, oldmetadata)
end,
paramtype = "light",
use_texture_alpha = techage.CLIP,
sunlight_propagates = true,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
})
minetest.register_craft({
output = "techage:ta4_terminal",
recipe = {
{"", "techage:ta4_display", ""},
{"dye:black", "techage:ta4_wlanchip", "default:copper_ingot"},
{"", "techage:aluminum", ""},
},
})
techage.register_node({"techage:ta4_terminal"}, {
on_recv_message = function(pos, src, topic, payload)
if topic == "term" then
output(pos, payload)
return true
elseif topic == "clear" then
local mem = techage.get_mem(pos)
mem.output = ""
mem.command = ""
M(pos):set_string("formspec", formspec2(mem))
return true
end
end,
})

View File

@ -208,6 +208,7 @@ techage.Items = {
techage_ta5 = "techage:ta5_fr_nucleus", techage_ta5 = "techage:ta5_fr_nucleus",
ta5_flycontroller = "techage:ta5_flycontroller", ta5_flycontroller = "techage:ta5_flycontroller",
ta5_aichip = "techage:ta5_aichip", ta5_aichip = "techage:ta5_aichip",
ta5_aichip2 = "techage:ta5_aichip2",
ta5_tele_pipe = "techage:ta5_tele_pipe", ta5_tele_pipe = "techage:ta5_tele_pipe",
ta5_tele_tube = "techage:ta5_tele_tube", ta5_tele_tube = "techage:ta5_tele_tube",
ta5_chest = "techage:ta5_hl_chest", ta5_chest = "techage:ta5_hl_chest",

View File

@ -254,6 +254,7 @@ techage.manual_DE.aTitel = {
"2,Weitere TA5 Blöcke/Items", "2,Weitere TA5 Blöcke/Items",
"3,TA5 Container (geplant)", "3,TA5 Container (geplant)",
"3,TA5 KI Chip / TA5 AI Chip", "3,TA5 KI Chip / TA5 AI Chip",
"3,TA5 KI Chip II / TA5 AI Chip II",
} }
techage.manual_DE.aText = { techage.manual_DE.aText = {
@ -360,13 +361,20 @@ techage.manual_DE.aText = {
"\n", "\n",
"Den Köhler brauchst du\\, um Holzkohle herzustellen. Holzkohle wird für den Brenner\\, aber auch bspw. in TA2 für die Dampfmaschine benötigt.\n".. "Den Köhler brauchst du\\, um Holzkohle herzustellen. Holzkohle wird für den Brenner\\, aber auch bspw. in TA2 für die Dampfmaschine benötigt.\n"..
"\n".. "\n"..
"Für den Köhler brauchst du:\n"..
"\n"..
" - einen Anzünderblock ('techage:lighter')\n"..
" - 26 Hölzblöcke (wood)\\, die zu einem Würfen aufgeschichtet werden. Die Holzsorte spielt keine Rolle\n"..
" - Erde (dirt) um den Holzhaufen abzudecken\n"..
" - Flint and Iron (technischer Name: 'fire:flint_and_steel') um den Anzünderblock anzuzünden\n"..
"\n"..
"Bauanleitung (siehe auch Plan):\n".. "Bauanleitung (siehe auch Plan):\n"..
"\n".. "\n"..
" - Baue eine 5x5 große Fläche aus Erde (dirt)\n".. " - Baue eine 5x5 große Fläche aus Erde (dirt)\n"..
" - Platziere in die Mitte einen Anzünder (lighter)\n".. " - Platziere in die Mitte einen Anzünder (lighter)\n"..
" - Baue aus Holz (wood) einen 3x3x3 großen Würfel darüber\n".. " - Platziere rund um den Anzünder 7 Holz (wood)\\, aber lasse ein Loch zum Anzünder frei\n"..
" - Überdecke alles mit einer Schicht Erde zu einem 5x5x5 großen Würfel\n".. " - Baue weitere 2 Schichten Holz darüber\\, so dass ein 3x3x3 großen Holzwürfel entsteht\n"..
" - Lasse ein Loch zum Anzünder\n".. " - Überdecke alles mit einer Schicht Erde zu einem 5x5x5 großen Würfel\\, aber lasse das Loch zum Anzünder frei\n"..
" - Zünde den Anzünder an und verschließe das Loch sofort mit jeweils einem Block Holz und Erde\n".. " - Zünde den Anzünder an und verschließe das Loch sofort mit jeweils einem Block Holz und Erde\n"..
" - Wenn du alles richtig gemacht hast\\, beginnt der Köhler nach wenigen Sekunden an zu rauchen\n".. " - Wenn du alles richtig gemacht hast\\, beginnt der Köhler nach wenigen Sekunden an zu rauchen\n"..
" - Öffne den Köhler erst\\, wenn der Rauch verschwunden ist (ca. 20 min)\n".. " - Öffne den Köhler erst\\, wenn der Rauch verschwunden ist (ca. 20 min)\n"..
@ -1479,6 +1487,8 @@ techage.manual_DE.aText = {
"\n".. "\n"..
"Der Elektrolyseur besitzt ein Schraubenschlüssel-Menü zur Einstellung der Stromaufnahme und des Abschaltpunkts.\n".. "Der Elektrolyseur besitzt ein Schraubenschlüssel-Menü zur Einstellung der Stromaufnahme und des Abschaltpunkts.\n"..
"\n".. "\n"..
"Unterschreitet die im Stromnetz gespeicherte Leistung den angegebenen Wert des Abschaltpunkts\\, so schaltet sich der Elektrolyseur automatisch ab. Damit kann ein Leerlaufen der Speichersysteme verhindert werden.\n"..
"\n"..
"\n".. "\n"..
"\n", "\n",
"Die Brennstoffzelle wandelt Wasserstoff in Strom um.\n".. "Die Brennstoffzelle wandelt Wasserstoff in Strom um.\n"..
@ -1494,16 +1504,18 @@ techage.manual_DE.aText = {
"\n", "\n",
"Der Reaktor dient dazu\\, die über den Destillationsturm oder aus anderen Rezepten gewonnenen Zutaten zu neuen Produkten weiter zu verarbeiten. Der Plan links zeigt nur eine mögliche Variante\\, da die Anordnung der Silos und Tanks rezeptabhängig ist.\n".. "Der Reaktor dient dazu\\, die über den Destillationsturm oder aus anderen Rezepten gewonnenen Zutaten zu neuen Produkten weiter zu verarbeiten. Der Plan links zeigt nur eine mögliche Variante\\, da die Anordnung der Silos und Tanks rezeptabhängig ist.\n"..
"\n".. "\n"..
"Das primäre Ausgabeprodukt wird immer an der Seite des Reaktorständers ausgegeben\\, unabhängig davon\\, ob es sich um ein Pulver oder eine Flüssigkeit handelt. Das (sekundäre) Abfallprodukt wird immer unten am Reaktorständers ausgegeben.\n"..
"\n"..
"Ein Reaktor besteht aus:\n".. "Ein Reaktor besteht aus:\n"..
"\n".. "\n"..
" - div. Tanks und Silos mit den Zutaten\\, die über Leitungen mit dem Dosierer verbunden sind\n".. " - div. Tanks und Silos mit den Zutaten\\, die über Leitungen mit dem Dosierer verbunden sind\n"..
" - optional einem Reaktorsockel\\, welcher die Abfälle aus dem Reaktor ableitet (nur bei Rezepten mit zwei Ausgangsstoffen notwendig)\n".. " - optional einem Reaktorsockel\\, welcher die Abfälle aus dem Reaktor ableitet (nur bei Rezepten mit zwei Ausgabestoffen notwendig)\n"..
" - dem Reaktorständer\\, der auf den Sockel gesetzt werden muss (sofern vorhanden). Der Ständer hat einen Stromanschluss und zieht bei Betrieb 8 ku.\n".. " - dem Reaktorständer\\, der auf den Sockel gesetzt werden muss (sofern vorhanden). Der Ständer hat einen Stromanschluss und zieht bei Betrieb 8 ku.\n"..
" - dem eigentlichen Reaktorbehälter\\, der auf den Reaktorständer gesetzt werden muss\n".. " - dem eigentlichen Reaktorbehälter\\, der auf den Reaktorständer gesetzt werden muss\n"..
" - dem Einfüllstutzen der auf den Reaktorbehälter gesetzt werden muss\n".. " - dem Einfüllstutzen der auf den Reaktorbehälter gesetzt werden muss\n"..
" - dem Dosierer\\, welcher über Leitungen mit den Tanks oder Silos sowie dem Einfüllstutzen verbunden werden muss\n".. " - dem Dosierer\\, welcher über Leitungen mit den Tanks oder Silos sowie dem Einfüllstutzen verbunden werden muss\n"..
"\n".. "\n"..
"Hinweis 1: Flüssigkeiten werden nur in Tanks gelagert\\, feste Stoffe und Stoffe in Pulverform nur in Silos. Dies gilt für Zutaten und Ausgangsstoffe.\n".. "Hinweis 1: Flüssigkeiten werden nur in Tanks gelagert\\, feste Stoffe und Stoffe in Pulverform nur in Silos. Dies gilt für Zutaten und Ausgabestoffe.\n"..
"\n".. "\n"..
"Hinweis 2: Tanks oder Silos mit verschiedenen Inhalten dürfen nicht zu einem Leitungssystem verbunden werden. Mehrere Tanks oder Silos mit gleichem Inhalt dürfen dagegen parallel an einer Leitung hängen.\n".. "Hinweis 2: Tanks oder Silos mit verschiedenen Inhalten dürfen nicht zu einem Leitungssystem verbunden werden. Mehrere Tanks oder Silos mit gleichem Inhalt dürfen dagegen parallel an einer Leitung hängen.\n"..
"\n".. "\n"..
@ -1524,7 +1536,7 @@ techage.manual_DE.aText = {
"Wie auch bei anderen Maschinen:\n".. "Wie auch bei anderen Maschinen:\n"..
"\n".. "\n"..
" - geht der Dosierer in den standby Zustand\\, so fehlen ein oder mehrere Zutaten\n".. " - geht der Dosierer in den standby Zustand\\, so fehlen ein oder mehrere Zutaten\n"..
" - geht der Dosierer in den blocked Zustand\\, so ist Ausgangstank oder Silo voll\\, defekt oder falsch angeschlossen\n".. " - geht der Dosierer in den blocked Zustand\\, so ist Ausgabetank oder Silo voll\\, defekt oder falsch angeschlossen\n"..
"\n".. "\n"..
"Der Dosierer benötigt keinen Strom. Alle 10 s wird ein Rezept abgearbeitet.\n".. "Der Dosierer benötigt keinen Strom. Alle 10 s wird ein Rezept abgearbeitet.\n"..
"\n".. "\n"..
@ -1541,7 +1553,7 @@ techage.manual_DE.aText = {
"\n", "\n",
"Teil des Chemischen Reaktors. Hier ist auch der Stromanschluss für den Reaktor. Der Reaktor benötigt 8 ku Strom.\n".. "Teil des Chemischen Reaktors. Hier ist auch der Stromanschluss für den Reaktor. Der Reaktor benötigt 8 ku Strom.\n"..
"\n".. "\n"..
"Der Ständer hat zwei Leitungsanschlüsse\\, nach rechst für das Ausgangsprodukt und nach unten für den Abfall\\, wie bspw. Rotschlamm bei der Aluminiumherstellung.\n".. "Der Ständer hat zwei Leitungsanschlüsse\\, nach rechst für das primäre Ausgabeprodukt und nach unten für den Abfall\\, wie bspw. Rotschlamm bei der Aluminiumherstellung.\n"..
"\n".. "\n"..
"\n".. "\n"..
"\n", "\n",
@ -1722,6 +1734,11 @@ techage.manual_DE.aText = {
" - 'b2a' Bewege Block von B nach A\n".. " - 'b2a' Bewege Block von B nach A\n"..
" - 'move' Bewege Block auf die andere Seite\n".. " - 'move' Bewege Block auf die andere Seite\n"..
"\n".. "\n"..
"Über das Schraubenschlüssel-Menü kann auf die Betriebsart 'move xyz' umgeschaltet werden. Nach der Umschaltung werden folgende techage Kommandos unterstützt:\n"..
"\n"..
" - 'move2' Beim Kommando muss zusätzlich die Flugstrecke als x\\,y\\,z Vektor angegeben werden.\nBeispiel Lua Controller: '$send_cmnd(MOVE_CTLR\\, \"move2\"\\, \"0\\,12\\,0\")'\n"..
" - 'reset' Block/Blöcke zurück in Startposition bewegen\n"..
"\n"..
"*Wichtige Hinweise:*\n".. "*Wichtige Hinweise:*\n"..
"\n".. "\n"..
" - Sofern mehrere Blöcke bewegt werden sollen\\, muss der Block\\, der die Spieler/Mobs mitnehmen soll\\, beim Antrainieren als erstes angeklickt werden.\n".. " - Sofern mehrere Blöcke bewegt werden sollen\\, muss der Block\\, der die Spieler/Mobs mitnehmen soll\\, beim Antrainieren als erstes angeklickt werden.\n"..
@ -2146,6 +2163,10 @@ techage.manual_DE.aText = {
"\n".. "\n"..
"\n".. "\n"..
"\n", "\n",
"Der TA5 KI Chip II wird zur Herstellung des TA5 Fusionsreaktors benötigt. Der TA5 KI Chip II kann nur auf der TA4 Elektronik Fab hergestellt werden. Dazu werden 50 Erfahrungspunkte benötigt.\n"..
"\n"..
"\n"..
"\n",
} }
techage.manual_DE.aItemName = { techage.manual_DE.aItemName = {
@ -2402,6 +2423,7 @@ techage.manual_DE.aItemName = {
"", "",
"", "",
"ta5_aichip", "ta5_aichip",
"ta5_aichip2",
} }
techage.manual_DE.aPlanTable = { techage.manual_DE.aPlanTable = {
@ -2658,5 +2680,6 @@ techage.manual_DE.aPlanTable = {
"", "",
"", "",
"", "",
"",
} }

View File

@ -254,6 +254,7 @@ techage.manual_EN.aTitel = {
"2,More TA5 Blocks/Items", "2,More TA5 Blocks/Items",
"3,TA5 Container (planned)", "3,TA5 Container (planned)",
"3,TA5 AI Chip", "3,TA5 AI Chip",
"3,TA5 AI Chip II",
} }
techage.manual_EN.aText = { techage.manual_EN.aText = {
@ -368,13 +369,19 @@ techage.manual_EN.aText = {
"\n", "\n",
"You need the Charcoal Pile to make charcoal. Charcoal is required for the melting furnace\\, but also\\, for example\\, in TA2 for the steam engine.\n".. "You need the Charcoal Pile to make charcoal. Charcoal is required for the melting furnace\\, but also\\, for example\\, in TA2 for the steam engine.\n"..
"\n".. "\n"..
"For the charcoal burner you need:\n"..
"\n"..
" - a lighter block ('techage:lighter')\n"..
" - 26 wooden blocks that are stacked into a pile of wood. The type of wood is irrelevant\n"..
" - Dirt to cover the pile of wood\n"..
" - Flint and Iron (technical name: 'fire:flint_and_steel') to light the lighter block\n"..
"\n"..
"Building instructions (see also plan):\n".. "Building instructions (see also plan):\n"..
"\n".. "\n"..
" - Build a 5x5 area of dirt\n".. " - Build a 5x5 area of dirt\n"..
" - Place a lighter in the middle\n".. " - Place 7 wood around the lighter but leave a hole to the lighter\n"..
" - Build a 3x3x3 cube above it out of wood\n".. " - Build another 2 layers of wood on top\\, making a 3x3x3 wooden cube\n"..
" - Cover everything with a layer of dirt to form a 5x5x5 cube\n".. " - Cover everything with a layer of dirt into a 5x5x5 cube\\, but keep the hole to the lighter open\n"..
" - Leave a hole to the lighter\n"..
" - Light the lighter and immediately close the hole with a block of wood and dirt\n".. " - Light the lighter and immediately close the hole with a block of wood and dirt\n"..
" - If you have done everything correctly\\, the coal burner will start smoking after a few seconds\n".. " - If you have done everything correctly\\, the coal burner will start smoking after a few seconds\n"..
" - Only open the charcoal burner when the smoke has disappeared (approx. 20 min)\n".. " - Only open the charcoal burner when the smoke has disappeared (approx. 20 min)\n"..
@ -1486,6 +1493,8 @@ techage.manual_EN.aText = {
"\n".. "\n"..
"The electrolyzer has a wrench menu for setting the current consumption and the switch-off point.\n".. "The electrolyzer has a wrench menu for setting the current consumption and the switch-off point.\n"..
"\n".. "\n"..
"If the power stored in the power grid falls below the specified value of the switch-off point\\, the electrolyzer switches off automatically. This prevents the storage systems from running empty.\n"..
"\n"..
"\n".. "\n"..
"\n", "\n",
"The fuel cell converts hydrogen into electricity.\n".. "The fuel cell converts hydrogen into electricity.\n"..
@ -1501,16 +1510,18 @@ techage.manual_EN.aText = {
"The reactor is used to process the ingredients obtained from the distillation tower or from other recipes into new products.\n".. "The reactor is used to process the ingredients obtained from the distillation tower or from other recipes into new products.\n"..
"The plan on the left shows only one possible variant\\, since the arrangement of the silos and tanks depends on the recipe.\n".. "The plan on the left shows only one possible variant\\, since the arrangement of the silos and tanks depends on the recipe.\n"..
"\n".. "\n"..
"The primary output product is always output to the side of the reactor stand\\, regardless of whether it is a powder or a liquid. The (secondary) waste product is always discharged at the bottom of the reactor stand.\n"..
"\n"..
"A reactor consists of:\n".. "A reactor consists of:\n"..
"\n".. "\n"..
" - Various tanks and silos with the ingredients that are connected to the doser via pipes\n".. " - Various tanks and silos with the ingredients that are connected to the doser via pipes\n"..
" - optionally a reactor base\\, which discharges the waste from the reactor (only necessary for recipes with two starting materials)\n".. " - optionally a reactor base\\, which discharges the waste from the reactor (only necessary for recipes with two output products)\n"..
" - the reactor stand\\, which must be placed on the base (if available). The stand has a power connection and draws 8 ku during operation.\n".. " - the reactor stand\\, which must be placed on the base (if available). The stand has a power connection and draws 8 ku during operation.\n"..
" - The reactor vessel that has to be placed on the reactor stand\n".. " - The reactor vessel that has to be placed on the reactor stand\n"..
" - The filler pipe that must be placed on the reactor vessel\n".. " - The filler pipe that must be placed on the reactor vessel\n"..
" - The dosing device\\, which has to be connected to the tanks or silos and the filler pipe via pipes\n".. " - The dosing device\\, which has to be connected to the tanks or silos and the filler pipe via pipes\n"..
"\n".. "\n"..
"Note 1: Liquids are only stored in tanks\\, solids and substances in powder form only in silos. This applies to ingredients and raw materials.\n".. "Note 1: Liquids are only stored in tanks\\, solids and substances in powder form only in silos. This applies to ingredients and output products.\n"..
"\n".. "\n"..
"Note 2: Tanks or silos with different contents must not be connected to a pipe system. In contrast\\, several tanks or silos with the same content may hang in parallel on one line.\n".. "Note 2: Tanks or silos with different contents must not be connected to a pipe system. In contrast\\, several tanks or silos with the same content may hang in parallel on one line.\n"..
"\n".. "\n"..
@ -1531,7 +1542,7 @@ techage.manual_EN.aText = {
"As with other machines:\n".. "As with other machines:\n"..
"\n".. "\n"..
" - if the doser is in standby mode\\, one or more ingredients are missing\n".. " - if the doser is in standby mode\\, one or more ingredients are missing\n"..
" - if the doser is in the blocked state\\, the outlet tank or silo is full\\, defective or incorrectly connected\n".. " - if the doser is in the blocked state\\, the output tank or silo is full\\, defective or incorrectly connected\n"..
"\n".. "\n"..
"The doser does not need any electricity. A recipe is processed every 10 s.\n".. "The doser does not need any electricity. A recipe is processed every 10 s.\n"..
"\n".. "\n"..
@ -1728,6 +1739,11 @@ techage.manual_EN.aText = {
" - 'b2a' Move block from B to A.\n".. " - 'b2a' Move block from B to A.\n"..
" - 'move' Move block to the other side\n".. " - 'move' Move block to the other side\n"..
"\n".. "\n"..
"You can switch to the 'move xyz' operating mode via the wrench menu. After switching\\, the following techage commands are supported: \n"..
"\n"..
" - 'move2' With the command\\, the flight route must also be specified as an x\\,y\\,z vector.\nExample Lua Controller: '$send_cmnd(MOVE_CTLR\\, \"move2\"\\, \"0\\,12\\,0\")'\n"..
" - 'reset' move block(s) back to start position\n"..
"\n"..
"*Important instructions:*\n".. "*Important instructions:*\n"..
"\n".. "\n"..
" - If several blocks are to be moved\\, the block that is to take the players/mobs must be clicked first when training.\n".. " - If several blocks are to be moved\\, the block that is to take the players/mobs must be clicked first when training.\n"..
@ -2151,6 +2167,10 @@ techage.manual_EN.aText = {
"\n".. "\n"..
"\n".. "\n"..
"\n", "\n",
"The TA5 AI Chip II is required to build the TA5 Fusion Reactor. The TA5 AI Chip II can only be manufactured at the TA4 Electronics Fab. This requires 50 experience points.\n"..
"\n"..
"\n"..
"\n",
} }
techage.manual_EN.aItemName = { techage.manual_EN.aItemName = {
@ -2407,6 +2427,7 @@ techage.manual_EN.aItemName = {
"", "",
"", "",
"ta5_aichip", "ta5_aichip",
"ta5_aichip2",
} }
techage.manual_EN.aPlanTable = { techage.manual_EN.aPlanTable = {
@ -2663,5 +2684,6 @@ techage.manual_EN.aPlanTable = {
"", "",
"", "",
"", "",
"",
} }

View File

@ -346,7 +346,8 @@ local STAND = {"techage_reactor_stand_side.png", "techage:ta4_reactor_stand"}
local REACT = {"techage_reactor_plan.png", "techage:ta4_reactor"} local REACT = {"techage_reactor_plan.png", "techage:ta4_reactor"}
local FILLR = {"techage_reactor_filler_plan.png", "techage:ta4_reactor_fillerpipe"} local FILLR = {"techage_reactor_filler_plan.png", "techage:ta4_reactor_fillerpipe"}
local DOSER = {"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_pump_up.png", "techage:ta4_doser"} local DOSER = {"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_pump_up.png", "techage:ta4_doser"}
local SILO = {"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_silo.png", "techage:ta3_silo"} local SILO4 = {"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_silo.png", "techage:ta4_silo"}
local TANK4 = {"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_tank.png", "techage:ta4_tank"}
techage.ConstructionPlans["ta4_reactor"] = { techage.ConstructionPlans["ta4_reactor"] = {
{false, false, false, false, false, false, SIDEV, false, false, false, false}, {false, false, false, false, false, false, SIDEV, false, false, false, false},
@ -354,9 +355,9 @@ techage.ConstructionPlans["ta4_reactor"] = {
{false, false, false, false, PN000, PIPEH, PIPEH, PN270, false, false, false}, {false, false, false, false, PN000, PIPEH, PIPEH, PN270, false, false, false},
{false, false, false, false, PIPEV, false, false, FILLR, false, false, false}, {false, false, false, false, PIPEV, false, false, FILLR, false, false, false},
{false, false, false, false, PIPEV, false, false, REACT, false, false, false}, {false, false, false, false, PIPEV, false, false, REACT, false, false, false},
{false, false, false, false, PIPEV, false, false, STAND, PIPEH, PIPEH, SILO}, {false, false, false, false, PIPEV, false, false, STAND, PIPEH, PIPEH, SILO4},
{false, TANK3, PIPEH, PIPEH, DOSER, PN270, false, RBASE, PIPEH, PIPEH, TANK3}, {false, TANK4, PIPEH, PIPEH, DOSER, PN270, false, RBASE, PIPEH, PIPEH, TANK4},
{false, SILO, PIPEH, PIPEH, PIPEH, PN180, false, false, false, false, false}, {false, SILO4, PIPEH, PIPEH, PIPEH, PN180, false, false, false, false, false},
} }
-- --

View File

@ -30,7 +30,7 @@ minetest.register_node("techage:glow_gravel", {
}}, }},
paramtype = "light", paramtype = "light",
light_source = 8, light_source = 8,
groups = {crumbly = 2, falling_node = 1}, groups = {crumbly = 2, falling_node = 1, not_in_creative_inventory = 1},
sounds = default.node_sound_gravel_defaults(), sounds = default.node_sound_gravel_defaults(),
drop = "", drop = "",
}) })

View File

@ -200,7 +200,7 @@ local tool_config = {
choices = "0%,20%,40%,60%,80%,98%", choices = "0%,20%,40%,60%,80%,98%",
name = "turnoff", name = "turnoff",
label = S("Turnoff point"), label = S("Turnoff point"),
tooltip = S("If the charge of the storage\nsystem exceeds the configured value,\nthe block switches off"), tooltip = S("If the charge of the storage\nsystem falls below the configured value,\nthe block switches off"),
default = "98%", default = "98%",
}, },
} }

View File

@ -105,6 +105,7 @@ dofile(MP.."/basis/submenu.lua")
dofile(MP.."/basis/shared_inv.lua") dofile(MP.."/basis/shared_inv.lua")
dofile(MP.."/basis/shared_tank.lua") dofile(MP.."/basis/shared_tank.lua")
dofile(MP.."/basis/teleport.lua") dofile(MP.."/basis/teleport.lua")
dofile(MP.."/basis/fly_lib.lua")
-- Main doc -- Main doc
dofile(MP.."/doc/manual_DE.lua") dofile(MP.."/doc/manual_DE.lua")

View File

@ -14,9 +14,29 @@
local S = techage.S local S = techage.S
local Stone2Gravel = {
["default:stone"] = "default:gravel",
["default:cobble"] = "default:gravel",
["default:desert_stone"] = "default:gravel",
["techage:basalt_stone"] = "techage:basalt_gravel",
["techage:basalt_cobble"] = "techage:basalt_gravel",
["techage:bauxite_stone"] = "techage:bauxite_gravel",
["techage:bauxite_cobble"] = "techage:bauxite_gravel",
}
function techage.register_stone_gravel_pair(stone_name, gravel_name)
Stone2Gravel[stone_name] = gravel_name
end
-- Pipeworks uses a fakeplayer based on the owner of the nodebraker.
local function is_real_player(player)
return minetest.is_player(player) and not player.is_fake_player
end
local function handler(player_name, node, itemstack, digparams) local function handler(player_name, node, itemstack, digparams)
local pos = techage.dug_node[player_name] local pos = techage.dug_node[player_name]
if not pos then return end if not pos then return end
techage.dug_node[player_name] = nil
if minetest.is_protected(pos, player_name) then if minetest.is_protected(pos, player_name) then
minetest.record_protection_violation(pos, player_name) minetest.record_protection_violation(pos, player_name)
@ -30,6 +50,7 @@ local function handler(player_name, node, itemstack, digparams)
local item = ItemStack(ndef.drop or node.name) local item = ItemStack(ndef.drop or node.name)
local inv = minetest.get_inventory({type="player", name=player_name}) local inv = minetest.get_inventory({type="player", name=player_name})
if inv and inv:room_for_item("main", item) then if inv and inv:room_for_item("main", item) then
-- item should have been added and can therefore be removed again
local taken = inv:remove_item("main", item) local taken = inv:remove_item("main", item)
else else
for _,obj in ipairs(minetest.get_objects_inside_radius(pos, 1)) do for _,obj in ipairs(minetest.get_objects_inside_radius(pos, 1)) do
@ -38,8 +59,8 @@ local function handler(player_name, node, itemstack, digparams)
end end
end end
end end
if node.name == "techage:basalt_stone" or node.name == "techage:basalt_cobble" then if Stone2Gravel[node.name] then
node.name = "techage:basalt_gravel" node.name = Stone2Gravel[node.name]
else else
node.name = "default:gravel" node.name = "default:gravel"
end end
@ -61,7 +82,9 @@ minetest.register_tool("techage:hammer_stone", {
}, },
sound = {breaks = "default_tool_breaks"}, sound = {breaks = "default_tool_breaks"},
after_use = function(itemstack, user, node, digparams) after_use = function(itemstack, user, node, digparams)
if is_real_player(user) then
minetest.after(0.01, handler, user:get_player_name(), node) minetest.after(0.01, handler, user:get_player_name(), node)
end
itemstack:add_wear(digparams.wear) itemstack:add_wear(digparams.wear)
return itemstack return itemstack
end, end,
@ -80,7 +103,9 @@ minetest.register_tool("techage:hammer_bronze", {
}, },
sound = {breaks = "default_tool_breaks"}, sound = {breaks = "default_tool_breaks"},
after_use = function(itemstack, user, node, digparams) after_use = function(itemstack, user, node, digparams)
if is_real_player(user) then
minetest.after(0.01, handler, user:get_player_name(), node) minetest.after(0.01, handler, user:get_player_name(), node)
end
itemstack:add_wear(digparams.wear) itemstack:add_wear(digparams.wear)
return itemstack return itemstack
end, end,
@ -99,7 +124,9 @@ minetest.register_tool("techage:hammer_steel", {
}, },
sound = {breaks = "default_tool_breaks"}, sound = {breaks = "default_tool_breaks"},
after_use = function(itemstack, user, node, digparams) after_use = function(itemstack, user, node, digparams)
if is_real_player(user) then
minetest.after(0.01, handler, user:get_player_name(), node) minetest.after(0.01, handler, user:get_player_name(), node)
end
itemstack:add_wear(digparams.wear) itemstack:add_wear(digparams.wear)
return itemstack return itemstack
end, end,
@ -118,7 +145,9 @@ minetest.register_tool("techage:hammer_mese", {
}, },
sound = {breaks = "default_tool_breaks"}, sound = {breaks = "default_tool_breaks"},
after_use = function(itemstack, user, node, digparams) after_use = function(itemstack, user, node, digparams)
if is_real_player(user) then
minetest.after(0.01, handler, user:get_player_name(), node) minetest.after(0.01, handler, user:get_player_name(), node)
end
itemstack:add_wear(digparams.wear) itemstack:add_wear(digparams.wear)
return itemstack return itemstack
end, end,
@ -137,7 +166,9 @@ minetest.register_tool("techage:hammer_diamond", {
}, },
sound = {breaks = "default_tool_breaks"}, sound = {breaks = "default_tool_breaks"},
after_use = function(itemstack, user, node, digparams) after_use = function(itemstack, user, node, digparams)
if is_real_player(user) then
minetest.after(0.01, handler, user:get_player_name(), node) minetest.after(0.01, handler, user:get_player_name(), node)
end
itemstack:add_wear(digparams.wear) itemstack:add_wear(digparams.wear)
return itemstack return itemstack
end, end,

View File

@ -10,7 +10,8 @@
Cracking breaks long chains of hydrocarbons into short chains using a catalyst. Cracking breaks long chains of hydrocarbons into short chains using a catalyst.
Gibbsite powder serves as a catalyst (is not consumed). Gibbsite powder serves as a catalyst (is not consumed).
It can be used to convert bitumen into fueloil, fueloil into naphtha and naphtha into gasoline. It can be used to convert bitumen into fueloil, fueloil into naphtha, naphtha into gasoline,
and gasoline into gas.
In hydrogenation, pairs of hydrogen atoms are added to a molecule to convert short-chain In hydrogenation, pairs of hydrogen atoms are added to a molecule to convert short-chain
hydrocarbons into long ones. hydrocarbons into long ones.
@ -45,6 +46,22 @@ techage.recipes.add("ta4_doser", {
catalyst = "techage:gibbsite_powder", catalyst = "techage:gibbsite_powder",
}) })
techage.recipes.add("ta4_doser", {
output = "techage:isobutane 1",
input = {
"techage:gasoline 1",
},
catalyst = "techage:gibbsite_powder",
})
techage.recipes.add("ta4_doser", {
output = "techage:gas 1",
input = {
"techage:isobutane 1",
},
catalyst = "techage:gibbsite_powder",
})
-- Hydrogenate -- Hydrogenate
techage.recipes.add("ta4_doser", { techage.recipes.add("ta4_doser", {
output = "techage:isobutane 1", output = "techage:isobutane 1",

View File

@ -70,3 +70,21 @@ techage.add_grinder_recipe({input="techage:aluminum", output="techage:aluminum_p
techage.add_grinder_recipe({input="default:silver_sandstone", output="techage:silver_sandstone_powder"}) techage.add_grinder_recipe({input="default:silver_sandstone", output="techage:silver_sandstone_powder"})
techage.add_grinder_recipe({input="default:coal_lump", output="techage:graphite_powder"}) techage.add_grinder_recipe({input="default:coal_lump", output="techage:graphite_powder"})
if minetest.get_modpath("ethereal") then
techage.add_grinder_recipe({input="ethereal:bush", output="techage:leave_powder"})
techage.add_grinder_recipe({input="ethereal:bush2", output="techage:leave_powder"})
techage.add_grinder_recipe({input="ethereal:bush3", output="techage:leave_powder"})
techage.add_grinder_recipe({input="ethereal:bamboo_leaves", output="techage:leave_powder"})
techage.add_grinder_recipe({input="ethereal:bananaleaves", output="techage:leave_powder"})
techage.add_grinder_recipe({input="ethereal:birch_leaves", output="techage:leave_powder"})
techage.add_grinder_recipe({input="ethereal:frost_leaves", output="techage:leave_powder"})
techage.add_grinder_recipe({input="ethereal:lemon_leaves", output="techage:leave_powder"})
techage.add_grinder_recipe({input="ethereal:olive_leaves", output="techage:leave_powder"})
techage.add_grinder_recipe({input="ethereal:orange_leaves", output="techage:leave_powder"})
techage.add_grinder_recipe({input="ethereal:palmleaves", output="techage:leave_powder"})
techage.add_grinder_recipe({input="ethereal:redwood_leaves", output="techage:leave_powder"})
techage.add_grinder_recipe({input="ethereal:sakura_leaves", output="techage:leave_powder"})
techage.add_grinder_recipe({input="ethereal:ethereal:sakura_leaves2", output="techage:leave_powder"})
techage.add_grinder_recipe({input="ethereal:willow_twig", output="techage:leave_powder"})
techage.add_grinder_recipe({input="ethereal:yellowleaves", output="techage:leave_powder"})
end

View File

@ -58,11 +58,13 @@ minetest.register_craft({
}) })
techage.furnace.register_recipe({ techage.furnace.register_recipe({
output = "techage:red_stone", output = "techage:red_stone 3",
recipe = { recipe = {
"techage:canister_redmud", "techage:canister_redmud",
"default:sand", "default:sand",
"default:sand",
"default:sand",
}, },
waste = "techage:ta3_canister_empty", waste = "techage:ta3_canister_empty",
time = 4, time = 10,
}) })

View File

@ -19,7 +19,19 @@ minetest.register_craftitem("techage:ta4_silicon_wafer", {
inventory_image = "techage_silicon_wafer.png", inventory_image = "techage_silicon_wafer.png",
}) })
techage.furnace.register_recipe({ if minetest.global_exists("mesecon") then
techage.furnace.register_recipe({
output = "techage:ta4_silicon_wafer 16",
recipe = {
"mesecons_materials:silicon",
"mesecons_materials:silicon",
"mesecons_materials:silicon",
"techage:baborium_ingot"
},
time = 6,
})
else
techage.furnace.register_recipe({
output = "techage:ta4_silicon_wafer 16", output = "techage:ta4_silicon_wafer 16",
recipe = { recipe = {
"basic_materials:silicon", "basic_materials:silicon",
@ -28,4 +40,6 @@ techage.furnace.register_recipe({
"techage:baborium_ingot" "techage:baborium_ingot"
}, },
time = 6, time = 6,
}) })
end

View File

@ -60,7 +60,7 @@ local State4 = techage.NodeStates:new({
-- Function returns the number of pumped units -- Function returns the number of pumped units
local function pump(pos, mem, nvm, state, outdir, units) local function pump(pos, mem, nvm, state, outdir, units)
local taken, name = liquid.take(pos, Pipe, Flip[outdir], nil, units, mem.dbg_cycles > 0) local taken, name = liquid.take(pos, Pipe, Flip[outdir], nil, units, mem.dbg_cycles > 0)
if taken > 0 then if taken > 0 and name then
local leftover = liquid.put(pos, Pipe, outdir, name, taken, mem.dbg_cycles > 0) local leftover = liquid.put(pos, Pipe, outdir, name, taken, mem.dbg_cycles > 0)
if leftover and leftover > 0 then if leftover and leftover > 0 then
-- air needs no tank -- air needs no tank

View File

@ -128,9 +128,15 @@ TA4 Button/Switch=TA4 Schalter/Taster
Access=Zugriff Access=Zugriff
Button protection=Tastenschutz Button protection=Tastenschutz
Type=Typ
### button.lua ###
### button_2x.lua ###
### button_4x.lua ###
### player_detector.lua ###
Command=Kommando Command=Kommando
Number=Nummer Number=Nummer
Type=Typ
### button.lua ### ### button.lua ###
### cart_detector.lua ### ### cart_detector.lua ###
@ -163,10 +169,15 @@ TA4 2x Button=TA4 2x Taster
### button_4x.lua ### ### button_4x.lua ###
Command to be sent (ignored for switches)=Zu sendender Befehl (wird für Schalter ignoriert) Command to be sent (ignored for switches)=Zu sendender Befehl (wird für Schalter ignoriert)
Destination block number=Zielblocknummer
Label for the button=Beschriftung für die Taste Label for the button=Beschriftung für die Taste
Momentary button or on/off switch=Taster oder Ein-/Ausschalter Momentary button or on/off switch=Taster oder Ein-/Ausschalter
### button_2x.lua ###
### button_4x.lua ###
### player_detector.lua ###
Destination block number=Zielblocknummer
### button_2x.lua ### ### button_2x.lua ###
### button_4x.lua ### ### button_4x.lua ###
### signallamp_2x.lua ### ### signallamp_2x.lua ###
@ -387,7 +398,7 @@ Power=Strom
Configurable value@nfor the current limit=Konfigurierbarer Wert@nfür die Strombegrenzung Configurable value@nfor the current limit=Konfigurierbarer Wert@nfür die Strombegrenzung
Current limitation=Strombegrenzung Current limitation=Strombegrenzung
Electrolyzer=Eletrolyseur Electrolyzer=Eletrolyseur
If the charge of the storage@nsystem exceeds the configured value,@nthe block switches off=Überschreitet die Ladung des@nSpeichersystems den konfigurierten@nWert, schaltet sich der Block ab If the charge of the storage@nsystem falls below the configured value,@nthe block switches off=unterschreitet die Ladung des@nSpeichersystems den konfigurierten@nWert, schaltet sich der Block ab
Maximum possible@ncurrent consumption=Maximal mögliche Stromaufnahme Maximum possible@ncurrent consumption=Maximal mögliche Stromaufnahme
Maximum power consumption [ku]=maximale Stromaufnahme Maximum power consumption [ku]=maximale Stromaufnahme
Storage full=Speicher voll Storage full=Speicher voll
@ -453,7 +464,9 @@ Destination position is protected=Zielposition ist geschützt
Error: Max. length of the flight route exceeded by @1 blocks !!=Fehler: max. Länge der Flugstrecke um @1 Blöcke überschritten !! Error: Max. length of the flight route exceeded by @1 blocks !!=Fehler: max. Länge der Flugstrecke um @1 Blöcke überschritten !!
No valid destination position=Keine gültige Zielposition No valid destination position=Keine gültige Zielposition
No valid node at the start position=Kein gültiger Block an der Startposition No valid node at the start position=Kein gültiger Block an der Startposition
Running=In Betrieb
Start position is protected=Startposition ist geschützt Start position is protected=Startposition ist geschützt
Stopped=Gestoppt
### fly_lib.lua ### ### fly_lib.lua ###
### flycontroller.lua ### ### flycontroller.lua ###
@ -959,6 +972,10 @@ Plastic Granules=Plastikgranulat
### player_detector.lua ### ### player_detector.lua ###
Command to send when player is detected=Befehl zum Senden, wenn ein Spieler erkannt wird
Command to send when player moves away=Befehl zum Senden, wenn sich der Spieler wegbewegt
Radius=Radius
Search radius=Suchradius
TA4 Player Detector=TA4 Spieler Detektor TA4 Player Detector=TA4 Spieler Detektor
### powder.lua ### ### powder.lua ###
@ -1151,12 +1168,14 @@ stopped=gestoppt
<num> is a number from 1 to 50000 and is@n=<num> ist eine Nummer von 1 bis 50000 und ist@n <num> is a number from 1 to 50000 and is@n=<num> ist eine Nummer von 1 bis 50000 und ist@n
@n=@n @n=@n
Commands=Kommandos Commands=Kommandos
Cycle time=Zykluszeit
Example:@n=Beispiel: Example:@n=Beispiel:
Invalid command!=Ungültiges Kommando! Invalid command!=Ungültiges Kommando!
Start=Start Start=Start
Stop=Stopp Stop=Stopp
Syntax:@n=Syntax:@n Syntax:@n=Syntax:@n
TA4 Sequencer=TA4 Sequenzer TA4 Sequencer=TA4 Sequenzer
Timer cycle time (default: 100 ms)=Zykluszeit (normal: 100 ms)
running=läuft running=läuft
the timeslot when the command is executed.@n=der Zeitpunkt, wenn der Befehl ausgeführt wird.@n the timeslot when the command is executed.@n=der Zeitpunkt, wenn der Befehl ausgeführt wird.@n

View File

@ -115,7 +115,7 @@ TA3 Booster=
Access:= Access:=
Button or switch= Button or switch=
Change the node name (infotext)= Change the block name (infotext)=
Command to be sent= Command to be sent=
Destination block number(s)= Destination block number(s)=
Infotext= Infotext=
@ -128,9 +128,15 @@ TA4 Button/Switch=
Access= Access=
Button protection= Button protection=
Type=
### button.lua ###
### button_2x.lua ###
### button_4x.lua ###
### player_detector.lua ###
Command= Command=
Number= Number=
Type=
### button.lua ### ### button.lua ###
### cart_detector.lua ### ### cart_detector.lua ###
@ -163,10 +169,15 @@ TA4 2x Button=
### button_4x.lua ### ### button_4x.lua ###
Command to be sent (ignored for switches)= Command to be sent (ignored for switches)=
Destination block number=
Label for the button= Label for the button=
Momentary button or on/off switch= Momentary button or on/off switch=
### button_2x.lua ###
### button_4x.lua ###
### player_detector.lua ###
Destination block number=
### button_2x.lua ### ### button_2x.lua ###
### button_4x.lua ### ### button_4x.lua ###
### signallamp_2x.lua ### ### signallamp_2x.lua ###
@ -387,7 +398,7 @@ Power=
Configurable value@nfor the current limit= Configurable value@nfor the current limit=
Current limitation= Current limitation=
Electrolyzer= Electrolyzer=
If the charge of the storage@nsystem exceeds the configured value,@nthe block switches off= If the charge of the storage@nsystem falls below the configured value,@nthe block switches off=
Maximum possible@ncurrent consumption= Maximum possible@ncurrent consumption=
Maximum power consumption [ku]= Maximum power consumption [ku]=
Storage full= Storage full=
@ -453,7 +464,9 @@ Destination position is protected=
Error: Max. length of the flight route exceeded by @1 blocks !!= Error: Max. length of the flight route exceeded by @1 blocks !!=
No valid destination position= No valid destination position=
No valid node at the start position= No valid node at the start position=
Running=
Start position is protected= Start position is protected=
Stopped=
### fly_lib.lua ### ### fly_lib.lua ###
### flycontroller.lua ### ### flycontroller.lua ###
@ -959,6 +972,10 @@ Plastic Granules=
### player_detector.lua ### ### player_detector.lua ###
Command to send when player is detected=
Command to send when player moves away=
Radius=
Search radius=
TA4 Player Detector= TA4 Player Detector=
### powder.lua ### ### powder.lua ###
@ -1151,12 +1168,14 @@ stopped=
<num> is a number from 1 to 50000 and is@n= <num> is a number from 1 to 50000 and is@n=
@n= @n=
Commands= Commands=
Cycle time=
Example:@n= Example:@n=
Invalid command!= Invalid command!=
Start= Start=
Stop= Stop=
Syntax:@n= Syntax:@n=
TA4 Sequencer= TA4 Sequencer=
Timer cycle time (default: 100 ms)=
running= running=
the timeslot when the command is executed.@n= the timeslot when the command is executed.@n=

View File

@ -68,10 +68,7 @@ local function switch_on(pos)
elseif name == "techage:ta4_button_off" then elseif name == "techage:ta4_button_off" then
logic.swap_node(pos, "techage:ta4_button_on") logic.swap_node(pos, "techage:ta4_button_on")
end end
local meta = M(pos) logic.send_cmnd(pos, "command", "on", cycle_time)
local s = meta:contains("command") and meta:get_string("command") or "on"
local command, payload = unpack(string.split(s, " ", false, 1))
logic.send_cmnd(pos, M(pos), command, payload, cycle_time)
minetest.sound_play("techage_button", { minetest.sound_play("techage_button", {
pos = pos, pos = pos,
gain = 0.5, gain = 0.5,

View File

@ -67,13 +67,16 @@ function techage.logic.send_on(pos, meta, time)
return own_num == numbers return own_num == numbers
end end
function techage.logic.send_cmnd(pos, meta, cmnd, payload, time) function techage.logic.send_cmnd(pos, ident, default, time)
local meta = M(pos)
local s = meta:contains(ident) and meta:get_string(ident) or default
local command, payload = unpack(string.split(s, " ", false, 1))
local own_num = meta:get_string("node_number") or "" local own_num = meta:get_string("node_number") or ""
local numbers = meta:get_string("numbers") or "" local numbers = meta:get_string("numbers") or ""
if time and time > 0 then if time and time > 0 then
minetest.get_node_timer(pos):start(time) minetest.get_node_timer(pos):start(time)
end end
techage.send_multi(own_num, numbers, cmnd, payload) techage.send_multi(own_num, numbers, command, payload)
end end
function techage.logic.send_off(pos, meta) function techage.logic.send_off(pos, meta)

View File

@ -20,15 +20,48 @@ local NDEF = function(pos) return (minetest.registered_nodes[techage.get_node_lv
local logic = techage.logic local logic = techage.logic
local CYCLE_TIME = 1 local CYCLE_TIME = 1
local WRENCH_MENU = {
{
type = "dropdown",
choices = "1,2,3,4,5,6,7,8",
name = "radius",
label = S("Radius"),
tooltip = S("Search radius"),
default = "4",
},
{
type = "numbers",
name = "numbers",
label = S("Number"),
tooltip = S("Destination block number"),
default = "",
check = techage.check_numbers,
},
{
type = "ascii",
name = "command1",
label = "On " .. S("Command"),
tooltip = S("Command to send when player is detected"),
default = "on",
},
{
type = "ascii",
name = "command2",
label = "Off " .. S("Command"),
tooltip = S("Command to send when player moves away"),
default = "off",
},
}
local function switch_on(pos, stage) local function switch_on(pos, stage)
if logic.swap_node(pos, "techage:ta"..stage.."_playerdetector_on") then if logic.swap_node(pos, "techage:ta"..stage.."_playerdetector_on") then
logic.send_on(pos, M(pos)) logic.send_cmnd(pos, "command1", "on")
end end
end end
local function switch_off(pos, stage) local function switch_off(pos, stage)
if logic.swap_node(pos, "techage:ta"..stage.."_playerdetector_off") then if logic.swap_node(pos, "techage:ta"..stage.."_playerdetector_off") then
logic.send_off(pos, M(pos)) logic.send_cmnd(pos, "command2", "off")
end end
end end
@ -36,7 +69,8 @@ local function scan_for_player(pos)
local nvm = techage.get_nvm(pos) local nvm = techage.get_nvm(pos)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local names = meta:get_string("names") or "" local names = meta:get_string("names") or ""
for _, object in pairs(minetest.get_objects_inside_radius(pos, 4)) do local radius = meta:contains("radius") and meta:get_int("radius") or 4
for _, object in pairs(minetest.get_objects_inside_radius(pos, radius)) do
if object:is_player() then if object:is_player() then
if names == "" then if names == "" then
nvm.player_name = object:get_player_name() nvm.player_name = object:get_player_name()
@ -228,6 +262,7 @@ minetest.register_node("techage:ta4_playerdetector_off", {
techage.del_mem(pos) techage.del_mem(pos)
end, end,
ta4_formspec = WRENCH_MENU,
paramtype = "light", paramtype = "light",
sunlight_propagates = true, sunlight_propagates = true,
paramtype2 = "facedir", paramtype2 = "facedir",
@ -277,6 +312,7 @@ minetest.register_node("techage:ta4_playerdetector_on", {
techage.del_mem(pos) techage.del_mem(pos)
end, end,
ta4_formspec = WRENCH_MENU,
paramtype = "light", paramtype = "light",
sunlight_propagates = true, sunlight_propagates = true,
paramtype2 = "facedir", paramtype2 = "facedir",

View File

@ -17,13 +17,22 @@ Die Haltbarkeit/Härte bspw. für eine Axt ist:
Den Köhler brauchst du, um Holzkohle herzustellen. Holzkohle wird für den Brenner, aber auch bspw. in TA2 für die Dampfmaschine benötigt. Den Köhler brauchst du, um Holzkohle herzustellen. Holzkohle wird für den Brenner, aber auch bspw. in TA2 für die Dampfmaschine benötigt.
Für den Köhler brauchst du:
- einen Anzünderblock (`techage:lighter`)
- 26 Hölzblöcke (wood), die zu einem Würfen aufgeschichtet werden. Die Holzsorte spielt keine Rolle
- Erde (dirt) um den Holzhaufen abzudecken
- Flint and Iron (technischer Name: `fire:flint_and_steel`) um den Anzünderblock anzuzünden
Bauanleitung (siehe auch Plan): Bauanleitung (siehe auch Plan):
- Baue eine 5x5 große Fläche aus Erde (dirt) - Baue eine 5x5 große Fläche aus Erde (dirt)
- Platziere in die Mitte einen Anzünder (lighter) - Platziere in die Mitte einen Anzünder (lighter)
- Baue aus Holz (wood) einen 3x3x3 großen Würfel darüber - Platziere rund um den Anzünder 7 Holz (wood), aber lasse ein Loch zum Anzünder frei
- Überdecke alles mit einer Schicht Erde zu einem 5x5x5 großen Würfel - Baue weitere 2 Schichten Holz darüber, so dass ein 3x3x3 großen Holzwürfel entsteht
- Lasse ein Loch zum Anzünder - Überdecke alles mit einer Schicht Erde zu einem 5x5x5 großen Würfel, aber lasse das Loch zum Anzünder frei
- Zünde den Anzünder an und verschließe das Loch sofort mit jeweils einem Block Holz und Erde - Zünde den Anzünder an und verschließe das Loch sofort mit jeweils einem Block Holz und Erde
- Wenn du alles richtig gemacht hast, beginnt der Köhler nach wenigen Sekunden an zu rauchen - Wenn du alles richtig gemacht hast, beginnt der Köhler nach wenigen Sekunden an zu rauchen
- Öffne den Köhler erst, wenn der Rauch verschwunden ist (ca. 20 min) - Öffne den Köhler erst, wenn der Rauch verschwunden ist (ca. 20 min)

View File

@ -17,13 +17,21 @@ The durability / hardness for an axe, for example:
You need the Charcoal Pile to make charcoal. Charcoal is required for the melting furnace, but also, for example, in TA2 for the steam engine. You need the Charcoal Pile to make charcoal. Charcoal is required for the melting furnace, but also, for example, in TA2 for the steam engine.
For the charcoal burner you need:
- a lighter block (`techage:lighter`)
- 26 wooden blocks that are stacked into a pile of wood. The type of wood is irrelevant
- Dirt to cover the pile of wood
- Flint and Iron (technical name: `fire:flint_and_steel`) to light the lighter block
Building instructions (see also plan): Building instructions (see also plan):
- Build a 5x5 area of dirt - Build a 5x5 area of dirt
- Place a lighter in the middle - Place 7 wood around the lighter but leave a hole to the lighter
- Build a 3x3x3 cube above it out of wood - Build another 2 layers of wood on top, making a 3x3x3 wooden cube
- Cover everything with a layer of dirt to form a 5x5x5 cube - Cover everything with a layer of dirt into a 5x5x5 cube, but keep the hole to the lighter open
- Leave a hole to the lighter
- Light the lighter and immediately close the hole with a block of wood and dirt - Light the lighter and immediately close the hole with a block of wood and dirt
- If you have done everything correctly, the coal burner will start smoking after a few seconds - If you have done everything correctly, the coal burner will start smoking after a few seconds
- Only open the charcoal burner when the smoke has disappeared (approx. 20 min) - Only open the charcoal burner when the smoke has disappeared (approx. 20 min)

View File

@ -258,6 +258,8 @@ In den Elektrolyseur passen 200 Einheiten Wasserstoff.
Der Elektrolyseur besitzt ein Schraubenschlüssel-Menü zur Einstellung der Stromaufnahme und des Abschaltpunkts. Der Elektrolyseur besitzt ein Schraubenschlüssel-Menü zur Einstellung der Stromaufnahme und des Abschaltpunkts.
Unterschreitet die im Stromnetz gespeicherte Leistung den angegebenen Wert des Abschaltpunkts, so schaltet sich der Elektrolyseur automatisch ab. Damit kann ein Leerlaufen der Speichersysteme verhindert werden.
[ta4_electrolyzer|image] [ta4_electrolyzer|image]
@ -279,15 +281,17 @@ In diesem Fall können keine anderen Blöcke der Kategorie 2 wie der Akku-Block
Der Reaktor dient dazu, die über den Destillationsturm oder aus anderen Rezepten gewonnenen Zutaten zu neuen Produkten weiter zu verarbeiten. Der Plan links zeigt nur eine mögliche Variante, da die Anordnung der Silos und Tanks rezeptabhängig ist. Der Reaktor dient dazu, die über den Destillationsturm oder aus anderen Rezepten gewonnenen Zutaten zu neuen Produkten weiter zu verarbeiten. Der Plan links zeigt nur eine mögliche Variante, da die Anordnung der Silos und Tanks rezeptabhängig ist.
Das primäre Ausgabeprodukt wird immer an der Seite des Reaktorständers ausgegeben, unabhängig davon, ob es sich um ein Pulver oder eine Flüssigkeit handelt. Das (sekundäre) Abfallprodukt wird immer unten am Reaktorständers ausgegeben.
Ein Reaktor besteht aus: Ein Reaktor besteht aus:
- div. Tanks und Silos mit den Zutaten, die über Leitungen mit dem Dosierer verbunden sind - div. Tanks und Silos mit den Zutaten, die über Leitungen mit dem Dosierer verbunden sind
- optional einem Reaktorsockel, welcher die Abfälle aus dem Reaktor ableitet (nur bei Rezepten mit zwei Ausgangsstoffen notwendig) - optional einem Reaktorsockel, welcher die Abfälle aus dem Reaktor ableitet (nur bei Rezepten mit zwei Ausgabestoffen notwendig)
- dem Reaktorständer, der auf den Sockel gesetzt werden muss (sofern vorhanden). Der Ständer hat einen Stromanschluss und zieht bei Betrieb 8 ku. - dem Reaktorständer, der auf den Sockel gesetzt werden muss (sofern vorhanden). Der Ständer hat einen Stromanschluss und zieht bei Betrieb 8 ku.
- dem eigentlichen Reaktorbehälter, der auf den Reaktorständer gesetzt werden muss - dem eigentlichen Reaktorbehälter, der auf den Reaktorständer gesetzt werden muss
- dem Einfüllstutzen der auf den Reaktorbehälter gesetzt werden muss - dem Einfüllstutzen der auf den Reaktorbehälter gesetzt werden muss
- dem Dosierer, welcher über Leitungen mit den Tanks oder Silos sowie dem Einfüllstutzen verbunden werden muss - dem Dosierer, welcher über Leitungen mit den Tanks oder Silos sowie dem Einfüllstutzen verbunden werden muss
Hinweis 1: Flüssigkeiten werden nur in Tanks gelagert, feste Stoffe und Stoffe in Pulverform nur in Silos. Dies gilt für Zutaten und Ausgangsstoffe. Hinweis 1: Flüssigkeiten werden nur in Tanks gelagert, feste Stoffe und Stoffe in Pulverform nur in Silos. Dies gilt für Zutaten und Ausgabestoffe.
Hinweis 2: Tanks oder Silos mit verschiedenen Inhalten dürfen nicht zu einem Leitungssystem verbunden werden. Mehrere Tanks oder Silos mit gleichem Inhalt dürfen dagegen parallel an einer Leitung hängen. Hinweis 2: Tanks oder Silos mit verschiedenen Inhalten dürfen nicht zu einem Leitungssystem verbunden werden. Mehrere Tanks oder Silos mit gleichem Inhalt dürfen dagegen parallel an einer Leitung hängen.
@ -310,7 +314,7 @@ Auf allen 4 Seiten der Dosierers können Leitungen für Eingangsmaterialien ange
Wie auch bei anderen Maschinen: Wie auch bei anderen Maschinen:
- geht der Dosierer in den standby Zustand, so fehlen ein oder mehrere Zutaten - geht der Dosierer in den standby Zustand, so fehlen ein oder mehrere Zutaten
- geht der Dosierer in den blocked Zustand, so ist Ausgangstank oder Silo voll, defekt oder falsch angeschlossen - geht der Dosierer in den blocked Zustand, so ist Ausgabetank oder Silo voll, defekt oder falsch angeschlossen
Der Dosierer benötigt keinen Strom. Alle 10 s wird ein Rezept abgearbeitet. Der Dosierer benötigt keinen Strom. Alle 10 s wird ein Rezept abgearbeitet.
@ -335,7 +339,7 @@ Teil des Chemischen Reaktors. Muss auf den Reaktor gesetzt werden. Wenn dies nic
Teil des Chemischen Reaktors. Hier ist auch der Stromanschluss für den Reaktor. Der Reaktor benötigt 8 ku Strom. Teil des Chemischen Reaktors. Hier ist auch der Stromanschluss für den Reaktor. Der Reaktor benötigt 8 ku Strom.
Der Ständer hat zwei Leitungsanschlüsse, nach rechst für das Ausgangsprodukt und nach unten für den Abfall, wie bspw. Rotschlamm bei der Aluminiumherstellung. Der Ständer hat zwei Leitungsanschlüsse, nach rechst für das primäre Ausgabeprodukt und nach unten für den Abfall, wie bspw. Rotschlamm bei der Aluminiumherstellung.
[ta4_reactorstand|image] [ta4_reactorstand|image]
@ -583,6 +587,12 @@ Der Move Controller unterstützt folgende techage Kommandos:
- `b2a` Bewege Block von B nach A - `b2a` Bewege Block von B nach A
- `move` Bewege Block auf die andere Seite - `move` Bewege Block auf die andere Seite
Über das Schraubenschlüssel-Menü kann auf die Betriebsart `move xyz` umgeschaltet werden. Nach der Umschaltung werden folgende techage Kommandos unterstützt:
- `move2` Beim Kommando muss zusätzlich die Flugstrecke als x,y,z Vektor angegeben werden.
Beispiel Lua Controller: `$send_cmnd(MOVE_CTLR, "move2", "0,12,0")`
- `reset` Block/Blöcke zurück in Startposition bewegen
**Wichtige Hinweise:** **Wichtige Hinweise:**
- Sofern mehrere Blöcke bewegt werden sollen, muss der Block, der die Spieler/Mobs mitnehmen soll, beim Antrainieren als erstes angeklickt werden. - Sofern mehrere Blöcke bewegt werden sollen, muss der Block, der die Spieler/Mobs mitnehmen soll, beim Antrainieren als erstes angeklickt werden.

View File

@ -251,6 +251,8 @@ The electrolyzer can draw up to 35 ku of electricity and then generates a hydrog
The electrolyzer has a wrench menu for setting the current consumption and the switch-off point. The electrolyzer has a wrench menu for setting the current consumption and the switch-off point.
If the power stored in the power grid falls below the specified value of the switch-off point, the electrolyzer switches off automatically. This prevents the storage systems from running empty.
[ta4_electrolyzer|image] [ta4_electrolyzer|image]
@ -272,15 +274,17 @@ In this case, no other category 2 blocks such as the battery block can be charge
The reactor is used to process the ingredients obtained from the distillation tower or from other recipes into new products. The reactor is used to process the ingredients obtained from the distillation tower or from other recipes into new products.
The plan on the left shows only one possible variant, since the arrangement of the silos and tanks depends on the recipe. The plan on the left shows only one possible variant, since the arrangement of the silos and tanks depends on the recipe.
The primary output product is always output to the side of the reactor stand, regardless of whether it is a powder or a liquid. The (secondary) waste product is always discharged at the bottom of the reactor stand.
A reactor consists of: A reactor consists of:
- Various tanks and silos with the ingredients that are connected to the doser via pipes - Various tanks and silos with the ingredients that are connected to the doser via pipes
- optionally a reactor base, which discharges the waste from the reactor (only necessary for recipes with two starting materials) - optionally a reactor base, which discharges the waste from the reactor (only necessary for recipes with two output products)
- the reactor stand, which must be placed on the base (if available). The stand has a power connection and draws 8 ku during operation. - the reactor stand, which must be placed on the base (if available). The stand has a power connection and draws 8 ku during operation.
- The reactor vessel that has to be placed on the reactor stand - The reactor vessel that has to be placed on the reactor stand
- The filler pipe that must be placed on the reactor vessel - The filler pipe that must be placed on the reactor vessel
- The dosing device, which has to be connected to the tanks or silos and the filler pipe via pipes - The dosing device, which has to be connected to the tanks or silos and the filler pipe via pipes
Note 1: Liquids are only stored in tanks, solids and substances in powder form only in silos. This applies to ingredients and raw materials. Note 1: Liquids are only stored in tanks, solids and substances in powder form only in silos. This applies to ingredients and output products.
Note 2: Tanks or silos with different contents must not be connected to a pipe system. In contrast, several tanks or silos with the same content may hang in parallel on one line. Note 2: Tanks or silos with different contents must not be connected to a pipe system. In contrast, several tanks or silos with the same content may hang in parallel on one line.
@ -304,7 +308,7 @@ The recipe can be set and the reactor started via the doser.
As with other machines: As with other machines:
- if the doser is in standby mode, one or more ingredients are missing - if the doser is in standby mode, one or more ingredients are missing
- if the doser is in the blocked state, the outlet tank or silo is full, defective or incorrectly connected - if the doser is in the blocked state, the output tank or silo is full, defective or incorrectly connected
The doser does not need any electricity. A recipe is processed every 10 s. The doser does not need any electricity. A recipe is processed every 10 s.
@ -574,6 +578,12 @@ The Move Controller supports the following techage commands:
- `b2a` Move block from B to A. - `b2a` Move block from B to A.
- `move` Move block to the other side - `move` Move block to the other side
You can switch to the `move xyz` operating mode via the wrench menu. After switching, the following techage commands are supported:
- `move2` With the command, the flight route must also be specified as an x,y,z vector.
Example Lua Controller: `$send_cmnd(MOVE_CTLR, "move2", "0,12,0")`
- `reset` move block(s) back to start position
**Important instructions:** **Important instructions:**
- If several blocks are to be moved, the block that is to take the players/mobs must be clicked first when training. - If several blocks are to be moved, the block that is to take the players/mobs must be clicked first when training.

View File

@ -159,3 +159,9 @@ Für die Nutzung des TA5 Containers werden 80 Erfahrungspunkte benötigt.
Der TA5 KI Chip wird teilweise zur Herstellung von TA5 Blöcken benötigt. Der TA5 KI Chip kann nur auf der TA4 Elektronik Fab hergestellt werden. Dazu werden 10 Erfahrungspunkte benötigt. Der TA5 KI Chip wird teilweise zur Herstellung von TA5 Blöcken benötigt. Der TA5 KI Chip kann nur auf der TA4 Elektronik Fab hergestellt werden. Dazu werden 10 Erfahrungspunkte benötigt.
[ta5_aichip|image] [ta5_aichip|image]
### TA5 KI Chip II / TA5 AI Chip II
Der TA5 KI Chip II wird zur Herstellung des TA5 Fusionsreaktors benötigt. Der TA5 KI Chip II kann nur auf der TA4 Elektronik Fab hergestellt werden. Dazu werden 50 Erfahrungspunkte benötigt.
[ta5_aichip2|image]

View File

@ -159,3 +159,9 @@ The TA5 container allows Techage systems to be packed and unpacked at another lo
The TA5 AI Chip is partly required for the production of TA5 blocks. The TA5 AI Chip can only be manufactured at the TA4 Electronics Fab. This requires 10 experience points. The TA5 AI Chip is partly required for the production of TA5 blocks. The TA5 AI Chip can only be manufactured at the TA4 Electronics Fab. This requires 10 experience points.
[ta5_aichip|image] [ta5_aichip|image]
### TA5 AI Chip II
The TA5 AI Chip II is required to build the TA5 Fusion Reactor. The TA5 AI Chip II can only be manufactured at the TA4 Electronics Fab. This requires 50 experience points.
[ta5_aichip2|image]

View File

@ -5,7 +5,7 @@
import re import re
import sys import sys
import pprint import pprint
import mistune # must be v0.8.4 import mistune # must be v0.8.4, install with 'sudo pip install mistune==0.8.4'
def formspec_escape(text): def formspec_escape(text):
text = text.replace("\\", "") text = text.replace("\\", "")

View File

@ -406,7 +406,7 @@ Please note, that this is not a technical distinction, only a logical.
| "a2b" | nil | TA4 Move Controller command to move the block(s) from position A to B | | "a2b" | nil | TA4 Move Controller command to move the block(s) from position A to B |
| "b2a" | nil | TA4 Move Controller command to move the block(s) from position B to A | | "b2a" | nil | TA4 Move Controller command to move the block(s) from position B to A |
| "move" | nil | TA4 Move Controller command to move the block(s) to the opposite position | | "move" | nil | TA4 Move Controller command to move the block(s) to the opposite position |
| "move2" | x,y,z | TA4 Move Controller command to move the block(s) by the given<br /> x/y/z-distance. Valid ranges for x, y, and z are -100 to 100. | | "move2" | x,y,z | TA4 Move Controller command to move the block(s) by the given<br /> x/y/z-distance. Valid ranges for x, y, and z are -100 to 100.<br />Example: `$send_cmnd("1674", "move2", "0,4,0")` |
| "reset" | nil | Reset TA4 Move Controller (move block(s) to start position) | | "reset" | nil | Reset TA4 Move Controller (move block(s) to start position) |
| "left" | nil | TA4 Turn Controller command to turn the block(s) to the left | | "left" | nil | TA4 Turn Controller command to turn the block(s) to the left |
| "right" | nil | TA4 Turn Controller command to turn the block(s) to the right | | "right" | nil | TA4 Turn Controller command to turn the block(s) to the right |

View File

@ -253,3 +253,4 @@
- [Weitere TA5 Blöcke/Items](./manual_ta5_DE.md#weitere-ta5-blöckeitems) - [Weitere TA5 Blöcke/Items](./manual_ta5_DE.md#weitere-ta5-blöckeitems)
- [TA5 Container (geplant)](./manual_ta5_DE.md#ta5-container-(geplant)) - [TA5 Container (geplant)](./manual_ta5_DE.md#ta5-container-(geplant))
- [TA5 KI Chip / TA5 AI Chip](./manual_ta5_DE.md#ta5-ki-chip--ta5-ai-chip) - [TA5 KI Chip / TA5 AI Chip](./manual_ta5_DE.md#ta5-ki-chip--ta5-ai-chip)
- [TA5 KI Chip II / TA5 AI Chip II](./manual_ta5_DE.md#ta5-ki-chip-ii--ta5-ai-chip-ii)

View File

@ -253,3 +253,4 @@
- [More TA5 Blocks/Items](./manual_ta5_EN.md#more-ta5-blocksitems) - [More TA5 Blocks/Items](./manual_ta5_EN.md#more-ta5-blocksitems)
- [TA5 Container (planned)](./manual_ta5_EN.md#ta5-container-(planned)) - [TA5 Container (planned)](./manual_ta5_EN.md#ta5-container-(planned))
- [TA5 AI Chip](./manual_ta5_EN.md#ta5-ai-chip) - [TA5 AI Chip](./manual_ta5_EN.md#ta5-ai-chip)
- [TA5 AI Chip II](./manual_ta5_EN.md#ta5-ai-chip-ii)

View File

@ -1,4 +1,4 @@
name = techage name = techage
depends = default,doors,flowers,tubelib2,networks,basic_materials,bucket,stairs,screwdriver,minecart,lcdlib,safer_lua depends = default,doors,flowers,tubelib2,networks,basic_materials,bucket,stairs,screwdriver,minecart,lcdlib,safer_lua
optional_depends = unified_inventory,wielded_light,unifieddyes,moreores,ethereal,mesecons,digtron,bakedclay,moreblocks,i3,creative,craftguide optional_depends = unified_inventory,wielded_light,unifieddyes,moreores,ethereal,mesecons,mesecons_materials,mesecons_mvps,digtron,bakedclay,moreblocks,i3,creative,craftguide
description = Techage, go through 5 tech ages in search of wealth and power! description = Techage, go through 5 tech ages in search of wealth and power!

View File

@ -17,9 +17,8 @@ local M = minetest.get_meta
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local S = techage.S local S = techage.S
local MP = minetest.get_modpath("techage")
local flylib = dofile(MP .. "/basis/fly_lib.lua")
local logic = techage.logic local logic = techage.logic
local fly = techage.flylib
local MarkedNodes = {} -- t[player] = {{entity, pos},...} local MarkedNodes = {} -- t[player] = {{entity, pos},...}
local CurrentPos -- to mark punched entities local CurrentPos -- to mark punched entities
@ -141,9 +140,9 @@ local function exchange_node(pos, item, param2)
local node = minetest.get_node_or_nil(pos) local node = minetest.get_node_or_nil(pos)
if node and is_simple_node(node.name) then if node and is_simple_node(node.name) then
if item and item:get_name() ~= "" and minetest.registered_nodes[item:get_name()] then if item and item:get_name() ~= "" and minetest.registered_nodes[item:get_name()] then
flylib.exchange_node(pos, item:get_name(), param2) fly.exchange_node(pos, item:get_name(), param2)
else else
flylib.remove_node(pos) fly.remove_node(pos)
end end
if not techage.is_air_like(node.name) then if not techage.is_air_like(node.name) then
return ItemStack(node.name), node.param2 return ItemStack(node.name), node.param2
@ -439,7 +438,7 @@ local Doors = {
for _, name in ipairs(Doors) do for _, name in ipairs(Doors) do
for _, postfix in ipairs({"a", "b", "c", "d"}) do for _, postfix in ipairs({"a", "b", "c", "d"}) do
techage.register_simple_nodes({name .. "_" .. postfix}, true) techage.register_simple_nodes({name .. "_" .. postfix}, true)
flylib.protect_door_from_being_opened(name .. "_" .. postfix) fly.protect_door_from_being_opened(name .. "_" .. postfix)
end end
end end
@ -453,6 +452,6 @@ local ProtectorDoors = {
for _, name in ipairs(ProtectorDoors) do for _, name in ipairs(ProtectorDoors) do
for _, postfix in ipairs({"b_1", "b_2", "t_1", "t_2"}) do for _, postfix in ipairs({"b_1", "b_2", "t_1", "t_2"}) do
techage.register_simple_nodes({name .. "_" .. postfix}, true) techage.register_simple_nodes({name .. "_" .. postfix}, true)
flylib.protect_door_from_being_opened(name .. "_" .. postfix) fly.protect_door_from_being_opened(name .. "_" .. postfix)
end end
end end

View File

@ -19,8 +19,8 @@ local S2P = minetest.string_to_pos
local S = techage.S local S = techage.S
local MP = minetest.get_modpath("techage") local MP = minetest.get_modpath("techage")
local fly = dofile(MP .. "/basis/fly_lib.lua")
local mark = dofile(MP .. "/basis/mark_lib.lua") local mark = dofile(MP .. "/basis/mark_lib.lua")
local fly = techage.flylib
local MAX_DIST = 500 local MAX_DIST = 500
local MAX_BLOCKS = 16 local MAX_BLOCKS = 16

View File

@ -19,8 +19,8 @@ local S2P = minetest.string_to_pos
local S = techage.S local S = techage.S
local MP = minetest.get_modpath("techage") local MP = minetest.get_modpath("techage")
local fly = dofile(MP .. "/basis/fly_lib.lua")
local mark = dofile(MP .. "/basis/mark_lib.lua") local mark = dofile(MP .. "/basis/mark_lib.lua")
local fly = techage.flylib
local MAX_DIST = 200 local MAX_DIST = 200
local MAX_BLOCKS = 16 local MAX_BLOCKS = 16
@ -40,7 +40,7 @@ local WRENCH_MENU = {
label = S("Handover to B"), label = S("Handover to B"),
tooltip = S("Number of the next movecontroller"), tooltip = S("Number of the next movecontroller"),
default = "", default = "",
check = techage.check_numbers, check = techage.check_number,
}, },
{ {
type = "number", type = "number",
@ -48,7 +48,7 @@ local WRENCH_MENU = {
label = S("Handover to A"), label = S("Handover to A"),
tooltip = S("Number of the previous movecontroller"), tooltip = S("Number of the previous movecontroller"),
default = "", default = "",
check = techage.check_numbers, check = techage.check_number,
}, },
{ {
type = "float", type = "float",
@ -131,6 +131,7 @@ minetest.register_node("techage:ta4_movecontroller", {
nvm.lpos2 = {} nvm.lpos2 = {}
nvm.moveBA = false nvm.moveBA = false
nvm.running = nil nvm.running = nil
nvm.lastpos = nil
meta:set_string("status", S("Recording...")) meta:set_string("status", S("Recording..."))
local name = player:get_player_name() local name = player:get_player_name()
minetest.chat_send_player(name, S("Click on all blocks that shall be moved")) minetest.chat_send_player(name, S("Click on all blocks that shall be moved"))
@ -144,6 +145,7 @@ minetest.register_node("techage:ta4_movecontroller", {
end end
local text = #pos_list.." "..S("block positions are stored.") local text = #pos_list.." "..S("block positions are stored.")
nvm.running = nil nvm.running = nil
nvm.lastpos = nil
meta:set_string("status", text) meta:set_string("status", text)
nvm.lpos1 = pos_list nvm.lpos1 = pos_list
mark.unmark_all(name) mark.unmark_all(name)
@ -161,11 +163,10 @@ minetest.register_node("techage:ta4_movecontroller", {
mark.stop(name) mark.stop(name)
nvm.moveBA = false nvm.moveBA = false
nvm.running = nil nvm.running = nil
nvm.lastpos = nil
elseif fields.moveAB then elseif fields.moveAB then
meta:set_string("status", "") meta:set_string("status", "")
if fly.move_to_other_pos(pos, false) then if fly.move_to_other_pos(pos, false) then
nvm.moveBA = true
nvm.running = true
meta:set_string("formspec", formspec(nvm, meta)) meta:set_string("formspec", formspec(nvm, meta))
local name = player:get_player_name() local name = player:get_player_name()
mark.stop(name) mark.stop(name)
@ -174,8 +175,6 @@ minetest.register_node("techage:ta4_movecontroller", {
elseif fields.moveBA then elseif fields.moveBA then
meta:set_string("status", "") meta:set_string("status", "")
if fly.move_to_other_pos(pos, true) then if fly.move_to_other_pos(pos, true) then
nvm.moveBA = false
nvm.running = true
meta:set_string("formspec", formspec(nvm, meta)) meta:set_string("formspec", formspec(nvm, meta))
local name = player:get_player_name() local name = player:get_player_name()
mark.stop(name) mark.stop(name)
@ -187,11 +186,9 @@ minetest.register_node("techage:ta4_movecontroller", {
end end
local line = fly.to_vector(meta:get_string("path"), MAX_DIST) local line = fly.to_vector(meta:get_string("path"), MAX_DIST)
if line then if line then
nvm.running = true
fly.move_to(pos, line) fly.move_to(pos, line)
end end
elseif fields.reset then elseif fields.reset then
nvm.running = true
fly.reset_move(pos) fly.reset_move(pos)
end end
end, end,
@ -230,28 +227,18 @@ techage.register_node({"techage:ta4_movecontroller"}, {
elseif topic == "state" then elseif topic == "state" then
return nvm.running and "running" or "stopped" return nvm.running and "running" or "stopped"
elseif not move_xyz and topic == "a2b" then elseif not move_xyz and topic == "a2b" then
nvm.moveBA = true
nvm.running = true
return fly.move_to_other_pos(pos, false) return fly.move_to_other_pos(pos, false)
elseif not move_xyz and topic == "b2a" then elseif not move_xyz and topic == "b2a" then
nvm.moveBA = false
nvm.running = true
return fly.move_to_other_pos(pos, true) return fly.move_to_other_pos(pos, true)
elseif not move_xyz and topic == "move" then elseif not move_xyz and 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 == false)
elseif move_xyz and topic == "move2" then elseif move_xyz and topic == "move2" then
local line = fly.to_vector(payload, MAX_DIST) local line = fly.to_vector(payload, MAX_DIST)
if line then if line then
nvm.running = true
nvm.controller_mode = true
return fly.move_to(pos, line) return fly.move_to(pos, line)
end end
return false return false
elseif topic == "reset" then elseif topic == "reset" then
nvm.running = true
nvm.controller_mode = true
return fly.reset_move(pos) return fly.reset_move(pos)
end end
return false return false
@ -262,16 +249,10 @@ techage.register_node({"techage:ta4_movecontroller"}, {
--print("on_beduino_receive_cmnd", P2S(pos), move_xyz, topic, payload[1]) --print("on_beduino_receive_cmnd", P2S(pos), move_xyz, topic, payload[1])
if not move_xyz and topic == 11 then if not move_xyz and topic == 11 then
if payload[1] == 1 then if payload[1] == 1 then
nvm.moveBA = true
nvm.running = true
return fly.move_to_other_pos(pos, false) and 0 or 3 return fly.move_to_other_pos(pos, false) and 0 or 3
elseif payload[1] == 2 then elseif payload[1] == 2 then
nvm.moveBA = false
nvm.running = true
return fly.move_to_other_pos(pos, true) and 0 or 3 return fly.move_to_other_pos(pos, true) and 0 or 3
elseif payload[1] == 3 then 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 == false) and 0 or 3
end end
elseif move_xyz and topic == 18 then -- move xyz elseif move_xyz and topic == 18 then -- move xyz
@ -280,12 +261,8 @@ techage.register_node({"techage:ta4_movecontroller"}, {
y = techage.in_range(techage.beduino_signed_var(payload[2]), -100, 100), y = techage.in_range(techage.beduino_signed_var(payload[2]), -100, 100),
z = techage.in_range(techage.beduino_signed_var(payload[3]), -100, 100), z = techage.in_range(techage.beduino_signed_var(payload[3]), -100, 100),
} }
nvm.running = true
nvm.controller_mode = true
return fly.move_to(pos, line) and 0 or 3 return fly.move_to(pos, line) and 0 or 3
elseif move_xyz and topic == 19 then -- reset elseif move_xyz and topic == 19 then -- reset
nvm.running = true
nvm.controller_mode = true
return fly.reset_move(pos) and 0 or 3 return fly.reset_move(pos) and 0 or 3
else else
return 2 return 2
@ -298,6 +275,10 @@ techage.register_node({"techage:ta4_movecontroller"}, {
end end
return 2, "" return 2, ""
end, end,
on_node_load = function(pos, node)
local nvm = techage.get_nvm(pos)
nvm.running = false
end,
}) })
minetest.register_craft({ minetest.register_craft({

View File

@ -19,8 +19,8 @@ local S2P = minetest.string_to_pos
local S = techage.S local S = techage.S
local MP = minetest.get_modpath("techage") local MP = minetest.get_modpath("techage")
local fly = dofile(MP .. "/basis/fly_lib.lua")
local mark = dofile(MP .. "/basis/mark_lib.lua") local mark = dofile(MP .. "/basis/mark_lib.lua")
local fly = techage.flylib
local MAX_BLOCKS = 16 local MAX_BLOCKS = 16

View File

@ -229,7 +229,7 @@ local function build_crane_up(pos, owner, height, width)
", "..S("Crane size")..": "..height..","..width) ", "..S("Crane size")..": "..height..","..width)
meta:set_string("formspec", formspec(height, width)) meta:set_string("formspec", formspec(height, width))
else else
chat(owner, S("Area is protected or too less space for the crane!")) chat(owner, S("Area is protected or not enough space for the crane!"))
end end
end end
else else

View File

@ -0,0 +1,16 @@
# textdomain: towercrane
Area is protected.=
Tower Crane Mast Ctrl On=
Switch crane on/off=
Tower Crane Mast Ctrl Off=
Construction area size=
Build=
Owner=
Crane size=
Area is protected or too less space for the crane!=
Invalid input!=
Tower Crane Base=
Tower Crane Balance=
Tower Crane Mast=
Tower Crane Arm=
Tower Crane Arm2=

View File

@ -0,0 +1,16 @@
# textdomain: towercrane
Area is protected.=Areo estas protektita.
Tower Crane Mast Ctrl On=Turgruomasta Kontrolo Enŝaltita
Switch crane on/off=Ŝaltu/malŝaltu gruon
Tower Crane Mast Ctrl Off=Turgruomasta Kontrolo Malŝaltita
Construction area size=Konstrua area grandeco
Build=Konstruita
Owner=Posedanto
Crane size=Gruo grandeco
Area is protected or not enough space for the crane!=Areo estas protektita aŭ ne sufice da spaco por la gruo!
Invalid input!=Nevalida enigo!
Tower Crane Base=Bazo de Turgruo
Tower Crane Balance=Ekvilibro de Turgruo
Tower Crane Mast=Masto de Turgruo
Tower Crane Arm=Brako de Turgruo
Tower Crane Arm2=Brako2 de Turgruo

View File

@ -249,7 +249,7 @@ function Tube:determine_tube_dirs(pos, preferred_pos, fdir)
if friendly then if friendly then
local v = vector.direction(pos, preferred_pos) local v = vector.direction(pos, preferred_pos)
local dir1 = self:vector_to_dir(v) local dir1 = self:vector_to_dir(v)
local dir2 = Turn180Deg[fdir] local dir2 = fdir < 5 and Turn180Deg[fdir] or fdir
return dir1, dir2, 1 return dir1, dir2, 1
end end
end end

View File

@ -1,7 +1,7 @@
# textdomain: tubelib2 # textdomain: tubelib2
Not connected!=Nicht verbunden! Not connected!=Nicht verbunden!
Paired with @1=Gepaart mit @1 Paired with @1=Gepaart mit @1
Connected to @1=erbunden mit @1 Connected to @1=Verbunden mit @1
Maximum length reached!=Maximale Länge erreicht! Maximum length reached!=Maximale Länge erreicht!
Pairing is missing=Das Pairing fehlt Pairing is missing=Das Pairing fehlt
Connection to a tube is missing!=Eine Verbindung zu einer Röhre fehlt! Connection to a tube is missing!=Eine Verbindung zu einer Röhre fehlt!

View File

@ -0,0 +1,8 @@
# textdomain: tubelib2
Not connected!=未连接!
Paired with @1=与@1配对
Connected to @1=已连接到 @1
Maximum length reached!=已达到最大长度!
Pairing is missing=配对缺失
Connection to a tube is missing!=管道连接缺失!
Pairing is missing (@1)=配对缺失 (@1)

View File

@ -0,0 +1,8 @@
# textdomain: tubelib2
Not connected!=未連接!
Paired with @1=與@1配對
Connected to @1=已連接到 @1
Maximum length reached!=已達到最大長度!
Pairing is missing=配對缺失
Connection to a tube is missing!=管道連接缺失!
Pairing is missing (@1)=配對缺失 (@1)

View File

@ -2,48 +2,59 @@ local S = minetest.get_translator("unified_inventory")
local F = minetest.formspec_escape local F = minetest.formspec_escape
local ui = unified_inventory local ui = unified_inventory
local function is_recipe_craftable(recipe)
-- Ensure the ingedients exist
for _, itemname in pairs(recipe.items) do
local groups = string.find(itemname, "group:")
if groups then
if not ui.get_group_item(string.sub(groups, 8)).item then
return false
end
else
-- Possibly an item
if not minetest.registered_items[itemname]
or minetest.get_item_group(itemname, "not_in_craft_guide") ~= 0 then
return false
end
end
end
return true
end
-- Create detached creative inventory after loading all mods -- Create detached creative inventory after loading all mods
minetest.after(0.01, function() minetest.after(0.01, function()
local rev_aliases = {} local rev_aliases = {}
for source, target in pairs(minetest.registered_aliases) do for original, newname in pairs(minetest.registered_aliases) do
if not rev_aliases[target] then rev_aliases[target] = {} end if not rev_aliases[newname] then
table.insert(rev_aliases[target], source) rev_aliases[newname] = {}
end end
table.insert(rev_aliases[newname], original)
end
-- Filtered item list
ui.items_list = {} ui.items_list = {}
for name, def in pairs(minetest.registered_items) do for name, def in pairs(minetest.registered_items) do
if (not def.groups.not_in_creative_inventory or if ui.is_itemdef_listable(def) then
def.groups.not_in_creative_inventory == 0) and
def.description and def.description ~= "" then
table.insert(ui.items_list, name) table.insert(ui.items_list, name)
-- Alias processing: Find recipes that belong to the current item name
local all_names = rev_aliases[name] or {} local all_names = rev_aliases[name] or {}
table.insert(all_names, name) table.insert(all_names, name)
for _, player_name in ipairs(all_names) do for _, itemname in ipairs(all_names) do
local recipes = minetest.get_all_craft_recipes(player_name) local recipes = minetest.get_all_craft_recipes(itemname)
if recipes then for _, recipe in ipairs(recipes or {}) do
for _, recipe in ipairs(recipes) do if is_recipe_craftable(recipe) then
local unknowns
for _,chk in pairs(recipe.items) do
local groupchk = string.find(chk, "group:")
if (not groupchk and not minetest.registered_items[chk])
or (groupchk and not ui.get_group_item(string.gsub(chk, "group:", "")).item)
or minetest.get_item_group(chk, "not_in_craft_guide") ~= 0 then
unknowns = true
end
end
if not unknowns then
ui.register_craft(recipe) ui.register_craft(recipe)
end end
end end
end end
end end
end end
end
table.sort(ui.items_list) table.sort(ui.items_list)
ui.items_list_size = #ui.items_list ui.items_list_size = #ui.items_list
print("Unified Inventory. Inventory size: "..ui.items_list_size) print("Unified Inventory. Inventory size: "..ui.items_list_size)
-- Analyse dropped items -> custom "digging" recipes
for _, name in ipairs(ui.items_list) do for _, name in ipairs(ui.items_list) do
local def = minetest.registered_items[name] local def = minetest.registered_items[name]
-- Simple drops -- Simple drops
@ -201,8 +212,8 @@ minetest.after(0.01, function()
end end
end) end)
---------------- Home API ----------------
-- load_home
local function load_home() local function load_home()
local input = io.open(ui.home_filename, "r") local input = io.open(ui.home_filename, "r")
if not input then if not input then
@ -219,6 +230,7 @@ local function load_home()
end end
io.close(input) io.close(input)
end end
load_home() load_home()
function ui.set_home(player, pos) function ui.set_home(player, pos)
@ -247,7 +259,8 @@ function ui.go_home(player)
return false return false
end end
-- register_craft ---------------- Crafting API ----------------
function ui.register_craft(options) function ui.register_craft(options)
if not options.output then if not options.output then
return return
@ -270,14 +283,12 @@ function ui.register_craft(options)
end end
end end
local craft_type_defaults = { local craft_type_defaults = {
width = 3, width = 3,
height = 3, height = 3,
uses_crafting_grid = false, uses_crafting_grid = false,
} }
function ui.craft_type_defaults(name, options) function ui.craft_type_defaults(name, options)
if not options.description then if not options.description then
options.description = name options.description = name
@ -288,8 +299,7 @@ end
function ui.register_craft_type(name, options) function ui.register_craft_type(name, options)
ui.registered_craft_types[name] = ui.registered_craft_types[name] = ui.craft_type_defaults(name, options)
ui.craft_type_defaults(name, options)
end end
@ -346,6 +356,8 @@ ui.register_craft_type("digging_chance", {
height = 1, height = 1,
}) })
---------------- GUI registrations ----------------
function ui.register_page(name, def) function ui.register_page(name, def)
ui.pages[name] = def ui.pages[name] = def
end end
@ -361,6 +373,8 @@ function ui.register_button(name, def)
table.insert(ui.buttons, def) table.insert(ui.buttons, def)
end end
---------------- Callback registrations ----------------
function ui.register_on_initialized(callback) function ui.register_on_initialized(callback)
if type(callback) ~= "function" then if type(callback) ~= "function" then
error(("Initialized callback must be a function, %s given."):format(type(callback))) error(("Initialized callback must be a function, %s given."):format(type(callback)))
@ -375,6 +389,8 @@ function ui.register_on_craft_registered(callback)
table.insert(ui.craft_registered_callbacks, callback) table.insert(ui.craft_registered_callbacks, callback)
end end
---------------- List getters ----------------
function ui.get_recipe_list(output) function ui.get_recipe_list(output)
return ui.crafts_for.recipe[output] return ui.crafts_for.recipe[output]
end end
@ -387,11 +403,15 @@ function ui.get_registered_outputs()
return outputs return outputs
end end
---------------- Player utilities ----------------
function ui.is_creative(playername) function ui.is_creative(playername)
return minetest.check_player_privs(playername, {creative=true}) return minetest.check_player_privs(playername, {creative=true})
or minetest.settings:get_bool("creative_mode") or minetest.settings:get_bool("creative_mode")
end end
---------------- Formspec helpers ----------------
function ui.single_slot(xpos, ypos, bright) function ui.single_slot(xpos, ypos, bright)
return string.format("background9[%f,%f;%f,%f;ui_single_slot%s.png;false;16]", return string.format("background9[%f,%f;%f,%f;ui_single_slot%s.png;false;16]",
xpos, ypos, ui.imgscale, ui.imgscale, (bright and "_bright" or "") ) xpos, ypos, ui.imgscale, ui.imgscale, (bright and "_bright" or "") )

View File

@ -96,6 +96,9 @@ function unified_inventory.register_category(category_name, config)
end end
update_category_list() update_category_list()
end end
-- TODO: Mark these for removal. They are pretty much useless
function unified_inventory.set_category_symbol(category_name, symbol) function unified_inventory.set_category_symbol(category_name, symbol)
ensure_category_exists(category_name) ensure_category_exists(category_name)
unified_inventory.registered_categories[category_name].symbol = symbol unified_inventory.registered_categories[category_name].symbol = symbol

View File

@ -1,7 +1,8 @@
unified_inventory API unified_inventory API
===================== =====================
This file provides information about the API of unified_inventory. This file provides information about the API of unified_inventory
and can be viewed in Markdown readers.
API revisions within unified_inventory can be checked using: API revisions within unified_inventory can be checked using:
@ -163,68 +164,57 @@ Register a non-standard craft recipe:
Categories Categories
---------- ----------
Register a new category: * `unified_inventory.register_category(name, def)`
The config table (second argument) is optional, and all its members are optional * Registers a new category
See the unified_inventory.set_category_* functions for more details on the members of the config table * `name` (string): internal category name
* `def` (optional, table): also its fields are optional
unified_inventory.register_category("category_name", { unified_inventory.register_category("category_name", {
symbol = "mod_name:item_name" or "texture.png", symbol = source,
-- ^ Can be in the format "mod_name:item_name" or "texture.png",
label = "Human Readable Label", label = "Human Readable Label",
index = 5, index = 5,
-- ^ Categories are sorted by index. Lower numbers appear before higher ones.
-- By default, the name is translated to a number: AA -> 0.0101, ZZ -> 0.2626
--- Predefined category indices: "all" = -2, "uncategorized" = -1
items = { items = {
"mod_name:item_name", "mod_name:item_name",
"another_mod:different_item" "another_mod:different_item"
} }
-- ^ List of items within this category
}) })
* `unified_inventory.remove_category(name)`
* Removes an entire category
Add / override the symbol for a category: Modifier functions (to be removed)
The category does not need to exist first
The symbol can be an item name or a texture image
If unset this will default to "default:stick"
unified_inventory.set_category_symbol("category_name", "mod_name:item_name" or "texture.png") * `unified_inventory.set_category_symbol(name, source)`
* Changes the symbol of the category. The category does not need to exist yet.
* `name` (string): internal category name
* `source` (string, optional): `"mod_name:item_name"` or `"texture.png"`.
Defaults to `"default:stick"` if not specified.
* `unified_inventory.set_category_label(name, label)`
* Changes the human readable label of the category.
* `name` (string): internal category name
* `label` (string): human readable label. Defaults to the category name.
* `unified_inventory.set_category_index(name, index)`
* Changes the sorting index of the category.
* `name` (string): internal category name
* `index` (numeric): any real number
Add / override the human readable label for a category: Item management
If unset this will default to the category name
unified_inventory.set_category_label("category_name", "Human Readable Label") * ` unified_inventory.add_category_item(name, itemname)`
* Adds a single item to the category
* `itemname` (string): self-explanatory
* `unified_inventory.add_category_items(name, { itemname1, itemname2, ... }`
* Same as above but with multiple items
* `unified_inventory.remove_category_item(name, itemname)`
* Removes an item from the category
* `unified_inventory.find_category(itemname)`
* Looks up the first category containing this item
* Returns: category name (string) or nil
* `unified_inventory.find_categories(itemname)`
* Looks up the item name within all registered categories
* Returns: array of category names (table)
Add / override the sorting index of the category:
Must be a number, can also be negative (-5) or fractional (2.345)
This determines the position the category appears in the list of categories
The "all" meta-category has index -2, the "misc"/"uncategorized" meta-category has index -1, use a negative number smaller than these to make a category appear before these in the list
By default categories are sorted alphabetically with an index between 0.0101(AA) and 0.2626(ZZ)
unified_inventory.set_category_index("category_name", 5)
Add a single item to a category:
unified_inventory.add_category_item("category_name", "mod_name:item_name")
Add multiple items to a category:
unified_inventory.add_category_items("category_name", {
"mod_name:item_name",
"another_mod:different_item"
})
Remove an item from a category:
unified_inventory.remove_category_item("category_name", "mod_name:item_name")
Remove a category entirely:
unified_inventory.remove_category("category_name")
Finding existing items in categories:
This will find the first category an item exists in
It should be used for checking if an item is catgorised
Returns "category_name" or nil
unified_inventory.find_category("mod_name:item_name")
This will find all the categories an item exists in
Returns a number indexed table (list) of category names
unified_inventory.find_categories("mod_name:item_name")

View File

@ -52,6 +52,8 @@ unified_inventory = {
list_img_offset = 0.13, list_img_offset = 0.13,
standard_background = "bgcolor[#0000]background9[0,0;1,1;ui_formbg_9_sliced.png;true;16]", standard_background = "bgcolor[#0000]background9[0,0;1,1;ui_formbg_9_sliced.png;true;16]",
hide_disabled_buttons = minetest.settings:get_bool("unified_inventory_hide_disabled_buttons", false),
version = 4 version = 4
} }

View File

@ -52,11 +52,13 @@ local function formspec_tab_buttons(player, formspec, style)
local filtered_inv_buttons = {} local filtered_inv_buttons = {}
for i, def in pairs(ui.buttons) do for _, def in pairs(ui.buttons) do
if not (style.is_lite_mode and def.hide_lite) then if not (style.is_lite_mode and def.hide_lite) then
if def.condition == nil or def.condition(player) or not ui.hide_disabled_buttons then
table.insert(filtered_inv_buttons, def) table.insert(filtered_inv_buttons, def)
end end
end end
end
local needs_scrollbar = #filtered_inv_buttons > style.main_button_cols * style.main_button_rows local needs_scrollbar = #filtered_inv_buttons > style.main_button_cols * style.main_button_rows
@ -71,13 +73,14 @@ local function formspec_tab_buttons(player, formspec, style)
local pos_y = math.floor((i - 1) / style.main_button_cols) * style.btn_spc local pos_y = math.floor((i - 1) / style.main_button_cols) * style.btn_spc
if def.type == "image" then if def.type == "image" then
if (def.condition == nil or def.condition(player) == true) then if (def.condition == nil or def.condition(player)) then
formspec[n] = string.format("image_button[%g,%g;%g,%g;%s;%s;]", formspec[n] = string.format("image_button[%g,%g;%g,%g;%s;%s;]",
pos_x, pos_y, style.btn_size, style.btn_size, pos_x, pos_y, style.btn_size, style.btn_size,
F(def.image), F(def.image),
F(def.name)) F(def.name))
formspec[n+1] = "tooltip["..F(def.name)..";"..(def.tooltip or "").."]" formspec[n+1] = "tooltip["..F(def.name)..";"..(def.tooltip or "").."]"
n = n+2 n = n+2
else else
formspec[n] = string.format("image[%g,%g;%g,%g;%s^[colorize:#808080:alpha]", formspec[n] = string.format("image[%g,%g;%g,%g;%s^[colorize:#808080:alpha]",
pos_x, pos_y, style.btn_size, style.btn_size, pos_x, pos_y, style.btn_size, style.btn_size,
@ -88,9 +91,10 @@ local function formspec_tab_buttons(player, formspec, style)
end end
formspec[n] = "scroll_container_end[]" formspec[n] = "scroll_container_end[]"
if needs_scrollbar then if needs_scrollbar then
local total_rows = math.ceil(#filtered_inv_buttons / style.main_button_cols)
formspec[n+1] = ("scrollbaroptions[max=%i;arrows=hide]"):format( formspec[n+1] = ("scrollbaroptions[max=%i;arrows=hide]"):format(
-- This calculation is not 100% accurate but "good enough" -- This calculation is not 100% accurate but "good enough"
math.ceil((#filtered_inv_buttons - 1) / style.main_button_cols) * style.btn_spc * 5 (total_rows - style.main_button_rows) * style.btn_spc * 10
) )
formspec[n+2] = ("scrollbar[%g,%g;0.4,%g;vertical;tabbtnscroll;0]"):format( formspec[n+2] = ("scrollbar[%g,%g;0.4,%g;vertical;tabbtnscroll;0]"):format(
style.main_button_x + style.main_button_cols * style.btn_spc - 0.1, -- x pos style.main_button_x + style.main_button_cols * style.btn_spc - 0.1, -- x pos
@ -329,7 +333,7 @@ function ui.set_inventory_formspec(player, page)
end end
end end
local function valid_def(def) function ui.is_itemdef_listable(def)
return (not def.groups.not_in_creative_inventory return (not def.groups.not_in_creative_inventory
or def.groups.not_in_creative_inventory == 0) or def.groups.not_in_creative_inventory == 0)
and def.description and def.description
@ -342,9 +346,11 @@ function ui.apply_filter(player, filter, search_dir)
return false return false
end end
local player_name = player:get_player_name() local player_name = player:get_player_name()
local lfilter = string.lower(filter) local lfilter = string.lower(filter)
local ffilter local ffilter
if lfilter:sub(1, 6) == "group:" then if lfilter:sub(1, 6) == "group:" then
-- Group filter: all groups of the item must match
local groups = lfilter:sub(7):split(",") local groups = lfilter:sub(7):split(",")
ffilter = function(name, def) ffilter = function(name, def)
for _, group in ipairs(groups) do for _, group in ipairs(groups) do
@ -356,6 +362,7 @@ function ui.apply_filter(player, filter, search_dir)
return true return true
end end
else else
-- Name filter: fuzzy match item names and descriptions
local player_info = minetest.get_player_information(player_name) local player_info = minetest.get_player_information(player_name)
local lang = player_info and player_info.lang_code or "" local lang = player_info and player_info.lang_code or ""
@ -368,35 +375,41 @@ function ui.apply_filter(player, filter, search_dir)
or llocaldesc and string.find(llocaldesc, lfilter, 1, true) or llocaldesc and string.find(llocaldesc, lfilter, 1, true)
end end
end end
ui.filtered_items_list[player_name]={}
local is_itemdef_listable = ui.is_itemdef_listable
local filtered_items = {}
local category = ui.current_category[player_name] or 'all' local category = ui.current_category[player_name] or 'all'
if category == 'all' then if category == 'all' then
for name, def in pairs(minetest.registered_items) do for name, def in pairs(minetest.registered_items) do
if valid_def(def) if is_itemdef_listable(def)
and ffilter(name, def) then and ffilter(name, def) then
table.insert(ui.filtered_items_list[player_name], name) table.insert(filtered_items, name)
end end
end end
elseif category == 'uncategorized' then elseif category == 'uncategorized' then
for name, def in pairs(minetest.registered_items) do for name, def in pairs(minetest.registered_items) do
if (not ui.find_category(name)) if is_itemdef_listable(def)
and valid_def(def) and not ui.find_category(name)
and ffilter(name, def) then and ffilter(name, def) then
table.insert(ui.filtered_items_list[player_name], name) table.insert(filtered_items, name)
end end
end end
else else
for name,exists in pairs(ui.registered_category_items[category]) do -- Any other category is selected
for name, exists in pairs(ui.registered_category_items[category]) do
local def = minetest.registered_items[name] local def = minetest.registered_items[name]
if exists and def if exists and def
and valid_def(def) and is_itemdef_listable(def)
and ffilter(name, def) then and ffilter(name, def) then
table.insert(ui.filtered_items_list[player_name], name) table.insert(filtered_items, name)
end end
end end
end end
table.sort(ui.filtered_items_list[player_name]) table.sort(filtered_items)
ui.filtered_items_list_size[player_name] = #ui.filtered_items_list[player_name]
ui.filtered_items_list_size[player_name] = #filtered_items
ui.filtered_items_list[player_name] = filtered_items
ui.current_index[player_name] = 1 ui.current_index[player_name] = 1
ui.activefilter[player_name] = filter ui.activefilter[player_name] = filter
ui.active_search_direction[player_name] = search_dir ui.active_search_direction[player_name] = search_dir

View File

@ -1,6 +1,6 @@
name = unified_inventory name = unified_inventory
optional_depends = default, creative, sfinv, datastorage, farming optional_depends = default, creative, sfinv, datastorage
description = """ description = """
Unified Inventory replaces the default survival and creative inventory. Unified Inventory replaces the default survival and creative inventory.
It adds a nicer interface and a number of features, such as a crafting guide. It adds a nicer interface and a number of features, such as a crafting guide.

View File

@ -10,5 +10,8 @@ unified_inventory_bags (Enable bags) bool true
#and the give privilege. #and the give privilege.
unified_inventory_trash (Enable trash) bool true unified_inventory_trash (Enable trash) bool true
#If enabled, disabled buttons will be hidden instead of grayed out.
unified_inventory_hide_disabled_buttons (Hide disabled buttons) bool false
unified_inventory_automatic_categorization (Items automatically added to categories) bool true unified_inventory_automatic_categorization (Items automatically added to categories) bool true