Cristiano Magro
2024-08-27 535e04d542520f58a2db8d4d7a222b73e5c96ef1
Merge branch 'master' into xno_tree_trap
21 files added
8 files renamed
48 files modified
106 files deleted
3853 ■■■■■ changed files
.luacheckrc 24 ●●●● patch | view | raw | blame | history
LICENSE.txt 16 ●●●●● patch | view | raw | blame | history
README.md 13 ●●●● patch | view | raw | blame | history
concrete/depends.txt 4 ●●●● patch | view | raw | blame | history
concrete/init.lua 7 ●●●●● patch | view | raw | blame | history
concrete/locale/ja.txt 9 ●●●●● patch | view | raw | blame | history
extranodes/depends.txt 9 ●●●●● patch | view | raw | blame | history
extranodes/init.lua 77 ●●●●● patch | view | raw | blame | history
extranodes/locale/ja.txt 8 ●●●●● patch | view | raw | blame | history
manual.md 465 ●●●●● patch | view | raw | blame | history
modpack.conf 2 ●●●●● patch | view | raw | blame | history
settingtypes.txt 9 ●●●●● patch | view | raw | blame | history
technic/README.md 32 ●●●● patch | view | raw | blame | history
technic/doc/api.md 150 ●●●● patch | view | raw | blame | history
technic/helpers.lua 17 ●●●● patch | view | raw | blame | history
technic/init.lua 18 ●●●● patch | view | raw | blame | history
technic/legacy.lua 37 ●●●●● patch | view | raw | blame | history
technic/machines/HV/forcefield.lua 7 ●●●● patch | view | raw | blame | history
technic/machines/HV/nuclear_reactor.lua 34 ●●●●● patch | view | raw | blame | history
technic/machines/HV/quarry.lua 75 ●●●●● patch | view | raw | blame | history
technic/machines/LV/grinder.lua 20 ●●●●● patch | view | raw | blame | history
technic/machines/LV/init.lua 3 ●●●●● patch | view | raw | blame | history
technic/machines/LV/lamp.lua 156 ●●●●● patch | view | raw | blame | history
technic/machines/LV/led.lua 95 ●●●●● patch | view | raw | blame | history
technic/machines/MV/power_radiator.lua 3 ●●●● patch | view | raw | blame | history
technic/machines/other/anchor.lua 28 ●●●●● patch | view | raw | blame | history
technic/machines/other/frames.lua 28 ●●●● patch | view | raw | blame | history
technic/machines/register/battery_box.lua 82 ●●●●● patch | view | raw | blame | history
technic/machines/register/cables.lua 39 ●●●●● patch | view | raw | blame | history
technic/machines/register/compressor_recipes.lua 110 ●●●● patch | view | raw | blame | history
technic/machines/register/grinder_recipes.lua 161 ●●●●● patch | view | raw | blame | history
technic/machines/supply_converter.lua 42 ●●●● patch | view | raw | blame | history
technic/machines/switching_station.lua 181 ●●●● patch | view | raw | blame | history
technic/mod.conf 2 ●●● patch | view | raw | blame | history
technic/radiation.lua 2 ●●● patch | view | raw | blame | history
technic/register.lua 5 ●●●● patch | view | raw | blame | history
technic/textures/hires/technic_hv_nuclear_reactor_core_128.png patch | view | raw | blame | history
technic/textures/hires/technic_hv_nuclear_reactor_core_16.png patch | view | raw | blame | history
technic/textures/hires/technic_hv_nuclear_reactor_core_256.png patch | view | raw | blame | history
technic/textures/hires/technic_hv_nuclear_reactor_core_32.png patch | view | raw | blame | history
technic/textures/hires/technic_hv_nuclear_reactor_core_64.png patch | view | raw | blame | history
technic/textures/power_meter.png patch | view | raw | blame | history
technic/textures/technic_copper_coil.png patch | view | raw | blame | history
technic/textures/technic_deployer_back.png patch | view | raw | blame | history
technic/textures/technic_deployer_bottom.png patch | view | raw | blame | history
technic/textures/technic_deployer_front_off.png patch | view | raw | blame | history
technic/textures/technic_deployer_front_on.png patch | view | raw | blame | history
technic/textures/technic_deployer_side.png patch | view | raw | blame | history
technic/textures/technic_deployer_side1.png patch | view | raw | blame | history
technic/textures/technic_deployer_side2.png patch | view | raw | blame | history
technic/textures/technic_deployer_top.png patch | view | raw | blame | history
technic/textures/technic_generator_front.png patch | view | raw | blame | history
technic/textures/technic_generator_front_active.png patch | view | raw | blame | history
technic/textures/technic_generator_side.png patch | view | raw | blame | history
technic/textures/technic_generator_top.png patch | view | raw | blame | history
technic/textures/technic_injector_side.png patch | view | raw | blame | history
technic/textures/technic_injector_top.png patch | view | raw | blame | history
technic/textures/technic_lv_lamp_bottom.png patch | view | raw | blame | history
technic/textures/technic_lv_lamp_side.png patch | view | raw | blame | history
technic/textures/technic_lv_lamp_top.png patch | view | raw | blame | history
technic/textures/technic_lv_led.png patch | view | raw | blame | history
technic/textures/technic_lv_led_inv.png patch | view | raw | blame | history
technic/textures/technic_lv_led_side.png patch | view | raw | blame | history
technic/textures/technic_lv_led_side2.png patch | view | raw | blame | history
technic/textures/technic_lv_led_top.png patch | view | raw | blame | history
technic/textures/technic_music_player_top.png patch | view | raw | blame | history
technic/textures/technic_nether_dust.png patch | view | raw | blame | history
technic/textures/technic_nodebreaker_back.png patch | view | raw | blame | history
technic/textures/technic_nodebreaker_bottom.png patch | view | raw | blame | history
technic/textures/technic_nodebreaker_bottom_off.png patch | view | raw | blame | history
technic/textures/technic_nodebreaker_bottom_on.png patch | view | raw | blame | history
technic/textures/technic_nodebreaker_front_off.png patch | view | raw | blame | history
technic/textures/technic_nodebreaker_front_on.png patch | view | raw | blame | history
technic/textures/technic_nodebreaker_side.png patch | view | raw | blame | history
technic/textures/technic_nodebreaker_side1.png patch | view | raw | blame | history
technic/textures/technic_nodebreaker_side1_off.png patch | view | raw | blame | history
technic/textures/technic_nodebreaker_side1_on.png patch | view | raw | blame | history
technic/textures/technic_nodebreaker_side2.png patch | view | raw | blame | history
technic/textures/technic_nodebreaker_side2_off.png patch | view | raw | blame | history
technic/textures/technic_nodebreaker_side2_on.png patch | view | raw | blame | history
technic/textures/technic_nodebreaker_top.png patch | view | raw | blame | history
technic/textures/technic_nodebreaker_top_off.png patch | view | raw | blame | history
technic/textures/technic_nodebreaker_top_on.png patch | view | raw | blame | history
technic/textures/technic_power_meter_bg.png patch | view | raw | blame | history
technic/textures/technic_power_meter_fg.png patch | view | raw | blame | history
technic/textures/technic_pyrite_dust.png patch | view | raw | blame | history
technic/textures/technic_river_water_can.png patch | view | raw | blame | history
technic/textures/technic_rubber_goo.png patch | view | raw | blame | history
technic/textures/technic_screwdriver.png patch | view | raw | blame | history
technic/textures/technicx32/technic_battery.png patch | view | raw | blame | history
technic/textures/technicx32/technic_deployer_back.png patch | view | raw | blame | history
technic/textures/technicx32/technic_deployer_bottom.png patch | view | raw | blame | history
technic/textures/technicx32/technic_deployer_front_off.png patch | view | raw | blame | history
technic/textures/technicx32/technic_deployer_front_on.png patch | view | raw | blame | history
technic/textures/technicx32/technic_deployer_side.png patch | view | raw | blame | history
technic/textures/technicx32/technic_deployer_side1.png patch | view | raw | blame | history
technic/textures/technicx32/technic_deployer_side2.png patch | view | raw | blame | history
technic/textures/technicx32/technic_deployer_top.png patch | view | raw | blame | history
technic/textures/technicx32/technic_fine_copper_wire.png patch | view | raw | blame | history
technic/textures/technicx32/technic_fine_gold_wire.png patch | view | raw | blame | history
technic/textures/technicx32/technic_fine_silver_wire.png patch | view | raw | blame | history
technic/textures/technicx32/technic_geothermal_side.png patch | view | raw | blame | history
technic/textures/technicx32/technic_geothermal_top.png patch | view | raw | blame | history
technic/textures/technicx32/technic_geothermal_top_active.png patch | view | raw | blame | history
technic/textures/technicx32/technic_grinder_front.png patch | view | raw | blame | history
technic/textures/technicx32/technic_grinder_side.png patch | view | raw | blame | history
technic/textures/technicx32/technic_grinder_top.png patch | view | raw | blame | history
technic/textures/technicx32/technic_laser_beam.png patch | view | raw | blame | history
technic/textures/technicx32/technic_nodebreaker_back.png patch | view | raw | blame | history
technic/textures/technicx32/technic_nodebreaker_bottom.png patch | view | raw | blame | history
technic/textures/technicx32/technic_nodebreaker_bottom_off.png patch | view | raw | blame | history
technic/textures/technicx32/technic_nodebreaker_bottom_on.png patch | view | raw | blame | history
technic/textures/technicx32/technic_nodebreaker_front_off.png patch | view | raw | blame | history
technic/textures/technicx32/technic_nodebreaker_front_on.png patch | view | raw | blame | history
technic/textures/technicx32/technic_nodebreaker_side.png patch | view | raw | blame | history
technic/textures/technicx32/technic_nodebreaker_side1.png patch | view | raw | blame | history
technic/textures/technicx32/technic_nodebreaker_side1_off.png patch | view | raw | blame | history
technic/textures/technicx32/technic_nodebreaker_side1_on.png patch | view | raw | blame | history
technic/textures/technicx32/technic_nodebreaker_side2.png patch | view | raw | blame | history
technic/textures/technicx32/technic_nodebreaker_side2_off.png patch | view | raw | blame | history
technic/textures/technicx32/technic_nodebreaker_side2_on.png patch | view | raw | blame | history
technic/textures/technicx32/technic_nodebreaker_top.png patch | view | raw | blame | history
technic/textures/technicx32/technic_nodebreaker_top_off.png patch | view | raw | blame | history
technic/textures/technicx32/technic_nodebreaker_top_on.png patch | view | raw | blame | history
technic/textures/technicx32/technic_pyrite_dust.png patch | view | raw | blame | history
technic/textures/technicx32/technic_rubber.png patch | view | raw | blame | history
technic/textures/technicx32/technic_rubber_sapling.png patch | view | raw | blame | history
technic/textures/technicx32/technic_rubber_tree_empty.png patch | view | raw | blame | history
technic/textures/technicx32/technic_rubber_tree_full.png patch | view | raw | blame | history
technic/textures/technicx32/technic_silicon_wafer.png patch | view | raw | blame | history
technic/tools/cans.lua 31 ●●●● patch | view | raw | blame | history
technic/tools/chainsaw.lua 573 ●●●●● patch | view | raw | blame | history
technic/tools/flashlight.lua 11 ●●●● patch | view | raw | blame | history
technic/tools/mining_drill.lua 184 ●●●●● patch | view | raw | blame | history
technic/tools/mining_lasers.lua 15 ●●●● patch | view | raw | blame | history
technic/tools/prospector.lua 73 ●●●●● patch | view | raw | blame | history
technic/tools/sonic_screwdriver.lua 11 ●●●● patch | view | raw | blame | history
technic/tools/vacuum.lua 23 ●●●● patch | view | raw | blame | history
technic_chests/common.lua 4 ●●●● patch | view | raw | blame | history
technic_chests/depends.txt 7 ●●●●● patch | view | raw | blame | history
technic_chests/locale/ja.txt 41 ●●●●● patch | view | raw | blame | history
technic_chests/register.lua 120 ●●●●● patch | view | raw | blame | history
technic_chests/textures/technic_chest_form_bg.png patch | view | raw | blame | history
technic_chests/textures/technic_copper_chest_inventory.png patch | view | raw | blame | history
technic_chests/textures/technic_form_bg.png patch | view | raw | blame | history
technic_chests/textures/technic_gold_chest_inventory.png patch | view | raw | blame | history
technic_chests/textures/technic_iron_chest_inventory.png patch | view | raw | blame | history
technic_chests/textures/technic_main_inventory.png patch | view | raw | blame | history
technic_chests/textures/technic_mithril_chest_inventory.png patch | view | raw | blame | history
technic_chests/textures/technic_silver_chest_inventory.png patch | view | raw | blame | history
technic_chests/textures/technic_wooden_chest_inventory.png patch | view | raw | blame | history
technic_cnc/cnc.lua 157 ●●●● patch | view | raw | blame | history
technic_cnc/cnc_api.lua 157 ●●●●● patch | view | raw | blame | history
technic_cnc/cnc_materials.lua 173 ●●●●● patch | view | raw | blame | history
technic_cnc/depends.txt 3 ●●●●● patch | view | raw | blame | history
technic_cnc/init.lua 6 ●●●● patch | view | raw | blame | history
technic_cnc/locale/de.txt 36 ●●●●● patch | view | raw | blame | history
technic_cnc/locale/technic_cnc.de.tr 50 ●●●●● patch | view | raw | blame | history
technic_cnc/locale/template.txt 84 ●●●●● patch | view | raw | blame | history
technic_cnc/textures/technic_cnc_slope_edge_upsdown.png patch | view | raw | blame | history
technic_cnc/textures/technic_cnc_slope_inner_edge_upsdown.png patch | view | raw | blame | history
technic_cnc/textures/technic_cnc_slope_upsdown.png patch | view | raw | blame | history
technic_worldgen/depends.txt 9 ●●●●● patch | view | raw | blame | history
technic_worldgen/nodes.lua 18 ●●●●● patch | view | raw | blame | history
technic_worldgen/textures/technic_granite_bricks.png patch | view | raw | blame | history
technic_worldgen/textures/technic_stainless_steel_ingot.png patch | view | raw | blame | history
technic_worldgen/textures/x32/technic_brass_ingot.png patch | view | raw | blame | history
technic_worldgen/textures/x32/technic_chromium_ingot.png patch | view | raw | blame | history
technic_worldgen/textures/x32/technic_chromium_lump.png patch | view | raw | blame | history
technic_worldgen/textures/x32/technic_concrete_block.png patch | view | raw | blame | history
technic_worldgen/textures/x32/technic_granite.png patch | view | raw | blame | history
technic_worldgen/textures/x32/technic_marble.png patch | view | raw | blame | history
technic_worldgen/textures/x32/technic_marble_bricks.png patch | view | raw | blame | history
technic_worldgen/textures/x32/technic_mineral_chromium.png patch | view | raw | blame | history
technic_worldgen/textures/x32/technic_mineral_uranium.png patch | view | raw | blame | history
technic_worldgen/textures/x32/technic_mineral_zinc.png patch | view | raw | blame | history
technic_worldgen/textures/x32/technic_rebar.png patch | view | raw | blame | history
technic_worldgen/textures/x32/technic_uranium.png patch | view | raw | blame | history
technic_worldgen/textures/x32/technic_zinc_ingot.png patch | view | raw | blame | history
technic_worldgen/textures/x32/technic_zinc_lump.png patch | view | raw | blame | history
wrench/depends.txt 6 ●●●●● patch | view | raw | blame | history
wrench/init.lua 85 ●●●●● patch | view | raw | blame | history
wrench/locale/ja.txt 6 ●●●●● patch | view | raw | blame | history
.luacheckrc
@@ -1,6 +1,8 @@
unused_args = false
allow_defined_top = true
max_line_length = 999
max_line_length = 150
-- Allow shadowed variables (callbacks in callbacks)
redefined = false
globals = {
    "technic", "minetest",
@@ -32,19 +34,7 @@
    "craftguide", "i3"
}
files["concrete/init.lua"].ignore = { "steel_ingot" }
files["technic/machines/MV/tool_workshop.lua"].ignore = { "pos" }
files["technic/machines/other/frames.lua"].ignore = { "item_texture", "item_type", "adj", "connected", "" }
files["technic/machines/register/battery_box.lua"].ignore = { "pos", "tube_upgrade" }
files["technic/machines/register/cables.lua"].ignore = { "name", "from_below", "p" }
files["technic/machines/register/common.lua"].ignore = { "result" }
files["technic/machines/register/generator.lua"].ignore = { "node" }
files["technic/machines/switching_station.lua"].ignore = { "pos1", "tier", "poshash" }
files["technic/radiation.lua"].ignore = { "LAVA_VISC" }
files["technic/tools/chainsaw.lua"].ignore = { "pos" }
files["technic/tools/mining_drill.lua"].ignore = { "mode" }
files["technic_chests/register.lua"].ignore = { "fs_helpers", "name", "locked_after_place" }
files["technic_cnc/cnc.lua"].ignore = { "multiplier" }
files["wrench/init.lua"].ignore = { "name", "stack" }
-- Loop warning
files["technic/machines/other/frames.lua"].ignore = { "" }
-- Long lines
files["technic_cnc/cnc_api.lua"].ignore = { "" }
LICENSE.txt
New file
@@ -0,0 +1,16 @@
Minetest Mod: technic
Copyright (C) 2012-2022  RealBadAngel and contributors
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
README.md
@@ -15,16 +15,17 @@
  * Minetest 5.0.0 or newer
  * [Minetest Game](https://github.com/minetest/minetest_game/)
  * [mesecons](https://github.com/minetest-mods/mesecons) -> signalling events
  * [pipeworks](https://gitlab.com/VanessaE/pipeworks/) -> automation of item transport
  * [pipeworks](https://github.com/mt-mods/pipeworks) -> automation of item transport
  * [moreores](https://github.com/minetest-mods/moreores/) -> additional ores
  * [basic_materials](https://gitlab.com/VanessaE/basic_materials) -> basic craft items
  * Supports [moretrees](https://gitlab.com/VanessaE/moretrees) -> rubber trees
  * [basic_materials](https://github.com/mt-mods/basic_materials) -> basic craft items
  * Supports [moretrees](https://github.com/mt-mods/moretrees) -> rubber trees
  * Consult `depends.txt` or `mod.conf` of each mod for further dependency information.
## FAQ
The modpack is explained in the [Manual](manual.md) included in this repository.
The modpack is explained in the **[Manual](manual.md)** included in this repository.
Machine and tool descriptions can be found on the **[GitHub Wiki](https://github.com/minetest-mods/technic/wiki)**.
1. My technic circuit doesn't work. No power is distributed.
    * Make sure you have a switching station connected.
@@ -32,13 +33,13 @@
    * Each machine type requires its own cable type. If you do not have a
      matching circuit, consider using a "Supply Converter" for simplicity.
The API documentation can be found here: [Technic API](technic/doc/api.md)
For modders: **[Technic Lua API](technic/doc/api.md)**
## License
Unless otherwise stated, all components of this modpack are licensed under the
LGPLv2 or later. See also the individual mod folders for their
[LGPLv2 or later](LICENSE.txt). See also the individual mod folders for their
secondary/alternate licenses, if any.
concrete/depends.txt
File was deleted
concrete/init.lua
@@ -16,13 +16,6 @@
            "technic:concrete_post_with_platform")
end
local steel_ingot
if minetest.get_modpath("technic_worldgen") then
    steel_ingot = "technic:carbon_steel_ingot"
else
    steel_ingot = "default:steel_ingot"
end
minetest.register_craft({
    output = 'technic:concrete_post_platform 6',
    recipe = {
concrete/locale/ja.txt
New file
@@ -0,0 +1,9 @@
# technic_concrete japanese translation
# technic_concreteの日本語への翻訳
# by damiemk
Rebar = 鉄筋
Concrete Block = コンクリートのブロック
Blast-resistant Concrete Block = 耐爆性コンクリートのブロック
Concrete Post Platform = コンクリートのプラットフォーム
Concrete Post = コンクリートポスト
extranodes/depends.txt
File was deleted
extranodes/init.lua
@@ -27,6 +27,12 @@
        tiles={"technic_granite.png"},
    })
    stairsplus:register_all("technic", "granite_bricks", "technic:granite_bricks", {
        description=S("Granite Bricks"),
        groups={cracky=1, not_in_creative_inventory=1},
        tiles={"technic_granite_bricks.png"},
    })
    stairsplus:register_all("technic", "concrete", "technic:concrete", {
        description=S("Concrete"),
        groups={cracky=3, not_in_creative_inventory=1},
@@ -57,36 +63,47 @@
        tiles={"technic_stainless_steel_block.png"},
    })
    function register_technic_stairs_alias(modname, origname, newmod, newname)
        minetest.register_alias(modname .. ":slab_" .. origname, newmod..":slab_" .. newname)
        minetest.register_alias(modname .. ":slab_" .. origname .. "_inverted", newmod..":slab_" .. newname .. "_inverted")
        minetest.register_alias(modname .. ":slab_" .. origname .. "_wall", newmod..":slab_" .. newname .. "_wall")
        minetest.register_alias(modname .. ":slab_" .. origname .. "_quarter", newmod..":slab_" .. newname .. "_quarter")
        minetest.register_alias(modname .. ":slab_" .. origname .. "_quarter_inverted", newmod..":slab_" .. newname .. "_quarter_inverted")
        minetest.register_alias(modname .. ":slab_" .. origname .. "_quarter_wall", newmod..":slab_" .. newname .. "_quarter_wall")
        minetest.register_alias(modname .. ":slab_" .. origname .. "_three_quarter", newmod..":slab_" .. newname .. "_three_quarter")
        minetest.register_alias(modname .. ":slab_" .. origname .. "_three_quarter_inverted", newmod..":slab_" .. newname .. "_three_quarter_inverted")
        minetest.register_alias(modname .. ":slab_" .. origname .. "_three_quarter_wall", newmod..":slab_" .. newname .. "_three_quarter_wall")
        minetest.register_alias(modname .. ":stair_" .. origname, newmod..":stair_" .. newname)
        minetest.register_alias(modname .. ":stair_" .. origname .. "_inverted", newmod..":stair_" .. newname .. "_inverted")
        minetest.register_alias(modname .. ":stair_" .. origname .. "_wall", newmod..":stair_" .. newname .. "_wall")
        minetest.register_alias(modname .. ":stair_" .. origname .. "_wall_half", newmod..":stair_" .. newname .. "_wall_half")
        minetest.register_alias(modname .. ":stair_" .. origname .. "_wall_half_inverted", newmod..":stair_" .. newname .. "_wall_half_inverted")
        minetest.register_alias(modname .. ":stair_" .. origname .. "_half", newmod..":stair_" .. newname .. "_half")
        minetest.register_alias(modname .. ":stair_" .. origname .. "_half_inverted", newmod..":stair_" .. newname .. "_half_inverted")
        minetest.register_alias(modname .. ":stair_" .. origname .. "_right_half", newmod..":stair_" .. newname .. "_right_half")
        minetest.register_alias(modname .. ":stair_" .. origname .. "_right_half_inverted", newmod..":stair_" .. newname .. "_right_half_inverted")
        minetest.register_alias(modname .. ":stair_" .. origname .. "_wall_half", newmod..":stair_" .. newname .. "_wall_half")
        minetest.register_alias(modname .. ":stair_" .. origname .. "_wall_half_inverted", newmod..":stair_" .. newname .. "_wall_half_inverted")
        minetest.register_alias(modname .. ":stair_" .. origname .. "_inner", newmod..":stair_" .. newname .. "_inner")
        minetest.register_alias(modname .. ":stair_" .. origname .. "_inner_inverted", newmod..":stair_" .. newname .. "_inner_inverted")
        minetest.register_alias(modname .. ":stair_" .. origname .. "_outer", newmod..":stair_" .. newname .. "_outer")
        minetest.register_alias(modname .. ":stair_" .. origname .. "_outer_inverted", newmod..":stair_" .. newname .. "_outer_inverted")
        minetest.register_alias(modname .. ":panel_" .. origname .. "_bottom", newmod..":panel_" .. newname .. "_bottom")
        minetest.register_alias(modname .. ":panel_" .. origname .. "_top", newmod..":panel_" .. newname .. "_top")
        minetest.register_alias(modname .. ":panel_" .. origname .. "_vertical", newmod..":panel_" .. newname .. "_vertical")
        minetest.register_alias(modname .. ":micro_" .. origname .. "_bottom", newmod..":micro_" .. newname .. "_bottom")
        minetest.register_alias(modname .. ":micro_" .. origname .. "_top", newmod..":micro_" .. newname .. "_top")
    function register_technic_stairs_alias(origmod, origname, newmod, newname)
        local func = minetest.register_alias
        local function remap(kind, suffix)
            -- Old: stairsplus:slab_concrete_wall
            -- New:    technic:slab_concrete_wall
            func(("%s:%s_%s%s"):format(origmod, kind, origname, suffix),
                ("%s:%s_%s%s"):format(newmod, kind, newname, suffix))
        end
        -- Slabs
        remap("slab", "")
        remap("slab", "_inverted")
        remap("slab", "_wall")
        remap("slab", "_quarter")
        remap("slab", "_quarter_inverted")
        remap("slab", "_quarter_wall")
        remap("slab", "_three_quarter")
        remap("slab", "_three_quarter_inverted")
        remap("slab", "_three_quarter_wall")
        -- Stairs
        remap("stair", "")
        remap("stair", "_inverted")
        remap("stair", "_wall")
        remap("stair", "_wall_half")
        remap("stair", "_wall_half_inverted")
        remap("stair", "_half")
        remap("stair", "_half_inverted")
        remap("stair", "_right_half")
        remap("stair", "_right_half_inverted")
        remap("stair", "_inner")
        remap("stair", "_inner_inverted")
        remap("stair", "_outer")
        remap("stair", "_outer_inverted")
        -- Other
        remap("panel", "_bottom")
        remap("panel", "_top")
        remap("panel", "_vertical")
        remap("micro", "_bottom")
        remap("micro", "_top")
    end
    register_technic_stairs_alias("stairsplus", "concrete", "technic", "concrete")
extranodes/locale/ja.txt
New file
@@ -0,0 +1,8 @@
# technic_extranodes japanese translation
# technic_extranodesの日本語への翻訳
# by damiemk
Marble = 大理石
Marble Bricks = 大理石のレンガ
Granite = 花崗岩
Concrete = コンクリート
manual.md
@@ -8,20 +8,20 @@
*   [Minetest Game Documentation](https://wiki.minetest.net/Main_Page)
*   [Mesecons Documentation](http://mesecons.net/items.html)
*   [Pipeworks Documentation](https://gitlab.com/VanessaE/pipeworks/-/wikis/home)
*   [Pipeworks Documentation](https://github.com/mt-mods/pipeworks/wiki/)
*   [Moreores Forum Post](https://forum.minetest.net/viewtopic.php?t=549)
*   [Basic materials Repository](https://gitlab.com/VanessaE/basic_materials)
## Recipes
## 1.0 Recipes
Recipes for items registered by technic are not specifically documented here.
Please consult a craft guide mod to look up the recipes in-game.
**Recommended mod:** [Unified Inventory](https://github.com/minetest-mods/unified_inventory)
## Substances
## 2.0 Substances
### Ores
### 2.1 Ores
Technic registers a few ores which are needed to craft machines or items.
Each ore type is found at a specific range of elevations so you will
@@ -34,6 +34,11 @@
Note ²: *These ores are provided by moreores. TODO: Add reference link*
#### Chromium
Use: stainless steel
Generated below: -100m, more commonly below -200m
#### Coal ¹
Use: Fuel, alloy as carbon
@@ -41,32 +46,60 @@
usually in dust form, as an ingredient in alloying recipes, wherever
elemental carbon is required.
#### Iron ¹
Use: multiple, mainly for alloys with carbon (coal).
#### Copper ¹
Copper is a common metal, used either on its own for its electrical
conductivity, or as the base component of alloys.
Although common, it is very heavily used, and most of the time it will
be the material that most limits your activity.
#### Diamond ¹
Use: mainly for cutting machines
Diamond is a precious gemstone. It is used moderately, mainly for reasons
connected to its extreme hardness.
#### Gold ¹
Use: various
Generated below: -64m, more commonly below -256m
Gold is a precious metal. It is most notably used in electrical items due to
its combination of good conductivity and corrosion resistance.
#### Iron ¹
Use: multiple, mainly for alloys with carbon (coal).
#### Lead
Use: batteries, HV nuclear reactor layout
Generated below: 16m, more common below -128m
#### Mese ¹
Use: various
Mese is a precious gemstone, and unlike diamond it is entirely fictional.
It is used in small quantities, wherever some magic needs to be imparted.
#### Mithril ²
Use: chests
Generated below: -512m, evenly common
Mithril is a fictional ore, being derived from J. R. R. Tolkien's
Middle-Earth setting.  It is little used.
#### Silver ²
Use: conductors
Generated below: -2m, evenly common
Silver is a semi-precious metal and is the best conductor of all the pure elements.
#### Tin ¹
Use: batteries, bronze
Tin is a common metal but is used rarely. Its abundance is well in excess
of its usage, so you will usually have a surplus of it.
#### Zinc
Use: brass
Depth: 2m, more commonly below -32m
Zinc only has a few uses but is a common metal.
#### Chromium
Use: stainless steel
Depth: -100m, more commonly below -200m
#### Uranium
Use: nuclear reactor fuel
@@ -79,42 +112,15 @@
Keep a safety distance of a meter to avoid being harmed by radiation.
#### Silver ²
Use: conductors
#### Zinc
Use: brass
Depth: -2m, evenly common
Generated below: 2m, more commonly below -32m
Silver is a semi-precious metal and is the best conductor of all the pure elements.
Zinc only has a few uses but is a common metal.
#### Gold ¹
Use: various
Depth: -64m, more commonly below -256m
Gold is a precious metal. It is most notably used in electrical items due to
its combination of good conductivity and corrosion resistance.
#### Mithril ²
Use: chests
Depth: -512m, evenly common
Mithril is a fictional ore, being derived from J. R. R. Tolkien's
Middle-Earth setting.  It is little used.
#### Mese ¹
Use: various
Mese is a precious gemstone, and unlike diamond it is entirely fictional.
It is used in small quantities, wherever some magic needs to be imparted.
#### Diamond ¹
Use: mainly for cutting machines
Diamond is a precious gemstone. It is used moderately, mainly for reasons
connected to its extreme hardness.
### Rocks
### 2.2 Rocks
This section describes the rock types added by technic. Further rock types
are supported by technic machines. These can be processed using the grinder:
@@ -136,7 +142,13 @@
stone. It has mainly decorative use, but also appears in a couple of
machine recipes.
### Rubber
#### Sulfur
Uses: battery box
Sulur is generated around some lava patches (caves).
### 2.3 Rubber
Rubber is a biologically-derived material that has industrial uses due
to its electrical resistivity and its impermeability.  In technic, it
is used in a few recipes, and it must be acquired by tapping rubber trees.
@@ -150,7 +162,7 @@
To obtain rubber from latex, alloy latex with coal dust.
### Metals
## 3.0 Metal processing
Generally, each metal can exist in five forms:
 * ore -> stone containing the lump
@@ -162,7 +174,7 @@
Metals can be converted between dust, ingot and block, but can't be converted
from them back to ore or lump forms.
#### Grinding
### Grinding
Ores can be processed as follows:
 * ore -> lump (digging) -> ingot (melting)
@@ -171,121 +183,59 @@
At the expense of some energy consumption, the grinder can extract more material
from the lump, resulting in 2x dust which can be melted to two ingots in total.
#### Alloying
Alloying recipes in which a metal is the base ingredient, to produce a
metal alloy, always come in two forms, using the metal either as dust
or as an ingot.  If the secondary ingredient is also a metal, it must
be supplied in the same form as the base ingredient.  The output alloy
is also returned in the same form.
### Alloying
Input: two ingredients of the same form - lump or dust
Example: 2x copper ingots + zinc ingot -> 3x brass ingot (alloying)
Output: resulting alloy, as an ingot
The same will also work for dust ingredients, resulting in brass dist.
Example: 2x copper ingots + 1x zinc ingot -> 3x brass ingot (alloying)
### iron and its alloys ###
Note that grinding before alloying is the preferred method to gain more output.
Iron forms several important alloys.  In real-life history, iron was the
second metal to be used as the base component of deliberately-constructed
alloys (the first was copper), and it was the first metal whose working
required processes of any metallurgical sophistication.  The game
mechanics around iron broadly imitate the historical progression of
processes around it, rather than the less-varied modern processes.
#### iron and its alloys
The two-component alloying system of iron with carbon is of huge
importance, both in the game and in real life.  The basic Minetest game
doesn't distinguish between these pure iron and these alloys at all,
but technic introduces a distinction based on the carbon content, and
renames some items of the basic game accordingly.
Historically iron was the first metal whose working required processes of any
metallurgical sophistication. The mod's mechanics around iron broadly imitate
the historical progression of processes around it to get more variety.
The iron/carbon spectrum is represented in the game by three metal
substances: wrought iron, carbon steel, and cast iron.  Wrought iron
has low carbon content (less than 0.25%), resists shattering, and
is easily welded, but is relatively soft and susceptible to rusting.
In real-life history it was used for rails, gates, chains, wire, pipes,
fasteners, and other purposes.  Cast iron has high carbon content
(2.1% to 4%), is especially hard, and resists corrosion, but is
relatively brittle, and difficult to work.  Historically it was used
to build large structures such as bridges, and for cannons, cookware,
and engine cylinders.  Carbon steel has medium carbon content (0.25%
to 2.1%), and intermediate properties: moderately hard and also tough,
somewhat resistant to corrosion.  In real life it is now used for most
of the purposes previously satisfied by wrought iron and many of those
of cast iron, but has historically been especially important for its
use in swords, armor, skyscrapers, large bridges, and machines.
Notable alloys:
In real-life history, the first form of iron to be refined was
wrought iron, which is nearly pure iron, having low carbon content.
It was produced from ore by a low-temperature furnace process (the
"bloomery") in which the ore/iron remains solid and impurities (slag)
are progressively removed by hammering ("working", hence "wrought").
This began in the middle East, around 1800 BCE.
 * Wrought iron: <0.25% carbon
     * Resists shattering but is relatively soft.
     * Known since: 1800 BC (approx.)
 * Cast iron: 2.1% to 4% carbon.
     * Especially hard and rather corrosion-resistant
     * Known since: 1200 BC (approx.)
 * Carbon steel: 0.25% to 2.1% carbon.
     * Intermediate of the two above.
     * Known since: 1600 AD (approx.)
Historically, the next forms of iron to be refined were those of high
carbon content.  This was the result of the development of a more
sophisticated kind of furnace, the blast furnace, capable of reaching
higher temperatures.  The real advantage of the blast furnace is that it
melts the metal, allowing it to be cast straight into a shape supplied by
a mould, rather than having to be gradually beaten into the desired shape.
A side effect of the blast furnace is that carbon from the furnace's fuel
is unavoidably incorporated into the metal.  Normally iron is processed
twice through the blast furnace: once producing "pig iron", which has
very high carbon content and lots of impurities but lower melting point,
casting it into rough ingots, then remelting the pig iron and casting it
into the final moulds.  The result is called "cast iron".  Pig iron was
first produced in China around 1200 BCE, and cast iron later in the 5th
century BCE.  Incidentally, the Chinese did not have the bloomery process,
so this was their first iron refining process, and, unlike the rest of
the world, their first wrought iron was made from pig iron rather than
directly from ore.
Technic introduces a distinction based on the carbon content, and renames some
items of the basic game accordingly. Iron and Steel are now distinguished.
Carbon steel, with intermediate carbon content, was developed much later,
in Europe in the 17th century CE.  It required a more sophisticated
process, because the blast furnace made it extremely difficult to achieve
a controlled carbon content.  Tweaks of the blast furnace would sometimes
produce an intermediate carbon content by luck, but the first processes to
reliably produce steel were based on removing almost all the carbon from
pig iron and then explicitly mixing a controlled amount of carbon back in.
Notable references:
In the game, the bloomery process is represented by ordinary cooking
or grinding of an iron lump.  The lump represents unprocessed ore,
and is identified only as "iron", not specifically as wrought iron.
This standard refining process produces dust or an ingot which is
specifically identified as wrought iron.  Thus the standard refining
process produces the (nearly) pure metal.
 * https://en.wikipedia.org/wiki/Iron
 * https://en.wikipedia.org/wiki/Stainless_steel
 * ... plus many more.
Cast iron is trickier.  You might expect from the real-life notes above
that cooking an iron lump (representing ore) would produce pig iron that
can then be cooked again to produce cast iron.  This is kind of the case,
but not exactly, because as already noted cooking an iron lump produces
wrought iron.  The game doesn't distinguish between low-temperature
and high-temperature cooking processes: the same furnace is used not
just to cast all kinds of metal but also to cook food.  So there is no
distinction between cooking processes to produce distinct wrought iron
and pig iron.  But repeated cooking *is* available as a game mechanic,
and is indeed used to produce cast iron: re-cooking a wrought iron ingot
produces a cast iron ingot.  So pig iron isn't represented in the game as
a distinct item; instead wrought iron stands in for pig iron in addition
to its realistic uses as wrought iron.
Processes:
Carbon steel is produced by a more regular in-game process: alloying
wrought iron with coal dust (which is essentially carbon).  This bears
a fair resemblance to the historical development of carbon steel.
This alloying recipe is relatively time-consuming for the amount of
material processed, when compared against other alloying recipes, and
carbon steel is heavily used, so it is wise to alloy it in advance,
when you're not waiting for it.
 * Iron -> Wrought iron (melting)
 * Wrought iron -> Cast iron (melting)
 * Wrought iron + coal dust -> Carbon steel (alloying)
 * Carbon steel + coal dust -> Cast iron (alloying)
 * Carbon steel + chromium -> Stainless steel (alloying)
There are additional recipes that permit all three of these types of iron
to be converted into each other.  Alloying carbon steel again with coal
dust produces cast iron, with its higher carbon content.  Cooking carbon
steel or cast iron produces wrought iron, in an abbreviated form of the
bloomery process.
Reversible processes:
There's one more iron alloy in the game: stainless steel.  It is managed
in a completely regular manner, created by alloying carbon steel with
chromium.
 * Cast iron -> Wrought iron (melting)
 * Carbon steel -> Wrought iron (melting)
### uranium enrichment ###
Check your preferred crafting guide for more information.
### Uranium enrichment
When uranium is to be used to fuel a nuclear reactor, it is not
sufficient to merely isolate and refine uranium metal.  It is necessary
@@ -460,35 +410,15 @@
industrial processes
--------------------
### alloying ###
### Alloying
In technic, alloying is a way of combining items to create other items,
distinct from standard crafting.  Alloying always uses inputs of exactly
two distinct types, and produces a single output.  Like cooking, which
takes a single input, it is performed using a powered machine, known
generically as an "alloy furnace".  An alloy furnace always has two
input slots, and it doesn't matter which way round the two ingredients
are placed in the slots.  Many alloying recipes require one or both
slots to contain a stack of more than one of the ingredient item: the
quantity required of each ingredient is part of the recipe.
In Technic, alloying is a way of combining items to create other items,
distinct from standard crafting. Alloying always uses inputs of exactly
two distinct types, and produces a single output.
As with the furnaces used for cooking, there are multiple kinds of alloy
furnace, powered in different ways.  The most-used alloy furnaces are
electrically powered.  There is also an alloy furnace that is powered
by directly burning fuel, just like the basic cooking furnace.  Building
almost any electrical machine, including the electrically-powered alloy
furnaces, requires a machine casing component, one ingredient of which
is brass, an alloy.  It is therefore necessary to use the fuel-fired
alloy furnace in the early part of the game, on the way to building
electrical machinery.
Check your preferred crafting guide for more information.
Alloying recipes are mainly concerned with metals.  These recipes
combine a base metal with some other element, most often another metal,
to produce a new metal.  This is discussed in the section on metal.
There are also a few alloying recipes in which the base ingredient is
non-metallic, such as the recipe for the silicon wafer.
### grinding, extracting, and compressing ###
### Grinding, extracting, and compressing
Grinding, extracting, and compressing are three distinct, but very
similar, ways of converting one item into another.  They are all quite
@@ -562,57 +492,17 @@
It recovers both components of binary metal/metal alloys.  It can't
recover the carbon from steel or cast iron.
chests
Chests
------
The technic mod replaces the basic Minetest game's single type of
chest with a range of chests that have different sizes and features.
The chest types are identified by the materials from which they are made;
the better chests are made from more exotic materials.  The chest types
form a linear sequence, each being (with one exception noted below)
strictly more powerful than the preceding one.  The sequence begins with
the wooden chest from the basic game, and each later chest type is built
by upgrading a chest of the preceding type.  The chest types are:
See [GitHub Wiki / Chests](https://github.com/minetest-mods/technic/wiki/Chests)
1.  wooden chest: 8&times;4 (32) slots
2.  iron chest: 9&times;5 (45) slots
3.  copper chest: 12&times;5 (60) slots
4.  silver chest: 12&times;6 (72) slots
5.  gold chest: 15&times;6 (90) slots
6.  mithril chest: 15&times;6 (90) slots
Features of extended chests:
The iron and later chests have the ability to sort their contents,
when commanded by a button in their interaction forms.  Item types are
sorted in the same order used in the unified\_inventory craft guide.
The copper and later chests also have an auto-sorting facility that can
be enabled from the interaction form.  An auto-sorting chest automatically
sorts its contents whenever a player closes the chest.  The contents will
then usually be in a sorted state when the chest is opened, but may not
be if pneumatic tubes have operated on the chest while it was closed,
or if two players have the chest open simultaneously.
 * Larger storage space
 * Labelling
 * Advanced item sorting
The silver and gold chests, but not the mithril chest, have a built-in
sign-like capability.  They can be given a textual label, which will
be visible when hovering over the chest.  The gold chest, but again not
the mithril chest, can be further labelled with a colored patch that is
visible from a moderate distance.
The mithril chest is currently an exception to the upgrading system.
It has only as many inventory slots as the preceding (gold) type, and has
fewer of the features.  It has no feature that other chests don't have:
it is strictly weaker than the gold chest.  It is planned that in the
future it will acquire some unique features, but for now the only reason
to use it is aesthetic.
The size of the largest chests is dictated by the maximum size
of interaction form that the game engine can successfully display.
If in the future the engine becomes capable of handling larger forms,
by scaling them to fit the screen, the sequence of chest sizes will
likely be revised.
As with the chest of the basic Minetest game, each chest type comes
in both locked and unlocked flavors.  All of the chests work with the
pneumatic tubes of the pipeworks mod.
radioactivity
-------------
@@ -750,115 +640,44 @@
Tricky shine paths can also be addressed by just keeping players out of
the dangerous area.
electrical power
----------------
## Electrical power
Most machines in technic are electrically powered.  To operate them it is
necessary to construct an electrical power network.  The network links
together power generators and power-consuming machines, connecting them
using power cables.
Electrical networks in Technic are defined by a single tier (see below)
and consist of:
There are three tiers of electrical networking: low voltage (LV),
medium voltage (MV), and high voltage (HV).  Each network must operate
at a single voltage, and most electrical items are specific to a single
voltage.  Generally, the machines of higher tiers are more powerful,
but consume more energy and are more expensive to build, than machines
of lower tiers.  It is normal to build networks of all three tiers,
in ascending order as one progresses through the game, but it is not
strictly necessary to do this.  Building HV equipment requires some parts
that can only be manufactured using electrical machines, either LV or MV,
so it is not possible to build an HV network first, but it is possible
to skip either LV or MV on the way to HV.
 * 1x Switching Station (central management unit)
     * Any further stations are disabled automatically
 * Electricity producers (PR)
 * Electricity consumers/receivers (RE)
 * Accumulators/batteries (BA)
Each voltage has its own cable type, with distinctive insulation.  Cable
segments connect to each other and to compatible machines automatically.
Incompatible electrical items don't connect.  All non-cable electrical
items must be connected via cable: they don't connect directly to each
other.  Most electrical items can connect to cables in any direction,
but there are a couple of important exceptions noted below.
### Tiers
To be useful, an electrical network must connect at least one power
generator to at least one power-consuming machine.  In addition to these
items, the network must have a "switching station" in order to operate:
no energy will flow without one.  Unlike most electrical items, the
switching station is not voltage-specific: the same item will manage
a network of any tier.  However, also unlike most electrical items,
it is picky about the direction in which it is connected to the cable:
the cable must be directly below the switching station.
 * LV: Low Voltage. Low material costs but is slower.
 * MV: Medium Voltage. Higher processing speed.
 * HV: High Voltage. High material costs but is the fastest.
Hovering over a network's switching station will show the aggregate energy
supply and demand, which is useful for troubleshooting.  Electrical energy
is measured in "EU", and power (energy flow) in EU per second (EU/s).
Energy is shifted around a network instantaneously once per second.
Tiers can be converted from one to another using the Supply Converter node.
Its top connects to the input, the bottom to the output network. Configure
the input power by right-clicking it.
In a simple network with only generators and consumers, if total
demand exceeds total supply then no energy will flow, the machines
will do nothing, and the generators' output will be lost.  To handle
this situation, it is recommended to add a battery box to the network.
A battery box will store generated energy, and when enough has been
stored to run the consumers for one second it will deliver it to the
consumers, letting them run part-time.  It also stores spare energy
when supply exceeds demand, to let consumers run full-time when their
demand occasionally peaks above the supply.  More battery boxes can
be added to cope with larger periods of mismatched supply and demand,
such as those resulting from using solar generators (which only produce
energy in the daytime).
### Machine upgrade slots
When there are electrical networks of multiple tiers, it can be appealing
to generate energy on one tier and transfer it to another.  The most
direct way to do this is with the "supply converter", which can be
directly wired into two networks.  It is another tier-independent item,
and also particular about the direction of cable connections: it must
have the cable of one network directly above, and the cable of another
network directly below.  The supply converter demands 10000 EU/s from
the network above, and when this network gives it power it supplies 9000
EU/s to the network below.  Thus it is only 90% efficient, unlike most of
the electrical system which is 100% efficient in moving energy around.
To transfer more than 10000 EU/s between networks, connect multiple
supply converters in parallel.
Generally, machines of MV and HV tiers have two upgrade slots.
Only specific items will have any upgrading effect. The occupied slots do
count, but not the actual stack size.
powered machines
----------------
**Type 1: Energy upgrade**
### powered machine tiers ###
Consists of any battery item. Reduces the machine's power consumption
regardless the charge of the item.
Each powered machine takes its power in some specific form, being
either fuel-fired (burning fuel directly) or electrically powered at
some specific voltage.  There is a general progression through the
game from using fuel-fired machines to electrical machines, and to
higher electrical voltages.  The most important kinds of machine come
in multiple variants that are powered in different ways, so the earlier
ones can be superseded.  However, some machines are only available for
a specific power tier, so the tier can't be entirely superseded.
**Type 2: Tube upgrade**
### powered machine upgrades ###
Consists of a control logic unit item. Ejects processed items into pneumatic
tubes for quicker processing.
Some machines have inventory slots that are used to upgrade them in
some way.  Generally, machines of MV and HV tiers have two upgrade slots,
and machines of lower tiers (fuel-fired and LV) do not.  Any item can
be placed in an upgrade slot, but only specific items will have any
upgrading effect.  It is possible to have multiple upgrades of the same
type, but this can't be achieved by stacking more than one upgrade item
in one slot: it is necessary to put the same kind of item in more than one
upgrade slot.  The ability to upgrade machines is therefore very limited.
Two kinds of upgrade are currently possible: an energy upgrade and a
tube upgrade.
An energy upgrade consists of a battery item, the same kind of battery
that serves as a mobile energy store.  The effect of an energy upgrade
is to improve in some way the machine's use of electrical energy, most
often by making it use less energy.  The upgrade effect has no relation
to energy stored in the battery: the battery's charge level is irrelevant
and will not be affected.
A tube upgrade consists of a control logic unit item.  The effect of a
tube upgrade is to make the machine able, or more able, to eject items
it has finished with into pneumatic tubes.  The machines that can take
this kind of upgrade are in any case capable of accepting inputs from
pneumatic tubes.  These upgrades are essential in using powered machines
as components in larger automated systems.
### tubes with powered machines ###
### Machines + Tubes (pipeworks)
Generally, powered machines of MV and HV tiers can work with pneumatic
tubes, and those of lower tiers cannot.  (As an exception, the fuel-fired
modpack.conf
@@ -1 +1,3 @@
name = technic
description = technic
min_minetest_version = 5.0
settingtypes.txt
New file
@@ -0,0 +1,9 @@
#    Safety feature for the chainsaw tool aimed to prevent cutting structures
#    built by players.
#
#    Trunk nodes generated by mapgen have a rotation of '0' whereas manually
#    placed trunks usually have another value. However, some mods might generate
#    trees with rotation != 0, which renders the chainsaw useless on them.
#
#    Disabling this feature will sacrifice safety for convenience.
technic_safe_chainsaw (Chainsaw safety feature) bool true
technic/README.md
@@ -1,14 +1,16 @@
Technic
=======
# Technic (main mod)
License
-------
## License
### Code
Copyright (C) 2012-2014 Maciej Kasatkin (RealBadAngel)
Technic chests code is licensed under the GNU LGPLv2+.
Texture licenses:
### Textures
BlockMen modified by Zefram (CC BY-SA 3.0):
  * technic_chernobylite_block.png
@@ -24,15 +26,29 @@
leftshift (CC BY-SA 3.0):
  * technic_river_water_can.png
Neuromancer vis Minetest Game (CC BY-SA 3.0)
 * `technic_*_electric_furnace_*.png` (derived)
[LB Photo Realism Reload](https://www.curseforge.com/minecraft/texture-packs/lb-photo-realism-reload) (CC 0)
 * `technic_geothermal_*.png` (derived)
 * `technic_water_mill_*.png` (derived)
 * `technic_*_alloy_furnace_*.png` (derived)
 * `technic_*_compressor_*.png` (derived)
 * `technic_*_grinder_*.png` (derived)
RealBadAngel: (WTFPL)
  * Everything else.
CC BY-SA 3.0: <http://creativecommons.org/licenses/by-sa/3.0/>
Sound licenses:
### Sounds
veikk0 (Veikko Mäkelä) (CC BY-SA 4.0):
  * technic_hv_nuclear_reactor_siren_danger_loop.ogg
    * Derived from "Nuclear alarm.wav" by Freesound.org user rene___ from <https://freesound.org/people/rene___/sounds/56778/>. Originally licensed under CC0 1.0 <https://creativecommons.org/publicdomain/zero/1.0/>
CC BY-SA 4.0: <https://creativecommons.org/licenses/by-sa/4.0/>
### References
CC BY-SA 3.0: http://creativecommons.org/licenses/by-sa/3.0/
CC BY-SA 4.0: https://creativecommons.org/licenses/by-sa/4.0/
technic/doc/api.md
@@ -2,6 +2,10 @@
This file documents the functions within the technic modpack for use in mods.
[Switch to plaintext format](https://raw.githubusercontent.com/minetest-mods/technic/master/technic/doc/api.md)
**Undocumented API may change at any time.**
## Tiers
Tier are network types. List of pre-registered tiers:
@@ -31,39 +35,94 @@
* `technic.is_tier_cable(nodename, tier)`
    * Tells whether the node `nodename` is the cable of the tier `tier`.
    * Short version of `technic.get_cable_tier(nodename) == tier`
* `technic.register_cable_tier(nodename, tier)`
    * Register user defined cable to list of known tier cables.
    * `nodename`: string, name of the node
    * `tier`: string, tier name
## Machines
The machine type indicates the direction of power flow.
List of pre-registered machine types:
* `technic.receiver = "RE"` e.g. grinder
* `technic.producer = "PR"` e.g. solar panel
* `technic.receiver = "RE"`: consumes energy. e.g. grinder
* `technic.producer = "PR"`: provides energy. e.g. solar panel
* `technic.producer_receiver = "PR_RE"` supply converter
* `technic.battery  = "BA"` e.g. LV battery box
* `technic.battery  = "BA"`: stores energy. e.g. LV battery box
Available functions:
* `technic.register_base_machine(data)`
    * Registers a new node and defines the underlying machine behaviour. `data` fields:
    * `tier`: string, see #Tiers
    * `typename`: string, equivalent to the processing type registered
      by `technic.register_recipe`. Examples: `"cooking"` `"alloy"`
    * `machine_name`: string, node name
    * `machine_desc`: string, node description
    * `demand`: table, EU consumption values for each upgrade level.
      Up to three indices. Index 1 == no upgrade. Example: `{3000, 2000, 1000}`.
    * `upgrade`: (boolean), whether to add upgrade slots
    * `modname`: (string), mod origin
    * `tube`: (boolean), whether the machine has Pipeworks connectivity
    * `can_insert`: (func), see Pipeworks documentation
        * Accepts all inputs by default, if `tube = 1`
        * See also: `technic.can_insert_unique_stack`
    * `insert_object`: (func), see Pipeworks documentation
        * Accepts all inputs by default, if `tube = 1`
        * See also: `technic.insert_object_unique_stack`
    * `connect_sides`: (table), see Lua API documentation. Defaults to all directions but front.
* `technic.register_machine(tier, nodename, machine_type)`
    * Register an existing node as machine, bound to the network tier
    * `tier`: see `register_tier`
    * `tier`: string, see #Tiers
    * `nodename`: string, node name
    * `machine_type`: string, following options are possible:
        * `"RE"`: Receiver
        * `"PR"`: Producer
        * `"BA"`: Battery, energy storage
        * `technic.receiver = "RE"`: Consumes energy
        * `technic.producer = "PR"`: Provides energy
        * `technic.battery = "BA"`: Energy storage
    * See also `Machine types`
Functions to use for callbacks:
Callbacks for pipeworks item transfer:
* `technic.can_insert_unique_stack(pos, node, stack, direction)`
* `technic.insert_object_unique_stack(pos, node, stack, direction)`
    * Functions for the parameters `can_insert` and `insert_object` to avoid
      filling multiple inventory slots with same type of item.
### Specific machines
* `technic.register_solar_array(data)`
    * data is a table (TODO)
### Recipes
* `technic.register_recipe_type(typename, recipedef)`
    * Registers a new recipe type used for machine processing
    * `typename`: string, name of the recipe type
    * Fields of `recipedef`:
        * `description`: string, descriptor of the recipe type
        * `input_size`: (numeric), count of input ItemStacks. default 1
        * `output_size`: (numeric), count of output ItemStacks. default 1
* `technic.register_recipe(recipe)`
    * Registers a individual input/output recipe. Fields of `recipe`:
    * `input`: table, integer-indexed list of input ItemStacks.
    * `output`: table/ItemStack, single output or list of output ItemStacks.
    * `time`: numeric, process time in seconds.
* `technic.get_recipe(typename, items)`
    * `typename`: string, see `technic.register_recipe_type`
    * `items`: table, integer-indexed list of input ItemStacks.
    * Returns: `recipe` table on success, `nil` otherwise
The following functions can be used to register recipes for
a specific machine type:
* Centrifuge
    * `technic.register_separating_recipe(recipe)`
* Compressor
    * `technic.register_compressor_recipe(recipe)`
* Furnaces (electric, normal)
    * `minetest.register_recipe(recipe)`
* Extractor
    * `technic.register_extractor_recipe(recipe)`
* Freezer
    * `technic.register_freezer_recipe(recipe)`
* Grinder
    * `technic.register_grinder_recipe(recipe)`
## Tools
@@ -101,6 +160,11 @@
    * If `technic.power_tools[itemstack:get_name()]` is `nil` (or `false`), this
      function does nothing, else that value is the maximum charge.
    * The itemstack metadata is changed to contain the charge.
* `technic.get_charge(itemstack)`
    * Returns the charge and max charge of the given itemstack.
    * If the itemstack is not an RE chargeable item, both return values will be zero.
* `technic.set_charge(itemstack, charge)`
    * Modifies the charge of the given itemstack.
### Node-specific
* `technic.get_or_load_node(pos)`
@@ -132,14 +196,31 @@
Additional definition fields:
* `wear_represents = "string"`
* `<itemdef>.wear_represents = "string"`
    * Specifies how the tool wear level is handled. Available modes:
        * `"mechanical_wear"`: represents physical damage
        * `"technic_RE_charge"`: represents electrical charge
* `<itemdef>.technic_run(pos, node)`
    * This function is currently used to update the node.
* `<itemdef>.technic_run = function(pos, node) ...`
    * This callback is used to update the node.
      Modders have to manually change the information about supply etc. in the
      node metadata.
    * Technic-registered machines use this callback by default.
* `<itemdef>.technic_disabled_machine_name = "string"`
    * Specifies the machine's node name to use when it's not connected connected to a network
* `<itemdef>.technic_on_disable = function(pos, node) ...`
    * This callback is run when the machine is no longer connected to a technic-powered network.
* `<itemdef>.technic_get_charge = function(itemstack) ...`
    * Optional callback to overwrite the default charge behaviour.
    * `itemstack`: ItemStack, the tool to analyse
    * Return values:
        * `charge`: Electrical charge of the tool
        * `max_charge`: Upper charge limit
    * Etc. `local charge, maxcharge = itemdef.technic_get_charge(itemstack)`
* `<itemdef>.technic_set_charge = function(itemstack, charge) ...`
    * Optional callback to overwrite the default charge behaviour.
    * `itemstack`: ItemStack, the tool to update
    * `charge`: numeric, value between `0` and `max_charge`
## Node Metadata fields
Nodes connected to the network will have one or more of these parameters as meta
@@ -160,33 +241,30 @@
multiple tiers (or networks).
## Switching Station mechanics
## Manual: Network basics
The switching station is the center of all power distribution on an electric
network.
network. This node is used to calculate the power supply of the network and
to distribute the power across nodes.
The station collects power from sources (PR), distributes it to sinks (RE),
and uses the excess/shortfall to charge and discharge batteries (BA).
The switching station is the center of all electricity distribution. It collects
power from sources (PR), distributes it to sinks (RE), and uses the
excess/shortfall to charge and discharge batteries (BA).
For now, all supply and demand values are expressed in kW.
As a thumb of rule, "EU" (energy unit) values are expressed in kW.
It works like this:
 All PR,BA,RE nodes are indexed and tagged with the switching station.
The tagging is a workaround to allow more stations to be built without allowing
a cheat with duplicating power.
 All the RE nodes are queried for their current EU demand. Those which are off
would require no or a small standby EU demand, while those which are on would
require more.
If the total demand is less than the available power they are all updated with
the demand number.
If any surplus exists from the PR nodes the batteries will be charged evenly
with this.
If the total demand requires draw on the batteries they will be discharged
evenly.
Network functionality:
If the total demand is more than the available power all RE nodes will be shut
down. We have a brown-out situation.
Hence for now all the power distribution logic resides in this single node.
1. All PR, BA, RE nodes are indexed and tagged with one switching station.
   The tagging is a workaround to allow more stations to be built without allowing
   a cheat with duplicating power.
2. All the RE nodes are queried for their current EU demand.
   If the total demand is less than the available power they are all updated
   with the demand number.
3. BA nodes are evenly charged from energy surplus.
4. Excess power draw will discharge batteries evenly.
5. If the total demand is more than the available power all RE nodes will be shut
   down. We have a brown-out situation.
## Deprecated functions
technic/helpers.lua
@@ -65,15 +65,26 @@
end
--- Returns the meta of an item
-- Gets overridden when legacy.lua is loaded
function technic.get_stack_meta(itemstack)
    return itemstack:get_meta()
end
--- Same as technic.get_stack_meta for cans
function technic.get_stack_meta_cans(itemstack)
    return itemstack:get_meta()
end
--- Fully charge RE chargeable item.
-- Must be defined early to reference in item definitions.
function technic.refill_RE_charge(stack)
    local max_charge = technic.power_tools[stack:get_name()]
    if not max_charge then return stack end
    local meta = technic.get_stack_meta(stack)
    meta:set_int("technic:charge", max_charge)
    technic.set_RE_wear(stack, max_charge, max_charge)
    local meta = minetest.deserialize(stack:get_metadata()) or {}
    meta.charge = max_charge
    stack:set_metadata(minetest.serialize(meta))
    return stack
end
technic/init.lua
@@ -1,6 +1,10 @@
-- Minetest 0.4.7 mod: technic
-- namespace: technic
-- (c) 2012-2013 by RealBadAngel <mk@realbadangel.pl>
if not minetest.get_translator then
    error("[technic] Your Minetest version is no longer supported."
        .. " (version < 5.0.0)")
end
local load_start = os.clock()
@@ -16,7 +20,17 @@
if rawget(_G, "intllib") then
    technic.getter = intllib.Getter()
else
    technic.getter = function(s,a,...)if a==nil then return s end a={a,...}return s:gsub("(@?)@(%(?)(%d+)(%)?)",function(e,o,n,c)if e==""then return a[tonumber(n)]..(o==""and c or"")else return"@"..o..n..c end end) end
    -- Intllib copypasta: TODO replace with the client-side translation API
    technic.getter = function(s,a,...)
        if a==nil then return s end
        a={a,...}
        return s:gsub("(@?)@(%(?)(%d+)(%)?)", function(e,o,n,c)
            if e==""then
                return a[tonumber(n)]..(o==""and c or"")
            end
            return "@"..o..n..c
        end)
    end
end
local S = technic.getter
technic/legacy.lua
@@ -39,3 +39,40 @@
    minetest.register_alias("technic:lv_cable"..i, "technic:lv_cable")
end
-- Item meta
-- Meta keys that have changed
technic.legacy_meta_keys = {
    ["charge"] = "technic:charge",
}
-- Converts legacy itemstack metadata string to itemstack meta and returns the ItemStackMetaRef
function technic.get_stack_meta(itemstack)
    local meta = itemstack:get_meta()
    local legacy_string = meta:get("") -- Get deprecated metadata
    if legacy_string then
        local legacy_table = minetest.deserialize(legacy_string)
        if legacy_table then
            local table = meta:to_table()
            for k, v in pairs(legacy_table) do
                table.fields[technic.legacy_meta_keys[k] or k] = v
            end
            meta:from_table(table)
        end
        meta:set_string("", "") -- Remove deprecated metadata
    end
    return meta
end
-- Same as technic.get_stack_meta for cans.
-- (Cans didn't store a serialized table in the legacy metadata string, but just a number.)
function technic.get_stack_meta_cans(itemstack)
    local meta = itemstack:get_meta()
    local legacy_string = meta:get("") -- Get deprecated metadata
    if legacy_string then
        meta:set_string("can_level", legacy_string)
        meta:set_string("", "") -- Remove deprecated metadata
        return meta
    end
    return meta
end
technic/machines/HV/forcefield.lua
@@ -112,10 +112,13 @@
    else
        formspec = formspec.."button[0,1;5,1;mesecon_mode_0;"..S("Controlled by Mesecon Signal").."]"
    end
    -- TODO: String replacement with %s will stop working with client-side translations
    if meta:get_int("enabled") == 0 then
        formspec = formspec.."button[0,1.75;5,1;enable;"..S("%s Disabled"):format(S("%s Forcefield Emitter"):format("HV")).."]"
        formspec = formspec.."button[0,1.75;5,1;enable;"..
            S("%s Disabled"):format(S("%s Forcefield Emitter"):format("HV")).."]"
    else
        formspec = formspec.."button[0,1.75;5,1;disable;"..S("%s Enabled"):format(S("%s Forcefield Emitter"):format("HV")).."]"
        formspec = formspec.."button[0,1.75;5,1;disable;"..
            S("%s Enabled"):format(S("%s Forcefield Emitter"):format("HV")).."]"
    end
    meta:set_string("formspec", formspec)
end
technic/machines/HV/nuclear_reactor.lua
@@ -31,24 +31,26 @@
})
local function make_reactor_formspec(meta)
    local f = "size[8,9]"..
    "label[0,0;"..S("Nuclear Reactor Rod Compartment").."]"..
    "list[current_name;src;2,1;3,2;]"..
    "list[current_player;main;0,5;8,4;]"..
    "listring[]"..
    "button[5.5,1.5;2,1;start;Start]"..
    "checkbox[5.5,2.5;autostart;automatic Start;"..meta:get_string("autostart").."]"
    local f =
        "formspec_version[4]"..
        "size[10.75,10.75]"..
        "label[0.2,0.4;"..S("Nuclear Reactor Rod Compartment").."]"..
        "list[current_name;src;1.5,1;3,2;]"..
        "list[current_player;main;0.5,5.5;8,4;]"..
        "listring[]"..
        "button[5.7,1;2,1;start;Start]"..
        "checkbox[5.7,2.75;autostart;automatic Start;"..meta:get_string("autostart").."]"
    if not digiline_remote_path then
        return f
    end
    local digiline_enabled = meta:get_string("enable_digiline")
    f = f.."checkbox[0.5,2.8;enable_digiline;Enable Digiline;"..digiline_enabled.."]"
    f = f.."checkbox[1.5,3.75;enable_digiline;Enable Digiline channel;"..digiline_enabled.."]"
    if digiline_enabled ~= "true" then
        return f
    end
    return f..
        "button_exit[4.6,3.69;2,1;save;Save]"..
        "field[1,4;4,1;remote_channel;Digiline Remote Channel;${remote_channel}]"
        "field[2,4.2;4.25,1;remote_channel;;${remote_channel}]" ..
        "button_exit[6.5,4.2;2,1;save;Save]"
end
local SS_OFF = 0
@@ -140,8 +142,11 @@
--]]
local function reactor_structure_badness(pos)
    local vm = VoxelManip()
    -- Blast-resistant Concrete Block layer outer positions
    local pos1 = vector.subtract(pos, 3)
    local pos2 = vector.add(pos, 3)
    local MinEdge, MaxEdge = vm:read_from_map(pos1, pos2)
    local data = vm:get_data()
    local area = VoxelArea:new({MinEdge=MinEdge, MaxEdge=MaxEdge})
@@ -157,16 +162,19 @@
    for z = pos1.z, pos2.z do
    for y = pos1.y, pos2.y do
    for x = pos1.x, pos2.x do
        -- In the entire volume, make sure there is:
        local cid = data[area:index(x, y, z)]
        if x == pos1.x or x == pos2.x or
           y == pos1.y or y == pos2.y or
           z == pos1.z or z == pos2.z then
            -- r=3 : Blast-resistant Concrete Block shell
            if cid == c_blast_concrete then
                blast_layer = blast_layer + 1
            end
        elseif x == pos1.x+1 or x == pos2.x-1 or
               y == pos1.y+1 or y == pos2.y-1 or
               z == pos1.z+1 or z == pos2.z-1 then
            -- r=2 : Lead Block shell
            if cid == c_lead then
                lead_layer = lead_layer + 1
            elseif cid == c_steel then
@@ -175,6 +183,7 @@
        elseif x == pos1.x+2 or x == pos2.x-2 or
               y == pos1.y+2 or y == pos2.y-2 or
               z == pos1.z+2 or z == pos2.z-2 then
            -- r=1 : Water cooling
            if cid == c_water_source or cid == c_water_flowing then
                water_layer = water_layer + 1
            end
@@ -184,6 +193,8 @@
    end
    if steel_layer >= 96 then
        -- Legacy: convert stainless steel to lead
        -- Why don't we accept both without conversion?
        for z = pos1.z+1, pos2.z-1 do
        for y = pos1.y+1, pos2.y-1 do
        for x = pos1.x+1, pos2.x-1 do
@@ -206,6 +217,7 @@
    if water_layer > 25 then water_layer = 25 end
    if lead_layer > 96 then lead_layer = 96 end
    if blast_layer > 216 then blast_layer = 216 end
    -- Amount of missing blocks
    return (25 - water_layer) + (96 - lead_layer) + (216 - blast_layer)
end
@@ -297,7 +309,7 @@
        end
        meta:set_int("HV_EU_supply", 0)
        meta:set_int("burn_time", 0)
        meta:set_string("infotext", S("%s Idle"):format(reactor_desc))
        meta:set_string("infotext", S("@1 Idle", reactor_desc))
        technic.swap_node(pos, "technic:hv_nuclear_reactor_core")
        meta:set_int("structure_accumulated_badness", 0)
        siren_clear(pos, meta)
technic/machines/HV/quarry.lua
@@ -47,14 +47,19 @@
    local radius = meta:get_int("size")
    local diameter = radius*2 + 1
    local machine_name = S("%s Quarry"):format("HV")
    if meta:get_int("enabled") == 0 or meta:get_int("purge_on") == 1 then
        meta:set_string("infotext", S(meta:get_int("purge_on") == 1 and "%s purging cache" or "%s Disabled"):format(machine_name))
    local do_purge = meta:get_int("purge_on") == 1
    if meta:get_int("enabled") == 0 or do_purge then
        local infotext = do_purge and
            S("%s purging cache") or S("%s Disabled")
        meta:set_string("infotext", infotext:format(machine_name))
        meta:set_int("HV_EU_demand", 0)
    elseif meta:get_int("dug") == diameter*diameter * (quarry_dig_above_nodes+1+quarry_max_depth) then
        meta:set_string("infotext", S("%s Finished"):format(machine_name))
        meta:set_int("HV_EU_demand", 0)
    else
        meta:set_string("infotext", S(meta:get_int("HV_EU_input") >= quarry_demand and "%s Active" or "%s Unpowered"):format(machine_name))
        local infotext = meta:get_int("HV_EU_input") >= quarry_demand
            and S("%s Active") or S("%s Unpowered")
        meta:set_string("infotext", infotext:format(machine_name))
        meta:set_int("HV_EU_demand", quarry_demand)
    end
end
@@ -105,6 +110,40 @@
    end
end
-- Determines whether the quarry can dig the node at "pos"
-- "startpos" is located a few nodes above the quarry in South West direction (X-, Z-)
-- Returns the node to dig (to avoid double minetest.get_node lookup)
local function quarry_can_dig_node(startpos, pos, quarry_owner)
    if minetest.is_protected(pos, quarry_owner) then
        return nil
    end
    local node = technic.get_or_load_node(pos) or minetest.get_node(pos)
    local def = minetest.registered_nodes[node.name] or {diggable=false}
    -- doors mod among other thing does NOT like a nil digger...
    local fakedigger = pipeworks.create_fake_player({
        name = quarry_owner
    })
    if not def.diggable or (def.can_dig and not def.can_dig(pos, fakedigger)) then
        return nil
    end
    -- Find airlike nodes on top of the current node. The entire Y column must be free.
    for ay = pos.y+1, startpos.y do
        local checkpos = {x=pos.x, y=ay, z=pos.z}
        local checknode = technic.get_or_load_node(checkpos) or minetest.get_node(checkpos)
        local cdef = minetest.registered_nodes[checknode.name] or {}
        local is_kind_of_gas = cdef.buildable_to and cdef.sunlight_propagates and not cdef.walkable
            and not cdef.diggable and (cdef.drawtype == "airlike" or cdef.drawtype == "glasslike")
        if not is_kind_of_gas then
            return nil
        end
    end
    return node
end
local function quarry_run(pos, node)
    local meta = minetest.get_meta(pos)
    local inv = meta:get_inventory()
@@ -148,35 +187,11 @@
                vector.new(0, -ry, 0)),
                vector.multiply(pdir, rp)),
                vector.multiply(qdir, rq))
            local can_dig = true
            if can_dig and minetest.is_protected and minetest.is_protected(digpos, owner) then
                can_dig = false
            end
            local dignode
            if can_dig then
                dignode = technic.get_or_load_node(digpos) or minetest.get_node(digpos)
                local dignodedef = minetest.registered_nodes[dignode.name] or {diggable=false}
                -- doors mod among other thing does NOT like a nil digger...
                local fakedigger = pipeworks.create_fake_player({
                    name = owner
                })
                if not dignodedef.diggable or (dignodedef.can_dig and not dignodedef.can_dig(digpos, fakedigger)) then
                    can_dig = false
                end
            end
            if can_dig then
                for ay = startpos.y, digpos.y+1, -1 do
                    local checkpos = {x=digpos.x, y=ay, z=digpos.z}
                    local checknode = technic.get_or_load_node(checkpos) or minetest.get_node(checkpos)
                    if checknode.name ~= "air" then
                        can_dig = false
                        break
                    end
                end
            end
            nd = nd + 1
            if can_dig then
            local dignode = quarry_can_dig_node(startpos, digpos, owner)
            if dignode then
                minetest.remove_node(digpos)
                local drops = minetest.get_node_drops(dignode.name, "")
                for _, dropped_item in ipairs(drops) do
technic/machines/LV/grinder.lua
@@ -9,5 +9,25 @@
    }
})
if (minetest.get_modpath('everness')) then
    minetest.register_craft({
        output = 'technic:lv_grinder',
        recipe = {
            {'everness:coral_desert_stone', 'default:diamond',        'everness:coral_desert_stone'},
            {'everness:coral_desert_stone', 'technic:machine_casing', 'everness:coral_desert_stone'},
            {'technic:granite',             'technic:lv_cable',       'technic:granite'},
        }
    })
    minetest.register_craft({
        output = 'technic:lv_grinder',
        recipe = {
            {'everness:forsaken_desert_stone', 'default:diamond',        'everness:forsaken_desert_stone'},
            {'everness:forsaken_desert_stone', 'technic:machine_casing', 'everness:forsaken_desert_stone'},
            {'technic:granite',                'technic:lv_cable',       'technic:granite'},
        }
    })
end
technic.register_grinder({tier="LV", demand={200}, speed=1})
technic/machines/LV/init.lua
@@ -22,3 +22,6 @@
dofile(path.."/compressor.lua")
dofile(path.."/music_player.lua")
-- NEW LV LAMPS
dofile(path.."/led.lua")
dofile(path.."/lamp.lua")
technic/machines/LV/lamp.lua
New file
@@ -0,0 +1,156 @@
-- LV Lamp
-- Illuminates a 7x7x3(H) volume below itself with light bright as the sun.
local S = technic.getter
local desc = S("@1 Lamp", S("LV"))
local active_desc = S("@1 Active", desc)
local unpowered_desc = S("@1 Unpowered", desc)
local off_desc = S("@1 Off", desc)
local demand = 50
-- Invisible light source node used for illumination
minetest.register_node("technic:dummy_light_source", {
    description = S("Dummy light source node"),
    inventory_image = "technic_dummy_light_source.png",
    wield_image = "technic_dummy_light_source.png",
    paramtype = "light",
    drawtype = "airlike",
    light_source = 14,
    sunlight_propagates = true,
    walkable = false,
    buildable_to = true,
    diggable = false,
    pointable = false,
    --drop = "",  -- Intentionally allowed to drop itself
    groups = {not_in_creative_inventory = 1}
})
local function illuminate(pos, active)
    local pos1 = {x = pos.x - 3, y = pos.y - 1, z = pos.z - 3}
    local pos2 = {x = pos.x + 3, y = pos.y - 3, z = pos.z + 3}
    local find_node = active and "air" or "technic:dummy_light_source"
    local set_node = {name = (active and "technic:dummy_light_source" or "air")}
    for _,p in pairs(minetest.find_nodes_in_area(pos1, pos2, find_node)) do
        minetest.set_node(p, set_node)
    end
end
local function lamp_run(pos, node)
    local meta = minetest.get_meta(pos)
    if meta:get_int("LV_EU_demand") == 0 then
        return  -- Lamp is turned off
    end
    local eu_input = meta:get_int("LV_EU_input")
    if node.name == "technic:lv_lamp_active" then
        if eu_input < demand then
            technic.swap_node(pos, "technic:lv_lamp")
            meta:set_string("infotext", unpowered_desc)
            illuminate(pos, false)
        else
            illuminate(pos, true)
        end
    elseif node.name == "technic:lv_lamp" then
        if eu_input >= demand then
            technic.swap_node(pos, "technic:lv_lamp_active")
            meta:set_string("infotext", active_desc)
            illuminate(pos, true)
        end
    end
end
local function lamp_toggle(pos, node, player)
    if not player or minetest.is_protected(pos, player:get_player_name()) then
        return
    end
    local meta = minetest.get_meta(pos)
    if meta:get_int("LV_EU_demand") == 0 then
        meta:set_string("infotext", active_desc)
        meta:set_int("LV_EU_demand", demand)
    else
        illuminate(pos, false)
        technic.swap_node(pos, "technic:lv_lamp")
        meta:set_string("infotext", off_desc)
        meta:set_int("LV_EU_demand", 0)
    end
end
local common_fields = {
    drawtype = "nodebox",
    node_box = {
        type = "fixed",
        fixed = {0.5,0.5,0.5,-0.5,-0.2,-0.5}
    },
    tiles = {
        "technic_lv_lamp_top.png",
        "technic_lv_lamp_bottom.png",
        "technic_lv_lamp_side.png",
        "technic_lv_lamp_side.png",
        "technic_lv_lamp_side.png",
        "technic_lv_lamp_side.png"
    },
    connect_sides = {"front", "back", "left", "right", "top"},
    can_dig = technic.machine_can_dig,
    technic_run = lamp_run,
    on_destruct = illuminate,
    on_rightclick = lamp_toggle
}
local ndef
ndef = {
    description = desc,
    groups = {cracky = 2, technic_machine = 1, technic_lv = 1},
    on_construct = function(pos)
        local meta = minetest.get_meta(pos)
        meta:set_string("infotext", desc)
        meta:set_int("LV_EU_demand", demand)
    end
}
for k, v in pairs(common_fields) do
    ndef[k] = v
end
minetest.register_node("technic:lv_lamp", ndef)
ndef = {
    description = active_desc,
    paramtype = "light",
    light_source = 14,
    drop = "technic:lv_lamp",
    groups = {cracky = 2, technic_machine = 1, technic_lv = 1, not_in_creative_inventory = 1},
    technic_on_disable = function(pos)
        illuminate(pos, false)
        technic.swap_node(pos, "technic:lv_lamp")
    end,
}
for k, v in pairs(common_fields) do
    ndef[k] = v
end
minetest.register_node("technic:lv_lamp_active", ndef)
technic.register_machine("LV", "technic:lv_lamp", technic.receiver)
technic.register_machine("LV", "technic:lv_lamp_active", technic.receiver)
minetest.register_craft({
    output = "technic:lv_lamp",
    recipe = {
        {"default:glass", "default:glass", "default:glass"},
        {"technic:lv_led", "technic:lv_led", "technic:lv_led"},
        {"mesecons_materials:glue", "technic:lv_cable", "mesecons_materials:glue"},
    }
})
technic/machines/LV/led.lua
New file
@@ -0,0 +1,95 @@
-- LED
-- Intended primarily as a core component for LED lamps.
local S = technic.getter
local desc = S("@1 LED", S("LV"))
local active_desc = S("@1 Active", desc)
local unpowered_desc = S("@1 Unpowered", desc)
local demand = 5
local function led_run(pos, node)
    local meta = minetest.get_meta(pos)
    local eu_input = meta:get_int("LV_EU_input")
    if eu_input < demand and node.name == "technic:lv_led_active" then
        technic.swap_node(pos, "technic:lv_led")
        meta:set_string("infotext", unpowered_desc)
    elseif eu_input >= demand and node.name == "technic:lv_led" then
        technic.swap_node(pos, "technic:lv_led_active")
        meta:set_string("infotext", active_desc)
    end
end
local common_fields = {
    drawtype = "nodebox",
    node_box = {
        type = "fixed",
        fixed = {0.5, 0.5, 0.5, -0.5, 0.3, -0.5}
    },
    tiles = {
        "technic_lv_led_top.png",
        "technic_lv_led.png",
        "technic_lv_led_side.png",
        "technic_lv_led_side2.png",
        "technic_lv_led_side2.png",
        "technic_lv_led_side2.png",
    },
    connect_sides = {"front", "back", "left", "right", "top", "bottom"},
    can_dig = technic.machine_can_dig,
    technic_run = led_run,
}
local ndef
ndef = {
    description = desc,
    inventory_image = "technic_lv_led_inv.png",
    sunlight_propagates = true,
    groups = {cracky = 2, technic_machine = 1, technic_lv = 1},
    on_construct = function(pos)
        local meta = minetest.get_meta(pos)
        meta:set_string("infotext", desc)
        meta:set_int("LV_EU_demand", demand)
    end,
}
for k, v in pairs(common_fields) do
    ndef[k] = v
end
minetest.register_node("technic:lv_led", ndef)
ndef = {
    description = active_desc,
    paramtype = "light",
    light_source = 9,
    drop = "technic:lv_led",
    groups = {cracky = 2, technic_machine = 1, technic_lv = 1, not_in_creative_inventory = 1},
    technic_on_disable = function(pos)
        technic.swap_node(pos, "technic:lv_led")
    end,
}
for k, v in pairs(common_fields) do
    ndef[k] = v
end
minetest.register_node("technic:lv_led_active", ndef)
technic.register_machine("LV", "technic:lv_led", technic.receiver)
technic.register_machine("LV", "technic:lv_led_active", technic.receiver)
minetest.register_craft({
    output = "technic:lv_led 2",
    recipe = {
        {"", "homedecor:plastic_sheeting", ""},
        {"homedecor:plastic_sheeting", "technic:doped_silicon_wafer", "homedecor:plastic_sheeting"},
        {"", "technic:fine_silver_wire", ""},
    }
})
technic/machines/MV/power_radiator.lua
@@ -36,7 +36,8 @@
end
-- Appliances:
--  has_supply: pos of supply node if the appliance has a power radiator near with sufficient power for the demand else ""
--  has_supply: pos of supply node if the appliance has a power radiator near
--              with sufficient power for the demand else ""
--  EU_demand: The power demand of the device.
--  EU_charge: Actual use. set to EU_demand if active==1
--  active: set to 1 if the device is on
technic/machines/other/anchor.lua
@@ -48,20 +48,23 @@
end
local function set_display(pos, meta)
    local ESC = minetest.formspec_escape
    meta:set_string("infotext", S(meta:get_int("enabled") ~= 0 and "%s Enabled" or "%s Disabled"):format(desc))
    meta:set_string("formspec",
        "size[5,3.5]"..
        "item_image[0,0;1,1;technic:admin_anchor]"..
        "label[1,0;"..minetest.formspec_escape(desc).."]"..
        "label[0,1;"..minetest.formspec_escape(S("Owner:").." "..meta:get_string("owner")).."]"..
        "label[1,0;"..ESC(desc).."]"..
        "label[0,1;"..ESC(S("Owner:").." "..meta:get_string("owner")).."]"..
        (meta:get_int("locked") == 0 and
            "button[3,1;2,1;lock;"..minetest.formspec_escape(S("Unlocked")).."]" or
            "button[3,1;2,1;unlock;"..minetest.formspec_escape(S("Locked")).."]")..
        "field[0.25,2.3;1,1;radius;"..minetest.formspec_escape(S("Radius:"))..";"..meta:get_int("radius").."]"..
            "button[3,1;2,1;lock;"..ESC(S("Unlocked")).."]" or
            "button[3,1;2,1;unlock;"..ESC(S("Locked")).."]")..
        "field[0.25,2.3;1,1;radius;"..ESC(S("Radius:"))..";"..meta:get_int("radius").."]"..
        (meta:get_int("enabled") == 0 and
            "button[3,2;2,1;enable;"..minetest.formspec_escape(S("Disabled")).."]" or
            "button[3,2;2,1;disable;"..minetest.formspec_escape(S("Enabled")).."]")..
        "label[0,3;"..minetest.formspec_escape(S("Keeping %d/%d map blocks loaded"):format(#currently_forceloaded_positions(meta), #compute_forceload_positions(pos, meta))).."]")
            "button[3,2;2,1;enable;"..ESC(S("Disabled")).."]" or
            "button[3,2;2,1;disable;"..ESC(S("Enabled")).."]")..
        "label[0,3;"..ESC(S("Keeping %d/%d map blocks loaded"):format(
            #currently_forceloaded_positions(meta), #compute_forceload_positions(pos, meta)
        )).."]")
end
minetest.register_node("technic:admin_anchor", {
@@ -80,7 +83,8 @@
    end,
    can_dig = function (pos, player)
        local meta = minetest.get_meta(pos)
        return meta:get_int("locked") == 0 or (player and player:is_player() and player:get_player_name() == meta:get_string("owner"))
        return meta:get_int("locked") == 0 or
            (player and player:is_player() and player:get_player_name() == meta:get_string("owner"))
    end,
    on_destruct = function (pos)
        local meta = minetest.get_meta(pos)
@@ -99,7 +103,11 @@
            forceload_off(meta)
            if fields.disable then meta:set_int("enabled", 0) end
            if fields.enable then meta:set_int("enabled", 1) end
            if fields.radius and string.find(fields.radius, "^[0-9]+$") and tonumber(fields.radius) < 256 then meta:set_int("radius", fields.radius) end
            if fields.radius
                    and string.find(fields.radius, "^[0-9]+$")
                    and tonumber(fields.radius) < 256 then
                meta:set_int("radius", fields.radius)
            end
            if meta:get_int("enabled") ~= 0 then
                forceload_on(pos, meta)
            end
technic/machines/other/frames.lua
@@ -88,22 +88,22 @@
    return false
end
local function table_empty(table)
    for _, __ in pairs(table) do
local function table_empty(what)
    for _ in pairs(what) do
        return false
    end
    return true
end
local function add_table(table, toadd)
local function add_table(what, toadd)
    local i = 1
    while true do
        local o = table[i]
        local o = what[i]
        if o == toadd then return end
        if o == nil then break end
        i = i + 1
    end
    table[i] = toadd
    what[i] = toadd
end
local function move_nodes_vect(poslist, vect, must_not_move, owner)
@@ -324,6 +324,7 @@
        on_rightclick = function(pos, node, placer, itemstack, pointed_thing)
            if is_supported_node(itemstack:get_name()) then
                -- Stripped down version of "core.item_place_node"
                if minetest.is_protected(pos, placer:get_player_name()) then
                    minetest.log("action", placer:get_player_name()
                        .. " tried to place " .. itemstack:get_name()
@@ -347,8 +348,7 @@
                end
                -- Run script hook
                local callback = nil
                for _, _ in ipairs(minetest.registered_on_placenodes) do
                for _, callback in ipairs(minetest.registered_on_placenodes) do
                    -- Copy pos and node because callback can modify them
                    local pos_copy = { x = pos.x, y = pos.y, z = pos.z }
                    local newnode_copy = { name = def.name, param1 = 0, param2 = 0 }
@@ -398,20 +398,6 @@
        local pos = vector.round(self.object:getpos())
        frames_pos[pos_to_string(pos)] = node.name
        local stack = ItemStack(node.name)
        local itemtable = stack:to_table()
        local itemname = nil
        if itemtable then
            itemname = stack:to_table().name
        end
        local item_texture = nil
        local item_type = ""
        if minetest.registered_items[itemname] then
            item_texture = minetest.registered_items[itemname].inventory_image
            item_type = minetest.registered_items[itemname].type
        end
        local prop = {
            is_visible = true,
            textures = { node.name },
technic/machines/register/battery_box.lua
@@ -192,10 +192,10 @@
    end
    local run = function(pos, node)
        local below = minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z})
        local meta           = minetest.get_meta(pos)
        local meta = minetest.get_meta(pos)
        local network_id = tonumber(meta:get_string(tier.."_network"))
        if not technic.is_tier_cable(below.name, tier) then
        if not technic.networks[network_id] then
            meta:set_string("infotext", S("%s Battery Box Has No Network"):format(tier))
            return
        end
@@ -304,9 +304,9 @@
            drop = "technic:"..ltier.."_battery_box0",
            on_construct = function(pos)
                local meta = minetest.get_meta(pos)
                local EU_upgrade, tube_upgrade = 0, 0
                local EU_upgrade, _ = 0
                if data.upgrade then
                    EU_upgrade, tube_upgrade = technic.handle_machine_upgrades(meta)
                    EU_upgrade, _ = technic.handle_machine_upgrades(meta)
                end
                local max_charge = data.max_charge * (1 + EU_upgrade / 10)
                local charge = meta:get_int("internal_EU_charge")
@@ -345,9 +345,9 @@
                    meta = minetest.get_meta(pos)
                    if not pipeworks.may_configure(pos, sender) then return end
                    fs_helpers.on_receive_fields(pos, fields)
                    local EU_upgrade, tube_upgrade = 0, 0
                    local EU_upgrade, _ = 0
                    if data.upgrade then
                        EU_upgrade, tube_upgrade = technic.handle_machine_upgrades(meta)
                        EU_upgrade, _ = technic.handle_machine_upgrades(meta)
                    end
                    local max_charge = data.max_charge * (1 + EU_upgrade / 10)
                    local charge = meta:get_int("internal_EU_charge")
@@ -409,6 +409,25 @@
    end
)
function technic.get_charge(itemstack)
    -- check if is chargable
    local tool_name = itemstack:get_name()
    if not technic.power_tools[tool_name] then
        return 0, 0
    end
    local item_meta = technic.get_stack_meta(itemstack)
    return item_meta:get_int("technic:charge"), technic.power_tools[tool_name]
end
function technic.set_charge(itemstack, charge)
    local tool_name = itemstack:get_name()
    if technic.power_tools[tool_name] then
        technic.set_RE_wear(itemstack, charge, technic.power_tools[tool_name])
    end
    local item_meta = technic.get_stack_meta(itemstack)
    item_meta:set_int("technic:charge", charge)
end
function technic.charge_tools(meta, batt_charge, charge_step)
    local inv = meta:get_inventory()
    if inv:is_empty("src") then
@@ -416,18 +435,18 @@
    end
    local src_stack = inv:get_stack("src", 1)
    local tool_name = src_stack:get_name()
    if not technic.power_tools[tool_name] then
    -- get callbacks
    local src_def = src_stack:get_definition()
    local technic_get_charge = src_def.technic_get_charge or technic.get_charge
    local technic_set_charge = src_def.technic_set_charge or technic.set_charge
    -- get tool charge
    local tool_charge, item_max_charge = technic_get_charge(src_stack)
    if item_max_charge==0 then
        return batt_charge, false
    end
    -- Set meta data for the tool if it didn't do it itself
    local src_meta = minetest.deserialize(src_stack:get_metadata()) or {}
    if not src_meta.charge then
        src_meta.charge = 0
    end
    -- Do the charging
    local item_max_charge = technic.power_tools[tool_name]
    local tool_charge     = src_meta.charge
    if tool_charge >= item_max_charge then
        return batt_charge, true
    elseif batt_charge <= 0 then
@@ -437,9 +456,7 @@
    charge_step = math.min(charge_step, item_max_charge - tool_charge)
    tool_charge = tool_charge + charge_step
    batt_charge = batt_charge - charge_step
    technic.set_RE_wear(src_stack, tool_charge, item_max_charge)
    src_meta.charge = tool_charge
    src_stack:set_metadata(minetest.serialize(src_meta))
    technic_set_charge(src_stack, tool_charge)
    inv:set_stack("src", 1, src_stack)
    return batt_charge, (tool_charge == item_max_charge)
end
@@ -450,21 +467,20 @@
    if inv:is_empty("dst") then
        return batt_charge, false
    end
    local srcstack = inv:get_stack("dst", 1)
    local toolname = srcstack:get_name()
    if technic.power_tools[toolname] == nil then
    local src_stack = inv:get_stack("dst", 1)
    -- get callbacks
    local src_def = src_stack:get_definition()
    local technic_get_charge = src_def.technic_get_charge or technic.get_charge
    local technic_set_charge = src_def.technic_set_charge or technic.set_charge
    -- get tool charge
    local tool_charge, item_max_charge = technic_get_charge(src_stack)
    if item_max_charge==0 then
        return batt_charge, false
    end
    -- Set meta data for the tool if it didn't do it itself :-(
    local src_meta = minetest.deserialize(srcstack:get_metadata())
    src_meta = src_meta or {}
    if not src_meta.charge then
        src_meta.charge = 0
    end
    -- Do the discharging
    local item_max_charge = technic.power_tools[toolname]
    local tool_charge     = src_meta.charge
    if tool_charge <= 0 then
        return batt_charge, true
    elseif batt_charge >= max_charge then
@@ -474,10 +490,8 @@
    charge_step = math.min(charge_step, tool_charge)
    tool_charge = tool_charge - charge_step
    batt_charge = batt_charge + charge_step
    technic.set_RE_wear(srcstack, tool_charge, item_max_charge)
    src_meta.charge = tool_charge
    srcstack:set_metadata(minetest.serialize(src_meta))
    inv:set_stack("dst", 1, srcstack)
    technic_set_charge(src_stack, tool_charge)
    inv:set_stack("dst", 1, src_stack)
    return batt_charge, (tool_charge == 0)
end
technic/machines/register/cables.lua
@@ -11,6 +11,13 @@
    return cable_tier[name]
end
function technic.register_cable_tier(name, tier)
    assert(technic.machines[tier], "Tier does not exist")
    assert(type(name) == "string", "Invalid node name")
    cable_tier[name] = tier
end
local function check_connections(pos)
    -- Build a table of all machines
    local machines = {}
@@ -60,27 +67,27 @@
                local tier = network.tier
                -- Actually add it to the (cached) network
                -- This is similar to check_node_subp
                -- !! IMPORTANT: ../switching_station.lua -> check_node_subp() must be kept in sync
                technic.cables[minetest.hash_node_position(pos)] = network_id
                pos.visited = 1
                if technic.is_tier_cable(name, tier) then
                if technic.is_tier_cable(node.name, tier) then
                    -- Found a cable
                    table.insert(network.all_nodes,pos)
                elseif technic.machines[tier][node.name] then
                    meta:set_string(tier.."_network",minetest.pos_to_string(sw_pos))
                    if     technic.machines[tier][node.name] == technic.producer then
                        table.insert(network.PR_nodes,pos)
                    elseif technic.machines[tier][node.name] == technic.receiver then
                        table.insert(network.RE_nodes,pos)
                    elseif technic.machines[tier][node.name] == technic.producer_receiver then
                        table.insert(network.PR_nodes,pos)
                        table.insert(network.RE_nodes,pos)
                    elseif technic.machines[tier][node.name] == "SPECIAL" and
                            (pos.x ~= sw_pos.x or pos.y ~= sw_pos.y or pos.z ~= sw_pos.z) and
                            from_below then
                        table.insert(network.SP_nodes,pos)
                    elseif technic.machines[tier][node.name] == technic.battery then
                        table.insert(network.BA_nodes,pos)
                    -- Found a machine
                    local eu_type = technic.machines[tier][node.name]
                    meta:set_string(tier.."_network", string.format("%.20g", network_id))
                    if     eu_type == technic.producer then
                        table.insert(network.PR_nodes, pos)
                    elseif eu_type == technic.receiver then
                        table.insert(network.RE_nodes, pos)
                    elseif eu_type == technic.producer_receiver then
                        table.insert(network.PR_nodes, pos)
                        table.insert(network.RE_nodes, pos)
                    elseif eu_type == technic.battery then
                        table.insert(network.BA_nodes, pos)
                    end
                    -- Note: SPECIAL (i.e. switching station) is not traversed!
                end
            elseif dead_end and not placed then
                -- Dead end removed, remove it from the network
technic/machines/register/compressor_recipes.lua
@@ -8,6 +8,71 @@
    technic.register_recipe("compressing", data)
end
-- Defuse the default recipes, since we have
-- the compressor to take over in a more realistic manner.
local crafts_to_clear = {
    "default:desert_sand",
    "default:sand",
    "default:silver_sand",
}
local dependent_crafts_to_clear = {
    everness = {
        "everness:coral_sand",
        "everness:coral_forest_deep_ocean_sand",
        "everness:coral_white_sand",
        "everness:crystal_sand",
        "everness:cursed_sand",
        "everness:cursed_lands_deep_ocean_sand",
        "everness:crystal_forest_deep_ocean_sand",
        "everness:mineral_sand",
    },
    nether = {
        "nether:brick",
        "nether:brick_compressed",
        "nether:rack",
        "nether:rack_deep",
    },
}
-- Add dependent recipes to main collection of
-- recipes to be cleared if their mods are used.
for dependency, crafts in pairs(dependent_crafts_to_clear) do
    if minetest.get_modpath(dependency) then
        for _, craft_entry in ipairs(crafts) do
            table.insert(crafts_to_clear, craft_entry)
        end
    end
end
-- Clear recipes
for _, craft_name in ipairs(crafts_to_clear) do
    -- Regular bricks are 2x2 shaped, nether bricks are 3x3 shaped (irregular)
    local is_regular = string.sub(craft_name, 1, 12) ~= "nether:brick"
    local shaped_recipe
    if is_regular then
        shaped_recipe = {
            {craft_name, craft_name},
            {craft_name, craft_name},
        }
    else
        shaped_recipe = {
            {craft_name, craft_name, craft_name},
            {craft_name, craft_name, craft_name},
            {craft_name, craft_name, craft_name},
        }
    end
    minetest.clear_craft({
        type = "shaped",
        recipe = shaped_recipe,
    })
end
--
-- Compile compressor recipes
--
local recipes = {
    {"default:snowblock",          "default:ice"},
    {"default:sand 2",             "default:sandstone"},
@@ -21,27 +86,36 @@
    {"technic:uranium35_ingot 5",  "technic:uranium_fuel"},
}
-- defuse the default sandstone recipe, since we have the compressor to take over in a more realistic manner
minetest.clear_craft({
    recipe = {
        {"default:sand", "default:sand"},
        {"default:sand", "default:sand"},
local dependent_recipes = {
    everness = {
        {"everness:coral_deep_ocean_sand 2",          "everness:coral_deep_ocean_sandstone_block"},
        {"everness:coral_sand 2",                     "everness:coral_sandstone"},
        {"everness:coral_white_sand 2",               "everness:coral_white_sandstone"},
        {"everness:crystal_forest_deep_ocean_sand 2", "everness:crystal_forest_deep_ocean_sandstone_block"},
        {"everness:crystal_sand 2",                   "everness:crystal_sandstone"},
        {"everness:cursed_lands_deep_ocean_sand 2",   "everness:cursed_lands_deep_ocean_sandstone_block"},
        {"everness:cursed_sand 2",                    "everness:cursed_sandstone_block"},
        {"everness:mineral_sand 2",                   "everness:mineral_sandstone"},
    },
})
minetest.clear_craft({
    recipe = {
        {"default:desert_sand", "default:desert_sand"},
        {"default:desert_sand", "default:desert_sand"},
    nether = {
        {"nether:brick 9",                "nether:brick_compressed"},
        {"nether:brick_compressed 9",    "nether:nether_lump"},
        {"nether:rack",                 "nether:brick",},
        {"nether:rack_deep",            "nether:brick_deep"},
    },
})
minetest.clear_craft({
    recipe = {
        {"default:silver_sand", "default:silver_sand"},
        {"default:silver_sand", "default:silver_sand"},
    },
})
}
-- Add dependent recipes to main recipe collection
-- if their mods are used.
for dependency, recipes_to_add in pairs(dependent_recipes) do
    if minetest.get_modpath(dependency) then
        for _, recipe_entry in ipairs(recipes_to_add) do
            table.insert(recipes, recipe_entry)
        end
    end
end
-- Register compressor recipes
for _, data in pairs(recipes) do
    technic.register_compressor_recipe({input = {data[1]}, output = data[2]})
end
technic/machines/register/grinder_recipes.lua
@@ -23,6 +23,8 @@
    {"technic:sulfur_lump",        "technic:sulfur_dust 2"},
    {"default:stone",              "technic:stone_dust"},
    {"default:sand",               "technic:stone_dust"},
    {"default:desert_sand",        "technic:stone_dust"},
    {"default:silver_sand",        "technic:stone_dust"},
    -- Other
    {"default:cobble",           "default:gravel"},
@@ -34,49 +36,73 @@
    {"default:ice",              "default:snowblock"},
}
local dependent_recipes = {
    -- Sandstones
    everness = {
        {"everness:coral_deep_ocean_sandstone_block",            "everness:coral_deep_ocean_sand 2"},
        {"everness:coral_sandstone",                            "everness:coral_sand 2"},
        {"everness:coral_white_sandstone",                        "everness:coral_white_sand 2"},
        {"everness:crystal_forest_deep_ocean_sandstone_block",    "everness:crystal_forest_deep_ocean_sand 2"},
        {"everness:crystal_sandstone",                            "everness:crystal_sand 2"},
        {"everness:cursed_lands_deep_ocean_sandstone_block",    "everness:cursed_lands_deep_ocean_sand 2"},
        {"everness:cursed_sandstone_block",                        "everness:cursed_sand 2"},
        {"everness:mineral_sandstone",                            "everness:mineral_sand 2"},
    -- Lumps and wheat
        {"everness:pyrite_lump",    "technic:pyrite_dust 2"},
    },
    farming = {
        {"farming:seed_wheat",        "farming:flour 1"},
    },
    gloopores = {
        {"gloopores:alatro_lump",    "technic:alatro_dust 2"},
        {"gloopores:kalite_lump",    "technic:kalite_dust 2"},
        {"gloopores:arol_lump",        "technic:arol_dust 2"},
        {"gloopores:talinite_lump",    "technic:talinite_dust 2"},
        {"gloopores:akalin_lump",    "technic:akalin_dust 2"},
    },
    homedecor = {
        {"home_decor:brass_ingot",    "technic:brass_dust 1"},
    },
    moreores = {
        {"moreores:mithril_lump",    "technic:mithril_dust 2"},
        {"moreores:silver_lump",    "technic:silver_dust 2"},
    },
    nether = {
        {"nether:nether_lump",        "technic:nether_dust 2"},
    },
}
for dependency, materials_to_add in pairs(dependent_recipes) do
    if minetest.get_modpath(dependency) then
        for _, material_entry in ipairs(materials_to_add) do
            table.insert(recipes, material_entry)
        end
    end
end
-- defuse the sandstone -> 4 sand recipe to avoid infinite sand bugs (also consult the inverse compressor recipe)
minetest.clear_craft({
    recipe = {
        {"default:sandstone"}
    },
    recipe = {{"default:sandstone"}},
})
minetest.clear_craft({
    recipe = {
        {"default:desert_sandstone"}
    },
    recipe = {{"default:desert_sandstone"}},
})
minetest.clear_craft({
    recipe = {
        {"default:silver_sandstone"}
    },
    recipe = {{"default:silver_sandstone"}},
})
if minetest.get_modpath("farming") then
    table.insert(recipes, {"farming:seed_wheat",   "farming:flour 1"})
if minetest.get_modpath("everness") then
    minetest.clear_craft({
        recipe = {{"everness:mineral_sandstone"}},
    })
    -- Currently (2024-03-09), there seem to be no reverse recipes for any of the other everness sandstones.
end
if minetest.get_modpath("moreores") then
    table.insert(recipes, {"moreores:mithril_lump",   "technic:mithril_dust 2"})
    table.insert(recipes, {"moreores:silver_lump",    "technic:silver_dust 2"})
end
if minetest.get_modpath("gloopores") or minetest.get_modpath("glooptest") then
    table.insert(recipes, {"gloopores:alatro_lump",   "technic:alatro_dust 2"})
    table.insert(recipes, {"gloopores:kalite_lump",   "technic:kalite_dust 2"})
    table.insert(recipes, {"gloopores:arol_lump",     "technic:arol_dust 2"})
    table.insert(recipes, {"gloopores:talinite_lump", "technic:talinite_dust 2"})
    table.insert(recipes, {"gloopores:akalin_lump",   "technic:akalin_dust 2"})
end
if minetest.get_modpath("homedecor") then
    table.insert(recipes, {"home_decor:brass_ingot", "technic:brass_dust 1"})
end
for _, data in pairs(recipes) do
for _, data in ipairs(recipes) do
    technic.register_grinder_recipe({input = {data[1]}, output = data[2]})
end
-- dusts
-- Dusts
local function register_dust(name, ingot)
    local lname = string.lower(name)
    lname = string.gsub(lname, ' ', '_')
@@ -94,33 +120,57 @@
    end
end
-- Sorted alphibeticaly
register_dust("Brass",           "basic_materials:brass_ingot")
register_dust("Bronze",          "default:bronze_ingot")
register_dust("Carbon Steel",    "technic:carbon_steel_ingot")
register_dust("Cast Iron",       "technic:cast_iron_ingot")
register_dust("Chernobylite",    "technic:chernobylite_block")
register_dust("Chromium",        "technic:chromium_ingot")
register_dust("Coal",            nil)
register_dust("Copper",          "default:copper_ingot")
register_dust("Lead",            "technic:lead_ingot")
register_dust("Gold",            "default:gold_ingot")
register_dust("Mithril",         "moreores:mithril_ingot")
register_dust("Silver",          "moreores:silver_ingot")
register_dust("Stainless Steel", "technic:stainless_steel_ingot")
register_dust("Stone",           "default:stone")
register_dust("Sulfur",          nil)
register_dust("Tin",             "default:tin_ingot")
register_dust("Wrought Iron",    "technic:wrought_iron_ingot")
register_dust("Zinc",            "technic:zinc_ingot")
if minetest.get_modpath("gloopores") or minetest.get_modpath("glooptest") then
    register_dust("Akalin",          "glooptest:akalin_ingot")
    register_dust("Alatro",          "glooptest:alatro_ingot")
    register_dust("Arol",            "glooptest:arol_ingot")
    register_dust("Kalite",          nil)
    register_dust("Talinite",        "glooptest:talinite_ingot")
-- Sorted alphabetically
local dusts = {
    {"Brass",           "basic_materials:brass_ingot"},
    {"Bronze",          "default:bronze_ingot"},
    {"Carbon Steel",    "technic:carbon_steel_ingot"},
    {"Cast Iron",       "technic:cast_iron_ingot"},
    {"Chernobylite",    "technic:chernobylite_block"},
    {"Chromium",        "technic:chromium_ingot"},
    {"Coal",            nil},
    {"Copper",          "default:copper_ingot"},
    {"Lead",            "technic:lead_ingot"},
    {"Gold",            "default:gold_ingot"},
    {"Mithril",         "moreores:mithril_ingot"},
    {"Silver",          "moreores:silver_ingot"},
    {"Stainless Steel", "technic:stainless_steel_ingot"},
    {"Stone",           "default:stone"},
    {"Sulfur",          nil},
    {"Tin",             "default:tin_ingot"},
    {"Wrought Iron",    "technic:wrought_iron_ingot"},
    {"Zinc",            "technic:zinc_ingot"},
}
local dependent_dusts = {
    everness = {
        {"Pyrite",          "everness:pyrite_ingot"},
    },
    gloopores = {
        {"Akalin",          "glooptest:akalin_ingot"},
        {"Alatro",          "glooptest:alatro_ingot"},
        {"Arol",            "glooptest:arol_ingot"},
        {"Kalite",          nil},
        {"Talinite",        "glooptest:talinite_ingot"},
    },
    nether = {
        {"Nether",          "nether:nether_ingot"},
    },
}
for dependency, dusts_to_add in pairs(dependent_dusts) do
    if minetest.get_modpath(dependency) then
        for _, dust_entry in pairs(dusts_to_add) do
            table.insert(dusts, dust_entry)
        end
    end
end
for _, data in ipairs(dusts) do
    register_dust(data[1], data[2])
end
-- Uranium
for p = 0, 35 do
    local nici = (p ~= 0 and p ~= 7 and p ~= 35) and 1 or nil
    local psuffix = p == 7 and "" or p
@@ -156,6 +206,7 @@
    end
end
-- Fuels
minetest.register_craft({
    type = "fuel",
    recipe = "technic:coal_dust",
technic/machines/supply_converter.lua
@@ -120,7 +120,7 @@
        return
    end
    local remain = 0.9
    local efficiency = 0.9
    -- Machine information
    local machine_name  = S("Supply Converter")
    local meta          = minetest.get_meta(pos)
@@ -133,7 +133,6 @@
        enabled = enabled == "1"
    end
    enabled = enabled and (meta:get_int("mesecon_mode") == 0 or meta:get_int("mesecon_effect") ~= 0)
    local demand = enabled and meta:get_int("power") or 0
    local pos_up        = {x=pos.x, y=pos.y+1, z=pos.z}
    local pos_down      = {x=pos.x, y=pos.y-1, z=pos.z}
@@ -144,14 +143,37 @@
    local to   = technic.get_cable_tier(name_down)
    if from and to then
        local input = meta:get_int(from.."_EU_input")
        meta:set_int(from.."_EU_demand", demand)
        meta:set_int(from.."_EU_supply", 0)
        meta:set_int(to.."_EU_demand", 0)
        meta:set_int(to.."_EU_supply", input * remain)
        meta:set_string("infotext", S("@1 (@2 @3 -> @4 @5)", machine_name,
            technic.EU_string(input), from,
            technic.EU_string(input * remain), to))
        -- Get the "to" network switching station for EU demand calculation
        local network_hash = technic.cables[minetest.hash_node_position(pos_down)]
        local network = network_hash and minetest.get_position_from_hash(network_hash)
        local sw_pos = network and {x=network.x,y=network.y+1,z=network.z}
        local timeout = 0
        for tier in pairs(technic.machines) do
            -- Supply converter must be connected to a network
            timeout = math.max(meta:get_int(tier.."_EU_timeout"), timeout)
        end
        if timeout > 0 and sw_pos and minetest.get_node(sw_pos).name == "technic:switching_station" then
            local sw_meta = minetest.get_meta(sw_pos)
            local demand = 0
            if enabled then
                -- Reverse evaluate the required machine and round to a nice number
                demand = sw_meta:get_int("ba_demand") + sw_meta:get_int("demand")
                demand = 100 * math.ceil(demand / efficiency / 100)
                -- Do not draw more than the limit
                demand = math.min(demand, meta:get_int("power"))
            end
            local input = meta:get_int(from.."_EU_input") -- actual input
            meta:set_int(from.."_EU_demand", demand) -- desired input
            meta:set_int(from.."_EU_supply", 0)
            meta:set_int(to.."_EU_demand", 0)
            meta:set_int(to.."_EU_supply", input * efficiency)
            meta:set_string("infotext", S("@1 (@2 @3 -> @4 @5)", machine_name,
                technic.EU_string(input), from,
                technic.EU_string(input * efficiency), to))
        else
            meta:set_string("infotext",S("%s Has No Network"):format(machine_name))
        end
    else
        meta:set_string("infotext", S("%s Has Bad Cabling"):format(machine_name))
        if to then
technic/machines/switching_station.lua
@@ -45,14 +45,12 @@
        meta:set_string("active", 1)
        meta:set_string("channel", "switching_station"..minetest.pos_to_string(pos))
        meta:set_string("formspec", "field[channel;Channel;${channel}]")
        local poshash = minetest.hash_node_position(pos)
        technic.redundant_warn.poshash = nil
    end,
    after_dig_node = function(pos)
        minetest.forceload_free_block(pos)
        pos.y = pos.y - 1
        minetest.forceload_free_block(pos)
        local poshash = minetest.hash_node_position(pos)
        technic.redundant_warn.poshash = nil
    end,
    on_receive_fields = function(pos, formname, fields, sender)
@@ -100,8 +98,10 @@
end
-- Add a wire node to the LV/MV/HV network
-- Returns: indicator whether the cable is new in the network
local hash_node_position = minetest.hash_node_position
local function add_network_node(nodes, pos, network_id)
    local node_id = minetest.hash_node_position(pos)
    local node_id = hash_node_position(pos)
    technic.cables[node_id] = network_id
    if nodes[node_id] then
        return false
@@ -117,39 +117,48 @@
end
-- Generic function to add found connected nodes to the right classification array
local check_node_subp = function(PR_nodes, RE_nodes, BA_nodes, SP_nodes, all_nodes, pos, machines, tier, sw_pos, from_below, network_id, queue)
-- !! IMPORTANT: register/cables.lua -> clear_networks() must be kept in sync
local check_node_subp = function(network, pos, machines, sw_pos, from_below, network_id, queue)
    technic.get_or_load_node(pos)
    local name = minetest.get_node(pos).name
    if technic.is_tier_cable(name, tier) then
        add_cable_node(all_nodes, pos,network_id, queue)
    elseif machines[name] then
        --dprint(name.." is a "..machines[name])
        local meta = minetest.get_meta(pos)
        meta:set_string(tier.."_network",minetest.pos_to_string(sw_pos))
        if     machines[name] == technic.producer then
            add_network_node(PR_nodes, pos, network_id)
        elseif machines[name] == technic.receiver then
            add_network_node(RE_nodes, pos, network_id)
        elseif machines[name] == technic.producer_receiver then
            add_network_node(PR_nodes, pos, network_id)
            add_network_node(RE_nodes, pos, network_id)
        elseif machines[name] == "SPECIAL" and
                (pos.x ~= sw_pos.x or pos.y ~= sw_pos.y or pos.z ~= sw_pos.z) and
                from_below then
            -- Another switching station -> disable it
            add_network_node(SP_nodes, pos, network_id)
            meta:set_int("active", 0)
        elseif machines[name] == technic.battery then
            add_network_node(BA_nodes, pos, network_id)
        end
        meta:set_int(tier.."_EU_timeout", 2) -- Touch node
    if technic.is_tier_cable(name, network.tier) then
        add_cable_node(network.all_nodes, pos, network_id, queue)
        return
    end
    local eu_type = machines[name]
    if not eu_type then
        return
    end
    --dprint(name.." is a "..machines[name])
    local meta = minetest.get_meta(pos)
    -- Normal tostring() does not have enough precision, neither does meta:set_int()
    -- Lua 5.1 bug: Cannot use hexadecimal notation for compression (see LuaJIT #911)
    meta:set_string(network.tier.."_network", string.format("%.20g", network_id))
    if     eu_type == technic.producer then
        add_network_node(network.PR_nodes, pos, network_id)
    elseif eu_type == technic.receiver then
        add_network_node(network.RE_nodes, pos, network_id)
    elseif eu_type == technic.producer_receiver then
        add_network_node(network.PR_nodes, pos, network_id)
        add_network_node(network.RE_nodes, pos, network_id)
    elseif eu_type == technic.battery then
        add_network_node(network.BA_nodes, pos, network_id)
    elseif eu_type == "SPECIAL" and from_below and
            not vector.equals(pos, sw_pos) then
        -- Another switching station -> disable it
        add_network_node(network.SP_nodes, pos, network_id)
        meta:set_int("active", 0)
    end
    meta:set_int(network.tier.."_EU_timeout", 2) -- Touch node
end
-- Traverse a network given a list of machines and a cable type name
local traverse_network = function(PR_nodes, RE_nodes, BA_nodes, SP_nodes, all_nodes, pos, machines, tier, sw_pos, network_id, queue)
local traverse_network = function(network, pos, machines, sw_pos, network_id, queue)
    local positions = {
        {x=pos.x+1, y=pos.y,   z=pos.z},
        {x=pos.x-1, y=pos.y,   z=pos.z},
@@ -158,7 +167,7 @@
        {x=pos.x,   y=pos.y,   z=pos.z+1},
        {x=pos.x,   y=pos.y,   z=pos.z-1}}
    for i, cur_pos in pairs(positions) do
        check_node_subp(PR_nodes, RE_nodes, BA_nodes, SP_nodes, all_nodes, cur_pos, machines, tier, sw_pos, i == 3, network_id, queue)
        check_node_subp(network, cur_pos, machines, sw_pos, i == 3, network_id, queue)
    end
end
@@ -169,43 +178,51 @@
    end
end
local get_network = function(sw_pos, pos1, tier)
    local network_id = minetest.hash_node_position(pos1)
local get_network = function(sw_pos, cable_pos, tier)
    local network_id = minetest.hash_node_position(cable_pos)
    local cached = technic.networks[network_id]
    if cached and cached.tier == tier then
        -- Re-use cached system data
        touch_nodes(cached.PR_nodes, tier)
        touch_nodes(cached.BA_nodes, tier)
        touch_nodes(cached.RE_nodes, tier)
        for _, pos in ipairs(cached.SP_nodes) do
            -- Disable all other switching stations (again)
            local meta = minetest.get_meta(pos)
            meta:set_int("active", 0)
            meta:set_string("active_pos", minetest.serialize(sw_pos))
        end
        return cached.PR_nodes, cached.BA_nodes, cached.RE_nodes
    end
    local PR_nodes = {}
    local BA_nodes = {}
    local RE_nodes = {}
    local SP_nodes = {}
    local all_nodes = {}
    local machines = technic.machines[tier]
    local network = {
        tier = tier,
        PR_nodes = {},
        BA_nodes = {},
        RE_nodes = {},
        SP_nodes = {},
        all_nodes = {}
    }
    -- Traverse the network step by step starting from the node underneath the switching station
    local queue = {}
    add_cable_node(all_nodes, pos1, network_id, queue)
    add_cable_node(network.all_nodes, cable_pos, network_id, queue)
    while next(queue) do
        local to_visit = {}
        for _, pos in ipairs(queue) do
            traverse_network(PR_nodes, RE_nodes, BA_nodes, SP_nodes, all_nodes,
                    pos, technic.machines[tier], tier, sw_pos, network_id, to_visit)
            traverse_network(network, pos, machines, sw_pos, network_id, to_visit)
        end
        queue = to_visit
    end
    PR_nodes = flatten(PR_nodes)
    BA_nodes = flatten(BA_nodes)
    RE_nodes = flatten(RE_nodes)
    SP_nodes = flatten(SP_nodes)
    all_nodes = flatten(all_nodes)
    technic.networks[network_id] = {tier = tier, all_nodes = all_nodes, SP_nodes = SP_nodes,
            PR_nodes = PR_nodes, RE_nodes = RE_nodes, BA_nodes = BA_nodes}
    return PR_nodes, BA_nodes, RE_nodes
    -- Convert { [hash] = pos, ... } to { pos, ... }
    network.PR_nodes = flatten(network.PR_nodes)
    network.BA_nodes = flatten(network.BA_nodes)
    network.RE_nodes = flatten(network.RE_nodes)
    network.SP_nodes = flatten(network.SP_nodes)
    network.all_nodes = flatten(network.all_nodes)
    technic.networks[network_id] = network
    return network.PR_nodes, network.BA_nodes, network.RE_nodes
end
-----------------------------------------------
@@ -215,19 +232,17 @@
technic.powerctrl_state = true
minetest.register_chatcommand("powerctrl", {
    params = "state",
    params = "[on/off]",
    description = "Enables or disables technic's switching station ABM",
    privs = { basic_privs = true },
    func = function(name, state)
        if state == "on" then
            technic.powerctrl_state = true
        else
            technic.powerctrl_state = false
        end
        technic.powerctrl_state = (state:trim():lower() == "on")
        minetest.chat_send_player(name, "Technic switching station: " ..
            (technic.powerctrl_state and "on" or "off"))
    end
})
-- Run all the nodes
-- Run `technic_run` on all nodes in the power grid
local function run_nodes(list, run_stage)
    for _, pos in ipairs(list) do
        technic.get_or_load_node(pos)
@@ -243,28 +258,23 @@
minetest.register_abm({
    nodenames = {"technic:switching_station"},
    label = "Switching Station", -- allows the mtt profiler to profile this abm individually
    label = "Switching Station", -- name for the Minetest mod profiler
    interval   = 1,
    chance     = 1,
    action = function(pos, node, active_object_count, active_object_count_wider)
        if not technic.powerctrl_state then return end
        local meta             = minetest.get_meta(pos)
        local meta = minetest.get_meta(pos)
        local meta1
        local pos1             = {}
        local tier      = ""
        local PR_nodes
        local BA_nodes
        local RE_nodes
        local PR_nodes, BA_nodes, RE_nodes
        local machine_name = S("Switching Station")
        -- Which kind of network are we on:
        pos1 = {x=pos.x, y=pos.y-1, z=pos.z}
        local cable_pos = {x=pos.x, y=pos.y-1, z=pos.z}
        --Disable if necessary
        if meta:get_int("active") ~= 1 then
            minetest.forceload_free_block(pos)
            minetest.forceload_free_block(pos1)
            minetest.forceload_free_block(cable_pos)
            meta:set_string("infotext",S("%s Already Present"):format(machine_name))
            local poshash = minetest.hash_node_position(pos)
@@ -276,18 +286,18 @@
            return
        end
        local name = minetest.get_node(pos1).name
        local name = minetest.get_node(cable_pos).name
        local tier = technic.get_cable_tier(name)
        if tier then
            -- Forceload switching station
            minetest.forceload_block(pos)
            minetest.forceload_block(pos1)
            PR_nodes, BA_nodes, RE_nodes = get_network(pos, pos1, tier)
            minetest.forceload_block(cable_pos)
            PR_nodes, BA_nodes, RE_nodes = get_network(pos, cable_pos, tier)
        else
            --dprint("Not connected to a network")
            meta:set_string("infotext", S("%s Has No Network"):format(machine_name))
            minetest.forceload_free_block(pos)
            minetest.forceload_free_block(pos1)
            minetest.forceload_free_block(cable_pos)
            return
        end
@@ -340,20 +350,16 @@
        end
        --dprint("Total RE demand:"..RE_eu_demand)
        -- Get all the power from the BA nodes
        local BA_eu_supply = 0
        -- Batteries
        local BA_eu_supply, BA_eu_demand = 0, 0
        for _, pos1 in pairs(BA_nodes) do
            meta1 = minetest.get_meta(pos1)
            BA_eu_supply = BA_eu_supply + meta1:get_int(eu_supply_str)
        end
        --dprint("Total BA supply:"..BA_eu_supply)
        -- Get all the demand from the BA nodes
        local BA_eu_demand = 0
        for _, pos1 in pairs(BA_nodes) do
            meta1 = minetest.get_meta(pos1)
            BA_eu_demand = BA_eu_demand + meta1:get_int(eu_demand_str)
        end
        -- Expose value for the supply converter
        meta:set_int("ba_demand", BA_eu_demand)
        --dprint("Total BA supply:"..BA_eu_supply)
        --dprint("Total BA demand:"..BA_eu_demand)
        meta:set_string("infotext", S("@1. Supply: @2 Demand: @3",
@@ -374,8 +380,8 @@
        end
        -- Data that will be used by the power monitor
        meta:set_int("supply",PR_eu_supply)
        meta:set_int("demand",RE_eu_demand)
        meta:set_int("supply", PR_eu_supply)
        meta:set_int("demand", RE_eu_demand)
        -- If the PR supply is enough for the RE demand supply them all
        if PR_eu_supply >= RE_eu_demand then
@@ -465,16 +471,17 @@
        for tier, machines in pairs(technic.machines) do
            if machines[node.name] and switching_station_timeout_count(pos, tier) then
                local nodedef = minetest.registered_nodes[node.name]
                if nodedef and nodedef.technic_disabled_machine_name then
                    node.name = nodedef.technic_disabled_machine_name
                    minetest.swap_node(pos, node)
                elseif nodedef and nodedef.technic_on_disable then
                    nodedef.technic_on_disable(pos, node)
                end
                if nodedef then
                    local meta = minetest.get_meta(pos)
                    meta:set_string("infotext", S("%s Has No Network"):format(nodedef.description))
                end
                if nodedef and nodedef.technic_disabled_machine_name then
                    node.name = nodedef.technic_disabled_machine_name
                    minetest.swap_node(pos, node)
                end
                if nodedef and nodedef.technic_on_disable then
                    nodedef.technic_on_disable(pos, node)
                end
            end
        end
    end,
technic/mod.conf
@@ -1,3 +1,3 @@
name = technic
depends = default, pipeworks, technic_worldgen, basic_materials
optional_depends = bucket, screwdriver, mesecons, mesecons_mvps, digilines, digiline_remote, intllib, unified_inventory, vector_extras, dye, craftguide,i3
optional_depends = bucket, screwdriver, mesecons, mesecons_mvps, digilines, digiline_remote, intllib, unified_inventory, vector_extras, dye, craftguide, i3, everness, nether
technic/radiation.lua
@@ -428,7 +428,7 @@
        liquidtype = state,
        liquid_alternative_flowing = "technic:corium_flowing",
        liquid_alternative_source = "technic:corium_source",
        liquid_viscosity = LAVA_VISC,
        liquid_viscosity = 7, -- like lava
        liquid_renewable = false,
        damage_per_second = 6,
        post_effect_color = {a=192, r=80, g=160, b=80},
technic/register.lua
@@ -44,7 +44,10 @@
-- Wear down a tool depending on the remaining charge.
function technic.set_RE_wear(itemstack, item_load, max_load)
    if (minetest.registered_items[itemstack:get_name()].wear_represents or "mechanical_wear") ~= "technic_RE_charge" then return itemstack end
    local def = minetest.registered_items[itemstack:get_name()]
    if (def.wear_represents or "mechanical_wear") ~= "technic_RE_charge" then
        return itemstack
    end
    local temp
    if item_load == 0 then
        temp = 0
technic/textures/hires/technic_hv_nuclear_reactor_core_128.png
Binary files differ
technic/textures/hires/technic_hv_nuclear_reactor_core_16.png
Binary files differ
technic/textures/hires/technic_hv_nuclear_reactor_core_256.png
Binary files differ
technic/textures/hires/technic_hv_nuclear_reactor_core_32.png
Binary files differ
technic/textures/hires/technic_hv_nuclear_reactor_core_64.png
Binary files differ
technic/textures/power_meter.png
Binary files differ
technic/textures/technic_copper_coil.png
Binary files differ
technic/textures/technic_deployer_back.png
Binary files differ
technic/textures/technic_deployer_bottom.png
Binary files differ
technic/textures/technic_deployer_front_off.png
Binary files differ
technic/textures/technic_deployer_front_on.png
Binary files differ
technic/textures/technic_deployer_side.png
Binary files differ
technic/textures/technic_deployer_side1.png
Binary files differ
technic/textures/technic_deployer_side2.png
Binary files differ
technic/textures/technic_deployer_top.png
Binary files differ
technic/textures/technic_generator_front.png

technic/textures/technic_generator_front_active.png

technic/textures/technic_generator_side.png

technic/textures/technic_generator_top.png

technic/textures/technic_injector_side.png
Binary files differ
technic/textures/technic_injector_top.png
Binary files differ
technic/textures/technic_lv_lamp_bottom.png
technic/textures/technic_lv_lamp_side.png
technic/textures/technic_lv_lamp_top.png
technic/textures/technic_lv_led.png
technic/textures/technic_lv_led_inv.png
technic/textures/technic_lv_led_side.png
technic/textures/technic_lv_led_side2.png
technic/textures/technic_lv_led_top.png
technic/textures/technic_music_player_top.png

technic/textures/technic_nether_dust.png
technic/textures/technic_nodebreaker_back.png
Binary files differ
technic/textures/technic_nodebreaker_bottom.png
Binary files differ
technic/textures/technic_nodebreaker_bottom_off.png
Binary files differ
technic/textures/technic_nodebreaker_bottom_on.png
Binary files differ
technic/textures/technic_nodebreaker_front_off.png
Binary files differ
technic/textures/technic_nodebreaker_front_on.png
Binary files differ
technic/textures/technic_nodebreaker_side.png
Binary files differ
technic/textures/technic_nodebreaker_side1.png
Binary files differ
technic/textures/technic_nodebreaker_side1_off.png
Binary files differ
technic/textures/technic_nodebreaker_side1_on.png
Binary files differ
technic/textures/technic_nodebreaker_side2.png
Binary files differ
technic/textures/technic_nodebreaker_side2_off.png
Binary files differ
technic/textures/technic_nodebreaker_side2_on.png
Binary files differ
technic/textures/technic_nodebreaker_top.png
Binary files differ
technic/textures/technic_nodebreaker_top_off.png
Binary files differ
technic/textures/technic_nodebreaker_top_on.png
Binary files differ
technic/textures/technic_power_meter_bg.png
Binary files differ
technic/textures/technic_power_meter_fg.png
Binary files differ
technic/textures/technic_pyrite_dust.png
technic/textures/technic_river_water_can.png

technic/textures/technic_rubber_goo.png

technic/textures/technic_screwdriver.png
Binary files differ
technic/textures/technicx32/technic_battery.png
Binary files differ
technic/textures/technicx32/technic_deployer_back.png
Binary files differ
technic/textures/technicx32/technic_deployer_bottom.png
Binary files differ
technic/textures/technicx32/technic_deployer_front_off.png
Binary files differ
technic/textures/technicx32/technic_deployer_front_on.png
Binary files differ
technic/textures/technicx32/technic_deployer_side.png
Binary files differ
technic/textures/technicx32/technic_deployer_side1.png
Binary files differ
technic/textures/technicx32/technic_deployer_side2.png
Binary files differ
technic/textures/technicx32/technic_deployer_top.png
Binary files differ
technic/textures/technicx32/technic_fine_copper_wire.png
Binary files differ
technic/textures/technicx32/technic_fine_gold_wire.png
Binary files differ
technic/textures/technicx32/technic_fine_silver_wire.png
Binary files differ
technic/textures/technicx32/technic_geothermal_side.png
Binary files differ
technic/textures/technicx32/technic_geothermal_top.png
Binary files differ
technic/textures/technicx32/technic_geothermal_top_active.png
Binary files differ
technic/textures/technicx32/technic_grinder_front.png
Binary files differ
technic/textures/technicx32/technic_grinder_side.png
Binary files differ
technic/textures/technicx32/technic_grinder_top.png
Binary files differ
technic/textures/technicx32/technic_laser_beam.png
Binary files differ
technic/textures/technicx32/technic_nodebreaker_back.png
Binary files differ
technic/textures/technicx32/technic_nodebreaker_bottom.png
Binary files differ
technic/textures/technicx32/technic_nodebreaker_bottom_off.png
Binary files differ
technic/textures/technicx32/technic_nodebreaker_bottom_on.png
Binary files differ
technic/textures/technicx32/technic_nodebreaker_front_off.png
Binary files differ
technic/textures/technicx32/technic_nodebreaker_front_on.png
Binary files differ
technic/textures/technicx32/technic_nodebreaker_side.png
Binary files differ
technic/textures/technicx32/technic_nodebreaker_side1.png
Binary files differ
technic/textures/technicx32/technic_nodebreaker_side1_off.png
Binary files differ
technic/textures/technicx32/technic_nodebreaker_side1_on.png
Binary files differ
technic/textures/technicx32/technic_nodebreaker_side2.png
Binary files differ
technic/textures/technicx32/technic_nodebreaker_side2_off.png
Binary files differ
technic/textures/technicx32/technic_nodebreaker_side2_on.png
Binary files differ
technic/textures/technicx32/technic_nodebreaker_top.png
Binary files differ
technic/textures/technicx32/technic_nodebreaker_top_off.png
Binary files differ
technic/textures/technicx32/technic_nodebreaker_top_on.png
Binary files differ
technic/textures/technicx32/technic_pyrite_dust.png
technic/textures/technicx32/technic_rubber.png
Binary files differ
technic/textures/technicx32/technic_rubber_sapling.png
Binary files differ
technic/textures/technicx32/technic_rubber_tree_empty.png
Binary files differ
technic/textures/technicx32/technic_rubber_tree_full.png
Binary files differ
technic/textures/technicx32/technic_silicon_wafer.png
Binary files differ
technic/tools/cans.lua
@@ -12,14 +12,6 @@
    itemstack:set_wear(temp)
end
local function get_can_level(itemstack)
    if itemstack:get_metadata() == "" then
        return 0
    else
        return tonumber(itemstack:get_metadata())
    end
end
function technic.register_can(d)
    local data = {}
    for k, v in pairs(d) do data[k] = v end
@@ -33,15 +25,19 @@
            if pointed_thing.type ~= "node" then return end
            local node = minetest.get_node(pointed_thing.under)
            if node.name ~= data.liquid_source_name then return end
            local charge = get_can_level(itemstack)
            local meta = technic.get_stack_meta_cans(itemstack)
            local charge = meta:get_int("can_level")
            if charge == data.can_capacity then return end
            if minetest.is_protected(pointed_thing.under, user:get_player_name()) then
                minetest.log("action", user:get_player_name().." tried to take "..node.name.." at protected position "..minetest.pos_to_string(pointed_thing.under).." with a "..data.can_name)
                minetest.log("action", user:get_player_name()..
                    " tried to take "..node.name..
                    " at protected position "..minetest.pos_to_string(pointed_thing.under)..
                    " with a "..data.can_name)
                return
            end
            minetest.remove_node(pointed_thing.under)
            charge = charge + 1
            itemstack:set_metadata(tostring(charge))
            meta:set_int("can_level", charge)
            set_can_wear(itemstack, charge, data.can_capacity)
            return itemstack
        end,
@@ -60,20 +56,25 @@
                -- Try to place node above the pointed source, or abort.
                if not def.buildable_to or node_name == data.liquid_source_name then return end
            end
            local charge = get_can_level(itemstack)
            local meta = technic.get_stack_meta_cans(itemstack)
            local charge = meta:get_int("can_level")
            if charge == 0 then return end
            if minetest.is_protected(pos, user:get_player_name()) then
                minetest.log("action", user:get_player_name().." tried to place "..data.liquid_source_name.." at protected position "..minetest.pos_to_string(pos).." with a "..data.can_name)
                minetest.log("action", user:get_player_name()..
                    " tried to place "..data.liquid_source_name..
                    " at protected position "..minetest.pos_to_string(pos)..
                    " with a "..data.can_name)
                return
            end
            minetest.set_node(pos, {name=data.liquid_source_name})
            charge = charge - 1
            itemstack:set_metadata(tostring(charge))
            meta:set_int("can_level", charge)
            set_can_wear(itemstack, charge, data.can_capacity)
            return itemstack
        end,
        on_refill = function(stack)
            stack:set_metadata(tostring(data.can_capacity))
            local meta = technic.get_stack_meta_cans(stack)
            meta:set_int("can_level", data.can_capacity)
            set_can_wear(stack, data.can_capacity, data.can_capacity)
            return stack
        end,
technic/tools/chainsaw.lua
@@ -1,338 +1,224 @@
-- Configuration
local chainsaw_max_charge      = 30000 -- Maximum charge of the saw
-- Gives 2500 nodes on a single charge (about 50 complete normal trees)
local chainsaw_charge_per_node = 12
-- Cut down tree leaves.  Leaf decay may cause slowness on large trees
-- if this is disabled.
local chainsaw_leaves = true
-- First value is node name; second is whether the node is considered even if chainsaw_leaves is false.
local nodes = {
    -- The default trees
    {"default:acacia_tree", true},
    {"default:aspen_tree", true},
    {"default:jungletree", true},
    {"default:papyrus", true},
    {"default:cactus", true},
    {"default:tree", true},
    {"default:apple", true},
    {"default:pine_tree", true},
    {"default:acacia_leaves", false},
    {"default:aspen_leaves", false},
    {"default:leaves", false},
    {"default:jungleleaves", false},
    {"default:pine_needles", false},
local chainsaw_efficiency = 0.92 -- Drops less items
    -- The default bushes
    {"default:acacia_bush_stem", true},
    {"default:bush_stem", true},
    {"default:pine_bush_stem", true},
    {"default:acacia_bush_leaves", false},
    {"default:blueberry_bush_leaves", false},
    {"default:blueberry_bush_leaves_with_berries", false},
    {"default:bush_leaves", false},
    {"default:pine_bush_needles", false},
    -- Rubber trees from moretrees or technic_worldgen if moretrees isn't installed
--    {"moretrees:rubber_tree_trunk_empty", true},
--    {"moretrees:rubber_tree_trunk", true},
    {"moretrees:rubber_tree_leaves", false},
    -- Support moretrees (trunk)
    {"moretrees:acacia_trunk", true},
    {"moretrees:apple_tree_trunk", true},
    {"moretrees:beech_trunk", true},
    {"moretrees:birch_trunk", true},
    {"moretrees:cedar_trunk", true},
    {"moretrees:date_palm_ffruit_trunk", true},
    {"moretrees:date_palm_fruit_trunk", true},
    {"moretrees:date_palm_mfruit_trunk", true},
    {"moretrees:date_palm_trunk", true},
    {"moretrees:fir_trunk", true},
    {"moretrees:jungletree_trunk", true},
    {"moretrees:oak_trunk", true},
    {"moretrees:palm_trunk", true},
    {"moretrees:palm_fruit_trunk", true},
    {"moretrees:palm_fruit_trunk_gen", true},
    {"moretrees:pine_trunk", true},
    {"moretrees:poplar_trunk", true},
    {"moretrees:sequoia_trunk", true},
    {"moretrees:spruce_trunk", true},
    {"moretrees:willow_trunk", true},
    -- Support moretrees (leaves)
    {"moretrees:acacia_leaves", false},
    {"moretrees:apple_tree_leaves", false},
    {"moretrees:beech_leaves", false},
    {"moretrees:birch_leaves", false},
    {"moretrees:cedar_leaves", false},
    {"moretrees:date_palm_leaves", false},
    {"moretrees:fir_leaves", false},
    {"moretrees:fir_leaves_bright", false},
    {"moretrees:jungletree_leaves_green", false},
    {"moretrees:jungletree_leaves_yellow", false},
    {"moretrees:jungletree_leaves_red", false},
    {"moretrees:oak_leaves", false},
    {"moretrees:palm_leaves", false},
    {"moretrees:poplar_leaves", false},
    {"moretrees:pine_leaves", false},
    {"moretrees:sequoia_leaves", false},
    {"moretrees:spruce_leaves", false},
    {"moretrees:willow_leaves", false},
    -- Support moretrees (fruit)
    {"moretrees:acorn", false},
    {"moretrees:apple_blossoms", false},
    {"moretrees:cedar_cone", false},
    {"moretrees:coconut", false},
    {"moretrees:coconut_0", false},
    {"moretrees:coconut_1", false},
    {"moretrees:coconut_2", false},
    {"moretrees:coconut_3", false},
    {"moretrees:dates_f0", false},
    {"moretrees:dates_f1", false},
    {"moretrees:dates_f2", false},
    {"moretrees:dates_f3", false},
    {"moretrees:dates_f4", false},
    {"moretrees:dates_fn", false},
    {"moretrees:dates_m0", false},
    {"moretrees:dates_n", false},
    {"moretrees:fir_cone", false},
    {"moretrees:pine_cone", false},
    {"moretrees:spruce_cone", false},
    -- Support growing_trees
    {"growing_trees:trunk", true},
    {"growing_trees:medium_trunk", true},
    {"growing_trees:big_trunk", true},
    {"growing_trees:trunk_top", true},
    {"growing_trees:trunk_sprout", true},
    {"growing_trees:branch_sprout", true},
    {"growing_trees:branch", true},
    {"growing_trees:branch_xmzm", true},
    {"growing_trees:branch_xpzm", true},
    {"growing_trees:branch_xmzp", true},
    {"growing_trees:branch_xpzp", true},
    {"growing_trees:branch_zz", true},
    {"growing_trees:branch_xx", true},
    {"growing_trees:leaves", false},
    -- Support cool_trees
    {"bamboo:trunk", true},
    {"bamboo:leaves", false},
    {"birch:trunk", true},
    {"birch:leaves", false},
    {"cherrytree:trunk", true},
    {"cherrytree:blossom_leaves", false},
    {"cherrytree:leaves", false},
    {"chestnuttree:trunk", true},
    {"chestnuttree:leaves", false},
    {"clementinetree:trunk", true},
    {"clementinetree:leaves", false},
    {"ebony:trunk", true},
    {"ebony:creeper", false},
    {"ebony:creeper_leaves", false},
    {"ebony:leaves", false},
    {"jacaranda:trunk", true},
    {"jacaranda:blossom_leaves", false},
    {"larch:trunk", true},
    {"larch:leaves", false},
    {"lemontree:trunk", true},
    {"lemontree:leaves", false},
    {"mahogany:trunk", true},
    {"mahogany:leaves", false},
    {"palm:trunk", true},
    {"palm:leaves", false},
    -- Support growing_cactus
    {"growing_cactus:sprout", true},
    {"growing_cactus:branch_sprout_vertical", true},
    {"growing_cactus:branch_sprout_vertical_fixed", true},
    {"growing_cactus:branch_sprout_xp", true},
    {"growing_cactus:branch_sprout_xm", true},
    {"growing_cactus:branch_sprout_zp", true},
    {"growing_cactus:branch_sprout_zm", true},
    {"growing_cactus:trunk", true},
    {"growing_cactus:branch_trunk", true},
    {"growing_cactus:branch", true},
    {"growing_cactus:branch_xp", true},
    {"growing_cactus:branch_xm", true},
    {"growing_cactus:branch_zp", true},
    {"growing_cactus:branch_zm", true},
    {"growing_cactus:branch_zz", true},
    {"growing_cactus:branch_xx", true},
    -- Support farming_plus
    {"farming_plus:banana_leaves", false},
    {"farming_plus:banana", false},
    {"farming_plus:cocoa_leaves", false},
    {"farming_plus:cocoa", false},
    -- Support nature
    {"nature:blossom", false},
    -- Support snow
    {"snow:needles", false},
    {"snow:needles_decorated", false},
    {"snow:star", false},
    -- Support vines (also generated by moretrees if available)
    {"vines:vines", false},
    {"trunks:moss", false},
    {"trunks:moss_fungus", false},
    {"trunks:treeroot", false},
    -- Support ethereal
    {"ethereal:bamboo", true},
    {"ethereal:bamboo_leaves", false},
    {"ethereal:banana_trunk", true},
    {"ethereal:bananaleaves", false},
    {"ethereal:banana", false},
    {"ethereal:birch_trunk", true},
    {"ethereal:birch_leaves", false},
    {"ethereal:frost_tree", true},
    {"ethereal:frost_leaves", false},
    {"ethereal:mushroom_trunk", true},
    {"ethereal:mushroom", false},
    {"ethereal:mushroom_pore", true},
    {"ethereal:orangeleaves", false},
    {"ethereal:orange", false},
    {"ethereal:palm_trunk", true},
    {"ethereal:palmleaves", false},
    {"ethereal:coconut", false},
    {"ethereal:redwood_trunk", true},
    {"ethereal:redwood_leaves", false},
    {"ethereal:sakura_trunk", true},
    {"ethereal:sakura_leaves", false},
    {"ethereal:sakura_leaves2", false},
    {"ethereal:scorched_tree", true},
    {"ethereal:willow_trunk", true},
    {"ethereal:willow_twig", false},
    {"ethereal:yellow_trunk", true},
    {"ethereal:yellowleaves", false},
    {"ethereal:golden_apple", false},
}
local timber_nodenames = {}
for _, node in pairs(nodes) do
    if chainsaw_leaves or node[2] then
        timber_nodenames[node[1]] = true
    end
end
-- Maximal dimensions of the tree to cut (giant sequoia)
local tree_max_radius = 10
local tree_max_height = 70
local S = technic.getter
--[[
Format: [node_name] = dig_cost
This table is filled automatically afterwards to support mods such as:
    cool_trees
    ethereal
    moretrees
]]
local tree_nodes = {
    -- For the sake of maintenance, keep this sorted alphabetically!
    ["default:acacia_bush_stem"] = -1,
    ["default:bush_stem"] = -1,
    ["default:pine_bush_stem"] = -1,
    ["default:cactus"] = -1,
    ["default:papyrus"] = -1,
    -- dfcaves "fruits"
    ["df_trees:blood_thorn_spike"] = -1,
    ["df_trees:blood_thorn_spike_dead"] = -1,
    ["df_trees:tunnel_tube_fruiting_body"] = -1,
    ["ethereal:bamboo"] = -1,
}
local tree_nodes_by_cid = {
    -- content ID indexed table, data populated on mod load.
    -- Format: [node_name] = cost_number
}
-- Function to decide whether or not to cut a certain node (and at which energy cost)
local function populate_costs(name, def)
    repeat
        if tree_nodes[name] then
            break -- Manually specified node to chop
        end
        if (def.groups.tree or 0) > 0 then
            break -- Tree node
        end
        if (def.groups.leaves or 0) > 0 and chainsaw_leaves then
            break -- Leaves
        end
        if (def.groups.leafdecay_drop or 0) > 0 then
            break -- Food
        end
        return -- Abort function: do not dig this node
    -- luacheck: push ignore 511
    until 1
    -- luacheck: pop
    -- Add the node cost to the content ID indexed table
    local content_id = minetest.get_content_id(name)
    -- Make it so that the giant sequoia can be cut with a full charge
    local cost = tree_nodes[name] or 0
    if def.groups.choppy then
        cost = math.max(cost, def.groups.choppy * 14) -- trunks (usually 3 * 14)
    end
    if def.groups.snappy then
        cost = math.max(cost, def.groups.snappy * 2) -- leaves
    end
    tree_nodes_by_cid[content_id] = math.max(4, cost)
end
minetest.register_on_mods_loaded(function()
    local ndefs = minetest.registered_nodes
    -- Populate hardcoded nodes
    for name in pairs(tree_nodes) do
        local ndef = ndefs[name]
        if ndef and ndef.groups then
            populate_costs(name, ndef)
        end
    end
    -- Find all trees and leaves
    for name, def in pairs(ndefs) do
        if def.groups then
            populate_costs(name, def)
        end
    end
end)
technic.register_power_tool("technic:chainsaw", chainsaw_max_charge)
-- This function checks if the specified node should be sawed
local function check_if_node_sawed(pos)
    local node_name = minetest.get_node(pos).name
    if timber_nodenames[node_name]
            or (chainsaw_leaves and minetest.get_item_group(node_name, "leaves") ~= 0)
            or minetest.get_item_group(node_name, "tree") ~= 0 then
        return true
local pos9dir = {
    { 1, 0,  0},
    {-1, 0,  0},
    { 0, 0,  1},
    { 0, 0, -1},
    { 1, 0,  1},
    {-1, 0, -1},
    { 1, 0, -1},
    {-1, 0,  1},
    { 0, 1,  0}, -- up
}
local cutter = {
    -- See function cut_tree()
}
local safe_cut = minetest.settings:get_bool("technic_safe_chainsaw") ~= false
local c_air = minetest.get_content_id("air")
local function dig_recursive(x, y, z)
    local i = cutter.area:index(x, y, z)
    if cutter.seen[i] then
        return
    end
    cutter.seen[i] = 1 -- Mark as visited
    if safe_cut and cutter.param2[i] ~= 0 then
        -- Do not dig manually placed nodes
        -- Problem: moretrees' generated jungle trees use param2 = 2
        return
    end
    return false
end
    local c_id = cutter.data[i]
    local cost = tree_nodes_by_cid[c_id]
    if not cost or cost > cutter.charge then
        return -- Cannot dig this node
    end
-- Table for saving what was sawed down
local produced = {}
    -- Count dug nodes
    cutter.drops[c_id] = (cutter.drops[c_id] or 0) + 1
    cutter.seen[i] = 2 -- Mark as dug (for callbacks)
    cutter.data[i] = c_air
    cutter.charge = cutter.charge - cost
-- Save the items sawed down so that we can drop them in a nice single stack
local function handle_drops(drops)
    for _, item in ipairs(drops) do
        local stack = ItemStack(item)
        local name = stack:get_name()
        local p = produced[name]
        if not p then
            produced[name] = stack
        else
            p:set_count(p:get_count() + stack:get_count())
    -- Expand maximal bounds for area protection check
    if x < cutter.minp.x then cutter.minp.x = x end
    if y < cutter.minp.y then cutter.minp.y = y end
    if z < cutter.minp.z then cutter.minp.z = z end
    if x > cutter.maxp.x then cutter.maxp.x = x end
    if y > cutter.maxp.y then cutter.maxp.y = y end
    if z > cutter.maxp.z then cutter.maxp.z = z end
    -- Traverse neighbors
    local xn, yn, zn
    for _, offset in ipairs(pos9dir) do
        xn, yn, zn = x + offset[1], y + offset[2], z + offset[3]
        if cutter.area:contains(xn, yn, zn) then
             dig_recursive(xn, yn, zn)
        end
    end
end
--- Iterator over positions to try to saw around a sawed node.
-- This returns positions in a 3x1x3 area around the position, plus the
-- position above it.  This does not return the bottom position to prevent
-- the chainsaw from cutting down nodes below the cutting position.
-- @param pos Sawing position.
local function iterSawTries(pos)
    -- Copy position to prevent mangling it
    local pos = vector.new(pos)
    local i = 0
local handle_drops
    return function()
        i = i + 1
        -- Given a (top view) area like so (where 5 is the starting position):
        -- X -->
        -- Z 123
        -- | 456
        -- V 789
        -- This will return positions 1, 4, 7, 2, 8 (skip 5), 3, 6, 9,
        -- and the position above 5.
        if i == 1 then
            -- Move to starting position
            pos.x = pos.x - 1
            pos.z = pos.z - 1
        elseif i == 4 or i == 7 then
            -- Move to next X and back to start of Z when we reach
            -- the end of a Z line.
            pos.x = pos.x + 1
            pos.z = pos.z - 2
        elseif i == 5 then
            -- Skip the middle position (we've already run on it)
            -- and double-increment the counter.
            pos.z = pos.z + 2
            i = i + 1
        elseif i <= 9 then
            -- Go to next Z.
            pos.z = pos.z + 1
        elseif i == 10 then
            -- Move back to center and up.
            -- The Y+ position must be last so that we don't dig
            -- straight upward and not come down (since the Y-
            -- position isn't checked).
            pos.x = pos.x - 1
            pos.z = pos.z - 1
            pos.y = pos.y + 1
        else
            return nil
        end
        return pos
    end
end
local function chainsaw_dig(player, pos, remaining_charge)
    local minp = {
        x = pos.x - (tree_max_radius + 1),
        y = pos.y,
        z = pos.z - (tree_max_radius + 1)
    }
    local maxp = {
        x = pos.x + (tree_max_radius + 1),
        y = pos.y + tree_max_height,
        z = pos.z + (tree_max_radius + 1)
    }
-- This function does all the hard work. Recursively we dig the node at hand
-- if it is in the table and then search the surroundings for more stuff to dig.
local function recursive_dig(pos, remaining_charge)
    if remaining_charge < chainsaw_charge_per_node then
        return remaining_charge
    end
    local node = minetest.get_node(pos)
    local vm = minetest.get_voxel_manip()
    local emin, emax = vm:read_from_map(minp, maxp)
    if not check_if_node_sawed(pos) then
        return remaining_charge
    cutter = {
        area = VoxelArea:new{MinEdge=emin, MaxEdge=emax},
        data = vm:get_data(),
        param2 = vm:get_param2_data(),
        seen = {},
        drops = {}, -- [content_id] = count
        minp = vector.copy(pos),
        maxp = vector.copy(pos),
        charge = remaining_charge
    }
    dig_recursive(pos.x, pos.y, pos.z)
    -- Check protection
    local player_name = player:get_player_name()
    if minetest.is_area_protected(cutter.minp, cutter.maxp, player_name, 6) then
        minetest.chat_send_player(player_name, "The chainsaw cannot cut this tree. The cuboid " ..
            minetest.pos_to_string(cutter.minp) .. ", " .. minetest.pos_to_string(cutter.maxp) ..
            " contains protected nodes.")
        minetest.record_protection_violation(pos, player_name)
        return
    end
    -- Wood found - cut it
    handle_drops(minetest.get_node_drops(node.name, ""))
    minetest.remove_node(pos)
    remaining_charge = remaining_charge - chainsaw_charge_per_node
    minetest.sound_play("chainsaw", {
        pos = pos,
        gain = 1.0,
        max_hear_distance = 20
    })
    -- Check surroundings and run recursively if any charge left
    for npos in iterSawTries(pos) do
        if remaining_charge < chainsaw_charge_per_node then
            break
        end
        if check_if_node_sawed(npos) then
            remaining_charge = recursive_dig(npos, remaining_charge)
        else
            minetest.check_for_falling(npos)
    handle_drops(pos)
    vm:set_data(cutter.data)
    vm:write_to_map(true)
    vm:update_map()
    -- Update falling nodes
    for i, status in pairs(cutter.seen) do
        if status == 2 then -- actually dug
            minetest.check_for_falling(cutter.area:position(i))
        end
    end
    return remaining_charge
end
-- Function to randomize positions for new node drops
@@ -369,30 +255,50 @@
    return pos
end
-- Chainsaw entry point
local function chainsaw_dig(pos, current_charge)
    -- Start sawing things down
    local remaining_charge = recursive_dig(pos, current_charge)
    minetest.sound_play("chainsaw", {pos = pos, gain = 1.0,
            max_hear_distance = 10})
local drop_inv = minetest.create_detached_inventory("technic:chainsaw_drops", {}, ":technic")
handle_drops = function(pos)
    local n_slots = 100
    drop_inv:set_size("main", n_slots)
    drop_inv:set_list("main", {})
    -- Now drop items for the player
    for name, stack in pairs(produced) do
        -- Drop stacks of stack max or less
        local count, max = stack:get_count(), stack:get_stack_max()
        stack:set_count(max)
        while count > max do
            minetest.add_item(get_drop_pos(pos), stack)
            count = count - max
    -- Put all dropped items into the detached inventory
    for c_id, count in pairs(cutter.drops) do
        local name = minetest.get_name_from_content_id(c_id)
        -- Add drops in bulk -> keep some randomness
        while count > 0 do
            local drops = minetest.get_node_drops(name, "")
            -- higher numbers are faster but return uneven sapling counts
            local decrement = math.ceil(count * 0.3)
            decrement = math.min(count, math.max(5, decrement))
            for _, stack in ipairs(drops) do
                stack = ItemStack(stack)
                local total = math.ceil(stack:get_count() * decrement * chainsaw_efficiency)
                local stack_max = stack:get_stack_max()
                -- Split into full stacks
                while total > 0 do
                    local size = math.min(total, stack_max)
                    stack:set_count(size)
                    drop_inv:add_item("main", stack)
                    total = total - size
                end
            end
            count = count - decrement
        end
        stack:set_count(count)
    end
    -- Drop in random places
    for i = 1, n_slots do
        local stack = drop_inv:get_stack("main", i)
        if stack:is_empty() then
            break
        end
        minetest.add_item(get_drop_pos(pos), stack)
    end
    -- Clean up
    produced = {}
    return remaining_charge
    drop_inv:set_size("main", 0) -- free RAM
end
@@ -407,11 +313,8 @@
            return itemstack
        end
        local meta = minetest.deserialize(itemstack:get_metadata())
        if not meta or not meta.charge or
                meta.charge < chainsaw_charge_per_node then
            return
        end
        local meta = technic.get_stack_meta(itemstack)
        local charge = meta:get_int("technic:charge")
        local name = user:get_player_name()
        if minetest.is_protected(pointed_thing.under, name) then
@@ -421,10 +324,14 @@
        -- Send current charge to digging function so that the
        -- chainsaw will stop after digging a number of nodes
        meta.charge = chainsaw_dig(pointed_thing.under, meta.charge)
        chainsaw_dig(user, pointed_thing.under, charge)
        charge = cutter.charge
        cutter = {} -- Free RAM
        if not technic.creative_mode then
            technic.set_RE_wear(itemstack, meta.charge, chainsaw_max_charge)
            itemstack:set_metadata(minetest.serialize(meta))
            meta:set_int("technic:charge", charge)
            technic.set_RE_wear(itemstack, charge, chainsaw_max_charge)
        end
        return itemstack
    end,
technic/tools/flashlight.lua
@@ -38,12 +38,13 @@
    local hotbar = inv:get_list("main")
    for i = 1, 8 do
        if hotbar[i]:get_name() == "technic:flashlight" then
            local meta = minetest.deserialize(hotbar[i]:get_metadata())
            if meta and meta.charge and meta.charge >= 2 then
            local meta = technic.get_stack_meta(hotbar[i])
            local charge = meta:get_int("technic:charge")
            if charge >= 2 then
                if not technic.creative_mode then
                    meta.charge = meta.charge - 2;
                    technic.set_RE_wear(hotbar[i], meta.charge, flashlight_max_charge)
                    hotbar[i]:set_metadata(minetest.serialize(meta))
                    charge = charge - 2;
                    meta:set_int("technic:charge", charge)
                    technic.set_RE_wear(hotbar[i], charge, flashlight_max_charge)
                    inv:set_stack("main", i, hotbar[i])
                end
                return true
technic/tools/mining_drill.lua
@@ -46,20 +46,27 @@
    {S("3x3 nodes.")},
}
local function drill_dig_it0 (pos,player)
local function drill_dig_it0(pos, player)
    if minetest.is_protected(pos, player:get_player_name()) then
        minetest.record_protection_violation(pos, player:get_player_name())
        return
    end
    local node = minetest.get_node(pos)
    if node.name == "air" or node.name == "ignore" then return end
    if node.name == "default:lava_source" then return end
    if node.name == "default:lava_flowing" then return end
    if node.name == "default:water_source" then minetest.remove_node(pos) return end
    if node.name == "default:water_flowing" then minetest.remove_node(pos) return end
    local def = minetest.registered_nodes[node.name]
    if not def then return end
    def.on_dig(pos, node, player)
    local ndef = minetest.registered_nodes[node.name]
    if not ndef or ndef.drawtype == "airlike" then
        -- Covers "air", "ignore", unknown nodes and more.
        return
    end
    local groups = ndef and ndef.groups or {}
    if groups.lava then
        return
    end
    if groups.water then
        minetest.remove_node(pos)
        return
    end
    ndef.on_dig(pos, node, player)
end
local function drill_dig_it1 (player)
@@ -239,97 +246,60 @@
    return nodedef and nodedef.pointable
end
local function mining_drill_mk2_setmode(user,itemstack)
    local player_name=user:get_player_name()
    local item=itemstack:to_table()
    local mode = nil
    local meta=minetest.deserialize(item["metadata"])
    if meta==nil then
        meta={}
        mode=0
local function mining_drill_mkX_setmode(user, itemstack, drill_type, max_modes)
    local player_name = user:get_player_name()
    local meta = technic.get_stack_meta(itemstack)
    if not meta:contains("mode") then
        minetest.chat_send_player(player_name,
            S("Use while sneaking to change Mining Drill Mk%d modes."):format(drill_type))
    end
    if meta["mode"]==nil then
        minetest.chat_send_player(player_name, S("Use while sneaking to change Mining Drill Mk%d modes."):format(2))
        meta["mode"]=0
        mode=0
    end
    mode=(meta["mode"])
    mode=mode+1
    if mode>=5 then mode=1 end
    minetest.chat_send_player(player_name, S("Mining Drill Mk%d Mode %d"):format(2, mode)..": "..mining_drill_mode_text[mode][1])
    itemstack:set_name("technic:mining_drill_mk2_"..mode);
    meta["mode"]=mode
    itemstack:set_metadata(minetest.serialize(meta))
    local mode = meta:get_int("mode") + 1
    if mode > max_modes then mode = 1 end
    minetest.chat_send_player(player_name,
        S("Mining Drill Mk%d Mode %d"):format(2, mode)..
        ": "..mining_drill_mode_text[mode][1])
    itemstack:set_name(("technic:mining_drill_mk%d_%s"):format(drill_type, mode))
    meta:set_int("mode", mode)
    return itemstack
end
local function mining_drill_mk3_setmode(user,itemstack)
    local player_name=user:get_player_name()
    local item=itemstack:to_table()
    local meta=minetest.deserialize(item["metadata"])
    if meta==nil then
        meta={}
        mode=0
    end
    if meta["mode"]==nil then
        minetest.chat_send_player(player_name, S("Use while sneaking to change Mining Drill Mk%d modes."):format(3))
        meta["mode"]=0
        mode=0
    end
    mode=(meta["mode"])
    mode=mode+1
    if mode>=6 then mode=1 end
    minetest.chat_send_player(player_name, S("Mining Drill Mk%d Mode %d"):format(3, mode)..": "..mining_drill_mode_text[mode][1])
    itemstack:set_name("technic:mining_drill_mk3_"..mode);
    meta["mode"]=mode
    itemstack:set_metadata(minetest.serialize(meta))
    return itemstack
end
local function mining_drill_mk2_handler(itemstack, user, pointed_thing)
local function mining_drill_mkX_handler(itemstack, user, pointed_thing, drill_type, max_modes)
    local keys = user:get_player_control()
    local meta = minetest.deserialize(itemstack:get_metadata())
    if not meta or not meta.mode or keys.sneak then
        return mining_drill_mk2_setmode(user, itemstack)
    end
    if pointed_thing.type ~= "node" or not pos_is_pointable(pointed_thing.under) or not meta.charge then
        return
    end
    local charge_to_take = cost_to_use(2, meta.mode)
    if meta.charge >= charge_to_take then
        local pos = minetest.get_pointed_thing_position(pointed_thing, false)
        drill_dig_it(pos, user, meta.mode)
        if not technic.creative_mode then
            meta.charge = meta.charge - charge_to_take
            itemstack:set_metadata(minetest.serialize(meta))
            technic.set_RE_wear(itemstack, meta.charge, max_charge[2])
    local meta = technic.get_stack_meta(itemstack)
    -- Mode switching (if possible)
    if max_modes > 1 then
        if not meta:contains("mode") or keys.sneak then
            return mining_drill_mkX_setmode(user, itemstack, drill_type, max_modes)
        end
    end
    if pointed_thing.type ~= "node" or not pos_is_pointable(pointed_thing.under) then
        return
    end
    local charge = meta:get_int("technic:charge")
    local mode = meta:contains("mode") and meta:get_int("mode") or 1
    -- Check whether the tool has enough charge
    local charge_to_take = cost_to_use(drill_type, mode)
    if charge < charge_to_take then
        return
    end
    -- Do the actual shoorting action
    local pos = minetest.get_pointed_thing_position(pointed_thing, false)
    drill_dig_it(pos, user, mode)
    if not technic.creative_mode then
        charge = charge - charge_to_take
        meta:set_int("technic:charge", charge)
        technic.set_RE_wear(itemstack, charge, max_charge[drill_type])
    end
    return itemstack
end
local function mining_drill_mk3_handler(itemstack, user, pointed_thing)
    local keys = user:get_player_control()
    local meta = minetest.deserialize(itemstack:get_metadata())
    if not meta or not meta.mode or keys.sneak then
        return mining_drill_mk3_setmode(user, itemstack)
    end
    if pointed_thing.type ~= "node" or not pos_is_pointable(pointed_thing.under) or not meta.charge then
        return
    end
    local charge_to_take = cost_to_use(3, meta.mode)
    if meta.charge >= charge_to_take then
        local pos = minetest.get_pointed_thing_position(pointed_thing, false)
        drill_dig_it(pos, user, meta.mode)
        if not technic.creative_mode then
            meta.charge = meta.charge - charge_to_take
            itemstack:set_metadata(minetest.serialize(meta))
            technic.set_RE_wear(itemstack, meta.charge, max_charge[3])
        end
    end
    return itemstack
end
-- Simple mining drill registration
technic.register_power_tool("technic:mining_drill", max_charge[1])
@@ -340,26 +310,12 @@
    wear_represents = "technic_RE_charge",
    on_refill = technic.refill_RE_charge,
    on_use = function(itemstack, user, pointed_thing)
        if pointed_thing.type ~= "node" or not pos_is_pointable(pointed_thing.under) then
            return itemstack
        end
        local meta = minetest.deserialize(itemstack:get_metadata())
        if not meta or not meta.charge then
            return
        end
        local charge_to_take = cost_to_use(1, 1)
        if meta.charge >= charge_to_take then
            local pos = minetest.get_pointed_thing_position(pointed_thing, false)
            drill_dig_it(pos, user, 1)
            if not technic.creative_mode then
                meta.charge = meta.charge - charge_to_take
                itemstack:set_metadata(minetest.serialize(meta))
                technic.set_RE_wear(itemstack, meta.charge, max_charge[1])
            end
        end
        mining_drill_mkX_handler(itemstack, user, pointed_thing, 1, 1)
        return itemstack
    end,
})
-- Mk2 registration
minetest.register_tool("technic:mining_drill_mk2", {
    description = S("Mining Drill Mk%d"):format(2),
@@ -367,7 +323,7 @@
    wear_represents = "technic_RE_charge",
    on_refill = technic.refill_RE_charge,
    on_use = function(itemstack, user, pointed_thing)
        mining_drill_mk2_handler(itemstack, user, pointed_thing)
        mining_drill_mkX_handler(itemstack, user, pointed_thing, 2, 4)
        return itemstack
    end,
})
@@ -384,11 +340,13 @@
        on_refill = technic.refill_RE_charge,
        groups = {not_in_creative_inventory=1},
        on_use = function(itemstack, user, pointed_thing)
            mining_drill_mk2_handler(itemstack, user, pointed_thing)
            mining_drill_mkX_handler(itemstack, user, pointed_thing, 2, 4)
            return itemstack
        end,
    })
end
-- Mk3 registration
minetest.register_tool("technic:mining_drill_mk3", {
    description = S("Mining Drill Mk%d"):format(3),
@@ -396,8 +354,8 @@
    wear_represents = "technic_RE_charge",
    on_refill = technic.refill_RE_charge,
    on_use = function(itemstack, user, pointed_thing)
    mining_drill_mk3_handler(itemstack,user,pointed_thing)
    return itemstack
        mining_drill_mkX_handler(itemstack, user, pointed_thing, 3, 5)
        return itemstack
    end,
})
@@ -413,8 +371,8 @@
        on_refill = technic.refill_RE_charge,
        groups = {not_in_creative_inventory=1},
        on_use = function(itemstack, user, pointed_thing)
        mining_drill_mk3_handler(itemstack,user,pointed_thing)
        return itemstack
            mining_drill_mkX_handler(itemstack, user, pointed_thing, 3, 5)
            return itemstack
        end,
    })
end
technic/tools/mining_lasers.lua
@@ -101,25 +101,26 @@
        wear_represents = "technic_RE_charge",
        on_refill = technic.refill_RE_charge,
        on_use = function(itemstack, user)
            local meta = minetest.deserialize(itemstack:get_metadata())
            if not meta or not meta.charge or meta.charge == 0 then
            local meta = technic.get_stack_meta(itemstack)
            local charge = meta:get_int("technic:charge")
            if charge == 0 then
                return
            end
            local range = m[2]
            if meta.charge < m[4] then
            if charge < m[4] then
                if not allow_entire_discharging then
                    return
                end
                -- If charge is too low, give the laser a shorter range
                range = range * meta.charge / m[4]
                range = range * charge / m[4]
            end
            laser_shoot(user, range, "technic_laser_beam_mk" .. m[1] .. ".png",
                "technic_laser_mk" .. m[1])
            if not technic.creative_mode then
                meta.charge = math.max(meta.charge - m[4], 0)
                technic.set_RE_wear(itemstack, meta.charge, m[3])
                itemstack:set_metadata(minetest.serialize(meta))
                charge = math.max(charge - m[4], 0)
                meta:set_int("technic:charge", charge)
                technic.set_RE_wear(itemstack, charge, m[3])
            end
            return itemstack
        end,
technic/tools/prospector.lua
@@ -2,14 +2,16 @@
technic.register_power_tool("technic:prospector", 300000)
local function get_metadata(toolstack)
    local m = minetest.deserialize(toolstack:get_metadata())
    if not m then m = {} end
    if not m.charge then m.charge = 0 end
    if not m.target then m.target = "" end
    if not m.look_depth then m.look_depth = 7 end
    if not m.look_radius then m.look_radius = 1 end
    return m
-- Helper function to consolidate ItemStackMetaRef access and initialize
local function meta_to_table(meta)
    local t = {}
    local mt = meta:to_table()
    t.charge = tonumber(mt.fields["technic:charge"]) or 0
    t.target = mt.fields.target or ""
    t.look_depth = tonumber(mt.fields.look_depth) or 7
    t.look_radius = tonumber(mt.fields.look_radius) or 1
    return t
end
minetest.register_tool("technic:prospector", {
@@ -20,7 +22,8 @@
    on_use = function(toolstack, user, pointed_thing)
        if not user or not user:is_player() or user.is_fake_player then return end
        if pointed_thing.type ~= "node" then return end
        local toolmeta = get_metadata(toolstack)
        local meta = technic.get_stack_meta(toolstack)
        local toolmeta = meta_to_table(meta)
        local look_diameter = toolmeta.look_radius * 2 + 1
        local charge_to_take = toolmeta.look_depth * (toolmeta.look_depth + 1) * look_diameter * look_diameter
        if toolmeta.charge < charge_to_take then return end
@@ -30,9 +33,10 @@
        end
        if not technic.creative_mode then
            toolmeta.charge = toolmeta.charge - charge_to_take
            toolstack:set_metadata(minetest.serialize(toolmeta))
            meta:set_int("technic:charge", toolmeta.charge)
            technic.set_RE_wear(toolstack, toolmeta.charge, technic.power_tools[toolstack:get_name()])
        end
        -- What in the heaven's name is this evil sorcery ?
        local start_pos = pointed_thing.under
        local forward = minetest.facedir_to_dir(minetest.dir_to_facedir(user:get_look_dir(), true))
        local right = forward.x ~= 0 and { x=0, y=1, z=0 } or (forward.y ~= 0 and { x=0, y=0, z=1 } or { x=1, y=0, z=0 })
@@ -42,18 +46,42 @@
        for f = 0, toolmeta.look_depth-1 do
            for r = 0, look_diameter-1 do
                for u = 0, look_diameter-1 do
                    if minetest.get_node(vector.add(vector.add(vector.add(base_pos, vector.multiply(forward, f)), vector.multiply(right, r)), vector.multiply(up, u))).name == toolmeta.target then found = true end
                    if minetest.get_node(
                            vector.add(
                                vector.add(
                                    vector.add(base_pos,
                                        vector.multiply(forward, f)),
                                    vector.multiply(right, r)),
                                vector.multiply(up, u))
                            ).name == toolmeta.target then
                        found = true
                        break
                    end
                end
                if found then break end
            end
            if found then break end
        end
        if math.random() < 0.02 then found = not found end
        minetest.chat_send_player(user:get_player_name(), minetest.registered_nodes[toolmeta.target].description.." is "..(found and "present" or "absent").." in "..look_diameter.."x"..look_diameter.."x"..toolmeta.look_depth.." region")
        minetest.sound_play("technic_prospector_"..(found and "hit" or "miss"), { pos = vector.add(user:get_pos(), { x = 0, y = 1, z = 0 }), gain = 1.0, max_hear_distance = 10 })
        if math.random() < 0.02 then
            found = not found
        end
        local ndef = minetest.registered_nodes[toolmeta.target]
        minetest.chat_send_player(user:get_player_name(),
            ndef.description.." is "..(found and "present" or "absent")..
            " in "..look_diameter.."x"..look_diameter.."x"..toolmeta.look_depth.." region")
        minetest.sound_play("technic_prospector_"..(found and "hit" or "miss"), {
            pos = vector.add(user:get_pos(), { x = 0, y = 1, z = 0 }),
            gain = 1.0,
            max_hear_distance = 10
        })
        return toolstack
    end,
    on_place = function(toolstack, user, pointed_thing)
        if not user or not user:is_player() or user.is_fake_player then return end
        local toolmeta = get_metadata(toolstack)
        local meta = technic.get_stack_meta(toolstack)
        local toolmeta = meta_to_table(meta)
        local pointed
        if pointed_thing.type == "node" then
            local pname = minetest.get_node(pointed_thing.under).name
@@ -101,19 +129,16 @@
    if not user or not user:is_player() or user.is_fake_player then return end
    local toolstack = user:get_wielded_item()
    if toolstack:get_name() ~= "technic:prospector" then return true end
    local toolmeta = get_metadata(toolstack)
    local meta = technic.get_stack_meta(toolstack)
    for field, value in pairs(fields) do
        if field:sub(1, 7) == "target_" then
            toolmeta.target = field:sub(8)
        end
        if field:sub(1, 12) == "look_radius_" then
            toolmeta.look_radius = field:sub(13)
        end
        if field:sub(1, 11) == "look_depth_" then
            toolmeta.look_depth = field:sub(12)
            meta:set_string("target", field:sub(8))
        elseif field:sub(1, 12) == "look_radius_" then
            meta:set_string("look_radius", field:sub(13))
        elseif field:sub(1, 11) == "look_depth_" then
            meta:set_string("look_depth", field:sub(12))
        end
    end
    toolstack:set_metadata(minetest.serialize(toolmeta))
    user:set_wielded_item(toolstack)
    return true
end)
technic/tools/sonic_screwdriver.lua
@@ -41,8 +41,9 @@
    -- contrary to the default screwdriver, do not check for can_dig, to allow rotating machines with CLU's in them
    -- this is consistent with the previous sonic screwdriver
    local meta1 = minetest.deserialize(itemstack:get_metadata())
    if not meta1 or not meta1.charge or meta1.charge < 100 then
    local meta = technic.get_stack_meta(itemstack)
    local charge = meta:get_int("technic:charge")
    if charge < 100 then
        return
    end
@@ -64,9 +65,9 @@
    minetest.swap_node(pos, node)
    if not technic.creative_mode then
        meta1.charge = meta1.charge - 100
        itemstack:set_metadata(minetest.serialize(meta1))
        technic.set_RE_wear(itemstack, meta1.charge, sonic_screwdriver_max_charge)
        charge = charge - 100
        meta:set_int("technic:charge", charge)
        technic.set_RE_wear(itemstack, charge, sonic_screwdriver_max_charge)
    end
    return itemstack
technic/tools/vacuum.lua
@@ -14,24 +14,23 @@
    wear_represents = "technic_RE_charge",
    on_refill = technic.refill_RE_charge,
    on_use = function(itemstack, user, pointed_thing)
        local meta = minetest.deserialize(itemstack:get_metadata())
        if not meta or not meta.charge then
        local meta = technic.get_stack_meta(itemstack)
        local charge = meta:get_int("technic:charge")
        if charge < vacuum_charge_per_object then
            return
        end
        if meta.charge > vacuum_charge_per_object then
            minetest.sound_play("vacuumcleaner", {
                to_player = user:get_player_name(),
                gain = 0.4,
            })
        end
        minetest.sound_play("vacuumcleaner", {
            to_player = user:get_player_name(),
            gain = 0.4,
        })
        local pos = user:get_pos()
        local inv = user:get_inventory()
        for _, object in ipairs(minetest.get_objects_inside_radius(pos, vacuum_range)) do
            local luaentity = object:get_luaentity()
            if not object:is_player() and luaentity and luaentity.name == "__builtin:item" and luaentity.itemstring ~= "" then
                if inv and inv:room_for_item("main", ItemStack(luaentity.itemstring)) then
                    meta.charge = meta.charge - vacuum_charge_per_object
                    if meta.charge < vacuum_charge_per_object then
                    charge = charge - vacuum_charge_per_object
                    if charge < vacuum_charge_per_object then
                        return
                    end
                    inv:add_item("main", ItemStack(luaentity.itemstring))
@@ -45,8 +44,8 @@
            end
        end
        technic.set_RE_wear(itemstack, meta.charge, vacuum_max_charge)
        itemstack:set_metadata(minetest.serialize(meta))
        meta:set_int("technic:charge", charge)
        technic.set_RE_wear(itemstack, charge, vacuum_max_charge)
        return itemstack
    end,
})
technic_chests/common.lua
@@ -1,7 +1,7 @@
technic.chests.groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
        tubedevice=1, tubedevice_receiver=1}
        tubedevice=1, tubedevice_receiver=1, technic_chest=1}
technic.chests.groups_noinv = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
        tubedevice=1, tubedevice_receiver=1, not_in_creative_inventory=1}
        tubedevice=1, tubedevice_receiver=1, not_in_creative_inventory=1, technic_chest=1}
technic.chests.tube = {
    insert_object = function(pos, node, stack, direction)
technic_chests/depends.txt
File was deleted
technic_chests/locale/ja.txt
New file
@@ -0,0 +1,41 @@
# technic_chests japanese translation
# technic_chestsの日本語への翻訳
# by damiemk
%s Chest = %s のチェスト
%s Locked Chest = %s ロックされたチェスト
%s Locked Chest (owned by %s) = %s のロックされたチェスト (%s が所有)
Color Filter: %s = カラーフィルター: %s
Edit chest description: = チェストの説明を編集する:
# Colors
Black = 黒
Blue = 青
Brown = 茶色
Cyan = シアン
Dark Green = 濃い緑色
Dark Grey = 暗灰色
Green = 緑
Grey = 灰色
Magenta = マジェンタ
Orange = 橙色
Pink = 桃色
Red = 赤
Violet = 紫
White = 白
Yellow = 黄色
None = デフォルト
# Materials
Copper = 銅
Gold = 金
Iron = 鉄
Mithril = ミスリル
Silver = 銀
Wooden = 木造
# Sorting
Sort = 組織する
Auto-sort is %s = 自動組織が %s になっている
Off = オフ
On = オン
technic_chests/register.lua
@@ -1,12 +1,13 @@
local S = rawget(_G, "intllib") and intllib.Getter() or function(s) return s end
local pipeworks = rawget(_G, "pipeworks")
local fs_helpers = rawget(_G, "fs_helpers")
local fs_helpers
local tubelib_exists = minetest.global_exists("tubelib")
local registered_chest_data = {} -- data passed to :register()
local allow_label = ""
local tube_entry = ""
local shift_edit_field = 0
if not minetest.get_modpath("pipeworks") then
    -- Pipeworks is not installed. Simulate using a dummy table...
@@ -26,11 +27,11 @@
    fs_helpers.cycling_button = function() return "" end
else
    fs_helpers = pipeworks.fs_helpers
    allow_label = "label[0.9,0.36;Allow splitting incoming stacks from tubes]"
    shift_edit_field = 3
    allow_label = "Allow splitting incoming stacks from tubes"
    tube_entry = "^pipeworks_tube_connection_metallic.png"
end
-- Change the appearance of the chest
local chest_mark_colors = {
    {"black", S("Black")},
    {"blue", S("Blue")},
@@ -84,32 +85,43 @@
local function set_formspec(pos, data, page)
    local meta = minetest.get_meta(pos)
    local formspec = data.base_formspec
    formspec = formspec..fs_helpers.cycling_button(
                meta,
                "image_button[0,0.35;1,0.6",
                "splitstacks",
                {
                    pipeworks.button_off,
                    pipeworks.button_on
                }
            )..allow_label
    -- Static formspec elements are in base_formspec
    local fs = { data.base_formspec }
    -- Pipeworks splitting setting
    fs[#fs + 1] = fs_helpers.cycling_button(
        meta,
        "image_button[0,0.5;1,0.6",
        "splitstacks",
        {
            pipeworks.button_off,
            pipeworks.button_on
        }
    )
    if data.autosort then
        local status = meta:get_int("autosort")
        formspec = formspec.."button["..(data.hileft+2)..","..(data.height+1.1)..";3,0.8;autosort_to_"..(1-status)..";"..S("Auto-sort is %s"):format(status == 1 and S("On") or S("Off")).."]"
        fs[#fs + 1] = ("checkbox[%g,%g;autosort_to_%s;%s;%s]"):format(
            data.hileft + 2.2, data.lotop - 1.15,
            tostring(1 - status), S("Auto-sort upon exit"), tostring(status == 1))
    end
    if data.infotext then
        local formspec_infotext = minetest.formspec_escape(meta:get_string("infotext"))
        local button_fmt = "image_button[%g,0;0.8,0.8;%s;%s;]"
        if page == "main" then
            formspec = formspec.."image_button["..(shift_edit_field+data.hileft+2.1)..",0.1;0.8,0.8;"
                    .."technic_pencil_icon.png;edit_infotext;]"
                    .."label["..(shift_edit_field+data.hileft+3)..",0;"..formspec_infotext.."]"
            fs[#fs + 1] = button_fmt:format(data.hileft + 6.1,
                "technic_pencil_icon.png", "edit_infotext")
            fs[#fs + 1] = "label["..(data.hileft+7.1)..",0.1;"..formspec_infotext.."]"
        elseif page == "edit_infotext" then
            formspec = formspec.."image_button["..(shift_edit_field+data.hileft+2.1)..",0.1;0.8,0.8;"
                    .."technic_checkmark_icon.png;save_infotext;]"
                    .."field["..(shift_edit_field+data.hileft+3.3)..",0.2;4.8,1;"
                    .."infotext_box;"..S("Edit chest description:")..";"
            fs[#fs + 1] = button_fmt:format(data.hileft + 6.1,
                "technic_checkmark_icon.png", "save_infotext")
            fs[#fs + 1] = "field["..(data.hileft+7.3)..",0.2;4,1;"
                    .."infotext_box;;"
                    ..formspec_infotext.."]"
        end
    end
@@ -121,9 +133,9 @@
        else
            colorName = S("None")
        end
        formspec = formspec.."label["..(data.coleft+0.2)..","..(data.lotop+3)..";"..S("Color Filter: %s"):format(colorName).."]"
        fs[#fs + 1] = "label["..(data.coleft+0.2)..","..(data.lotop+3)..";"..S("Color Filter: %s"):format(colorName).."]"
    end
    meta:set_string("formspec", formspec)
    meta:set_string("formspec", table.concat(fs))
end
local function sort_inventory(inv)
@@ -170,6 +182,7 @@
        if fields.sort or (data.autosort and fields.quit and meta:get_int("autosort") == 1) then
            sort_inventory(meta:get_inventory())
            return -- No formspec update
        end
        if fields.edit_infotext then
            page = "edit_infotext"
@@ -190,7 +203,6 @@
            fs_helpers.on_receive_fields(pos, fields)
        end
        meta:get_inventory():set_size("main", data.width * data.height)
        set_formspec(pos, data, page)
    end
end
@@ -198,10 +210,8 @@
function technic.chests:definition(name, data)
    local lname = name:lower()
    name = S(name)
    local d = {}
    for k, v in pairs(data) do d[k] = v end
    data = d
    -- Calculate formspec positions
    data.lowidth = 8
    data.ovwidth = math.max(data.lowidth, data.width)
    data.hileft = (data.ovwidth - data.width) / 2
@@ -221,24 +231,30 @@
    data.lotop = data.height + 2
    data.ovheight = data.lotop + 4
    local locked_after_place = nil
    local front = {"technic_"..lname.."_chest_front.png"}
    data.base_formspec = "size["..data.ovwidth..","..data.ovheight.."]"..
            "label[0,0;"..S("%s Chest"):format(name).."]"..
            "list[context;main;"..data.hileft..",1;"..data.width..","..data.height..";]"..
            "list[current_player;main;"..data.loleft..","..data.lotop..";8,4;]"..
            "background[-0.19,-0.25;"..(data.ovwidth+0.4)..","..(data.ovheight+0.75)..";technic_chest_form_bg.png]"..
            "background["..data.hileft..",1;"..data.width..","..data.height..";technic_"..lname.."_chest_inventory.png]"..
            "background["..data.loleft..","..data.lotop..";8,4;technic_main_inventory.png]"..
            "listring[]"
    -- Set up constant formspec fields
    local fs = {
        "size["..data.ovwidth..","..data.ovheight.."]",
        "label[0,0;"..S("%s Chest"):format(name).."]",
        "list[context;main;"..data.hileft..",1;"..data.width..","..data.height..";]",
        "list[current_player;main;"..data.loleft..","..data.lotop..";8,4;]",
        "listring[]"
    }
    if #allow_label > 0 then
        fs[#fs + 1] = ("label[0.9,0.5;%s]"):format(allow_label)
    end
    if data.color then
        fs[#fs + 1] = get_color_buttons(data.coleft, data.lotop)
    end
    if data.sort then
        data.base_formspec = data.base_formspec.."button["..data.hileft..","..(data.height+1.1)..";1,0.8;sort;"..S("Sort").."]"
        fs[#fs + 1] = ("button[%g,%g;2,0.7;sort;%s]"):format(
            data.hileft, data.lotop - 1, S("Sort now"))
    end
    if data.color then
        data.base_formspec = data.base_formspec..get_color_buttons(data.coleft, data.lotop)
    end
    data.base_formspec = table.concat(fs)
    local front = {"technic_"..lname.."_chest_front.png"}
    local locked_after_place
    if data.locked then
        locked_after_place = function(pos, placer)
            local meta = minetest.get_meta(pos)
@@ -371,10 +387,12 @@
}
function technic.chests:register(name, data)
    data = table.copy(data) -- drop reference
    local def = technic.chests:definition(name, data)
    local nn = "technic:"..name:lower()..(data.locked and "_locked" or "").."_chest"
    minetest.register_node(":"..nn, def)
    registered_chest_data[nn] = data
    if tubelib_exists then
        tubelib.register_node(nn, {}, _TUBELIB_CALLBACKS)
@@ -396,7 +414,11 @@
            colordef.drop = nn
            colordef.groups = self.groups_noinv
            colordef.tiles = { def.tiles[1], def.tiles[2], def.tiles[3], def.tiles[4], def.tiles[5], mk_front("technic_chest_overlay"..postfix..".png") }
            minetest.register_node(":"..nn..postfix, colordef)
            local new_name = nn .. postfix
            minetest.register_node(":" .. new_name, colordef)
            registered_chest_data[new_name] = data -- for all colors
            if tubelib_exists then
                tubelib.register_node(nn..postfix, {}, _TUBELIB_CALLBACKS)
            end
@@ -404,3 +426,15 @@
    end
end
-- Migration of chest formspecs
-- Group is specified in common.lua
minetest.register_lbm({
    label = "technic_chests formspec upgrade",
    name = "technic_chests:upgrade_formspec",
    nodenames = {"group:technic_chest"},
    run_at_every_load = false,
    action = function(pos, node)
        set_formspec(pos, registered_chest_data[node.name], "main")
    end
})
technic_chests/textures/technic_chest_form_bg.png
Binary files differ
technic_chests/textures/technic_copper_chest_inventory.png
Binary files differ
technic_chests/textures/technic_form_bg.png
Binary files differ
technic_chests/textures/technic_gold_chest_inventory.png
Binary files differ
technic_chests/textures/technic_iron_chest_inventory.png
Binary files differ
technic_chests/textures/technic_main_inventory.png
Binary files differ
technic_chests/textures/technic_mithril_chest_inventory.png
Binary files differ
technic_chests/textures/technic_silver_chest_inventory.png
Binary files differ
technic_chests/textures/technic_wooden_chest_inventory.png
Binary files differ
technic_cnc/cnc.lua
@@ -29,7 +29,7 @@
    allow_metadata_inventory_take = technic.machine_inventory_take
    allow_metadata_inventory_move = technic.machine_inventory_move
    can_dig = technic.machine_can_dig
    desc_tr = S("%s CNC Machine"):format("LV")
    desc_tr = S("@1 CNC Machine", S("LV"))
else
    minetest.register_craft({
        output = 'technic:cnc',
@@ -72,74 +72,68 @@
    end
end
local onesize_products = {
    slope                    = 2,
    slope_edge               = 1,
    slope_inner_edge         = 1,
    pyramid                  = 2,
    spike                    = 1,
    cylinder                 = 2,
    oblate_spheroid          = 1,
    sphere                   = 1,
    stick                    = 8,
    slope_upsdown            = 2,
    slope_edge_upsdown       = 1,
    slope_inner_edge_upsdown = 1,
    cylinder_horizontal      = 2,
    slope_lying              = 2,
    onecurvededge            = 1,
    twocurvededge            = 1,
}
local twosize_products = {
    element_straight         = 2,
    element_end              = 2,
    element_cross            = 1,
    element_t                = 1,
    element_edge             = 2,
}
local function add_buttons(do_variants, t, x, y)
    local X_OFFSET = 1
    local BUTTONS_PER_ROW = 7
local cnc_formspec =
    "size[9,11;]"..
    "label[1,0;"..S("Choose Milling Program:").."]"..
    "image_button[1,0.5;1,1;technic_cnc_slope.png;slope; ]"..
    "image_button[2,0.5;1,1;technic_cnc_slope_edge.png;slope_edge; ]"..
    "image_button[3,0.5;1,1;technic_cnc_slope_inner_edge.png;slope_inner_edge; ]"..
    "image_button[4,0.5;1,1;technic_cnc_pyramid.png;pyramid; ]"..
    "image_button[5,0.5;1,1;technic_cnc_spike.png;spike; ]"..
    "image_button[6,0.5;1,1;technic_cnc_cylinder.png;cylinder; ]"..
    "image_button[7,0.5;1,1;technic_cnc_oblate_spheroid.png;oblate_spheroid; ]"..
    "image_button[8,0.5;1,1;technic_cnc_stick.png;stick; ]"..
    -- ipairs: only iterate over continuous integers
    for _, data in ipairs(technic_cnc.programs) do
        -- Never add full variants. Only add half variants when asked
        if not data.half_counterpart and (do_variants == (data.full_counterpart ~= nil)) then
            --print("add", data.suffix)
            t[#t + 1] = ("image_button[%g,%g;1,1;%s.png;%s; ]"):format(
                x + X_OFFSET, y, data.suffix, data.short_name
            )
            t[#t + 1] = ("tooltip[%s;%s]"):format(
                data.short_name, minetest.formspec_escape(data.desc .. " (* " .. data.output .. ")")
            )
    "image_button[1,1.5;1,1;technic_cnc_slope_upsdwn.png;slope_upsdown; ]"..
    "image_button[2,1.5;1,1;technic_cnc_slope_edge_upsdwn.png;slope_edge_upsdown; ]"..
    "image_button[3,1.5;1,1;technic_cnc_slope_inner_edge_upsdwn.png;slope_inner_edge_upsdown; ]"..
    "image_button[4,1.5;1,1;technic_cnc_cylinder_horizontal.png;cylinder_horizontal; ]"..
    "image_button[5,1.5;1,1;technic_cnc_sphere.png;sphere; ]"..
            x = x + 1
            if x == BUTTONS_PER_ROW then
                x = 0
                y = y + 1
            end
        end
    end
end
    "image_button[1,2.5;1,1;technic_cnc_slope_lying.png;slope_lying; ]"..
    "image_button[2,2.5;1,1;technic_cnc_onecurvededge.png;onecurvededge; ]"..
    "image_button[3,2.5;1,1;technic_cnc_twocurvededge.png;twocurvededge; ]"..
local function make_formspec()
    local t = {
        "size[9,11;]",
        "label[1,0;"..S("Choose Milling Program:").."]",
    }
    add_buttons(false, t, 0, 0.5)
    "label[1,3.5;"..S("Slim Elements half / normal height:").."]"..
    t[#t + 1] = (
        "label[1,3.5;"..S("Slim Elements half / normal height:").."]"..
        "image_button[1,4;1,0.5;technic_cnc_full.png;full; ]"..
        "image_button[1,4.5;1,0.5;technic_cnc_half.png;half; ]"
    )
    add_buttons(true, t, 1, 4)
    "image_button[1,4;1,0.5;technic_cnc_full.png;full; ]"..
    "image_button[1,4.5;1,0.5;technic_cnc_half.png;half; ]"..
    "image_button[2,4;1,1;technic_cnc_element_straight.png;element_straight; ]"..
    "image_button[3,4;1,1;technic_cnc_element_end.png;element_end; ]"..
    "image_button[4,4;1,1;technic_cnc_element_cross.png;element_cross; ]"..
    "image_button[5,4;1,1;technic_cnc_element_t.png;element_t; ]"..
    "image_button[6,4;1,1;technic_cnc_element_edge.png;element_edge; ]"..
    t[#t + 1] = (
        "label[0, 5;"..S("In:").."]"..
        "list[current_name;src;0.5,5.5;1,1;]"..
        "label[4, 5;"..S("Out:").."]"..
        "list[current_name;dst;5,5.5;4,1;]"..
    "label[0, 5.5;"..S("In:").."]"..
    "list[current_name;src;0.5,5.5;1,1;]"..
    "label[4, 5.5;"..S("Out:").."]"..
    "list[current_name;dst;5,5.5;4,1;]"..
        "list[current_player;main;0,7;8,4;]"..
        "listring[current_name;dst]"..
        "listring[current_player;main]"..
        "listring[current_name;src]"..
        "listring[current_player;main]"
    )
    "list[current_player;main;0,7;8,4;]"..
    "listring[current_name;dst]"..
    "listring[current_player;main]"..
    "listring[current_name;src]"..
    "listring[current_player;main]"
    return table.concat(t)
end
local cnc_formspec = nil
minetest.register_on_mods_loaded(function()
    technic_cnc._populate_shortcuts()
    cnc_formspec = make_formspec()
end)
-- The form handler is declared here because we need it in both the inactive and active modes
-- in order to be able to change programs wile it is running.
@@ -163,32 +157,33 @@
    local inv        = meta:get_inventory()
    local inputstack = inv:get_stack("src", 1)
    local inputname  = inputstack:get_name()
    local multiplier = 0
    local size       = meta:get_int("size")
    if size < 1 then size = 1 end
    for k, _ in pairs(fields) do
        -- Set a multipier for the half/full size capable blocks
        if twosize_products[k] ~= nil then
            multiplier = size * twosize_products[k]
        else
            multiplier = onesize_products[k]
        local program = technic_cnc.programs["technic_cnc_" .. k]
        if size == 1 and program and program.full_counterpart then
            program = technic_cnc.programs["technic_cnc_" .. k .. "_double"]
        end
        if program then
            local multiplier = program.output
            local product = inputname .. "_" .. program.suffix
        if onesize_products[k] ~= nil or twosize_products[k] ~= nil then
            meta:set_float( "cnc_multiplier", multiplier)
            meta:set_string("cnc_user", sender:get_player_name())
        end
        if onesize_products[k] ~= nil or (twosize_products[k] ~= nil and size==2) then
            meta:set_string("cnc_product",  inputname .. "_technic_cnc_" .. k)
            --print(inputname .. "_technic_cnc_" .. k)
            break
        end
            if program.half_counterpart then -- is full
                if size == 1 then
                    meta:set_string("cnc_product", product)
                    --print(product, multiplier)
                end
                break -- no larger sizes allowed
            end
        if twosize_products[k] ~= nil and size==1 then
            meta:set_string("cnc_product",  inputname .. "_technic_cnc_" .. k .. "_double")
            --print(inputname .. "_technic_cnc_" .. k .. "_double")
            -- half for normal
            meta:set_string("cnc_product", product)
            --print(product, multiplier)
            break
        end
    end
@@ -221,7 +216,7 @@
       (not minetest.registered_nodes[result]) or
       (not inv:room_for_item("dst", result)) then
        technic.swap_node(pos, machine_node)
        meta:set_string("infotext", S("%s Idle"):format(machine_name))
        meta:set_string("infotext", S("@1 Idle", machine_name))
        meta:set_string("cnc_product", "")
        meta:set_int("LV_EU_demand", 0)
        return
@@ -229,14 +224,14 @@
    if eu_input < demand then
        technic.swap_node(pos, machine_node)
        meta:set_string("infotext", S("%s Unpowered"):format(machine_name))
        meta:set_string("infotext", S("@1 Unpowered", machine_name))
    elseif eu_input >= demand then
        technic.swap_node(pos, machine_node.."_active")
        meta:set_string("infotext", S("%s Active"):format(machine_name))
        meta:set_string("infotext", S("@1 Active", machine_name))
        meta:set_int("src_time", meta:get_int("src_time") + 1)
        if meta:get_int("src_time") >= 3 then -- 3 ticks per output
            meta:set_int("src_time", 0)
            srcstack = inv:get_stack("src", 1)
            local srcstack = inv:get_stack("src", 1)
            srcstack:take_item()
            inv:set_stack("src", 1, srcstack)
            inv:add_item("dst", result.." "..meta:get_int("cnc_multiplier"))
technic_cnc/cnc_api.lua
@@ -2,21 +2,33 @@
-- Again code is adapted from the NonCubic Blocks MOD v1.4 by yves_de_beck
local S = technic_cnc.getter
local ALPHA_CLIP = minetest.features.use_texture_alpha_string_modes and "clip" or true
-- REGISTER NONCUBIC FORMS, CREATE MODELS AND RECIPES:
------------------------------------------------------
-- Define slope boxes for the various nodes
--[[
Additional keys after registration:
    programs[program.suffix] = program
Additional fields after registration:
    program.short_name = (trimmed suffix)
    program.full_counterpart = suffix (optional, for full/half variants)
    program.half_counterpart = suffix (optional, for full/half variants)
]]
-------------------------------------------
technic_cnc.programs = {
    { suffix  = "technic_cnc_stick",
        model = {-0.15, -0.5, -0.15, 0.15, 0.5, 0.15},
        desc  = S("Stick")
        desc  = S("Stick"),
        output = 8
    },
    { suffix  = "technic_cnc_element_end_double",
        model = {-0.3, -0.5, -0.3, 0.3, 0.5, 0.5},
        desc  = S("Element End Double")
        desc  = S("Element End Double"),
        output = 2
    },
    { suffix  = "technic_cnc_element_cross_double",
@@ -24,7 +36,8 @@
            {0.3, -0.5, -0.3, 0.5, 0.5, 0.3},
            {-0.3, -0.5, -0.5, 0.3, 0.5, 0.5},
            {-0.5, -0.5, -0.3, -0.3, 0.5, 0.3}},
        desc  = S("Element Cross Double")
        desc  = S("Element Cross Double"),
        output = 1
    },
    { suffix  = "technic_cnc_element_t_double",
@@ -32,24 +45,28 @@
            {-0.3, -0.5, -0.5, 0.3, 0.5, 0.3},
            {-0.5, -0.5, -0.3, -0.3, 0.5, 0.3},
            {0.3, -0.5, -0.3, 0.5, 0.5, 0.3}},
        desc  = S("Element T Double")
        desc  = S("Element T Double"),
        output = 1
    },
    { suffix  = "technic_cnc_element_edge_double",
        model = {
            {-0.3, -0.5, -0.5, 0.3, 0.5, 0.3},
            {-0.5, -0.5, -0.3, -0.3, 0.5, 0.3}},
        desc  = S("Element Edge Double")
        desc  = S("Element Edge Double"),
        output = 2
    },
    { suffix  = "technic_cnc_element_straight_double",
        model = {-0.3, -0.5, -0.5, 0.3, 0.5, 0.5},
        desc  = S("Element Straight Double")
        desc  = S("Element Straight Double"),
        output = 2
    },
    { suffix  = "technic_cnc_element_end",
        model = {-0.3, -0.5, -0.3, 0.3, 0, 0.5},
        desc  = S("Element End")
        desc  = S("Element End"),
        output = nil -- calculated
    },
    { suffix  = "technic_cnc_element_cross",
@@ -57,7 +74,8 @@
            {0.3, -0.5, -0.3, 0.5, 0, 0.3},
            {-0.3, -0.5, -0.5, 0.3, 0, 0.5},
            {-0.5, -0.5, -0.3, -0.3, 0, 0.3}},
        desc  = S("Element Cross")
        desc  = S("Element Cross"),
        output = nil -- calculated
    },
    { suffix  = "technic_cnc_element_t",
@@ -65,19 +83,22 @@
            {-0.3, -0.5, -0.5, 0.3, 0, 0.3},
            {-0.5, -0.5, -0.3, -0.3, 0, 0.3},
            {0.3, -0.5, -0.3, 0.5, 0, 0.3}},
        desc  = S("Element T")
        desc  = S("Element T"),
        output = nil -- calculated
    },
    { suffix  = "technic_cnc_element_edge",
        model = {
            {-0.3, -0.5, -0.5, 0.3, 0, 0.3},
            {-0.5, -0.5, -0.3, -0.3, 0, 0.3}},
        desc  = S("Element Edge")
        desc  = S("Element Edge"),
        output = nil -- calculated
    },
    { suffix  = "technic_cnc_element_straight",
        model = {-0.3, -0.5, -0.5, 0.3, 0, 0.5},
        desc  = S("Element Straight")
        desc  = S("Element Straight"),
        output = nil -- calculated
    },
    { suffix  = "technic_cnc_oblate_spheroid",
@@ -90,32 +111,38 @@
                { -8/16, -4/16, -8/16, 8/16,  4/16, 8/16 },
                { -6/16, -8/16, -6/16, 6/16, -4/16, 6/16 }
            }
        }
        },
        output = 1
    },
    { suffix  = "technic_cnc_sphere",
        model = "technic_cnc_sphere.obj",
        desc  = S("Sphere")
        desc  = S("Sphere"),
        output = 1
    },
    { suffix  = "technic_cnc_cylinder_horizontal",
        model = "technic_cnc_cylinder_horizontal.obj",
        desc  = S("Horizontal Cylinder")
        desc  = S("Horizontal Cylinder"),
        output = 2
    },
    { suffix  = "technic_cnc_cylinder",
        model = "technic_cnc_cylinder.obj",
        desc  = S("Cylinder")
        desc  = S("Cylinder"),
        output = 2
    },
    { suffix  = "technic_cnc_twocurvededge",
        model = "technic_cnc_two_curved_edge.obj",
        desc  = S("Two Curved Edge/Corner Block")
        desc  = S("Two Curved Edge/Corner Block"),
        output = 1
    },
    { suffix  = "technic_cnc_onecurvededge",
        model = "technic_cnc_one_curved_edge.obj",
        desc  = S("One Curved Edge Block")
        desc  = S("One Curved Edge Block"),
        output = 1
    },
    { suffix  = "technic_cnc_spike",
@@ -129,7 +156,8 @@
                { -6/16, -4/16, -6/16, 6/16,     0, 6/16 },
                { -8/16, -8/16, -8/16, 8/16, -4/16, 8/16 }
            }
        }
        },
        output = 1
    },
    { suffix  = "technic_cnc_pyramid",
@@ -143,7 +171,8 @@
                { -6/16, -6/16, -6/16, 6/16, -4/16, 6/16 },
                { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 }
            }
        }
        },
        output = 2
    },
    { suffix  = "technic_cnc_slope_inner_edge_upsdown",
@@ -164,7 +193,8 @@
                { -0.5,   0.25, -0.25, 0.5,  0,     0.5  },
                { -0.5,   0.5,  -0.5,  0.5,  0.25,  0.5  }
            }
        }
        },
        output = 1
    },
    { suffix  = "technic_cnc_slope_edge_upsdown",
@@ -178,7 +208,8 @@
                {     0,     0,     0, 8/16, -4/16, 8/16 },
                {  4/16, -4/16,  4/16, 8/16, -8/16, 8/16 }
            }
        }
        },
        output = 1
    },
    { suffix  = "technic_cnc_slope_inner_edge",
@@ -199,7 +230,8 @@
                { -0.5,   0.25,  0.25, 0.5,  0.5,   0.5  },
                {  0.25,  0.25, -0.5,  0.5,  0.5,   0.5  }
            }
        }
        },
        output = 1
    },
    { suffix  = "technic_cnc_slope_edge",
@@ -213,7 +245,8 @@
                { -4/16, -4/16, -4/16, 8/16,     0, 8/16 },
                { -8/16, -8/16, -8/16, 8/16, -4/16, 8/16 }
            }
        }
        },
        output = 1
    },
    { suffix  = "technic_cnc_slope_upsdown",
@@ -227,7 +260,8 @@
                { -8/16,     0,     0, 8/16, -4/16, 8/16 },
                { -8/16, -4/16,  4/16, 8/16, -8/16, 8/16 }
            }
        }
        },
        output = 1
    },
    { suffix  = "technic_cnc_slope_lying",
@@ -241,7 +275,8 @@
                { -4/16, -8/16, -4/16,     0, 8/16, 8/16 },
                { -8/16, -8/16, -8/16, -4/16, 8/16, 8/16 }
            }
        }
        },
        output = 2
    },
    { suffix  = "technic_cnc_slope",
@@ -255,10 +290,39 @@
                { -8/16, -4/16, -4/16, 8/16,     0, 8/16 },
                { -8/16, -8/16, -8/16, 8/16, -4/16, 8/16 }
            }
        }
        },
        output = 2
    },
}
technic_cnc._populate_shortcuts = function()
    -- Program quick access by string key
    for _, data in ipairs(technic_cnc.programs) do
        technic_cnc.programs[data.suffix] = data
        data.short_name = assert(data.suffix:match("technic_cnc_(%S+)"))
    end
    -- Detect half/full counterparts
    for k, data in pairs(technic_cnc.programs) do
        if type(k) == "string" then
            local full = technic_cnc.programs[k .. "_double"]
            if full then
                full.half_counterpart = k
                data.full_counterpart = k .. "_double"
                data.output = full.output * 2
                --print("populate", k)
            end
        end
    end
    -- Final checks
    for _, data in ipairs(technic_cnc.programs) do
        assert(type(data.output) == "number", data.suffix)
        assert(type(data.short_name) == "string", data.suffix)
    end
end
-- Allow disabling certain programs for some node. Default is allowing all types for all nodes
technic_cnc.programs_disable = {
@@ -298,6 +362,7 @@
        tiles         = images,
        paramtype     = "light",
        paramtype2    = "facedir",
        use_texture_alpha = ALPHA_CLIP,
        walkable      = true,
        groups        = groups,
        selection_box = sbox,
@@ -323,45 +388,5 @@
                groups, images, description.." "..data.desc, data.cbox, data.sbox)
        end
    end
end
-- REGISTER NEW TECHNIC_CNC_API's PART 2: technic_cnc..register_element_end(subname, recipeitem, groups, images, desc_element_xyz)
-----------------------------------------------------------------------------------------------------------------------
function technic_cnc.register_slope_edge_etc(recipeitem, groups, images, desc_slope, desc_slope_lying, desc_slope_upsdown, desc_slope_edge, desc_slope_inner_edge, desc_slope_upsdwn_edge, desc_slope_upsdwn_inner_edge, desc_pyramid, desc_spike, desc_onecurvededge, desc_twocurvededge, desc_cylinder, desc_cylinder_horizontal, desc_spheroid, desc_element_straight, desc_element_edge, desc_element_t, desc_element_cross, desc_element_end)
         technic_cnc.register_slope(recipeitem, groups, images, desc_slope)
         technic_cnc.register_slope_lying(recipeitem, groups, images, desc_slope_lying)
         technic_cnc.register_slope_upsdown(recipeitem, groups, images, desc_slope_upsdown)
         technic_cnc.register_slope_edge(recipeitem, groups, images, desc_slope_edge)
         technic_cnc.register_slope_inner_edge(recipeitem, groups, images, desc_slope_inner_edge)
         technic_cnc.register_slope_edge_upsdown(recipeitem, groups, images, desc_slope_upsdwn_edge)
         technic_cnc.register_slope_inner_edge_upsdown(recipeitem, groups, images, desc_slope_upsdwn_inner_edge)
         technic_cnc.register_pyramid(recipeitem, groups, images, desc_pyramid)
         technic_cnc.register_spike(recipeitem, groups, images, desc_spike)
         technic_cnc.register_onecurvededge(recipeitem, groups, images, desc_onecurvededge)
         technic_cnc.register_twocurvededge(recipeitem, groups, images, desc_twocurvededge)
         technic_cnc.register_cylinder(recipeitem, groups, images, desc_cylinder)
         technic_cnc.register_cylinder_horizontal(recipeitem, groups, images, desc_cylinder_horizontal)
         technic_cnc.register_spheroid(recipeitem, groups, images, desc_spheroid)
         technic_cnc.register_element_straight(recipeitem, groups, images, desc_element_straight)
         technic_cnc.register_element_edge(recipeitem, groups, images, desc_element_edge)
         technic_cnc.register_element_t(recipeitem, groups, images, desc_element_t)
         technic_cnc.register_element_cross(recipeitem, groups, images, desc_element_cross)
         technic_cnc.register_element_end(recipeitem, groups, images, desc_element_end)
end
-- REGISTER STICKS: noncubic.register_xyz(recipeitem, groups, images, desc_element_xyz)
------------------------------------------------------------------------------------------------------------
function technic_cnc.register_stick_etc(recipeitem, groups, images, desc_stick)
         technic_cnc.register_stick(recipeitem, groups, images, desc_stick)
end
function technic_cnc.register_elements(recipeitem, groups, images, desc_element_straight_double, desc_element_edge_double, desc_element_t_double, desc_element_cross_double, desc_element_end_double)
         technic_cnc.register_element_straight_double(recipeitem, groups, images, desc_element_straight_double)
         technic_cnc.register_element_edge_double(recipeitem, groups, images, desc_element_edge_double)
         technic_cnc.register_element_t_double(recipeitem, groups, images, desc_element_t_double)
         technic_cnc.register_element_cross_double(recipeitem, groups, images, desc_element_cross_double)
         technic_cnc.register_element_end_double(recipeitem, groups, images, desc_element_end_double)
end
technic_cnc/cnc_materials.lua
@@ -3,129 +3,68 @@
local S = technic_cnc.getter
-- DIRT
-------
technic_cnc.register_all("default:dirt",
                {snappy=2,choppy=2,oddly_breakable_by_hand=3,not_in_creative_inventory=1},
                {"default_dirt.png"},
                S("Dirt"))
-- (DIRT WITH) GRASS
--------------------
technic_cnc.register_all("default:dirt_with_grass",
                {snappy=2,choppy=2,oddly_breakable_by_hand=3,not_in_creative_inventory=1},
                {"default_grass.png"},
                S("Grassy dirt"))
-- WOOD
-------
technic_cnc.register_all("default:wood",
                {snappy=2, choppy=2, oddly_breakable_by_hand=2, not_in_creative_inventory=1},
                {"default_wood.png"},
                S("Wooden"))
-- STONE
--------
technic_cnc.register_all("default:stone",
                {cracky=3, not_in_creative_inventory=1},
                {"default_stone.png"},
                S("Stone"))
-- COBBLE
---------
technic_cnc.register_all("default:cobble",
                {cracky=3, not_in_creative_inventory=1},
                {"default_cobble.png"},
                S("Cobble"))
-- BRICK
--------
technic_cnc.register_all("default:brick",
                {cracky=3, not_in_creative_inventory=1},
                {"default_brick.png"},
                S("Brick"))
local function register_material(nodename, tiles_override, descr_override)
    local ndef = minetest.registered_nodes[nodename]
    if not ndef then
        return
    end
-- SANDSTONE
------------
technic_cnc.register_all("default:sandstone",
                {crumbly=2, cracky=3, not_in_creative_inventory=1},
                {"default_sandstone.png"},
                S("Sandstone"))
    local groups = {
        cracky = ndef.groups.cracky,
        crumbly = ndef.groups.crumbly,
        choppy = ndef.groups.choppy,
        flammable = ndef.groups.flammable,
        level = ndef.groups.level,
        snappy = ndef.groups.snappy,
        wood = ndef.groups.wood,
        oddly_breakable_by_hand = ndef.groups.oddly_breakable_by_hand,
        not_in_creative_inventory = 1,
    }
    local count = 0
    for _ in pairs(groups) do
        count = count + 1
    end
    assert(count >= 2, "Too few groups. node name=" .. nodename)
-- LEAVES
---------
technic_cnc.register_all("default:leaves",
                {snappy=2, choppy=2, oddly_breakable_by_hand=3, not_in_creative_inventory=1},
                {"default_leaves.png"},
                S("Leaves"))
-- TREE
-------
technic_cnc.register_all("default:tree",
                {snappy=1, choppy=2, oddly_breakable_by_hand=2, flammable=3, wood=1, not_in_creative_inventory=1},
                {"default_tree.png"},
                S("Tree"))
    local tiles = tiles_override or { ndef.tiles[#ndef.tiles] }
    assert(tiles and #tiles == 1, "Unknown tile format in node name=" .. nodename)
-- Bronze
--------
technic_cnc.register_all("default:bronzeblock",
                {cracky=1, level=2, not_in_creative_inventory=1},
                {"default_bronze_block.png"},
                S("Bronze"))
local steeltex = "default_steel_block.png"
local steelname = "Steel"
if technic_cnc.technic_modpath then
    steeltex = "technic_wrought_iron_block.png"
    steelname = "Wrought Iron"
    -- Stainless Steel
    --------
    technic_cnc.register_all("technic:stainless_steel_block",
                    {cracky=1, level=2, not_in_creative_inventory=1},
                    {"technic_stainless_steel_block.png"},
                    S("Stainless Steel"))
    -- Marble
    ------------
    technic_cnc.register_all("technic:marble",
                    {cracky=3, not_in_creative_inventory=1},
                    {"technic_marble.png"},
                    S("Marble"))
    -- Granite
    ------------
    technic_cnc.register_all("technic:granite",
                    {cracky=1, not_in_creative_inventory=1},
                    {"technic_granite.png"},
                    S("Granite"))
    -- Blast-resistant concrete
    ---------------------------
    technic_cnc.register_all("technic:blast_resistant_concrete",
                    {cracky=2, level=2, not_in_creative_inventory=1},
                    {"technic_blast_resistant_concrete_block.png"},
                    S("Blast-resistant concrete"))
    technic_cnc.register_all(nodename,
        groups,
        tiles,
        descr_override or ndef.description or "<unknown>"
    )
end
-- STEEL
---------------
technic_cnc.register_all("default:steelblock",
                {cracky=1, level=2, not_in_creative_inventory=1},
                {steeltex},
                S(steelname))
register_material("default:dirt")
register_material("default:dirt_with_grass", {"default_grass.png"}, S("Grassy dirt"))
register_material("default:wood", nil, S("Wooden"))
register_material("default:stone")
register_material("default:cobble")
register_material("default:sandstone")
register_material("default:leaves")
register_material("default:tree")
register_material("default:bronzeblock", nil, S("Bronze"))
local steelname = S("Steel")
if technic_cnc.technic_modpath then
    steelname = S("Wrought Iron")
    register_material("technic:stainless_steel_block", nil, S("Stainless Steel"))
    register_material("technic:stainless_steel_block")
    register_material("technic:marble")
    register_material("technic:granite")
    register_material("technic:blast_resistant_concrete")
    register_material("technic:blast_resistant_concrete")
end
register_material("default:steelblock", nil, steelname)
-- CONCRETE AND CEMENT
----------------------
technic_cnc.register_all("basic_materials:concrete_block",
                {cracky=2, level=2, not_in_creative_inventory=1},
                {"basic_materials_concrete_block.png"},
                S("Concrete"))
technic_cnc.register_all("basic_materials:cement_block",
                {cracky=2, level=2, not_in_creative_inventory=1},
                {"basic_materials_cement_block.png"},
                S("Cement"))
technic_cnc.register_all("basic_materials:brass_block",
                {cracky=1, level=2, not_in_creative_inventory=1},
                {"basic_materials_brass_block.png"},
                S("Brass block"))
register_material("basic_materials:concrete_block")
register_material("basic_materials:cement_block")
register_material("basic_materials:brass_block")
technic_cnc/depends.txt
File was deleted
technic_cnc/init.lua
@@ -3,15 +3,11 @@
technic_cnc = {}
technic_cnc.technic_modpath = minetest.get_modpath("technic")
technic_cnc.getter = minetest.get_translator("technic_cnc")
technic_cnc.use_technic = technic_cnc.technic_modpath
                          and minetest.settings:get_bool("technic_cnc_use_technic") ~= false
if rawget(_G, "intllib") then
    technic_cnc.getter = intllib.Getter()
else
    technic_cnc.getter = function(s,a,...)if a==nil then return s end a={a,...}return s:gsub("(@?)@(%(?)(%d+)(%)?)",function(e,o,n,c)if e==""then return a[tonumber(n)]..(o==""and c or"")else return"@"..o..n..c end end) end
end
dofile(modpath.."/cnc.lua")
dofile(modpath.."/cnc_api.lua")
technic_cnc/locale/de.txt
File was deleted
technic_cnc/locale/technic_cnc.de.tr
New file
@@ -0,0 +1,50 @@
# textdomain: technic_cnc
## CNC
##[ cnc.lua ]##
CNC Machine=CNC-Maschine
@1 CNC Machine=@1 CNC-Maschine
LV=LV
Choose Milling Program:=Wähle das Fräsprogramm aus:
Slim Elements half / normal height:=Schmale halb- / normalhohe Elemente:
In:=Eingabe:
Out:=Ausgabe:
@1 Idle=@1 (wartend)
@1 Unpowered=@1 (unbestromt)
@1 Active=@1 (aktiv)
##[ cnc_api.lua ]##
Stick=Stock
Element End Double=Endelement
Element Cross Double=Kreuzelement
Element T Double=T-Element
Element Edge Double=Eckelement
Element Straight Double=Gerades Element
Element End=Halbes Endelement
Element Cross=Halbes Kreuzelement
Element T=Halbes T-Element
Element Edge=Halbes Eckelement
Element Straight=Halbes Gerades Eleent
Oblate spheroid=Sphärenstück
Sphere=Sphäre
Horizontal Cylinder=Horizontaler Zylinder
Cylinder=Zylinder
Two Curved Edge/Corner Block=Doppelt gekrümmter Eck/-Randblock
One Curved Edge Block=Einfach gekrümmter Eckblock
Spike=Spitze
Pyramid=Pyramide
Slope Upside Down Inner Edge/Corner=Schräge (kopfüber), innere Ecke
Slope Upside Down Outer Edge/Corner=Schräge (kopfüber), äussere Ecke
Slope Inner Edge/Corner=Schräge, innere Ecke
Slope Outer Edge/Corner=Schräge äussere Ecke
Slope Upside Down=Schräge (kopfüber)
Slope Lying=Liegende Schräge
Slope=Schräge
##[ cnc_materials.lua ]##
Grassy dirt=Erde mit Gras
Wooden=Holz
Bronze=Bronze
Steel=Stahl
Wrought Iron=Schmiedeeisen
Stainless Steel=Edelstahl
technic_cnc/locale/template.txt
@@ -1,36 +1,50 @@
# textdomain: technic_cnc
## CNC
CNC Machine =
%s CNC Machine =
Cylinder =
Element Cross =
Element Cross Double =
Element Edge =
Element Edge Double =
Element End =
Element End Double =
Element Straight =
Element Straight Double =
Element T =
Element T Double =
Horizontal Cylinder =
One Curved Edge Block =
Pyramid =
Slope =
Slope Edge =
Slope Inner Edge =
Slope Lying =
Slope Upside Down =
Slope Upside Down Edge =
Slope Upside Down Inner Edge =
Sphere =
Spike =
Stick =
Two Curved Edge Block =
Brick =
Cobble =
Dirt =
Leaves =
Sandstone =
Stone =
Tree =
Wooden =
##[ cnc.lua ]##
CNC Machine=
@1 CNC Machine=
LV=
Choose Milling Program:=
Slim Elements half / normal height:=
In:=
Out:=
@1 Idle=
@1 Unpowered=
@1 Active=
##[ cnc_api.lua ]##
Stick=
Element End Double=
Element Cross Double=
Element T Double=
Element Edge Double=
Element Straight Double=
Element End=
Element Cross=
Element T=
Element Edge=
Element Straight=
Oblate spheroid=
Sphere=
Horizontal Cylinder=
Cylinder=
Two Curved Edge/Corner Block=
One Curved Edge Block=
Spike=
Pyramid=
Slope Upside Down Inner Edge/Corner=
Slope Upside Down Outer Edge/Corner=
Slope Inner Edge/Corner=
Slope Outer Edge/Corner=
Slope Upside Down=
Slope Lying=
Slope=
##[ cnc_materials.lua ]##
Grassy dirt=
Wooden=
Bronze=
Steel=
Wrought Iron=
Stainless Steel=
technic_cnc/textures/technic_cnc_slope_edge_upsdown.png

technic_cnc/textures/technic_cnc_slope_inner_edge_upsdown.png

technic_cnc/textures/technic_cnc_slope_upsdown.png

technic_worldgen/depends.txt
File was deleted
technic_worldgen/nodes.lua
@@ -54,6 +54,14 @@
    sounds = default.node_sound_stone_defaults(),
})
minetest.register_node( ":technic:granite_bricks", {
    description = S("Granite Bricks"),
    tiles = { "technic_granite_bricks.png" },
    is_ground_content = false,
    groups = {cracky=1},
    sounds = default.node_sound_stone_defaults(),
})
minetest.register_node( ":technic:marble", {
    description = S("Marble"),
    tiles = { "technic_marble.png" },
@@ -65,7 +73,7 @@
minetest.register_node( ":technic:marble_bricks", {
    description = S("Marble Bricks"),
    tiles = { "technic_marble_bricks.png" },
    is_ground_content = true,
    is_ground_content = false,
    groups = {cracky=3},
    sounds = default.node_sound_stone_defaults(),
})
@@ -134,6 +142,14 @@
})
minetest.register_craft({
    output = 'technic:granite_bricks 4',
    recipe = {
        {'technic:granite','technic:granite'},
        {'technic:granite','technic:granite'}
    }
})
minetest.register_craft({
    output = 'technic:marble_bricks 4',
    recipe = {
        {'technic:marble','technic:marble'},
technic_worldgen/textures/technic_granite_bricks.png
technic_worldgen/textures/technic_stainless_steel_ingot.png

technic_worldgen/textures/x32/technic_brass_ingot.png
Binary files differ
technic_worldgen/textures/x32/technic_chromium_ingot.png
Binary files differ
technic_worldgen/textures/x32/technic_chromium_lump.png
Binary files differ
technic_worldgen/textures/x32/technic_concrete_block.png
Binary files differ
technic_worldgen/textures/x32/technic_granite.png
Binary files differ
technic_worldgen/textures/x32/technic_marble.png
Binary files differ
technic_worldgen/textures/x32/technic_marble_bricks.png
Binary files differ
technic_worldgen/textures/x32/technic_mineral_chromium.png
Binary files differ
technic_worldgen/textures/x32/technic_mineral_uranium.png
Binary files differ
technic_worldgen/textures/x32/technic_mineral_zinc.png
Binary files differ
technic_worldgen/textures/x32/technic_rebar.png
Binary files differ
technic_worldgen/textures/x32/technic_uranium.png
Binary files differ
technic_worldgen/textures/x32/technic_zinc_ingot.png
Binary files differ
technic_worldgen/textures/x32/technic_zinc_lump.png
Binary files differ
wrench/depends.txt
File was deleted
wrench/init.lua
@@ -32,57 +32,63 @@
end
local function restore(pos, placer, itemstack)
    local name = itemstack:get_name()
    local node = minetest.get_node(pos)
    local meta = minetest.get_meta(pos)
    local inv = meta:get_inventory()
    local data = itemstack:get_meta():get_string("data")
    data = (data ~= "" and data) or    itemstack:get_metadata()
    data = minetest.deserialize(data)
    if not data then
        minetest.remove_node(pos)
        minetest.log("error", placer:get_player_name().." wanted to place "..
                name.." at "..minetest.pos_to_string(pos)..
                itemstack:get_name().." at "..minetest.pos_to_string(pos)..
                ", but it had no data.")
        minetest.log("verbose", "itemstack: "..itemstack:to_string())
        return true
    end
    local node = minetest.get_node(pos)
    minetest.set_node(pos, {name = data.name, param2 = node.param2})
    for name, value in pairs(data.metas) do
        local meta_type = get_meta_type(data.name, name)
    -- Apply stored metadata to the current node
    local meta = minetest.get_meta(pos)
    local inv = meta:get_inventory()
    for key, value in pairs(data.metas) do
        local meta_type = get_meta_type(data.name, key)
        if meta_type == wrench.META_TYPE_INT then
            meta:set_int(name, value)
            meta:set_int(key, value)
        elseif meta_type == wrench.META_TYPE_FLOAT then
            meta:set_float(name, value)
            meta:set_float(key, value)
        elseif meta_type == wrench.META_TYPE_STRING then
            meta:set_string(name, value)
            meta:set_string(key, value)
        end
    end
    local lists = data.lists
    for listname, list in pairs(lists) do
    for listname, list in pairs(data.lists) do
        inv:set_list(listname, list)
    end
    itemstack:take_item()
    return itemstack
end
for name, info in pairs(wrench.registered_nodes) do
    local olddef = minetest.registered_nodes[name]
    if olddef then
        local newdef = {}
        for key, value in pairs(olddef) do
            newdef[key] = value
minetest.register_on_mods_loaded(function()
    -- Delayed registration for foreign mod support
    for name, info in pairs(wrench.registered_nodes) do
        local olddef = minetest.registered_nodes[name]
        if olddef then
            local newdef = {}
            for key, value in pairs(olddef) do
                newdef[key] = value
            end
            newdef.stack_max = 1
            newdef.description = S("%s with items"):format(newdef.description)
            newdef.groups = {}
            newdef.groups.not_in_creative_inventory = 1
            newdef.on_construct = nil
            newdef.on_destruct = nil
            newdef.after_place_node = restore
            minetest.register_node(":"..get_pickup_name(name), newdef)
        end
        newdef.stack_max = 1
        newdef.description = S("%s with items"):format(newdef.description)
        newdef.groups = {}
        newdef.groups.not_in_creative_inventory = 1
        newdef.on_construct = nil
        newdef.on_destruct = nil
        newdef.after_place_node = restore
        minetest.register_node(":"..get_pickup_name(name), newdef)
    end
end
end)
minetest.register_tool("wrench:wrench", {
    description = S("Wrench"),
@@ -108,15 +114,15 @@
            minetest.record_protection_violation(pos, player_name)
            return
        end
        local name = minetest.get_node(pos).name
        local def = wrench.registered_nodes[name]
        local node_name = minetest.get_node(pos).name
        local def = wrench.registered_nodes[node_name]
        if not def then
            return
        end
        local stack = ItemStack(get_pickup_name(name))
        local stack_pickup = ItemStack(get_pickup_name(node_name))
        local player_inv = placer:get_inventory()
        if not player_inv:room_for_item("main", stack) then
        if not player_inv:room_for_item("main", stack_pickup) then
            return
        end
        local meta = minetest.get_meta(pos)
@@ -131,10 +137,12 @@
            end
        end
        -- Do the actual pickup:
        local metadata = {}
        metadata.name = name
        metadata.name = node_name
        metadata.version = LATEST_SERIALIZATION_VERSION
        -- Serialize inventory lists + items
        local inv = meta:get_inventory()
        local lists = {}
        for _, listname in pairs(def.lists or {}) do
@@ -146,22 +154,23 @@
        end
        metadata.lists = lists
        local item_meta = stack:get_meta()
        -- Serialize node metadata fields
        local item_meta = stack_pickup:get_meta()
        metadata.metas = {}
        for name, meta_type in pairs(def.metas or {}) do
        for key, meta_type in pairs(def.metas or {}) do
            if meta_type == wrench.META_TYPE_INT then
                metadata.metas[name] = meta:get_int(name)
                metadata.metas[key] = meta:get_int(key)
            elseif meta_type == wrench.META_TYPE_FLOAT then
                metadata.metas[name] = meta:get_float(name)
                metadata.metas[key] = meta:get_float(key)
            elseif meta_type == wrench.META_TYPE_STRING then
                metadata.metas[name] = meta:get_string(name)
                metadata.metas[key] = meta:get_string(key)
            end
        end
        item_meta:set_string("data", minetest.serialize(metadata))
        minetest.remove_node(pos)
        itemstack:add_wear(65535 / 20)
        player_inv:add_item("main", stack)
        player_inv:add_item("main", stack_pickup)
        return itemstack
    end,
})
wrench/locale/ja.txt
New file
@@ -0,0 +1,6 @@
# technic_wrench japanese translation
# technic_wrenchの日本語への翻訳
# by damiemk
Wrench = レンチ
%s with items = アイテム付きレンチ %s