460 lines
11 KiB
Lua
460 lines
11 KiB
Lua
local utils, mod_storage = ...
|
|
local S = utils.S
|
|
|
|
|
|
|
|
if utils.hopper_supported then
|
|
|
|
|
|
|
|
local hopper_interval = 1.0
|
|
|
|
|
|
|
|
local hopper_list = minetest.deserialize (mod_storage:get_string ("hopper_list"))
|
|
if type (hopper_list) ~= "table" then
|
|
hopper_list = {}
|
|
end
|
|
|
|
|
|
|
|
local function add_hopper_to_list (pos)
|
|
hopper_list[minetest.pos_to_string (pos, 0)] = true
|
|
|
|
mod_storage:set_string ("hopper_list", minetest.serialize (hopper_list))
|
|
end
|
|
|
|
|
|
|
|
local function remove_hopper_from_list (pos)
|
|
hopper_list[minetest.pos_to_string (pos, 0)] = nil
|
|
|
|
mod_storage:set_string ("hopper_list", minetest.serialize (hopper_list))
|
|
end
|
|
|
|
|
|
|
|
local input_dir =
|
|
{
|
|
[0] = { x = 0, y = 1, z = 0 },
|
|
{ x = 0, y = 0, z = 1 },
|
|
{ x = 0, y = 0, z = -1 },
|
|
{ x = 1, y = 0, z = 0 },
|
|
{ x = -1, y = 0, z = 0 },
|
|
{ x = 0, y = -1, z = 0 }
|
|
}
|
|
|
|
local function get_input_dir (node)
|
|
return input_dir[math.floor (node.param2 / 4)]
|
|
end
|
|
|
|
|
|
|
|
local function get_output_dir (node)
|
|
if node then
|
|
if node.name == "lwcomponents:hopper" then
|
|
return vector.multiply (get_input_dir (node), -1)
|
|
elseif node.name == "lwcomponents:hopper_horz" then
|
|
return minetest.facedir_to_dir (node.param2)
|
|
end
|
|
end
|
|
|
|
return nil
|
|
end
|
|
|
|
|
|
|
|
local function get_drop (pos)
|
|
local objs = minetest.get_objects_inside_radius (pos, 1)
|
|
|
|
for _, obj in pairs (objs) do
|
|
local obj_pos = (obj.get_pos and obj:get_pos ())
|
|
|
|
if obj_pos and utils.is_drop (obj) then
|
|
obj_pos = vector.round (obj_pos)
|
|
|
|
if vector.equals (pos, obj_pos) then
|
|
local stack = ItemStack (obj:get_luaentity ().itemstring)
|
|
|
|
if stack and not stack:is_empty () then
|
|
stack:set_count (1)
|
|
|
|
return stack, obj
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
|
|
local function take_drop (obj)
|
|
if utils.is_drop (obj) then
|
|
local stack = ItemStack (obj:get_luaentity ().itemstring)
|
|
|
|
if stack and not stack:is_empty () then
|
|
stack:set_count (stack:get_count () - 1)
|
|
|
|
if stack:is_empty () then
|
|
obj:get_luaentity().itemstring = ""
|
|
obj:remove()
|
|
else
|
|
obj:get_luaentity().itemstring = stack:to_string ()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
|
|
local function next_item_to_take (src_pos, src_node, src_inv_name)
|
|
if not src_inv_name or not minetest.registered_nodes[src_node.name] then
|
|
return
|
|
end
|
|
|
|
local src_meta = minetest.get_meta (src_pos)
|
|
local src_inv = (src_meta and src_meta:get_inventory ()) or nil
|
|
|
|
if src_inv then
|
|
local slots = src_inv:get_size (src_inv_name)
|
|
|
|
for slot = 1, slots, 1 do
|
|
local stack = src_inv:get_stack (src_inv_name, slot)
|
|
|
|
if stack and not stack:is_empty () then
|
|
stack:set_count (1)
|
|
|
|
return stack, slot
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
|
|
local function take_item (src_pos, src_inv_name, slot)
|
|
local src_meta = minetest.get_meta (src_pos)
|
|
local src_inv = (src_meta and src_meta:get_inventory ()) or nil
|
|
|
|
if src_inv then
|
|
local stack = src_inv:get_stack (src_inv_name, slot)
|
|
|
|
if stack and not stack:is_empty () then
|
|
stack:set_count (stack:get_count () - 1)
|
|
|
|
src_inv:set_stack (src_inv_name, slot, stack)
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
|
|
local function call_allow_metadata_inventory_put (def, pos, listname, index, stack, placer)
|
|
if def.allow_metadata_inventory_put and placer then
|
|
local result, count = pcall (def.allow_metadata_inventory_put,
|
|
pos, listname, index, stack, placer)
|
|
|
|
if result then
|
|
return count
|
|
end
|
|
end
|
|
|
|
return utils.settings.default_stack_max
|
|
end
|
|
|
|
|
|
|
|
local function call_on_metadata_inventory_put (def, pos, listname, index, stack, placer)
|
|
if def.on_metadata_inventory_put and placer then
|
|
pcall (def.on_metadata_inventory_put, pos, listname, index, stack, placer)
|
|
end
|
|
end
|
|
|
|
|
|
|
|
local function place_item (dest_pos, dest_node, dest_inv_name, stack, placer)
|
|
local dest_def = minetest.registered_nodes[dest_node.name]
|
|
|
|
if not dest_inv_name or not dest_def then
|
|
return
|
|
end
|
|
|
|
local dest_meta = minetest.get_meta (dest_pos)
|
|
local dest_inv = (dest_meta and dest_meta:get_inventory ()) or nil
|
|
|
|
if dest_inv then
|
|
local slots = dest_inv:get_size (dest_inv_name)
|
|
|
|
-- find existing stack
|
|
for slot = 1, slots, 1 do
|
|
local inv_stack = dest_inv:get_stack (dest_inv_name, slot)
|
|
|
|
if inv_stack and not inv_stack:is_empty () and
|
|
utils.is_same_item (inv_stack, stack) and
|
|
inv_stack:get_count () < inv_stack:get_stack_max () and
|
|
(call_allow_metadata_inventory_put (dest_def, dest_pos, dest_inv_name, slot, stack, placer) > 0) then
|
|
|
|
inv_stack:set_count (inv_stack:get_count () + 1)
|
|
dest_inv:set_stack (dest_inv_name, slot, inv_stack)
|
|
|
|
call_on_metadata_inventory_put (dest_def, dest_pos, dest_inv_name, slot, stack, placer)
|
|
|
|
return true
|
|
end
|
|
end
|
|
|
|
-- find empty slot
|
|
for slot = 1, slots, 1 do
|
|
local inv_stack = dest_inv:get_stack (dest_inv_name, slot)
|
|
|
|
if not inv_stack or inv_stack:is_empty () and
|
|
(call_allow_metadata_inventory_put (dest_def, dest_pos, dest_inv_name, slot, stack, placer) > 0) then
|
|
|
|
dest_inv:set_stack (dest_inv_name, slot, stack)
|
|
|
|
call_on_metadata_inventory_put (dest_def, dest_pos, dest_inv_name, slot, stack, placer)
|
|
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
|
|
|
|
local function get_player_object (pos)
|
|
local meta = minetest.get_meta (pos)
|
|
local placer_name = "<unknown>"
|
|
|
|
if meta then
|
|
placer_name = meta:get_string ("placer_name")
|
|
local placer = minetest.get_player_by_name (placer_name)
|
|
|
|
if placer then
|
|
return placer
|
|
end
|
|
|
|
if placer_name == "" then
|
|
placer_name = "<unknown>"
|
|
end
|
|
end
|
|
|
|
return utils.get_dummy_player (true, placer_name)
|
|
end
|
|
|
|
|
|
|
|
local function run_hopper_action (pos)
|
|
local node = utils.get_far_node (pos)
|
|
local dest_dir = get_output_dir (node)
|
|
|
|
if dest_dir then
|
|
local dest_pos = vector.add (pos, dest_dir)
|
|
local dest_node = utils.get_far_node (dest_pos)
|
|
|
|
if dest_node then
|
|
local registered_dest_invs = hopper.get_registered_inventories_for (dest_node.name)
|
|
|
|
if registered_dest_invs then
|
|
local placer = get_player_object (pos)
|
|
local src_pos = vector.add (pos, get_input_dir (node))
|
|
local drop
|
|
local stack
|
|
local slot = nil
|
|
local src_inv_name = nil
|
|
|
|
stack, drop = get_drop (src_pos)
|
|
|
|
if not stack then
|
|
local src_node = utils.get_far_node (src_pos)
|
|
|
|
if src_node then
|
|
local registered_src_invs = hopper.get_registered_inventories_for (src_node.name)
|
|
|
|
if registered_src_invs then
|
|
src_inv_name = registered_src_invs["top"]
|
|
stack, slot = next_item_to_take (src_pos, src_node, src_inv_name)
|
|
end
|
|
end
|
|
end
|
|
|
|
if stack then
|
|
local dest_side = (node.name == "lwcomponents:hopper" and "bottom") or "side"
|
|
local dest_inv_name = registered_dest_invs[dest_side]
|
|
|
|
if place_item (dest_pos, dest_node, dest_inv_name, stack, placer) then
|
|
if drop then
|
|
take_drop (drop)
|
|
else
|
|
take_item (src_pos, src_inv_name, slot)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
|
|
local function on_construct (pos)
|
|
add_hopper_to_list (pos)
|
|
end
|
|
|
|
|
|
|
|
local function on_destruct (pos)
|
|
remove_hopper_from_list (pos)
|
|
end
|
|
|
|
|
|
|
|
local function after_place_node (pos, placer, itemstack, pointed_thing)
|
|
local meta = minetest.get_meta (pos)
|
|
|
|
meta:set_string ("placer_name", (placer and placer:get_player_name ()) or "")
|
|
|
|
-- If return true no item is taken from itemstack
|
|
return false
|
|
end
|
|
|
|
|
|
|
|
local function on_place (itemstack, placer, pointed_thing)
|
|
if pointed_thing and pointed_thing.type == "node" then
|
|
local stack = ItemStack (itemstack)
|
|
local dir = vector.direction (pointed_thing.above, pointed_thing.under)
|
|
|
|
if dir.y == 0 then
|
|
minetest.item_place (ItemStack ("lwcomponents:hopper_horz"), placer, pointed_thing)
|
|
|
|
if not utils.is_creative (placer) then
|
|
stack:set_count (stack:get_count () - 1)
|
|
end
|
|
|
|
return stack
|
|
end
|
|
end
|
|
|
|
return minetest.item_place (itemstack, placer, pointed_thing)
|
|
end
|
|
|
|
|
|
|
|
minetest.register_node ("lwcomponents:hopper", {
|
|
description = S("Conduit Hopper"),
|
|
tiles = { "lwcomponents_hopper_top.png", "lwcomponents_hopper_vert_spout.png",
|
|
"lwcomponents_hopper_side.png", "lwcomponents_hopper_side.png",
|
|
"lwcomponents_hopper_side.png", "lwcomponents_hopper_side.png" },
|
|
drawtype = "nodebox",
|
|
node_box = {
|
|
type = "fixed",
|
|
fixed = {
|
|
{-0.3125, -0.5, -0.3125, 0.3125, -0.25, 0.3125}, -- spout_vert
|
|
{-0.375, -0.375, -0.375, 0.375, 0.3125, 0.375}, -- body
|
|
{-0.5, 0, -0.5, -0.3125, 0.5, 0.5}, -- funnel_1
|
|
{-0.5, 0, 0.3125, 0.5, 0.5, 0.5}, -- funnel_2
|
|
{-0.5, 0, -0.5, 0.5, 0.5, -0.3125}, -- funnel_3
|
|
{0.3125, 0, -0.5, 0.5, 0.5, 0.5}, -- funnel_4
|
|
}
|
|
},
|
|
selection_box = {
|
|
type = "fixed",
|
|
fixed = {
|
|
{-0.3125, -0.5, -0.3125, 0.3125, -0.25, 0.3125}, -- spout_vert
|
|
{-0.375, -0.375, -0.375, 0.375, 0.3125, 0.375}, -- body
|
|
{-0.5, 0, -0.5, 0.5, 0.5, 0.5}, -- funnel
|
|
}
|
|
},
|
|
collision_box = {
|
|
type = "fixed",
|
|
fixed = {
|
|
{-0.375, -0.375, -0.375, 0.375, 0.3125, 0.375}, -- body
|
|
{-0.5, 0, -0.5, 0.5, 0.5, 0.5}, -- funnel
|
|
{-0.3125, -0.3125, 0.5, 0.3125, 0.3125, 0.3125}, -- spout_side
|
|
}
|
|
},
|
|
paramtype = "light",
|
|
param1 = 0,
|
|
paramtype2 = "facedir",
|
|
param2 = 0,
|
|
drop = "lwcomponents:hopper",
|
|
groups = { cracky = 3 },
|
|
sounds = default.node_sound_stone_defaults (),
|
|
|
|
on_construct = on_construct,
|
|
on_destruct = on_destruct,
|
|
after_place_node = after_place_node,
|
|
on_place = on_place,
|
|
})
|
|
|
|
|
|
|
|
minetest.register_node ("lwcomponents:hopper_horz", {
|
|
description = S("Conduit Hopper"),
|
|
tiles = { "lwcomponents_hopper_top.png", "lwcomponents_hopper_bottom.png",
|
|
"lwcomponents_hopper_side.png", "lwcomponents_hopper_side.png",
|
|
"lwcomponents_hopper_side_spout.png", "lwcomponents_hopper_side.png" },
|
|
drawtype = "nodebox",
|
|
node_box = {
|
|
type = "fixed",
|
|
fixed = {
|
|
{-0.375, -0.375, -0.375, 0.375, 0.3125, 0.375}, -- body
|
|
{-0.5, 0, -0.5, -0.3125, 0.5, 0.5}, -- funnel_1
|
|
{-0.5, 0, 0.3125, 0.5, 0.5, 0.5}, -- funnel_2
|
|
{-0.5, 0, -0.5, 0.5, 0.5, -0.3125}, -- funnel_3
|
|
{0.3125, 0, -0.5, 0.5, 0.5, 0.5}, -- funnel_4
|
|
{-0.3125, -0.3125, 0.5, 0.3125, 0.3125, 0.3125}, -- spout_side
|
|
}
|
|
},
|
|
selection_box = {
|
|
type = "fixed",
|
|
fixed = {
|
|
{-0.375, -0.375, -0.375, 0.375, 0.3125, 0.375}, -- body
|
|
{-0.5, 0, -0.5, 0.5, 0.5, 0.5}, -- funnel
|
|
{-0.3125, -0.3125, 0.5, 0.3125, 0.3125, 0.3125}, -- spout_side
|
|
}
|
|
},
|
|
collision_box = {
|
|
type = "fixed",
|
|
fixed = {
|
|
{-0.375, -0.375, -0.375, 0.375, 0.3125, 0.375}, -- body
|
|
{-0.5, 0, -0.5, 0.5, 0.5, 0.5}, -- funnel
|
|
{-0.3125, -0.3125, 0.5, 0.3125, 0.3125, 0.3125}, -- spout_side
|
|
}
|
|
},
|
|
paramtype = "light",
|
|
param1 = 0,
|
|
paramtype2 = "facedir",
|
|
param2 = 0,
|
|
drop = "lwcomponents:hopper",
|
|
groups = { cracky = 3, not_in_creative_inventory = 1 },
|
|
sounds = default.node_sound_stone_defaults (),
|
|
|
|
on_construct = on_construct,
|
|
on_destruct = on_destruct,
|
|
after_place_node = after_place_node,
|
|
})
|
|
|
|
|
|
|
|
local function run_hoppers ()
|
|
for spos, _ in pairs (hopper_list) do
|
|
run_hopper_action (minetest.string_to_pos (spos))
|
|
end
|
|
|
|
minetest.after (hopper_interval, run_hoppers)
|
|
end
|
|
|
|
|
|
|
|
minetest.register_on_mods_loaded (function ()
|
|
minetest.after (3.0, run_hoppers)
|
|
end)
|
|
|
|
|
|
|
|
end -- utils.hopper_supported
|