mods that hopefully work for a TechAge-based pack

This commit is contained in:
2026-09-09 06:27:51 -06:00
parent 4e6b08d194
commit c27db2a537
6234 changed files with 375578 additions and 0 deletions
@@ -0,0 +1,562 @@
--[[
TechAge
=======
Copyright (C) 2019-2023 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
The autocrafter is derived from pipeworks:
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> WTFPL
TA2/TA3/TA4 Autocrafter
]]--
-- for lazy programmers
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local S2P = minetest.string_to_pos
local M = minetest.get_meta
-- Consumer Related Data
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end
local S = techage.S
local STANDBY_TICKS = 3
local COUNTDOWN_TICKS = 4
local CYCLE_TIME = 4
local UncraftableItems = {}
-- Add all nodes/items which should not be crafted with the autocrafter
function techage.register_uncraftable_items(item_name)
UncraftableItems[item_name] = true
end
local function formspec(self, pos, nvm)
return "size[8,9.2]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"list[context;recipe;0,0;3,3;]"..
"image[2.9,1;1,1;techage_form_arrow.png]"..
"image[3.8,0;1,1;"..techage.get_power_image(pos, nvm).."]"..
"list[context;output;3.8,1;1,1;]"..
"image_button[3.8,2;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
"tooltip[3.8,2;1,1;"..self:get_state_tooltip(nvm).."]"..
"list[context;src;0,3.2;8,2;]"..
"list[context;dst;5,0;3,3;]"..
"list[current_player;main;0,5.4;8,4;]" ..
"listring[current_player;main]"..
"listring[context;src]" ..
"listring[current_player;main]"..
"listring[context;dst]" ..
"listring[current_player;main]"..
default.get_hotbar_bg(0, 5.4)
end
local function count_index(invlist)
local index = {}
for _, stack in pairs(invlist) do
if not stack:is_empty() then
local stack_name = stack:get_name()
index[stack_name] = (index[stack_name] or 0) + stack:get_count()
end
end
return index
end
local function flush_input_inventory(pos)
local inv = M(pos):get_inventory()
if not inv:is_empty("src") then
for idx = 1, 16 do
local stack = inv:get_stack("src", idx)
if not inv:room_for_item("dst", stack) then
return false
end
inv:add_item("dst", stack)
inv:set_stack("src", idx, nil)
end
end
return true
end
-- caches some recipe data
local autocrafterCache = {}
local function get_craft(pos, inventory, hash)
hash = hash or minetest.hash_node_position(pos)
local craft = autocrafterCache[hash]
if not craft then
local recipe = inventory:get_list("recipe")
local output, decremented_input = minetest.get_craft_result(
{method = "normal", width = 3, items = recipe})
-- check if registered item
if UncraftableItems[output.item:get_name()] then
output.item = ItemStack()
end
craft = {recipe = recipe, consumption = count_index(recipe),
output = output, decremented_input = decremented_input}
autocrafterCache[hash] = craft
end
return craft
end
local function autocraft(pos, crd, nvm, inv)
local craft = get_craft(pos, inv)
if not craft then
crd.State:idle(pos, nvm)
return
end
local output_item = craft.output.item
if output_item:get_name() == "" then
crd.State:idle(pos, nvm)
return
end
-- check if we have enough room in dst
if not inv:room_for_item("dst", output_item) then
crd.State:blocked(pos, nvm)
return
end
local consumption = craft.consumption
local inv_index = count_index(inv:get_list("src"))
-- check if we have enough material available
for itemname, number in pairs(consumption) do
if (not inv_index[itemname]) or inv_index[itemname] < number then
crd.State:idle(pos, nvm)
return
end
end
-- consume material
for itemname, number in pairs(consumption) do
for i = 1, number do -- We have to do that since remove_item does not work if count > stack_max
inv:remove_item("src", ItemStack(itemname))
end
end
-- craft the result into the dst inventory and add any "replacements" as well
inv:add_item("dst", output_item)
for i = 1, 9 do
inv:add_item("dst", craft.decremented_input.items[i])
end
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
end
local function keep_running(pos, elapsed)
local nvm = techage.get_nvm(pos)
local crd = CRD(pos)
local inv = M(pos):get_inventory()
autocraft(pos, crd, nvm, inv)
end
-- note, that this function assumes allready being updated to virtual items
-- and doesn't handle recipes with stacksizes > 1
local function after_recipe_change(pos, inventory)
local nvm = techage.get_nvm(pos)
local crd = CRD(pos)
-- if we emptied the grid, there's no point in keeping it running or cached
if inventory:is_empty("recipe") then
autocrafterCache[minetest.hash_node_position(pos)] = nil
inventory:set_stack("output", 1, "")
crd.State:stop(pos, nvm)
return
end
local recipe = inventory:get_list("recipe")
local hash = minetest.hash_node_position(pos)
local craft = autocrafterCache[hash]
if craft then
-- check if it changed
local cached_recipe = craft.recipe
for i = 1, 9 do
if recipe[i]:get_name() ~= cached_recipe[i]:get_name() then
autocrafterCache[hash] = nil -- invalidate recipe
craft = nil
break
end
end
end
craft = craft or get_craft(pos, inventory, hash)
local output_item = craft.output.item
inventory:set_stack("output", 1, output_item)
crd.State:stop(pos, nvm)
end
-- clean out unknown items and groups, which would be handled like unknown items in the crafting grid
-- if minetest supports query by group one day, this might replace them
-- with a canonical version instead
local function normalize(item_list)
for i = 1, #item_list do
local name = item_list[i]
if not minetest.registered_items[name] then
item_list[i] = ""
end
end
return item_list
end
local function get_input_from_recipeblock(pos, number, idx)
local own_num = M(pos):get_string("node_number")
local owner = M(pos):get_string("owner")
if techage.check_numbers(number, owner) then
local input = techage.send_single(own_num, number, "input", idx)
if input and type(input) == "string" then
return input
end
end
end
local function on_output_change(pos, inventory, stack)
if not stack then
inventory:set_list("output", {})
inventory:set_list("recipe", {})
else
local input = minetest.get_craft_recipe(stack:get_name())
if not input.items or input.type ~= "normal" then return end
local items, width = normalize(input.items), input.width
local item_idx, width_idx = 1, 1
for i = 1, 9 do
if width_idx <= width then
inventory:set_stack("recipe", i, items[item_idx])
item_idx = item_idx + 1
else
inventory:set_stack("recipe", i, ItemStack(""))
end
width_idx = (width_idx < 3) and (width_idx + 1) or 1
end
-- we'll set the output slot in after_recipe_change to the actual result of the new recipe
end
after_recipe_change(pos, inventory)
end
local function determine_recipe_items(pos, input)
local num, idx
if input and type(input) == "string" then -- Lua controller
-- Test if "<node-number>.<recipe-number>"
if string.find(input, "%d+%.%d+") then
num, idx = unpack(string.split(input, ".", false, 1))
elseif input and type(input) == "table" then -- Beduino
if input[3] == 0 then -- TA3 Terminal Basic
num = tostring(input[1])
idx = tostring(input[2])
else -- 16 bit Beduino controller
num = tostring(input[1] * 65536 + input[2])
idx = tostring(input[3])
end
end
if num and idx then
input = get_input_from_recipeblock(pos, num, idx)
end
end
if input and type(input) == "string" then
-- "<item>,<item>,..." input
local items = string.split(input, ",", true, 8)
if items and type(items) == "table" and next(items) then
return items
end
end
end
local function on_new_recipe(pos, input)
local items = determine_recipe_items(pos, input)
local inv = M(pos):get_inventory()
if items then
for i = 1, 9 do
inv:set_stack("recipe", i, items[i])
end
else
inv:set_list("recipe", {})
end
local hash = minetest.hash_node_position(pos)
autocrafterCache[hash] = nil
local craft = get_craft(pos, inv, hash)
if craft.output and craft.output.item then
inv:set_stack("output", 1, craft.output.item)
else
inv:set_stack("output", 1, nil)
end
end
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
if listname == "output" then
return 0
end
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
local inv = M(pos):get_inventory()
if listname == "recipe" then
stack:set_count(1)
inv:set_stack(listname, index, stack)
after_recipe_change(pos, inv)
return 0
elseif listname == "output" then
on_output_change(pos, inv, stack)
return 0
elseif listname == "src" then
CRD(pos).State:start_if_standby(pos)
end
return stack:get_count()
end
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
if listname == "output" then
return 0
end
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
-- upgrade_autocrafter(pos)
local inv = minetest.get_meta(pos):get_inventory()
if listname == "recipe" then
inv:set_stack(listname, index, ItemStack(""))
after_recipe_change(pos, inv)
return 0
elseif listname == "output" then
on_output_change(pos, inv, nil)
return 0
end
return stack:get_count()
end
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
if from_list == "output" or "to_list" == "output" then
return 0
end
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
local inv = minetest.get_meta(pos):get_inventory()
local stack = inv:get_stack(from_list, from_index)
if to_list == "output" then
on_output_change(pos, inv, stack)
return 0
elseif from_list == "output" then
on_output_change(pos, inv, nil)
if to_list ~= "recipe" then
return 0
end -- else fall through to recipe list handling
end
if from_list == "recipe" or to_list == "recipe" then
if from_list == "recipe" then
inv:set_stack(from_list, from_index, ItemStack(""))
end
if to_list == "recipe" then
stack:set_count(1)
inv:set_stack(to_list, to_index, stack)
end
after_recipe_change(pos, inv)
return 0
end
return count
end
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local nvm = techage.get_nvm(pos)
CRD(pos).State:state_button_event(pos, nvm, fields)
end
local function can_dig(pos, player)
if minetest.is_protected(pos, player:get_player_name()) then
return false
end
local inv = M(pos):get_inventory()
return inv:is_empty("dst") and inv:is_empty("src")
end
local tiles = {}
-- '#' will be replaced by the stage number
-- '{power}' will be replaced by the power PNG
tiles.pas = {
-- up, down, right, left, back, front
"techage_filling_ta#.png^techage_appl_autocrafter.png^techage_frame_ta#_top.png",
"techage_filling_ta#.png^techage_frame_ta#_bottom.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
"techage_filling_ta#.png^techage_appl_autocrafter.png^techage_frame_ta#.png",
"techage_filling_ta#.png^techage_appl_autocrafter.png^techage_frame_ta#.png",
}
tiles.act = {
-- up, down, right, left, back, front
{
name = "techage_filling4_ta#.png^techage_appl_autocrafter4.png^techage_frame4_ta#_top.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 0.5,
},
},
"techage_filling_ta#.png^techage_frame_ta#_bottom.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
{
name = "techage_filling4_ta#.png^techage_appl_autocrafter4.png^techage_frame4_ta#.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 0.5,
},
},
{
name = "techage_filling4_ta#.png^techage_appl_autocrafter4.png^techage_frame4_ta#.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 0.5,
},
},
}
local INFO = [[Commands: 'state', 'recipe']]
local tubing = {
on_inv_request = function(pos, in_dir, access_type)
if access_type == "push" then
local meta = minetest.get_meta(pos)
if meta:get_int("push_dir") == in_dir or in_dir == 5 then
return meta:get_inventory(), "src"
end
end
end,
on_pull_item = function(pos, in_dir, num)
local meta = minetest.get_meta(pos)
if meta:get_int("pull_dir") == in_dir then
local inv = M(pos):get_inventory()
return techage.get_items(pos, inv, "dst", num)
end
end,
on_push_item = function(pos, in_dir, stack, idx)
local meta = minetest.get_meta(pos)
if meta:get_int("push_dir") == in_dir or in_dir == 5 then
local inv = M(pos):get_inventory()
--CRD(pos).State:start_if_standby(pos) -- would need power!
return techage.put_items(inv, "src", stack, idx)
end
end,
on_unpull_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
if meta:get_int("pull_dir") == in_dir then
local inv = M(pos):get_inventory()
return techage.put_items(inv, "dst", stack)
end
end,
on_recv_message = function(pos, src, topic, payload)
if topic == "recipe" and CRD(pos).stage == 4 then
if payload and payload ~= "" then
on_new_recipe(pos, payload)
return true
else
local inv = M(pos):get_inventory()
return inv:get_stack("output", 1):get_name()
end
elseif topic == "flush" and CRD(pos).stage == 4 then
return flush_input_inventory(pos)
elseif topic == "info" and CRD(pos).stage == 4 then
return INFO
else
return CRD(pos).State:on_receive_message(pos, topic, payload)
end
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
if topic == 10 and CRD(pos).stage == 4 then
on_new_recipe(pos, payload)
return 1, ""
elseif topic == 11 and CRD(pos).stage == 4 then
if flush_input_inventory(pos) then
return 1, ""
else
return 0, ""
end
end
return CRD(pos).State:on_beduino_receive_cmnd(pos, topic, payload)
end,
on_beduino_request_data = function(pos, src, topic, payload)
return CRD(pos).State:on_beduino_request_data(pos, topic, payload)
end,
on_node_load = function(pos)
CRD(pos).State:on_node_load(pos)
end,
}
local node_name_ta2, node_name_ta3, node_name_ta4 =
techage.register_consumer("autocrafter", S("Autocrafter"), tiles, {
drawtype = "normal",
cycle_time = CYCLE_TIME,
standby_ticks = STANDBY_TICKS,
formspec = formspec,
tubing = tubing,
after_place_node = function(pos, placer)
local inv = M(pos):get_inventory()
inv:set_size("src", 2*8)
inv:set_size("recipe", 3*3)
inv:set_size("dst", 3*3)
inv:set_size("output", 1)
end,
can_dig = can_dig,
node_timer = keep_running,
on_receive_fields = on_receive_fields,
allow_metadata_inventory_put = allow_metadata_inventory_put,
allow_metadata_inventory_move = allow_metadata_inventory_move,
allow_metadata_inventory_take = allow_metadata_inventory_take,
groups = {choppy=2, cracky=2, crumbly=2},
sounds = default.node_sound_wood_defaults(),
num_items = {0,1,2,4},
power_consumption = {0,4,6,9},
},
{false, true, true, true}) -- TA2/TA3/TA4
minetest.register_craft({
output = node_name_ta2,
recipe = {
{"group:wood", "default:diamond", "group:wood"},
{"techage:tubeS", "basic_materials:gear_steel", "techage:tubeS"},
{"group:wood", "techage:iron_ingot", "group:wood"},
},
})
minetest.register_craft({
output = node_name_ta3,
recipe = {
{"", "default:diamond", ""},
{"", node_name_ta2, ""},
{"", "techage:vacuum_tube", ""},
},
})
minetest.register_craft({
output = node_name_ta4,
recipe = {
{"", "default:diamond", ""},
{"", node_name_ta3, ""},
{"", "techage:ta4_wlanchip", ""},
},
})
local Cable = techage.ElectricCable
local power = networks.power
techage.register_node_for_v1_transition({"techage:ta3_autocrafter_pas", "techage:ta4_autocrafter_pas"}, function(pos, node)
power.update_network(pos, nil, Cable)
end)
@@ -0,0 +1,89 @@
--[[
TechAge
=======
Copyright (C) 2019 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
All items and liquids disappear.
]]--
local S = techage.S
local Pipe = techage.LiquidPipe
local liquid = networks.liquid
local function take_liquid(pos, indir, name, amount)
return 0, name
end
local function put_liquid(pos, indir, name, amount)
return 0
end
local function peek_liquid(pos, indir)
return nil
end
minetest.register_node("techage:blackhole", {
description = S("TechAge Black Hole"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta2.png^techage_frame_ta2.png",
"techage_filling_ta2.png^techage_frame_ta2_bottom.png",
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_blackhole.png^techage_appl_hole_pipe.png",
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_blackhole.png^techage_appl_inp.png",
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_blackhole.png",
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_blackhole.png",
},
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos)
local node = minetest.get_node(pos)
meta:set_int("push_dir", techage.side_to_indir("L", node.param2))
meta:set_string("infotext", S("TechAge Black Hole (let items and liquids disappear)"))
Pipe:after_place_node(pos)
end,
after_dig_node = function(pos, oldnode)
Pipe:after_dig_node(pos)
end,
on_rotate = screwdriver.disallow,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
})
minetest.register_craft({
output = "techage:blackhole",
recipe = {
{"group:wood", "", "group:wood"},
{"techage:tubeS", "default:coal_lump", "techage:ta3_pipeS"},
{"group:wood", "techage:iron_ingot", "group:wood"},
},
})
techage.register_node({"techage:blackhole"}, {
on_pull_item = nil, -- not needed
on_unpull_item = nil, -- not needed
on_push_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
if meta:get_int("push_dir") == in_dir then
return true
end
end,
})
liquid.register_nodes({"techage:blackhole"},
Pipe, "tank", {"R"}, {
capa = 9999999,
peek = peek_liquid,
put = put_liquid,
take = take_liquid,
}
)
@@ -0,0 +1,479 @@
--[[
TechAge
=======
Copyright (C) 2019-2022 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA2/TA3/TA4 Chest
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local TA4_INV_SIZE = 50
local MP = minetest.get_modpath(minetest.get_current_modname())
local mConf = dofile(MP.."/basis/conf_inv.lua")
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return stack:get_count()
end
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return stack:get_count()
end
local function can_dig(pos, player)
if minetest.is_protected(pos, player:get_player_name()) then
return false
end
local inv = minetest.get_meta(pos):get_inventory()
return inv:is_empty("main")
end
local function after_dig_node(pos, oldnode, oldmetadata, digger)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end
local function formspec2()
return "size[9,8]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"list[context;main;0.5,0;8,4;]"..
"list[current_player;main;0.5,4.3;8,4;]"..
"listring[context;main]"..
"listring[current_player;main]"
end
minetest.register_node("techage:chest_ta2", {
description = S("TA2 Protected Chest"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta2.png^techage_frame_ta2.png",
"techage_filling_ta2.png^techage_frame_ta2_bottom.png",
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_chest_back_ta3.png",
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_chest_back_ta3.png",
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_chest_back_ta3.png",
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_chest_front_ta3.png",
},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
inv:set_size('main', 32)
end,
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos)
meta:set_string("owner", placer:get_player_name())
meta:set_string("formspec", formspec2())
end,
techage_set_numbers = function(pos, numbers, player_name)
return techage.logic.set_numbers(pos, numbers, player_name, S("TA2 Protected Chest"))
end,
can_dig = can_dig,
after_dig_node = after_dig_node,
allow_metadata_inventory_put = allow_metadata_inventory_put,
allow_metadata_inventory_take = allow_metadata_inventory_take,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
})
local function formspec3()
return "size[10,8]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"list[context;main;0,0;10,4;]"..
"list[current_player;main;1,4.3;8,4;]"..
"listring[context;main]"..
"listring[current_player;main]"
end
minetest.register_node("techage:chest_ta3", {
description = S("TA3 Protected Chest"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3.png",
"techage_filling_ta3.png^techage_frame_ta3_bottom.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_chest_back_ta3.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_chest_back_ta3.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_chest_back_ta3.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_chest_front_ta3.png",
},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
inv:set_size('main', 40)
end,
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos)
local number = techage.add_node(pos, "techage:chest_ta3")
meta:set_string("node_number", number)
meta:set_string("owner", placer:get_player_name())
meta:set_string("formspec", formspec3())
meta:set_string("infotext", S("TA3 Protected Chest").." "..number)
end,
techage_set_numbers = function(pos, numbers, player_name)
return techage.logic.set_numbers(pos, numbers, player_name, S("TA3 Protected Chest"))
end,
can_dig = can_dig,
after_dig_node = after_dig_node,
allow_metadata_inventory_put = allow_metadata_inventory_put,
allow_metadata_inventory_take = allow_metadata_inventory_take,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
})
techage.register_node({"techage:chest_ta2", "techage:chest_ta3"}, {
on_inv_request = function(pos, in_dir, access_type)
local meta = minetest.get_meta(pos)
return meta:get_inventory(), "main"
end,
on_pull_item = function(pos, in_dir, num, item_name)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.get_items(pos, inv, "main", num)
end,
on_push_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.put_items(inv, "main", stack)
end,
on_unpull_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.put_items(inv, "main", stack)
end,
on_recv_message = function(pos, src, topic, payload)
if topic == "state" then
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.get_inv_state(inv, "main")
elseif topic == "count" then
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.check_inv_item(inv, "main", payload)
else
return "unsupported"
end
end,
on_beduino_request_data = function(pos, src, topic, payload)
if topic == 131 then
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return 0, {techage.get_inv_state_num(inv, "main")}
elseif topic == 192 and payload and type(payload) == "string" then
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return 0, {techage.check_inv_item(inv, "main", payload)}
else
return 2, ""
end
end,
})
local function formspec4(pos)
return "size[10,9]"..
"tabheader[0,0;tab;"..S("Inventory,Pre-Assignment,Config")..";1;;true]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"list[context;main;0,0;10,5;]"..
mConf.preassigned_stacks(pos, 10, 5)..
"list[current_player;main;1,5.3;8,4;]"..
"listring[context;main]"..
"listring[current_player;main]"
end
local function formspec4_pre(pos)
return "size[10,9]"..
"tabheader[0,0;tab;"..S("Inventory,Pre-Assignment,Config")..";2;;true]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"list[context;conf;0,0;10,5;]"..
"list[current_player;main;1,5.3;8,4;]"..
"listring[context;conf]"..
"listring[current_player;main]"
end
local function formspec4_cfg(pos)
local meta = minetest.get_meta(pos)
local label = meta:get_string("label") or ""
local public = dump((meta:get_int("public") or 0) == 1)
return "size[10,5]"..
"tabheader[0,0;tab;"..S("Inventory,Pre-Assignment,Config")..";3;;true]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"field[0.5,1;9,1;label;"..S("Node label:")..";"..label.."]" ..
"checkbox[1,2;public;"..S("Allow public access to the chest")..";"..public.."]"..
"button_exit[3.5,4;3,1;exit;"..S("Save").."]"
end
local function ta4_allow_metadata_inventory_put(pos, listname, index, stack, player)
local public = M(pos):get_int("public") == 1
if not public and minetest.is_protected(pos, player:get_player_name()) then
return 0
end
if listname == "main" then
return stack:get_count()
else
return mConf.allow_conf_inv_put(pos, listname, index, stack, player)
end
end
local function ta4_allow_metadata_inventory_take(pos, listname, index, stack, player)
local public = M(pos):get_int("public") == 1
if not public and minetest.is_protected(pos, player:get_player_name()) then
return 0
end
if listname == "main" then
return stack:get_count()
else
return mConf.allow_conf_inv_take(pos, listname, index, stack, player)
end
end
local function ta4_allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
local public = M(pos):get_int("public") == 1
if not public and minetest.is_protected(pos, player:get_player_name()) then
return 0
end
if from_list == "main" then
return count
else
return mConf.allow_conf_inv_move(pos, from_list, from_index, to_list, to_index, count, player)
end
end
minetest.register_node("techage:chest_ta4", {
description = S("TA4 Protected Chest"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta4.png^techage_frame_ta4_top.png",
"techage_filling_ta4.png^techage_frame_ta4_bottom.png",
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_back_ta4.png",
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_back_ta4.png",
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_back_ta4.png",
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_front_ta4.png",
},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
inv:set_size('main', 50)
inv:set_size('conf', 50)
end,
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos)
local number = techage.add_node(pos, "techage:chest_ta4")
meta:set_string("node_number", number)
meta:set_string("owner", placer:get_player_name())
meta:set_string("formspec", formspec4(pos))
meta:set_string("infotext", S("TA4 Protected Chest").." "..number)
end,
on_receive_fields = function(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local meta = minetest.get_meta(pos)
local mem = techage.get_mem(pos)
if fields.tab == "1" then
mem.filter = nil
meta:set_string("formspec", formspec4(pos))
elseif fields.tab == "2" then
meta:set_string("formspec", formspec4_pre(pos))
elseif fields.tab == "3" then
meta:set_string("formspec", formspec4_cfg(pos))
elseif fields.quit == "true" then
mem.filter = nil
end
if fields.public then
meta:set_int("public", fields.public == "true" and 1 or 0)
end
if fields.exit then
local number = meta:get_string("node_number")
if fields.label ~= "" then
meta:set_string("infotext", minetest.formspec_escape(fields.label).." #"..number)
else
meta:set_string("infotext", S("TA4 Protected Chest").." "..number)
end
meta:set_string("label", fields.label)
meta:set_string("formspec", formspec4_cfg(pos))
end
end,
techage_set_numbers = function(pos, numbers, player_name)
return techage.logic.set_numbers(pos, numbers, player_name, S("TA4 Protected Chest"))
end,
can_dig = can_dig,
after_dig_node = after_dig_node,
allow_metadata_inventory_put = ta4_allow_metadata_inventory_put,
allow_metadata_inventory_take = ta4_allow_metadata_inventory_take,
allow_metadata_inventory_move = ta4_allow_metadata_inventory_move,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
})
techage.register_node({"techage:chest_ta4"}, {
on_inv_request = function(pos, in_dir, access_type)
local meta = minetest.get_meta(pos)
return meta:get_inventory(), "main"
end,
on_pull_item = function(pos, in_dir, num, item_name)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local mem = techage.get_mem(pos)
mem.filter = mem.filter or mConf.item_filter(pos, TA4_INV_SIZE)
mem.chest_configured = mem.chest_configured or not inv:is_empty("conf")
if inv:is_empty("main") then
return nil
end
if item_name then
if mem.filter[item_name] or not mem.chest_configured then
local taken = inv:remove_item("main", {name = item_name, count = num})
if taken:get_count() > 0 then
return taken
end
end
else -- no item given
if mem.chest_configured then
return mConf.take_item(pos, inv, "main", num, mem.filter["unconfigured"])
else
return techage.get_items(pos, inv, "main", num)
end
end
end,
on_push_item = function(pos, in_dir, item, idx)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local mem = techage.get_mem(pos)
mem.filter = mem.filter or mConf.item_filter(pos, TA4_INV_SIZE)
mem.chest_configured = mem.chest_configured or not inv:is_empty("conf")
if mem.chest_configured then
local name = item:get_name()
local stacks = mem.filter[name] or mem.filter["unconfigured"]
return mConf.put_items(pos, inv, "main", item, stacks, idx)
else
return techage.put_items(inv, "main", item, idx)
end
end,
on_unpull_item = function(pos, in_dir, item)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local mem = techage.get_mem(pos)
mem.filter = mem.filter or mConf.item_filter(pos, TA4_INV_SIZE)
mem.chest_configured = mem.chest_configured or not inv:is_empty("conf")
if mem.chest_configured then
local name = item:get_name()
local stacks = mem.filter[name] or mem.filter["unconfigured"]
return mConf.put_items(pos, inv, "main", item, stacks)
else
return techage.put_items(inv, "main", item)
end
end,
on_recv_message = function(pos, src, topic, payload)
if topic == "state" then
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.get_inv_state(inv, "main")
elseif topic == "count" then
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.check_inv_item(inv, "main", payload)
else
return "unsupported"
end
end,
on_beduino_request_data = function(pos, src, topic, payload)
if topic == 131 then
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return 0, {techage.get_inv_state_num(inv, "main")}
elseif topic == 192 and payload and type(payload) == "string" then
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return 0, {techage.check_inv_item(inv, "main", payload)}
else
return 2, ""
end
end,
})
minetest.register_craft({
type = "shapeless",
output = "techage:chest_ta2",
recipe = {"default:chest", "techage:tubeS", "techage:iron_ingot"}
})
minetest.register_craft({
type = "shapeless",
output = "techage:chest_ta2",
recipe = {"default:chest_locked", "techage:tubeS", "techage:iron_ingot"}
})
minetest.register_craft({
type = "shapeless",
output = "techage:chest_ta2",
recipe = {"protector:chest", "techage:tubeS", "techage:iron_ingot"}
})
minetest.register_craft({
type = "shapeless",
output = "techage:chest_ta3",
recipe = {"techage:chest_ta2", "default:chest"}
})
minetest.register_craft({
type = "shapeless",
output = "techage:chest_ta4",
recipe = {"techage:chest_ta3", "default:chest"}
})
@@ -0,0 +1,154 @@
--[[
TechAge
=======
Copyright (C) 2019-2022 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
Tube Concentrator
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local Tube = techage.Tube
local size = 2/8
local Boxes = {
{{-size, -size, size, size, size, 0.5 }}, -- z+
{{-size, -size, -size, 0.5, size, size}}, -- x+
{{-size, -size, -0.5, size, size, size}}, -- z-
{{-0.5, -size, -size, size, size, size}}, -- x-
{{-size, -0.5, -size, size, size, size}}, -- y-
{{-size, -size, -size, size, 0.5, size}}, -- y+
}
local names = networks.register_junction("techage:concentrator", 2/8, Boxes, Tube, {
description = S("Tube Concentrator"),
tiles = {
"techage_tube_junction.png^techage_appl_arrow2.png^[transformR270",
"techage_tube_junction.png^techage_appl_arrow2.png^[transformR270",
"techage_tube_junction.png^techage_tube_hole.png",
"techage_tube_junction.png",
"techage_tube_junction.png^techage_appl_arrow2.png^[transformR90",
"techage_tube_junction.png^techage_appl_arrow2.png^[transformR270",
},
paramtype2 = "facedir", -- important!
use_texture_alpha = techage.CLIP,
groups = {snappy = 2, choppy = 2, oddly_breakable_by_hand = 3, techage_trowel = 1},
sounds = default.node_sound_defaults(),
after_place_node = function(pos, placer, itemstack, pointed_thing)
local node = minetest.get_node(pos)
local name = "techage:concentrator"..networks.junction_type(pos, Tube, "R", node.param2)
minetest.swap_node(pos, {name = name, param2 = node.param2})
M(pos):set_int("push_dir", techage.side_to_outdir("R", node.param2))
Tube:after_place_node(pos)
end,
tubelib2_on_update2 = function(pos, dir1, tlib2, node)
local name = "techage:concentrator"..networks.junction_type(pos, Tube, "R", node.param2)
minetest.swap_node(pos, {name = name, param2 = node.param2})
end,
ta_rotate_node = function(pos, node, new_param2)
Tube:after_dig_node(pos)
minetest.swap_node(pos, {name = node.name, param2 = new_param2})
Tube:after_place_node(pos)
M(pos):set_int("push_dir", techage.side_to_outdir("R", new_param2))
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
Tube:after_dig_node(pos)
end,
}, 27)
for _, name in ipairs(names) do
Tube:set_valid_sides(name, {"B", "R", "F", "L", "D", "U"})
end
techage.register_node(names, {
on_push_item = function(pos, in_dir, stack)
local push_dir = M(pos):get_int("push_dir")
if networks.Flip[push_dir] ~= in_dir then
return techage.safe_push_items(pos, push_dir, stack)
else
return stack
end
end,
is_pusher = true, -- is a pulling/pushing node
})
names = networks.register_junction("techage:ta4_concentrator", 2/8, Boxes, Tube, {
description = S("TA4 Tube Concentrator"),
tiles = {
"techage_tubeta4_junction.png^techage_appl_arrow2.png^[transformR270",
"techage_tubeta4_junction.png^techage_appl_arrow2.png^[transformR270",
"techage_tubeta4_junction.png^techage_tube_hole.png",
"techage_tubeta4_junction.png",
"techage_tubeta4_junction.png^techage_appl_arrow2.png^[transformR90",
"techage_tubeta4_junction.png^techage_appl_arrow2.png^[transformR270",
},
paramtype2 = "facedir", -- important!
use_texture_alpha = techage.CLIP,
groups = {snappy = 2, choppy = 2, oddly_breakable_by_hand = 3, techage_trowel = 1},
sounds = default.node_sound_defaults(),
after_place_node = function(pos, placer, itemstack, pointed_thing)
local node = minetest.get_node(pos)
local name = "techage:ta4_concentrator"..networks.junction_type(pos, Tube, "R", node.param2)
minetest.swap_node(pos, {name = name, param2 = node.param2})
M(pos):set_int("push_dir", techage.side_to_outdir("R", node.param2))
Tube:after_place_node(pos)
end,
tubelib2_on_update2 = function(pos, dir1, tlib2, node)
local name = "techage:ta4_concentrator"..networks.junction_type(pos, Tube, "R", node.param2)
minetest.swap_node(pos, {name = name, param2 = node.param2})
end,
ta_rotate_node = function(pos, node, new_param2)
Tube:after_dig_node(pos)
minetest.swap_node(pos, {name = node.name, param2 = new_param2})
Tube:after_place_node(pos)
M(pos):set_int("push_dir", techage.side_to_outdir("R", new_param2))
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
Tube:after_dig_node(pos)
end,
}, 27)
for _, name in ipairs(names) do
Tube:set_valid_sides(name, {"B", "R", "F", "L", "D", "U"})
end
techage.register_node(names, {
on_push_item = function(pos, in_dir, stack)
local push_dir = M(pos):get_int("push_dir")
if networks.Flip[push_dir] ~= in_dir then
return techage.safe_push_items(pos, push_dir, stack)
else
return stack
end
end,
is_pusher = true, -- is a pulling/pushing node
})
minetest.register_craft({
output = "techage:concentrator27",
recipe = {
{"", "techage:tubeS", ""},
{"techage:tubeS", "", "techage:tubeS"},
{"", "techage:tubeS", ""},
},
})
minetest.register_craft({
output = "techage:ta4_concentrator27",
recipe = {
{"", "techage:ta4_tubeS", ""},
{"techage:ta4_tubeS", "", "techage:ta4_tubeS"},
{"", "techage:ta4_tubeS", ""},
},
})
@@ -0,0 +1,314 @@
--[[
TechAge
=======
Copyright (C) 2019-2022 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
Consumer node basis functionality.
It handles:
- up to 4 stages of nodes (TA2/TA3/TA4/TA5)
- power consumption
- node state handling
- registration of passive and active nodes
- Tube connections are on left and right side (from left to right)
- Power connection are on front and back side (front or back)
]]--
-- for lazy programmers
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local P = minetest.string_to_pos
local M = minetest.get_meta
-- Consumer Related Data
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end
local CRDN = function(node) return (minetest.registered_nodes[node.name] or {}).consumer end
local Tube = techage.Tube
local power = networks.power
local liquid = networks.liquid
local CYCLE_TIME = 2
local function get_keys(tbl)
local keys = {}
for k,v in pairs(tbl) do
keys[#keys + 1] = k
end
return keys
end
local function has_power(pos, nvm, state)
local crd = CRD(pos)
return power.power_available(pos, crd.power_netw)
end
local function start_node(pos, nvm, state)
end
local function stop_node(pos, nvm, state)
end
local function node_timer_pas(pos, elapsed)
local crd = CRD(pos)
local nvm = techage.get_nvm(pos)
-- handle power consumption
if crd.power_netw and techage.needs_power(nvm) then
local consumed = power.consume_power(pos, crd.power_netw, nil, crd.power_consumption)
if consumed == crd.power_consumption then
crd.State:start(pos, nvm)
end
end
-- call the node timer routine
if techage.is_operational(nvm) then
nvm.node_timer_call_cyle = (nvm.node_timer_call_cyle or 0) + 1
if nvm.node_timer_call_cyle >= crd.call_cycle then
crd.node_timer(pos, crd.cycle_time)
nvm.node_timer_call_cyle = 0
end
end
return crd.State:is_active(nvm)
end
local function node_timer_act(pos, elapsed)
local crd = CRD(pos)
local nvm = techage.get_nvm(pos)
-- handle power consumption
if crd.power_netw and techage.needs_power(nvm) then
local consumed = power.consume_power(pos, crd.power_netw, nil, crd.power_consumption)
if consumed < crd.power_consumption then
crd.State:nopower(pos, nvm)
end
end
-- call the node timer routine
if techage.is_operational(nvm) then
nvm.node_timer_call_cyle = (nvm.node_timer_call_cyle or 0) + 1
if nvm.node_timer_call_cyle >= crd.call_cycle then
crd.node_timer(pos, crd.cycle_time)
nvm.node_timer_call_cyle = 0
end
end
return crd.State:is_active(nvm)
end
local function prepare_tiles(tiles, stage, power_png)
local tbl = {}
for _,item in ipairs(tiles) do
if type(item) == "string" then
tbl[#tbl+1] = item:gsub("#", stage):gsub("{power}", power_png):gsub("@@", '#')
else
local temp = table.copy(item)
temp.name = temp.name:gsub("#", stage):gsub("{power}", power_png):gsub("@@", '#')
tbl[#tbl+1] = temp
end
end
return tbl
end
-- 'validStates' is optional and can be used to e.g. enable
-- only one TA2 node {false, true, false, false}
function techage.register_consumer(base_name, inv_name, tiles, tNode, validStates, node_name_prefix, inv_name_prefix)
local names = {}
validStates = validStates or {true, true, true, true}
node_name_prefix = node_name_prefix or "techage:ta"
if inv_name_prefix then
inv_name_prefix = inv_name_prefix.." "
else
inv_name_prefix = ""
end
for stage = 2,5 do
local name_pas = node_name_prefix..stage.."_"..base_name.."_pas"
local name_act = node_name_prefix..stage.."_"..base_name.."_act"
local name_inv = inv_name_prefix.."TA"..stage.." "..inv_name
names[#names+1] = name_pas
if validStates[stage] then
local power_network
local power_png = 'techage_axle_clutch.png'
local power_used = tNode.power_consumption ~= nil
local sides
-- power needed?
if power_used then
if stage > 2 then
power_network = techage.ElectricCable
power_png = 'techage_appl_hole_electric.png'
sides = get_keys(tNode.power_sides or {F=1, B=1, U=1, D=1})
else
power_network = techage.Axle
power_png = 'techage_axle_clutch.png'
sides = get_keys(tNode.power_sides or {F=1, B=1, U=1, D=1})
end
end
local tState = techage.NodeStates:new({
node_name_passive = name_pas,
node_name_active = name_act,
infotext_name = name_inv,
cycle_time = CYCLE_TIME,
standby_ticks = tNode.standby_ticks,
formspec_func = tNode.formspec,
on_state_change = tNode.on_state_change,
can_start = tNode.can_start,
quick_start = tNode.quick_start,
has_power = tNode.has_power or power_used and has_power or nil,
start_node = power_used and start_node or nil,
stop_node = power_used and stop_node or nil,
})
local tConsumer = {
stage = stage,
State = tState,
-- number of items to be processed per cycle
num_items = tNode.num_items and tNode.num_items[stage],
power_consumption = power_used and
tNode.power_consumption[stage] or 0,
node_timer = tNode.node_timer,
cycle_time = tNode.cycle_time,
call_cycle = tNode.cycle_time / 2,
power_netw = power_network,
}
local after_place_node = function(pos, placer, itemstack, pointed_thing)
local crd = CRD(pos)
local meta = M(pos)
local nvm = techage.get_nvm(pos)
local node = minetest.get_node(pos)
meta:set_int("push_dir", techage.side_to_indir("L", node.param2))
meta:set_int("pull_dir", techage.side_to_indir("R", node.param2))
meta:set_string("owner", placer:get_player_name())
-- Delete existing node number. Needed for Digtron compatibility.
if (meta:contains("node_number")) then
meta:set_string("node_number", "")
end
local number = techage.add_node(pos, name_pas, stage == 2)
if crd.power_netw then
crd.power_netw:after_place_node(pos)
end
if tNode.after_place_node then
tNode.after_place_node(pos, placer, itemstack, pointed_thing)
end
crd.State:node_init(pos, nvm, number)
end
local after_dig_node = function(pos, oldnode, oldmetadata, digger)
if tNode.after_dig_node then
tNode.after_dig_node(pos, oldnode, oldmetadata, digger)
end
local crd = CRDN(oldnode)
if crd.power_netw then
crd.power_netw:after_dig_node(pos)
end
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end
tNode.groups.not_in_creative_inventory = 0
local def_pas = {
description = name_inv,
tiles = prepare_tiles(tiles.pas, stage, power_png),
consumer = tConsumer,
drawtype = tNode.drawtype,
node_box = tNode.node_box,
selection_box = tNode.selection_box,
can_dig = tNode.can_dig,
on_rotate = tNode.on_rotate or screwdriver.disallow,
on_timer = node_timer_pas,
on_receive_fields = tNode.on_receive_fields,
on_rightclick = tNode.on_rightclick,
after_place_node = after_place_node,
after_dig_node = after_dig_node,
preserve_metadata = tNode.preserve_metadata,
allow_metadata_inventory_put = tNode.allow_metadata_inventory_put,
allow_metadata_inventory_move = tNode.allow_metadata_inventory_move,
allow_metadata_inventory_take = tNode.allow_metadata_inventory_take,
on_metadata_inventory_move = tNode.on_metadata_inventory_move,
on_metadata_inventory_put = tNode.on_metadata_inventory_put,
on_metadata_inventory_take = tNode.on_metadata_inventory_take,
ta_rotate_node = tNode.ta_rotate_node,
ta3_formspec = stage == 3 and tNode.ta3_formspec,
ta4_formspec = stage == 4 and tNode.ta4_formspec,
paramtype = tNode.paramtype,
paramtype2 = "facedir",
drop = tNode.drop,
groups = table.copy(tNode.groups),
is_ground_content = false,
sounds = tNode.sounds,
}
-- Copy custom properties (starting with an underscore)
for k,v in pairs(tNode) do
if string.sub(k, 1, 1) == "_" then
def_pas[k] = v
end
end
minetest.register_node(name_pas, def_pas)
tNode.groups.not_in_creative_inventory = 1
local def_act = {
description = name_inv,
tiles = prepare_tiles(tiles.act, stage, power_png),
consumer = tConsumer,
drawtype = tNode.drawtype,
node_box = tNode.node_box,
selection_box = tNode.selection_box,
on_rotate = tNode.on_rotate or screwdriver.disallow,
on_timer = node_timer_act,
on_receive_fields = tNode.on_receive_fields,
on_rightclick = tNode.on_rightclick,
after_place_node = after_place_node,
after_dig_node = after_dig_node,
allow_metadata_inventory_put = tNode.allow_metadata_inventory_put,
allow_metadata_inventory_move = tNode.allow_metadata_inventory_move,
allow_metadata_inventory_take = tNode.allow_metadata_inventory_take,
on_metadata_inventory_move = tNode.on_metadata_inventory_move,
on_metadata_inventory_put = tNode.on_metadata_inventory_put,
on_metadata_inventory_take = tNode.on_metadata_inventory_take,
ta_rotate_node = tNode.ta_rotate_node,
ta3_formspec = stage == 3 and tNode.ta3_formspec,
ta4_formspec = stage == 4 and tNode.ta4_formspec,
paramtype = tNode.paramtype,
paramtype2 = "facedir",
drop = "",
diggable = false,
groups = table.copy(tNode.groups),
is_ground_content = false,
sounds = tNode.sounds,
}
-- Copy custom properties (starting with an underscore)
for k,v in pairs(tNode) do
if string.sub(k, 1, 1) == "_" then
def_act[k] = v
end
end
minetest.register_node(name_act, def_act)
if power_used then
power.register_nodes({name_pas, name_act}, power_network, "con", sides)
end
techage.register_node({name_pas, name_act}, tNode.tubing)
if tNode.tube_sides then
Tube:set_valid_sides(name_pas, get_keys(tNode.tube_sides))
Tube:set_valid_sides(name_act, get_keys(tNode.tube_sides))
end
end
end
return names[1], names[2], names[3], names[4]
end
@@ -0,0 +1,684 @@
--[[
TechAge
=======
Copyright (C) 2019-2022 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA2/TA3/TA4 Distributor
]]--
-- for lazy programmers
local M = minetest.get_meta
local N = minetest.get_node
-- Consumer Related Data
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end
local Tube = techage.Tube
local S = techage.S
local SRC_INV_SIZE = 8
local STANDBY_TICKS = 3
local COUNTDOWN_TICKS = 4
local CYCLE_TIME = 4
local FILTER_ITEM_LIMIT_PER_STACK = 12
local FILTER_ITEM_LIMIT = 36
local INFO = [[Turn port on/off or read its state: command = 'port', payload = red/green/blue/yellow{=on/off}]]
--local Side2Color = {B="red", L="green", F="blue", R="yellow"}
local SlotColors = {"red", "green", "blue", "yellow"}
local SlotNumbers = {red = 1, green = 2, blue = 3, yellow = 4}
local Num2Ascii = {"B", "L", "F", "R"}
local FilterCache = {} -- local cache for filter settings
local function filter_settings(pos)
local meta = M(pos)
local param2 = techage.get_node_lvm(pos).param2
local inv = meta:get_inventory()
local filter = minetest.deserialize(meta:get_string("filter")) or {false,false,false,false}
local ItemFilter = {} -- {<item:name> = {dir,...}]
local OpenPorts = {} -- {dir, ...}
local counter = 0 -- counts total item number in filter configuration
-- collect all filter settings
for idx,slot in ipairs(SlotColors) do
if filter[idx] == true then
local side = Num2Ascii[idx]
local out_dir = techage.side_to_outdir(side, param2)
if inv:is_empty(slot) then
table.insert(OpenPorts, out_dir)
else
for idx2,stack in ipairs(inv:get_list(slot)) do
local name = stack:get_name()
if name ~= "" then
if not ItemFilter[name] then
ItemFilter[name] = {}
end
for _ = 1, math.min(FILTER_ITEM_LIMIT_PER_STACK, stack:get_count()) do
if counter > FILTER_ITEM_LIMIT then
break
end
table.insert(ItemFilter[name], out_dir)
counter = counter + 1
end
end
end
end
end
end
FilterCache[minetest.hash_node_position(pos)] = {
ItemFilter = ItemFilter,
OpenPorts = OpenPorts,
}
end
-- Return filter table and list of open ports.
-- (see test data)
local function get_filter_settings(pos)
-- local ItemFilter = {
-- ["default:dirt"] = {1,2},
-- ["default:cobble"] = {4},
-- }
-- local OpenPorts = {3}
-- return ItemFilter, OpenPorts
local hash = minetest.hash_node_position(pos)
if FilterCache[hash] == nil then
filter_settings(pos)
end
return FilterCache[hash].ItemFilter, FilterCache[hash].OpenPorts
end
local function blocking_checkbox(pos, filter, is_hp)
local cnt = 0
local _, open_ports = get_filter_settings(pos)
local fs_pos = is_hp and "0.25,5" or "3,3.9"
for _,val in ipairs(filter) do
if val then cnt = cnt + 1 end
end
if cnt > 1 and #open_ports > 0 then
local blocking = M(pos):get_int("blocking") == 1 and "true" or "false"
return "checkbox["..fs_pos..";blocking;"..S("blocking mode")..";"..blocking.."]"..
"tooltip["..fs_pos..";1,1;"..S("Block configured items for open ports")..";#0C3D32;#FFFFFF]"
else
M(pos):set_int("blocking", 0) -- disable blocking
end
return ""
end
local function formspec(self, pos, nvm)
local filter = minetest.deserialize(M(pos):get_string("filter")) or {false,false,false,false}
local is_hp = nvm.high_performance == true
local blocking = blocking_checkbox(pos, filter, is_hp)
if is_hp then
return "size[10.5,9.5]"..
"box[0.25,-0.1;9.6,1.1;#005500]"..
"label[0.6,0.2;"..S("Input").."]"..
"list[context;src;1.75,0;8,1;]"..
blocking..
"image_button[0.25,5.8;1,1;"..self:get_state_button_image(nvm)..";state_button;]"..
"tooltip[0.25,5.8;1,1;"..self:get_state_tooltip(nvm).."]"..
"checkbox[0.25,1.2;filter1;On;"..dump(filter[1]).."]"..
"checkbox[0.25,2.2;filter2;On;"..dump(filter[2]).."]"..
"checkbox[0.25,3.2;filter3;On;"..dump(filter[3]).."]"..
"checkbox[0.25,4.2;filter4;On;"..dump(filter[4]).."]"..
"image[1.25,1.2;0.3,1;techage_inv_red.png]"..
"image[1.25,2.2;0.3,1;techage_inv_green.png]"..
"image[1.25,3.2;0.3,1;techage_inv_blue.png]"..
"image[1.25,4.2;0.3,1;techage_inv_yellow.png]"..
"list[context;red;1.75,1.2;8,1;]"..
"list[context;green;1.75,2.2;8,1;]"..
"list[context;blue;1.75,3.2;8,1;]"..
"list[context;yellow;1.75,4.2;8,1;]"..
"list[current_player;main;1.75,5.8;8,4;]"..
"listring[context;src]"..
"listring[current_player;main]"..
default.get_hotbar_bg(1.75,5.8)
else
return "size[10.5,8.5]"..
"list[context;src;0,0;2,4;]"..
blocking..
"image[2,1.5;1,1;techage_form_arrow.png]"..
"image_button[0,4.8;1,1;"..self:get_state_button_image(nvm)..";state_button;]"..
"tooltip[0,4.8;1,1;"..self:get_state_tooltip(nvm).."]"..
"checkbox[3,0;filter1;On;"..dump(filter[1]).."]"..
"checkbox[3,1;filter2;On;"..dump(filter[2]).."]"..
"checkbox[3,2;filter3;On;"..dump(filter[3]).."]"..
"checkbox[3,3;filter4;On;"..dump(filter[4]).."]"..
"image[4,0;0.3,1;techage_inv_red.png]"..
"image[4,1;0.3,1;techage_inv_green.png]"..
"image[4,2;0.3,1;techage_inv_blue.png]"..
"image[4,3;0.3,1;techage_inv_yellow.png]"..
"list[context;red;4.5,0;6,1;]"..
"list[context;green;4.5,1;6,1;]"..
"list[context;blue;4.5,2;6,1;]"..
"list[context;yellow;4.5,3;6,1;]"..
"list[current_player;main;1.25,4.8;8,4;]"..
"listring[context;src]"..
"listring[current_player;main]"..
default.get_hotbar_bg(1.25,4.8)
end
end
local function get_total_filter_items_number(pos, except_listname, except_index)
local inv = M(pos):get_inventory()
local total = 0
for _, listname in ipairs(SlotColors) do
local list = inv:get_list(listname)
for idx, stack in ipairs(list) do
if not (listname == except_listname and idx == except_index) then
total = total + stack:get_count()
end
end
end
return total
end
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
local inv = M(pos):get_inventory()
local list = inv:get_list(listname)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
if listname == "src" then
CRD(pos).State:start_if_standby(pos)
return stack:get_count()
else
stack:add_item(list[index])
local max_items_to_limit = FILTER_ITEM_LIMIT - get_total_filter_items_number(pos, listname, index)
stack:set_count(math.min(FILTER_ITEM_LIMIT_PER_STACK, stack:get_count(), max_items_to_limit))
inv:set_stack(listname, index, stack)
return 0
end
return 0
end
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
if listname == "src" then
return stack:get_count()
else
local inv = M(pos):get_inventory()
local list = inv:get_list(listname)
list[index]:take_item(stack:get_count())
inv:set_stack(listname, index, list[index])
return 0
end
end
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
local inv = minetest.get_meta(pos):get_inventory()
local stack = inv:get_stack(from_list, from_index)
if from_list == "src" and to_list ~= "src" then
stack:add_item(inv:get_stack(to_list, to_index))
local max_items_to_limit = FILTER_ITEM_LIMIT - get_total_filter_items_number(pos, to_list, to_index)
stack:set_count(math.min(FILTER_ITEM_LIMIT_PER_STACK, stack:get_count(), max_items_to_limit))
inv:set_stack(to_list, to_index, stack)
return 0
elseif from_list ~= "src" and to_list == "src" then
inv:set_stack(from_list, from_index, nil)
return 0
elseif from_list ~= "src" and to_list ~= "src" then
return math.min(stack:get_count(), FILTER_ITEM_LIMIT_PER_STACK - inv:get_stack(to_list, to_index):get_count())
else
return stack:get_count()
end
end
local function tubelib2_on_update2(pos, outdir, tlib2, node)
local is_ta4_tube = true
for dir = 1,4 do
for i, pos, node in Tube:get_tube_line(pos, dir) do
is_ta4_tube = is_ta4_tube and techage.TA4tubes[node.name]
end
end
local nvm = techage.get_nvm(pos)
local crd = CRD(pos)
if CRD(pos).stage == 4 and not is_ta4_tube then
nvm.num_items = crd.num_items / 2
else
nvm.num_items = crd.num_items
end
end
local function shuffle(list)
for i = #list, 2, -1 do
local j = math.random(i)
list[i], list[j] = list[j], list[i]
end
return list
end
local function push_item(pos, base_filter, itemstack, num_items, nvm)
local filter = shuffle(table.copy(base_filter))
local idx = 1
local num_pushed = 0
local num_ports = #filter
local amount = math.floor(math.max((num_items + 1) / num_ports, 1))
local num_of_trials = 0
while num_pushed < num_items and num_of_trials <= 8 do
num_of_trials = num_of_trials + 1
local push_dir = filter[idx]
local num_to_push = math.min(amount, num_items - num_pushed)
local leftover = techage.push_items(pos, push_dir, itemstack:peek_item(num_to_push))
local pushed
if not leftover then
pushed = 0
elseif leftover ~= true then
pushed = num_to_push - leftover:get_count()
else -- leftover == true
pushed = num_to_push
end
num_pushed = num_pushed + pushed
nvm.port_counter[push_dir] = (nvm.port_counter[push_dir] or 0) + pushed
-- filter start offset
idx = idx + 1
if idx > num_ports then
idx = 1
end
end
return num_pushed
end
-- move items to output slots
local function distributing(pos, inv, crd, nvm)
local item_filter, open_ports = get_filter_settings(pos)
local sum_num_pushed = 0
local num_pushed = 0
local blocking_mode = M(pos):get_int("blocking") == 1
-- start searching after last position
local offs = nvm.last_index or 1
for i = 1, SRC_INV_SIZE do
local idx = ((i + offs - 1) % 8) + 1
local stack = inv:get_stack("src", idx)
local item_name = stack:get_name()
local num_items = stack:get_count()
local num_to_push = math.min((nvm.num_items or crd.num_items) - sum_num_pushed, num_items)
local stack_to_push = stack:peek_item(num_to_push)
local filter = item_filter[item_name]
num_pushed = 0
if filter and #filter > 0 then
-- Push items based on filter
num_pushed = push_item(pos, filter, stack_to_push, num_to_push, nvm)
elseif blocking_mode and #open_ports > 0 then
-- Push items based on open ports
num_pushed = push_item(pos, open_ports, stack_to_push, num_to_push, nvm)
end
if not blocking_mode and num_pushed == 0 and #open_ports > 0 then
-- Push items based on open ports
num_pushed = push_item(pos, open_ports, stack_to_push, num_to_push, nvm)
end
sum_num_pushed = sum_num_pushed + num_pushed
stack:take_item(num_pushed)
inv:set_stack("src", idx, stack)
if sum_num_pushed >= (nvm.num_items or crd.num_items) then
nvm.last_index = idx
break
end
end
if sum_num_pushed == 0 then
crd.State:blocked(pos, nvm)
else
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
end
end
-- move items to the output slots
local function keep_running(pos, elapsed)
local nvm = techage.get_nvm(pos)
nvm.port_counter = nvm.port_counter or {}
local crd = CRD(pos)
local inv = M(pos):get_inventory()
if not inv:is_empty("src") then
distributing(pos, inv, crd, nvm)
else
crd.State:idle(pos, nvm)
end
return crd.State:is_active(nvm)
end
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local meta = M(pos)
local crd = CRD(pos)
local filter = minetest.deserialize(meta:get_string("filter")) or {false,false,false,false}
if fields.filter1 ~= nil then
filter[1] = fields.filter1 == "true"
elseif fields.filter2 ~= nil then
filter[2] = fields.filter2 == "true"
elseif fields.filter3 ~= nil then
filter[3] = fields.filter3 == "true"
elseif fields.filter4 ~= nil then
filter[4] = fields.filter4 == "true"
elseif fields.blocking ~= nil then
meta:set_int("blocking", fields.blocking == "true" and 1 or 0)
end
meta:set_string("filter", minetest.serialize(filter))
filter_settings(pos)
local nvm = techage.get_nvm(pos)
if fields.state_button ~= nil then
crd.State:state_button_event(pos, nvm, fields)
else
meta:set_string("formspec", formspec(crd.State, pos, nvm))
end
end
-- techage command to turn on/off filter channels
local function change_filter_settings(pos, slot, val)
local meta = M(pos)
local filter = minetest.deserialize(meta:get_string("filter")) or {false,false,false,false}
local num = SlotNumbers[slot] or 1
if num >= 1 and num <= 4 then
filter[num] = val == "on"
end
meta:set_string("filter", minetest.serialize(filter))
local hash = minetest.hash_node_position(pos)
FilterCache[hash] = nil
local nvm = techage.get_nvm(pos)
meta:set_string("formspec", formspec(CRD(pos).State, pos, nvm))
return true
end
-- techage command to read filter channel status (on/off)
local function read_filter_settings(pos, slot)
local filter = minetest.deserialize(M(pos):get_string("filter")) or {false,false,false,false}
return filter[SlotNumbers[slot]] and "on" or "off"
end
local function get_payload_values(payload)
local color
local idx = 0
local items = {ItemStack(""), ItemStack(""), ItemStack(""), ItemStack(""), ItemStack(""), ItemStack("")}
for s in payload:gmatch("[^%s]+") do --- white spaces
if not color then
if SlotNumbers[s] then
color = s
else
return "red", {}
end
else
idx = idx + 1
if idx <= 6 then
items[idx] = ItemStack(s)
end
end
end
return color, items
end
local function str_of_inv_items(pos, color)
color = SlotColors[color] or color
if SlotNumbers[color] then
local inv = M(pos):get_inventory()
local t = {}
for idx = 1, 6 do
local item = inv:get_stack(color, idx)
if item:get_count() > 0 then
t[#t + 1] = item:get_name()
end
end
return table.concat(t, " ")
end
return ""
end
local function can_dig(pos, player)
if minetest.is_protected(pos, player:get_player_name()) then
return false
end
local inv = M(pos):get_inventory()
return inv:is_empty("src")
end
local get_tiles = function(is_hp)
local variant = is_hp and "_hp" or ""
local tiles = {}
-- '#' will be replaced by the stage number
-- '{power}' will be replaced by the power PNG
tiles.pas = {
-- up, down, right, left, back, front
"techage_filling_ta#.png^techage_appl_distri.png^techage_frame_ta#_top"..variant..".png^techage_appl_color_top.png",
"techage_filling_ta#.png^techage_frame_ta#_top"..variant..".png^(techage_appl_color_top.png^[transformFY)",
"techage_filling_ta#.png^techage_frame_ta#"..variant..".png^techage_appl_distri_yellow.png",
"techage_filling_ta#.png^techage_frame_ta#"..variant..".png^techage_appl_distri_green.png",
"techage_filling_ta#.png^techage_frame_ta#"..variant..".png^techage_appl_distri_red.png",
"techage_filling_ta#.png^techage_frame_ta#"..variant..".png^techage_appl_distri_blue.png",
}
tiles.act = {
-- up, down, right, left, back, front
{
name = "techage_filling4_ta#.png^techage_appl_distri4.png^techage_frame4_ta#_top"..variant..".png^techage_appl_color_top4.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 1.0,
},
},
"techage_filling_ta#.png^techage_frame_ta#_top"..variant..".png^(techage_appl_color_top.png^[transformFY)",
"techage_filling_ta#.png^techage_frame_ta#"..variant..".png^techage_appl_distri_yellow.png",
"techage_filling_ta#.png^techage_frame_ta#"..variant..".png^techage_appl_distri_green.png",
"techage_filling_ta#.png^techage_frame_ta#"..variant..".png^techage_appl_distri_red.png",
"techage_filling_ta#.png^techage_frame_ta#"..variant..".png^techage_appl_distri_blue.png",
}
return tiles
end
local tubing = {
on_pull_item = function(pos, in_dir, num)
local inv = M(pos):get_inventory()
return techage.get_items(pos, inv, "src", num)
end,
on_push_item = function(pos, in_dir, stack)
CRD(pos).State:start_if_standby(pos)
local inv = M(pos):get_inventory()
return techage.put_items(inv, "src", stack)
end,
on_unpull_item = function(pos, in_dir, stack)
local inv = M(pos):get_inventory()
return techage.put_items(inv, "src", stack)
end,
on_recv_message = function(pos, src, topic, payload)
if topic == "info" then
return INFO
elseif topic == "port" then
-- "red"/"green"/"blue"/"yellow" = "on"/"off"
local slot, val = techage.ident_value(payload)
if val == "" then
return read_filter_settings(pos, slot)
else
return change_filter_settings(pos, slot, val)
end
elseif topic == "config" then
local color, items = get_payload_values(payload)
local inv = M(pos):get_inventory()
for idx,item in ipairs(items) do
inv:set_stack(color, idx, item)
end
local hash = minetest.hash_node_position(pos)
FilterCache[hash] = nil
return true
elseif topic == "get" then
return str_of_inv_items(pos, payload)
else
return CRD(pos).State:on_receive_message(pos, topic, payload)
end
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
if topic == 4 then
local slot = SlotColors[payload[1]]
local state = payload[2] == 1 and "on" or "off"
change_filter_settings(pos, slot, state)
return 0
elseif topic == 67 then
local color, items = get_payload_values(payload)
local inv = M(pos):get_inventory()
for idx,item in ipairs(items) do
inv:set_stack(color, idx, item)
end
local hash = minetest.hash_node_position(pos)
FilterCache[hash] = nil
return 0
else
return CRD(pos).State:on_beduino_receive_cmnd(pos, topic, payload)
end
end,
on_beduino_request_data = function(pos, src, topic, payload)
if topic == 148 then
return 0, str_of_inv_items(pos, payload[1])
else
return CRD(pos).State:on_beduino_request_data(pos, topic, payload)
end
end,
on_node_load = function(pos)
CRD(pos).State:on_node_load(pos)
end,
}
local def = {
cycle_time = CYCLE_TIME,
standby_ticks = STANDBY_TICKS,
formspec = formspec,
tubing = tubing,
after_place_node = function(pos, placer)
local meta = M(pos)
local filter = {false,false,false,false}
meta:set_string("filter", minetest.serialize(filter))
local inv = meta:get_inventory()
inv:set_size('src', 8)
inv:set_size('yellow', 6)
inv:set_size('green', 6)
inv:set_size('red', 6)
inv:set_size('blue', 6)
end,
can_dig = can_dig,
node_timer = keep_running,
on_receive_fields = on_receive_fields,
allow_metadata_inventory_put = allow_metadata_inventory_put,
allow_metadata_inventory_move = allow_metadata_inventory_move,
allow_metadata_inventory_take = allow_metadata_inventory_take,
tubelib2_on_update2 = tubelib2_on_update2,
on_metadata_inventory_move = function(pos, from_list, from_index, to_list)
if from_list ~= "src" or to_list ~= "src" then
filter_settings(pos)
local nvm = techage.get_nvm(pos)
M(pos):set_string("formspec", formspec(CRD(pos).State, pos, nvm))
end
end,
on_metadata_inventory_put = function(pos, listname)
if listname ~= "src" then
filter_settings(pos)
local nvm = techage.get_nvm(pos)
M(pos):set_string("formspec", formspec(CRD(pos).State, pos, nvm))
end
end,
on_metadata_inventory_take = function(pos, listname)
if listname ~= "src" then
filter_settings(pos)
local nvm = techage.get_nvm(pos)
M(pos):set_string("formspec", formspec(CRD(pos).State, pos, nvm))
end
end,
groups = {choppy=2, cracky=2, crumbly=2},
sounds = default.node_sound_wood_defaults(),
num_items = {0,4,12,24},
tube_sides = {B=1, R=1, F=1, L=1, D=1, U=1}
}
local node_name_ta2, node_name_ta3, node_name_ta4 = techage.register_consumer(
"distributor",
S("Distributor"),
get_tiles(false),
def
)
local hp_def = table.copy(def)
hp_def.after_place_node = function(pos, placer)
local meta = M(pos)
local nvm = techage.get_nvm(pos)
nvm.high_performance = true
local filter = {false,false,false,false}
meta:set_string("filter", minetest.serialize(filter))
local inv = meta:get_inventory()
inv:set_size('src', 8)
inv:set_size('yellow', 8)
inv:set_size('green', 8)
inv:set_size('red', 8)
inv:set_size('blue', 8)
end
hp_def.num_items = {0,0,0,36}
local _, _, node_name_ta4_hp = techage.register_consumer(
"high_performance_distributor", S("High Performance Distributor"),
get_tiles(true),
hp_def,
{false, false, false, true}
)
minetest.register_craft({
output = node_name_ta2.." 2",
recipe = {
{"group:wood", "techage:iron_ingot", "group:wood"},
{"techage:tubeS", "default:mese_crystal", "techage:tubeS"},
{"group:wood", "techage:iron_ingot", "group:wood"},
},
})
minetest.register_craft({
output = node_name_ta3,
recipe = {
{"", "techage:iron_ingot", ""},
{"", node_name_ta2, ""},
{"", "techage:vacuum_tube", ""},
},
})
minetest.register_craft({
output = node_name_ta4,
recipe = {
{"", "techage:iron_ingot", ""},
{"", node_name_ta3, ""},
{"", "techage:ta4_wlanchip", ""},
},
})
minetest.register_craft({
output = node_name_ta4_hp,
recipe = {
{node_name_ta4, "default:copper_ingot"},
{"default:mese_crystal_fragment", node_name_ta4},
},
})
@@ -0,0 +1,314 @@
--[[
TechAge
=======
Copyright (C) 2019-2022 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA2/TA3/TA4 Electronic Fab
]]--
-- for lazy programmers
local M = minetest.get_meta
-- Consumer Related Data
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end
local S = techage.S
local STANDBY_TICKS = 3
local COUNTDOWN_TICKS = 4
local CYCLE_TIME = 6
local recipes = techage.recipes
local RecipeType = {
[2] = "ta2_electronic_fab",
[3] = "ta3_electronic_fab",
[4] = "ta4_electronic_fab",
}
local function formspec(self, pos, nvm)
local rtype = RecipeType[CRD(pos).stage]
local owner = M(pos):get_string("owner")
return "size[8.4,8.4]"..
"list[context;src;0,0;2,4;]"..
recipes.formspec(2.2, 0, rtype, nvm, owner)..
"list[context;dst;6.4,0;2,4;]"..
"image_button[3.7,3.3;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
"tooltip[3.7,3.3;1,1;"..self:get_state_tooltip(nvm).."]"..
"list[current_player;main;0.2,4.5;8,4;]"..
"listring[context;dst]"..
"listring[current_player;main]"..
"listring[context;src]"..
"listring[current_player;main]"..
default.get_hotbar_bg(0.2, 4.5)
end
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
local crd = CRD(pos)
if listname == "src" then
crd.State:start_if_standby(pos)
return stack:get_count()
end
return 0
end
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local stack = inv:get_stack(from_list, from_index)
return allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
end
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return stack:get_count()
end
local function get_original_waste(inv, waste)
-- Waste has meta data, so we need to find the original waste item
for i = 1, 8 do
local stack = inv:get_stack("src", i)
if stack:get_count() == 1 then
if stack:get_name() == waste:get_name() then
return stack
end
end
end
return waste
end
local function making(pos, crd, nvm, inv)
local owner = M(pos):get_string("owner")
local rtype = RecipeType[crd.stage]
local recipe = recipes.get(nvm, rtype, owner)
local output = ItemStack(recipe.output.name .. " " .. recipe.output.num)
local waste = recipe.waste and ItemStack(recipe.waste.name .. " " .. recipe.waste.num)
if inv:room_for_item("dst", output) and (not waste or inv:room_for_item("dst", waste)) then
for _,item in ipairs(recipe.input) do
local input = ItemStack(item.name.." "..item.num)
if not inv:contains_item("src", input) then
crd.State:idle(pos, nvm)
return
end
end
-- For some recipes, an item customized via metadata is used as a copy template.
-- This allows specially programmed items such as ROM chips to be produced.
-- The metadata of the copy template must be passed to the on_production function.
-- At the same time, the metadata of the copy template must not be lost when moving
-- as 'waste' to the output inventory.
local idef = minetest.registered_items[recipe.output.name]
if waste and idef and idef.on_production then
waste = get_original_waste(inv, waste)
local metadata = waste:get_meta():to_table().fields or {}
output = idef.on_production(output, metadata)
end
for _,item in ipairs(recipe.input) do
local input = ItemStack(item.name.." "..item.num)
inv:remove_item("src", input)
end
inv:add_item("dst", output)
if waste then
inv:add_item("dst", waste)
end
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
return
end
crd.State:idle(pos, nvm)
end
local function keep_running(pos, elapsed)
local nvm = techage.get_nvm(pos)
local crd = CRD(pos)
local inv = M(pos):get_inventory()
if inv then
making(pos, crd, nvm, inv)
end
end
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local nvm = techage.get_nvm(pos)
local crd = CRD(pos)
if not nvm.running then
recipes.on_receive_fields(pos, formname, fields, player)
end
crd.State:state_button_event(pos, nvm, fields)
M(pos):set_string("formspec", formspec(crd.State, pos, nvm))
end
local function can_dig(pos, player)
if minetest.is_protected(pos, player:get_player_name()) then
return false
end
local inv = M(pos):get_inventory()
return inv:is_empty("dst") and inv:is_empty("src")
end
local tiles = {}
-- '#' will be replaced by the stage number
tiles.pas = {
-- up, down, right, left, back, front
"techage_filling_ta#.png^techage_frame_ta#_top.png",
"techage_filling_ta#.png^techage_frame_ta#_bottom.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
"techage_filling_ta#.png^techage_appl_electronic_fab.png^techage_frame_ta#.png",
"techage_filling_ta#.png^techage_appl_electronic_fab.png^techage_frame_ta#.png",
}
tiles.act = {
-- up, down, right, left, back, front
"techage_filling_ta#.png^techage_frame_ta#_top.png",
"techage_filling_ta#.png^techage_frame_ta#_bottom.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
{
name = "techage_filling4_ta#.png^techage_appl_electronic_fab4.png^techage_frame4_ta#.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 0.5,
},
},
{
name = "techage_filling4_ta#.png^techage_appl_electronic_fab4.png^techage_frame4_ta#.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 0.5,
},
},
}
local tubing = {
on_inv_request = function(pos, in_dir, access_type)
if access_type == "push" then
local meta = minetest.get_meta(pos)
if meta:get_int("push_dir") == in_dir or in_dir == 5 then
return meta:get_inventory(), "src"
end
end
end,
on_pull_item = function(pos, in_dir, num)
local meta = minetest.get_meta(pos)
if meta:get_int("pull_dir") == in_dir then
local inv = M(pos):get_inventory()
return techage.get_items(pos, inv, "dst", num)
end
end,
on_push_item = function(pos, in_dir, stack, idx)
local meta = minetest.get_meta(pos)
if meta:get_int("push_dir") == in_dir or in_dir == 5 then
local inv = M(pos):get_inventory()
--CRD(pos).State:start_if_standby(pos) -- would need power!
return techage.put_items(inv, "src", stack, idx)
end
end,
on_unpull_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
if meta:get_int("pull_dir") == in_dir then
local inv = M(pos):get_inventory()
return techage.put_items(inv, "dst", stack)
end
end,
on_recv_message = function(pos, src, topic, payload)
return CRD(pos).State:on_receive_message(pos, topic, payload)
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
return CRD(pos).State:on_beduino_receive_cmnd(pos, topic, payload)
end,
on_beduino_request_data = function(pos, src, topic, payload)
return CRD(pos).State:on_beduino_request_data(pos, topic, payload)
end,
on_node_load = function(pos)
CRD(pos).State:on_node_load(pos)
end,
}
local node_name_ta2, node_name_ta3, node_name_ta4 =
techage.register_consumer("electronic_fab", S("Electronic Fab"), tiles, {
drawtype = "normal",
cycle_time = CYCLE_TIME,
standby_ticks = STANDBY_TICKS,
formspec = formspec,
tubing = tubing,
after_place_node = function(pos, placer)
local inv = M(pos):get_inventory()
inv:set_size("src", 8)
inv:set_size("dst", 8)
end,
can_dig = can_dig,
node_timer = keep_running,
on_receive_fields = on_receive_fields,
allow_metadata_inventory_put = allow_metadata_inventory_put,
allow_metadata_inventory_move = allow_metadata_inventory_move,
allow_metadata_inventory_take = allow_metadata_inventory_take,
groups = {choppy=2, cracky=2, crumbly=2},
sounds = default.node_sound_wood_defaults(),
num_items = {0,1,1,1},
power_consumption = {0,8,12,12},
},
{false, true, true, true}) -- TA2/TA3/TA4
minetest.register_craft({
output = node_name_ta2,
recipe = {
{"group:wood", "default:diamond", "group:wood"},
{"techage:tubeS", "basic_materials:gear_steel", "techage:tubeS"},
{"group:wood", "default:steel_ingot", "group:wood"},
},
})
minetest.register_craft({
output = node_name_ta3,
recipe = {
{"", "default:diamond", ""},
{"", node_name_ta2, ""},
{"", "techage:vacuum_tube", ""},
},
})
minetest.register_craft({
output = node_name_ta4,
recipe = {
{"", "default:diamond", ""},
{"", node_name_ta3, ""},
{"", "techage:ta4_wlanchip", ""},
},
})
techage.recipes.register_craft_type("ta2_electronic_fab", {
description = S("TA2 Ele Fab"),
icon = 'techage_filling_ta2.png^techage_appl_electronic_fab.png^techage_frame_ta2.png',
width = 2,
height = 2,
})
techage.recipes.register_craft_type("ta3_electronic_fab", {
description = S("TA3 Ele Fab"),
icon = 'techage_filling_ta3.png^techage_appl_electronic_fab.png^techage_frame_ta3.png',
width = 2,
height = 2,
})
techage.recipes.register_craft_type("ta4_electronic_fab", {
description = S("TA4 Ele Fab"),
icon = 'techage_filling_ta4.png^techage_appl_electronic_fab.png^techage_frame_ta4.png',
width = 2,
height = 2,
})
@@ -0,0 +1,445 @@
--[[
TechAge
=======
Copyright (C) 2019-2020 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
Forceload block
]]--
-- for lazy programmers
local M = minetest.get_meta
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local S = techage.S
-- Global registry so admins can see all forceload blocks, even for offline players
-- Keys use "flb_<pos>" prefix on techage.storage to avoid collisions with
-- other users of the shared mod_storage (numbers, nodedata, etc.)
local function registry_add(pos, owner)
techage.storage:set_string("flb_" .. P2S(pos), owner)
end
local function registry_remove(pos)
techage.storage:set_string("flb_" .. P2S(pos), "")
end
local function registry_get_all()
local entries = {}
for key, val in pairs(techage.storage:to_table().fields) do
if val ~= "" and key:sub(1, 4) == "flb_" then
local pos = minetest.string_to_pos(key:sub(5))
if pos then
entries[#entries+1] = {pos = pos, owner = val}
end
end
end
table.sort(entries, function(a, b)
if a.owner ~= b.owner then return a.owner < b.owner end
return P2S(a.pos) < P2S(b.pos)
end)
return entries
end
local function calc_area(pos)
local xpos = (math.floor(pos.x / 16) * 16)
local ypos = (math.floor(pos.y / 16) * 16)
local zpos = (math.floor(pos.z / 16) * 16)
local pos1 = {x=xpos, y=ypos, z=zpos}
local pos2 = {x=xpos+15, y=ypos+15, z=zpos+15}
return pos1, pos2
end
local function in_list(list, x)
local pos1 = calc_area(x)
for _,v in ipairs(list) do
local pos2 = calc_area(v)
if vector.equals(pos1, pos2) then return true end
end
return false
end
local function remove_list_elem(list, x)
local n = nil
for idx, v in ipairs(list) do
if vector.equals(v, x) then
n = idx
break
end
end
if n then
table.remove(list, n)
end
return list
end
local function chat(player, text)
minetest.chat_send_player(player:get_player_name(), "[Techage] "..text)
end
local function postload_area(pos)
minetest.log("warning", "[FLB] area "..P2S(pos).." not loaded!")
if not minetest.forceload_block(pos, true) then
minetest.after(60, postload_area, pos)
end
end
local function add_pos(pos, player)
local meta = player:get_meta()
local lPos = minetest.deserialize(meta:get_string("techage_forceload_blocks")) or {}
if not in_list(lPos, pos) and (#lPos < techage.max_num_forceload_blocks or
minetest.global_exists("creative") and creative.is_enabled_for and
creative.is_enabled_for(player:get_player_name())) then
lPos[#lPos+1] = pos
local meta = player:get_meta()
meta:set_string("techage_forceload_blocks", minetest.serialize(lPos))
return true
end
return false
end
local function del_pos(pos, player)
local meta = player:get_meta()
local lPos = minetest.deserialize(meta:get_string("techage_forceload_blocks")) or {}
lPos = remove_list_elem(lPos, pos)
if next(lPos) then
meta:set_string("techage_forceload_blocks", minetest.serialize(lPos))
else
meta:set_string("techage_forceload_blocks", "")
end
end
local function get_pos_list(player)
local meta = player:get_meta()
return minetest.deserialize(meta:get_string("techage_forceload_blocks")) or {}
end
local function set_pos_list(player, lPos)
local meta = player:get_meta()
if next(lPos) then
meta:set_string("techage_forceload_blocks", minetest.serialize(lPos))
else
meta:set_string("techage_forceload_blocks", "")
end
end
local function show_flbs(pos, name, range)
local pos1 = {x=pos.x-range, y=pos.y-range, z=pos.z-range}
local pos2 = {x=pos.x+range, y=pos.y+range, z=pos.z+range}
for _,npos in ipairs(minetest.find_nodes_in_area(pos1, pos2, {"techage:forceload", "techage:forceloadtile"})) do
local _pos1, _pos2 = calc_area(npos)
local owner = M(npos):get_string("owner")
techage.mark_region(name, _pos1, _pos2, owner .. " " .. P2S(npos))
end
end
local function get_data(pos, player)
local pos1, pos2 = calc_area(pos)
local meta = player:get_meta()
local num = #minetest.deserialize(meta:get_string("techage_forceload_blocks")) or 0
local max = techage.max_num_forceload_blocks
return pos1, pos2, num, max
end
local function formspec(name)
local player = minetest.get_player_by_name(name)
if player then
local lPos = get_pos_list(player)
local tRes = {}
tRes[#tRes+1] = "#"
tRes[#tRes+1] = S("Block at pos")
tRes[#tRes+1] = S("Area from")
tRes[#tRes+1] = S("Area to")
tRes[#tRes+1] = S("Status")
for idx,pos in ipairs(lPos) do
local pos1, pos2 = calc_area(pos)
tRes[#tRes+1] = idx
tRes[#tRes+1] = minetest.formspec_escape(P2S(pos))
tRes[#tRes+1] = minetest.formspec_escape(P2S(pos1))
tRes[#tRes+1] = minetest.formspec_escape(P2S(pos2))
tRes[#tRes+1] = minetest.forceload_block(pos, true) and 'Loaded' or 'Unloaded'
end
return "size[9,9]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"label[0,0;"..S("List of your Forceload Blocks:").."]"..
"tablecolumns[text,width=1.8;text,width=12;text,width=12;text,width=12;text,width=12]"..
"table[0,0.6;8.8,8.4;output;"..table.concat(tRes, ",")..";1]"
end
end
local function on_place(itemstack, placer, pointed_thing)
if pointed_thing.type ~= "node" then
return itemstack
end
return minetest.rotate_and_place(itemstack, placer, pointed_thing)
end
local function after_place_node(pos, placer, itemstack)
if add_pos(pos, placer) then
minetest.forceload_block(pos, true)
local pos1, pos2, num, max = get_data(pos, placer)
M(pos):set_string("infotext", "Area "..P2S(pos1).." to "..P2S(pos2).." "..S("loaded").."!\n"..
S("Punch the block to make the area visible."))
chat(placer, "Area ("..num.."/"..max..") "..P2S(pos1).." to "..P2S(pos2).." "..S("loaded").."!")
techage.mark_region(placer:get_player_name(), pos1, pos2)
M(pos):set_string("owner", placer:get_player_name())
registry_add(pos, placer:get_player_name())
else
chat(placer, S("Area already loaded or max. number of Forceload Blocks reached!"))
minetest.remove_node(pos)
return itemstack
end
end
local function after_dig_node(pos, oldnode, oldmetadata, digger)
local player = minetest.get_player_by_name(oldmetadata.fields.owner)
if player then
del_pos(pos, player)
end
minetest.forceload_free_block(pos, true)
techage.unmark_region(oldmetadata.fields.owner)
registry_remove(pos)
end
local function on_rightclick(pos, node, clicker, itemstack, pointed_thing)
local owner = M(pos):get_string("owner")
local name = clicker:get_player_name()
if name == owner or minetest.check_player_privs(name, "server") then
local s = formspec(owner)
if s then
minetest.show_formspec(name, "techage:forceload", s)
end
end
end
local function on_punch(pos, node, puncher, pointed_thing)
local pos1, pos2 = calc_area(pos)
techage.switch_region(puncher:get_player_name(), pos1, pos2)
end
minetest.register_node("techage:forceload", {
description = S("Techage Forceload Block"),
tiles = {
-- up, down, right, left, back, front
'techage_filling_ta2.png^techage_frame_ta2_top.png',
'techage_filling_ta2.png^techage_frame_ta2_top.png',
{
name = "techage_filling_ta2.png^techage_frame_ta2_top.png^techage_appl_forceload.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 0.5,
},
},
},
after_place_node = after_place_node,
after_dig_node = after_dig_node,
on_rightclick = on_rightclick,
on_punch = on_punch,
paramtype = "light",
sunlight_propagates = true,
use_texture_alpha = techage.CLIP,
groups = {choppy=2, cracky=2, crumbly=2,
digtron_protected = 1,
not_in_creative_inventory = techage.max_num_forceload_blocks == 0 and 1 or 0},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
})
minetest.register_node("techage:forceloadtile", {
description = S("Techage Forceload Tile"),
tiles = {
-- up, down, right, left, back, front
{
name = "techage_filling_ta2.png^techage_frame_ta2_top.png^techage_appl_forceload.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 0.5,
},
},
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
--{-5/16, -7/16, -5/16, 5/16, -5/16, 5/16},
{-4/16, -8/16, -4/16, 4/16, -15/32, 4/16},
},
},
on_place = on_place,
after_place_node = after_place_node,
after_dig_node = after_dig_node,
on_rightclick = on_rightclick,
on_punch = on_punch,
paramtype = "light",
paramtype2 = "facedir",
sunlight_propagates = true,
groups = {choppy=2, cracky=2, crumbly=2,
not_in_creative_inventory = techage.max_num_forceload_blocks == 0 and 1 or 0},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
})
if techage.max_num_forceload_blocks > 0 then
minetest.register_craft({
output = "techage:forceload",
recipe = {
{"group:wood", "", "group:wood"},
{"default:mese_crystal_fragment", "techage:usmium_nuggets", "default:mese_crystal_fragment"},
{"group:wood", "techage:iron_ingot", "group:wood"},
},
})
minetest.register_craft({
type = "shapeless",
output = "techage:forceloadtile",
recipe = {"techage:forceload"},
})
minetest.register_craft({
type = "shapeless",
output = "techage:forceload",
recipe = {"techage:forceloadtile"},
})
end
minetest.register_on_joinplayer(function(player)
local lPos = {}
local name = player:get_player_name()
for _,pos in ipairs(get_pos_list(player)) do
local node = techage.get_node_lvm(pos)
if node.name == "techage:forceload" or node.name == "techage:forceloadtile" then
if not minetest.forceload_block(pos, true) then
minetest.after(60, postload_area, pos)
end
lPos[#lPos+1] = pos
-- Populate registry for blocks placed before the registry existed
if techage.storage:get_string("flb_" .. P2S(pos)) == "" then
registry_add(pos, name)
end
end
end
set_pos_list(player, lPos)
end)
minetest.register_on_leaveplayer(function(player)
for _,pos in ipairs(get_pos_list(player)) do
minetest.forceload_free_block(pos, true)
end
end)
local function admin_formspec()
local entries = registry_get_all()
local engine_limit = tonumber(minetest.settings:get("max_forceloaded_blocks")) or 16
local per_player = techage.max_num_forceload_blocks
local total = #entries
local warn
if total >= engine_limit then
warn = "label[0,0.5;\27(c@#FF4444)WARNING: Engine limit reached (" ..
total .. "/" .. engine_limit .. ")! No new forceload blocks can be placed!]"
elseif total >= math.floor(engine_limit * 0.8) then
warn = "label[0,0.5;\27(c@#FFA500)Notice: " ..
total .. " of " .. engine_limit .. " engine-level slots in use (80 %+ full).]"
else
warn = "label[0,0.5;Engine limit: " .. engine_limit ..
" Per-player limit: " .. per_player .. "]"
end
local tRes = {}
tRes[#tRes+1] = "#"
tRes[#tRes+1] = "Owner"
tRes[#tRes+1] = "Position"
tRes[#tRes+1] = "Online"
for idx, e in ipairs(entries) do
local online = minetest.get_player_by_name(e.owner) ~= nil
tRes[#tRes+1] = idx
tRes[#tRes+1] = minetest.formspec_escape(e.owner)
tRes[#tRes+1] = minetest.formspec_escape(P2S(e.pos))
tRes[#tRes+1] = online and "yes" or "no"
end
return "size[9,9.5]" ..
default.gui_bg ..
default.gui_bg_img ..
default.gui_slots ..
"label[0,0;Forceload Blocks: " .. total .. " total | Engine limit: " .. engine_limit ..
" | Per-player limit: " .. per_player .. "]" ..
warn ..
"tablecolumns[text,width=1.8;text,width=10;text,width=10;text,width=5]" ..
"table[0,1.1;8.8,8.4;flb_admin;" .. table.concat(tRes, ",") .. ";1]"
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= "techage:forceload_admin" then return end
if fields.flb_admin then
local event = minetest.explode_table_event(fields.flb_admin)
if event.type == "DCL" then
local idx = event.row - 1 -- row 1 is the header
if idx >= 1 and minetest.check_player_privs(player:get_player_name(), "server") then
local entries = registry_get_all()
local entry = entries[idx]
if entry then
player:set_pos(entry.pos)
end
end
end
end
end)
minetest.register_chatcommand("forceload_admin", {
params = "",
description = "[Admin] Show all forceload blocks on the server; double-click to teleport",
privs = {server = true},
func = function(name, param)
local player = minetest.get_player_by_name(name)
if player then
minetest.show_formspec(name, "techage:forceload_admin", admin_formspec())
end
end,
})
minetest.register_chatcommand("forceload", {
params = "",
description = S("Show all forceload blocks in a 64x64x64 range"),
func = function(name, param)
local player = minetest.get_player_by_name(name)
if player then
local pos = player:get_pos()
pos = vector.round(pos)
show_flbs(pos, name, 64)
end
end,
})
minetest.register_chatcommand("forceload_verify", {
params = "",
description = "Checks each forceload block and returns a count of active/placed blocks",
func = function(name, param)
local player = minetest.get_player_by_name(name)
if player then
local loaded = {}
local wanted = get_pos_list(player)
for _,pos in ipairs(wanted) do
if minetest.forceload_block(pos, true) then
loaded[#loaded+1] = pos
end
end
minetest.chat_send_player(name, "Found "..#loaded.." out of ".. #wanted .. " force loads")
end
end,
})
@@ -0,0 +1,129 @@
--[[
TechAge
=======
Copyright (C) 2019-2020 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
Tube support for default chests and furnace
]]--
local OwnerCache = {
}
-- Check if the chest is in the protected area of the owner
local function is_owner(pos, meta)
local owner = meta:get_string("owner")
local key = minetest.hash_node_position(pos)
-- If successfull, store info in cache
if OwnerCache[key] ~= owner then
if not minetest.is_protected(pos, owner) then
OwnerCache[key] = owner
end
end
return OwnerCache[key] == owner
end
techage.register_node({"default:chest", "default:chest_open"}, {
on_inv_request = function(pos, in_dir, access_type)
local meta = minetest.get_meta(pos)
return meta:get_inventory(), "main"
end,
on_pull_item = function(pos, in_dir, num)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.get_items(pos, inv, "main", num)
end,
on_push_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.put_items(inv, "main", stack)
end,
on_unpull_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.put_items(inv, "main", stack)
end,
})
techage.register_node({"default:chest_locked", "default:chest_locked_open"}, {
on_inv_request = function(pos, in_dir, access_type)
local meta = minetest.get_meta(pos)
if is_owner(pos, meta) then
return meta:get_inventory(), "main"
end
end,
on_pull_item = function(pos, in_dir, num)
local meta = minetest.get_meta(pos)
if is_owner(pos, meta) then
local inv = meta:get_inventory()
return techage.get_items(pos, inv, "main", num)
end
end,
on_push_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.put_items(inv, "main", stack)
end,
on_unpull_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.put_items(inv, "main", stack)
end,
})
techage.register_node({"default:furnace", "default:furnace_active"}, {
on_pull_item = function(pos, in_dir, num)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.get_items(pos, inv, "dst", num)
end,
on_push_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
minetest.get_node_timer(pos):start(1.0)
if in_dir == 5 then
return techage.put_items(inv, "src", stack)
elseif minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then
return techage.put_items(inv, "fuel", stack)
else
return techage.put_items(inv, "src", stack)
end
end,
on_unpull_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.put_items(inv, "dst", stack)
end,
})
techage.register_node({"mobs:beehive"}, {
on_pull_item = function(pos, in_dir, num)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.get_items(pos, inv, "beehive", num)
end,
on_unpull_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.put_items(inv, "beehive", stack)
end,
})
techage.register_node({"xdecor:hive"}, {
on_pull_item = function(pos, in_dir, num)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.get_items(pos, inv, "honey", num)
end,
on_unpull_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.put_items(inv, "honey", stack)
end,
})
@@ -0,0 +1,341 @@
--[[
TechAge
=======
Copyright (C) 2019-2022 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA2 Gravel Rinser, washing sieved gravel to find more ores
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
-- Consumer Related Data
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end
local STANDBY_TICKS = 3
local COUNTDOWN_TICKS = 4
local CYCLE_TIME = 4
local Probability = {}
local function formspec(self, pos, nvm)
return "size[8,8]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"list[context;src;0,0;3,3;]"..
"item_image[0,0;1,1;default:gravel]"..
"image[0,0;1,1;techage_form_mask.png]"..
"image[3.5,0;1,1;"..techage.get_power_image(pos, nvm).."]"..
"image[3.5,1;1,1;techage_form_arrow.png]"..
"image_button[3.5,2;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
"tooltip[3.5,2;1,1;"..self:get_state_tooltip(nvm).."]"..
"list[context;dst;5,0;3,3;]"..
"list[current_player;main;0,4;8,4;]"..
"listring[context;dst]"..
"listring[current_player;main]"..
"listring[context;src]"..
"listring[current_player;main]"..
default.get_hotbar_bg(0, 4)
end
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
if listname == "src" then
CRD(pos).State:start_if_standby(pos)
return stack:get_count()
elseif listname == "dst" then
return 0
end
end
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local stack = inv:get_stack(from_list, from_index)
return allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
end
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return stack:get_count()
end
local function get_water_level(pos)
local node = techage.get_node_lvm(pos)
if minetest.get_item_group(node.name, "water") > 0 then
local ndef = minetest.registered_nodes[node.name]
if ndef and ndef.liquidtype == "flowing" then
return node.param2
end
end
return 99
end
local function determine_water_dir(pos)
local lvl = get_water_level(pos)
if lvl > get_water_level({x=pos.x+1, y=pos.y, z=pos.z}) then
return 2
end
if lvl > get_water_level({x=pos.x-1, y=pos.y, z=pos.z}) then
return 4
end
if lvl > get_water_level({x=pos.x, y=pos.y, z=pos.z+1}) then
return 1
end
if lvl > get_water_level({x=pos.x, y=pos.y, z=pos.z-1}) then
return 3
end
return 0
end
local function set_velocity(obj, pos, vel)
if obj then
obj:set_acceleration({x = 0, y = 0, z = 0})
local p = obj:get_pos()
if p then
obj:set_pos({x=p.x, y=p.y-0.3, z=p.z})
obj:set_velocity(vel)
end
end
end
local function add_object(pos, name)
local dir = determine_water_dir(pos)
if dir > 0 then
local obj = minetest.add_item(pos, ItemStack(name))
local vel = vector.multiply(tubelib2.Dir6dToVector[dir], 0.3)
minetest.after(0.3, set_velocity, obj, pos, vel)
end
end
local function get_random_gravel_ore()
for ore, probability in pairs(Probability) do
if math.random(probability) == 1 then
return ore
end
end
end
local function remove_objects(pos)
for _, object in pairs(minetest.get_objects_inside_radius(pos, 1)) do
local lua_entity = object:get_luaentity()
if not object:is_player() and lua_entity and lua_entity.name == "__builtin:item" then
object:remove()
end
end
end
local function washing(pos, crd, nvm, inv)
-- for testing purposes
if inv:contains_item("src", ItemStack("default:stick")) then
add_object({x=pos.x, y=pos.y+1, z=pos.z}, "default:stick")
inv:remove_item("src", ItemStack("default:stick"))
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
return
end
local src = ItemStack("techage:sieved_gravel")
local dst = ItemStack("default:sand")
if inv:contains_item("src", src) then
if not inv:room_for_item("dst", dst) then
crd.State:blocked(pos, nvm)
return
end
local ore = get_random_gravel_ore()
if ore then
add_object({x=pos.x, y=pos.y+1, z=pos.z}, ore)
end
inv:add_item("dst", dst)
inv:remove_item("src", src)
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
return
else
crd.State:idle(pos, nvm)
return
end
end
local function keep_running(pos, elapsed)
local nvm = techage.get_nvm(pos)
local crd = CRD(pos)
local inv = M(pos):get_inventory()
washing(pos, crd, nvm, inv)
end
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local nvm = techage.get_nvm(pos)
CRD(pos).State:state_button_event(pos, nvm, fields)
end
local function can_dig(pos, player)
if minetest.is_protected(pos, player:get_player_name()) then
return false
end
local inv = M(pos):get_inventory()
return inv:is_empty("dst") and inv:is_empty("src")
end
local tiles = {}
-- '#' will be replaced by the stage number
-- '{power}' will be replaced by the power PNG
tiles.pas = {
-- up, down, right, left, back, front
"techage_appl_rinser_top.png^techage_frame_ta#_top.png",
"techage_filling_ta#.png^techage_frame_ta#_bottom.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
"techage_filling_ta#.png^techage_appl_rinser.png^techage_frame_ta#.png",
"techage_filling_ta#.png^techage_appl_rinser.png^techage_frame_ta#.png",
}
tiles.act = {
-- up, down, right, left, back, front
{
name = "techage_appl_rinser4_top.png^techage_frame4_ta#_top.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 2.0,
},
},
"techage_filling_ta#.png^techage_frame_ta#_bottom.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
"techage_filling_ta#.png^techage_appl_rinser.png^techage_frame_ta#.png",
"techage_filling_ta#.png^techage_appl_rinser.png^techage_frame_ta#.png",
}
local tubing = {
on_pull_item = function(pos, in_dir, num)
local meta = minetest.get_meta(pos)
if meta:get_int("pull_dir") == in_dir then
local inv = M(pos):get_inventory()
return techage.get_items(pos, inv, "dst", num)
end
end,
on_push_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
if meta:get_int("push_dir") == in_dir or in_dir == 5 then
local inv = M(pos):get_inventory()
-- CRD(pos).State:start_if_standby(pos) -- would need power!
return techage.put_items(inv, "src", stack)
end
end,
on_unpull_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
if meta:get_int("pull_dir") == in_dir then
local inv = M(pos):get_inventory()
return techage.put_items(inv, "dst", stack)
end
end,
on_recv_message = function(pos, src, topic, payload)
return CRD(pos).State:on_receive_message(pos, topic, payload)
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
return CRD(pos).State:on_beduino_receive_cmnd(pos, topic, payload)
end,
on_beduino_request_data = function(pos, src, topic, payload)
return CRD(pos).State:on_beduino_request_data(pos, topic, payload)
end,
on_node_load = function(pos)
remove_objects({x=pos.x, y=pos.y+1, z=pos.z})
CRD(pos).State:on_node_load(pos)
end,
}
local node_name_ta2, node_name_ta3, node_name_ta4 =
techage.register_consumer("rinser", S("Gravel Rinser"), tiles, {
drawtype = "nodebox",
paramtype = "light",
node_box = {
type = "fixed",
fixed = {
{-8/16, -8/16, -8/16, 8/16, 8/16, -6/16},
{-8/16, -8/16, 6/16, 8/16, 8/16, 8/16},
{-8/16, -8/16, -8/16, -6/16, 8/16, 8/16},
{ 6/16, -8/16, -8/16, 8/16, 8/16, 8/16},
{-6/16, -8/16, -6/16, 6/16, 6/16, 6/16},
{-6/16, 6/16, -1/16, 6/16, 8/16, 1/16},
{-1/16, 6/16, -6/16, 1/16, 8/16, 6/16},
},
},
selection_box = {
type = "fixed",
fixed = {-8/16, -8/16, -8/16, 8/16, 8/16, 8/16},
},
cycle_time = CYCLE_TIME,
standby_ticks = STANDBY_TICKS,
formspec = formspec,
tubing = tubing,
after_place_node = function(pos, placer)
local inv = M(pos):get_inventory()
inv:set_size('src', 9)
inv:set_size('dst', 9)
end,
can_dig = can_dig,
node_timer = keep_running,
on_receive_fields = on_receive_fields,
allow_metadata_inventory_put = allow_metadata_inventory_put,
allow_metadata_inventory_move = allow_metadata_inventory_move,
allow_metadata_inventory_take = allow_metadata_inventory_take,
groups = {choppy=2, cracky=2, crumbly=2},
sounds = default.node_sound_wood_defaults(),
num_items = {0,1,1,1},
power_consumption = {0,3,3,3},
tube_sides = {L=1, R=1, U=1},
},
{false, true, true, false}) -- TA2/TA3
minetest.register_craft({
output = node_name_ta2,
recipe = {
{"group:wood", "default:mese_crystal", "group:wood"},
{"techage:tubeS", "techage:sieve", "techage:tubeS"},
{"group:wood", "default:tin_ingot", "group:wood"},
},
})
minetest.register_craft({
output = node_name_ta3,
recipe = {
{"", "default:mese_crystal", ""},
{"", node_name_ta2, ""},
{"", "techage:vacuum_tube", ""},
},
})
techage.recipes.register_craft_type("rinsing", {
description = S("Rinsing"),
icon = "techage_appl_rinser_top.png^techage_frame_ta2_top.png",
width = 2,
height = 2,
})
function techage.add_rinser_recipe(recipe)
Probability[recipe.output] = recipe.probability
recipe.items = {recipe.input}
recipe.type = "rinsing"
techage.recipes.register_craft(recipe)
end
techage.add_rinser_recipe({input="techage:sieved_gravel", output="techage:usmium_nuggets", probability=30})
techage.add_rinser_recipe({input="techage:sieved_gravel", output="default:copper_lump", probability=15})
@@ -0,0 +1,298 @@
--[[
TechAge
=======
Copyright (C) 2019-2022 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA2/TA3/TA4 Gravel Sieve, sieving gravel to find ores
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
-- Consumer Related Data
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end
local STANDBY_TICKS = 3
local COUNTDOWN_TICKS = 4
local CYCLE_TIME = 4
local get_random_gravel_ore = techage.gravelsieve_get_random_gravel_ore
local get_random_basalt_ore = techage.gravelsieve_get_random_basalt_ore
local function formspec(self, pos, nvm)
return "size[8,8]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"list[context;src;0,0;3,3;]"..
"item_image[0,0;1,1;default:gravel]"..
"image[0,0;1,1;techage_form_mask.png]"..
"image[3.5,0;1,1;"..techage.get_power_image(pos, nvm).."]"..
"image[3.5,1;1,1;techage_form_arrow.png]"..
"image_button[3.5,2;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
"tooltip[3.5,2;1,1;"..self:get_state_tooltip(nvm).."]"..
"list[context;dst;5,0;3,3;]"..
"list[current_player;main;0,4;8,4;]"..
"listring[context;dst]"..
"listring[current_player;main]"..
"listring[context;src]"..
"listring[current_player;main]"..
default.get_hotbar_bg(0, 4)
end
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
if listname == "src" then
CRD(pos).State:start_if_standby(pos)
return stack:get_count()
elseif listname == "dst" then
return 0
end
end
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
local inv = M(pos):get_inventory()
local stack = inv:get_stack(from_list, from_index)
return allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
end
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return stack:get_count()
end
local sieving_recipes = {}
function techage.register_sieving_recipe(material, get_ore_func)
sieving_recipes[ItemStack(material)] = get_ore_func
end
techage.register_sieving_recipe("techage:basalt_gravel", get_random_basalt_ore)
techage.register_sieving_recipe("default:gravel", get_random_gravel_ore)
local function sieving(pos, crd, nvm, inv)
for i = 1, crd.num_items do
local src, dst
for input_material, get_ore_func in pairs(sieving_recipes) do
if inv:contains_item("src", ItemStack(input_material)) then
dst, src = get_ore_func(), ItemStack(input_material)
break
end
end
if dst == nil then
crd.State:idle(pos, nvm)
return
end
if not inv:room_for_item("dst", dst) then
crd.State:idle(pos, nvm)
return
end
inv:add_item("dst", dst)
inv:remove_item("src", src)
end
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
end
local function keep_running(pos, elapsed)
local nvm = techage.get_nvm(pos)
local crd = CRD(pos)
local inv = M(pos):get_inventory()
sieving(pos, crd, nvm, inv)
end
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local nvm = techage.get_nvm(pos)
CRD(pos).State:state_button_event(pos, nvm, fields)
end
local function can_dig(pos, player)
if minetest.is_protected(pos, player:get_player_name()) then
return false
end
local inv = M(pos):get_inventory()
return inv:is_empty("dst") and inv:is_empty("src")
end
local tiles = {}
-- '#' will be replaced by the stage number
-- '{power}' will be replaced by the power PNG
tiles.pas = {
-- up, down, right, left, back, front
"techage_appl_sieve_top.png^techage_frame_ta#_top.png",
"techage_filling_ta#.png^techage_frame_ta#_bottom.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
"techage_filling_ta#.png^techage_appl_sieve.png^techage_frame_ta#.png",
"techage_filling_ta#.png^techage_appl_sieve.png^techage_frame_ta#.png",
}
tiles.act = {
-- up, down, right, left, back, front
{
name = "techage_appl_sieve4_top.png^techage_frame4_ta#_top.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 2.0,
},
},
"techage_filling_ta#.png^techage_frame_ta#_bottom.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
"techage_filling_ta#.png^techage_appl_sieve.png^techage_frame_ta#.png",
"techage_filling_ta#.png^techage_appl_sieve.png^techage_frame_ta#.png",
}
local tubing = {
on_pull_item = function(pos, in_dir, num)
local meta = minetest.get_meta(pos)
if meta:get_int("pull_dir") == in_dir then
local inv = M(pos):get_inventory()
return techage.get_items(pos, inv, "dst", num)
end
end,
on_push_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
if meta:get_int("push_dir") == in_dir or in_dir == 5 then
local inv = M(pos):get_inventory()
return techage.put_items(inv, "src", stack)
end
end,
on_unpull_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
if meta:get_int("pull_dir") == in_dir then
local inv = M(pos):get_inventory()
return techage.put_items(inv, "dst", stack)
end
end,
on_recv_message = function(pos, src, topic, payload)
return CRD(pos).State:on_receive_message(pos, topic, payload)
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
return CRD(pos).State:on_beduino_receive_cmnd(pos, topic, payload)
end,
on_beduino_request_data = function(pos, src, topic, payload)
return CRD(pos).State:on_beduino_request_data(pos, topic, payload)
end,
on_node_load = function(pos)
CRD(pos).State:on_node_load(pos)
end,
}
local node_name_ta2, node_name_ta3, node_name_ta4 =
techage.register_consumer("gravelsieve", S("Gravel Sieve"), tiles, {
drawtype = "nodebox",
paramtype = "light",
node_box = {
type = "fixed",
fixed = {
{-8/16, -8/16, -8/16, 8/16, 8/16, -6/16},
{-8/16, -8/16, 6/16, 8/16, 8/16, 8/16},
{-8/16, -8/16, -8/16, -6/16, 8/16, 8/16},
{ 6/16, -8/16, -8/16, 8/16, 8/16, 8/16},
{-6/16, -8/16, -6/16, 6/16, 4/16, 6/16},
},
},
selection_box = {
type = "fixed",
fixed = {-8/16, -8/16, -8/16, 8/16, 8/16, 8/16},
},
cycle_time = CYCLE_TIME,
standby_ticks = STANDBY_TICKS,
formspec = formspec,
tubing = tubing,
after_place_node = function(pos, placer)
local inv = M(pos):get_inventory()
inv:set_size('src', 9)
inv:set_size('dst', 9)
end,
can_dig = can_dig,
node_timer = keep_running,
on_receive_fields = on_receive_fields,
allow_metadata_inventory_put = allow_metadata_inventory_put,
allow_metadata_inventory_move = allow_metadata_inventory_move,
allow_metadata_inventory_take = allow_metadata_inventory_take,
groups = {choppy=2, cracky=2, crumbly=2},
sounds = default.node_sound_wood_defaults(),
num_items = {0,1,2,4},
power_consumption = {0,3,4,5},
tube_sides = {L=1, R=1, U=1},
})
minetest.register_craft({
output = node_name_ta2,
recipe = {
{"group:wood", "default:mese_crystal", "group:wood"},
{"techage:tubeS", "techage:sieve", "techage:tubeS"},
{"group:wood", "techage:iron_ingot", "group:wood"},
},
})
minetest.register_craft({
output = node_name_ta3,
recipe = {
{"", "default:mese_crystal", ""},
{"", node_name_ta2, ""},
{"", "techage:vacuum_tube", ""},
},
})
minetest.register_craft({
output = node_name_ta4,
recipe = {
{"", "default:mese_crystal", ""},
{"", node_name_ta3, ""},
{"", "techage:ta4_wlanchip", ""},
},
})
techage.recipes.register_craft_type("ta2_gravelsieve", {
description = S("TA2 Gravel Sieve"),
icon = 'techage_sieve_sieve_ta1.png',
width = 1,
height = 1,
})
techage.recipes.register_craft_type("ta3_gravelsieve", {
description = S("TA3 Gravel Sieve"),
icon = 'techage_filling_ta3.png^techage_appl_sieve.png^techage_frame_ta3.png',
width = 1,
height = 1,
})
techage.recipes.register_craft_type("ta4_gravelsieve", {
description = S("TA4 Gravel Sieve"),
icon = 'techage_filling_ta4.png^techage_appl_sieve.png^techage_frame_ta4.png',
width = 1,
height = 1,
})
techage.recipes.register_craft({
output = "techage:sieved_basalt_gravel",
items = {"techage:basalt_gravel"},
type = "ta2_gravelsieve",
})
techage.recipes.register_craft({
output = "techage:sieved_basalt_gravel",
items = {"techage:basalt_gravel"},
type = "ta3_gravelsieve",
})
techage.recipes.register_craft({
output = "techage:sieved_basalt_gravel",
items = {"techage:basalt_gravel"},
type = "ta4_gravelsieve",
})
@@ -0,0 +1,435 @@
--[[
TechAge
=======
Copyright (C) 2019-2022 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA2/TA3/TA4 Grinder, grinding Cobble/Basalt to Gravel
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
-- Consumer Related Data
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer or {} end
local STANDBY_TICKS = 3
local COUNTDOWN_TICKS = 4
local CYCLE_TIME = 4
-- Grinder recipes TA1
local RecipesTa1 = {}
-- Grinder recipes TA2 - TA4
local Recipes = {}
local function formspec(self, pos, nvm)
return "size[8,8]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"list[context;src;0,0;3,3;]"..
"item_image[0,0;1,1;default:cobble]"..
"image[0,0;1,1;techage_form_mask.png]"..
"image[3.5,0;1,1;"..techage.get_power_image(pos, nvm).."]"..
"image[3.5,1;1,1;techage_form_arrow.png]"..
"image_button[3.5,2;1,1;"..self:get_state_button_image(nvm)..";state_button;]"..
"tooltip[3.5,2;1,1;"..self:get_state_tooltip(nvm).."]"..
"list[context;dst;5,0;3,3;]"..
"item_image[5,0;1,1;default:gravel]"..
"image[5,0;1,1;techage_form_mask.png]"..
"list[current_player;main;0,4;8,4;]"..
"listring[context;dst]"..
"listring[current_player;main]"..
"listring[context;src]"..
"listring[current_player;main]"..
default.get_hotbar_bg(0, 4)
end
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
if listname == "src" then
local state = CRD(pos).State
if state then
state:start_if_standby(pos)
end
end
return stack:get_count()
end
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
local inv = M(pos):get_inventory()
local stack = inv:get_stack(from_list, from_index)
return allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
end
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return stack:get_count()
end
-- Grinder normaly handles 'num_items' per cycle. 'num_items' is node stage dependent.
-- But if 'inp_num' > 1 (wheat recipes), use 'inp_num' and produce one output item.
local function src_to_dst(src_stack, idx, src_name, num_items, inp_num, inv, dst_name)
if inp_num > 1 then
local input = ItemStack(src_name)
input:set_count(inp_num)
local output = ItemStack(dst_name)
if inv:contains_item("src", input) and inv:room_for_item("dst", output) then
inv:remove_item("src", input)
inv:add_item("dst", output)
return true
end
else
local taken = src_stack:take_item(num_items)
local output = ItemStack(dst_name)
output:set_count(output:get_count() * taken:get_count())
if inv:room_for_item("dst", output) then
inv:set_stack("src", idx, src_stack)
inv:add_item("dst", output)
return true
end
end
return false
end
local function grinding(pos, crd, nvm, inv)
local blocked = false -- idle
for idx,stack in ipairs(inv:get_list("src")) do
if not stack:is_empty() then
local name = stack:get_name()
if Recipes[name] then
local recipe = Recipes[name]
if src_to_dst(stack, idx, name, crd.num_items, recipe.inp_num, inv, recipe.output) then
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
return
else
blocked = true
end
else
crd.State:fault(pos, nvm)
return
end
end
end
if blocked then
crd.State:blocked(pos, nvm)
else
crd.State:idle(pos, nvm)
end
end
local function keep_running(pos, elapsed)
local nvm = techage.get_nvm(pos)
local crd = CRD(pos)
local inv = M(pos):get_inventory()
grinding(pos, crd, nvm, inv)
end
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local nvm = techage.get_nvm(pos)
CRD(pos).State:state_button_event(pos, nvm, fields)
end
local function can_dig(pos, player)
if minetest.is_protected(pos, player:get_player_name()) then
return false
end
local inv = M(pos):get_inventory()
return inv:is_empty("dst") and inv:is_empty("src")
end
local tiles = {}
-- '#' will be replaced by the stage number
-- '{power}' will be replaced by the power PNG
tiles.pas = {
-- up, down, right, left, back, front
"techage_appl_grinder.png^techage_frame_ta#_top.png",
"techage_filling_ta#.png^techage_frame_ta#_bottom.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
"techage_filling_ta#.png^techage_appl_grinder2.png^techage_frame_ta#.png",
"techage_filling_ta#.png^techage_appl_grinder2.png^techage_frame_ta#.png",
}
tiles.act = {
-- up, down, right, left, back, front
{
name = "techage_appl_grinder4.png^techage_frame4_ta#_top.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 1.0,
},
},
"techage_filling_ta#.png^techage_frame_ta#_bottom.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
"techage_filling_ta#.png^techage_appl_grinder2.png^techage_frame_ta#.png",
"techage_filling_ta#.png^techage_appl_grinder2.png^techage_frame_ta#.png",
}
local tubing = {
on_pull_item = function(pos, in_dir, num)
local meta = minetest.get_meta(pos)
if meta:get_int("pull_dir") == in_dir then
local inv = M(pos):get_inventory()
return techage.get_items(pos, inv, "dst", num)
end
end,
on_push_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
if meta:get_int("push_dir") == in_dir or in_dir == 5 then
local inv = M(pos):get_inventory()
--CRD(pos).State:start_if_standby(pos) -- would need power!
return techage.put_items(inv, "src", stack)
end
end,
on_unpull_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
if meta:get_int("pull_dir") == in_dir then
local inv = M(pos):get_inventory()
return techage.put_items(inv, "dst", stack)
end
end,
on_recv_message = function(pos, src, topic, payload)
return CRD(pos).State:on_receive_message(pos, topic, payload)
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
return CRD(pos).State:on_beduino_receive_cmnd(pos, topic, payload)
end,
on_beduino_request_data = function(pos, src, topic, payload)
return CRD(pos).State:on_beduino_request_data(pos, topic, payload)
end,
on_node_load = function(pos)
CRD(pos).State:on_node_load(pos)
end,
}
local node_name_ta2, node_name_ta3, node_name_ta4 =
techage.register_consumer("grinder", S("Grinder"), tiles, {
drawtype = "nodebox",
paramtype = "light",
node_box = {
type = "fixed",
fixed = {
{-8/16, -8/16, -8/16, 8/16, 8/16, -6/16},
{-8/16, -8/16, 6/16, 8/16, 8/16, 8/16},
{-8/16, -8/16, -8/16, -6/16, 8/16, 8/16},
{ 6/16, -8/16, -8/16, 8/16, 8/16, 8/16},
{-6/16, -8/16, -6/16, 6/16, 6/16, 6/16},
},
},
selection_box = {
type = "fixed",
fixed = {-8/16, -8/16, -8/16, 8/16, 8/16, 8/16},
},
cycle_time = CYCLE_TIME,
standby_ticks = STANDBY_TICKS,
formspec = formspec,
tubing = tubing,
after_place_node = function(pos, placer)
local inv = M(pos):get_inventory()
inv:set_size('src', 9)
inv:set_size('dst', 9)
end,
can_dig = can_dig,
node_timer = keep_running,
on_receive_fields = on_receive_fields,
allow_metadata_inventory_put = allow_metadata_inventory_put,
allow_metadata_inventory_move = allow_metadata_inventory_move,
allow_metadata_inventory_take = allow_metadata_inventory_take,
groups = {choppy=2, cracky=2, crumbly=2},
sounds = default.node_sound_wood_defaults(),
num_items = {0,1,2,4},
power_consumption = {0,4,6,9},
tube_sides = {L=1, R=1, U=1},
})
-------------------------------------------------------------------------------
-- TA1 Mill (watermill)
-------------------------------------------------------------------------------
local formspecStr = "size[8,8]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"list[context;src;1,1;1,1;]"..
"item_image[1,1;1,1;farming:wheat]"..
"image[1,1;1,1;techage_form_mask.png]"..
"image[3.5,1;1,1;techage_form_arrow.png]"..
"list[context;dst;6,1;1,1;]"..
"item_image[6,1;1,1;farming:flour]"..
"image[6,1;1,1;techage_form_mask.png]"..
"list[current_player;main;0,4;8,4;]"..
"listring[context;dst]"..
"listring[current_player;main]"..
"listring[context;src]"..
"listring[current_player;main]"..
default.get_hotbar_bg(0, 4)
local function node_timer(pos, elapsed)
if techage.ta1_mill_has_power(pos, 2) then
local inv = M(pos):get_inventory()
local stack = inv:get_stack("src", 1)
if not stack:is_empty() then
local name = stack:get_name()
if RecipesTa1[name] then
local recipe = RecipesTa1[name]
src_to_dst(stack, 1, name, 1, recipe.inp_num, inv, recipe.output)
end
end
end
return true
end
minetest.register_node("techage:ta1_mill_base", {
description = S("TA1 Mill Base"),
tiles = {
"techage_mill_base.png",
"default_stone_brick.png",
},
after_place_node = function(pos, placer)
M(pos):set_string("formspec", formspecStr)
local inv = M(pos):get_inventory()
inv:set_size('src', 1)
inv:set_size('dst', 1)
minetest.get_node_timer(pos):start(4)
end,
can_dig = can_dig,
on_timer = node_timer,
allow_metadata_inventory_put = allow_metadata_inventory_take,
allow_metadata_inventory_move = allow_metadata_inventory_move,
allow_metadata_inventory_take = allow_metadata_inventory_take,
is_ground_content = false,
groups = {cracky = 2, crumbly = 2, choppy = 2},
})
techage.register_node({"techage:ta1_mill_base"}, {
on_node_load = function(pos, node)
minetest.get_node_timer(pos):start(4)
end,
})
minetest.register_craft({
output = "techage:ta1_mill_base",
recipe = {
{"default:stonebrick", "", "default:stonebrick"},
{"", "techage:iron_ingot", ""},
{"default:stonebrick", "", "default:stonebrick"},
},
})
minetest.register_craft({
output = node_name_ta2,
recipe = {
{"group:wood", "default:mese_crystal", "group:wood"},
{"techage:tubeS", "techage:hammer_steel", "techage:tubeS"},
{"group:wood", "techage:iron_ingot", "group:wood"},
},
})
minetest.register_craft({
output = node_name_ta3,
recipe = {
{"", "default:mese_crystal", ""},
{"", node_name_ta2, ""},
{"", "techage:vacuum_tube", ""},
},
})
minetest.register_craft({
output = node_name_ta4,
recipe = {
{"", "default:mese_crystal", ""},
{"", node_name_ta3, ""},
{"", "techage:ta4_wlanchip", ""},
},
})
techage.recipes.register_craft_type("grinding", {
description = S("Grinding"),
icon = 'techage_appl_grinder.png',
width = 2,
height = 2,
})
techage.recipes.register_craft_type("milling", {
description = S("Milling"),
icon = 'techage_mill_inv.png',
width = 2,
height = 2,
})
function techage.add_grinder_recipe(recipe, ta1_permitted)
local name, num = unpack(string.split(recipe.input, " ", false, 1))
if minetest.registered_items[name] then
if ta1_permitted then
RecipesTa1[name] = {input = name,inp_num = tonumber(num) or 1, output = recipe.output}
recipe.items = {recipe.input}
recipe.type = "milling"
techage.recipes.register_craft(table.copy(recipe))
end
Recipes[name] = {input = name,inp_num = tonumber(num) or 1, output = recipe.output}
recipe.items = {recipe.input}
recipe.type = "grinding"
techage.recipes.register_craft(recipe)
end
end
techage.add_grinder_recipe({input="default:cobble", output="default:gravel"})
techage.add_grinder_recipe({input="default:desert_cobble", output="default:gravel"})
techage.add_grinder_recipe({input="default:mossycobble", output="default:gravel"})
techage.add_grinder_recipe({input="default:gravel", output="default:sand"})
techage.add_grinder_recipe({input="techage:sieved_gravel", output="default:sand"})
techage.add_grinder_recipe({input="default:coral_skeleton", output="default:silver_sand"})
if minetest.global_exists("skytest") then
techage.add_grinder_recipe({input="default:desert_sand", output="skytest:dust"})
techage.add_grinder_recipe({input="default:silver_sand", output="skytest:dust"})
techage.add_grinder_recipe({input="default:sand", output="skytest:dust"})
else
techage.add_grinder_recipe({input="default:desert_sand", output="default:clay"})
techage.add_grinder_recipe({input="default:silver_sand", output="default:clay"})
techage.add_grinder_recipe({input="default:sand", output="default:clay"})
end
techage.add_grinder_recipe({input="default:sandstone", output="default:sand 4"})
techage.add_grinder_recipe({input="default:desert_sandstone", output="default:desert_sand 4"})
techage.add_grinder_recipe({input="default:silver_sandstone", output="default:silver_sand 4"})
techage.add_grinder_recipe({input="default:tree", output="default:leaves 8"})
techage.add_grinder_recipe({input="default:jungletree", output="default:jungleleaves 8"})
techage.add_grinder_recipe({input="default:pine_tree", output="default:pine_needles 8"})
techage.add_grinder_recipe({input="default:acacia_tree", output="default:acacia_leaves 8"})
techage.add_grinder_recipe({input="default:aspen_tree", output="default:aspen_leaves 8"})
if minetest.global_exists("farming") then
techage.add_grinder_recipe({input="farming:wheat 3", output="farming:flour"}, true)
techage.add_grinder_recipe({input="farming:seed_wheat 6", output="farming:flour"}, true)
techage.add_grinder_recipe({input="farming:barley 3", output="farming:flour"}, true)
techage.add_grinder_recipe({input="farming:seed_barley 6", output="farming:flour"}, true)
techage.add_grinder_recipe({input="farming:rye 3", output="farming:flour"}, true)
techage.add_grinder_recipe({input="farming:seed_rye 6", output="farming:flour"}, true)
techage.add_grinder_recipe({input="farming:rice 3", output="farming:rice_flour"}, true)
techage.add_grinder_recipe({input="farming:seed_rice 6", output="farming:rice_flour"}, true)
techage.add_grinder_recipe({input="farming:oat 3", output="farming:flour"}, true)
techage.add_grinder_recipe({input="farming:seed_oat 6", output="farming:flour"}, true)
techage.add_grinder_recipe({input="farming:seed_cotton 3", output="basic_materials:oil_extract"}, true)
end
@@ -0,0 +1,106 @@
--[[
TechAge
=======
Copyright (C) 2019-2020 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
Item Source Block
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local CYCLE_TIME = 30
local function formspec()
return "size[8,7.2]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"list[context;main;3.5,0.8;1,1;]"..
"list[current_player;main;0,3.5;8,4;]"..
"listring[context;main]"..
"listring[current_player;main]"
end
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return stack:get_count()
end
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return stack:get_count()
end
minetest.register_node("techage:itemsource", {
description = "Techage Item Source",
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png^techage_appl_arrow.png",
"techage_filling_ta3.png^techage_frame_ta3.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_outp.png",
"techage_filling_ta3.png^techage_frame_ta3.png",
"techage_filling_ta3.png^techage_appl_nodedetector.png^techage_frame_ta3.png",
"techage_filling_ta3.png^techage_appl_nodedetector.png^techage_frame_ta3.png",
},
after_place_node = function(pos, placer)
local meta = M(pos)
local node = minetest.get_node(pos)
meta:set_int("push_dir", techage.side_to_outdir("R", node.param2))
local inv = meta:get_inventory()
inv:set_size('main', 1)
minetest.get_node_timer(pos):start(CYCLE_TIME)
meta:set_string("infotext", "Techage Item Source")
meta:set_string("formspec", formspec())
end,
on_timer = function(pos, elapsed)
local meta = M(pos)
local inv = meta:get_inventory()
local stack = inv:get_stack('main', 1)
if stack:get_count() > 0 then
local push_dir = meta:get_int("push_dir")
local leftover = techage.push_items(pos, push_dir, stack)
local pushed
if not leftover then
pushed = 0
elseif leftover ~= true then
pushed = stack:get_count() - leftover:get_count()
else -- leftover == true
pushed = stack:get_count()
end
meta:set_int("counter", pushed)
meta:set_string("infotext", "Techage Item Source: "..pushed)
end
return true
end,
allow_metadata_inventory_put = allow_metadata_inventory_put,
allow_metadata_inventory_take = allow_metadata_inventory_take,
paramtype2 = "facedir", -- important!
on_rotate = screwdriver.disallow, -- important!
is_ground_content = false,
drop = "",
groups = {crumbly = 3, cracky = 3, snappy = 3},
sounds = default.node_sound_glass_defaults(),
})
techage.register_node({"techage:itemsource"}, {
on_node_load = function(pos)
minetest.get_node_timer(pos):start(CYCLE_TIME)
end,
})
@@ -0,0 +1,228 @@
--[[
TechAge
=======
Copyright (C) 2019-2022 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA2/TA3 Bucket based Liquid Sampler
]]--
-- for lazy programmers
local M = minetest.get_meta
-- Consumer Related Data
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end
local S = techage.S
local STANDBY_TICKS = 2
local COUNTDOWN_TICKS = 3
local CYCLE_TIME = 8
local function formspec(self, pos, nvm)
return "size[9,8.5]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"list[context;src;0,0;1,4;]"..
"image[0,0;1,1;bucket.png]"..
"image[1,0;1,1;"..techage.get_power_image(pos, nvm).."]"..
"image[1,1.5;1,1;techage_form_arrow.png]"..
"image_button[1,3;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
"tooltip[1,3;1,1;"..self:get_state_tooltip(nvm).."]"..
"list[context;dst;2,0;7,4;]"..
"list[current_player;main;0.5,4.5;8,4;]"..
"listring[current_player;main]"..
"listring[context;src]" ..
"listring[current_player;main]"..
"listring[context;dst]" ..
"listring[current_player;main]"
end
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
if listname == "src" then
CRD(pos).State:start_if_standby(pos)
end
return stack:get_count()
end
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
local inv = M(pos):get_inventory()
local stack = inv:get_stack(from_list, from_index)
return allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
end
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return stack:get_count()
end
local function is_water(pos)
local node = minetest.get_node(pos)
local liquiddef = bucket.liquids[node.name]
if liquiddef ~= nil and liquiddef.itemname ~= nil and node.name == liquiddef.source then
return true
end
end
local function can_start(pos, nvm, state)
local water_pos = minetest.string_to_pos(M(pos):get_string("water_pos"))
if not is_water(water_pos) then
return S("no usable water")
end
return true
end
local function sample_liquid(pos, crd, nvm, inv)
if inv:room_for_item("dst", {name = "bucket:bucket_water"}) and
inv:contains_item("src", {name = "bucket:bucket_empty"}) then
inv:remove_item("src", {name = "bucket:bucket_empty"})
inv:add_item("dst", {name = "bucket:bucket_water"})
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
else
crd.State:idle(pos, nvm)
end
end
local function keep_running(pos, elapsed)
--if tubelib.data_not_corrupted(pos) then
local nvm = techage.get_nvm(pos)
local crd = CRD(pos)
local inv = M(pos):get_inventory()
sample_liquid(pos, crd, nvm, inv)
end
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local nvm = techage.get_nvm(pos)
CRD(pos).State:state_button_event(pos, nvm, fields)
end
local function can_dig(pos, player)
if minetest.is_protected(pos, player:get_player_name()) then
return false
end
local inv = M(pos):get_inventory()
return inv:is_empty("dst") and inv:is_empty("src")
end
local tiles = {}
-- '#' will be replaced by the stage number
-- '{power}' will be replaced by the power PNG
tiles.pas = {
-- up, down, right, left, back, front
"techage_filling_ta#.png^{power}^techage_frame_ta#_top.png",
"techage_filling_ta#.png^techage_frame_ta#_bottom.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
"techage_filling_ta#.png^techage_liquidsampler.png^techage_frame_ta#.png",
"techage_filling_ta#.png^techage_appl_liquidsampler.png^techage_frame_ta#.png",
}
tiles.act = {
-- up, down, right, left, back, front
"techage_filling_ta#.png^{power}^techage_frame_ta#_top.png",
"techage_filling_ta#.png^techage_frame_ta#_bottom.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
{
name = "techage_filling4_ta#.png^techage_liquidsampler4.png^techage_frame4_ta#.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 1.0,
},
},
"techage_filling_ta#.png^techage_appl_liquidsampler.png^techage_frame_ta#.png",
}
local tubing = {
on_pull_item = function(pos, in_dir, num)
local meta = minetest.get_meta(pos)
if meta:get_int("pull_dir") == in_dir then
local inv = M(pos):get_inventory()
return techage.get_items(pos, inv, "dst", num)
end
end,
on_push_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
if meta:get_int("push_dir") == in_dir then
local inv = M(pos):get_inventory()
return techage.put_items(inv, "src", stack)
end
end,
on_unpull_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
if meta:get_int("pull_dir") == in_dir then
local inv = M(pos):get_inventory()
return techage.put_items(inv, "dst", stack)
end
end,
on_recv_message = function(pos, src, topic, payload)
return CRD(pos).State:on_receive_message(pos, topic, payload)
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
return CRD(pos).State:on_beduino_receive_cmnd(pos, topic, payload)
end,
on_beduino_request_data = function(pos, src, topic, payload)
return CRD(pos).State:on_beduino_request_data(pos, topic, payload)
end,
}
local node_name_ta2, node_name_ta3, _ =
techage.register_consumer("liquidsampler", S("Liquid Sampler"), tiles, {
cycle_time = CYCLE_TIME,
standby_ticks = STANDBY_TICKS,
formspec = formspec,
tubing = tubing,
can_start = can_start,
after_place_node = function(pos, placer)
local inv = M(pos):get_inventory()
inv:set_size("src", 4)
inv:set_size("dst", 28)
local water_pos = techage.get_pos(pos, "B")
M(pos):set_string("water_pos", minetest.pos_to_string(water_pos))
end,
can_dig = can_dig,
node_timer = keep_running,
on_receive_fields = on_receive_fields,
allow_metadata_inventory_put = allow_metadata_inventory_put,
allow_metadata_inventory_move = allow_metadata_inventory_move,
allow_metadata_inventory_take = allow_metadata_inventory_take,
groups = {choppy=2, cracky=2, crumbly=2},
sounds = default.node_sound_wood_defaults(),
num_items = {0,1,2,4},
power_consumption = {0,3,5,8},
power_sides = {U=1},
},
{false, true, true, false}) -- TA2/A3
minetest.register_craft({
output = node_name_ta2,
recipe = {
{"group:wood", "default:mese_crystal", "group:wood"},
{"techage:tubeS", "bucket:bucket_empty", "techage:tubeS"},
{"group:wood", "techage:iron_ingot", "group:wood"},
},
})
minetest.register_craft({
output = node_name_ta3,
recipe = {
{"", "default:mese_crystal", ""},
{"", node_name_ta2, ""},
{"", "techage:vacuum_tube", ""},
},
})
@@ -0,0 +1,131 @@
--[[
TechAge
=======
Copyright (C) 2019-2020 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
Tube support for digtron and protector chests
]]--
-- for lazy programmers
local M = minetest.get_meta
local CacheForFuelNodeNames = {}
local function is_fuel(stack)
local name = stack:get_name()
if CacheForFuelNodeNames[name] then
return true
end
if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then
CacheForFuelNodeNames[name] = true
end
return CacheForFuelNodeNames[name]
end
------------------------------------------------------------------------------
-- digtron
------------------------------------------------------------------------------
techage.register_node({"digtron:inventory"}, {
on_inv_request = function(pos, in_dir, access_type)
local meta = minetest.get_meta(pos)
return meta:get_inventory(), "main"
end,
on_pull_item = function(pos, in_dir, num)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.get_items(pos, inv, "main", num)
end,
on_push_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.put_items(inv, "main", stack)
end,
on_unpull_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.put_items(inv, "main", stack)
end,
})
techage.register_node({"digtron:fuelstore"}, {
on_inv_request = function(pos, in_dir, access_type)
local meta = minetest.get_meta(pos)
return meta:get_inventory(), "fuel"
end,
on_pull_item = function(pos, in_dir, num)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.get_items(pos, inv, "fuel", num)
end,
on_push_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.put_items(inv, "fuel", stack)
end,
on_unpull_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.put_items(inv, "fuel", stack)
end,
})
techage.register_node({"digtron:combined_storage"}, {
on_inv_request = function(pos, in_dir, access_type)
local meta = minetest.get_meta(pos)
return meta:get_inventory(), "main"
end,
on_pull_item = function(pos, in_dir, num)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.get_items(pos, inv, "main", num)
end,
on_push_item = function(pos, side, stack)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
minetest.get_node_timer(pos):start(1.0)
if is_fuel(stack) then
return techage.put_items(inv, "fuel", stack)
else
return techage.put_items(inv, "main", stack)
end
end,
on_unpull_item = function(pos, side, stack)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.put_items(inv, "main", stack)
end,
})
------------------------------------------------------------------------------
-- protector
------------------------------------------------------------------------------
techage.register_node({"protector:chest"}, {
on_inv_request = function(pos, in_dir, access_type)
local meta = minetest.get_meta(pos)
return meta:get_inventory(), "main"
end,
on_pull_item = function(pos, in_dir, num)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.get_items(pos, inv, "main", num)
end,
on_push_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.put_items(inv, "main", stack)
end,
on_unpull_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return techage.put_items(inv, "main", stack)
end,
})
@@ -0,0 +1,438 @@
--[[
TechAge
=======
Copyright (C) 2019-2022 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA2/TA3/TA4 Pusher
Nodes for push/pull operation of StackItems from chests or other
inventory/server nodes to tubes or other inventory/server nodes.
+--------+
/ /|
+--------+ |
IN (L) -->| |X--> OUT (R)
| PUSHER | +
| |/
+--------+
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
-- Consumer Related Data
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end
local Tube = techage.Tube
local STANDBY_TICKS = 2
local COUNTDOWN_TICKS = 4
local CYCLE_TIME = 2
local WRENCH_MENU = {
{
type = "number",
name = "limit",
label = S("Number of items"),
tooltip = S("Number of items that are allowed to be pushed"),
default = "0",
},
}
local function ta4_formspec(self, pos, nvm)
if CRD(pos).stage == 4 then -- TA4 node?
return "size[8,7.2]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"box[0,-0.1;7.8,0.5;#c6e8ff]"..
"label[3,-0.1;"..minetest.colorize("#000000", S("Pusher")).."]"..
techage.question_mark_help(7.5, S("Optionally configure\nthe pusher with one item"))..
techage.wrench_image(7.4, -0.05) ..
"list[context;main;3.5,0.8;1,1;]"..
"image_button[3.5,2;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
"tooltip[3.5,2;1,1;"..self:get_state_tooltip(nvm).."]"..
"list[current_player;main;0,3.5;8,4;]"..
"listring[context;main]"..
"listring[current_player;main]"
end
end
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
local nvm = techage.get_nvm(pos)
if CRD(pos).State:get_state(nvm) ~= techage.STOPPED then
return 0
end
local inv = M(pos):get_inventory()
local list = inv:get_list(listname)
if list[index]:get_count() == 0 then
stack:set_count(1)
inv:set_stack(listname, index, stack)
return 0
end
return 0
end
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
local nvm = techage.get_nvm(pos)
if CRD(pos).State:get_state(nvm) ~= techage.STOPPED then
return 0
end
local inv = M(pos):get_inventory()
inv:set_stack(listname, index, nil)
return 0
end
local function set_limit(pos, nvm, val)
val = tonumber(val) or 0
if val > 0 then
nvm.limit = val
nvm.num_items = 0
M(pos):set_int("limit", val)
else
nvm.limit = nil
nvm.num_items = nil
M(pos):set_string("limit", "")
end
end
-- Function returns the number of pushed items
local function push(pos, crd, meta, nvm, pull_dir, push_dir, num)
local items = techage.pull_items(pos, pull_dir, num, nvm.item_name)
if items ~= nil then
local taken = items:get_count()
local leftover = techage.push_items(pos, push_dir, items)
if not leftover then
-- place item back
techage.unpull_items(pos, pull_dir, items)
crd.State:blocked(pos, nvm)
return 0
elseif leftover ~= true then
-- place item back
taken = taken - leftover:get_count()
techage.unpull_items(pos, pull_dir, leftover)
crd.State:blocked(pos, nvm)
return taken
end
return taken
end
crd.State:idle(pos, nvm)
return 0
end
local function pushing(pos, crd, meta, nvm)
local pull_dir = meta:get_int("pull_dir")
local push_dir = meta:get_int("push_dir")
if not nvm.limit then
local num = nvm.item_count or nvm.num_items or crd.num_items
num = push(pos, crd, meta, nvm, pull_dir, push_dir, num)
if num > 0 then
if nvm.item_count then
nvm.item_count = nvm.item_count - num
if nvm.item_count <= 0 then
crd.State:stop(pos, nvm)
nvm.item_count = nil
end
end
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
end
elseif nvm.num_items < nvm.limit then
local num = math.min(crd.num_items, nvm.limit - nvm.num_items)
num = push(pos, crd, meta, nvm, pull_dir, push_dir, num)
if num > 0 then
nvm.num_items = nvm.num_items + num
if nvm.num_items >= nvm.limit then
crd.State:stop(pos, nvm)
else
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
end
end
end
end
local function keep_running(pos, elapsed)
local nvm = techage.get_nvm(pos)
local crd = CRD(pos)
pushing(pos, crd, M(pos), nvm)
crd.State:is_active(nvm)
end
local function on_rightclick(pos, node, clicker)
if CRD(pos).stage ~= 4 then -- Not TA4 node?
local nvm = techage.get_nvm(pos)
if not minetest.is_protected(pos, clicker:get_player_name()) then
if CRD(pos).State:get_state(nvm) == techage.STOPPED then
CRD(pos).State:start(pos, nvm)
else
CRD(pos).State:stop(pos, nvm)
end
end
end
end
local function on_receive_fields(pos, formname, fields, player)
if CRD(pos).stage == 4 then -- TA4 node?
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local nvm = techage.get_nvm(pos)
CRD(pos).State:state_button_event(pos, nvm, fields)
M(pos):set_string("formspec", ta4_formspec(CRD(pos).State, pos, nvm))
end
end
local function tubelib2_on_update2(pos, outdir, tlib2, node)
local pull_dir = M(pos):get_int("pull_dir")
local push_dir = M(pos):get_int("push_dir")
local is_ta4_tube = true
for i, pos, node in Tube:get_tube_line(pos, pull_dir) do
is_ta4_tube = is_ta4_tube and techage.TA4tubes[node.name]
end
for i, pos, node in Tube:get_tube_line(pos, push_dir) do
is_ta4_tube = is_ta4_tube and techage.TA4tubes[node.name]
end
local nvm = techage.get_nvm(pos)
local crd = CRD(pos)
if CRD(pos).stage == 4 and not is_ta4_tube then
nvm.num_items = crd.num_items / 2
else
nvm.num_items = crd.num_items
end
end
local function can_start(pos, nvm, state)
if CRD(pos).stage == 4 then -- TA4 node?
local inv = M(pos):get_inventory()
local name = inv:get_stack("main", 1):get_name()
if name ~= "" then
nvm.item_name = name
else
nvm.item_name = nil
end
else
nvm.item_name = nil
end
return true
end
local function ta_after_formspec(pos, fields, playername)
local nvm = techage.get_nvm(pos)
set_limit(pos, nvm, fields.limit)
end
local function on_state_change(pos, old_state, new_state)
if old_state == techage.STOPPED and new_state == techage.RUNNING then
local nvm = techage.get_nvm(pos)
set_limit(pos, nvm, M(pos):get_int("limit"))
end
end
local function config_item(pos, payload)
if type(payload) == "string" then
if payload == "" then
local inv = M(pos):get_inventory()
inv:set_stack("main", 1, nil)
return 0
else
local name, count = unpack(payload:split(" "))
if name and (minetest.registered_nodes[name] or minetest.registered_items[name]
or minetest.registered_craftitems[name]) then
count = tonumber(count) or 1
local inv = M(pos):get_inventory()
inv:set_stack("main", 1, {name = name, count = 1})
return count
end
end
end
return 0
end
local tiles = {}
-- '#' will be replaced by the stage number
-- '{power}' will be replaced by the power PNG
tiles.pas = {
"techage_filling_ta#.png^techage_frame_ta#_top.png^techage_appl_arrow.png",
"techage_filling_ta#.png^techage_frame_ta#_bottom.png^techage_appl_arrow.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
"techage_appl_pusher.png^[transformR180]^techage_frame_ta#.png",
"techage_appl_pusher.png^techage_frame_ta#.png",
}
tiles.act = {
-- up, down, right, left, back, front
"techage_filling_ta#.png^techage_frame_ta#_top.png^techage_appl_arrow.png",
"techage_filling_ta#.png^techage_frame_ta#_bottom.png^techage_appl_arrow.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
{
name = "techage_appl_pusher14.png^[transformR180]^techage_frame14_ta#.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 2.0,
},
},
{
name = "techage_appl_pusher14.png^techage_frame14_ta#.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 2.0,
},
},
}
local tubing = {
-- push item through the pusher in opposit direction
on_push_item = function(pos, in_dir, stack)
return in_dir == M(pos):get_int("pull_dir") and techage.safe_push_items(pos, in_dir, stack)
end,
is_pusher = true, -- is a pulling/pushing node
on_recv_message = function(pos, src, topic, payload)
if topic == "pull" then -- Deprecated command, use config/limit/start instead
local nvm = techage.get_nvm(pos)
CRD(pos).State:stop(pos, nvm)
nvm.item_count = math.min(config_item(pos, payload), 12)
nvm.rmt_num = src
CRD(pos).State:start(pos, nvm)
return true
elseif topic == "config" then -- Set item type
local nvm = techage.get_nvm(pos)
CRD(pos).State:stop(pos, nvm)
config_item(pos, payload)
return true
elseif topic == "limit" then -- Set push limit
local nvm = techage.get_nvm(pos)
CRD(pos).State:stop(pos, nvm)
set_limit(pos, nvm, payload)
return true
elseif topic == "count" then -- Get number of push items
local nvm = techage.get_nvm(pos)
return nvm.num_items or 0
else
return CRD(pos).State:on_receive_message(pos, topic, payload)
end
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
if topic == 65 then -- Set item type
local nvm = techage.get_nvm(pos)
CRD(pos).State:stop(pos, nvm)
config_item(pos, payload)
return 0
elseif topic == 68 or topic == 20 then -- Set push limit
local nvm = techage.get_nvm(pos)
CRD(pos).State:stop(pos, nvm)
set_limit(pos, nvm, payload[1])
return 0
else
local nvm = techage.get_nvm(pos)
if nvm.limit then
nvm.num_items = 0
end
return CRD(pos).State:on_beduino_receive_cmnd(pos, topic, payload)
end
end,
on_beduino_request_data = function(pos, src, topic, payload)
if topic == 150 then -- Get number of pushed items
local nvm = techage.get_nvm(pos)
return 0, {nvm.num_items or 0}
else
return CRD(pos).State:on_beduino_request_data(pos, topic, payload)
end
end,
}
local node_name_ta2, node_name_ta3, node_name_ta4 =
techage.register_consumer("pusher", S("Pusher"), tiles, {
cycle_time = CYCLE_TIME,
standby_ticks = STANDBY_TICKS,
formspec = ta4_formspec,
tubing = tubing,
can_start = can_start,
on_state_change = on_state_change,
after_place_node = function(pos, placer)
local meta = M(pos)
local node = minetest.get_node(pos)
meta:set_int("pull_dir", techage.side_to_outdir("L", node.param2))
meta:set_int("push_dir", techage.side_to_outdir("R", node.param2))
if CRD(pos).stage == 4 then -- TA4 node?
local inv = M(pos):get_inventory()
inv:set_size('main', 1)
local nvm = techage.get_nvm(pos)
M(pos):set_string("formspec", ta4_formspec(CRD(pos).State, pos, nvm))
end
end,
ta_rotate_node = function(pos, node, new_param2)
Tube:after_dig_node(pos)
minetest.swap_node(pos, {name = node.name, param2 = new_param2})
Tube:after_place_node(pos)
local meta = M(pos)
meta:set_int("pull_dir", techage.side_to_outdir("L", new_param2))
meta:set_int("push_dir", techage.side_to_outdir("R", new_param2))
end,
allow_metadata_inventory_put = allow_metadata_inventory_put,
allow_metadata_inventory_take = allow_metadata_inventory_take,
on_rightclick = on_rightclick,
on_receive_fields = on_receive_fields,
node_timer = keep_running,
on_rotate = screwdriver.disallow,
tubelib2_on_update2 = tubelib2_on_update2,
ta4_formspec = WRENCH_MENU,
ta_after_formspec = ta_after_formspec,
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
num_items = {0,2,6,12},
tube_sides = {L=1, R=1},
})
minetest.register_craft({
output = node_name_ta2.." 2",
recipe = {
{"group:wood", "wool:dark_green", "group:wood"},
{"techage:tubeS", "default:mese_crystal", "techage:tubeS"},
{"group:wood", "techage:iron_ingot", "group:wood"},
},
})
minetest.register_craft({
output = node_name_ta3,
recipe = {
{"", "techage:iron_ingot", ""},
{"", node_name_ta2, ""},
{"", "techage:vacuum_tube", ""},
},
})
minetest.register_craft({
output = node_name_ta4,
recipe = {
{"", "techage:iron_ingot", ""},
{"", node_name_ta3, ""},
{"", "techage:ta4_wlanchip", ""},
},
})
@@ -0,0 +1,517 @@
--[[
TechAge
=======
Copyright (C) 2019-2023 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
Quarry machine to dig stones and other ground blocks.
The Quarry digs a hole (default) 5x5 blocks large and up to 80 blocks deep.
It starts at the given level (0 is same level as the quarry block,
1 is one level higher and so on)) and goes down to the given depth number.
It digs one block every 4 seconds.
]]--
-- for lazy programmers
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local S2P = minetest.string_to_pos
local M = minetest.get_meta
-- Consumer Related Data
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end
local S = techage.S
-- Per-player active quarry registry (mod_storage key: "q_<pos>" → owner)
-- Uses the shared techage.storage with "q_" prefix to avoid collisions
local function quarry_reg_add(pos, owner)
techage.storage:set_string("q_" .. P2S(pos), owner)
end
local function quarry_reg_remove(pos)
techage.storage:set_string("q_" .. P2S(pos), "")
end
local function quarry_count_running(owner)
local count = 0
for key, val in pairs(techage.storage:to_table().fields) do
if val == owner and key:sub(1, 2) == "q_" then
local pos = minetest.string_to_pos(key:sub(3))
if pos then
local nvm = techage.get_nvm(pos)
if nvm.techage_state == techage.RUNNING then
count = count + 1
else
-- Stale entry (e.g. after server restart), clean up
techage.storage:set_string(key, "")
end
end
end
end
return count
end
local CYCLE_TIME = 4
local STANDBY_TICKS = 4
local COUNTDOWN_TICKS = 4
local Side2Facedir = {F=0, R=1, B=2, L=3, D=4, U=5}
local Depth2Idx = {[1]=1 ,[2]=2, [3]=3, [5]=4, [7]=5, [10]=6, [15]=7, [20]=8, [25]=9, [40]=10, [60]=11, [80]=12}
local Holesize2Idx = {["3x3"] = 1, ["5x5"] = 2, ["7x7"] = 3, ["9x9"] = 4, ["11x11"] = 5}
local Holesize2Diameter = {["3x3"] = 3, ["5x5"] = 5, ["7x7"] = 7, ["9x9"] = 9, ["11x11"] = 11}
local Level2Idx = {[2]=1, [1]=2, [0]=3, [-1]=4, [-2]=5, [-3]=6,
[-5]=7, [-10]=8, [-15]=9, [-20]=10}
local function formspec(self, pos, nvm)
local tooltip = S("Start level = 0\nmeans the same level\nas the quarry is placed")
local level_idx = Level2Idx[nvm.start_level or 1] or 2
local depth_idx = Depth2Idx[nvm.quarry_depth or 1] or 1
local hsize_idx = Holesize2Idx[nvm.hole_size or "5x5"] or 2
local level = nvm.level or "-"
local hsize_list = "5x5"
if CRD(pos).stage == 4 then
hsize_list = "3x3,5x5,7x7,9x9,11x11"
elseif CRD(pos).stage == 3 then
hsize_list = "3x3,5x5,7x7"
end
local depth_list = "1,2,3,5,7,10,15,20,25,40,60,80"
if CRD(pos).stage == 3 then
depth_list = "1,2,3,5,7,10,15,20,25,40"
elseif CRD(pos).stage == 2 then
depth_list = "1,2,3,5,7,10,15,20"
end
return "size[8,8]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"box[0,-0.1;7.8,0.5;#c6e8ff]"..
"label[3.5,-0.1;"..minetest.colorize( "#000000", S("Quarry")).."]"..
techage.question_mark_help(8, tooltip)..
"dropdown[0,0.8;1.5;level;2,1,0,-1,-2,-3,-5,-10,-15,-20;"..level_idx.."]"..
"label[1.6,0.9;"..S("Start level").."]"..
"dropdown[0,1.8;1.5;depth;"..depth_list..";"..depth_idx.."]"..
"label[1.6,1.9;"..S("Digging depth").." ("..level..")]"..
"dropdown[0,2.8;1.5;hole_size;"..hsize_list..";"..hsize_idx.."]"..
"label[1.6,2.9;"..S("Hole size").."]"..
"list[context;main;5,0.8;3,3;]"..
"image[4,0.8;1,1;"..techage.get_power_image(pos, nvm).."]"..
"image_button[4,2.8;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
"tooltip[4,2.8;1,1;"..self:get_state_tooltip(nvm).."]"..
"list[current_player;main;0,4.3;8,4;]"..
"listring[context;main]"..
"listring[current_player;main]"
end
local function play_sound(pos)
local mem = techage.get_mem(pos)
-- Stop any lingering handle first, so newly arrived clients get the fresh sound
if mem.handle then
minetest.sound_stop(mem.handle)
mem.handle = nil
end
mem.handle = minetest.sound_play("techage_quarry", {
pos = pos,
gain = 1.5,
max_hear_distance = 15,
loop = true})
if mem.handle == -1 then
mem.handle = nil
minetest.after(1, play_sound, pos)
end
end
local function stop_sound(pos)
local mem = techage.get_mem(pos)
if mem.handle then
minetest.sound_stop(mem.handle)
-- Keep handle: on_node_load can re-send stop to clients that came back in range
end
end
local function on_node_state_change(pos, old_state, new_state)
local mem = techage.get_mem(pos)
local owner = M(pos):get_string("owner")
mem.co = nil
techage.unmark_position(owner)
if new_state == techage.RUNNING then
play_sound(pos)
quarry_reg_add(pos, owner)
else
stop_sound(pos)
quarry_reg_remove(pos)
end
end
local function get_pos(pos, facedir, side, steps)
facedir = (facedir + Side2Facedir[side]) % 4
local dir = vector.multiply(minetest.facedir_to_dir(facedir), steps or 1)
return vector.add(pos, dir)
end
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return stack:get_count()
end
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return stack:get_count()
end
local function get_quarry_pos(pos, xoffs, zoffs)
return {x = pos.x + xoffs - 1, y = pos.y, z = pos.z + zoffs - 1}
end
-- pos is the quarry pos
local function get_corner_positions(pos, facedir, hole_diameter)
local _pos = get_pos(pos, facedir, "L")
local pos1 = get_pos(_pos, facedir, "F", math.floor((hole_diameter - 1) / 2))
local pos2 = get_pos(_pos, facedir, "B", math.floor((hole_diameter - 1) / 2))
pos2 = get_pos(pos2, facedir, "L", hole_diameter - 1)
if pos1.x > pos2.x then pos1.x, pos2.x = pos2.x, pos1.x end
if pos1.y > pos2.y then pos1.y, pos2.y = pos2.y, pos1.y end
if pos1.z > pos2.z then pos1.z, pos2.z = pos2.z, pos1.z end
return pos1, pos2
end
local function is_air_level(pos1, pos2, hole_diameter)
return #minetest.find_nodes_in_area(pos1, pos2, {"air"}) == hole_diameter * hole_diameter
end
local function mark_area(pos1, pos2, owner)
pos1.y = pos1.y + 0.2
techage.mark_cube(owner, pos1, pos2, "quarry", "#FF0000", 20)
pos1.y = pos1.y - 0.2
end
local function quarry_task(pos, crd, nvm)
nvm.start_level = nvm.start_level or 0
nvm.quarry_depth = nvm.quarry_depth or 1
nvm.hole_diameter = nvm.hole_diameter or 5
local y_first = pos.y + nvm.start_level
local y_last = y_first - nvm.quarry_depth + 1
local facedir = minetest.get_node(pos).param2
local owner = M(pos):get_string("owner")
local fake_player = techage.Fake_player:new()
fake_player.get_pos = function (...)
return pos
end
fake_player.get_inventory = function(...)
return M(pos):get_inventory()
end
local add_to_inv = function(itemstacks)
local at_least_one_added = false
local inv = M(pos):get_inventory()
if #itemstacks == 0 then
return true
end
for _,stack in ipairs(itemstacks) do
if inv:room_for_item("main", stack) then
inv:add_item("main", stack)
at_least_one_added = true
elseif at_least_one_added then
minetest.add_item({x=pos.x,y=pos.y+1,z=pos.z}, stack)
end
end
return at_least_one_added
end
local pos1, pos2 = get_corner_positions(pos, facedir, nvm.hole_diameter)
nvm.level = 1
for y_curr = y_first, y_last, -1 do
pos1.y = y_curr
pos2.y = y_curr
-- Restarting the server can detach the coroutine data.
-- Therefore, read nvm again.
nvm = techage.get_nvm(pos)
nvm.level = y_first - y_curr
if minetest.is_area_protected(pos1, pos2, owner, 5) then
crd.State:fault(pos, nvm, S("area is protected"))
return
end
if not is_air_level(pos1, pos2, nvm.hole_diameter) then
mark_area(pos1, pos2, owner)
M(pos):set_string("formspec", formspec(CRD(pos).State, pos, nvm))
coroutine.yield()
for zoffs = 1, nvm.hole_diameter do
for xoffs = 1, nvm.hole_diameter do
local qpos = get_quarry_pos(pos1, xoffs, zoffs)
local dig_state = techage.dig_like_player(qpos, fake_player, add_to_inv)
if dig_state == techage.dig_states.INV_FULL then
crd.State:blocked(pos, nvm, S("inventory full"))
coroutine.yield()
elseif dig_state == techage.dig_states.DUG then
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
coroutine.yield()
end
end
end
techage.unmark_position(owner)
end
end
crd.State:stop(pos, nvm, S("finished"))
stop_sound(pos)
end
local function keep_running(pos, elapsed)
local mem = techage.get_mem(pos)
if not mem.co then
mem.co = coroutine.create(quarry_task)
end
local nvm = techage.get_nvm(pos)
local crd = CRD(pos)
local _, err = coroutine.resume(mem.co, pos, crd, nvm)
if err then
minetest.log("error", "[TA4 Quarry Coroutine Error] at pos " .. minetest.pos_to_string(pos) .. " " .. err)
end
if techage.is_activeformspec(pos) then
M(pos):set_string("formspec", formspec(crd.State, pos, nvm))
end
if nvm.techage_state ~= techage.RUNNING then
stop_sound(pos)
end
end
local function on_rightclick(pos, node, clicker)
local nvm = techage.get_nvm(pos)
techage.set_activeformspec(pos, clicker)
M(pos):set_string("formspec", formspec(CRD(pos).State, pos, nvm))
end
local function can_dig(pos, player)
if minetest.is_protected(pos, player:get_player_name()) then
return false
end
local inv = M(pos):get_inventory()
return inv:is_empty("main")
end
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local nvm = techage.get_nvm(pos)
local mem = techage.get_mem(pos)
if fields.depth then
if tonumber(fields.depth) ~= nvm.quarry_depth then
nvm.quarry_depth = tonumber(fields.depth)
if CRD(pos).stage == 2 then
nvm.quarry_depth = math.min(nvm.quarry_depth, 20)
elseif CRD(pos).stage == 3 then
nvm.quarry_depth = math.min(nvm.quarry_depth, 40)
end
mem.co = nil
CRD(pos).State:stop(pos, nvm)
end
end
if fields.level then
if tonumber(fields.level) ~= nvm.start_level then
nvm.start_level = tonumber(fields.level)
mem.co = nil
CRD(pos).State:stop(pos, nvm)
end
end
if fields.hole_size then
if CRD(pos).stage == 4 then
if fields.hole_size ~= nvm.hole_size then
nvm.hole_size = fields.hole_size
nvm.hole_diameter = Holesize2Diameter[fields.hole_size or "5x5"] or 5
mem.co = nil
CRD(pos).State:stop(pos, nvm)
end
elseif CRD(pos).stage == 3 then
if fields.hole_size ~= nvm.hole_size then
nvm.hole_size = fields.hole_size
nvm.hole_diameter = Holesize2Diameter[fields.hole_size or "7x7"] or 7
mem.co = nil
CRD(pos).State:stop(pos, nvm)
end
else
nvm.hole_size = "5x5"
nvm.hole_diameter = 5
end
end
-- Per-player active quarry limit check (only on start attempts)
if fields.state_button ~= nil and techage.max_active_quarries > 0 then
local state = nvm.techage_state or techage.STOPPED
if state == techage.STOPPED or state == techage.STANDBY or state == techage.BLOCKED then
local owner = M(pos):get_string("owner")
if quarry_count_running(owner) >= techage.max_active_quarries then
minetest.chat_send_player(player:get_player_name(),
"[Techage] " .. S("Max. number of active quarries reached!") ..
" (" .. techage.max_active_quarries .. ")")
return
end
end
end
CRD(pos).State:state_button_event(pos, nvm, fields)
end
local tiles = {}
-- '#' will be replaced by the stage number
tiles.pas = {
-- up, down, right, left, back, front
"techage_filling_ta#.png^techage_frame_ta#_top.png",
"techage_filling_ta#.png^techage_frame_ta#_bottom.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_quarry_left.png",
"techage_filling_ta#.png^techage_appl_quarry.png^techage_frame_ta#.png",
"techage_filling_ta#.png^techage_appl_quarry.png^techage_frame_ta#.png",
}
tiles.act = {
-- up, down, right, left, back, front
"techage_filling_ta#.png^techage_frame_ta#_top.png",
"techage_filling_ta#.png^techage_frame_ta#_bottom.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
{
name = "techage_frame14_ta#.png^techage_quarry_left14.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 2.0,
},
},
"techage_filling_ta#.png^techage_appl_quarry.png^techage_frame_ta#.png",
"techage_filling_ta#.png^techage_appl_quarry.png^techage_frame_ta#.png",
}
local tubing = {
on_pull_item = function(pos, in_dir, num)
local meta = minetest.get_meta(pos)
if meta:get_int("pull_dir") == in_dir then
local inv = M(pos):get_inventory()
return techage.get_items(pos, inv, "main", num)
end
end,
on_push_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
if meta:get_int("push_dir") == in_dir or in_dir == 5 then
local inv = M(pos):get_inventory()
--CRD(pos).State:start_if_standby(pos) -- would need power!
return techage.put_items(inv, "main", stack)
end
end,
on_unpull_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
if meta:get_int("pull_dir") == in_dir then
local inv = M(pos):get_inventory()
return techage.put_items(inv, "main", stack)
end
end,
on_recv_message = function(pos, src, topic, payload)
if topic == "depth" then
local nvm = techage.get_nvm(pos)
return nvm.level or 0
elseif topic == "on" and techage.max_active_quarries > 0 then
local nvm = techage.get_nvm(pos)
local state = nvm.techage_state or techage.STOPPED
if state ~= techage.RUNNING then
local owner = M(pos):get_string("owner")
if quarry_count_running(owner) >= techage.max_active_quarries then
return "limit"
end
end
return CRD(pos).State:on_receive_message(pos, topic, payload)
else
return CRD(pos).State:on_receive_message(pos, topic, payload)
end
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
return CRD(pos).State:on_beduino_receive_cmnd(pos, topic, payload)
end,
on_beduino_request_data = function(pos, src, topic, payload)
if topic == 133 then -- Quarry Depth
local nvm = techage.get_nvm(pos)
return 0, {nvm.level or 0}
else
return CRD(pos).State:on_beduino_request_data(pos, topic, payload)
end
end,
on_node_load = function(pos)
CRD(pos).State:on_node_load(pos)
local nvm = techage.get_nvm(pos)
if nvm.techage_state == techage.RUNNING then
play_sound(pos) -- stops lingering handle and restarts fresh
else
stop_sound(pos) -- re-send stop to clients that came back in range
end
end,
}
local node_name_ta2, node_name_ta3, node_name_ta4 =
techage.register_consumer("quarry", S("Quarry"), tiles, {
drawtype = "normal",
cycle_time = CYCLE_TIME,
standby_ticks = STANDBY_TICKS,
formspec = formspec,
tubing = tubing,
on_state_change = on_node_state_change,
after_place_node = function(pos, placer)
local inv = M(pos):get_inventory()
local nvm = techage.get_nvm(pos)
inv:set_size('main', 9)
M(pos):set_string("owner", placer:get_player_name())
end,
can_dig = can_dig,
node_timer = keep_running,
on_receive_fields = on_receive_fields,
on_rightclick = on_rightclick,
allow_metadata_inventory_put = allow_metadata_inventory_put,
allow_metadata_inventory_take = allow_metadata_inventory_take,
groups = {choppy=2, cracky=2, crumbly=2},
sounds = default.node_sound_wood_defaults(),
num_items = {0,1,1,1},
power_consumption = {0,10,12,14},
}
)
minetest.register_craft({
output = node_name_ta2,
recipe = {
{"group:wood", "default:mese_crystal", "group:wood"},
{"techage:tubeS", "default:pick_diamond", "techage:iron_ingot"},
{"group:wood", "techage:iron_ingot", "group:wood"},
},
})
minetest.register_craft({
output = node_name_ta3,
recipe = {
{"", "default:mese_crystal", ""},
{"", node_name_ta2, ""},
{"", "techage:vacuum_tube", ""},
},
})
minetest.register_craft({
output = node_name_ta4,
recipe = {
{"", "default:mese_crystal", ""},
{"", node_name_ta3, ""},
{"", "techage:ta4_wlanchip", ""},
},
})
@@ -0,0 +1,243 @@
--[[
TechAge
=======
Copyright (C) 2019-2021 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA4 Recipe Block for the TA4 Autocrafter
]]--
-- for lazy programmers
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local S2P = minetest.string_to_pos
local M = minetest.get_meta
local S = techage.S
local MAX_RECIPE = 10
local function recipes_formspec(x, y, idx)
return "container[" .. x .. "," .. y .. "]" ..
"background[0,0;8,3.2;techage_form_grey.png]" ..
"list[context;input;0.1,0.1;3,3;]" ..
"image[3,1.1;1,1;techage_form_arrow.png]" ..
"list[context;output;3.9,1.1;1,1;]" ..
"button[5.5,1.1;1,1;priv;<<]" ..
"button[6.5,1.1;1,1;next;>>]" ..
"label[5.5,0.5;"..S("Recipe") .. ": " .. idx .. "/" .. MAX_RECIPE .. "]" ..
"container_end[]"
end
local function formspec(pos, nvm)
return "size[8,7.4]"..
recipes_formspec(0, 0, nvm.recipe_idx or 1) ..
"list[current_player;main;0,3.6;8,4;]" ..
"listring[current_player;main]"..
"listring[context;src]" ..
"listring[current_player;main]"..
"listring[context;dst]" ..
"listring[current_player;main]"
end
local function determine_new_input(pos, inv)
local output = inv:get_stack("output", 1):get_name()
if output and output ~= "" then
local recipe = minetest.get_craft_recipe(output)
if recipe.items and recipe.type == "normal" then
for i = 1, 9 do
local name = recipe.items[i]
if name then
if minetest.registered_items[name] then
inv:set_stack("input", i, name)
end
end
end
inv:set_stack("output", 1, recipe.output)
end
else
for i = 1, 9 do
inv:set_stack("input", i, nil)
end
end
end
local function determine_new_output(pos, inv)
local items = {}
for i = 1, 9 do
items[i] = inv:get_stack("input", i):get_name()
end
local input = {
method = "normal",
width = 3,
items = items,
}
local output, _ = minetest.get_craft_result(input)
inv:set_stack("output", 1, output.item)
end
local function get_recipe(inv)
local items = {}
local last_idx = 0
for i = 1, 9 do
local name = inv:get_stack("input", i):get_name()
if name ~= "" then
last_idx = i
end
items[i] = name
end
local input = table.concat(items, ",", 1, last_idx)
local stack = inv:get_stack("output", 1)
return {
input = input,
output = stack:get_name() .. " " .. stack:get_count()
}
end
local function after_recipe_change(pos, inv, listname)
if listname == "input" then
determine_new_output(pos, inv)
else
determine_new_input(pos, inv)
end
local nvm = techage.get_nvm(pos)
nvm.recipes = nvm.recipes or {}
nvm.recipes[nvm.recipe_idx or 1] = get_recipe(inv)
end
local function update_inventor(pos, inv, idx)
local nvm = techage.get_nvm(pos)
nvm.recipes = nvm.recipes or {}
local recipe = nvm.recipes[idx]
if recipe then
local items = string.split(recipe.input, ",", true)
for i = 1, 9 do
inv:set_stack("input", i, items[i] or "")
end
inv:set_stack("output", 1, recipe.output)
else
for i = 1, 9 do
inv:set_stack("input", i, nil)
end
inv:set_stack("output", 1, nil)
end
end
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
local inv = M(pos):get_inventory()
local list = inv:get_list(listname)
stack:set_count(1)
inv:set_stack(listname, index, stack)
after_recipe_change(pos, inv, listname)
return 0
end
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
local inv = M(pos):get_inventory()
inv:set_stack(listname, index, nil)
after_recipe_change(pos, inv, listname)
return 0
end
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
local inv = M(pos):get_inventory()
if from_list == to_list then
minetest.after(0.1, after_recipe_change, pos, inv, from_list)
return 1
end
return 0
end
minetest.register_node("techage:ta4_recipeblock", {
description = S("TA4 Recipe Block"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta4.png^techage_frame_ta4_top.png",
"techage_filling_ta4.png^techage_frame_ta4_top.png",
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_recipeblock.png",
},
on_construct = function(pos)
local inv = M(pos):get_inventory()
inv:set_size('input', 9)
inv:set_size('output', 1)
end,
after_place_node = function(pos, placer, itemstack)
local nvm = techage.get_nvm(pos)
local number = techage.add_node(pos, "techage:ta4_chest")
M(pos):set_string("owner", placer:get_player_name())
M(pos):set_string("node_number", number)
M(pos):set_string("formspec", formspec(pos, nvm))
M(pos):set_string("infotext", S("TA4 Recipe Block") .. " " .. number)
end,
on_receive_fields = function(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local nvm = techage.get_nvm(pos)
nvm.recipe_idx = nvm.recipe_idx or 1
if fields.next == ">>" then
nvm.recipe_idx = techage.in_range(nvm.recipe_idx + 1, 1, MAX_RECIPE)
elseif fields.priv == "<<" then
nvm.recipe_idx = techage.in_range(nvm.recipe_idx - 1, 1, MAX_RECIPE)
end
local inv = M(pos):get_inventory()
update_inventor(pos, inv, nvm.recipe_idx or 1)
M(pos):set_string("formspec", formspec(pos, nvm))
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end,
allow_metadata_inventory_put = allow_metadata_inventory_put,
allow_metadata_inventory_take = allow_metadata_inventory_take,
allow_metadata_inventory_move = allow_metadata_inventory_move,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
})
techage.register_node({"techage:ta4_recipeblock"}, {
on_recv_message = function(pos, src, topic, payload)
local nvm = techage.get_nvm(pos)
if topic == "input" and payload and payload ~= "" then
nvm.recipes = nvm.recipes or {}
local recipe = nvm.recipes[tonumber(payload) or 1]
if recipe then
return recipe.input
end
else
return "unsupported"
end
end,
})
minetest.register_craft({
output = "techage:ta4_recipeblock",
recipe = {
{"techage:ta4_carbon_fiber", "dye:blue", "techage:aluminum"},
{"", "basic_materials:ic", ""},
{"default:steel_ingot", "techage:ta4_wlanchip", "default:steel_ingot"},
},
})
@@ -0,0 +1,358 @@
--[[
TechAge
=======
Copyright (C) 2019-2022 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA4 Recycler, recycling techage machines
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
-- Consumer Related Data
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end
local STANDBY_TICKS = 3
local COUNTDOWN_TICKS = 4
local CYCLE_TIME = 8
local Recipes = {}
local SpecialItems = {
["techage:sieved_gravel"] = "default:sand",
["basic_materials:heating_element"] = "default:copper_ingot",
["techage:ta4_wlanchip"] = "",
["techage:basalt_cobble"] = "default:sand",
["default:stone"] = "techage:sieved_gravel",
["default:wood"] = "default:stick 5",
["basic_materials:concrete_block"] = "techage:sieved_gravel",
["dye:green"] = "",
["dye:red"] = "",
["dye:white"] = "",
["dye:blue"] = "",
["dye:brown"] = "",
["dye:cyan"] = "",
["dye:yellow"] = "",
["dye:grey"] = "",
["dye:orange"] = "",
["dye:black"] = "",
["techage:basalt_glass_thin"] = "",
["group:stone"] = "techage:sieved_gravel",
--["basic_materials:plastic_sheet"] = "",
["group:wood"] = "default:stick 5",
["techage:basalt_glass"] = "",
["default:junglewood"] = "default:stick 5",
["techage:ta4_silicon_wafer"] = "",
["default:cobble"] = "techage:sieved_gravel",
["default:pick_diamond"] = "default:stick",
["techage:hammer_steel"] = "default:stick",
["default:paper"] = "",
["stairs:slab_basalt_glass2"] = "",
["techage:basalt_stone"] = "techage:sieved_gravel",
["techage:ta4_ramchip"] = "",
["protector:chest"] = "default:chest",
["techage:ta4_rotor_blade"] = "",
["techage:ta4_carbon_fiber"] = "",
["techage:ta4_round_ceramic"] = "",
["techage:ta4_furnace_ceramic"] = "",
["techage:ta5_aichip"] = "",
["techage:ta4_leds"] = "",
}
local function formspec(self, pos, nvm)
return "size[8,8]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"list[context;src;0,0;3,3;]"..
--"item_image[0,0;1,1;default:cobble]"..
"image[0,0;1,1;techage_form_mask.png]"..
"image[3.5,0;1,1;"..techage.get_power_image(pos, nvm).."]"..
"image[3.5,1;1,1;techage_form_arrow.png]"..
"image_button[3.5,2;1,1;"..self:get_state_button_image(nvm)..";state_button;]"..
"tooltip[3.5,2;1,1;"..self:get_state_tooltip(nvm).."]"..
"list[context;dst;5,0;3,3;]"..
--"item_image[5,0;1,1;default:gravel]"..
"image[5,0;1,1;techage_form_mask.png]"..
"list[current_player;main;0,4;8,4;]"..
"listring[context;dst]"..
"listring[current_player;main]"..
"listring[context;src]"..
"listring[current_player;main]"..
default.get_hotbar_bg(0, 4)
end
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
if listname == "src" then
CRD(pos).State:start_if_standby(pos)
end
return stack:get_count()
end
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
local inv = M(pos):get_inventory()
local stack = inv:get_stack(from_list, from_index)
return allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
end
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return stack:get_count()
end
local function cook_reverse(stack, inv, idx, recipe)
-- check space
for _,item in ipairs(recipe.items) do
if not inv:room_for_item("dst", item) then
return false
end
end
-- take item
inv:remove_item("src", ItemStack(recipe.output))
-- add items
for _,item in ipairs(recipe.items) do
inv:add_item("dst", item)
end
return true
end
local function get_recipe(stack)
local name = stack:get_name()
local recipe = Recipes[name]
if recipe then
if stack:get_count() >= ItemStack(recipe.output):get_count() then
return recipe
end
end
end
local function recycling(pos, crd, nvm, inv)
for idx,stack in ipairs(inv:get_list("src")) do
local recipe = not stack:is_empty() and get_recipe(stack)
if recipe then
if cook_reverse(stack, inv, idx, recipe) then
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
else
crd.State:blocked(pos, nvm)
end
return
end
end
crd.State:idle(pos, nvm)
end
local function keep_running(pos, elapsed)
local nvm = techage.get_nvm(pos)
local crd = CRD(pos)
local inv = M(pos):get_inventory()
recycling(pos, crd, nvm, inv)
end
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local nvm = techage.get_nvm(pos)
CRD(pos).State:state_button_event(pos, nvm, fields)
end
local function can_dig(pos, player)
if minetest.is_protected(pos, player:get_player_name()) then
return false
end
local inv = M(pos):get_inventory()
return inv:is_empty("dst") and inv:is_empty("src")
end
local tiles = {}
-- '#' will be replaced by the stage number
-- '{power}' will be replaced by the power PNG
tiles.pas = {
-- up, down, right, left, back, front
"techage_appl_grinder.png^[colorize:@@000000:100^techage_frame_ta#_top.png",
--"techage_appl_grinder.png^techage_frame_ta#_top.png^[multiply:#FF0000",
"techage_filling_ta#.png^techage_frame_ta#_bottom.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
"techage_filling_ta#.png^techage_appl_recycler.png^techage_frame_ta#.png",
"techage_filling_ta#.png^techage_appl_recycler.png^techage_frame_ta#.png",
}
tiles.act = {
-- up, down, right, left, back, front
{
name = "techage_appl_grinder4.png^[colorize:@@000000:100^techage_frame4_ta#_top.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 1.0,
},
},
"techage_filling_ta#.png^techage_frame_ta#_bottom.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
"techage_filling_ta#.png^techage_appl_recycler.png^techage_frame_ta#.png",
"techage_filling_ta#.png^techage_appl_recycler.png^techage_frame_ta#.png",
}
local tubing = {
on_pull_item = function(pos, in_dir, num)
local meta = minetest.get_meta(pos)
if meta:get_int("pull_dir") == in_dir then
local inv = M(pos):get_inventory()
return techage.get_items(pos, inv, "dst", num)
end
end,
on_push_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
if meta:get_int("push_dir") == in_dir or in_dir == 5 then
local inv = M(pos):get_inventory()
--CRD(pos).State:start_if_standby(pos) -- would need power!
return techage.put_items(inv, "src", stack)
end
end,
on_unpull_item = function(pos, in_dir, stack)
local meta = minetest.get_meta(pos)
if meta:get_int("pull_dir") == in_dir then
local inv = M(pos):get_inventory()
return techage.put_items(inv, "dst", stack)
end
end,
on_recv_message = function(pos, src, topic, payload)
return CRD(pos).State:on_receive_message(pos, topic, payload)
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
return CRD(pos).State:on_beduino_receive_cmnd(pos, topic, payload)
end,
on_beduino_request_data = function(pos, src, topic, payload)
return CRD(pos).State:on_beduino_request_data(pos, topic, payload)
end,
on_node_load = function(pos)
CRD(pos).State:on_node_load(pos)
end,
}
local _, _, node_name_ta4 =
techage.register_consumer("recycler", S("Recycler"), tiles, {
drawtype = "nodebox",
paramtype = "light",
node_box = {
type = "fixed",
fixed = {
{-8/16, -8/16, -8/16, 8/16, 8/16, -6/16},
{-8/16, -8/16, 6/16, 8/16, 8/16, 8/16},
{-8/16, -8/16, -8/16, -6/16, 8/16, 8/16},
{ 6/16, -8/16, -8/16, 8/16, 8/16, 8/16},
{-6/16, -8/16, -6/16, 6/16, 6/16, 6/16},
},
},
selection_box = {
type = "fixed",
fixed = {-8/16, -8/16, -8/16, 8/16, 8/16, 8/16},
},
cycle_time = CYCLE_TIME,
standby_ticks = STANDBY_TICKS,
formspec = formspec,
tubing = tubing,
after_place_node = function(pos, placer)
local inv = M(pos):get_inventory()
inv:set_size('src', 9)
inv:set_size('dst', 9)
end,
can_dig = can_dig,
node_timer = keep_running,
on_receive_fields = on_receive_fields,
allow_metadata_inventory_put = allow_metadata_inventory_put,
allow_metadata_inventory_move = allow_metadata_inventory_move,
allow_metadata_inventory_take = allow_metadata_inventory_take,
groups = {choppy=2, cracky=2, crumbly=2},
sounds = default.node_sound_wood_defaults(),
num_items = {0,0,0,1},
power_consumption = {0,0,0,16},
},
{false, false, false, true}) -- TA4 only
minetest.register_craft({
output = node_name_ta4,
recipe = {
{"", "default:mese_crystal", ""},
{"", "techage:ta4_grinder_pas", ""},
{"", "techage:ta4_wlanchip", ""},
},
})
-------------------------------------------------------------------------------
-- Prepare recipes
-------------------------------------------------------------------------------
-- Nodes from mods that can be recycled
local ModNames = {
techage = true,
hyperloop = true,
}
local function get_item_list(inputs)
local lst = {}
for _,input in pairs(inputs or {}) do
if SpecialItems[input] then
input = SpecialItems[input]
end
if input and input ~= "" then
if minetest.registered_nodes[input] or minetest.registered_items[input] then
table.insert(lst, input)
end
end
end
return lst
end
local function get_special_recipe(name)
if SpecialItems[name] then
return {
output = name,
items = {SpecialItems[name]}
}
end
end
local function collect_recipes()
local add = function(name, ndef)
local _, _, mod, _ = string.find(name, "([%w_]+):([%w_]+)")
local recipe = get_special_recipe(name) or
techage.recipes.get_recipe(name) or
minetest.get_craft_recipe(name)
local items = get_item_list(recipe.items)
if ModNames[mod]
and ndef.groups.not_in_creative_inventory ~= 1
and not ndef.tool_capabilities
and recipe.output
and next(items) then
local s = table.concat(items, ", ")
--print(string.format("%-36s {%s}", recipe.output, s))
Recipes[name] = {output = recipe.output, items = items}
end
end
for name, ndef in pairs(minetest.registered_nodes) do
add(name, ndef)
end
for name, ndef in pairs(minetest.registered_items) do
add(name, ndef)
end
end
minetest.after(2, collect_recipes)
@@ -0,0 +1,715 @@
--[[
TechAge
=======
Copyright (C) 2019-2023 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA4 8x2000 Chest
]]--
-- for lazy programmers
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local M = minetest.get_meta
local S = techage.S
local DESCRIPTION = S("TA4 8x2000 Chest")
local STACK_SIZE = 2000
local function gen_stack(inv, idx)
inv[idx] = {name = "", count = 0}
return inv[idx]
end
local function gen_inv(nvm)
nvm.inventory = {}
for i = 1,8 do
gen_stack(nvm.inventory, i)
end
return nvm.inventory
end
local function repair_inv(nvm)
nvm.inventory = nvm.inventory or {}
for i = 1,8 do
local item = nvm.inventory[i]
if not item or type(item) ~= "table"
or not item.name or type(item.name) ~= "string" or item.name == ""
or not item.count or type(item.count) ~= "number" or item.count < 1
then
gen_stack(nvm.inventory, i)
end
end
end
local function get_stack(nvm, idx)
nvm.inventory = nvm.inventory or {}
return nvm.inventory[idx] or gen_stack(nvm.inventory, idx)
end
local function get_count(nvm, idx)
nvm.inventory = nvm.inventory or {}
if idx and idx > 0 then
return nvm.inventory[idx] and nvm.inventory[idx].count or 0
else
local count = 0
for _,item in ipairs(nvm.inventory) do
count = count + item.count or 0
end
return count
end
end
local function get_itemstring(nvm, idx)
if idx and idx > 0 then
nvm.inventory = nvm.inventory or {}
return nvm.inventory[idx] and nvm.inventory[idx].name or ""
end
return ""
end
local function inv_empty(nvm)
for _,item in ipairs(nvm.inventory or {}) do
if item.count and item.count > 0 then
return false
end
end
return true
end
local function inv_state(nvm)
local num = 0
for _,item in ipairs(nvm.inventory or {}) do
if item.count and item.count > 0 then
num = num + 1
end
end
if num == 0 then return "empty" end
if num == 8 then return "full" end
return "loaded"
end
local function inv_state_num(nvm)
local num = 0
for _,item in ipairs(nvm.inventory or {}) do
if item.count and item.count > 0 then
num = num + 1
end
end
if num == 0 then return 0 end
if num == 8 then return 2 end
return 1
end
local function max_stacksize(item_name)
-- It is sufficient to use minetest.registered_items as all registration
-- functions (node, craftitems, tools) add the definitions there.
local ndef = minetest.registered_items[item_name]
-- Return 1 as fallback so that slots with unknown items can be emptied.
return ndef and ndef.stack_max or 1
end
local function get_stacksize(pos)
local size = M(pos):get_int("stacksize")
if size == 0 then
return STACK_SIZE
end
return size
end
-- Returns a boolean that indicates if an itemstack and nvmstack can be combined.
-- The second return value is a string describing the reason.
-- This function guarantees not to modify any of both stacks.
local function doesItemStackMatchNvmStack(itemstack, nvmstack)
if itemstack:get_count() == 0 or nvmstack.count == 0 then
return true, "Empty stack"
end
if nvmstack.name and nvmstack.name ~= "" and nvmstack.name ~= itemstack:get_name() then
return false, "Mismatching names"
end
-- The following seems to be the most reliable approach to compare meta.
local nvm_meta = ItemStack():get_meta()
nvm_meta:from_table(minetest.deserialize(nvmstack.meta or ""))
if not nvm_meta:equals(itemstack:get_meta()) then
return false, "Mismatching meta"
end
if (nvmstack.wear or 0) ~= itemstack:get_wear() then
return false, "Mismatching wear"
end
return true, "Stacks match"
end
-- Generic function for adding items to the 8x2000 Chest
-- This function guarantees not to modify the itemstack.
-- The number of items that were added to the chest is returned.
local function add_to_chest(pos, input_stack, idx)
local nvm = techage.get_nvm(pos)
local nvm_stack = get_stack(nvm, idx)
if input_stack:get_count() == 0 then
return 0
end
if not doesItemStackMatchNvmStack(input_stack, nvm_stack) then
return 0
end
local count = math.min(input_stack:get_count(), get_stacksize(pos) - (nvm_stack.count or 0))
if nvm_stack.count == 0 then
nvm_stack.name = input_stack:get_name()
nvm_stack.meta = minetest.serialize(input_stack:get_meta():to_table())
nvm_stack.wear = input_stack:get_wear()
end
nvm_stack.count = nvm_stack.count + count
return count
end
local function stackOrNil(stack)
if stack and stack.get_count and stack:get_count() > 0 then
return stack
end
return nil
end
-- Generic function for taking items from the 8x2000 Chest
-- output_stack is directly modified; but nil can also be supplied.
-- The resulting output_stack is returned from the function.
-- keep_assignment indicates if the meta information for this function should be considered (manual vs. tubes).
local function take_from_chest(pos, idx, output_stack, max_total_count, keep_assignment)
local nvm = techage.get_nvm(pos)
local nvm_stack = get_stack(nvm, idx)
output_stack = output_stack or ItemStack()
local assignment_count = keep_assignment and M(pos):get_int("assignment") == 1 and 1 or 0
local count = math.min(nvm_stack.count - assignment_count, max_stacksize(nvm_stack.name) - output_stack:get_count())
if max_total_count then
count = math.min(count, max_total_count - output_stack:get_count())
end
if count < 1 then
return stackOrNil(output_stack)
end
if not doesItemStackMatchNvmStack(output_stack, nvm_stack) then
return stackOrNil(output_stack)
end
output_stack:add_item(ItemStack({
name = nvm_stack.name,
count = count,
wear = nvm_stack.wear,
}))
output_stack:get_meta():from_table(minetest.deserialize(nvm_stack.meta or ""))
nvm_stack.count = nvm_stack.count - count
if nvm_stack.count == 0 then
gen_stack(nvm.inventory or {}, idx)
end
return stackOrNil(output_stack)
end
-- Function for adding items to the 8x2000 Chest via automation, e.g. pushers
local function tube_add_to_chest(pos, input_stack)
local nvm = techage.get_nvm(pos)
nvm.inventory = nvm.inventory or {}
for idx = 1,8 do
input_stack:take_item(add_to_chest(pos, input_stack, idx))
end
if input_stack:get_count() > 0 then
return input_stack -- Not all items were added to chest
else
return true -- All items were added
end
end
-- Function for taking items from the 8x2000 Chest via automation, e.g. pushers
local function tube_take_from_chest(pos, item_name, count)
local nvm = techage.get_nvm(pos)
local mem = techage.get_mem(pos)
nvm.inventory = nvm.inventory or {}
mem.startpos = mem.startpos or 1
local prio = M(pos):get_int("priority") == 1
local startpos = prio and 8 or mem.startpos
local endpos = prio and 1 or mem.startpos + 8
local step = prio and -1 or 1
local itemstack = ItemStack()
for idx = startpos,endpos,step do
idx = ((idx - 1) % 8) + 1
local nvmstack = get_stack(nvm, idx)
if not item_name or item_name == nvmstack.name then
take_from_chest(pos, idx, itemstack, count - itemstack:get_count(), true)
if itemstack:get_count() == count then
mem.startpos = idx + 1
return itemstack
end
end
mem.startpos = idx + 1
end
return stackOrNil(itemstack)
end
-- Function for manually adding items to the 8x2000 Chest via the formspec
local function inv_add_to_chest(pos, idx)
local inv = M(pos):get_inventory()
local inv_stack = inv:get_stack("main", idx)
local count = add_to_chest(pos, inv_stack, idx)
inv_stack:set_count(inv_stack:get_count() - count)
inv:set_stack("main", idx, inv_stack)
end
-- Function for manually taking items from the 8x2000 Chest via the formspec
local function inv_take_from_chest(pos, idx)
local inv = M(pos):get_inventory()
local inv_stack = inv:get_stack("main", idx)
if inv_stack:get_count() > 0 then
return
end
local output_stack = take_from_chest(pos, idx)
if output_stack then
inv:set_stack("main", idx, output_stack)
end
end
local function formspec_container(x, y, nvm, inv)
local tbl = {"container["..x..","..y.."]"}
for i = 1,8 do
local xpos = i - 1
tbl[#tbl+1] = "box["..(xpos - 0.03)..",0;0.86,0.9;#808080]"
local stack = get_stack(nvm, i)
if stack.name ~= "" then
local itemstack = ItemStack({
name = stack.name,
count = stack.count,
wear = stack.wear,
})
local stack_meta_table = (minetest.deserialize(stack.meta) or {}).fields or {}
for _, key in ipairs({"description", "short_description", "color", "palette_index"}) do
if stack_meta_table[key] then
itemstack:get_meta():set_string(key, stack_meta_table[key]:gsub("%[", "("):gsub("%]", ")"))
end
end
local itemname = itemstack:to_string()
--tbl[#tbl+1] = "item_image["..xpos..",1;1,1;"..itemname.."]"
tbl[#tbl+1] = techage.item_image(xpos, 0, itemname, stack.count)
end
if inv:get_stack("main", i):get_count() == 0 then
tbl[#tbl+1] = "image_button["..xpos..",1;1,1;techage_form_get_arrow.png;get"..i..";]"
else
tbl[#tbl+1] = "image_button["..xpos..",1;1,1;techage_form_add_arrow.png;add"..i..";]"
end
end
tbl[#tbl+1] = "list[context;main;0,2;8,1;]"
tbl[#tbl+1] = "container_end[]"
return table.concat(tbl, "")
end
local function formspec(pos)
local nvm = techage.get_nvm(pos)
local inv = M(pos):get_inventory()
local size = get_stacksize(pos)
local assignment = M(pos):get_int("assignment") == 1 and "true" or "false"
local priority = M(pos):get_int("priority") == 1 and "true" or "false"
return "size[8,8.3]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
formspec_container(0, 0, nvm, inv)..
"button[0,3.5;3,1;unlock;"..S("Unlock").."]"..
"tooltip[0,3.5;3,1;"..S("Unlock connected chest\nif all slots are below 2000")..";#0C3D32;#FFFFFF]"..
"label[0,3;"..S("Size")..": 8x"..size.."]"..
"checkbox[4,3;assignment;"..S("keep assignment")..";"..assignment.."]"..
"tooltip[4,3;2,0.6;"..S("Never completely empty the slots\nwith the pusher to keep the item assignment")..";#0C3D32;#FFFFFF]"..
"checkbox[4,3.6;priority;"..S("right to left")..";"..priority.."]"..
"tooltip[4,3.6;2,0.6;"..S("Empty the slots always \nfrom right to left")..";#0C3D32;#FFFFFF]"..
"list[current_player;main;0,4.6;8,4;]"..
"listring[context;main]"..
"listring[current_player;main]"
end
local function count_number_of_chests(pos)
local node = techage.get_node_lvm(pos)
local dir = techage.side_to_outdir("B", node.param2)
local pos1 = tubelib2.get_pos(pos, dir)
local param2 = node.param2
local cnt = 1
while cnt < 50 do
node = techage.get_node_lvm(pos1)
if node.name ~= "techage:ta4_chest_dummy" then
break
end
local meta = M(pos1)
if meta:contains("param2") and meta:get_int("param2") ~= param2 then
break
end
pos1 = tubelib2.get_pos(pos1, dir)
cnt = cnt + 1
end
M(pos):set_int("stacksize", STACK_SIZE * cnt)
end
local function dummy_chest_behind(pos, node)
local dir = techage.side_to_outdir("B", node.param2)
local pos1 = tubelib2.get_pos(pos, dir)
node = techage.get_node_lvm(pos1)
return node.name == "techage:ta4_chest_dummy"
end
local function part_of_a_chain(pos, node)
local dir = techage.side_to_outdir("F", node.param2)
local pos1 = tubelib2.get_pos(pos, dir)
node = techage.get_node_lvm(pos1)
return node.name == "techage:ta4_chest_dummy" or node.name == "techage:ta4_chest"
end
local function search_chest_in_front(pos, node)
local dir = techage.side_to_outdir("F", node.param2)
local pos1 = tubelib2.get_pos(pos, dir)
local param2 = node.param2
local cnt = 1
while cnt < 50 do
node = techage.get_node_lvm(pos1)
if node.name ~= "techage:ta4_chest_dummy" then
break
end
local meta = M(pos1)
if meta:contains("param2") and meta:get_int("param2") ~= param2 then
break
end
pos1 = tubelib2.get_pos(pos1, dir)
cnt = cnt + 1
end
if node.name == "techage:ta4_chest" and node.param2 == param2 then
minetest.after(1, count_number_of_chests, pos1)
local nvm = techage.get_nvm(pos)
nvm.front_chest_pos = pos1
return true
end
return false
end
local function get_front_chest_pos(pos)
local nvm = techage.get_nvm(pos)
if nvm.front_chest_pos then
return nvm.front_chest_pos
end
local node = techage.get_node_lvm(pos)
if search_chest_in_front(pos, node) then
return nvm.front_chest_pos
end
return pos
end
local function convert_to_chest_again(pos, node, player)
local dir = techage.side_to_outdir("B", node.param2)
local pos1 = tubelib2.get_pos(pos, dir)
local node1 = techage.get_node_lvm(pos1)
if minetest.is_protected(pos1, player:get_player_name()) then
return
end
if node1.name == "techage:ta4_chest_dummy" then
node1.name = "techage:ta4_chest"
minetest.swap_node(pos1, node1)
--M(pos1):set_int("disabled", 1)
local nvm = techage.get_nvm(pos1)
gen_inv(nvm)
local number = techage.add_node(pos1, "techage:ta4_chest")
M(pos1):set_string("owner", player:get_player_name())
M(pos1):set_string("formspec", formspec(pos1))
M(pos1):set_string("infotext", DESCRIPTION.." "..number)
end
end
local function unlock_chests(pos, player)
local nvm = techage.get_nvm(pos)
for idx = 1,8 do
if get_count(nvm, idx) > STACK_SIZE then return end
end
local node = techage.get_node_lvm(pos)
convert_to_chest_again(pos, node, player)
M(pos):set_int("stacksize", STACK_SIZE)
end
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return stack:get_count()
end
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return stack:get_count()
end
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
return count
end
local function on_metadata_inventory_put(pos, listname, index, stack, player)
M(pos):set_string("formspec", formspec(pos))
techage.set_activeformspec(pos, player)
end
local function on_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
M(pos):set_string("formspec", formspec(pos))
techage.set_activeformspec(pos, player)
end
local function on_metadata_inventory_take(pos, listname, index, stack, player)
M(pos):set_string("formspec", formspec(pos))
techage.set_activeformspec(pos, player)
end
local function on_rightclick(pos, node, clicker)
if M(pos):get_int("disabled") ~= 1 then
local nvm = techage.get_nvm(pos)
repair_inv(nvm)
M(pos):set_string("formspec", formspec(pos))
techage.set_activeformspec(pos, clicker)
end
end
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
for i = 1,8 do
if fields["get"..i] ~= nil then
inv_take_from_chest(pos, i)
break
elseif fields["add"..i] ~= nil then
inv_add_to_chest(pos, i)
break
end
end
if fields.unlock then
unlock_chests(pos, player)
end
if fields.assignment then
M(pos):set_int("assignment", fields.assignment == "true" and 1 or 0)
end
if fields.priority then
M(pos):set_int("priority", fields.priority == "true" and 1 or 0)
end
M(pos):set_string("formspec", formspec(pos))
end
local function can_dig(pos, player)
if minetest.is_protected(pos, player:get_player_name()) then
return false
end
local inv = minetest.get_meta(pos):get_inventory()
local nvm = techage.get_nvm(pos)
return inv:is_empty("main") and inv_empty(nvm)
end
local function on_rotate(pos, node, user, mode, new_param2)
if get_stacksize(pos) == STACK_SIZE then
return screwdriver.rotate_simple(pos, node, user, mode, new_param2)
else
return screwdriver.disallow(pos, node, user, mode, new_param2)
end
end
local function after_dig_node(pos, oldnode, oldmetadata, digger)
techage.remove_node(pos, oldnode, oldmetadata)
convert_to_chest_again(pos, oldnode, digger)
end
minetest.register_node("techage:ta4_chest", {
description = DESCRIPTION,
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta4.png^techage_frame_ta4_top.png",
"techage_filling_ta4.png^techage_frame_ta4.png",
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_back_ta4.png",
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_back_ta4.png",
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_back_ta4.png",
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_front_ta4.png^techage_appl_warehouse.png",
},
on_construct = function(pos)
local inv = M(pos):get_inventory()
inv:set_size('main', 8)
end,
after_place_node = function(pos, placer)
local node = minetest.get_node(pos)
if dummy_chest_behind(pos, node) then
minetest.remove_node(pos)
return true
end
if search_chest_in_front(pos, node) then
node.name = "techage:ta4_chest_dummy"
minetest.swap_node(pos, node)
M(pos):set_int("param2", node.param2)
else
local nvm = techage.get_nvm(pos)
gen_inv(nvm)
local number = techage.add_node(pos, "techage:ta4_chest")
M(pos):set_string("owner", placer:get_player_name())
M(pos):set_string("formspec", formspec(pos))
M(pos):set_string("infotext", DESCRIPTION.." "..number)
end
end,
techage_set_numbers = function(pos, numbers, player_name)
return techage.logic.set_numbers(pos, numbers, player_name, DESCRIPTION)
end,
on_rotate = on_rotate,
on_rightclick = on_rightclick,
on_receive_fields = on_receive_fields,
can_dig = can_dig,
after_dig_node = after_dig_node,
allow_metadata_inventory_put = allow_metadata_inventory_put,
allow_metadata_inventory_take = allow_metadata_inventory_take,
on_metadata_inventory_put = on_metadata_inventory_put,
on_metadata_inventory_move = on_metadata_inventory_move,
on_metadata_inventory_take = on_metadata_inventory_take,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
})
minetest.register_node("techage:ta4_chest_dummy", {
description = DESCRIPTION,
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta4.png^techage_frame_ta4_top.png",
"techage_filling_ta4.png^techage_frame_ta4.png",
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_back_ta4.png",
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_back_ta4.png",
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_back_ta4.png",
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_front_ta4.png^techage_appl_warehouse.png",
},
on_rightclick = function(pos, node, clicker)
end,
paramtype2 = "facedir",
diggable = false,
groups = {not_in_creative_inventory = 1},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
})
techage.register_node({"techage:ta4_chest"}, {
on_pull_item = function(pos, in_dir, num, item_name)
local res = tube_take_from_chest(pos, item_name, num)
if techage.is_activeformspec(pos) then
M(pos):set_string("formspec", formspec(pos))
end
return res
end,
on_push_item = function(pos, in_dir, stack)
local res = tube_add_to_chest(pos, stack)
if techage.is_activeformspec(pos) then
M(pos):set_string("formspec", formspec(pos))
end
return res
end,
on_unpull_item = function(pos, in_dir, stack)
local res = tube_add_to_chest(pos, stack)
if techage.is_activeformspec(pos) then
M(pos):set_string("formspec", formspec(pos))
end
return res
end,
on_recv_message = function(pos, src, topic, payload)
if topic == "count" then
local nvm = techage.get_nvm(pos)
return get_count(nvm, tonumber(payload or 0) or 0)
elseif topic == "itemstring" then
local nvm = techage.get_nvm(pos)
return get_itemstring(nvm, tonumber(payload or 0) or 0)
elseif topic == "storesize" then
return get_stacksize(pos)
elseif topic == "state" then
local nvm = techage.get_nvm(pos)
return inv_state(nvm)
else
return "unsupported"
end
end,
on_beduino_request_data = function(pos, src, topic, payload)
if topic == 140 and payload[1] == 1 then -- Inventory Item Count
local nvm = techage.get_nvm(pos)
return 0, {get_count(nvm, tonumber(payload[2] or 0) or 0)}
elseif topic == 140 and payload[1] == 2 then -- Inventory Item Name
local nvm = techage.get_nvm(pos)
return 0, get_itemstring(nvm, tonumber(payload[2] or 0) or 0)
elseif topic == 140 and payload[1] == 3 then -- storesize
return 0, {get_stacksize(pos)}
elseif topic == 131 then -- Chest State
local nvm = techage.get_nvm(pos)
return 0, {inv_state_num(nvm)}
else
return 2, ""
end
end,
})
techage.register_node({"techage:ta4_chest_dummy"}, {
on_pull_item = function(pos, in_dir, num, item_name)
local fc_pos = get_front_chest_pos(pos)
local res = tube_take_from_chest(fc_pos, item_name, num)
if techage.is_activeformspec(fc_pos) then
M(fc_pos):set_string("formspec", formspec(fc_pos))
end
return res
end,
on_push_item = function(pos, in_dir, stack)
local fc_pos = get_front_chest_pos(pos)
local res = tube_add_to_chest(fc_pos, stack)
if techage.is_activeformspec(fc_pos) then
M(fc_pos):set_string("formspec", formspec(fc_pos))
end
return res
end,
on_unpull_item = function(pos, in_dir, stack)
local fc_pos = get_front_chest_pos(pos)
local res = tube_add_to_chest(fc_pos, stack)
if techage.is_activeformspec(fc_pos) then
M(fc_pos):set_string("formspec", formspec(fc_pos))
end
return res
end
})
minetest.register_lbm({
label = "Repair Dummy Chests",
name = "techage:chest_dummy",
nodenames = {"techage:ta4_chest_dummy"},
run_at_every_load = true,
action = function(pos, node)
if not part_of_a_chain(pos, node) then
minetest.swap_node(pos, {name = "techage:ta4_chest", param2 = node.param2})
end
end,
})
minetest.register_craft({
type = "shapeless",
output = "techage:ta4_chest",
recipe = {"techage:chest_ta4"}
})
minetest.register_craft({
type = "shapeless",
output = "techage:chest_ta4",
recipe = {"techage:ta4_chest"}
})
@@ -0,0 +1,334 @@
--[[
TechAge
=======
Copyright (C) 2019-2022 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA4 Injector
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
-- Consumer Related Data
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end
local tooltip = S("Switch to pull mode \nto pull items out of inventory slots \naccording the injector configuration")
local Tube = techage.Tube
local STANDBY_TICKS = 2
local COUNTDOWN_TICKS = 3
local CYCLE_TIME = 4
local function formspec(self, pos, nvm)
local pull_mode = dump(nvm.pull_mode or false)
return "size[8,7.2]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"box[0,-0.1;7.8,0.5;#c6e8ff]"..
"label[3,-0.1;"..minetest.colorize("#000000", S("Injector")).."]"..
techage.question_mark_help(8, S("Configure up to 8 items \nto be pushed by the injector"))..
"list[context;filter;0,0.8;8,1;]"..
"image_button[2,2;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
"tooltip[2,2;1,1;"..self:get_state_tooltip(nvm).."]"..
"checkbox[3.5,1.9;pull_mode;"..S("pull mode")..";"..pull_mode.."]"..
"tooltip[3.5,1.9;2,0.8;"..tooltip..";#0C3D32;#FFFFFF]"..
"list[current_player;main;0,3.5;8,4;]"..
"listring[context;filter]"..
"listring[current_player;main]"
end
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
local nvm = techage.get_nvm(pos)
if CRD(pos).State:get_state(nvm) ~= techage.STOPPED then
return 0
end
local inv = M(pos):get_inventory()
local list = inv:get_list(listname)
local cdr = CRD(pos)
if list[index]:get_count() < cdr.num_items then
local num = math.min(cdr.num_items - list[index]:get_count(), stack:get_count()) + list[index]:get_count()
stack:set_count(num)
inv:set_stack(listname, index, stack)
return 0
end
return 0
end
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
local nvm = techage.get_nvm(pos)
if CRD(pos).State:get_state(nvm) ~= techage.STOPPED then
return 0
end
local inv = M(pos):get_inventory()
inv:set_stack(listname, index, nil)
return 0
end
local function pull_items(pos, out_dir, idx, name, num)
local inv, listname = techage.get_inv_access(pos, out_dir, "pull")
if inv and listname then
if idx and idx ~= 0 then
local stack = inv:get_stack(listname, idx)
if stack and not stack:is_empty() and stack:get_name() == name then
local taken = stack:take_item(num)
inv:set_stack(listname, idx, stack)
return (taken:get_count() > 0) and taken or nil
end
else
local taken = inv:remove_item(listname, {name = name, count = num})
return (taken:get_count() > 0) and taken or nil
end
else
return techage.pull_items(pos, out_dir, num, name)
end
end
local function push_items(pos, out_dir, idx, items)
local inv, listname, callafter, dpos = techage.get_inv_access(pos, out_dir, "push")
if inv and listname then
if idx and idx ~= 0 then
if idx > inv:get_size(listname) then
return false
end
local stack = inv:get_stack(listname, idx)
if stack:item_fits(items) then
stack:add_item(items)
inv:set_stack(listname, idx, stack)
if callafter then
callafter(dpos)
end
return true
end
else
if inv:room_for_item(listname, items) then
inv:add_item(listname, items)
if callafter then
callafter(dpos)
end
return true
end
end
return false
else
local taken = items:get_count()
local leftover = techage.push_items(pos, out_dir, items, idx)
if not leftover or leftover == false then
return false -- No items placed
elseif leftover ~= true then
-- One or more items placed?
if leftover:get_count() < taken then
-- place the rest back
local pull_dir = M(pos):get_int("pull_dir")
techage.unpull_items(pos, pull_dir, leftover)
return true -- Some items placed
end
return false -- No items placed
end
return true -- All items placed
end
end
local function unpull_items(pos, out_dir, idx, items)
local inv, listname = techage.get_inv_access(pos, out_dir, "unpull")
if inv and listname then
if idx and idx ~= 0 then
local stack = inv:get_stack(listname, idx)
stack:add_item(items)
inv:set_stack(listname, idx, stack)
else
inv:add_item(listname, items)
end
else
techage.unpull_items(pos, out_dir, items)
end
end
local function pushing(pos, crd, meta, nvm)
local pull_dir = meta:get_int("pull_dir")
local push_dir = meta:get_int("push_dir")
local inv = M(pos):get_inventory()
local filter = inv:get_list("filter")
local pushed = false
local pulled = false
for idx, item in ipairs(filter) do
local name = item:get_name()
local num = math.min(item:get_count(), crd.num_items)
if name ~= "" and num > 0 then
local items = pull_items(pos, pull_dir, nvm.pull_mode and idx, name, num)
if items ~= nil then
pulled = true
if push_items(pos, push_dir, not nvm.pull_mode and idx, items) then
pushed = true
else
-- place item back - destination is full or unreachable
unpull_items(pos, pull_dir, nvm.pull_mode and idx, items)
end
end
end
end
if not pulled then
crd.State:idle(pos, nvm)
elseif not pushed then
crd.State:blocked(pos, nvm)
else
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
end
end
local function node_timer(pos, elapsed)
local nvm = techage.get_nvm(pos)
local crd = CRD(pos)
pushing(pos, crd, M(pos), nvm)
end
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local nvm = techage.get_nvm(pos)
if fields.pull_mode then
nvm.pull_mode = fields.pull_mode == "true"
end
CRD(pos).State:state_button_event(pos, nvm, fields)
M(pos):set_string("formspec", formspec(CRD(pos).State, pos, nvm))
end
local tiles = {}
-- '#' will be replaced by the stage number
-- '{power}' will be replaced by the power PNG
tiles.pas = {
"techage_filling_ta#.png^techage_frame_ta#_top.png^techage_appl_arrow.png",
"techage_filling_ta#.png^techage_frame_ta#_bottom.png^techage_appl_arrow.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
"techage_appl_pusher.png^[transformR180]^techage_frame_ta#.png^techage_appl_injector.png",
"techage_appl_pusher.png^techage_frame_ta#.png^techage_appl_injector.png",
}
tiles.act = {
-- up, down, right, left, back, front
"techage_filling_ta#.png^techage_frame_ta#_top.png^techage_appl_arrow.png",
"techage_filling_ta#.png^techage_frame_ta#_bottom.png^techage_appl_arrow.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
{
name = "techage_appl_pusher14.png^[transformR180]^techage_frame14_ta#.png^techage_appl_injector14.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 2.0,
},
},
{
name = "techage_appl_pusher14.png^techage_frame14_ta#.png^techage_appl_injector14.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 2.0,
},
},
}
local tubing = {
-- push item through the injector in opposit direction
on_push_item = function(pos, in_dir, stack)
return in_dir == M(pos):get_int("pull_dir") and techage.safe_push_items(pos, in_dir, stack)
end,
is_pusher = true, -- is a pulling/pushing node
on_recv_message = function(pos, src, topic, payload)
return CRD(pos).State:on_receive_message(pos, topic, payload)
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
return CRD(pos).State:on_beduino_receive_cmnd(pos, topic, payload)
end,
on_beduino_request_data = function(pos, src, topic, payload)
return CRD(pos).State:on_beduino_request_data(pos, topic, payload)
end,
on_node_load = function(pos)
CRD(pos).State:on_node_load(pos)
end,
}
local _, node_name_ta3, node_name_ta4 =
techage.register_consumer("injector", S("Injector"), tiles, {
cycle_time = CYCLE_TIME,
standby_ticks = STANDBY_TICKS,
formspec = formspec,
tubing = tubing,
quick_start = node_timer,
after_place_node = function(pos, placer)
local meta = M(pos)
local node = minetest.get_node(pos)
meta:set_int("pull_dir", techage.side_to_outdir("L", node.param2))
meta:set_int("push_dir", techage.side_to_outdir("R", node.param2))
local inv = M(pos):get_inventory()
inv:set_size('filter', 8)
local nvm = techage.get_nvm(pos)
M(pos):set_string("formspec", formspec(CRD(pos).State, pos, nvm))
end,
ta_rotate_node = function(pos, node, new_param2)
local nvm = techage.get_nvm(pos)
if CRD(pos).State:get_state(nvm) == techage.STOPPED then
Tube:after_dig_node(pos)
minetest.swap_node(pos, {name = node.name, param2 = new_param2})
Tube:after_place_node(pos)
local meta = M(pos)
meta:set_int("pull_dir", techage.side_to_outdir("L", new_param2))
meta:set_int("push_dir", techage.side_to_outdir("R", new_param2))
M(pos):set_string("formspec", formspec(CRD(pos).State, pos, nvm))
end
end,
allow_metadata_inventory_put = allow_metadata_inventory_put,
allow_metadata_inventory_take = allow_metadata_inventory_take,
allow_metadata_inventory_move = function() return 0 end,
on_receive_fields = on_receive_fields,
node_timer = node_timer,
on_rotate = screwdriver.disallow,
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
num_items = {0,0,1,4},
}, {false, false, true, true})
minetest.register_craft({
output = node_name_ta3,
recipe = {
{"", "default:steel_ingot", ""},
{"", "techage:ta3_pusher_pas", ""},
{"", "basic_materials:ic", ""},
},
})
minetest.register_craft({
output = node_name_ta4,
recipe = {
{"", "techage:aluminum", ""},
{"", "techage:ta4_pusher_pas", ""},
{"", "basic_materials:ic", ""},
},
})
@@ -0,0 +1,264 @@
--[[
TechAge
=======
Copyright (C) 2019-2023 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA5 Hyperloop Chest
]]--
-- for lazy programmers
local S2P = minetest.string_to_pos
local P2S = minetest.pos_to_string
local M = minetest.get_meta
local S = techage.S
local TA4_INV_SIZE = 32
local EX_POINTS = 15
local hyperloop = techage.hyperloop
local remote_pos = techage.hyperloop.remote_pos
local shared_inv = techage.shared_inv
local menu = techage.menu
local function formspec(pos)
local ndef = minetest.registered_nodes["techage:ta5_hl_chest"]
local status = M(pos):get_string("conn_status")
if hyperloop.is_server(pos) then
local title = ndef.description .. " " .. status
return "size[8,9]"..
"box[0,-0.1;7.8,0.5;#c6e8ff]" ..
"label[0.2,-0.1;" .. minetest.colorize( "#000000", title) .. "]" ..
"list[context;main;0,1;8,4;]"..
"list[current_player;main;0,5.3;8,4;]"..
"listring[context;main]"..
"listring[current_player;main]"
elseif hyperloop.is_client(pos) then
local title = ndef.description .. " " .. status
return "size[8,9]"..
"box[0,-0.1;7.8,0.5;#c6e8ff]" ..
"label[0.2,-0.1;" .. minetest.colorize( "#000000", title) .. "]" ..
"label[0.2,2;Inventory access on this node is disabled\ndue to minetest engine issues!]" ..
"list[current_player;main;0,5.3;8,4;]"
else
return menu.generate_formspec(pos, ndef, hyperloop.SUBMENU)
end
end
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
if techage.hyperloop.is_client(pos) then
return 0
end
shared_inv.before_inv_access(pos, listname)
local inv = minetest.get_inventory({type="node", pos=pos})
if inv and inv:room_for_item(listname, stack) then
return stack:get_count()
end
return 0
end
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
if techage.hyperloop.is_client(pos) then
return 0
end
shared_inv.before_inv_access(pos, listname)
local inv = minetest.get_inventory({type="node", pos=pos})
if inv and inv:contains_item(listname, stack) then
return stack:get_count()
end
return 0
end
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
if shared_inv.before_inv_access(pos, "main") then
return 0
end
if techage.hyperloop.is_client(pos) then
return 0
end
return count
end
minetest.register_node("techage:ta5_hl_chest", {
description = S("TA5 Hyperloop Chest"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta4.png^techage_frame_ta5_top.png",
"techage_filling_ta4.png^techage_frame_ta5.png",
"techage_filling_ta4.png^techage_frame_ta5.png^techage_appl_chest_back_ta4.png",
"techage_filling_ta4.png^techage_frame_ta5.png^techage_appl_chest_back_ta4.png",
"techage_filling_ta4.png^techage_frame_ta5.png^techage_appl_chest_back_ta4.png",
"techage_filling_ta4.png^techage_frame_ta5.png^techage_appl_chest_front_ta4.png",
},
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
inv:set_size('main', 32)
local number = techage.add_node(pos, "techage:ta5_hl_chest")
meta:set_string("node_number", number)
meta:set_string("owner", placer:get_player_name())
meta:set_string("formspec", formspec(pos))
meta:set_string("infotext", S("TA5 Hyperloop Chest").." "..number)
hyperloop.after_place_node(pos, placer, "chest")
end,
on_receive_fields = function(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
if techage.get_expoints(player) >= EX_POINTS then
if techage.menu.eval_input(pos, hyperloop.SUBMENU, fields) then
hyperloop.after_formspec(pos, fields)
shared_inv.on_rightclick(pos, player, "main")
M(pos):set_string("formspec", formspec(pos))
end
end
end,
on_timer = shared_inv.node_timer,
on_rightclick = function(pos, node, clicker)
shared_inv.on_rightclick(pos, clicker, "main")
M(pos):set_string("formspec", formspec(pos))
end,
can_dig = function(pos, player)
if minetest.is_protected(pos, player:get_player_name()) then
return false
end
shared_inv.before_inv_access(pos, "main")
local inv = minetest.get_meta(pos):get_inventory()
return inv:is_empty("main")
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
techage.remove_node(pos, oldnode, oldmetadata)
hyperloop.after_dig_node(pos, oldnode, oldmetadata, digger)
techage.del_mem(pos)
end,
allow_metadata_inventory_put = allow_metadata_inventory_put,
allow_metadata_inventory_take = allow_metadata_inventory_take,
allow_metadata_inventory_move = allow_metadata_inventory_move,
on_metadata_inventory_put = shared_inv.after_inv_access,
on_metadata_inventory_take = shared_inv.after_inv_access,
paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false,
sounds = default.node_sound_wood_defaults(),
})
techage.register_node({"techage:ta5_hl_chest"}, {
on_inv_request = function(pos, in_dir, access_type)
pos = remote_pos(pos)
if pos then
local meta = minetest.get_meta(pos)
if meta then
return meta:get_inventory(), "main"
end
end
end,
on_pull_item = function(pos, in_dir, num, item_name)
pos = remote_pos(pos)
if pos then
local meta = minetest.get_meta(pos)
if meta then
local inv = meta:get_inventory()
if inv then
return techage.get_items(pos, inv, "main", num)
end
end
end
return false
end,
on_push_item = function(pos, in_dir, stack)
if techage.hyperloop.is_paired(pos) then
pos = remote_pos(pos)
if pos then
local meta = minetest.get_meta(pos)
if meta then
local inv = meta:get_inventory()
if inv then
return techage.put_items(inv, "main", stack)
end
end
end
end
return false
end,
on_unpull_item = function(pos, in_dir, stack)
pos = remote_pos(pos)
if pos then
local meta = minetest.get_meta(pos)
if meta then
local inv = meta:get_inventory()
if inv then
return techage.put_items(inv, "main", stack)
end
end
end
return false
end,
on_recv_message = function(pos, src, topic, payload)
if topic == "state" then
local meta = minetest.get_meta(pos)
if meta then
local inv = meta:get_inventory()
if inv then
return techage.get_inv_state(inv, "main")
end
end
return "error"
elseif topic == "count" then
local meta = minetest.get_meta(pos)
if meta then
local inv = meta:get_inventory()
if inv then
return techage.check_inv_item(inv, "main", payload)
end
end
else
return "unsupported"
end
end,
on_beduino_request_data = function(pos, src, topic, payload)
if topic == 131 then -- Chest State
local meta = minetest.get_meta(pos)
if meta then
local inv = meta:get_inventory()
if inv then
return 0, {techage.get_inv_state_num(inv, "main")}
end
end
elseif topic == 192 and payload and type(payload) == "string" then
local meta = minetest.get_meta(pos)
if meta then
local inv = meta:get_inventory()
if inv then
return 0, {techage.check_inv_item(inv, "main", payload)}
end
end
else
return 2, ""
end
end,
})
minetest.register_craft({
type = "shapeless",
output = "techage:ta5_hl_chest",
recipe = {"techage:chest_ta4", "techage:ta5_aichip"}
})
minetest.register_on_mods_loaded(function()
if not minetest.global_exists("hyperloop") then
minetest.clear_craft({output = "techage:ta5_hl_chest"})
end
end)
@@ -0,0 +1,351 @@
--[[
TechAge
=======
Copyright (C) 2019-2025 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
Water Remover
The Water Remover removes water from an area of ​​up to 21 x 21 x 80 m.
It is mainly used to drain caves. The water remover is placed at the highest
point of the cave and removes the water from the cave to the lowest point.
It digs one water block every two seconds.
]]--
-- for lazy programmers
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local S2P = minetest.string_to_pos
local M = minetest.get_meta
local NDEF = function(pos) return minetest.registered_nodes[techage.get_node_lvm(pos).name] or {} end
-- Consumer Related Data
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end
local S = techage.S
local Pipe = techage.LiquidPipe
local liquid = networks.liquid
local menu = techage.menu
local TITLE = S("Water Remover")
local CYCLE_TIME = 2
local STANDBY_TICKS = 4
local COUNTDOWN_TICKS = 4
local Side2Facedir = {F=0, R=1, B=2, L=3, D=4, U=5}
local MENU = {
{
type = "dropdown",
choices = "9x9,11x11,13x13,15x15,17x17,19x19,21x21",
name = "area_size",
label = S("Area size"),
tooltip = S("Area where the water is to be removed"),
default = "9",
values = {9,11,13,15,17,19,21},
},
{
type = "numbers",
name = "area_depth",
label = S("Area depth"),
tooltip = S("Depth of the area where the water is to be removed (1-80)"),
default = "10",
check = function(value, player_name) return tonumber(value) >= 1 and tonumber(value) <= 80 end,
},
{
type = "output",
name = "curr_depth",
label = S("Current depth"),
tooltip = S("Current working depth of the water remover"),
},
}
local WaterNodes = {
"default:water_source",
"default:river_water_source",
-- Add more water nodes here
}
core.register_node("techage:air", {
description = "Techage Air",
inventory_image = "air.png",
wield_image = "air.png",
drawtype = "airlike",
paramtype = "light",
sunlight_propagates = true,
walkable = false,
pointable = false,
diggable = false,
buildable_to = true,
floodable = false, -- This is important!
air_equivalent = true,
drop = "",
groups = {not_in_creative_inventory=1},
})
local function formspec(self, pos, nvm)
return "size[8,4.4]" ..
"box[0,-0.1;7.8,0.5;#c6e8ff]" ..
"label[3.0,-0.1;" .. minetest.colorize( "#000000", TITLE) .. "]" ..
"image[6.4,0.8;1,1;" .. techage.get_power_image(pos, nvm) .. "]" ..
"image_button[6.4,2.2;1,1;".. self:get_state_button_image(nvm) .. ";state_button;]" ..
"tooltip[6.4,2.2;1,1;" .. self:get_state_tooltip(nvm) .. "]" ..
menu.generate_formspec_container(pos, NDEF(pos), MENU, 0.0, 5.8)
end
local function play_sound(pos)
minetest.sound_play("techage_scoopwater", {
pos = pos,
gain = 1.5,
max_hear_distance = 15})
end
local function on_node_state_change(pos, old_state, new_state)
local mem = techage.get_mem(pos)
local owner = M(pos):get_string("owner")
mem.co = nil
techage.unmark_position(owner)
end
local function get_pos(pos, facedir, side, steps)
facedir = (facedir + Side2Facedir[side]) % 4
local dir = vector.multiply(minetest.facedir_to_dir(facedir), steps or 1)
return vector.add(pos, dir)
end
local function get_dig_pos(pos, xoffs, zoffs)
return {x = pos.x + xoffs - 1, y = pos.y, z = pos.z + zoffs - 1}
end
-- pos is the quarry pos
local function get_corner_positions(pos, facedir, hole_diameter)
local _pos = get_pos(pos, facedir, "L")
local pos1 = get_pos(_pos, facedir, "F", math.floor((hole_diameter - 1) / 2))
local pos2 = get_pos(_pos, facedir, "B", math.floor((hole_diameter - 1) / 2))
pos2 = get_pos(pos2, facedir, "L", hole_diameter - 1)
if pos1.x > pos2.x then pos1.x, pos2.x = pos2.x, pos1.x end
if pos1.y > pos2.y then pos1.y, pos2.y = pos2.y, pos1.y end
if pos1.z > pos2.z then pos1.z, pos2.z = pos2.z, pos1.z end
return pos1, pos2
end
local function is_water(pos1, pos2)
return #minetest.find_nodes_in_area(pos1, pos2, WaterNodes) > 0
end
local function mark_area(pos1, pos2, owner)
pos1.y = pos1.y + 0.2
techage.mark_cube(owner, pos1, pos2, TITLE, "#FF0000", 40)
pos1.y = pos1.y - 0.2
end
local function dig_water_node(mypos, dig_pos)
local outdir = M(mypos):get_int("outdir")
local node = techage.get_node_lvm(dig_pos)
if node.name == "default:water_source" then
minetest.swap_node(dig_pos, {name = "techage:air", param2 = 0})
local leftover = liquid.put(mypos, Pipe, outdir, "techage:water", 1)
if leftover and leftover > 0 then
return "tank full"
end
return "ok"
end
return "no_water"
end
local function drain_task(pos, crd, nvm)
nvm.area_depth = M(pos):get_int("area_depth")
nvm.area_size = M(pos):get_int("area_size")
local y_first = pos.y
local y_last = y_first - nvm.area_depth + 1
local facedir = minetest.get_node(pos).param2
local owner = M(pos):get_string("owner")
local cnt = 0
local pos1, pos2 = get_corner_positions(pos, facedir, nvm.area_size)
nvm.level = 1
--print("drain_task", nvm.area_depth, nvm.area_size, y_first, y_last, M(pos):get_int("area_depth"))
for y_curr = y_first, y_last, -1 do
pos1.y = y_curr
pos2.y = y_curr
-- Restarting the server can detach the coroutine data.
-- Therefore, read nvm again.
nvm = techage.get_nvm(pos)
nvm.level = y_first - y_curr + 1
if minetest.is_area_protected(pos1, pos2, owner, 5) then
crd.State:fault(pos, nvm, S("area is protected"))
return
end
if is_water(pos1, pos2) then
mark_area(pos1, pos2, owner)
M(pos):set_string("curr_depth", nvm.level)
coroutine.yield()
for zoffs = 1, nvm.area_size do
for xoffs = 1, nvm.area_size do
local qpos = get_dig_pos(pos1, xoffs, zoffs)
while true do
local res = dig_water_node(pos, qpos)
if res == "tank full" then
crd.State:blocked(pos, nvm, S("tank full"))
techage.unmark_position(owner)
coroutine.yield()
elseif res == "no_water" then
break
else
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
if cnt % 4 == 0 then
play_sound(pos)
end
cnt = cnt + 1
coroutine.yield()
break
end
end
end
coroutine.yield()
end
techage.unmark_position(owner)
end
end
M(pos):set_string("curr_depth", nvm.level)
crd.State:stop(pos, nvm, S("finished"))
end
local function keep_running(pos, elapsed)
local mem = techage.get_mem(pos)
if not mem.co then
mem.co = coroutine.create(drain_task)
end
local nvm = techage.get_nvm(pos)
local crd = CRD(pos)
local _, err = coroutine.resume(mem.co, pos, crd, nvm)
if err then
minetest.log("error", "[" .. TITLE .. "] at pos " .. minetest.pos_to_string(pos) .. " " .. err)
end
end
local function on_rightclick(pos, node, clicker)
local nvm = techage.get_nvm(pos)
techage.set_activeformspec(pos, clicker)
M(pos):set_string("formspec", formspec(CRD(pos).State, pos, nvm))
end
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
local nvm = techage.get_nvm(pos)
if menu.eval_input(pos, MENU, fields) then
M(pos):set_string("formspec", formspec(CRD(pos).State, pos, nvm))
else
CRD(pos).State:state_button_event(pos, nvm, fields)
end
end
local function after_dig_node(pos, oldnode, oldmetadata, digger)
Pipe:after_dig_node(pos)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end
local tiles = {}
-- '#' will be replaced by the stage number
tiles.pas = {
-- up, down, right, left, back, front
"techage_filling_ta#.png^techage_frame_ta#_top.png",
"techage_filling_ta#.png^techage_frame_ta#_bottom.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_hole_pipe.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_quarry_left.png",
"techage_filling_ta#.png^techage_appl_liquidsampler.png^techage_frame_ta#.png",
"techage_filling_ta#.png^techage_appl_liquidsampler.png^techage_frame_ta#.png",
}
tiles.act = {
-- up, down, right, left, back, front
"techage_filling_ta#.png^techage_frame_ta#_top.png",
"techage_filling_ta#.png^techage_frame_ta#_bottom.png",
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_hole_pipe.png",
{
name = "techage_frame14_ta#.png^techage_quarry_left14.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 2.0,
},
},
"techage_filling_ta#.png^techage_appl_liquidsampler.png^techage_frame_ta#.png",
"techage_filling_ta#.png^techage_appl_liquidsampler.png^techage_frame_ta#.png",
}
local tubing = {
on_recv_message = function(pos, src, topic, payload)
if topic == "depth" then
local nvm = techage.get_nvm(pos)
return nvm.level or 0
else
return CRD(pos).State:on_receive_message(pos, topic, payload)
end
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
return CRD(pos).State:on_beduino_receive_cmnd(pos, topic, payload)
end,
on_beduino_request_data = function(pos, src, topic, payload)
if topic == 153 then -- Current Depth
local nvm = techage.get_nvm(pos)
return 0, {nvm.level or 0}
else
return CRD(pos).State:on_beduino_request_data(pos, topic, payload)
end
end,
on_node_load = function(pos)
CRD(pos).State:on_node_load(pos)
end,
}
local _, _, node_name_ta4 =
techage.register_consumer("waterremover", TITLE, tiles, {
drawtype = "normal",
cycle_time = CYCLE_TIME,
standby_ticks = STANDBY_TICKS,
formspec = formspec,
tubing = tubing,
on_state_change = on_node_state_change,
after_place_node = function(pos, placer)
M(pos):set_string("owner", placer:get_player_name())
M(pos):set_int("outdir", networks.side_to_outdir(pos, "R"))
M(pos):set_int("area_depth", 10)
M(pos):set_int("area_size", 9)
Pipe:after_place_node(pos)
end,
node_timer = keep_running,
on_receive_fields = on_receive_fields,
on_rightclick = on_rightclick,
after_dig_node = after_dig_node,
groups = {choppy=2, cracky=2, crumbly=2},
sounds = default.node_sound_wood_defaults(),
num_items = {0,0,0,1},
power_consumption = {0,0,0,10},
}, {false, false, false, true}
)
liquid.register_nodes({
"techage:ta4_waterremover_pas", "techage:ta4_waterremover_act",
}, Pipe, "pump", {"R"}, {})
minetest.register_craft({
output = node_name_ta4,
recipe = {
{"", "default:mese_crystal", ""},
{"", "techage:ta3_liquidsampler_pas", ""},
{"", "techage:ta4_wlanchip", ""},
},
})