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
local door_pos1 = hyperloop.new_pos(floor_pos, facedir, "1B", 0)
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 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").."]" ..
"button_exit[2,3;2,1;exit;"..S("Save").."]"
meta:set_string("formspec", formspec)
meta:set_string("owner", placer:get_player_name())
-- add upper part of the car
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
connection_walk(pos, outdir, Flip[outdir], node, tlib2, function(pos, indir, node)
local ndef = net_def2(pos, node.name, netw_type)
if ndef then
if ndef and ndef.ntype then
local ntype = ndef.ntype
if not netw[ntype] then netw[ntype] = {} end
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
break
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)
elseif s:find('%[string ".-"%]') then
local line, err = s:match('^%[string ".-"%]:(%d+): (.+)$')
@ -134,6 +134,24 @@ local function compile(pos, text, label, err_clbk)
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
-------------------------------------------------------------------------------
@ -148,7 +166,7 @@ function safer_lua.init(pos, init, loop, environ, err_clbk)
env.S = {}
env.S = map(env.S, environ)
setfenv(code, env)
local res, err = xpcall(code, debug.traceback)
local res, err = xpcall(runtime_delimiter, error_handler, code)
if not res then
err_clbk(pos, format_error(err, "init"))
else
@ -171,7 +189,7 @@ function safer_lua.run_loop(pos, elapsed, code, err_clbk)
env.event = false
env.ticks = env.ticks + 1
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
err_clbk(pos, "Error: Data memory limit exceeded")
return false
@ -188,7 +206,7 @@ end
-------------------------------------------------------------------------------
local function thread(pos, code, err_clbk)
while true do
local res, err = xpcall(code, debug.traceback)
local res, err = xpcall(runtime_delimiter, error_handler, code)
if not res then
err_clbk(pos, format_error(err, "loop"))
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
### 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
@ -19,3 +19,4 @@ none
- 2018-06-24 v0.01 * first draft
- 2020-03-14 v1.00 * extracted from TechPack and released
- 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
minetest.swap_node(pos4, mem.stored_node or {name = "air"})
end
minetest.swap_node(pos3, {name = "air"})
minetest.remove_node(pos)
else
minetest.swap_node(pos, mem.stored_node or {name = "air"})
@ -319,7 +320,7 @@ signs_bot.register_botcommand("stop", {
if mem.capa then
mem.capa = mem.capa + 2
end
return signs_bot.DONE
return signs_bot.BUSY
end,
})

View File

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

View File

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

View File

@ -109,17 +109,19 @@ techage.register_node({"default:furnace", "default:furnace_active"}, {
local inv = meta:get_inventory()
return techage.get_items(pos, inv, "dst", num)
end,
on_push_item = function(pos, side, stack)
on_push_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
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)
else
return techage.put_items(inv, "src", stack)
end
end,
on_unpull_item = function(pos, side, stack)
on_unpull_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
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: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_cotton 3", output="basic_materials:oil_extract"}, true)
end

View File

@ -26,7 +26,7 @@ local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm
local S = techage.S
local CYCLE_TIME = 3
local CYCLE_TIME = 4
local STANDBY_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
mark_area(pos1, pos2, owner)
M(pos):set_string("formspec", formspec(CRD(pos).State, pos, nvm))
coroutine.yield()
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)
if topic == "count" then
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
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
local nvm = techage.get_nvm(pos)
return inv_state(nvm)
@ -627,10 +627,10 @@ techage.register_node({"techage:ta4_chest"}, {
on_beduino_request_data = function(pos, src, topic, payload)
if topic == 140 and payload[1] == 1 then -- Inventory Item Count
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
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
local nvm = techage.get_nvm(pos)
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 P = minetest.string_to_pos
--local M = minetest.get_meta
local has_mesecons = minetest.global_exists("mesecon")
local NodeInfoCache = {}
local NumbersToBeRecycled = {}
@ -171,7 +172,7 @@ end)
techage.dug_node = {}
minetest.register_on_dignode(function(pos, oldnode, digger)
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
end)
@ -317,6 +318,13 @@ function techage.register_node(names, node_definition)
if node_definition.on_node_load then
register_lbm(names[1], names)
end
-- register mvps stopper
if has_mesecons then
for _, name in ipairs(names) do
mesecon.register_mvps_stopper(name)
end
end
end
-------------------------------------------------------------------
@ -331,6 +339,19 @@ function techage.not_protected(number, placer_name, clicker_name)
return false
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.
-- Returns true if number(s) is/are valid, point to real nodes and
-- and the nodes are not protected for the given player_name.

View File

@ -3,7 +3,7 @@
TechAge
=======
Copyright (C) 2020-2021 Joachim Stolberg
Copyright (C) 2020-2022 Joachim Stolberg
AGPL v3
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}
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
-------------------------------------------------------------------------------
@ -310,41 +400,15 @@ end
local function entity_to_node(pos, obj)
local self = obj:get_luaentity()
if self then
local name = self.item_name or "air"
local param2 = self.param2 or 0
local metadata = self.metadata or {}
if self and self.item then
detach_objects(pos, self)
if self.base_pos then
local nvm = techage.get_nvm(self.base_pos)
nvm.running = nil
end
monitoring_del_entity(self.item)
minetest.after(0.1, obj.remove, obj)
local node = minetest.get_node(pos)
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
set_node(self.item)
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 node, metadata
@ -356,7 +420,7 @@ local function node_to_entity(start_pos)
meta:set_string("ta_block_locked", "true")
elseif not meta:contains("ta_block_locked") then
-- Block with other metadata
node = minetest.get_node(start_pos)
node = techage.get_node_lvm(start_pos)
metadata = meta:to_table()
minetest.after(0.1, minetest.remove_node, start_pos)
else
@ -371,9 +435,15 @@ local function node_to_entity(start_pos)
obj:set_armor_groups({immortal=1})
-- To be able to revert to node
self.item_name = node.name
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
self.players = {}
@ -436,7 +506,6 @@ local function handover_to(obj, self, pos1)
end
local pos2 = next_path_pos(pos1, self.lpath, 1)
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
local nvm = techage.get_nvm(info.pos)
nvm.lpos1 = nvm.lpos1 or {}
@ -458,7 +527,7 @@ minetest.register_entity("techage:move_item", {
initial_properties = {
pointable = true,
makes_footstep_sound = true,
static_save = true,
static_save = false,
collide_with_objects = false,
physical = false,
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},
},
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)
local stop_obj = function(obj, self)
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})
self.dest_pos = nil
self.old_dist = nil
self.ttl = 2
return dest_pos
end
@ -571,19 +596,13 @@ minetest.register_entity("techage:move_item", {
end
end
elseif self.ttl then
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
monitoring_trigger_entity(self.item)
end
end,
})
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
return true
end
@ -594,20 +613,19 @@ local function is_valid_dest(pos)
end
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]
return not techage.is_air_like(node.name) and techage.can_dig_node(node.name, ndef)
end
local function move_node(pos, pos1_idx, start_pos, lpath, max_speed, height, move2to1, handover, cpos)
local pos2 = next_path_pos(start_pos, lpath, 1)
--print("move_node", P2S(pos), P2S(start_pos), lpath, max_speed, height, move2to1, P2S(pos2))
-- optional for non-player objects
local yoffs = M(pos):get_float("offset")
if pos2 then
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
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.lpath = lpath
self.max_speed = max_speed
self.start_pos = start_pos
self.base_pos = pos
self.move2to1 = move2to1
self.handover = handover
self.yoffs = yoffs
--print("move_node", P2S(start_pos), P2S(pos2), P2S(dir), P2S(pos))
move_entity(obj, pos2, dir)
return true
else
return false
end
end
end
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")
techage.counting_add(owner, #nvm.lpos1 * #lpath)
techage.counting_add(owner, #lpath, #nvm.lpos1 * #lpath)
for idx = 1, #nvm.lpos1 do
local pos1 = nvm.lpos1[idx]
local pos2 = nvm.lpos2[idx]
--print("move_nodes", idx, P2S(pos1), P2S(pos2))
if move2to1 then
pos1, pos2 = pos2, pos1
end
--print("move_nodes", P2S(pos1), P2S(pos2))
if not minetest.is_protected(pos1, owner) and not minetest.is_protected(pos2, owner) then
if is_simple_node(pos1) and is_valid_dest(pos2) then
move_node(pos, idx, pos1, 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
if not is_simple_node(pos1) then
meta:set_string("status", S("No valid node at the start position"))
else
meta:set_string("status", S("No valid destination position"))
end
return false
end
else
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
end
end
meta:set_string("status", S("Running"))
return true
end
-- Move nodes from lpos1 by the given x/y/z 'line'
local function move_nodes2(pos, meta, lpos1, line, max_speed, height)
--print("move_nodes2", dump(lpos1), dump(line), max_speed, height)
local owner = meta:get_string("owner")
techage.counting_add(owner, #lpos1)
@ -691,6 +712,7 @@ local function move_nodes2(pos, meta, lpos1, line, max_speed, height)
else
meta:set_string("status", S("No valid destination position"))
end
return false, lpos1
end
else
if minetest.is_protected(pos1, owner) then
@ -702,7 +724,7 @@ local function move_nodes2(pos, meta, lpos1, line, max_speed, height)
end
end
meta:set_string("status", "")
meta:set_string("status", S("Running"))
return true, lpos2
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 handover
if err then return false end
if err or nvm.running then return false end
height = techage.in_range(height, 0, 1)
max_speed = techage.in_range(max_speed, MIN_SPEED, MAX_SPEED)
@ -732,7 +754,9 @@ function flylib.move_to_other_pos(pos, move2to1)
else
handover = meta:contains("handoverB") and meta:get_string("handoverB") or nil
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
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 resp
resp, nvm.lastpos = move_nodes2(pos, meta, nvm.lastpos or nvm.lpos1, line, max_speed, height)
return resp
if nvm.running then return false end
nvm.running, nvm.lastpos = move_nodes2(pos, meta, nvm.lastpos or nvm.lpos1, line, max_speed, height)
return nvm.running
end
function flylib.reset_move(pos)
@ -751,11 +777,17 @@ function flylib.reset_move(pos)
local nvm = techage.get_nvm(pos)
local height = techage.in_range(meta:contains("height") and meta:get_float("height") or 1, 0, 1)
local max_speed = meta:contains("max_speed") and meta:get_int("max_speed") or MAX_SPEED
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 resp
resp, nvm.lastpos = move_nodes2(pos, meta, nvm.lastpos or nvm.lpos1, move, max_speed, height)
return resp
nvm.running, nvm.lastpos = move_nodes2(pos, meta, nvm.lastpos or nvm.lpos1, move, max_speed, height)
return nvm.running
end
return false
end
-- rot is one of "l", "r", "2l", "2r"
@ -842,4 +874,4 @@ minetest.register_on_dieplayer(function(player)
end
end)
return flylib
techage.flylib = flylib

View File

@ -72,8 +72,11 @@ end
local api = {}
function api.store_mapblock_data(key, mapblock_data)
if use_marshal then
set_block(key, marshal.encode(mapblock_data))
if use_marshal and mapblock_data then
local data = marshal.encode(mapblock_data)
if data then
set_block(key, data)
end
else
set_block(key, minetest.serialize(mapblock_data))
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",
ta5_flycontroller = "techage:ta5_flycontroller",
ta5_aichip = "techage:ta5_aichip",
ta5_aichip2 = "techage:ta5_aichip2",
ta5_tele_pipe = "techage:ta5_tele_pipe",
ta5_tele_tube = "techage:ta5_tele_tube",
ta5_chest = "techage:ta5_hl_chest",

View File

@ -254,6 +254,7 @@ techage.manual_DE.aTitel = {
"2,Weitere TA5 Blöcke/Items",
"3,TA5 Container (geplant)",
"3,TA5 KI Chip / TA5 AI Chip",
"3,TA5 KI Chip II / TA5 AI Chip II",
}
techage.manual_DE.aText = {
@ -360,13 +361,20 @@ techage.manual_DE.aText = {
"\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"..
"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"..
"\n"..
" - Baue eine 5x5 große Fläche aus Erde (dirt)\n"..
" - Platziere in die Mitte einen Anzünder (lighter)\n"..
" - Baue aus Holz (wood) einen 3x3x3 großen Würfel darüber\n"..
" - Überdecke alles mit einer Schicht Erde zu einem 5x5x5 großen Würfel\n"..
" - Lasse ein Loch zum Anzünder\n"..
" - Platziere rund um den Anzünder 7 Holz (wood)\\, aber lasse ein Loch zum Anzünder frei\n"..
" - Baue weitere 2 Schichten Holz darüber\\, so dass ein 3x3x3 großen Holzwürfel entsteht\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"..
" - 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"..
@ -1479,6 +1487,8 @@ techage.manual_DE.aText = {
"\n"..
"Der Elektrolyseur besitzt ein Schraubenschlüssel-Menü zur Einstellung der Stromaufnahme und des Abschaltpunkts.\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",
"Die Brennstoffzelle wandelt Wasserstoff in Strom um.\n"..
@ -1494,16 +1504,18 @@ techage.manual_DE.aText = {
"\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"..
"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"..
"\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 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 Dosierer\\, welcher über Leitungen mit den Tanks oder Silos sowie dem Einfüllstutzen verbunden werden muss\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"..
"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"..
@ -1524,7 +1536,7 @@ techage.manual_DE.aText = {
"Wie auch bei anderen Maschinen:\n"..
"\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"..
"Der Dosierer benötigt keinen Strom. Alle 10 s wird ein Rezept abgearbeitet.\n"..
"\n"..
@ -1541,7 +1553,7 @@ techage.manual_DE.aText = {
"\n",
"Teil des Chemischen Reaktors. Hier ist auch der Stromanschluss für den Reaktor. Der Reaktor benötigt 8 ku Strom.\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",
@ -1722,6 +1734,11 @@ techage.manual_DE.aText = {
" - 'b2a' Bewege Block von B nach A\n"..
" - 'move' Bewege Block auf die andere Seite\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"..
"\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",
"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 = {
@ -2402,6 +2423,7 @@ techage.manual_DE.aItemName = {
"",
"",
"ta5_aichip",
"ta5_aichip2",
}
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",
"3,TA5 Container (planned)",
"3,TA5 AI Chip",
"3,TA5 AI Chip II",
}
techage.manual_EN.aText = {
@ -368,13 +369,19 @@ techage.manual_EN.aText = {
"\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"..
"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"..
"\n"..
" - Build a 5x5 area of dirt\n"..
" - Place a lighter in the middle\n"..
" - Build a 3x3x3 cube above it out of wood\n"..
" - Cover everything with a layer of dirt to form a 5x5x5 cube\n"..
" - Leave a hole to the lighter\n"..
" - Build a 5x5 area of dirt\n"..
" - Place 7 wood around the lighter but leave a hole to the lighter\n"..
" - Build another 2 layers of wood on top\\, making a 3x3x3 wooden cube\n"..
" - Cover everything with a layer of dirt into a 5x5x5 cube\\, but keep the hole to the lighter open\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"..
" - Only open the charcoal burner when the smoke has disappeared (approx. 20 min)\n"..
@ -1486,6 +1493,8 @@ techage.manual_EN.aText = {
"\n"..
"The electrolyzer has a wrench menu for setting the current consumption and the switch-off point.\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",
"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 plan on the left shows only one possible variant\\, since the arrangement of the silos and tanks depends on the recipe.\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"..
"\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 vessel that has to be placed on the reactor stand\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"..
"\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"..
"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"..
@ -1531,7 +1542,7 @@ techage.manual_EN.aText = {
"As with other machines:\n"..
"\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"..
"The doser does not need any electricity. A recipe is processed every 10 s.\n"..
"\n"..
@ -1728,6 +1739,11 @@ techage.manual_EN.aText = {
" - 'b2a' Move block from B to A.\n"..
" - 'move' Move block to the other side\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"..
"\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",
"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 = {
@ -2407,6 +2427,7 @@ techage.manual_EN.aItemName = {
"",
"",
"ta5_aichip",
"ta5_aichip2",
}
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 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 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"] = {
{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, 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, STAND, PIPEH, PIPEH, SILO},
{false, TANK3, PIPEH, PIPEH, DOSER, PN270, false, RBASE, PIPEH, PIPEH, TANK3},
{false, SILO, PIPEH, PIPEH, PIPEH, PN180, false, false, false, false, false},
{false, false, false, false, PIPEV, false, false, STAND, PIPEH, PIPEH, SILO4},
{false, TANK4, PIPEH, PIPEH, DOSER, PN270, false, RBASE, PIPEH, PIPEH, TANK4},
{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",
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(),
drop = "",
})

View File

@ -200,7 +200,7 @@ local tool_config = {
choices = "0%,20%,40%,60%,80%,98%",
name = "turnoff",
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%",
},
}

View File

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

View File

@ -14,9 +14,29 @@
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 pos = techage.dug_node[player_name]
if not pos then return end
techage.dug_node[player_name] = nil
if minetest.is_protected(pos, player_name) then
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 inv = minetest.get_inventory({type="player", name=player_name})
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)
else
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
if node.name == "techage:basalt_stone" or node.name == "techage:basalt_cobble" then
node.name = "techage:basalt_gravel"
if Stone2Gravel[node.name] then
node.name = Stone2Gravel[node.name]
else
node.name = "default:gravel"
end
@ -61,7 +82,9 @@ minetest.register_tool("techage:hammer_stone", {
},
sound = {breaks = "default_tool_breaks"},
after_use = function(itemstack, user, node, digparams)
if is_real_player(user) then
minetest.after(0.01, handler, user:get_player_name(), node)
end
itemstack:add_wear(digparams.wear)
return itemstack
end,
@ -80,7 +103,9 @@ minetest.register_tool("techage:hammer_bronze", {
},
sound = {breaks = "default_tool_breaks"},
after_use = function(itemstack, user, node, digparams)
if is_real_player(user) then
minetest.after(0.01, handler, user:get_player_name(), node)
end
itemstack:add_wear(digparams.wear)
return itemstack
end,
@ -99,7 +124,9 @@ minetest.register_tool("techage:hammer_steel", {
},
sound = {breaks = "default_tool_breaks"},
after_use = function(itemstack, user, node, digparams)
if is_real_player(user) then
minetest.after(0.01, handler, user:get_player_name(), node)
end
itemstack:add_wear(digparams.wear)
return itemstack
end,
@ -118,7 +145,9 @@ minetest.register_tool("techage:hammer_mese", {
},
sound = {breaks = "default_tool_breaks"},
after_use = function(itemstack, user, node, digparams)
if is_real_player(user) then
minetest.after(0.01, handler, user:get_player_name(), node)
end
itemstack:add_wear(digparams.wear)
return itemstack
end,
@ -137,7 +166,9 @@ minetest.register_tool("techage:hammer_diamond", {
},
sound = {breaks = "default_tool_breaks"},
after_use = function(itemstack, user, node, digparams)
if is_real_player(user) then
minetest.after(0.01, handler, user:get_player_name(), node)
end
itemstack:add_wear(digparams.wear)
return itemstack
end,

View File

@ -10,7 +10,8 @@
Cracking breaks long chains of hydrocarbons into short chains using a catalyst.
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
hydrocarbons into long ones.
@ -45,6 +46,22 @@ techage.recipes.add("ta4_doser", {
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
techage.recipes.add("ta4_doser", {
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: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({
output = "techage:red_stone",
output = "techage:red_stone 3",
recipe = {
"techage:canister_redmud",
"default:sand",
"default:sand",
"default:sand",
},
waste = "techage:ta3_canister_empty",
time = 4,
time = 10,
})

View File

@ -19,6 +19,18 @@ minetest.register_craftitem("techage:ta4_silicon_wafer", {
inventory_image = "techage_silicon_wafer.png",
})
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",
recipe = {
@ -29,3 +41,5 @@ techage.furnace.register_recipe({
},
time = 6,
})
end

View File

@ -60,7 +60,7 @@ local State4 = techage.NodeStates:new({
-- Function returns the number of pumped 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)
if taken > 0 then
if taken > 0 and name then
local leftover = liquid.put(pos, Pipe, outdir, name, taken, mem.dbg_cycles > 0)
if leftover and leftover > 0 then
-- air needs no tank

View File

@ -128,9 +128,15 @@ TA4 Button/Switch=TA4 Schalter/Taster
Access=Zugriff
Button protection=Tastenschutz
Type=Typ
### button.lua ###
### button_2x.lua ###
### button_4x.lua ###
### player_detector.lua ###
Command=Kommando
Number=Nummer
Type=Typ
### button.lua ###
### cart_detector.lua ###
@ -163,10 +169,15 @@ TA4 2x Button=TA4 2x Taster
### button_4x.lua ###
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
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_4x.lua ###
### signallamp_2x.lua ###
@ -387,7 +398,7 @@ Power=Strom
Configurable value@nfor the current limit=Konfigurierbarer Wert@nfür die Strombegrenzung
Current limitation=Strombegrenzung
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 power consumption [ku]=maximale Stromaufnahme
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 !!
No valid destination position=Keine gültige Zielposition
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
Stopped=Gestoppt
### fly_lib.lua ###
### flycontroller.lua ###
@ -959,6 +972,10 @@ Plastic Granules=Plastikgranulat
### 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
### 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
@n=@n
Commands=Kommandos
Cycle time=Zykluszeit
Example:@n=Beispiel:
Invalid command!=Ungültiges Kommando!
Start=Start
Stop=Stopp
Syntax:@n=Syntax:@n
TA4 Sequencer=TA4 Sequenzer
Timer cycle time (default: 100 ms)=Zykluszeit (normal: 100 ms)
running=läuft
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:=
Button or switch=
Change the node name (infotext)=
Change the block name (infotext)=
Command to be sent=
Destination block number(s)=
Infotext=
@ -128,9 +128,15 @@ TA4 Button/Switch=
Access=
Button protection=
Type=
### button.lua ###
### button_2x.lua ###
### button_4x.lua ###
### player_detector.lua ###
Command=
Number=
Type=
### button.lua ###
### cart_detector.lua ###
@ -163,10 +169,15 @@ TA4 2x Button=
### button_4x.lua ###
Command to be sent (ignored for switches)=
Destination block number=
Label for the button=
Momentary button or on/off switch=
### button_2x.lua ###
### button_4x.lua ###
### player_detector.lua ###
Destination block number=
### button_2x.lua ###
### button_4x.lua ###
### signallamp_2x.lua ###
@ -387,7 +398,7 @@ Power=
Configurable value@nfor the current limit=
Current limitation=
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 power consumption [ku]=
Storage full=
@ -453,7 +464,9 @@ Destination position is protected=
Error: Max. length of the flight route exceeded by @1 blocks !!=
No valid destination position=
No valid node at the start position=
Running=
Start position is protected=
Stopped=
### fly_lib.lua ###
### flycontroller.lua ###
@ -959,6 +972,10 @@ Plastic Granules=
### player_detector.lua ###
Command to send when player is detected=
Command to send when player moves away=
Radius=
Search radius=
TA4 Player Detector=
### powder.lua ###
@ -1151,12 +1168,14 @@ stopped=
<num> is a number from 1 to 50000 and is@n=
@n=
Commands=
Cycle time=
Example:@n=
Invalid command!=
Start=
Stop=
Syntax:@n=
TA4 Sequencer=
Timer cycle time (default: 100 ms)=
running=
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
logic.swap_node(pos, "techage:ta4_button_on")
end
local meta = M(pos)
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)
logic.send_cmnd(pos, "command", "on", cycle_time)
minetest.sound_play("techage_button", {
pos = pos,
gain = 0.5,

View File

@ -67,13 +67,16 @@ function techage.logic.send_on(pos, meta, time)
return own_num == numbers
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 numbers = meta:get_string("numbers") or ""
if time and time > 0 then
minetest.get_node_timer(pos):start(time)
end
techage.send_multi(own_num, numbers, cmnd, payload)
techage.send_multi(own_num, numbers, command, payload)
end
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 CYCLE_TIME = 1
local WRENCH_MENU = {
{
type = "dropdown",
choices = "1,2,3,4,5,6,7,8",
name = "radius",
label = S("Radius"),
tooltip = S("Search radius"),
default = "4",
},
{
type = "numbers",
name = "numbers",
label = S("Number"),
tooltip = S("Destination block number"),
default = "",
check = techage.check_numbers,
},
{
type = "ascii",
name = "command1",
label = "On " .. S("Command"),
tooltip = S("Command to send when player is detected"),
default = "on",
},
{
type = "ascii",
name = "command2",
label = "Off " .. S("Command"),
tooltip = S("Command to send when player moves away"),
default = "off",
},
}
local function switch_on(pos, stage)
if logic.swap_node(pos, "techage:ta"..stage.."_playerdetector_on") then
logic.send_on(pos, M(pos))
logic.send_cmnd(pos, "command1", "on")
end
end
local function switch_off(pos, stage)
if logic.swap_node(pos, "techage:ta"..stage.."_playerdetector_off") then
logic.send_off(pos, M(pos))
logic.send_cmnd(pos, "command2", "off")
end
end
@ -36,7 +69,8 @@ local function scan_for_player(pos)
local nvm = techage.get_nvm(pos)
local meta = minetest.get_meta(pos)
local names = meta:get_string("names") or ""
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 names == "" then
nvm.player_name = object:get_player_name()
@ -228,6 +262,7 @@ minetest.register_node("techage:ta4_playerdetector_off", {
techage.del_mem(pos)
end,
ta4_formspec = WRENCH_MENU,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
@ -277,6 +312,7 @@ minetest.register_node("techage:ta4_playerdetector_on", {
techage.del_mem(pos)
end,
ta4_formspec = WRENCH_MENU,
paramtype = "light",
sunlight_propagates = true,
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.
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):
- Baue eine 5x5 große Fläche aus Erde (dirt)
- Platziere in die Mitte einen Anzünder (lighter)
- Baue aus Holz (wood) einen 3x3x3 großen Würfel darüber
- Überdecke alles mit einer Schicht Erde zu einem 5x5x5 großen Würfel
- Lasse ein Loch zum Anzünder
- Platziere rund um den Anzünder 7 Holz (wood), aber lasse ein Loch zum Anzünder frei
- Baue weitere 2 Schichten Holz darüber, so dass ein 3x3x3 großen Holzwürfel entsteht
- Ü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
- 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)

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.
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):
- Build a 5x5 area of dirt
- Place a lighter in the middle
- Build a 3x3x3 cube above it out of wood
- Cover everything with a layer of dirt to form a 5x5x5 cube
- Leave a hole to the lighter
- Build a 5x5 area of dirt
- Place 7 wood around the lighter but leave a hole to the lighter
- Build another 2 layers of wood on top, making a 3x3x3 wooden cube
- Cover everything with a layer of dirt into a 5x5x5 cube, but keep the hole to the lighter open
- 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
- 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.
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]
@ -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.
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:
- 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 eigentlichen Reaktorbehälter, der auf den Reaktorständer 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
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.
@ -310,7 +314,7 @@ Auf allen 4 Seiten der Dosierers können Leitungen für Eingangsmaterialien ange
Wie auch bei anderen Maschinen:
- 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.
@ -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.
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]
@ -583,6 +587,12 @@ Der Move Controller unterstützt folgende techage Kommandos:
- `b2a` Bewege Block von B nach A
- `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:**
- 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.
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]
@ -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 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:
- 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 vessel that has to be placed on the reactor stand
- 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
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.
@ -304,7 +308,7 @@ The recipe can be set and the reactor started via the doser.
As with other machines:
- 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.
@ -574,6 +578,12 @@ The Move Controller supports the following techage commands:
- `b2a` Move block from B to A.
- `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:**
- 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.
[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.
[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 sys
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):
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 |
| "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 |
| "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) |
| "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 |

View File

@ -253,3 +253,4 @@
- [Weitere TA5 Blöcke/Items](./manual_ta5_DE.md#weitere-ta5-blöckeitems)
- [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 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)
- [TA5 Container (planned)](./manual_ta5_EN.md#ta5-container-(planned))
- [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
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!

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 S = techage.S
local MP = minetest.get_modpath("techage")
local flylib = dofile(MP .. "/basis/fly_lib.lua")
local logic = techage.logic
local fly = techage.flylib
local MarkedNodes = {} -- t[player] = {{entity, pos},...}
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)
if node and is_simple_node(node.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
flylib.remove_node(pos)
fly.remove_node(pos)
end
if not techage.is_air_like(node.name) then
return ItemStack(node.name), node.param2
@ -439,7 +438,7 @@ local Doors = {
for _, name in ipairs(Doors) do
for _, postfix in ipairs({"a", "b", "c", "d"}) do
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
@ -453,6 +452,6 @@ local ProtectorDoors = {
for _, name in ipairs(ProtectorDoors) do
for _, postfix in ipairs({"b_1", "b_2", "t_1", "t_2"}) do
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

View File

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

View File

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

View File

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

View File

@ -229,7 +229,7 @@ local function build_crane_up(pos, owner, height, width)
", "..S("Crane size")..": "..height..","..width)
meta:set_string("formspec", formspec(height, width))
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
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
local v = vector.direction(pos, preferred_pos)
local dir1 = self:vector_to_dir(v)
local dir2 = Turn180Deg[fdir]
local dir2 = fdir < 5 and Turn180Deg[fdir] or fdir
return dir1, dir2, 1
end
end

View File

@ -1,7 +1,7 @@
# textdomain: tubelib2
Not connected!=Nicht verbunden!
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!
Pairing is missing=Das Pairing 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 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
minetest.after(0.01, function()
local rev_aliases = {}
for source, target in pairs(minetest.registered_aliases) do
if not rev_aliases[target] then rev_aliases[target] = {} end
table.insert(rev_aliases[target], source)
for original, newname in pairs(minetest.registered_aliases) do
if not rev_aliases[newname] then
rev_aliases[newname] = {}
end
table.insert(rev_aliases[newname], original)
end
-- Filtered item list
ui.items_list = {}
for name, def in pairs(minetest.registered_items) do
if (not def.groups.not_in_creative_inventory or
def.groups.not_in_creative_inventory == 0) and
def.description and def.description ~= "" then
if ui.is_itemdef_listable(def) then
table.insert(ui.items_list, name)
-- Alias processing: Find recipes that belong to the current item name
local all_names = rev_aliases[name] or {}
table.insert(all_names, name)
for _, player_name in ipairs(all_names) do
local recipes = minetest.get_all_craft_recipes(player_name)
if recipes then
for _, recipe in ipairs(recipes) do
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
for _, itemname in ipairs(all_names) do
local recipes = minetest.get_all_craft_recipes(itemname)
for _, recipe in ipairs(recipes or {}) do
if is_recipe_craftable(recipe) then
ui.register_craft(recipe)
end
end
end
end
end
end
table.sort(ui.items_list)
ui.items_list_size = #ui.items_list
print("Unified Inventory. Inventory size: "..ui.items_list_size)
-- Analyse dropped items -> custom "digging" recipes
for _, name in ipairs(ui.items_list) do
local def = minetest.registered_items[name]
-- Simple drops
@ -201,8 +212,8 @@ minetest.after(0.01, function()
end
end)
---------------- Home API ----------------
-- load_home
local function load_home()
local input = io.open(ui.home_filename, "r")
if not input then
@ -219,6 +230,7 @@ local function load_home()
end
io.close(input)
end
load_home()
function ui.set_home(player, pos)
@ -247,7 +259,8 @@ function ui.go_home(player)
return false
end
-- register_craft
---------------- Crafting API ----------------
function ui.register_craft(options)
if not options.output then
return
@ -270,14 +283,12 @@ function ui.register_craft(options)
end
end
local craft_type_defaults = {
width = 3,
height = 3,
uses_crafting_grid = false,
}
function ui.craft_type_defaults(name, options)
if not options.description then
options.description = name
@ -288,8 +299,7 @@ end
function ui.register_craft_type(name, options)
ui.registered_craft_types[name] =
ui.craft_type_defaults(name, options)
ui.registered_craft_types[name] = ui.craft_type_defaults(name, options)
end
@ -346,6 +356,8 @@ ui.register_craft_type("digging_chance", {
height = 1,
})
---------------- GUI registrations ----------------
function ui.register_page(name, def)
ui.pages[name] = def
end
@ -361,6 +373,8 @@ function ui.register_button(name, def)
table.insert(ui.buttons, def)
end
---------------- Callback registrations ----------------
function ui.register_on_initialized(callback)
if type(callback) ~= "function" then
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)
end
---------------- List getters ----------------
function ui.get_recipe_list(output)
return ui.crafts_for.recipe[output]
end
@ -387,11 +403,15 @@ function ui.get_registered_outputs()
return outputs
end
---------------- Player utilities ----------------
function ui.is_creative(playername)
return minetest.check_player_privs(playername, {creative=true})
or minetest.settings:get_bool("creative_mode")
end
---------------- Formspec helpers ----------------
function ui.single_slot(xpos, ypos, bright)
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 "") )

View File

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

View File

@ -1,7 +1,8 @@
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:
@ -163,68 +164,57 @@ Register a non-standard craft recipe:
Categories
----------
Register a new category:
The config table (second argument) is optional, and all its members are optional
See the unified_inventory.set_category_* functions for more details on the members of the config table
* `unified_inventory.register_category(name, def)`
* Registers a new category
* `name` (string): internal category name
* `def` (optional, table): also its fields are optional
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",
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 = {
"mod_name:item_name",
"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:
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"
Modifier functions (to be removed)
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:
If unset this will default to the category name
Item management
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,
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
}

View File

@ -52,11 +52,13 @@ local function formspec_tab_buttons(player, formspec, style)
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 def.condition == nil or def.condition(player) or not ui.hide_disabled_buttons then
table.insert(filtered_inv_buttons, def)
end
end
end
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
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;]",
pos_x, pos_y, style.btn_size, style.btn_size,
F(def.image),
F(def.name))
formspec[n+1] = "tooltip["..F(def.name)..";"..(def.tooltip or "").."]"
n = n+2
else
formspec[n] = string.format("image[%g,%g;%g,%g;%s^[colorize:#808080:alpha]",
pos_x, pos_y, style.btn_size, style.btn_size,
@ -88,9 +91,10 @@ local function formspec_tab_buttons(player, formspec, style)
end
formspec[n] = "scroll_container_end[]"
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(
-- 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(
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
local function valid_def(def)
function ui.is_itemdef_listable(def)
return (not def.groups.not_in_creative_inventory
or def.groups.not_in_creative_inventory == 0)
and def.description
@ -342,9 +346,11 @@ function ui.apply_filter(player, filter, search_dir)
return false
end
local player_name = player:get_player_name()
local lfilter = string.lower(filter)
local ffilter
if lfilter:sub(1, 6) == "group:" then
-- Group filter: all groups of the item must match
local groups = lfilter:sub(7):split(",")
ffilter = function(name, def)
for _, group in ipairs(groups) do
@ -356,6 +362,7 @@ function ui.apply_filter(player, filter, search_dir)
return true
end
else
-- Name filter: fuzzy match item names and descriptions
local player_info = minetest.get_player_information(player_name)
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)
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'
if category == 'all' then
for name, def in pairs(minetest.registered_items) do
if valid_def(def)
if is_itemdef_listable(def)
and ffilter(name, def) then
table.insert(ui.filtered_items_list[player_name], name)
table.insert(filtered_items, name)
end
end
elseif category == 'uncategorized' then
for name, def in pairs(minetest.registered_items) do
if (not ui.find_category(name))
and valid_def(def)
if is_itemdef_listable(def)
and not ui.find_category(name)
and ffilter(name, def) then
table.insert(ui.filtered_items_list[player_name], name)
table.insert(filtered_items, name)
end
end
else
-- Any other category is selected
for name, exists in pairs(ui.registered_category_items[category]) do
local def = minetest.registered_items[name]
if exists and def
and valid_def(def)
and is_itemdef_listable(def)
and ffilter(name, def) then
table.insert(ui.filtered_items_list[player_name], name)
table.insert(filtered_items, name)
end
end
end
table.sort(ui.filtered_items_list[player_name])
ui.filtered_items_list_size[player_name] = #ui.filtered_items_list[player_name]
table.sort(filtered_items)
ui.filtered_items_list_size[player_name] = #filtered_items
ui.filtered_items_list[player_name] = filtered_items
ui.current_index[player_name] = 1
ui.activefilter[player_name] = filter
ui.active_search_direction[player_name] = search_dir

View File

@ -1,6 +1,6 @@
name = unified_inventory
optional_depends = default, creative, sfinv, datastorage, farming
optional_depends = default, creative, sfinv, datastorage
description = """
Unified Inventory replaces the default survival and creative inventory.
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.
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