38 Commits

Author SHA1 Message Date
8322f256c5 Merge branch 'master' into 'master'
Prevent the symptoms of Issue #33

See merge request VanessaE/pipeworks!31
2020-12-18 21:44:11 +00:00
ba7eb19317 Prevent the symptoms of Issue #33
Try to prevent the crash from issue #33 by
replaving nil velocity or acceleration with (0,0,0).
This does not fix the underlying cause of them being nil,
but should prevent the crash.
2020-12-18 19:28:07 +01:00
065c953eba use the right gear item in node breaker 2020-10-28 12:30:48 -04:00
9a63d17e4a Merge branch 'm_disable_print_message' into 'master'
Log the "Pipeworks loaded!" message to infostream instead of printing it

See merge request VanessaE/pipeworks!30
2020-10-17 18:55:24 +00:00
d814357ddf Merge branch 'm_table_extends_clean' into 'master'
Make pipeworks.table_extend easier to read

See merge request VanessaE/pipeworks!29
2020-10-17 18:55:01 +00:00
c2fe5fe956 Make pipeworks.table_extend easier to read
In my opinion this clarifies that tbl2 is attached at the end of tbl
2020-10-17 20:50:20 +02:00
9dbaa5f4f6 Log the "Pipeworks loaded!" message to infostream instead of printing it 2020-10-17 20:46:44 +02:00
d93396600f Merge branch 'protected-access-to-wielders' into 'master'
Allow protected access to wielder inventories.

Closes #40

See merge request VanessaE/pipeworks!27
2020-09-24 15:33:17 +00:00
c966a8a57d Allow players with the protection_bypass privilege or access to
the protection to access wielder node inventories.

Fixes #40.
2020-09-24 05:12:32 -06:00
61b061f669 Merge branch 'avoid-protection-check-on-chest-close' into 'master'
Avoid protection check on chest close.

Closes #23

See merge request VanessaE/pipeworks!26
2020-09-11 09:45:04 +00:00
fe91d5eb46 Avoid protection check on chest close.
Fixes #23.
2020-09-10 23:12:11 -06:00
eb1064ca6d Merge branch 'patch-1' into 'master'
Update pipeworks.zh_CN.tr

See merge request VanessaE/pipeworks!25
2020-09-05 07:14:04 +00:00
f54e25ec52 Update pipeworks.zh_CN.tr 2020-09-05 06:25:58 +00:00
dedb0dd54e Merge branch 'tptube-api' into 'master'
Expose teleport tube database API

See merge request VanessaE/pipeworks!24
2020-08-10 17:54:28 +00:00
SX
477a024034 Expose teleport tube database API 2020-08-09 23:09:31 +03:00
34cb0e7682 Merge branch 'master' into 'master'
Add "get_recipe" digiline command for querying the current autocrafter recipe.

See merge request VanessaE/pipeworks!23
2020-07-30 00:40:41 +00:00
505fc7cc49 Add "get_recipe" digiline command for querying the current autocrafter recipe.
Useful for having the player teach the luacontroller how to craft various items.
2020-07-23 01:49:37 +02:00
cb58a646cf Merge branch 'undefined' into 'master'
Add Chinese Translation

See merge request VanessaE/pipeworks!22
2020-07-19 03:55:39 +00:00
e3135c53f0 Add Chinese Translation 2020-07-19 02:22:12 +00:00
366d57f4da Merge branch 'fix_connect_sides' into 'master'
Consider connect_sides for item transport

See merge request VanessaE/pipeworks!21
2020-06-30 10:39:52 +00:00
c79e68a80c Consider connect_sides for item transport
Previously connect_sides was only used to choose the correct visual
model, but not during item transport. This allowed items to exit tubes
in directions without a visual connection and enter objects from sides
that should not be connectable according to connect_sides.
For example an item could enter a chest from the front, if a tube passed
there.

This change saves the connect_sides in the meta table of the object
whenever the visual representation is updated. When nothing is cached
yet, it uses the old behavior. That way it does not break existing
builds.
2020-06-30 10:11:22 +00:00
9338c109a6 Merge branch 'rebased_luacontroller' into 'master'
Rebase lua_tube onto upstream luacontroller

See merge request VanessaE/pipeworks!20
2020-06-30 09:41:01 +00:00
63bee98948 Fix typo in luatube update_real_port_states helper 2020-06-30 09:17:46 +00:00
ee6c9991b9 Rebase lua_tube onto upstream luacontroller
Adds:
 - various bug fixes
 - error label on the formspec
 - lightweight interrupts
2020-06-30 09:15:50 +00:00
9eefe9c7bb add minimum minetest version key for contentdb 2020-06-03 13:00:28 -04:00
8281e3d068 Merge branch 'nodebreaker_fix' into 'master'
Prevent the node breaker from digging all nodes

See merge request VanessaE/pipeworks!19
2020-05-26 20:56:09 +00:00
ed282b0298 Prevent node breaker from digging all nodes
A small mistake in the code allowed the node breaker to dig any node
using any tool, including hand.
2020-05-26 22:29:16 +02:00
5410ea74f2 Fix wrong inventory_image and wield_image types 2020-05-07 12:13:25 -04:00
4602290bc5 Merge branch 'master' into 'master'
Change digilines detector tube to send an item table instead of an item string

See merge request VanessaE/pipeworks!18
2020-04-28 03:29:22 +00:00
64fb90f3e5 Change digilines detector tube to send an item table instead of an item string 2020-04-28 04:08:31 +01:00
99cb3a5d9d Merge branch 'fix-21' into 'master'
Translation string errors

Closes #21

See merge request VanessaE/pipeworks!17
2020-02-20 14:29:07 +00:00
728d4e179a Translation string errors
Fixes #21.
Sorry, I had put wrong identifiers in translations strings.
Also, found another one to fix in `teleport_tube.lua`.

I hope, there is no other error.
2020-02-20 15:27:08 +01:00
760e9e383c Merge branch 'mt5-locale' into 'master'
French translation

See merge request VanessaE/pipeworks!16
2020-02-18 17:34:52 +00:00
62bc13078f Add translation support
- Created `locale/template.txt`
- Fixed some typos
- Replace some `print("[pipeworks]"..` with `pipeworks.logger()`
- Removed "You hacker, you" from descriptions
2020-02-18 17:34:52 +00:00
84d7104c66 Merge branch 'master' into 'master'
Fix injector compatibility with technic HV machines

Closes #20

See merge request VanessaE/pipeworks!15
2020-01-14 12:35:37 +00:00
2ca825f991 fix injector compatibility with technic hv machines 2020-01-14 23:07:09 +11:00
0fa88b7054 Merge branch 'filter-injector-refactor' into 'master'
filter-injector.lua: eliminate most parameters from grabAndFire by making it a closure

See merge request VanessaE/pipeworks!13
2020-01-10 19:48:57 +00:00
8f03c0e684 filter-injector.lua: Rename exact_match -> exmatch_mode
Also fixed bad conditional that would accept non-integer values of exmatch in a message.
2020-01-10 19:48:57 +00:00
28 changed files with 938 additions and 346 deletions

View File

@ -1,3 +1,4 @@
local S = minetest.get_translator("pipeworks")
local autocrafterCache = {} -- caches some recipe data to avoid to call the slow function minetest.get_craft_result() every second local autocrafterCache = {} -- caches some recipe data to avoid to call the slow function minetest.get_craft_result() every second
local craft_time = 1 local craft_time = 1
@ -16,7 +17,7 @@ end
local function get_item_info(stack) local function get_item_info(stack)
local name = stack:get_name() local name = stack:get_name()
local def = minetest.registered_items[name] local def = minetest.registered_items[name]
local description = def and def.description or "Unknown item" local description = def and def.description or S("Unknown item")
return description, name return description, name
end end
@ -68,7 +69,7 @@ local function run_autocrafter(pos, elapsed)
local output_item = craft.output.item local output_item = craft.output.item
-- only use crafts that have an actual result -- only use crafts that have an actual result
if output_item:is_empty() then if output_item:is_empty() then
meta:set_string("infotext", "unconfigured Autocrafter: unknown recipe") meta:set_string("infotext", S("unconfigured Autocrafter: unknown recipe"))
return false return false
end end
@ -101,7 +102,7 @@ local function after_recipe_change(pos, inventory)
if inventory:is_empty("recipe") then if inventory:is_empty("recipe") then
minetest.get_node_timer(pos):stop() minetest.get_node_timer(pos):stop()
autocrafterCache[minetest.hash_node_position(pos)] = nil autocrafterCache[minetest.hash_node_position(pos)] = nil
meta:set_string("infotext", "unconfigured Autocrafter") meta:set_string("infotext", S("unconfigured Autocrafter"))
inventory:set_stack("output", 1, "") inventory:set_stack("output", 1, "")
return return
end end
@ -126,7 +127,7 @@ local function after_recipe_change(pos, inventory)
craft = craft or get_craft(pos, inventory, hash) craft = craft or get_craft(pos, inventory, hash)
local output_item = craft.output.item local output_item = craft.output.item
local description, name = get_item_info(output_item) local description, name = get_item_info(output_item)
meta:set_string("infotext", string.format("'%s' Autocrafter (%s)", description, name)) meta:set_string("infotext", S("'@1' Autocrafter (@2)", description, name))
inventory:set_stack("output", 1, output_item) inventory:set_stack("output", 1, output_item)
after_inventory_change(pos) after_inventory_change(pos)
@ -190,8 +191,8 @@ local function update_meta(meta, enabled)
"listring[context;dst]" .. "listring[context;dst]" ..
"listring[current_player;main]" "listring[current_player;main]"
if minetest.get_modpath("digilines") then if minetest.get_modpath("digilines") then
fs = fs.."field[1,3.5;4,1;channel;Channel;${channel}]" fs = fs.."field[1,3.5;4,1;channel;"..S("Channel")..";${channel}]"
fs = fs.."button_exit[5,3.2;2,1;save;Save]" fs = fs.."button_exit[5,3.2;2,1;save;"..S("Save").."]"
end end
meta:set_string("formspec",fs) meta:set_string("formspec",fs)
@ -200,13 +201,13 @@ local function update_meta(meta, enabled)
-- this might be more written code, but actually executes less -- this might be more written code, but actually executes less
local output = meta:get_inventory():get_stack("output", 1) local output = meta:get_inventory():get_stack("output", 1)
if output:is_empty() then -- doesn't matter if paused or not if output:is_empty() then -- doesn't matter if paused or not
meta:set_string("infotext", "unconfigured Autocrafter") meta:set_string("infotext", S("unconfigured Autocrafter"))
return false return false
end end
local description, name = get_item_info(output) local description, name = get_item_info(output)
local infotext = enabled and string.format("'%s' Autocrafter (%s)", description, name) local infotext = enabled and S("'@1' Autocrafter (@2)", description, name)
or string.format("paused '%s' Autocrafter", description) or S("paused '@1' Autocrafter", description)
meta:set_string("infotext", infotext) meta:set_string("infotext", infotext)
return enabled return enabled
@ -226,7 +227,7 @@ local function upgrade_autocrafter(pos, meta)
update_meta(meta, true) update_meta(meta, true)
if meta:get_string("virtual_items") == "1" then -- we are version 2 if meta:get_string("virtual_items") == "1" then -- we are version 2
-- we allready dropped stuff, so lets remove the metadatasetting (we are not being called again for this node) -- we already dropped stuff, so lets remove the metadatasetting (we are not being called again for this node)
meta:set_string("virtual_items", "") meta:set_string("virtual_items", "")
else -- we are version 1 else -- we are version 1
local recipe = inv:get_list("recipe") local recipe = inv:get_list("recipe")
@ -248,7 +249,7 @@ local function upgrade_autocrafter(pos, meta)
end end
minetest.register_node("pipeworks:autocrafter", { minetest.register_node("pipeworks:autocrafter", {
description = "Autocrafter", description = S("Autocrafter"),
drawtype = "normal", drawtype = "normal",
tiles = {"pipeworks_autocrafter.png"}, tiles = {"pipeworks_autocrafter.png"},
groups = {snappy = 3, tubedevice = 1, tubedevice_receiver = 1}, groups = {snappy = 3, tubedevice = 1, tubedevice_receiver = 1},
@ -388,6 +389,27 @@ minetest.register_node("pipeworks:autocrafter", {
end end
end end
after_recipe_change(pos,inv) after_recipe_change(pos,inv)
elseif msg == "get_recipe" then
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local recipe = {}
for y=0,2,1 do
local row = {}
for x=1,3,1 do
local slot = y*3+x
table.insert(row, inv:get_stack("recipe",slot):get_name())
end
table.insert(recipe, row)
end
local setchan = meta:get_string("channel")
local output = inv:get_stack("output", 1)
digiline:receptor_send(pos, digiline.rules.default, setchan, {
recipe = recipe,
result = {
name = output:get_name(),
count = output:get_count(),
}
})
elseif msg == "off" then elseif msg == "off" then
update_meta(meta, false) update_meta(meta, false)
minetest.get_node_timer(pos):stop() minetest.get_node_timer(pos):stop()

View File

@ -53,27 +53,28 @@ local function tube_autoroute(pos)
} }
-- xm = 1, xp = 2, ym = 3, yp = 4, zm = 5, zp = 6 -- xm = 1, xp = 2, ym = 3, yp = 4, zm = 5, zp = 6
local positions = {} local adjlist = {} -- this will be used in item_transport
local nodes = {}
for i, adj in ipairs(adjustments) do for i, adj in ipairs(adjustments) do
positions[i] = vector.add(pos, adj) local position = vector.add(pos, adj)
nodes[i] = minetest.get_node(positions[i]) local node = minetest.get_node(position)
end
for i, node in ipairs(nodes) do
local idef = minetest.registered_nodes[node.name] local idef = minetest.registered_nodes[node.name]
-- handle the tubes themselves -- handle the tubes themselves
if is_tube(node.name) then if is_tube(node.name) then
active[i] = 1 active[i] = 1
table.insert(adjlist, adj)
-- handle new style connectors -- handle new style connectors
elseif idef and idef.tube and idef.tube.connect_sides then elseif idef and idef.tube and idef.tube.connect_sides then
local dir = adjustments[i] if idef.tube.connect_sides[nodeside(node, vector.multiply(adj, -1))] then
if idef.tube.connect_sides[nodeside(node, vector.multiply(dir, -1))] then
active[i] = 1 active[i] = 1
table.insert(adjlist, adj)
end end
end end
end end
minetest.get_meta(pos):set_string("adjlist", minetest.serialize(adjlist))
-- all sides checked, now figure which tube to use. -- all sides checked, now figure which tube to use.
local nodedef = minetest.registered_nodes[nctr.name] local nodedef = minetest.registered_nodes[nctr.name]

View File

@ -86,10 +86,9 @@ function pipeworks.table_contains(tbl, element)
end end
function pipeworks.table_extend(tbl, tbl2) function pipeworks.table_extend(tbl, tbl2)
local index = #tbl + 1 local oldlength = #tbl
for _, elt in ipairs(tbl2) do for i = 1,#tbl2 do
tbl[index] = elt tbl[oldlength + i] = tbl2[i]
index = index + 1
end end
end end

View File

@ -77,10 +77,8 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
pipeworks.after_place(pos) pipeworks.after_place(pos)
end) end)
minetest.sound_play(sound, {gain = 0.3, pos = pos, max_hear_distance = 10}) minetest.sound_play(sound, {gain = 0.3, pos = pos, max_hear_distance = 10})
end elseif pipeworks.may_configure(pos, player) then
-- Pipeworks Switch
-- Pipeworks Switch
if pipeworks.may_configure(pos, player) and not fields.quit then
fs_helpers.on_receive_fields(pos, fields) fs_helpers.on_receive_fields(pos, fields)
minetest.show_formspec(player:get_player_name(), "pipeworks:chest_formspec", get_chest_formspec(pos)) minetest.show_formspec(player:get_player_name(), "pipeworks:chest_formspec", get_chest_formspec(pos))
end end

View File

@ -1,7 +1,10 @@
-- this file is basically a modified copy of -- this file is basically a modified copy of
-- minetest_game/mods/default/furnaces.lua -- minetest_game/mods/default/furnaces.lua
-- translation support
local S = minetest.get_translator("pipeworks")
local DS = minetest.get_translator("default")
local fs_helpers = pipeworks.fs_helpers local fs_helpers = pipeworks.fs_helpers
tube_entry = "^pipeworks_tube_connection_stony.png" tube_entry = "^pipeworks_tube_connection_stony.png"
@ -36,7 +39,7 @@ local function active_formspec(fuel_percent, item_percent, pos, meta)
pipeworks.button_off, pipeworks.button_off,
pipeworks.button_on pipeworks.button_on
} }
).."label[0.9,3.51;Allow splitting incoming material (not fuel) stacks from tubes]" ).."label[0.9,3.51;"..S("Allow splitting incoming material (not fuel) stacks from tubes").."]"
return formspec return formspec
end end
@ -67,7 +70,7 @@ local function inactive_formspec(pos, meta)
pipeworks.button_off, pipeworks.button_off,
pipeworks.button_on pipeworks.button_on
} }
).."label[0.9,3.51;Allow splitting incoming material (not fuel) stacks from tubes]" ).."label[0.9,3.51;"..S("Allow splitting incoming material (not fuel) stacks from tubes").."]"
return formspec return formspec
end end
@ -90,7 +93,7 @@ local function allow_metadata_inventory_put(pos, listname, index, stack, player)
if listname == "fuel" then if listname == "fuel" then
if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then
if inv:is_empty("src") then if inv:is_empty("src") then
meta:set_string("infotext", "Furnace is empty") meta:set_string("infotext", DS("Furnace is empty"))
end end
return stack:get_count() return stack:get_count()
else else
@ -219,40 +222,40 @@ local function furnace_node_timer(pos, elapsed)
if cookable then if cookable then
item_percent = math.floor(src_time / cooked.time * 100) item_percent = math.floor(src_time / cooked.time * 100)
if item_percent > 100 then if item_percent > 100 then
item_state = "100% (output full)" item_state = DS("100% (output full)")
else else
item_state = item_percent .. "%" item_state = DS("@1%", item_percent)
end end
else else
if srclist[1]:is_empty() then if srclist[1]:is_empty() then
item_state = "Empty" item_state = DS("Empty")
else else
item_state = "Not cookable" item_state = DS("Not cookable")
end end
end end
local fuel_state = "Empty" local fuel_state = DS("Empty")
local active = "inactive " local active = DS("Furnace inactive")
local result = false local result = false
if fuel_totaltime ~= 0 then if fuel_totaltime ~= 0 then
active = "active " active = DS("Furnace active")
local fuel_percent = math.floor(fuel_time / fuel_totaltime * 100) local fuel_percent = math.floor(fuel_time / fuel_totaltime * 100)
fuel_state = fuel_percent .. "%" fuel_state = DS("@1%", fuel_percent)
formspec = active_formspec(fuel_percent, item_percent, pos, meta) formspec = active_formspec(fuel_percent, item_percent, pos, meta)
swap_node(pos, "default:furnace_active") swap_node(pos, "default:furnace_active")
-- make sure timer restarts automatically -- make sure timer restarts automatically
result = true result = true
else else
if not fuellist[1]:is_empty() then if not fuellist[1]:is_empty() then
fuel_state = "0%" fuel_state = DS("@1%", "0")
end end
swap_node(pos, "default:furnace") swap_node(pos, "default:furnace")
-- stop timer on the inactive furnace -- stop timer on the inactive furnace
minetest.get_node_timer(pos):stop() minetest.get_node_timer(pos):stop()
end end
local infotext = "Furnace " .. active .. "(Item: " .. item_state .. "; Fuel: " .. fuel_state .. ")" local infotext = active.." "..DS("(Item: @1; Fuel: @2)", item_state, fuel_state)
-- --
-- Set meta values -- Set meta values
@ -271,7 +274,7 @@ end
-- --
minetest.register_node(":default:furnace", { minetest.register_node(":default:furnace", {
description = "Furnace", description = DS("Furnace"),
tiles = { tiles = {
"default_furnace_top.png"..tube_entry, "default_furnace_top.png"..tube_entry,
"default_furnace_bottom.png"..tube_entry, "default_furnace_bottom.png"..tube_entry,
@ -360,7 +363,7 @@ minetest.register_node(":default:furnace", {
}) })
minetest.register_node(":default:furnace_active", { minetest.register_node(":default:furnace_active", {
description = "Furnace", description = DS("Furnace"),
tiles = { tiles = {
"default_furnace_top.png"..tube_entry, "default_furnace_top.png"..tube_entry,
"default_furnace_bottom.png"..tube_entry, "default_furnace_bottom.png"..tube_entry,

View File

@ -1,7 +1,9 @@
local S = minetest.get_translator("pipeworks")
local straight = function(pos, node, velocity, stack) return {velocity} end local straight = function(pos, node, velocity, stack) return {velocity} end
minetest.register_node("pipeworks:steel_block_embedded_tube", { minetest.register_node("pipeworks:steel_block_embedded_tube", {
description = "Airtight steelblock embedded tube", description = S("Airtight steelblock embedded tube"),
tiles = { tiles = {
"default_steel_block.png", "default_steel_block.png", "default_steel_block.png", "default_steel_block.png",
"default_steel_block.png", "default_steel_block.png", "default_steel_block.png", "default_steel_block.png",
@ -45,7 +47,7 @@ local pane_box = {
} }
minetest.register_node("pipeworks:steel_pane_embedded_tube", { minetest.register_node("pipeworks:steel_pane_embedded_tube", {
drawtype = "nodebox", drawtype = "nodebox",
description = "Airtight panel embedded tube ", description = S("Airtight panel embedded tube"),
tiles = { tiles = {
"pipeworks_pane_embedded_tube_sides.png^[transformR90", "pipeworks_pane_embedded_tube_sides.png^[transformR90",
"pipeworks_pane_embedded_tube_sides.png^[transformR90", "pipeworks_pane_embedded_tube_sides.png^[transformR90",

View File

@ -1,7 +0,0 @@
default
basic_materials
screwdriver
mesecons?
mesecons_mvps?
digilines?
signs_lib?

View File

@ -1 +0,0 @@
This mod uses mesh nodes and nodeboxes to supply a complete set of 3D pipes and tubes, along with devices that work with them.

View File

@ -1,3 +1,4 @@
local S = minetest.get_translator("pipeworks")
local new_flow_logic_register = pipeworks.flowables.register local new_flow_logic_register = pipeworks.flowables.register
local polys = "" local polys = ""
@ -135,7 +136,7 @@ for s in ipairs(states) do
local pumpname = "pipeworks:pump_"..states[s] local pumpname = "pipeworks:pump_"..states[s]
minetest.register_node(pumpname, { minetest.register_node(pumpname, {
description = "Pump/Intake Module", description = S("Pump/Intake Module"),
drawtype = "mesh", drawtype = "mesh",
mesh = "pipeworks_pump"..polys..".obj", mesh = "pipeworks_pump"..polys..".obj",
tiles = { "pipeworks_pump_"..states[s]..".png" }, tiles = { "pipeworks_pump_"..states[s]..".png" },
@ -178,7 +179,7 @@ for s in ipairs(states) do
local nodename_valve_empty = "pipeworks:valve_"..states[s].."_empty" local nodename_valve_empty = "pipeworks:valve_"..states[s].."_empty"
minetest.register_node(nodename_valve_empty, { minetest.register_node(nodename_valve_empty, {
description = "Valve", description = S("Valve"),
drawtype = "mesh", drawtype = "mesh",
mesh = "pipeworks_valve_"..states[s]..polys..".obj", mesh = "pipeworks_valve_"..states[s]..polys..".obj",
tiles = { "pipeworks_valve.png" }, tiles = { "pipeworks_valve.png" },
@ -224,7 +225,7 @@ end
local nodename_valve_loaded = "pipeworks:valve_on_loaded" local nodename_valve_loaded = "pipeworks:valve_on_loaded"
minetest.register_node(nodename_valve_loaded, { minetest.register_node(nodename_valve_loaded, {
description = "Valve", description = S("Valve"),
drawtype = "mesh", drawtype = "mesh",
mesh = "pipeworks_valve_on"..polys..".obj", mesh = "pipeworks_valve_on"..polys..".obj",
tiles = { "pipeworks_valve.png" }, tiles = { "pipeworks_valve.png" },
@ -272,7 +273,7 @@ new_flow_logic_register.directional_horizonal_rotate(nodename_valve_loaded, true
-- FIXME: should this do anything useful in the new flow logic? -- FIXME: should this do anything useful in the new flow logic?
minetest.register_node("pipeworks:grating", { minetest.register_node("pipeworks:grating", {
description = "Decorative grating", description = S("Decorative grating"),
tiles = { tiles = {
"pipeworks_grating_top.png", "pipeworks_grating_top.png",
"pipeworks_grating_sides.png", "pipeworks_grating_sides.png",
@ -305,7 +306,7 @@ minetest.register_node("pipeworks:grating", {
local nodename_spigot_empty = "pipeworks:spigot" local nodename_spigot_empty = "pipeworks:spigot"
minetest.register_node(nodename_spigot_empty, { minetest.register_node(nodename_spigot_empty, {
description = "Spigot outlet", description = S("Spigot outlet"),
drawtype = "mesh", drawtype = "mesh",
mesh = "pipeworks_spigot"..polys..".obj", mesh = "pipeworks_spigot"..polys..".obj",
tiles = { "pipeworks_spigot.png" }, tiles = { "pipeworks_spigot.png" },
@ -336,7 +337,7 @@ minetest.register_node(nodename_spigot_empty, {
local nodename_spigot_loaded = "pipeworks:spigot_pouring" local nodename_spigot_loaded = "pipeworks:spigot_pouring"
minetest.register_node(nodename_spigot_loaded, { minetest.register_node(nodename_spigot_loaded, {
description = "Spigot outlet", description = S("Spigot outlet"),
drawtype = "mesh", drawtype = "mesh",
mesh = "pipeworks_spigot_pouring"..polys..".obj", mesh = "pipeworks_spigot_pouring"..polys..".obj",
tiles = { tiles = {
@ -402,7 +403,7 @@ local panel_cbox = {
local nodename_panel_empty = "pipeworks:entry_panel_empty" local nodename_panel_empty = "pipeworks:entry_panel_empty"
minetest.register_node(nodename_panel_empty, { minetest.register_node(nodename_panel_empty, {
description = "Airtight Pipe entry/exit", description = S("Airtight Pipe entry/exit"),
drawtype = "mesh", drawtype = "mesh",
mesh = "pipeworks_entry_panel"..polys..".obj", mesh = "pipeworks_entry_panel"..polys..".obj",
tiles = { "pipeworks_entry_panel.png" }, tiles = { "pipeworks_entry_panel.png" },
@ -422,7 +423,7 @@ minetest.register_node(nodename_panel_empty, {
local nodename_panel_loaded = "pipeworks:entry_panel_loaded" local nodename_panel_loaded = "pipeworks:entry_panel_loaded"
minetest.register_node(nodename_panel_loaded, { minetest.register_node(nodename_panel_loaded, {
description = "Airtight Pipe entry/exit", description = S("Airtight Pipe entry/exit"),
drawtype = "mesh", drawtype = "mesh",
mesh = "pipeworks_entry_panel"..polys..".obj", mesh = "pipeworks_entry_panel"..polys..".obj",
tiles = { "pipeworks_entry_panel.png" }, tiles = { "pipeworks_entry_panel.png" },
@ -448,7 +449,7 @@ new_flow_logic_register.directional_horizonal_rotate(nodename_panel_loaded, true
local nodename_sensor_empty = "pipeworks:flow_sensor_empty" local nodename_sensor_empty = "pipeworks:flow_sensor_empty"
minetest.register_node(nodename_sensor_empty, { minetest.register_node(nodename_sensor_empty, {
description = "Flow Sensor", description = S("Flow Sensor"),
drawtype = "mesh", drawtype = "mesh",
mesh = "pipeworks_flow_sensor"..polys..".obj", mesh = "pipeworks_flow_sensor"..polys..".obj",
tiles = { "pipeworks_flow_sensor_off.png" }, tiles = { "pipeworks_flow_sensor_off.png" },
@ -487,7 +488,7 @@ minetest.register_node(nodename_sensor_empty, {
local nodename_sensor_loaded = "pipeworks:flow_sensor_loaded" local nodename_sensor_loaded = "pipeworks:flow_sensor_loaded"
minetest.register_node(nodename_sensor_loaded, { minetest.register_node(nodename_sensor_loaded, {
description = "Flow sensor (on)", description = S("Flow sensor (on)"),
drawtype = "mesh", drawtype = "mesh",
mesh = "pipeworks_flow_sensor"..polys..".obj", mesh = "pipeworks_flow_sensor"..polys..".obj",
tiles = { "pipeworks_flow_sensor_on.png" }, tiles = { "pipeworks_flow_sensor_on.png" },
@ -536,18 +537,18 @@ new_flow_logic_register.transition_simple_set(sensor_pressure_set, { mesecons=pi
-- TODO flow-logic-stub: these don't currently do anything under the new flow logic. -- TODO flow-logic-stub: these don't currently do anything under the new flow logic.
for fill = 0, 10 do for fill = 0, 10 do
local filldesc="empty" local filldesc=S("empty")
local sgroups = {snappy=3, pipe=1, tankfill=fill+1} local sgroups = {snappy=3, pipe=1, tankfill=fill+1}
local image = nil local image = nil
if fill ~= 0 then if fill ~= 0 then
filldesc=fill.."0% full" filldesc=S("@1% full", 10*fill)
sgroups = {snappy=3, pipe=1, tankfill=fill+1, not_in_creative_inventory=1} sgroups = {snappy=3, pipe=1, tankfill=fill+1, not_in_creative_inventory=1}
image = "pipeworks_storage_tank_fittings.png" image = "pipeworks_storage_tank_fittings.png"
end end
minetest.register_node("pipeworks:expansion_tank_"..fill, { minetest.register_node("pipeworks:expansion_tank_"..fill, {
description = "Expansion Tank ("..filldesc..")... You hacker, you.", description = S("Expansion Tank (@1)", filldesc),
tiles = { tiles = {
"pipeworks_storage_tank_fittings.png", "pipeworks_storage_tank_fittings.png",
"pipeworks_storage_tank_fittings.png", "pipeworks_storage_tank_fittings.png",
@ -575,7 +576,7 @@ for fill = 0, 10 do
}) })
minetest.register_node("pipeworks:storage_tank_"..fill, { minetest.register_node("pipeworks:storage_tank_"..fill, {
description = "Fluid Storage Tank ("..filldesc..")", description = S("Fluid Storage Tank (@1)", filldesc),
tiles = { tiles = {
"pipeworks_storage_tank_fittings.png", "pipeworks_storage_tank_fittings.png",
"pipeworks_storage_tank_fittings.png", "pipeworks_storage_tank_fittings.png",
@ -607,7 +608,7 @@ end
local nodename_fountain_empty = "pipeworks:fountainhead" local nodename_fountain_empty = "pipeworks:fountainhead"
minetest.register_node(nodename_fountain_empty, { minetest.register_node(nodename_fountain_empty, {
description = "Fountainhead", description = S("Fountainhead"),
drawtype = "mesh", drawtype = "mesh",
mesh = "pipeworks_fountainhead"..polys..".obj", mesh = "pipeworks_fountainhead"..polys..".obj",
tiles = { "pipeworks_fountainhead.png" }, tiles = { "pipeworks_fountainhead.png" },
@ -641,7 +642,7 @@ minetest.register_node(nodename_fountain_empty, {
local nodename_fountain_loaded = "pipeworks:fountainhead_pouring" local nodename_fountain_loaded = "pipeworks:fountainhead_pouring"
minetest.register_node(nodename_fountain_loaded, { minetest.register_node(nodename_fountain_loaded, {
description = "Fountainhead", description = S("Fountainhead"),
drawtype = "mesh", drawtype = "mesh",
mesh = "pipeworks_fountainhead"..polys..".obj", mesh = "pipeworks_fountainhead"..polys..".obj",
tiles = { "pipeworks_fountainhead.png" }, tiles = { "pipeworks_fountainhead.png" },
@ -691,7 +692,7 @@ local sp_cbox = {
local nodename_sp_empty = "pipeworks:straight_pipe_empty" local nodename_sp_empty = "pipeworks:straight_pipe_empty"
minetest.register_node(nodename_sp_empty, { minetest.register_node(nodename_sp_empty, {
description = "Straight-only Pipe", description = S("Straight-only Pipe"),
drawtype = "mesh", drawtype = "mesh",
mesh = "pipeworks_straight_pipe"..polys..".obj", mesh = "pipeworks_straight_pipe"..polys..".obj",
tiles = { "pipeworks_straight_pipe_empty.png" }, tiles = { "pipeworks_straight_pipe_empty.png" },
@ -713,7 +714,7 @@ minetest.register_node(nodename_sp_empty, {
local nodename_sp_loaded = "pipeworks:straight_pipe_loaded" local nodename_sp_loaded = "pipeworks:straight_pipe_loaded"
minetest.register_node(nodename_sp_loaded, { minetest.register_node(nodename_sp_loaded, {
description = "Straight-only Pipe", description = S("Straight-only Pipe"),
drawtype = "mesh", drawtype = "mesh",
mesh = "pipeworks_straight_pipe"..polys..".obj", mesh = "pipeworks_straight_pipe"..polys..".obj",
tiles = { "pipeworks_straight_pipe_loaded.png" }, tiles = { "pipeworks_straight_pipe_loaded.png" },

View File

@ -1,3 +1,4 @@
local S = minetest.get_translator("pipeworks")
local fs_helpers = pipeworks.fs_helpers local fs_helpers = pipeworks.fs_helpers
local function delay(x) local function delay(x)
@ -5,47 +6,47 @@ local function delay(x)
end end
local function set_filter_infotext(data, meta) local function set_filter_infotext(data, meta)
local infotext = data.wise_desc.." Filter-Injector" local infotext = S("@1 Filter-Injector", data.wise_desc)
if meta:get_int("slotseq_mode") == 2 then if meta:get_int("slotseq_mode") == 2 then
infotext = infotext .. " (slot #"..meta:get_int("slotseq_index").." next)" infotext = infotext .. " "..S("(slot #@1 next)", meta:get_int("slotseq_index"))
end end
meta:set_string("infotext", infotext) meta:set_string("infotext", infotext)
end end
local function set_filter_formspec(data, meta) local function set_filter_formspec(data, meta)
local itemname = data.wise_desc.." Filter-Injector" local itemname = S("@1 Filter-Injector", data.wise_desc)
local formspec local formspec
if data.digiline then if data.digiline then
formspec = "size[8,2.7]".. formspec = "size[8,2.7]"..
"item_image[0,0;1,1;pipeworks:"..data.name.."]".. "item_image[0,0;1,1;pipeworks:"..data.name.."]"..
"label[1,0;"..minetest.formspec_escape(itemname).."]".. "label[1,0;"..minetest.formspec_escape(itemname).."]"..
"field[0.3,1.5;8.0,1;channel;Channel;${channel}]".. "field[0.3,1.5;8.0,1;channel;"..S("Channel")..";${channel}]"..
fs_helpers.cycling_button(meta, "button[0,2;4,1", "slotseq_mode", fs_helpers.cycling_button(meta, "button[0,2;4,1", "slotseq_mode",
{"Sequence slots by Priority", {S("Sequence slots by Priority"),
"Sequence slots Randomly", S("Sequence slots Randomly"),
"Sequence slots by Rotation"}).. S("Sequence slots by Rotation")})..
fs_helpers.cycling_button(meta, "button[4,2;4,1", "exmatch_mode", fs_helpers.cycling_button(meta, "button[4,2;4,1", "exmatch_mode",
{"Exact match - off", {S("Exact match - off"),
"Exact match - on "}) S("Exact match - on")})
else else
local exmatch_button = "" local exmatch_button = ""
if data.stackwise then if data.stackwise then
exmatch_button = exmatch_button =
fs_helpers.cycling_button(meta, "button[4,3.5;4,1", "exmatch_mode", fs_helpers.cycling_button(meta, "button[4,3.5;4,1", "exmatch_mode",
{"Exact match - off", {S("Exact match - off"),
"Exact match - on "}) S("Exact match - on")})
end end
formspec = "size[8,8.5]".. formspec = "size[8,8.5]"..
"item_image[0,0;1,1;pipeworks:"..data.name.."]".. "item_image[0,0;1,1;pipeworks:"..data.name.."]"..
"label[1,0;"..minetest.formspec_escape(itemname).."]".. "label[1,0;"..minetest.formspec_escape(itemname).."]"..
"label[0,1;Prefer item types:]".. "label[0,1;"..S("Prefer item types:").."]"..
"list[context;main;0,1.5;8,2;]".. "list[context;main;0,1.5;8,2;]"..
fs_helpers.cycling_button(meta, "button[0,3.5;4,1", "slotseq_mode", fs_helpers.cycling_button(meta, "button[0,3.5;4,1", "slotseq_mode",
{"Sequence slots by Priority", {S("Sequence slots by Priority"),
"Sequence slots Randomly", S("Sequence slots Randomly"),
"Sequence slots by Rotation"}).. S("Sequence slots by Rotation")})..
exmatch_button.. exmatch_button..
"list[current_player;main;0,4.5;8,4;]" .. "list[current_player;main;0,4.5;8,4;]" ..
"listring[]" "listring[]"
@ -53,116 +54,6 @@ local function set_filter_formspec(data, meta)
meta:set_string("formspec", formspec) meta:set_string("formspec", formspec)
end end
-- todo SOON: this function has *way too many* parameters
local function grabAndFire(data,slotseq_mode,exmatch_mode,filtmeta,frominv,frominvname,frompos,fromnode,filterfor,fromtube,fromdef,dir,fakePlayer,all,digiline)
local sposes = {}
if not frominvname or not frominv:get_list(frominvname) then return end
for spos,stack in ipairs(frominv:get_list(frominvname)) do
local matches
if filterfor == "" then
matches = stack:get_name() ~= ""
else
local fname = filterfor.name
local fgroup = filterfor.group
local fwear = filterfor.wear
local fmetadata = filterfor.metadata
matches = (not fname -- If there's a name filter,
or stack:get_name() == fname) -- it must match.
and (not fgroup -- If there's a group filter,
or (type(fgroup) == "string" -- it must be a string
and minetest.get_item_group( -- and it must match.
stack:get_name(), fgroup) ~= 0))
and (not fwear -- If there's a wear filter:
or (type(fwear) == "number" -- If it's a number,
and stack:get_wear() == fwear) -- it must match.
or (type(fwear) == "table" -- If it's a table:
and (not fwear[1] -- If there's a lower bound,
or (type(fwear[1]) == "number" -- it must be a number
and fwear[1] <= stack:get_wear())) -- and it must be <= the actual wear.
and (not fwear[2] -- If there's an upper bound
or (type(fwear[2]) == "number" -- it must be a number
and stack:get_wear() < fwear[2])))) -- and it must be > the actual wear.
-- If the wear filter is of any other type, fail.
--
and (not fmetadata -- If there's a matadata filter,
or (type(fmetadata) == "string" -- it must be a string
and stack:get_metadata() == fmetadata)) -- and it must match.
end
if matches then table.insert(sposes, spos) end
end
if #sposes == 0 then return false end
if slotseq_mode == 1 then
for i = #sposes, 2, -1 do
local j = math.random(i)
local t = sposes[j]
sposes[j] = sposes[i]
sposes[i] = t
end
elseif slotseq_mode == 2 then
local headpos = filtmeta:get_int("slotseq_index")
table.sort(sposes, function (a, b)
if a >= headpos then
if b < headpos then return true end
else
if b >= headpos then return false end
end
return a < b
end)
end
for _, spos in ipairs(sposes) do
local stack = frominv:get_stack(frominvname, spos)
local doRemove = stack:get_count()
if fromtube.can_remove then
doRemove = fromtube.can_remove(frompos, fromnode, stack, dir, frominvname, spos)
elseif fromdef.allow_metadata_inventory_take then
doRemove = fromdef.allow_metadata_inventory_take(frompos, frominvname,spos, stack, fakePlayer)
end
-- stupid lack of continue statements grumble
if doRemove > 0 then
if slotseq_mode == 2 then
local nextpos = spos + 1
if nextpos > frominv:get_size(frominvname) then
nextpos = 1
end
filtmeta:set_int("slotseq_index", nextpos)
set_filter_infotext(data, filtmeta)
end
local item
local count
if all then
count = math.min(stack:get_count(), doRemove)
if filterfor.count and (filterfor.count > 1 or digiline) then
if exmatch_mode ~= 0 and filterfor.count > count then
return false -- not enough, fail
else
-- limit quantity to filter amount
count = math.min(filterfor.count, count)
end
end
else
count = 1
end
if fromtube.remove_items then
-- it could be the entire stack...
item = fromtube.remove_items(frompos, fromnode, stack, dir, count, frominvname, spos)
else
item = stack:take_item(count)
frominv:set_stack(frominvname, spos, stack)
if fromdef.on_metadata_inventory_take then
fromdef.on_metadata_inventory_take(frompos, frominvname, spos, item, fakePlayer)
end
end
local pos = vector.add(frompos, vector.multiply(dir, 1.4))
local start_pos = vector.add(frompos, dir)
local item1 = pipeworks.tube_inject_item(pos, start_pos, dir, item, fakePlayer:get_player_name())
return true-- only fire one item, please
end
end
return false
end
local function punch_filter(data, filtpos, filtnode, msg) local function punch_filter(data, filtpos, filtnode, msg)
local filtmeta = minetest.get_meta(filtpos) local filtmeta = minetest.get_meta(filtpos)
local filtinv = filtmeta:get_inventory() local filtinv = filtmeta:get_inventory()
@ -178,8 +69,6 @@ local function punch_filter(data, filtpos, filtnode, msg)
if not fromdef then return end if not fromdef then return end
local fromtube = fromdef.tube local fromtube = fromdef.tube
local input_special_cases = { local input_special_cases = {
["technic:mv_furnace"] = "dst",
["technic:mv_furnace_active"] = "dst",
["technic:mv_electric_furnace"] = "dst", ["technic:mv_electric_furnace"] = "dst",
["technic:mv_electric_furnace_active"] = "dst", ["technic:mv_electric_furnace_active"] = "dst",
["technic:mv_alloy_furnace"] = "dst", ["technic:mv_alloy_furnace"] = "dst",
@ -194,7 +83,13 @@ local function punch_filter(data, filtpos, filtnode, msg)
["technic:mv_grinder_active"] = "dst", ["technic:mv_grinder_active"] = "dst",
["technic:tool_workshop"] = "src", ["technic:tool_workshop"] = "src",
["technic:mv_freezer"] = "dst", ["technic:mv_freezer"] = "dst",
["technic:mv_freezer_active"] = "dst" ["technic:mv_freezer_active"] = "dst",
["technic:hv_electric_furnace"] = "dst",
["technic:hv_electric_furnace_active"] = "dst",
["technic:hv_compressor"] = "dst",
["technic:hv_compressor_active"] = "dst",
["technic:hv_grinder"] = "dst",
["technic:hv_grinder_active"] = "dst"
} }
-- make sure there's something appropriate to inject the item into -- make sure there's something appropriate to inject the item into
@ -214,7 +109,7 @@ local function punch_filter(data, filtpos, filtnode, msg)
if not (fromtube and fromtube.input_inventory) then return end if not (fromtube and fromtube.input_inventory) then return end
local slotseq_mode local slotseq_mode
local exact_match local exmatch_mode
local filters = {} local filters = {}
if data.digiline then if data.digiline then
@ -251,10 +146,10 @@ local function punch_filter(data, filtpos, filtnode, msg)
local exmatch = msg.exmatch local exmatch = msg.exmatch
local t_exmatch = type(exmatch) local t_exmatch = type(exmatch)
if t_exmatch == "number" and exmatch >= 0 and exmatch <= 1 then if t_exmatch == "number" and (exmatch == 0 or exmatch == 1) then
exact_match = exmatch exmatch_mode = exmatch
elseif t_exmatch == "boolean" then elseif t_exmatch == "boolean" then
exact_match = exmatch and 1 or 0 exmatch_mode = exmatch and 1 or 0
end end
local slotseq_index = msg.slotseq_index local slotseq_index = msg.slotseq_index
@ -271,11 +166,11 @@ local function punch_filter(data, filtpos, filtnode, msg)
filtmeta:set_int("slotseq_mode", slotseq_mode) filtmeta:set_int("slotseq_mode", slotseq_mode)
end end
if exact_match ~= nil then if exmatch_mode ~= nil then
filtmeta:set_int("exmatch_mode", exact_match) filtmeta:set_int("exmatch_mode", exmatch_mode)
end end
if slotseq_mode ~= nil or exact_match ~= nil then if slotseq_mode ~= nil or exmatch_mode ~= nil then
set_filter_formspec(data, filtmeta) set_filter_formspec(data, filtmeta)
end end
@ -313,8 +208,8 @@ local function punch_filter(data, filtpos, filtnode, msg)
slotseq_mode = filtmeta:get_int("slotseq_mode") slotseq_mode = filtmeta:get_int("slotseq_mode")
end end
if exact_match == nil then if exmatch_mode == nil then
exact_match = filtmeta:get_int("exmatch_mode") exmatch_mode = filtmeta:get_int("exmatch_mode")
end end
local frominv local frominv
@ -328,10 +223,120 @@ local function punch_filter(data, filtpos, filtnode, msg)
frominv = frommeta:get_inventory() frominv = frommeta:get_inventory()
end end
if fromtube.before_filter then fromtube.before_filter(frompos) end if fromtube.before_filter then fromtube.before_filter(frompos) end
local function grabAndFire(frominvname, filterfor)
local sposes = {}
if not frominvname or not frominv:get_list(frominvname) then return end
for spos,stack in ipairs(frominv:get_list(frominvname)) do
local matches
if filterfor == "" then
matches = stack:get_name() ~= ""
else
local fname = filterfor.name
local fgroup = filterfor.group
local fwear = filterfor.wear
local fmetadata = filterfor.metadata
matches = (not fname -- If there's a name filter,
or stack:get_name() == fname) -- it must match.
and (not fgroup -- If there's a group filter,
or (type(fgroup) == "string" -- it must be a string
and minetest.get_item_group( -- and it must match.
stack:get_name(), fgroup) ~= 0))
and (not fwear -- If there's a wear filter:
or (type(fwear) == "number" -- If it's a number,
and stack:get_wear() == fwear) -- it must match.
or (type(fwear) == "table" -- If it's a table:
and (not fwear[1] -- If there's a lower bound,
or (type(fwear[1]) == "number" -- it must be a number
and fwear[1] <= stack:get_wear())) -- and it must be <= the actual wear.
and (not fwear[2] -- If there's an upper bound
or (type(fwear[2]) == "number" -- it must be a number
and stack:get_wear() < fwear[2])))) -- and it must be > the actual wear.
-- If the wear filter is of any other type, fail.
and (not fmetadata -- If there's a metadata filter,
or (type(fmetadata) == "string" -- it must be a string
and stack:get_metadata() == fmetadata)) -- and it must match.
end
if matches then table.insert(sposes, spos) end
end
if #sposes == 0 then return false end
if slotseq_mode == 1 then
for i = #sposes, 2, -1 do
local j = math.random(i)
local t = sposes[j]
sposes[j] = sposes[i]
sposes[i] = t
end
elseif slotseq_mode == 2 then
local headpos = filtmeta:get_int("slotseq_index")
table.sort(sposes, function (a, b)
if a >= headpos then
if b < headpos then return true end
else
if b >= headpos then return false end
end
return a < b
end)
end
for _, spos in ipairs(sposes) do
local stack = frominv:get_stack(frominvname, spos)
local doRemove = stack:get_count()
if fromtube.can_remove then
doRemove = fromtube.can_remove(frompos, fromnode, stack, dir, frominvname, spos)
elseif fromdef.allow_metadata_inventory_take then
doRemove = fromdef.allow_metadata_inventory_take(frompos, frominvname,spos, stack, fakePlayer)
end
-- stupid lack of continue statements grumble
if doRemove > 0 then
if slotseq_mode == 2 then
local nextpos = spos + 1
if nextpos > frominv:get_size(frominvname) then
nextpos = 1
end
filtmeta:set_int("slotseq_index", nextpos)
set_filter_infotext(data, filtmeta)
end
local item
local count
if data.stackwise then
count = math.min(stack:get_count(), doRemove)
if filterfor.count and (filterfor.count > 1 or data.digiline) then
if exmatch_mode ~= 0 and filterfor.count > count then
return false -- not enough, fail
else
-- limit quantity to filter amount
count = math.min(filterfor.count, count)
end
end
else
count = 1
end
if fromtube.remove_items then
-- it could be the entire stack...
item = fromtube.remove_items(frompos, fromnode, stack, dir, count, frominvname, spos)
else
item = stack:take_item(count)
frominv:set_stack(frominvname, spos, stack)
if fromdef.on_metadata_inventory_take then
fromdef.on_metadata_inventory_take(frompos, frominvname, spos, item, fakePlayer)
end
end
local pos = vector.add(frompos, vector.multiply(dir, 1.4))
local start_pos = vector.add(frompos, dir)
local item1 = pipeworks.tube_inject_item(pos, start_pos, dir, item, fakePlayer:get_player_name())
return true -- only fire one item, please
end
end
return false
end
for _, frominvname in ipairs(type(fromtube.input_inventory) == "table" and fromtube.input_inventory or {fromtube.input_inventory}) do for _, frominvname in ipairs(type(fromtube.input_inventory) == "table" and fromtube.input_inventory or {fromtube.input_inventory}) do
local done = false local done = false
for _, filterfor in ipairs(filters) do for _, filterfor in ipairs(filters) do
if grabAndFire(data, slotseq_mode, exact_match, filtmeta, frominv, frominvname, frompos, fromnode, filterfor, fromtube, fromdef, dir, fakePlayer, data.stackwise, data.digiline) then if grabAndFire(frominvname, filterfor) then
done = true done = true
break break
end end
@ -344,23 +349,23 @@ end
for _, data in ipairs({ for _, data in ipairs({
{ {
name = "filter", name = "filter",
wise_desc = "Itemwise", wise_desc = S("Itemwise"),
stackwise = false, stackwise = false,
}, },
{ {
name = "mese_filter", name = "mese_filter",
wise_desc = "Stackwise", wise_desc = S("Stackwise"),
stackwise = true, stackwise = true,
}, },
{ -- register even if no digilines { -- register even if no digilines
name = "digiline_filter", name = "digiline_filter",
wise_desc = "Digiline", wise_desc = S("Digiline"),
stackwise = true, stackwise = true,
digiline = true, digiline = true,
}, },
}) do }) do
local node = { local node = {
description = data.wise_desc.." Filter-Injector", description = S("@1 Filter-Injector", data.wise_desc),
tiles = { tiles = {
"pipeworks_"..data.name.."_top.png", "pipeworks_"..data.name.."_top.png",
"pipeworks_"..data.name.."_top.png", "pipeworks_"..data.name.."_top.png",

View File

@ -10,6 +10,7 @@ local DEBUG = false
pipeworks.worldpath = minetest.get_worldpath() pipeworks.worldpath = minetest.get_worldpath()
pipeworks.modpath = minetest.get_modpath("pipeworks") pipeworks.modpath = minetest.get_modpath("pipeworks")
local S = minetest.get_translator("pipeworks")
dofile(pipeworks.modpath.."/default_settings.lua") dofile(pipeworks.modpath.."/default_settings.lua")
-- Read the external config file if it exists. -- Read the external config file if it exists.
@ -45,7 +46,7 @@ pipeworks.liquid_texture = "default_water.png"
pipeworks.button_off = {text="", texture="pipeworks_button_off.png", addopts="false;false;pipeworks_button_interm.png"} pipeworks.button_off = {text="", texture="pipeworks_button_off.png", addopts="false;false;pipeworks_button_interm.png"}
pipeworks.button_on = {text="", texture="pipeworks_button_on.png", addopts="false;false;pipeworks_button_interm.png"} pipeworks.button_on = {text="", texture="pipeworks_button_on.png", addopts="false;false;pipeworks_button_interm.png"}
pipeworks.button_base = "image_button[0,4.3;1,0.6" pipeworks.button_base = "image_button[0,4.3;1,0.6"
pipeworks.button_label = "label[0.9,4.31;Allow splitting incoming stacks from tubes]" pipeworks.button_label = "label[0.9,4.31;"..S("Allow splitting incoming stacks from tubes").."]"
-- Helper functions -- Helper functions
@ -70,8 +71,8 @@ function pipeworks.may_configure(pos, player)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local owner = meta:get_string("owner") local owner = meta:get_string("owner")
if owner ~= "" then -- wielders and filters if owner ~= "" and owner == name then -- wielders and filters
return owner == name return true
end end
return not minetest.is_protected(pos, name) return not minetest.is_protected(pos, name)
end end
@ -149,4 +150,4 @@ end
minetest.register_alias("pipeworks:pipe", "pipeworks:pipe_110000_empty") minetest.register_alias("pipeworks:pipe", "pipeworks:pipe_110000_empty")
print("Pipeworks loaded!") minetest.log("info", "Pipeworks loaded!")

View File

@ -25,7 +25,7 @@ end
-- both optional w/ sensible defaults and fallback to normal allow_* function -- both optional w/ sensible defaults and fallback to normal allow_* function
-- XXX: possibly change insert_object to insert_item -- XXX: possibly change insert_object to insert_item
local adjlist={{x=0,y=0,z=1},{x=0,y=0,z=-1},{x=0,y=1,z=0},{x=0,y=-1,z=0},{x=1,y=0,z=0},{x=-1,y=0,z=0}} local default_adjlist={{x=0,y=0,z=1},{x=0,y=0,z=-1},{x=0,y=1,z=0},{x=0,y=-1,z=0},{x=1,y=0,z=0},{x=-1,y=0,z=0}}
function pipeworks.notvel(tbl, vel) function pipeworks.notvel(tbl, vel)
local tbl2={} local tbl2={}
@ -61,7 +61,7 @@ local crunch_tube = function(pos, cnode, cmeta)
local itemcount = tube_item_count[h] or 0 local itemcount = tube_item_count[h] or 0
if itemcount > max_tube_limit then if itemcount > max_tube_limit then
cmeta:set_string("the_tube_was", minetest.serialize(cnode)) cmeta:set_string("the_tube_was", minetest.serialize(cnode))
print("[Pipeworks] Warning - a tube at "..minetest.pos_to_string(pos).." broke due to too many items ("..itemcount..")") pipeworks.logger("Warning - a tube at "..minetest.pos_to_string(pos).." broke due to too many items ("..itemcount..")")
minetest.swap_node(pos, {name = "pipeworks:broken_tube_1"}) minetest.swap_node(pos, {name = "pipeworks:broken_tube_1"})
pipeworks.scan_for_tube_objects(pos) pipeworks.scan_for_tube_objects(pos)
end end
@ -80,6 +80,9 @@ local function go_next_compat(pos, cnode, cmeta, cycledir, vel, stack, owner)
if minetest.registered_nodes[cnode.name] and minetest.registered_nodes[cnode.name].tube and minetest.registered_nodes[cnode.name].tube.can_go then if minetest.registered_nodes[cnode.name] and minetest.registered_nodes[cnode.name].tube and minetest.registered_nodes[cnode.name].tube.can_go then
can_go = minetest.registered_nodes[cnode.name].tube.can_go(pos, cnode, vel, stack) can_go = minetest.registered_nodes[cnode.name].tube.can_go(pos, cnode, vel, stack)
else else
local adjlist_string = minetest.get_meta(pos):get_string("adjlist")
local adjlist = minetest.deserialize(adjlist_string) or default_adjlist -- backward compat: if not found, use old behavior: all directions
can_go = pipeworks.notvel(adjlist, vel) can_go = pipeworks.notvel(adjlist, vel)
end end
-- can_go() is expected to return an array-like table of candidate offsets. -- can_go() is expected to return an array-like table of candidate offsets.

View File

@ -1,3 +1,4 @@
local S = minetest.get_translator("pipeworks")
if not minetest.get_modpath("auto_tree_tap") and if not minetest.get_modpath("auto_tree_tap") and
minetest.get_modpath("technic") then minetest.get_modpath("technic") then
@ -20,7 +21,7 @@ if not minetest.get_modpath("auto_tree_tap") and
}) })
minetest.register_node(":auto_tree_tap:off", { minetest.register_node(":auto_tree_tap:off", {
description = "Auto-Tap", description = S("Auto-Tap"),
tiles = {"pipeworks_nodebreaker_top_off.png","pipeworks_nodebreaker_bottom_off.png","pipeworks_nodebreaker_side2_off.png","pipeworks_nodebreaker_side1_off.png", tiles = {"pipeworks_nodebreaker_top_off.png","pipeworks_nodebreaker_bottom_off.png","pipeworks_nodebreaker_side2_off.png","pipeworks_nodebreaker_side1_off.png",
"pipeworks_nodebreaker_back.png","pipeworks_nodebreaker_front_off.png"}, "pipeworks_nodebreaker_back.png","pipeworks_nodebreaker_front_off.png"},
is_ground_content = true, is_ground_content = true,

112
locale/pipeworks.fr.tr Normal file
View File

@ -0,0 +1,112 @@
# textdomain: pipeworks
# License: CC-by-SA 4.0
# Author: Louis Royer <4259825-lroyer@users.noreply.gitlab.com>
## digilines interfacing
Channel=Canal
## init
Allow splitting incoming stacks from tubes=Séparer les piles venant des tubes
## autocrafter
Unknown item=Item inconnu
unconfigured Autocrafter: unknown recipe=Autocrafteur non-configuré : recette inconnue
unconfigured Autocrafter=Autocrafteur non-configuré
'@1' Autocrafter (@2)=Autocrafteur de '@1' (@2)
Save=Valider
paused '@1' Autocrafter=Autocrafteur de '@1' en pause
Autocrafter=Autocrafteur
## compat-furnaces
Allow splitting incoming material (not fuel) stacks from tubes=Séparer les piles (sauf le carburant) venant des tubes
## decorative tubes
Airtight steelblock embedded tube=Tube hermétique intégré à un bloc dacier
Airtight panel embedded tube=Tube hermétique intégré à un panneau
## devices
Pump/Intake Module=Module de pompage et dadmission
Valve=Vanne
Decorative grating=Grillage décoratif
Spigot outlet=Sortie de robinet
Airtight Pipe entry/exit=Extrémité de tube hermétique
Flow Sensor=Détecteur de débit
Flow sensor (on)=Détecteur de débit (actif)
empty=vide
@1% full=plein à @1 %
Expansion Tank (@1)=Réservoir dexpansion (@1)
Fluid Storage Tank (@1)=Réservoir de liquides (@1)
Fountainhead=Tête de fontaine
Straight-only Pipe=Tuyau droit
## filter-injector
(slot #@1 next)=(slot suivant : #@1)
@1 Filter-Injector=Filtre-injecteur @1
Sequence slots by Priority=Ordonner par priorité
Sequence slots Randomly=Ordonner aléatoirement
Sequence slots by Rotation=Ordonner en rotation
Exact match - off=Filtrage inactif
Exact match - on=Filtrage actif
Prefer item types:=Items à filtrer :
Itemwise=par item
Stackwise=par piles ditems
Digiline=digiline
## legacy
Auto-Tap=Robinet darbre automatique
## pipes
Pipe Segment=Segment de tuyau
Pipe Segment (legacy)=Segment de tuyau (obsolète)
## routing tubes
Pneumatic tube segment=Segment de tuyau pneumatique
Broken Tube=Tuyau cassé
High Priority Tube Segment=Segment de tuyau haute priorité
Accelerating Pneumatic Tube Segment=Segment de tuyau pneumatique accélérante
Crossing Pneumatic Tube Segment=Intersection de tuyau pneumatique
One way tube=Tuyau unidirectionnel
## signal tubes
Detecting Pneumatic Tube Segment on=Segment de tuyau pneumatique avec détecteur (actif)
Detecting Pneumatic Tube Segment=Segment de tuyau pneumatique avec détecteur
Digiline Detecting Pneumatic Tube Segment=Segment de tuyau pneumatique avec détecteur digiline
Digiline Detecting Tube=Tuyau avec détecteur digiline
Conducting Pneumatic Tube Segment=Segment de tuyau pneumatique conducteur
Conducting Pneumatic Tube Segment on=Segment de tuyau pneumatique conducteur actif
Digiline Conducting Pneumatic Tube Segment=Segment de tuyau pneumatique conducteur digiline
Mesecon and Digiline Conducting Pneumatic Tube Segment=Segment de tuyau pneumatique conducteur mesecon et digiline
Mesecon and Digiline Conducting Pneumatic Tube Segment on=Segment de tuyau pneumatique conducteur mesecon et digiline (actif)
## sorting tubes
Sorting Pneumatic Tube Segment=Segment de tuyau pneumatique triant
Sorting pneumatic tube=Tuyau pneumatique triant
## teleport tube
Receive=Reception
channels are public by default=Les canaux sont publics par défaut
use <player>:<channel> for fully private channels=Utilisez <joueur>:<canal> pour un canal entièrement privé
use <player>;<channel> for private receivers=Utilisez <joueur>;<canal> pour une réception privée
Teleporting Pneumatic Tube Segment=Segment de tuyau pneumatique téléporteur
unconfigured Teleportation Tube=Tuyau téléporteur non-configuré
Sorry, channel '@1' is reserved for exclusive use by @2=Désolé, le canal '@1' est réservé exclusivement à lutilisateur @2.
Sorry, receiving from channel '@1' is reserved for @2=Désolé, la réception depuis le canal '@1' est réservée pour @2.
Teleportation Tube @1 on '@2'=Tuyau de téléportation @1 sur '@2'
## trashcan
Trash Can=Poubelle
## tube registration
Pneumatic tube segment (legacy)=Segment de tuyau pneumatique (obsolète)
## vacuum tubes
Vacuuming Pneumatic Tube Segment=Segment de tuyau pneumatique aspirant
Adjustable Vacuuming Pneumatic Tube Segment=Segment de tuyau pneumatique aspirant réglable
Adjustable Vacuuming Pneumatic Tube Segment (@1m)=Segment de tuyau pneumatique aspirant réglable (@1 m)
## wielder
Node Breaker=Casseur de blocs
Deployer=Poseur de blocs
Dispenser=Distributeur

110
locale/pipeworks.zh_CN.tr Normal file
View File

@ -0,0 +1,110 @@
# textdomain: pipeworks
# License: CC-by-SA 4.0
# Author: pevernow <3450354617@qq.com>
## digilines interfacing
Channel=频道
## init
Allow splitting incoming stacks from tubes=允许从管道中拆分传入堆栈
## autocrafter
Unknown item=通道
unconfigured Autocrafter: unknown recipe=未配置的自动工作台: 未知配方
unconfigured Autocrafter=未配置的自动工作台
'@1' Autocrafter (@2)=自动工作台 '@1' (@2)
Save=保存
paused '@1' Autocrafter=暂停的自动工作台
Autocrafter=自动工作台
## compat-furnaces
Allow splitting incoming material (not fuel) stacks from tubes=允许从管子中分离进来的材料(不是燃料)堆
## decorative tubes
Airtight steelblock embedded tube=密封嵌入式铁块管道
Airtight panel embedded tube=密封嵌入式片状管道
## devices
Pump/Intake Module=泵/进气模块
Valve=阀门
Decorative grating=Decorative grating
Spigot outlet=龙头
Airtight Pipe entry/exit=密闭管进/出
Flow Sensor=流量传感器
Flow sensor (on)=流量传感器(上)
empty=空的
@1% full=满的@1 %
Expansion Tank (@1)=扩展水箱 (@1)
Fluid Storage Tank (@1)=储液罐 (@1)
Fountainhead=源泉
Straight-only Pipe=直管
## filter-injector
(slot #@1 next)=(下一个插槽 : #@1)
@1 Filter-Injector=@1取物器
Sequence slots by Priority=优先顺序排列
Sequence slots Randomly=随机排列时隙
Sequence slots by Rotation=旋转顺序槽
Exact match - off=完全匹配-关闭
Exact match - on=完全匹配-开启
Prefer item types:=偏好物品类型 :
Itemwise=逐项
Stackwise=堆叠方式
Digiline=Digiline
## legacy
Auto-Tap=自动轴阀
## pipes
Pipe Segment=管道
Pipe Segment (legacy)=管道(旧版)
## routing tubes
Pneumatic tube segment=普通管道
Broken Tube=断管
High Priority Tube Segment=高优先级管段
Accelerating Pneumatic Tube Segment=加速管道
Crossing Pneumatic Tube Segment=定向管道
One way tube=单向管
## signal tubes
Detecting Pneumatic Tube Segment on=检测管道(运行中)
Detecting Pneumatic Tube Segment=检测管道
Digiline Detecting Pneumatic Tube Segment=Digiline检测管道
Digiline Detecting Tube=Digiline检测管
Conducting Pneumatic Tube Segment=传导管道
Conducting Pneumatic Tube Segment on=传导管道(运行中)
Digiline Conducting Pneumatic Tube Segment=Digiline传导式气动管道
Mesecon and Digiline Conducting Pneumatic Tube Segment=Mesecon和Digiline传导管道
Mesecon and Digiline Conducting Pneumatic Tube Segment on=Mesecon和Digiline传导管道运行中
## sorting tubes
Sorting Pneumatic Tube Segment=分类管道
Sorting pneumatic tube=分类管道
## teleport tube
Receive=接收
channels are public by default=频道默认为公开
use <player>:<channel> for fully private channels=将<player>:<channel>用于完全私人的频道
use <player>;<channel> for private receivers=使用<player>;<channel>作为私人接收器
Teleporting Pneumatic Tube Segment=传送管道
unconfigured Teleportation Tube=未配置的传送管道
Sorry, channel '@1' is reserved for exclusive use by @2=抱歉,频道‘@1保留供@2专用
Sorry, receiving from channel '@1' is reserved for @2=抱歉,从频道'@1'接收的内容已保留给'@2'
Teleportation Tube @1 on '@2'=传送管'@1'在'@2'上
## trashcan
Trash Can=垃圾箱
## tube registration
Pneumatic tube segment (legacy)=普通管道(旧式)
## vacuum tubes
Vacuuming Pneumatic Tube Segment=拾取管道
Adjustable Vacuuming Pneumatic Tube Segment=高级拾取管道
Adjustable Vacuuming Pneumatic Tube Segment (@1m)=高级拾取管道(@1m)
## wielder
Node Breaker=方块破坏器
Deployer=放置器
Dispenser=投掷器

112
locale/template.txt Normal file
View File

@ -0,0 +1,112 @@
# textdomain: pipeworks
# License: CC-by-SA 4.0
# Author:
## digilines interfacing
Channel=
## init
Allow splitting incoming stacks from tubes=
## autocrafter
Unknown item=
unconfigured Autocrafter: unknown recipe=
unconfigured Autocrafter=
'@1' Autocrafter (@2)=
Save=
paused '@1' Autocrafter=
Autocrafter=
## compat-furnaces
Allow splitting incoming material (not fuel) stacks from tubes=
## decorative tubes
Airtight steelblock embedded tube=
Airtight panel embedded tube=
## devices
Pump/Intake Module=
Valve=
Decorative grating=
Spigot outlet=
Airtight Pipe entry/exit=
Flow Sensor=
Flow sensor (on)=
empty=
@1% full=
Expansion Tank (@1)=
Fluid Storage Tank (@1)=
Fountainhead=
Straight-only Pipe=
## filter-injector
(slot #@1 next)=
@1 Filter-Injector=
Sequence slots by Priority=
Sequence slots Randomly=
Sequence slots by Rotation=
Exact match - off=
Exact match - on=
Prefer item types:=
Itemwise=
Stackwise=
Digiline=
## legacy
Auto-Tap=
## pipes
Pipe Segment=
Pipe Segment (legacy)=
## routing tubes
Pneumatic tube segment=
Broken Tube=
High Priority Tube Segment=
Accelerating Pneumatic Tube Segment=
Crossing Pneumatic Tube Segment=
One way tube=
## signal tubes
Detecting Pneumatic Tube Segment on=
Detecting Pneumatic Tube Segment=
Digiline Detecting Pneumatic Tube Segment=
Digiline Detecting Tube=
Conducting Pneumatic Tube Segment=
Conducting Pneumatic Tube Segment on=
Digiline Conducting Pneumatic Tube Segment=
Mesecon and Digiline Conducting Pneumatic Tube Segment=
Mesecon and Digiline Conducting Pneumatic Tube Segment on=
## sorting tubes
Sorting Pneumatic Tube Segment=
Sorting pneumatic tube=
## teleport tube
Receive=
channels are public by default=
use <player>:<channel> for fully private channels=
use <player>;<channel> for private receivers=
Teleporting Pneumatic Tube Segment=
unconfigured Teleportation Tube=
Sorry, channel '@1' is reserved for exclusive use by @2=
Sorry, receiving from channel '@1' is reserved for @2=
Teleportation Tube @1 on '@2'=
## trashcan
Trash Can=
## tube registration
Pneumatic tube segment (legacy)=
## vacuum tubes
Vacuuming Pneumatic Tube Segment=
Adjustable Vacuuming Pneumatic Tube Segment=
Adjustable Vacuuming Pneumatic Tube Segment (@1m)=
## wielder
Node Breaker=
Deployer=
Dispenser=

View File

@ -13,8 +13,10 @@
-- newport = merge_port_states(state1, state2): just does result = state1 or state2 for every port -- newport = merge_port_states(state1, state2): just does result = state1 or state2 for every port
-- set_port(pos, rule, state): activates/deactivates the mesecons according to the port states -- set_port(pos, rule, state): activates/deactivates the mesecons according to the port states
-- set_port_states(pos, ports): Applies new port states to a Luacontroller at pos -- set_port_states(pos, ports): Applies new port states to a Luacontroller at pos
-- run(pos): runs the code in the controller at pos -- run_inner(pos, code, event): runs code on the controller at pos and event
-- reset_meta(pos, code, errmsg): performs a software-reset, installs new code and prints error messages -- reset_formspec(pos, code, errmsg): installs new code and prints error messages, without resetting LCID
-- reset_meta(pos, code, errmsg): performs a software-reset, installs new code and prints error message
-- run(pos, event): a wrapper for run_inner which gets code & handles errors via reset_meta
-- resetn(pos): performs a hardware reset, turns off all ports -- resetn(pos): performs a hardware reset, turns off all ports
-- --
-- The Sandbox -- The Sandbox
@ -24,7 +26,7 @@
-- use too much memory from the sandbox. -- use too much memory from the sandbox.
-- You can add more functions to the environment -- You can add more functions to the environment
-- (see where local env is defined) -- (see where local env is defined)
-- Something nice to play is is appending minetest.env to it. -- Something nice to play is appending minetest.env to it.
local BASENAME = "pipeworks:lua_tube" local BASENAME = "pipeworks:lua_tube"
@ -77,7 +79,7 @@ local function update_real_port_states(pos, rule_name, new_state)
if rule_name.x == nil then if rule_name.x == nil then
for _, rname in ipairs(rule_name) do for _, rname in ipairs(rule_name) do
local port = pos_to_side[rname.x + (2 * rname.y) + (3 * rname.z) + 4] local port = pos_to_side[rname.x + (2 * rname.y) + (3 * rname.z) + 4]
L[port] = (newstate == "on") and 1 or 0 L[port] = (new_state == "on") and 1 or 0
end end
else else
local port = pos_to_side[rule_name.x + (2 * rule_name.y) + (3 * rule_name.z) + 4] local port = pos_to_side[rule_name.x + (2 * rule_name.y) + (3 * rule_name.z) + 4]
@ -168,7 +170,7 @@ local function set_port_states(pos, ports)
-- Solution / Workaround: -- Solution / Workaround:
-- Remember which output was turned off and ignore next "off" event. -- Remember which output was turned off and ignore next "off" event.
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local ign = minetest.deserialize(meta:get_string("ignore_offevents")) or {} local ign = minetest.deserialize(meta:get_string("ignore_offevents"), true) or {}
if ports.red and not vports.red and not mesecon.is_powered(pos, rules.red) then ign.red = true end if ports.red and not vports.red and not mesecon.is_powered(pos, rules.red) then ign.red = true end
if ports.blue and not vports.blue and not mesecon.is_powered(pos, rules.blue) then ign.blue = true end if ports.blue and not vports.blue and not mesecon.is_powered(pos, rules.blue) then ign.blue = true end
if ports.yellow and not vports.yellow and not mesecon.is_powered(pos, rules.yellow) then ign.yellow = true end if ports.yellow and not vports.yellow and not mesecon.is_powered(pos, rules.yellow) then ign.yellow = true end
@ -214,7 +216,7 @@ end
local function ignore_event(event, meta) local function ignore_event(event, meta)
if event.type ~= "off" then return false end if event.type ~= "off" then return false end
local ignore_offevents = minetest.deserialize(meta:get_string("ignore_offevents")) or {} local ignore_offevents = minetest.deserialize(meta:get_string("ignore_offevents"), true) or {}
if ignore_offevents[event.pin.name] then if ignore_offevents[event.pin.name] then
ignore_offevents[event.pin.name] = nil ignore_offevents[event.pin.name] = nil
meta:set_string("ignore_offevents", minetest.serialize(ignore_offevents)) meta:set_string("ignore_offevents", minetest.serialize(ignore_offevents))
@ -227,7 +229,11 @@ end
------------------------- -------------------------
local function safe_print(param) local function safe_print(param)
local string_meta = getmetatable("")
local sandbox = string_meta.__index
string_meta.__index = string -- Leave string sandbox temporarily
print(dump(param)) print(dump(param))
string_meta.__index = sandbox -- Restore string sandbox
end end
local function safe_date() local function safe_date()
@ -267,6 +273,7 @@ local function remove_functions(x)
local seen = {} local seen = {}
local function rfuncs(x) local function rfuncs(x)
if x == nil then return end
if seen[x] then return end if seen[x] then return end
seen[x] = true seen[x] = true
if type(x) ~= "table" then return end if type(x) ~= "table" then return end
@ -290,49 +297,174 @@ local function remove_functions(x)
return x return x
end end
local function get_interrupt(pos) -- The setting affects API so is not intended to be changeable at runtime
-- iid = interrupt id local get_interrupt
local function interrupt(time, iid) if mesecon.setting("luacontroller_lightweight_interrupts", false) then
if type(time) ~= "number" then return end -- use node timer
local luac_id = minetest.get_meta(pos):get_int("luac_id") get_interrupt = function(pos, itbl, send_warning)
mesecon.queue:add_action(pos, "pipeworks:lc_tube_interrupt", {luac_id, iid}, time, iid, 1) return (function(time, iid)
if type(time) ~= "number" then error("Delay must be a number") end
if iid ~= nil then send_warning("Interrupt IDs are disabled on this server") end
table.insert(itbl, function() minetest.get_node_timer(pos):start(time) end)
end)
end
else
-- use global action queue
-- itbl: Flat table of functions to run after sandbox cleanup, used to prevent various security hazards
get_interrupt = function(pos, itbl, send_warning)
-- iid = interrupt id
local function interrupt(time, iid)
-- NOTE: This runs within string metatable sandbox, so don't *rely* on anything of the form (""):y
-- Hence the values get moved out. Should take less time than original, so totally compatible
if type(time) ~= "number" then error("Delay must be a number") end
table.insert(itbl, function ()
-- Outside string metatable sandbox, can safely run this now
local luac_id = minetest.get_meta(pos):get_int("luac_id")
-- Check if IID is dodgy, so you can't use interrupts to store an infinite amount of data.
-- Note that this is safe from alter-after-free because this code gets run after the sandbox has ended.
-- This runs outside of the timer and *shouldn't* harm perf. unless dodgy data is being sent in the first place
iid = remove_functions(iid)
local msg_ser = minetest.serialize(iid)
if #msg_ser <= mesecon.setting("luacontroller_interruptid_maxlen", 256) then
mesecon.queue:add_action(pos, "pipeworks:lc_tube_interrupt", {luac_id, iid}, time, iid, 1)
else
send_warning("An interrupt ID was too large!")
end
end)
end
return interrupt
end
end
-- Given a message object passed to digiline_send, clean it up into a form
-- which is safe to transmit over the network and compute its "cost" (a very
-- rough estimate of its memory usage).
--
-- The cleaning comprises the following:
-- 1. Functions (and userdata, though user scripts ought not to get hold of
-- those in the first place) are removed, because they break the model of
-- Digilines as a network that carries basic data, and they could exfiltrate
-- references to mutable objects from one Luacontroller to another, allowing
-- inappropriate high-bandwidth, no-wires communication.
-- 2. Tables are duplicated because, being mutable, they could otherwise be
-- modified after the send is complete in order to change what data arrives
-- at the recipient, perhaps in violation of the previous cleaning rule or
-- in violation of the message size limit.
--
-- The cost indication is only approximate; its not a perfect measurement of
-- the number of bytes of memory used by the message object.
--
-- Parameters:
-- msg -- the message to clean
-- back_references -- for internal use only; do not provide
--
-- Returns:
-- 1. The cleaned object.
-- 2. The approximate cost of the object.
local function clean_and_weigh_digiline_message(msg, back_references)
local t = type(msg)
if t == "string" then
-- Strings are immutable so can be passed by reference, and cost their
-- length plus the size of the Lua object header (24 bytes on a 64-bit
-- platform) plus one byte for the NUL terminator.
return msg, #msg + 25
elseif t == "number" then
-- Numbers are passed by value so need not be touched, and cost 8 bytes
-- as all numbers in Lua are doubles.
return msg, 8
elseif t == "boolean" then
-- Booleans are passed by value so need not be touched, and cost 1
-- byte.
return msg, 1
elseif t == "table" then
-- Tables are duplicated. Check if this table has been seen before
-- (self-referential or shared table); if so, reuse the cleaned value
-- of the previous occurrence, maintaining table topology and avoiding
-- infinite recursion, and charge zero bytes for this as the object has
-- already been counted.
back_references = back_references or {}
local bref = back_references[msg]
if bref then
return bref, 0
end
-- Construct a new table by cleaning all the keys and values and adding
-- up their costs, plus 8 bytes as a rough estimate of table overhead.
local cost = 8
local ret = {}
back_references[msg] = ret
for k, v in pairs(msg) do
local k_cost, v_cost
k, k_cost = clean_and_weigh_digiline_message(k, back_references)
v, v_cost = clean_and_weigh_digiline_message(v, back_references)
if k ~= nil and v ~= nil then
-- Only include an element if its key and value are of legal
-- types.
ret[k] = v
end
-- If we only counted the cost of a table element when we actually
-- used it, we would be vulnerable to the following attack:
-- 1. Construct a huge table (too large to pass the cost limit).
-- 2. Insert it somewhere in a table, with a function as a key.
-- 3. Insert it somewhere in another table, with a number as a key.
-- 4. The first occurrence doesnt pay the cost because functions
-- are stripped and therefore the element is dropped.
-- 5. The second occurrence doesnt pay the cost because its in
-- back_references.
-- By counting the costs regardless of whether the objects will be
-- included, we avoid this attack; it may overestimate the cost of
-- some messages, but only those that wont be delivered intact
-- anyway because they contain illegal object types.
cost = cost + k_cost + v_cost
end
return ret, cost
else
return nil, 0
end end
return interrupt
end end
local function get_digiline_send(pos) -- itbl: Flat table of functions to run after sandbox cleanup, used to prevent various security hazards
if not digiline then return end local function get_digiline_send(pos, itbl, send_warning)
if not minetest.global_exists("digilines") then return end
local chan_maxlen = mesecon.setting("luacontroller_digiline_channel_maxlen", 256)
local maxlen = mesecon.setting("luacontroller_digiline_maxlen", 50000)
return function(channel, msg) return function(channel, msg)
-- NOTE: This runs within string metatable sandbox, so don't *rely* on anything of the form (""):y
-- or via anything that could.
-- Make sure channel is string, number or boolean -- Make sure channel is string, number or boolean
if (type(channel) ~= "string" and type(channel) ~= "number" and type(channel) ~= "boolean") then if type(channel) == "string" then
if #channel > chan_maxlen then
send_warning("Channel string too long.")
return false
end
elseif (type(channel) ~= "string" and type(channel) ~= "number" and type(channel) ~= "boolean") then
send_warning("Channel must be string, number or boolean.")
return false return false
end end
-- It is technically possible to send functions over the wire since local msg_cost
-- the high performance impact of stripping those from the data has msg, msg_cost = clean_and_weigh_digiline_message(msg)
-- been decided to not be worth the added realism. if msg == nil or msg_cost > maxlen then
-- Make sure serialized version of the data is not insanely long to send_warning("Message was too complex, or contained invalid data.")
-- prevent DoS-like attacks
local msg_ser = minetest.serialize(msg)
if #msg_ser > mesecon.setting("luacontroller_digiline_maxlen", 50000) then
return false return false
end end
minetest.after(0, function() table.insert(itbl, function ()
digilines.receptor_send(pos, digiline_rules_luatube, channel, msg) -- Runs outside of string metatable sandbox
local luac_id = minetest.get_meta(pos):get_int("luac_id")
mesecon.queue:add_action(pos, "pipeworks:lt_digiline_relay", {channel, luac_id, msg})
end) end)
return true return true
end end
end end
local safe_globals = { local safe_globals = {
-- Don't add pcall/xpcall unless willing to deal with the consequences (unless very careful, incredibly likely to allow killing server indirectly)
"assert", "error", "ipairs", "next", "pairs", "select", "assert", "error", "ipairs", "next", "pairs", "select",
"tonumber", "tostring", "type", "unpack", "_VERSION" "tonumber", "tostring", "type", "unpack", "_VERSION"
} }
local function create_environment(pos, mem, event) local function create_environment(pos, mem, event, itbl, send_warning)
-- Make sure the tube hasn't broken. -- Make sure the tube hasn't broken.
local vports = minetest.registered_nodes[minetest.get_node(pos).name].virtual_portstates local vports = minetest.registered_nodes[minetest.get_node(pos).name].virtual_portstates
if not vports then return {} end if not vports then return {} end
@ -352,8 +484,8 @@ local function create_environment(pos, mem, event)
heat = mesecon.get_heat(pos), heat = mesecon.get_heat(pos),
heat_max = mesecon.setting("overheat_max", 20), heat_max = mesecon.setting("overheat_max", 20),
print = safe_print, print = safe_print,
interrupt = get_interrupt(pos), interrupt = get_interrupt(pos, itbl, send_warning),
digiline_send = get_digiline_send(pos), digiline_send = get_digiline_send(pos, itbl, send_warning),
string = { string = {
byte = string.byte, byte = string.byte,
char = string.char, char = string.char,
@ -441,10 +573,11 @@ local function create_sandbox(code, env)
jit.off(f, true) jit.off(f, true)
end end
local maxevents = mesecon.setting("luacontroller_maxevents", 10000)
return function(...) return function(...)
-- NOTE: This runs within string metatable sandbox, so the setting's been moved out for safety
-- Use instruction counter to stop execution -- Use instruction counter to stop execution
-- after luacontroller_maxevents -- after luacontroller_maxevents
local maxevents = mesecon.setting("luacontroller_maxevents", 10000)
debug.sethook(timeout, "", maxevents) debug.sethook(timeout, "", maxevents)
local ok, ret = pcall(f, ...) local ok, ret = pcall(f, ...)
debug.sethook() -- Clear hook debug.sethook() -- Clear hook
@ -455,7 +588,7 @@ end
local function load_memory(meta) local function load_memory(meta)
return minetest.deserialize(meta:get_string("lc_memory")) or {} return minetest.deserialize(meta:get_string("lc_memory"), true) or {}
end end
@ -465,6 +598,7 @@ local function save_memory(pos, meta, mem)
if (#memstring <= memsize_max) then if (#memstring <= memsize_max) then
meta:set_string("lc_memory", memstring) meta:set_string("lc_memory", memstring)
meta:mark_as_private("lc_memory")
else else
print("Error: lua_tube memory overflow. "..memsize_max.." bytes available, " print("Error: lua_tube memory overflow. "..memsize_max.." bytes available, "
..#memstring.." required. Controller overheats.") ..#memstring.." required. Controller overheats.")
@ -472,26 +606,42 @@ local function save_memory(pos, meta, mem)
end end
end end
-- Returns success (boolean), errmsg (string), retval(any, return value of the user supplied code)
local function run(pos, event) -- run (as opposed to run_inner) is responsible for setting up meta according to this output
local function run_inner(pos, code, event)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
if overheat(pos) then return end -- Note: These return success, presumably to avoid changing LC ID.
if ignore_event(event, meta) then return end if overheat(pos) then return true, "", nil end
if ignore_event(event, meta) then return true, "", nil end
-- Load code & mem from meta -- Load code & mem from meta
local mem = load_memory(meta) local mem = load_memory(meta)
local code = meta:get_string("code") local code = meta:get_string("code")
-- 'Last warning' label.
local warning = ""
local function send_warning(str)
warning = "Warning: " .. str
end
-- Create environment -- Create environment
local env = create_environment(pos, mem, event) local itbl = {}
local env = create_environment(pos, mem, event, itbl, send_warning)
-- Create the sandbox and execute code -- Create the sandbox and execute code
local f, msg = create_sandbox(code, env) local f, msg = create_sandbox(code, env)
if not f then return false, msg end if not f then return false, msg, nil end
local succ, msg = pcall(f) -- Start string true sandboxing
if not succ then return false, msg end local onetruestring = getmetatable("")
-- If a string sandbox is already up yet inconsistent, something is very wrong
assert(onetruestring.__index == string)
onetruestring.__index = env.string
local success, msg = pcall(f)
onetruestring.__index = string
-- End string true sandboxing
if not success then return false, msg, nil end
if type(env.port) ~= "table" then if type(env.port) ~= "table" then
return false, "Ports set are invalid." return false, "Ports set are invalid.", nil
end end
-- Actually set the ports -- Actually set the ports
@ -500,35 +650,81 @@ local function run(pos, event)
-- Save memory. This may burn the luacontroller if a memory overflow occurs. -- Save memory. This may burn the luacontroller if a memory overflow occurs.
save_memory(pos, meta, env.mem) save_memory(pos, meta, env.mem)
return succ, msg -- Execute deferred tasks
for _, v in ipairs(itbl) do
local failure = v()
if failure then
return false, failure, nil
end
end
return true, warning, msg
end end
mesecon.queue:add_function("pipeworks:lc_tube_interrupt", function (pos, luac_id, iid) local function reset_formspec(meta, code, errmsg)
-- There is no lua_tube anymore / it has been reprogrammed / replaced / burnt meta:set_string("code", code)
if (minetest.get_meta(pos):get_int("luac_id") ~= luac_id) then return end meta:mark_as_private("code")
if (minetest.registered_nodes[minetest.get_node(pos).name].is_burnt) then return end code = minetest.formspec_escape(code or "")
run(pos, {type = "interrupt", iid = iid}) errmsg = minetest.formspec_escape(tostring(errmsg or ""))
end) meta:set_string("formspec", "size[12,10]"
.."background[-0.2,-0.25;12.4,10.75;jeija_luac_background.png]"
.."label[0.1,8.3;"..errmsg.."]"
.."textarea[0.2,0.2;12.2,9.5;code;;"..code.."]"
.."image_button[4.75,8.75;2.5,1;jeija_luac_runbutton.png;program;]"
.."image_button_exit[11.72,-0.25;0.425,0.4;jeija_close_window.png;exit;]"
)
end
local function reset_meta(pos, code, errmsg) local function reset_meta(pos, code, errmsg)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
meta:set_string("code", code) reset_formspec(meta, code, errmsg)
code = minetest.formspec_escape(code or "")
errmsg = minetest.formspec_escape(tostring(errmsg or ""))
meta:set_string("formspec", "size[12,10]"..
"background[-0.2,-0.25;12.4,10.75;jeija_luac_background.png]"..
"textarea[0.2,0.2;12.2,9.5;code;;"..code.."]"..
"image_button[4.75,8.75;2.5,1;jeija_luac_runbutton.png;program;]"..
"image_button_exit[11.72,-0.25;0.425,0.4;jeija_close_window.png;exit;]"..
"label[0.1,9;"..errmsg.."]")
meta:set_int("luac_id", math.random(1, 65535)) meta:set_int("luac_id", math.random(1, 65535))
end end
-- Wraps run_inner with LC-reset-on-error
local function run(pos, event)
local meta = minetest.get_meta(pos)
local code = meta:get_string("code")
local ok, errmsg, retval = run_inner(pos, code, event)
if not ok then
reset_meta(pos, code, errmsg)
else
reset_formspec(meta, code, errmsg)
end
return ok, errmsg, retval
end
local function reset(pos) local function reset(pos)
set_port_states(pos, {red = false, blue = false, yellow = false, set_port_states(pos, {red = false, blue = false, yellow = false,
green = false, black = false, white = false}) green = false, black = false, white = false})
end end
local function node_timer(pos)
if minetest.registered_nodes[minetest.get_node(pos).name].is_burnt then
return false
end
run(pos, {type="interrupt"})
return false
end
-----------------------
-- A.Queue callbacks --
-----------------------
mesecon.queue:add_function("pipeworks:lc_tube_interrupt", function (pos, luac_id, iid)
-- There is no lua_tube anymore / it has been reprogrammed / replaced / burnt
if (minetest.get_meta(pos):get_int("luac_id") ~= luac_id) then return end
if (minetest.registered_nodes[minetest.get_node(pos).name].is_burnt) then return end
run(pos, {type="interrupt", iid = iid})
end)
mesecon.queue:add_function("pipeworks:lt_digiline_relay", function (pos, channel, luac_id, msg)
if not digiline then return end
-- This check is only really necessary because in case of server crash, old actions can be thrown into the future
if (minetest.get_meta(pos):get_int("luac_id") ~= luac_id) then return end
if (minetest.registered_nodes[minetest.get_node(pos).name].is_burnt) then return end
-- The actual work
digiline:receptor_send(pos, digiline_rules_luatube, channel, msg)
end)
----------------------- -----------------------
-- Node Registration -- -- Node Registration --
@ -558,6 +754,7 @@ local digiline = {
receptor = {}, receptor = {},
effector = { effector = {
action = function(pos, node, channel, msg) action = function(pos, node, channel, msg)
msg = clean_and_weigh_digiline_message(msg)
run(pos, {type = "digiline", channel = channel, msg = msg}) run(pos, {type = "digiline", channel = channel, msg = msg})
end end
}, },
@ -565,6 +762,18 @@ local digiline = {
rules = pipeworks.digilines_rules rules = pipeworks.digilines_rules
}, },
} }
local function get_program(pos)
local meta = minetest.get_meta(pos)
return meta:get_string("code")
end
local function set_program(pos, code)
reset(pos)
reset_meta(pos, code)
return run(pos, {type="program"})
end
local function on_receive_fields(pos, form_name, fields, sender) local function on_receive_fields(pos, form_name, fields, sender)
if not fields.program then if not fields.program then
return return
@ -574,12 +783,10 @@ local function on_receive_fields(pos, form_name, fields, sender)
minetest.record_protection_violation(pos, name) minetest.record_protection_violation(pos, name)
return return
end end
reset(pos) local ok, err = set_program(pos, fields.code)
reset_meta(pos, fields.code) if not ok then
local succ, err = run(pos, {type="program"}) -- it's not an error from the server perspective
if not succ then minetest.log("action", "Lua controller programming error: " .. tostring(err))
print(err)
reset_meta(pos, fields.code, err)
end end
end end
@ -715,7 +922,11 @@ for white = 0, 1 do
receptor = { receptor = {
state = mesecon.state.on, state = mesecon.state.on,
rules = output_rules[cid] rules = output_rules[cid]
} },
luacontroller = {
get_program = get_program,
set_program = set_program,
},
} }
minetest.register_node(node_name, { minetest.register_node(node_name, {
@ -723,6 +934,7 @@ for white = 0, 1 do
drawtype = "nodebox", drawtype = "nodebox",
tiles = tiles, tiles = tiles,
paramtype = "light", paramtype = "light",
is_ground_content = false,
groups = groups, groups = groups,
drop = BASENAME.."000000", drop = BASENAME.."000000",
sunlight_propagates = true, sunlight_propagates = true,
@ -749,6 +961,7 @@ for white = 0, 1 do
pipeworks.after_dig(pos, node) pipeworks.after_dig(pos, node)
end, end,
is_luacontroller = true, is_luacontroller = true,
on_timer = node_timer,
tubelike = 1, tubelike = 1,
tube = { tube = {
connect_sides = {front = 1, back = 1, left = 1, right = 1, top = 1, bottom = 1}, connect_sides = {front = 1, back = 1, left = 1, right = 1, top = 1, bottom = 1},
@ -762,7 +975,7 @@ for white = 0, 1 do
break break
end end
end end
local succ, msg = run(pos, { local succ, _, msg = run(pos, {
type = "item", type = "item",
pin = src, pin = src,
itemstring = stack:to_string(), itemstring = stack:to_string(),
@ -785,14 +998,6 @@ for white = 0, 1 do
minetest.swap_node(pos, {name = "pipeworks:broken_tube_1"}) minetest.swap_node(pos, {name = "pipeworks:broken_tube_1"})
pipeworks.scan_for_tube_objects(pos) pipeworks.scan_for_tube_objects(pos)
end, end,
on_blast = function(pos, intensity)
if not intensity or intensity > 1 + 3^0.5 then
minetest.remove_node(pos)
return {string.format("%s_%s", name, dropname)}
end
minetest.swap_node(pos, {name = "pipeworks:broken_tube_1"})
pipeworks.scan_for_tube_objects(pos)
end,
}) })
end end
end end
@ -836,6 +1041,7 @@ minetest.register_node(BASENAME .. "_burnt", {
tiles = tiles_burnt, tiles = tiles_burnt,
is_burnt = true, is_burnt = true,
paramtype = "light", paramtype = "light",
is_ground_content = false,
groups = {snappy = 3, tube = 1, tubedevice = 1, not_in_creative_inventory=1}, groups = {snappy = 3, tube = 1, tubedevice = 1, not_in_creative_inventory=1},
drop = BASENAME.."000000", drop = BASENAME.."000000",
sunlight_propagates = true, sunlight_propagates = true,

View File

@ -340,6 +340,8 @@ local move_entities_globalstep_part2 = function(dtime)
entity._velocity = master_entity:get_velocity() entity._velocity = master_entity:get_velocity()
entity._acceleration = master_entity:get_acceleration() entity._acceleration = master_entity:get_acceleration()
else else
entity._velocity = entity._velocity or vector.new(0,0,0)
entity._acceleration = entity._acceleration or vector.new(0,0,0)
entity._pos = vector.add(vector.add( entity._pos = vector.add(vector.add(
entity._pos, entity._pos,
vector.multiply(entity._velocity, dtime)), vector.multiply(entity._velocity, dtime)),

View File

@ -1 +1,5 @@
name = pipeworks name = pipeworks
description = This mod uses mesh nodes and nodeboxes to supply a complete set of 3D pipes and tubes, along with devices that work with them.
depends = default, basic_materials, screwdriver
optional_depends = mesecons, mesecons_mvps, digilines, signs_lib
min_minetest_version = 5.2.0

View File

@ -1,4 +1,5 @@
-- This file supplies the steel pipes -- This file supplies the steel pipes
local S = minetest.get_translator("pipeworks")
local REGISTER_COMPATIBILITY = true local REGISTER_COMPATIBILITY = true
@ -35,11 +36,11 @@ for index, connects in ipairs(cconnects) do
end end
local pgroups = {snappy = 3, pipe = 1, not_in_creative_inventory = 1} local pgroups = {snappy = 3, pipe = 1, not_in_creative_inventory = 1}
local pipedesc = "Pipe segement".." "..dump(connects).."... You hacker, you." local pipedesc = S("Pipe Segment").." "..dump(connects)
if #connects == 0 then if #connects == 0 then
pgroups = {snappy = 3, tube = 1} pgroups = {snappy = 3, tube = 1}
pipedesc = "Pipe segment" pipedesc = S("Pipe Segment")
end end
local outimg_e = { "pipeworks_pipe_plain.png" } local outimg_e = { "pipeworks_pipe_plain.png" }
@ -140,7 +141,7 @@ if REGISTER_COMPATIBILITY then
drawtype = "airlike", drawtype = "airlike",
sunlight_propagates = true, sunlight_propagates = true,
paramtype = "light", paramtype = "light",
description = "Pipe Segment (legacy)", description = S("Pipe Segment (legacy)"),
groups = {not_in_creative_inventory = 1, pipe_to_update = 1}, groups = {not_in_creative_inventory = 1, pipe_to_update = 1},
drop = "pipeworks:pipe_1_empty", drop = "pipeworks:pipe_1_empty",
after_place_node = function(pos) after_place_node = function(pos)

View File

@ -1,6 +1,6 @@
local S = minetest.get_translator("pipeworks")
-- the default tube and default textures -- the default tube and default textures
pipeworks.register_tube("pipeworks:tube", "Pneumatic tube segment") pipeworks.register_tube("pipeworks:tube", S("Pneumatic tube segment"))
minetest.register_craft( { minetest.register_craft( {
output = "pipeworks:tube_1 6", output = "pipeworks:tube_1 6",
recipe = { recipe = {
@ -13,7 +13,7 @@ minetest.register_craft( {
local nodecolor = 0xffff3030 local nodecolor = 0xffff3030
pipeworks.register_tube("pipeworks:broken_tube", { pipeworks.register_tube("pipeworks:broken_tube", {
description = "Broken Tube (you hacker you)", description = S("Broken Tube"),
plain = { { name = "pipeworks_broken_tube_plain.png", backface_culling = false, color = nodecolor } }, plain = { { name = "pipeworks_broken_tube_plain.png", backface_culling = false, color = nodecolor } },
noctr = { { name = "pipeworks_broken_tube_plain.png", backface_culling = false, color = nodecolor } }, noctr = { { name = "pipeworks_broken_tube_plain.png", backface_culling = false, color = nodecolor } },
ends = { { name = "pipeworks_broken_tube_end.png", color = nodecolor } }, ends = { { name = "pipeworks_broken_tube_end.png", color = nodecolor } },
@ -35,7 +35,7 @@ pipeworks.register_tube("pipeworks:broken_tube", {
local itemstack = puncher:get_wielded_item() local itemstack = puncher:get_wielded_item()
local wieldname = itemstack:get_name() local wieldname = itemstack:get_name()
local playername = puncher:get_player_name() local playername = puncher:get_player_name()
print("[Pipeworks] "..playername.." struck a broken tube at "..minetest.pos_to_string(pos)) local log_msg = playername.." struck a broken tube at "..minetest.pos_to_string(pos).."\n"
if wieldname == "anvil:hammer" if wieldname == "anvil:hammer"
or wieldname == "cottages:hammer" or wieldname == "cottages:hammer"
or wieldname == "glooptest:hammer_steel" or wieldname == "glooptest:hammer_steel"
@ -47,17 +47,17 @@ pipeworks.register_tube("pipeworks:broken_tube", {
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local was_node = minetest.deserialize(meta:get_string("the_tube_was")) local was_node = minetest.deserialize(meta:get_string("the_tube_was"))
if was_node and was_node ~= "" then if was_node and was_node ~= "" then
print(" with "..wieldname.." to repair it.") pipeworks.logger(log_msg.." with "..wieldname.." to repair it.")
minetest.swap_node(pos, { name = was_node.name, param2 = was_node.param2 }) minetest.swap_node(pos, { name = was_node.name, param2 = was_node.param2 })
pipeworks.scan_for_tube_objects(pos) pipeworks.scan_for_tube_objects(pos)
itemstack:add_wear(1000) itemstack:add_wear(1000)
puncher:set_wielded_item(itemstack) puncher:set_wielded_item(itemstack)
return itemstack return itemstack
else else
print(" but it can't be repaired.") pipeworks.logger(log_msg.." but it can't be repaired.")
end end
else else
print(" with "..wieldname.." but that tool is too weak.") pipeworks.logger(log_msg.." with "..wieldname.." but that tool is too weak.")
end end
end end
} }
@ -69,7 +69,7 @@ pipeworks.register_tube("pipeworks:broken_tube", {
if pipeworks.enable_priority_tube then if pipeworks.enable_priority_tube then
local color = "#ff3030:128" local color = "#ff3030:128"
pipeworks.register_tube("pipeworks:priority_tube", { pipeworks.register_tube("pipeworks:priority_tube", {
description = "High Priority Tube Segment", description = S("High Priority Tube Segment"),
inventory_image = "pipeworks_tube_inv.png^[colorize:" .. color, inventory_image = "pipeworks_tube_inv.png^[colorize:" .. color,
plain = { { name = "pipeworks_tube_plain.png", color = nodecolor } }, plain = { { name = "pipeworks_tube_plain.png", color = nodecolor } },
noctr = { { name = "pipeworks_tube_noctr.png", color = nodecolor } }, noctr = { { name = "pipeworks_tube_noctr.png", color = nodecolor } },
@ -91,7 +91,7 @@ end
if pipeworks.enable_accelerator_tube then if pipeworks.enable_accelerator_tube then
pipeworks.register_tube("pipeworks:accelerator_tube", { pipeworks.register_tube("pipeworks:accelerator_tube", {
description = "Accelerating Pneumatic Tube Segment", description = S("Accelerating Pneumatic Tube Segment"),
inventory_image = "pipeworks_accelerator_tube_inv.png", inventory_image = "pipeworks_accelerator_tube_inv.png",
plain = { "pipeworks_accelerator_tube_plain.png" }, plain = { "pipeworks_accelerator_tube_plain.png" },
noctr = { "pipeworks_accelerator_tube_noctr.png" }, noctr = { "pipeworks_accelerator_tube_noctr.png" },
@ -116,7 +116,7 @@ end
if pipeworks.enable_crossing_tube then if pipeworks.enable_crossing_tube then
pipeworks.register_tube("pipeworks:crossing_tube", { pipeworks.register_tube("pipeworks:crossing_tube", {
description = "Crossing Pneumatic Tube Segment", description = S("Crossing Pneumatic Tube Segment"),
inventory_image = "pipeworks_crossing_tube_inv.png", inventory_image = "pipeworks_crossing_tube_inv.png",
plain = { "pipeworks_crossing_tube_plain.png" }, plain = { "pipeworks_crossing_tube_plain.png" },
noctr = { "pipeworks_crossing_tube_noctr.png" }, noctr = { "pipeworks_crossing_tube_noctr.png" },
@ -138,7 +138,7 @@ end
if pipeworks.enable_one_way_tube then if pipeworks.enable_one_way_tube then
minetest.register_node("pipeworks:one_way_tube", { minetest.register_node("pipeworks:one_way_tube", {
description = "One way tube", description = S("One way tube"),
tiles = {"pipeworks_one_way_tube_top.png", "pipeworks_one_way_tube_top.png", "pipeworks_one_way_tube_output.png", tiles = {"pipeworks_one_way_tube_top.png", "pipeworks_one_way_tube_top.png", "pipeworks_one_way_tube_output.png",
"pipeworks_one_way_tube_input.png", "pipeworks_one_way_tube_side.png", "pipeworks_one_way_tube_top.png"}, "pipeworks_one_way_tube_input.png", "pipeworks_one_way_tube_side.png", "pipeworks_one_way_tube_top.png"},
paramtype2 = "facedir", paramtype2 = "facedir",

View File

@ -1,7 +1,8 @@
local S = minetest.get_translator("pipeworks")
if pipeworks.enable_detector_tube then if pipeworks.enable_detector_tube then
local detector_tube_step = 5 * tonumber(minetest.settings:get("dedicated_server_step")) local detector_tube_step = 5 * tonumber(minetest.settings:get("dedicated_server_step"))
pipeworks.register_tube("pipeworks:detector_tube_on", { pipeworks.register_tube("pipeworks:detector_tube_on", {
description = "Detecting Pneumatic Tube Segment on (you hacker you)", description = S("Detecting Pneumatic Tube Segment on"),
inventory_image = "pipeworks_detector_tube_inv.png", inventory_image = "pipeworks_detector_tube_inv.png",
plain = { "pipeworks_detector_tube_plain.png" }, plain = { "pipeworks_detector_tube_plain.png" },
node_def = { node_def = {
@ -40,7 +41,7 @@ if pipeworks.enable_detector_tube then
}, },
}) })
pipeworks.register_tube("pipeworks:detector_tube_off", { pipeworks.register_tube("pipeworks:detector_tube_off", {
description = "Detecting Pneumatic Tube Segment", description = S("Detecting Pneumatic Tube Segment"),
inventory_image = "pipeworks_detector_tube_inv.png", inventory_image = "pipeworks_detector_tube_inv.png",
plain = { "pipeworks_detector_tube_plain.png" }, plain = { "pipeworks_detector_tube_plain.png" },
node_def = { node_def = {
@ -70,7 +71,7 @@ end
local digiline_enabled = minetest.get_modpath("digilines") ~= nil local digiline_enabled = minetest.get_modpath("digilines") ~= nil
if digiline_enabled and pipeworks.enable_digiline_detector_tube then if digiline_enabled and pipeworks.enable_digiline_detector_tube then
pipeworks.register_tube("pipeworks:digiline_detector_tube", { pipeworks.register_tube("pipeworks:digiline_detector_tube", {
description = "Digiline Detecting Pneumatic Tube Segment", description = S("Digiline Detecting Pneumatic Tube Segment"),
inventory_image = "pipeworks_digiline_detector_tube_inv.png", inventory_image = "pipeworks_digiline_detector_tube_inv.png",
plain = { "pipeworks_digiline_detector_tube_plain.png" }, plain = { "pipeworks_digiline_detector_tube_plain.png" },
node_def = { node_def = {
@ -79,7 +80,7 @@ if digiline_enabled and pipeworks.enable_digiline_detector_tube then
local setchan = meta:get_string("channel") local setchan = meta:get_string("channel")
digiline:receptor_send(pos, digiline.rules.default, setchan, stack:to_string()) digiline:receptor_send(pos, digiline.rules.default, setchan, stack:to_table())
return pipeworks.notvel(pipeworks.meseadjlist, velocity) return pipeworks.notvel(pipeworks.meseadjlist, velocity)
end}, end},
@ -87,9 +88,9 @@ if digiline_enabled and pipeworks.enable_digiline_detector_tube then
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
meta:set_string("formspec", meta:set_string("formspec",
"size[8.6,2.2]".. "size[8.6,2.2]"..
"field[0.6,0.6;8,1;channel;Channel:;${channel}]".. "field[0.6,0.6;8,1;channel;"..S("Channel")..";${channel}]"..
"image[0.3,1.3;1,1;pipeworks_digiline_detector_tube_inv.png]".. "image[0.3,1.3;1,1;pipeworks_digiline_detector_tube_inv.png]"..
"label[1.6,1.2;Digiline Detecting Tube]" "label[1.6,1.2;"..S("Digiline Detecting Tube").."]"
) )
end, end,
on_receive_fields = function(pos, formname, fields, sender) on_receive_fields = function(pos, formname, fields, sender)
@ -122,7 +123,7 @@ end
if pipeworks.enable_conductor_tube then if pipeworks.enable_conductor_tube then
pipeworks.register_tube("pipeworks:conductor_tube_off", { pipeworks.register_tube("pipeworks:conductor_tube_off", {
description = "Conducting Pneumatic Tube Segment", description = S("Conducting Pneumatic Tube Segment"),
inventory_image = "pipeworks_conductor_tube_inv.png", inventory_image = "pipeworks_conductor_tube_inv.png",
short = "pipeworks_conductor_tube_short.png", short = "pipeworks_conductor_tube_short.png",
plain = { "pipeworks_conductor_tube_plain.png" }, plain = { "pipeworks_conductor_tube_plain.png" },
@ -136,7 +137,7 @@ if pipeworks.enable_conductor_tube then
}, },
}) })
pipeworks.register_tube("pipeworks:conductor_tube_on", { pipeworks.register_tube("pipeworks:conductor_tube_on", {
description = "Conducting Pneumatic Tube Segment on (you hacker you)", description = S("Conducting Pneumatic Tube Segment on"),
inventory_image = "pipeworks_conductor_tube_inv.png", inventory_image = "pipeworks_conductor_tube_inv.png",
short = "pipeworks_conductor_tube_short.png", short = "pipeworks_conductor_tube_short.png",
plain = { "pipeworks_conductor_tube_on_plain.png" }, plain = { "pipeworks_conductor_tube_on_plain.png" },
@ -160,7 +161,7 @@ end
if digiline_enabled and pipeworks.enable_digiline_conductor_tube then if digiline_enabled and pipeworks.enable_digiline_conductor_tube then
pipeworks.register_tube("pipeworks:digiline_conductor_tube", { pipeworks.register_tube("pipeworks:digiline_conductor_tube", {
description = "Digiline Conducting Pneumatic Tube Segment", description = S("Digiline Conducting Pneumatic Tube Segment"),
inventory_image = "pipeworks_tube_inv.png^pipeworks_digiline_conductor_tube_inv.png", inventory_image = "pipeworks_tube_inv.png^pipeworks_digiline_conductor_tube_inv.png",
short = "pipeworks_tube_short.png^pipeworks_digiline_conductor_tube_short.png", short = "pipeworks_tube_short.png^pipeworks_digiline_conductor_tube_short.png",
plain = {"pipeworks_tube_plain.png^pipeworks_digiline_conductor_tube_plain.png"}, plain = {"pipeworks_tube_plain.png^pipeworks_digiline_conductor_tube_plain.png"},
@ -178,7 +179,7 @@ end
if digiline_enabled and pipeworks.enable_digiline_conductor_tube and if digiline_enabled and pipeworks.enable_digiline_conductor_tube and
pipeworks.enable_conductor_tube then pipeworks.enable_conductor_tube then
pipeworks.register_tube("pipeworks:mesecon_and_digiline_conductor_tube_off", { pipeworks.register_tube("pipeworks:mesecon_and_digiline_conductor_tube_off", {
description = "Mesecon and Digiline Conducting Pneumatic Tube Segment", description = S("Mesecon and Digiline Conducting Pneumatic Tube Segment"),
inventory_image = "pipeworks_conductor_tube_inv.png^pipeworks_digiline_conductor_tube_inv.png", inventory_image = "pipeworks_conductor_tube_inv.png^pipeworks_digiline_conductor_tube_inv.png",
short = "pipeworks_conductor_tube_short.png^pipeworks_digiline_conductor_tube_short.png", short = "pipeworks_conductor_tube_short.png^pipeworks_digiline_conductor_tube_short.png",
plain = {"pipeworks_conductor_tube_plain.png^pipeworks_digiline_conductor_tube_plain.png"}, plain = {"pipeworks_conductor_tube_plain.png^pipeworks_digiline_conductor_tube_plain.png"},
@ -195,7 +196,7 @@ if digiline_enabled and pipeworks.enable_digiline_conductor_tube and
}, },
}) })
pipeworks.register_tube("pipeworks:mesecon_and_digiline_conductor_tube_on", { pipeworks.register_tube("pipeworks:mesecon_and_digiline_conductor_tube_on", {
description = "Mesecon and Digiline Conducting Pneumatic Tube Segment on (you hacker you)", description = S("Mesecon and Digiline Conducting Pneumatic Tube Segment on"),
inventory_image = "pipeworks_conductor_tube_inv.png^pipeworks_digiline_conductor_tube_inv.png", inventory_image = "pipeworks_conductor_tube_inv.png^pipeworks_digiline_conductor_tube_inv.png",
short = "pipeworks_conductor_tube_short.png^pipeworks_digiline_conductor_tube_short.png", short = "pipeworks_conductor_tube_short.png^pipeworks_digiline_conductor_tube_short.png",
plain = {"pipeworks_conductor_tube_on_plain.png^pipeworks_digiline_conductor_tube_plain.png"}, plain = {"pipeworks_conductor_tube_on_plain.png^pipeworks_digiline_conductor_tube_plain.png"},

View File

@ -1,3 +1,4 @@
local S = minetest.get_translator("pipeworks")
local fs_helpers = pipeworks.fs_helpers local fs_helpers = pipeworks.fs_helpers
if pipeworks.enable_mese_tube then if pipeworks.enable_mese_tube then
@ -55,7 +56,7 @@ if pipeworks.enable_mese_tube then
end end
pipeworks.register_tube("pipeworks:mese_tube", { pipeworks.register_tube("pipeworks:mese_tube", {
description = "Sorting Pneumatic Tube Segment", description = S("Sorting Pneumatic Tube Segment"),
inventory_image = "pipeworks_mese_tube_inv.png", inventory_image = "pipeworks_mese_tube_inv.png",
noctr = {"pipeworks_mese_tube_noctr_1.png", "pipeworks_mese_tube_noctr_2.png", "pipeworks_mese_tube_noctr_3.png", noctr = {"pipeworks_mese_tube_noctr_1.png", "pipeworks_mese_tube_noctr_2.png", "pipeworks_mese_tube_noctr_3.png",
"pipeworks_mese_tube_noctr_4.png", "pipeworks_mese_tube_noctr_5.png", "pipeworks_mese_tube_noctr_6.png"}, "pipeworks_mese_tube_noctr_4.png", "pipeworks_mese_tube_noctr_5.png", "pipeworks_mese_tube_noctr_6.png"},
@ -107,7 +108,7 @@ if pipeworks.enable_mese_tube then
inv:set_size("line"..tostring(i), 6*1) inv:set_size("line"..tostring(i), 6*1)
end end
update_formspec(pos) update_formspec(pos)
meta:set_string("infotext", "Sorting pneumatic tube") meta:set_string("infotext", S("Sorting pneumatic tube"))
end, end,
on_punch = update_formspec, on_punch = update_formspec,
on_receive_fields = function(pos, formname, fields, sender) on_receive_fields = function(pos, formname, fields, sender)

View File

@ -1,3 +1,4 @@
local S = minetest.get_translator("pipeworks")
local filename=minetest.get_worldpath() .. "/teleport_tubes" local filename=minetest.get_worldpath() .. "/teleport_tubes"
local tp_tube_db = nil -- nil forces a read local tp_tube_db = nil -- nil forces a read
@ -127,19 +128,19 @@ local function update_meta(meta, can_receive)
meta:set_int("can_receive", can_receive and 1 or 0) meta:set_int("can_receive", can_receive and 1 or 0)
local cr_state = can_receive and "on" or "off" local cr_state = can_receive and "on" or "off"
meta:set_string("formspec","size[8.6,2.2]".. meta:set_string("formspec","size[8.6,2.2]"..
"field[0.6,0.6;7,1;channel;Channel:;${channel}]".. "field[0.6,0.6;7,1;channel;"..S("Channel")..";${channel}]"..
"label[7.3,0;Receive]".. "label[7.3,0;"..S("Receive").."]"..
"image_button[7.3,0.3;1,0.6;pipeworks_button_" .. cr_state .. ".png;cr" .. (can_receive and 0 or 1) .. ";;;false;pipeworks_button_interm.png]".. "image_button[7.3,0.3;1,0.6;pipeworks_button_" .. cr_state .. ".png;cr" .. (can_receive and 0 or 1) .. ";;;false;pipeworks_button_interm.png]"..
"image[0.3,1.3;1,1;pipeworks_teleport_tube_inv.png]".. "image[0.3,1.3;1,1;pipeworks_teleport_tube_inv.png]"..
"label[1.6,1.2;channels are public by default]" .. "label[1.6,1.2;"..S("channels are public by default").."]" ..
"label[1.6,1.5;use <player>:<channel> for fully private channels]" .. "label[1.6,1.5;"..S("use <player>:<channel> for fully private channels").."]" ..
"label[1.6,1.8;use <player>\\;<channel> for private receivers]" .. "label[1.6,1.8;"..S("use <player>\\;<channel> for private receivers").."]" ..
default.gui_bg.. default.gui_bg..
default.gui_bg_img) default.gui_bg_img)
end end
pipeworks.register_tube("pipeworks:teleport_tube", { pipeworks.register_tube("pipeworks:teleport_tube", {
description = "Teleporting Pneumatic Tube Segment", description = S("Teleporting Pneumatic Tube Segment"),
inventory_image = "pipeworks_teleport_tube_inv.png", inventory_image = "pipeworks_teleport_tube_inv.png",
noctr = { "pipeworks_teleport_tube_noctr.png" }, noctr = { "pipeworks_teleport_tube_noctr.png" },
plain = { "pipeworks_teleport_tube_plain.png" }, plain = { "pipeworks_teleport_tube_plain.png" },
@ -169,7 +170,7 @@ pipeworks.register_tube("pipeworks:teleport_tube", {
on_construct = function(pos) on_construct = function(pos)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
update_meta(meta, true) update_meta(meta, true)
meta:set_string("infotext", "unconfigured Teleportation Tube") meta:set_string("infotext", S("unconfigured Teleportation Tube"))
end, end,
on_receive_fields = function(pos,formname,fields,sender) on_receive_fields = function(pos,formname,fields,sender)
if not fields.channel -- ignore escaping or clientside manipulation of the form if not fields.channel -- ignore escaping or clientside manipulation of the form
@ -189,12 +190,14 @@ pipeworks.register_tube("pipeworks:teleport_tube", {
if name and mode and name ~= sender_name then if name and mode and name ~= sender_name then
--channels starting with '[name]:' can only be used by the named player --channels starting with '[name]:' can only be used by the named player
if mode == ":" then if mode == ":" then
minetest.chat_send_player(sender_name, "Sorry, channel '"..new_channel.."' is reserved for exclusive use by "..name) minetest.chat_send_player(sender_name, S("Sorry, channel '@1' is reserved for exclusive use by @2",
new_channel, name))
return return
--channels starting with '[name];' can be used by other players, but cannot be received from --channels starting with '[name];' can be used by other players, but cannot be received from
elseif mode == ";" and (fields.cr1 or (can_receive ~= 0 and not fields.cr0)) then elseif mode == ";" and (fields.cr1 or (can_receive ~= 0 and not fields.cr0)) then
minetest.chat_send_player(sender_name, "Sorry, receiving from channel '"..new_channel.."' is reserved for "..name) minetest.chat_send_player(sender_name, S("Sorry, receiving from channel '@1' is reserved for @2",
new_channel, name))
return return
end end
end end
@ -226,11 +229,11 @@ pipeworks.register_tube("pipeworks:teleport_tube", {
if channel ~= "" then if channel ~= "" then
set_tube(pos, channel, can_receive) set_tube(pos, channel, can_receive)
local cr_description = (can_receive == 1) and "sending and receiving" or "sending" local cr_description = (can_receive == 1) and "sending and receiving" or "sending"
meta:set_string("infotext", string.format("Teleportation Tube %s on '%s'", cr_description, channel)) meta:set_string("infotext", S("Teleportation Tube @1 on '@2'", cr_description, channel))
else else
-- remove empty channel tubes, to not have to search through them -- remove empty channel tubes, to not have to search through them
remove_tube(pos) remove_tube(pos)
meta:set_string("infotext", "unconfigured Teleportation Tube") meta:set_string("infotext", S("unconfigured Teleportation Tube"))
end end
end end
end, end,
@ -258,3 +261,11 @@ if minetest.get_modpath("mesecons_mvps") ~= nil then
end end
end) end)
end end
-- Expose teleport tube database API for other mods
pipeworks.tptube = {
hash = hash,
save_tube_db = save_tube_db,
get_db = function() return tp_tube_db or read_tube_db() end,
tp_tube_db_version = tp_tube_db_version
}

View File

@ -1,5 +1,6 @@
local S = minetest.get_translator("pipeworks")
minetest.register_node("pipeworks:trashcan", { minetest.register_node("pipeworks:trashcan", {
description = "Trash Can", description = S("Trash Can"),
drawtype = "normal", drawtype = "normal",
tiles = { tiles = {
"pipeworks_trashcan_bottom.png", "pipeworks_trashcan_bottom.png",
@ -22,7 +23,7 @@ minetest.register_node("pipeworks:trashcan", {
meta:set_string("formspec", meta:set_string("formspec",
"size[8,7]".. "size[8,7]"..
"item_image[0,0;1,1;pipeworks:trashcan]".. "item_image[0,0;1,1;pipeworks:trashcan]"..
"label[1,0;Trash Can]".. "label[1,0;"..S("Trash Can").."]"..
"list[context;trash;3.5,1;1,1;]".. "list[context;trash;3.5,1;1,1;]"..
default.gui_bg.. default.gui_bg..
default.gui_bg_img.. default.gui_bg_img..
@ -30,7 +31,7 @@ minetest.register_node("pipeworks:trashcan", {
default.get_hotbar_bg(0,3) .. default.get_hotbar_bg(0,3) ..
"list[current_player;main;0,3;8,4;]" .. "list[current_player;main;0,3;8,4;]" ..
"listring[]") "listring[]")
meta:set_string("infotext", "Trash Can") meta:set_string("infotext", S("Trash Can"))
meta:get_inventory():set_size("trash", 1) meta:get_inventory():set_size("trash", 1)
end, end,
after_place_node = pipeworks.after_place, after_place_node = pipeworks.after_place,

View File

@ -1,4 +1,5 @@
-- This file supplies the various kinds of pneumatic tubes -- This file supplies the various kinds of pneumatic tubes
local S = minetest.get_translator("pipeworks")
local tubenodes = {} local tubenodes = {}
pipeworks.tubenodes = tubenodes pipeworks.tubenodes = tubenodes
@ -56,8 +57,8 @@ local register_one_tube = function(name, tname, dropname, desc, plain, noctrs, e
end end
local tgroups = {snappy = 3, tube = 1, tubedevice = 1, not_in_creative_inventory = 1} local tgroups = {snappy = 3, tube = 1, tubedevice = 1, not_in_creative_inventory = 1}
local tubedesc = string.format("%s %s... You hacker, you.", desc, dump(connects)) local tubedesc = string.format("%s %s", desc, dump(connects))
local iimg = plain[1] local iimg = type(plain[1]) == "table" and plain[1].name or plain[1]
local wscale = {x = 1, y = 1, z = 1} local wscale = {x = 1, y = 1, z = 1}
if #connects == 0 then if #connects == 0 then
@ -197,7 +198,7 @@ local register_all_tubes = function(name, desc, plain, noctrs, ends, short, inv,
wield_image = inv, wield_image = inv,
paramtype = "light", paramtype = "light",
sunlight_propagates = true, sunlight_propagates = true,
description = "Pneumatic tube segment (legacy)", description = S("Pneumatic tube segment (legacy)"),
after_place_node = pipeworks.after_place, after_place_node = pipeworks.after_place,
groups = {not_in_creative_inventory = 1, tube_to_update = 1, tube = 1}, groups = {not_in_creative_inventory = 1, tube_to_update = 1, tube = 1},
tube = {connect_sides = {front = 1, back = 1, left = 1, right = 1, top = 1, bottom = 1}}, tube = {connect_sides = {front = 1, back = 1, left = 1, right = 1, top = 1, bottom = 1}},

View File

@ -1,6 +1,7 @@
local S = minetest.get_translator("pipeworks")
if pipeworks.enable_sand_tube then if pipeworks.enable_sand_tube then
pipeworks.register_tube("pipeworks:sand_tube", { pipeworks.register_tube("pipeworks:sand_tube", {
description = "Vacuuming Pneumatic Tube Segment", description = S("Vacuuming Pneumatic Tube Segment"),
inventory_image = "pipeworks_sand_tube_inv.png", inventory_image = "pipeworks_sand_tube_inv.png",
short = "pipeworks_sand_tube_short.png", short = "pipeworks_sand_tube_short.png",
noctr = {"pipeworks_sand_tube_noctr.png"}, noctr = {"pipeworks_sand_tube_noctr.png"},
@ -28,7 +29,7 @@ end
if pipeworks.enable_mese_sand_tube then if pipeworks.enable_mese_sand_tube then
pipeworks.register_tube("pipeworks:mese_sand_tube", { pipeworks.register_tube("pipeworks:mese_sand_tube", {
description = "Adjustable Vacuuming Pneumatic Tube Segment", description = S("Adjustable Vacuuming Pneumatic Tube Segment"),
inventory_image = "pipeworks_mese_sand_tube_inv.png", inventory_image = "pipeworks_mese_sand_tube_inv.png",
short = "pipeworks_mese_sand_tube_short.png", short = "pipeworks_mese_sand_tube_short.png",
noctr = {"pipeworks_mese_sand_tube_noctr.png"}, noctr = {"pipeworks_mese_sand_tube_noctr.png"},
@ -44,7 +45,7 @@ if pipeworks.enable_mese_sand_tube then
"field[1.3,0.4;1,1;dist;radius;${dist}]".. "field[1.3,0.4;1,1;dist;radius;${dist}]"..
default.gui_bg.. default.gui_bg..
default.gui_bg_img) default.gui_bg_img)
meta:set_string("infotext", "Adjustable Vacuuming Pneumatic Tube Segment") meta:set_string("infotext", S("Adjustable Vacuuming Pneumatic Tube Segment"))
end, end,
on_receive_fields = function(pos,formname,fields,sender) on_receive_fields = function(pos,formname,fields,sender)
if not pipeworks.may_configure(pos, sender) then return end if not pipeworks.may_configure(pos, sender) then return end
@ -54,7 +55,7 @@ if pipeworks.enable_mese_sand_tube then
dist = math.max(0, dist) dist = math.max(0, dist)
dist = math.min(8, dist) dist = math.min(8, dist)
meta:set_int("dist", dist) meta:set_int("dist", dist)
meta:set_string("infotext", ("Adjustable Vacuuming Pneumatic Tube Segment (%dm)"):format(dist)) meta:set_string("infotext", (S("Adjustable Vacuuming Pneumatic Tube Segment (@1m)", dist)))
end end
end, end,
}, },

View File

@ -1,3 +1,4 @@
local S = minetest.get_translator("pipeworks")
local assumed_eye_pos = vector.new(0, 1.5, 0) local assumed_eye_pos = vector.new(0, 1.5, 0)
local function vector_copy(v) local function vector_copy(v)
@ -39,7 +40,7 @@ local can_tool_dig_node = function(nodename, toolcaps, toolname)
-- but a player holding one can - the game seems to fall back to the hand. -- but a player holding one can - the game seems to fall back to the hand.
-- fall back to checking the hand's properties if the tool isn't the correct one. -- fall back to checking the hand's properties if the tool isn't the correct one.
local hand_caps = minetest.registered_items[""].tool_capabilities local hand_caps = minetest.registered_items[""].tool_capabilities
diggable = minetest.get_dig_params(nodegroups, hand_caps) diggable = minetest.get_dig_params(nodegroups, hand_caps).diggable
end end
return diggable return diggable
end end
@ -259,7 +260,7 @@ if pipeworks.enable_node_breaker then
local wield_inv_name = "pick" local wield_inv_name = "pick"
data = { data = {
name_base = name_base, name_base = name_base,
description = "Node Breaker", description = S("Node Breaker"),
texture_base = "pipeworks_nodebreaker", texture_base = "pipeworks_nodebreaker",
texture_stateful = { top = true, bottom = true, side2 = true, side1 = true, front = true }, texture_stateful = { top = true, bottom = true, side2 = true, side1 = true, front = true },
tube_connect_sides = { top=1, bottom=1, left=1, right=1, back=1 }, tube_connect_sides = { top=1, bottom=1, left=1, right=1, back=1 },
@ -377,7 +378,7 @@ if pipeworks.enable_node_breaker then
minetest.register_craft({ minetest.register_craft({
output = "pipeworks:nodebreaker_off", output = "pipeworks:nodebreaker_off",
recipe = { recipe = {
{ "pipeworks:gear", "pipeworks:gear", "pipeworks:gear" }, { "basic_materials:gear_steel", "basic_materials:gear_steel", "basic_materials:gear_steel" },
{ "default:stone", "mesecons:piston", "default:stone" }, { "default:stone", "mesecons:piston", "default:stone" },
{ "group:wood", "mesecons:mesecon", "group:wood" }, { "group:wood", "mesecons:mesecon", "group:wood" },
} }
@ -408,7 +409,7 @@ end
if pipeworks.enable_deployer then if pipeworks.enable_deployer then
register_wielder({ register_wielder({
name_base = "pipeworks:deployer", name_base = "pipeworks:deployer",
description = "Deployer", description = S("Deployer"),
texture_base = "pipeworks_deployer", texture_base = "pipeworks_deployer",
texture_stateful = { front = true }, texture_stateful = { front = true },
tube_connect_sides = { back=1 }, tube_connect_sides = { back=1 },
@ -441,7 +442,7 @@ end
if pipeworks.enable_dispenser then if pipeworks.enable_dispenser then
register_wielder({ register_wielder({
name_base = "pipeworks:dispenser", name_base = "pipeworks:dispenser",
description = "Dispenser", description = S("Dispenser"),
texture_base = "pipeworks_dispenser", texture_base = "pipeworks_dispenser",
texture_stateful = { front = true }, texture_stateful = { front = true },
tube_connect_sides = { back=1 }, tube_connect_sides = { back=1 },