1
0
Fork 0

Merge pull request 'hopper reimplementation' (#3980) from Morik666/MineClone2:hopper into master

Reviewed-on: MineClone2/MineClone2#3980
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
This commit is contained in:
the-real-herowl 2023-11-27 23:34:47 +00:00
commit 19728c5a19
18 changed files with 504 additions and 444 deletions

View File

@ -170,16 +170,8 @@ These groups are used mostly for informational purposes
* `ammo_bow=1`: Item is used as ammo for bows
* `non_combat_armor=1`: Item can be equipped as armor, but is not made for combat (e.g. zombie head, pumpkin)
* `container`: Node is a container which physically stores items within and has at least 1 inventory
* `container=2`: Has one inventory with list name `"main"`. Items can be placed and taken freely
* `container=3`: Same as `container=2`, but shulker boxes can not be inserted
* `container=4`: Furnace-like, has lists `"src"`, `"fuel"` and `"dst"`.
It is expected that this also reacts on `on_timer`;
the node timer must be started from other mods when they add into `"src"` or `"fuel"`
* `container=5`: Left part of a 2-part horizontal connected container. Both parts have a `"main"` inventory
list. Both inventories are considered to belong together. This is used for large chests.
* `container=6`: Same as above, but for the right part.
* `container=7`: Has inventory list "`main`", no movement allowed
* `container=1`: Other/unspecified container type
* `container=1`: Container type, which does not allow hoppers to transfer items
* `container=2`: Items can be placed and taken freely. Can have inventory with list name `"main"` or define `_mcl_hoppers_on_try_pull`, `_mcl_hoppers_on_try_push`, `_mcl_hoppers_on_after_pull`, `_mcl_hoppers_on_after_push` to play along hoppers nicely.
* `spawn_egg=1`: Spawn egg
* `pressure_plate=1`: Pressure plate (off)

View File

@ -241,34 +241,25 @@ function mcl_util.get_double_container_neighbor_pos(pos, param2, side)
end
end
-- Iterates through all items in the given inventory and
-- returns the slot of the first item which matches a condition.
-- Returns nil if no item was found.
--- source_inventory: Inventory to take the item from
--- source_list: List name of the source inventory from which to take the item
--- destination_inventory: Put item into this inventory
--- destination_list: List name of the destination inventory to which to put the item into
--- condition: Function which takes an itemstack and returns true if it matches the desired item condition.
--- If set to nil, the slot of the first item stack will be taken unconditionally.
-- dst_inventory and dst_list can also be nil if condition is nil.
function mcl_util.get_eligible_transfer_item_slot(src_inventory, src_list, dst_inventory, dst_list, condition)
local size = src_inventory:get_size(src_list)
--- Selects item stack to transfer from
---@param src_inventory InvRef Source innentory to pull from
---@param src_list string Name of source inventory list to pull from
---@param dst_inventory InvRef Destination inventory to push to
---@param dst_list string Name of destination inventory list to push to
---@param condition? fun(stack: ItemStack) Condition which items are allowed to be transfered.
---@return integer Item stack number to be transfered
function mcl_util.select_stack(src_inventory, src_list, dst_inventory, dst_list, condition)
local src_size = src_inventory:get_size(src_list)
local stack
for i = 1, size do
for i = 1, src_size do
stack = src_inventory:get_stack(src_list, i)
if not stack:is_empty() and (condition == nil or condition(stack, src_inventory, src_list, dst_inventory, dst_list)) then
if not stack:is_empty() and dst_inventory:room_for_item(dst_list, stack) and ((condition == nil or condition(stack))) then
return i
end
end
return nil
end
-- Returns true if itemstack is a shulker box
local function is_not_shulker_box(itemstack)
local g = minetest.get_item_group(itemstack:get_name(), "shulker_box")
return g == 0 or g == nil
end
-- Moves a single item from one inventory to another.
--- source_inventory: Inventory to take the item from
--- source_list: List name of the source inventory from which to take the item
@ -279,13 +270,6 @@ end
-- Returns true on success and false on failure
-- Possible failures: No item in source slot, destination inventory full
function mcl_util.move_item(source_inventory, source_list, source_stack_id, destination_inventory, destination_list)
if source_stack_id == -1 then
source_stack_id = mcl_util.get_first_occupied_inventory_slot(source_inventory, source_list)
if source_stack_id == nil then
return false
end
end
if not source_inventory:is_empty(source_list) then
local stack = source_inventory:get_stack(source_list, source_stack_id)
if not stack:is_empty() then
@ -303,150 +287,75 @@ function mcl_util.move_item(source_inventory, source_list, source_stack_id, dest
return false
end
-- Moves a single item from one container node into another. Performs a variety of high-level
-- checks to prevent invalid transfers such as shulker boxes into shulker boxes
--- source_pos: Position ({x,y,z}) of the node to take the item from
--- destination_pos: Position ({x,y,z}) of the node to put the item into
--- source_list (optional): List name of the source inventory from which to take the item. Default is normally "main"; "dst" for furnace
--- source_stack_id (optional): The inventory position ID of the source inventory to take the item from (-1 for slot of the first valid item; -1 is default)
--- destination_list (optional): List name of the destination inventory. Default is normally "main"; "src" for furnace
-- Returns true on success and false on failure.
function mcl_util.move_item_container(source_pos, destination_pos, source_list, source_stack_id, destination_list)
local dpos = table.copy(destination_pos)
local spos = table.copy(source_pos)
local snode = minetest.get_node(spos)
local dnode = minetest.get_node(dpos)
--- Try pushing item from hopper inventory to destination inventory
---@param pos Vector
---@param dst_pos Vector
function mcl_util.hopper_push(pos, dst_pos)
local hop_inv = minetest.get_meta(pos):get_inventory()
local hop_list = 'main'
local dctype = minetest.get_item_group(dnode.name, "container")
local sctype = minetest.get_item_group(snode.name, "container")
-- Get node pos' for item transfer
local dst = minetest.get_node(dst_pos)
if not minetest.registered_nodes[dst.name] then return end
local dst_type = minetest.get_item_group(dst.name, "container")
if dst_type ~= 2 then return end
local dst_def = minetest.registered_nodes[dst.name]
-- Container type 7 does not allow any movement
if sctype == 7 then
return false
local dst_list = 'main'
local dst_inv, stack_id
if dst_def._mcl_hoppers_on_try_push then
dst_inv, dst_list, stack_id = dst_def._mcl_hoppers_on_try_push(dst_pos, pos, hop_inv, hop_list)
else
local dst_meta = minetest.get_meta(dst_pos)
dst_inv = dst_meta:get_inventory()
stack_id = mcl_util.select_stack(hop_inv, hop_list, dst_inv, dst_list)
end
-- Normalize double container by forcing to always use the left segment first
local function normalize_double_container(pos, node, ctype)
if ctype == 6 then
pos = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "right")
if not pos then
return false
end
node = minetest.get_node(pos)
ctype = minetest.get_item_group(node.name, "container")
-- The left segment seems incorrect? We better bail out!
if ctype ~= 5 then
return false
end
if stack_id ~= nil then
local ok = mcl_util.move_item(hop_inv, hop_list, stack_id, dst_inv, dst_list)
if dst_def._mcl_hoppers_on_after_push then
dst_def._mcl_hoppers_on_after_push(dst_pos)
end
return pos, node, ctype
end
spos, snode, sctype = normalize_double_container(spos, snode, sctype)
dpos, dnode, dctype = normalize_double_container(dpos, dnode, dctype)
if not spos or not dpos then return false end
local smeta = minetest.get_meta(spos)
local dmeta = minetest.get_meta(dpos)
local sinv = smeta:get_inventory()
local dinv = dmeta:get_inventory()
-- Default source lists
if not source_list then
-- Main inventory for most container types
if sctype == 2 or sctype == 3 or sctype == 5 or sctype == 6 or sctype == 7 then
source_list = "main"
-- Furnace: output
elseif sctype == 4 then
source_list = "dst"
-- Unknown source container type. Bail out
else
return false
if ok then
return true
end
end
-- Automatically select stack slot ID if set to automatic
if not source_stack_id then
source_stack_id = -1
end
if source_stack_id == -1 then
local cond = nil
-- Prevent shulker box inception
if dctype == 3 then
cond = is_not_shulker_box
end
source_stack_id = mcl_util.get_eligible_transfer_item_slot(sinv, source_list, dinv, dpos, cond)
if not source_stack_id then
-- Try again if source is a double container
if sctype == 5 then
spos = mcl_util.get_double_container_neighbor_pos(spos, snode.param2, "left")
smeta = minetest.get_meta(spos)
sinv = smeta:get_inventory()
source_stack_id = mcl_util.get_eligible_transfer_item_slot(sinv, source_list, dinv, dpos, cond)
if not source_stack_id then
return false
end
else
return false
end
end
end
-- Abort transfer if shulker box wants to go into shulker box
if dctype == 3 then
local stack = sinv:get_stack(source_list, source_stack_id)
if stack and minetest.get_item_group(stack:get_name(), "shulker_box") == 1 then
return false
end
end
-- Container type 7 does not allow any placement
if dctype == 7 then
return false
end
-- If it's a container, put it into the container
if dctype ~= 0 then
-- Automatically select a destination list if omitted
if not destination_list then
-- Main inventory for most container types
if dctype == 2 or dctype == 3 or dctype == 5 or dctype == 6 or dctype == 7 then
destination_list = "main"
-- Furnace source slot
elseif dctype == 4 then
destination_list = "src"
end
end
if destination_list then
-- Move item
local ok = mcl_util.move_item(sinv, source_list, source_stack_id, dinv, destination_list)
-- Try transfer to neighbor node if transfer failed and double container
if not ok and dctype == 5 then
dpos = mcl_util.get_double_container_neighbor_pos(dpos, dnode.param2, "left")
dmeta = minetest.get_meta(dpos)
dinv = dmeta:get_inventory()
ok = mcl_util.move_item(sinv, source_list, source_stack_id, dinv, destination_list)
end
-- Update furnace
if ok and dctype == 4 then
-- Start furnace's timer function, it will sort out whether furnace can burn or not.
minetest.get_node_timer(dpos):start(1.0)
end
return ok
end
end
return false
end
-- Returns the ID of the first non-empty slot in the given inventory list
-- or nil, if inventory is empty.
function mcl_util.get_first_occupied_inventory_slot(inventory, listname)
return mcl_util.get_eligible_transfer_item_slot(inventory, listname)
-- Try pulling from source inventory to hopper inventory
---@param pos Vector
---@param src_pos Vector
function mcl_util.hopper_pull(pos, src_pos)
local hop_inv = minetest.get_meta(pos):get_inventory()
local hop_list = 'main'
-- Get node pos' for item transfer
local src = minetest.get_node(src_pos)
if not minetest.registered_nodes[src.name] then return end
local src_type = minetest.get_item_group(src.name, "container")
if src_type ~= 2 then return end
local src_def = minetest.registered_nodes[src.name]
local src_list = 'main'
local src_inv, stack_id
if src_def._mcl_hoppers_on_try_pull then
src_inv, src_list, stack_id = src_def._mcl_hoppers_on_try_pull(src_pos, pos, hop_inv, hop_list)
else
local src_meta = minetest.get_meta(src_pos)
src_inv = src_meta:get_inventory()
stack_id = mcl_util.select_stack(src_inv, src_list, hop_inv, hop_list)
end
if stack_id ~= nil then
local ok = mcl_util.move_item(src_inv, src_list, stack_id, hop_inv, hop_list)
if src_def._mcl_hoppers_on_after_pull then
src_def._mcl_hoppers_on_after_pull(src_pos)
end
end
end
local function drop_item_stack(pos, stack)

View File

@ -125,7 +125,11 @@ local dropperdef = {
local dropnode = minetest.get_node(droppos)
-- Do not drop into solid nodes, unless they are containers
local dropnodedef = minetest.registered_nodes[dropnode.name]
if dropnodedef.walkable and not dropnodedef.groups.container then
if dropnodedef.groups.container == 2 then
-- If they are containers - double down as hopper
mcl_util.hopper_push(pos, droppos)
end
if dropnodedef.walkable then
return
end
local stacks = {}
@ -141,25 +145,18 @@ local dropperdef = {
local dropitem = ItemStack(stack)
dropitem:set_count(1)
local stack_id = stacks[r].stackpos
-- If it's a container, attempt to put it into the container
local dropped = mcl_util.move_item_container(pos, droppos, nil, stack_id)
-- No container?
if not dropped and not dropnodedef.groups.container then
-- Drop item normally
local pos_variation = 100
droppos = vector.offset(droppos,
math.random(-pos_variation, pos_variation) / 1000,
math.random(-pos_variation, pos_variation) / 1000,
math.random(-pos_variation, pos_variation) / 1000
)
local item_entity = minetest.add_item(droppos, dropitem)
local drop_vel = vector.subtract(droppos, pos)
local speed = 3
item_entity:set_velocity(vector.multiply(drop_vel, speed))
stack:take_item()
inv:set_stack("main", stack_id, stack)
end
local pos_variation = 100
droppos = vector.offset(droppos,
math.random(-pos_variation, pos_variation) / 1000,
math.random(-pos_variation, pos_variation) / 1000,
math.random(-pos_variation, pos_variation) / 1000
)
local item_entity = minetest.add_item(droppos, dropitem)
local drop_vel = vector.subtract(droppos, pos)
local speed = 3
item_entity:set_velocity(vector.multiply(drop_vel, speed))
stack:take_item()
inv:set_stack("main", stack_id, stack)
end
end,
rules = mesecon.rules.alldirs,

View File

@ -453,7 +453,7 @@ minetest.register_node("mcl_blast_furnace:blast_furnace", {
"blast_furnace_side.png", "blast_furnace_front.png"
},
paramtype2 = "facedir",
groups = { pickaxey = 1, container = 4, deco_block = 1, material_stone = 1 },
groups = { pickaxey = 1, container = 2, deco_block = 1, material_stone = 1 },
is_ground_content = false,
sounds = mcl_sounds.node_sound_stone_defaults(),
@ -514,6 +514,11 @@ minetest.register_node("mcl_blast_furnace:blast_furnace", {
_mcl_blast_resistance = 3.5,
_mcl_hardness = 3.5,
on_rotate = on_rotate,
_mcl_hoppers_on_try_pull = mcl_furnaces.hoppers_on_try_pull,
_mcl_hoppers_on_try_push = mcl_furnaces.hoppers_on_try_push,
_mcl_hoppers_on_after_push = function(pos)
minetest.get_node_timer(pos):start(1.0)
end,
})
minetest.register_node("mcl_blast_furnace:blast_furnace_active", {
@ -531,7 +536,7 @@ minetest.register_node("mcl_blast_furnace:blast_furnace_active", {
paramtype = "light",
light_source = LIGHT_ACTIVE_FURNACE,
drop = "mcl_blast_furnace:blast_furnace",
groups = { pickaxey = 1, container = 4, deco_block = 1, not_in_creative_inventory = 1, material_stone = 1 },
groups = { pickaxey = 1, container = 2, deco_block = 1, not_in_creative_inventory = 1, material_stone = 1 },
is_ground_content = false,
sounds = mcl_sounds.node_sound_stone_defaults(),
on_timer = blast_furnace_node_timer,
@ -574,6 +579,8 @@ minetest.register_node("mcl_blast_furnace:blast_furnace_active", {
_mcl_hardness = 3.5,
on_rotate = on_rotate,
after_rotate = after_rotate_active,
_mcl_hoppers_on_try_pull = mcl_furnaces.hoppers_on_try_pull,
_mcl_hoppers_on_try_push = mcl_furnaces.hoppers_on_try_push,
})
minetest.register_craft({

View File

@ -438,7 +438,7 @@ minetest.register_node("mcl_books:bookshelf", {
flammable = 3,
fire_encouragement = 30,
fire_flammability = 20,
container = 1
container = 2
},
drop = "mcl_books:book 3",
sounds = wood_sound,
@ -472,6 +472,12 @@ minetest.register_node("mcl_books:bookshelf", {
on_blast = on_blast,
on_rightclick = bookshelf_gui,
on_destruct = close_forms,
_mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv, "main", mcl_util.select_stack(hop_inv, hop_list, inv, "main",
function(stack) return minetest.get_item_group(stack:get_name(), "book") == 1 or stack:get_name() == "mcl_enchanting:book_enchanted" end)
end,
})
minetest.register_craft({

View File

@ -376,13 +376,41 @@ local function allow_take(pos, listname, index, stack, player)
end
end
local function hoppers_on_try_push(pos, hop_pos, hop_inv, hop_list)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if math.abs(pos.y - hop_pos.y) > math.abs(pos.x - hop_pos.x) and math.abs(pos.y - hop_pos.y) > math.abs(pos.z - hop_pos.z) then
return inv, "input", mcl_util.select_stack(hop_inv, hop_list, inv, "input",
function(stack) return minetest.get_item_group(stack:get_name(), "brewitem") == 1 and minetest.get_item_group(stack:get_name(), "bottle") == 0 end)
else
local stack = mcl_util.select_stack(hop_inv, hop_list, inv, "fuel", function(stack) return stack:get_name() == "mcl_mobitems:blaze_powder" end)
if stack then
return inv, "fuel", stack
else
return inv, "stand", mcl_util.select_stack(hop_inv, hop_list, inv, "stand",
function(stack) return minetest.get_item_group(stack:get_name(), "bottle") == 1 end)
end
end
end
local function hoppers_on_try_pull(pos, hop_pos, hop_inv, hop_list)
local meta = minetest.get_meta(pos)
local stand_timer = meta:get_float("stand_timer") or 0
if stand_timer > 0 then
return nil, nil, nil
end
local inv = meta:get_inventory()
local stack = mcl_util.select_stack(inv, "stand", hop_inv, hop_list)
return inv, "stand", stack
end
minetest.register_node("mcl_brewing:stand_000", {
description = S("Brewing Stand"),
_doc_items_longdesc = S("The stand allows you to brew potions!"),
_doc_items_usagehelp = doc_string,
_tt_help = S("Brew Potions"),
groups = {pickaxey=1, brewitem=1 },
groups = {pickaxey=1, container = 2, brewitem=1 },
tiles = tiles,
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "clip" or true,
drop = "mcl_brewing:stand",
@ -444,12 +472,20 @@ minetest.register_node("mcl_brewing:stand_000", {
on_timer = brewing_stand_timer,
on_rotate = on_rotate,
_mcl_hoppers_on_try_push = hoppers_on_try_push,
_mcl_hoppers_on_try_pull = hoppers_on_try_pull,
_mcl_hoppers_on_after_push = function(pos)
on_put(pos, nil, nil, nil, nil)
end,
_mcl_hoppers_on_after_pull = function(pos)
on_put(pos, nil, nil, nil, nil)
end,
})
minetest.register_node("mcl_brewing:stand_100", {
description = S("Brewing Stand"),
_doc_items_create_entry = false,
_tt_help = S("Brew Potions"),
groups = {pickaxey=1, not_in_creative_inventory = 1, not_in_craft_guide = 1},
groups = { pickaxey=1, container = 2, not_in_creative_inventory = 1, not_in_craft_guide = 1 },
tiles = tiles,
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "clip" or true,
drop = "mcl_brewing:stand",
@ -514,13 +550,21 @@ minetest.register_node("mcl_brewing:stand_100", {
end,
on_timer = brewing_stand_timer,
on_rotate = on_rotate,
_mcl_hoppers_on_try_push = hoppers_on_try_push,
_mcl_hoppers_on_try_pull = hoppers_on_try_pull,
_mcl_hoppers_on_after_push = function(pos)
on_put(pos, nil, nil, nil, nil)
end,
_mcl_hoppers_on_after_pull = function(pos)
on_put(pos, nil, nil, nil, nil)
end,
})
minetest.register_node("mcl_brewing:stand_010", {
description = S("Brewing Stand"),
_doc_items_create_entry = false,
_tt_help = S("Brew Potions"),
groups = {pickaxey=1, not_in_creative_inventory = 1, not_in_craft_guide = 1},
groups = {pickaxey=1, container = 2, not_in_creative_inventory = 1, not_in_craft_guide = 1},
tiles = tiles,
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "clip" or true,
drop = "mcl_brewing:stand",
@ -586,13 +630,21 @@ minetest.register_node("mcl_brewing:stand_010", {
end,
on_timer = brewing_stand_timer,
on_rotate = on_rotate,
_mcl_hoppers_on_try_push = hoppers_on_try_push,
_mcl_hoppers_on_try_pull = hoppers_on_try_pull,
_mcl_hoppers_on_after_push = function(pos)
on_put(pos, nil, nil, nil, nil)
end,
_mcl_hoppers_on_after_pull = function(pos)
on_put(pos, nil, nil, nil, nil)
end,
})
minetest.register_node("mcl_brewing:stand_001", {
description = S("Brewing Stand"),
_doc_items_create_entry = false,
_tt_help = S("Brew Potions"),
groups = {pickaxey=1, not_in_creative_inventory = 1, not_in_craft_guide = 1},
groups = {pickaxey=1, container = 2, not_in_creative_inventory = 1, not_in_craft_guide = 1},
tiles = tiles,
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "clip" or true,
drop = "mcl_brewing:stand",
@ -653,13 +705,21 @@ minetest.register_node("mcl_brewing:stand_001", {
end,
on_timer = brewing_stand_timer,
on_rotate = on_rotate,
_mcl_hoppers_on_try_push = hoppers_on_try_push,
_mcl_hoppers_on_try_pull = hoppers_on_try_pull,
_mcl_hoppers_on_after_push = function(pos)
on_put(pos, nil, nil, nil, nil)
end,
_mcl_hoppers_on_after_pull = function(pos)
on_put(pos, nil, nil, nil, nil)
end,
})
minetest.register_node("mcl_brewing:stand_110", {
description = S("Brewing Stand"),
_doc_items_create_entry = false,
_tt_help = S("Brew Potions"),
groups = {pickaxey=1, not_in_creative_inventory = 1, not_in_craft_guide = 1},
groups = {pickaxey=1, container = 2, not_in_creative_inventory = 1, not_in_craft_guide = 1},
tiles = tiles,
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "clip" or true,
drop = "mcl_brewing:stand",
@ -730,13 +790,21 @@ minetest.register_node("mcl_brewing:stand_110", {
end,
on_timer = brewing_stand_timer,
on_rotate = on_rotate,
_mcl_hoppers_on_try_push = hoppers_on_try_push,
_mcl_hoppers_on_try_pull = hoppers_on_try_pull,
_mcl_hoppers_on_after_push = function(pos)
on_put(pos, nil, nil, nil, nil)
end,
_mcl_hoppers_on_after_pull = function(pos)
on_put(pos, nil, nil, nil, nil)
end,
})
minetest.register_node("mcl_brewing:stand_101", {
description = S("Brewing Stand"),
_doc_items_create_entry = false,
_tt_help = S("Brew Potions"),
groups = {pickaxey=1, not_in_creative_inventory = 1, not_in_craft_guide = 1},
groups = {pickaxey=1, container = 2, not_in_creative_inventory = 1, not_in_craft_guide = 1},
tiles = tiles,
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "clip" or true,
drop = "mcl_brewing:stand",
@ -803,13 +871,21 @@ minetest.register_node("mcl_brewing:stand_101", {
end,
on_timer = brewing_stand_timer,
on_rotate = on_rotate,
_mcl_hoppers_on_try_push = hoppers_on_try_push,
_mcl_hoppers_on_try_pull = hoppers_on_try_pull,
_mcl_hoppers_on_after_push = function(pos)
on_put(pos, nil, nil, nil, nil)
end,
_mcl_hoppers_on_after_pull = function(pos)
on_put(pos, nil, nil, nil, nil)
end,
})
minetest.register_node("mcl_brewing:stand_011", {
description = S("Brewing Stand"),
_doc_items_create_entry = false,
_tt_help = S("Brew Potions"),
groups = {pickaxey=1, not_in_creative_inventory = 1, not_in_craft_guide = 1},
groups = {pickaxey=1, container = 2, not_in_creative_inventory = 1, not_in_craft_guide = 1},
tiles = tiles,
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "clip" or true,
drop = "mcl_brewing:stand",
@ -876,13 +952,21 @@ minetest.register_node("mcl_brewing:stand_011", {
end,
on_timer = brewing_stand_timer,
on_rotate = on_rotate,
_mcl_hoppers_on_try_push = hoppers_on_try_push,
_mcl_hoppers_on_try_pull = hoppers_on_try_pull,
_mcl_hoppers_on_after_push = function(pos)
on_put(pos, nil, nil, nil, nil)
end,
_mcl_hoppers_on_after_pull = function(pos)
on_put(pos, nil, nil, nil, nil)
end,
})
minetest.register_node("mcl_brewing:stand_111", {
description = S("Brewing Stand"),
_doc_items_create_entry = false,
_tt_help = S("Brew Potions"),
groups = {pickaxey=1, not_in_creative_inventory = 1, not_in_craft_guide = 1},
groups = {pickaxey=1, container = 2, not_in_creative_inventory = 1, not_in_craft_guide = 1},
tiles = tiles,
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "clip" or true,
drop = "mcl_brewing:stand",
@ -956,6 +1040,14 @@ minetest.register_node("mcl_brewing:stand_111", {
end,
on_timer = brewing_stand_timer,
on_rotate = on_rotate,
_mcl_hoppers_on_try_push = hoppers_on_try_push,
_mcl_hoppers_on_try_pull = hoppers_on_try_pull,
_mcl_hoppers_on_after_push = function(pos)
on_put(pos, nil, nil, nil, nil)
end,
_mcl_hoppers_on_after_pull = function(pos)
on_put(pos, nil, nil, nil, nil)
end,
})
minetest.register_craft({

View File

@ -10,6 +10,8 @@ local sf = string.format
local mod_doc = minetest.get_modpath("doc")
mcl_chests = {}
-- Christmas chest setup
local it_is_christmas = false
local date = os.date("*t")
@ -596,7 +598,7 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile
groups = {
handy = 1,
axey = 1,
container = 5,
container = 2,
not_in_creative_inventory = 1,
material_wood = 1,
flammable = -1,
@ -751,6 +753,34 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile
end,
mesecons = mesecons,
on_rotate = no_rotate,
_mcl_hoppers_on_try_pull = function(pos, hop_pos, hop_inv, hop_list)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local stack_id = mcl_util.select_stack(inv, "main", hop_inv, hop_list)
if stack_id ~= nil then
return inv, "main", stack_id
end
local node = minetest.get_node(pos)
local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "left")
local meta_other = minetest.get_meta(pos_other)
local inv_other = meta_other:get_inventory()
stack_id = mcl_util.select_stack(inv_other, "main", hop_inv, hop_list)
return inv_other, "main", stack_id
end,
_mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local stack_id = mcl_util.select_stack(hop_inv, hop_list, inv, "main")
if stack_id ~= nil then
return inv, "main", stack_id
end
local node = minetest.get_node(pos)
local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "left")
local meta_other = minetest.get_meta(pos_other)
local inv_other = meta_other:get_inventory()
stack_id = mcl_util.select_stack(hop_inv, hop_list, inv_other, "main")
return inv_other, "main", stack_id
end,
})
minetest.register_node("mcl_chests:" .. basename .. "_right", {
@ -766,7 +796,7 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile
groups = {
handy = 1,
axey = 1,
container = 6,
container = 2,
not_in_creative_inventory = 1,
material_wood = 1,
flammable = -1,
@ -916,6 +946,34 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile
end,
mesecons = mesecons,
on_rotate = no_rotate,
_mcl_hoppers_on_try_pull = function(pos, hop_pos, hop_inv, hop_list)
local node = minetest.get_node(pos)
local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "right")
local meta_other = minetest.get_meta(pos_other)
local inv_other = meta_other:get_inventory()
local stack_id = mcl_util.select_stack(inv_other, "main", hop_inv, hop_list)
if stack_id ~= nil then
return inv_other, "main", stack_id
end
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
stack_id = mcl_util.select_stack(inv, "main", hop_inv, hop_list)
return inv, "main", stack_id
end,
_mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list)
local node = minetest.get_node(pos)
local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "right")
local meta_other = minetest.get_meta(pos_other)
local inv_other = meta_other:get_inventory()
local stack_id = mcl_util.select_stack(hop_inv, hop_list, inv_other, "main")
if stack_id ~= nil then
return inv_other, "main", stack_id
end
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
stack_id = mcl_util.select_stack(hop_inv, hop_list, inv, "main")
return inv, "main", stack_id
end,
})
if mod_doc then
@ -1305,7 +1363,7 @@ for color, desc in pairs(boxtypes) do
groups = {
handy = 1,
pickaxey = 1,
container = 3,
container = 2,
deco_block = 1,
dig_by_piston = 1,
shulker_box = 1,
@ -1378,7 +1436,7 @@ for color, desc in pairs(boxtypes) do
groups = {
handy = 1,
pickaxey = 1,
container = 3,
container = 2,
deco_block = 1,
dig_by_piston = 1,
shulker_box = 1,
@ -1471,6 +1529,11 @@ for color, desc in pairs(boxtypes) do
end,
_mcl_blast_resistance = 6,
_mcl_hardness = 2,
_mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv, "main", mcl_util.select_stack(hop_inv, hop_list, inv, "main", mcl_chests.is_not_shulker_box)
end,
})
if mod_doc and not is_canonical then
@ -1487,6 +1550,14 @@ for color, desc in pairs(boxtypes) do
})
end
--- Returns false if itemstack is a shulker box
---@param itemstack ItemStack
---@return boolean
function mcl_chests.is_not_shulker_box(stack)
local g = minetest.get_item_group(stack:get_name(), "shulker_box")
return g == 0 or g == nil
end
minetest.register_craft({
output = "mcl_chests:violet_shulker_box",
recipe = {

View File

@ -81,34 +81,44 @@ local function composter_add_item(pos, node, player, itemstack, pointed_thing)
max_hear_distance = 16,
}, true)
end
-- calculate leveling up chance
local rand = math.random(0,100)
if chance >= rand then
-- get current compost level
local level = registered_nodes[node.name]["_mcl_compost_level"]
-- spawn green particles above new layer
mcl_dye.add_bone_meal_particle(vector_offset(pos, 0, level/8, 0))
-- update composter block
if level < 7 then
level = level + 1
else
level = "ready"
end
swap_node(pos, {name = "mcl_composters:composter_" .. level})
minetest.sound_play({name="default_grass_footstep", gain=0.4}, {
pos = pos,
gain= 0.4,
max_hear_distance = 16,
}, true)
-- a full composter becomes ready for harvest after one second
-- the block will get updated by the node timer callback set in node reg def
if level == 7 then
local timer = get_node_timer(pos)
composter_progress_chance(pos, node, chance)
end
return itemstack
end
--- Math and node swap during compost progression
---@param pos Vector Position of the node
---@param node node
---@param chance integer Value of "compostability" group of inserted item
function composter_progress_chance(pos, node, chance)
-- calculate leveling up chance
local rand = math.random(0,100)
if chance >= rand then
-- get current compost level
local level = registered_nodes[node.name]["_mcl_compost_level"]
-- spawn green particles above new layer
mcl_dye.add_bone_meal_particle(vector_offset(pos, 0, level/8, 0))
-- update composter block
if level < 7 then
level = level + 1
else
level = "ready"
end
swap_node(pos, {name = "mcl_composters:composter_" .. level})
minetest.sound_play({name="default_grass_footstep", gain=0.4}, {
pos = pos,
gain= 0.4,
max_hear_distance = 16,
}, true)
-- a full composter becomes ready for harvest after one second
-- the block will get updated by the node timer callback set in node reg def
if level == 7 then
local timer = get_node_timer(pos)
if not timer:is_started() then
timer:start(1)
end
end
end
return itemstack
end
--- Update a full composter block to ready for harvesting.
@ -147,6 +157,10 @@ local function composter_harvest(pos, node, player, itemstack, pointed_thing)
record_protection_violation(pos, name)
return itemstack
end
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
--remove bone meal from internal inventory
inv:set_stack("dst", 1, ItemStack())
-- reset ready type composter to empty type
swap_node(pos, {name="mcl_composters:composter"})
-- spawn bone meal item
@ -175,6 +189,14 @@ local function composter_get_nodeboxes(level)
}
end
local function hopper_push_condition(stack)
local chance = get_item_group(stack:get_name(), "compostability")
if chance > 0 then
return true
end
return false
end
--- Register empty composter node.
--
-- This is the craftable base model that can be placed in an inventory.
@ -197,12 +219,40 @@ minetest.register_node("mcl_composters:composter", {
groups = {
handy=1, material_wood=1, deco_block=1, dirtifier=1,
flammable=2, fire_encouragement=3, fire_flammability=4,
container = 2
},
sounds = mcl_sounds.node_sound_wood_defaults(),
_mcl_hardness = 0.6,
_mcl_blast_resistance = 0.6,
_mcl_compost_level = 0,
on_rightclick = composter_add_item
on_rightclick = composter_add_item,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
inv:set_size("src", 1)
inv:set_size("dst", 1)
end,
_mcl_hoppers_on_try_pull = function(pos, hop_pos, hop_inv, hop_list)
return nil, nil, nil
end,
_mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv, "src", mcl_util.select_stack(hop_inv, hop_list, inv, "src", hopper_push_condition)
end,
_mcl_hoppers_on_after_push = function(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local stack = inv:get_stack("src", 1)
if not stack:is_empty() then
local chance = get_item_group(stack:get_name(), "compostability")
if chance > 0 then
local node = minetest.get_node(pos)
composter_progress_chance(pos, node, chance)
end
end
inv:remove_item("src", stack)
end,
})
--- Template function for composters with compost.
@ -228,7 +278,7 @@ local function register_filled_composter(level)
handy=1, material_wood=1, deco_block=1, dirtifier=1,
not_in_creative_inventory=1, not_in_craft_guide=1,
flammable=2, fire_encouragement=3, fire_flammability=4,
comparator_signal=level
comparator_signal=level, container = 2
},
sounds = mcl_sounds.node_sound_wood_defaults(),
drop = "mcl_composters:composter",
@ -236,7 +286,28 @@ local function register_filled_composter(level)
_mcl_blast_resistance = 0.6,
_mcl_compost_level = level,
on_rightclick = composter_add_item,
on_timer = composter_ready
on_timer = composter_ready,
_mcl_hoppers_on_try_pull = function(pos, hop_pos, hop_inv, hop_list)
return nil, nil, nil
end,
_mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv, "src", mcl_util.select_stack(hop_inv, hop_list, inv, "src", hopper_push_condition)
end,
_mcl_hoppers_on_after_push = function(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local stack = inv:get_stack("src", 1)
if not stack:is_empty() then
local chance = get_item_group(stack:get_name(), "compostability")
if chance > 0 then
local node = minetest.get_node(pos)
composter_progress_chance(pos, node, chance)
end
end
inv:remove_item("src", stack)
end,
})
-- Add entry aliases for the Help
@ -270,14 +341,32 @@ minetest.register_node("mcl_composters:composter_ready", {
handy=1, material_wood=1, deco_block=1, dirtifier=1,
not_in_creative_inventory=1, not_in_craft_guide=1,
flammable=2, fire_encouragement=3, fire_flammability=4,
comparator_signal=8
comparator_signal=8, container = 2
},
sounds = mcl_sounds.node_sound_wood_defaults(),
drop = "mcl_composters:composter",
_mcl_hardness = 0.6,
_mcl_blast_resistance = 0.6,
_mcl_compost_level = 7,
on_rightclick = composter_harvest
on_rightclick = composter_harvest,
_mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list)
return nil, nil, nil
end,
_mcl_hoppers_on_try_pull = function(pos, hop_pos, hop_inv, hop_list)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
--remove bone meal from internal inventory
inv:set_stack("dst", 1, ItemStack())
inv:add_item("dst", "mcl_bone_meal:bone_meal")
local stack = inv:get_stack("dst", 1)
if not stack:is_empty() and hop_inv:room_for_item(hop_list, stack) then
return inv, "dst", 1
end
return nil, nil, nil
end,
_mcl_hoppers_on_after_pull = function(pos)
minetest.swap_node(pos, {name = "mcl_composters:composter"})
end,
})
-- Add entry aliases for the Help

View File

@ -1,5 +1,5 @@
name = mcl_composters
author = kabou
description = Composters can convert various organic items into bonemeal.
depends = mcl_core, mcl_sounds, mcl_dye
depends = mcl_core, mcl_sounds, mcl_dye, mcl_hoppers
optional_depends = doc

View File

@ -4,6 +4,8 @@ local F = minetest.formspec_escape
local LIGHT_ACTIVE_FURNACE = 13
mcl_furnaces = {}
--
-- Formspecs
--
@ -445,6 +447,31 @@ local function furnace_node_timer(pos, elapsed)
return result
end
function mcl_furnaces.hoppers_on_try_pull(pos, hop_pos, hop_inv, hop_list)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local stack = inv:get_stack("dst", 1)
if not stack:is_empty() and hop_inv:room_for_item(hop_list, stack) then
return inv, "dst", 1
end
-- Allow empty bucket extraction
stack = inv:get_stack("fuel", 1)
if not stack:is_empty() and not mcl_util.is_fuel(stack) and hop_inv:room_for_item(hop_list, stack) then
return inv, "fuel", 1
end
return nil, nil, nil
end
function mcl_furnaces.hoppers_on_try_push(pos, hop_pos, hop_inv, hop_list)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if math.abs(pos.y - hop_pos.y) > math.abs(pos.x - hop_pos.x) and math.abs(pos.y - hop_pos.y) > math.abs(pos.z - hop_pos.z) then
return inv, "src", mcl_util.select_stack(hop_inv, hop_list, inv, "src")
else
return inv, "fuel", mcl_util.select_stack(hop_inv, hop_list, inv, "fuel", mcl_util.is_fuel)
end
end
local on_rotate, after_rotate_active
if minetest.get_modpath("screwdriver") then
on_rotate = screwdriver.rotate_simple
@ -475,7 +502,7 @@ minetest.register_node("mcl_furnaces:furnace", {
"default_furnace_side.png", "default_furnace_front.png"
},
paramtype2 = "facedir",
groups = { pickaxey = 1, container = 4, deco_block = 1, material_stone = 1 },
groups = { pickaxey = 1, container = 2, deco_block = 1, material_stone = 1 },
is_ground_content = false,
sounds = mcl_sounds.node_sound_stone_defaults(),
@ -538,6 +565,11 @@ minetest.register_node("mcl_furnaces:furnace", {
_mcl_blast_resistance = 3.5,
_mcl_hardness = 3.5,
on_rotate = on_rotate,
_mcl_hoppers_on_try_pull = mcl_furnaces.hoppers_on_try_pull,
_mcl_hoppers_on_try_push = mcl_furnaces.hoppers_on_try_push,
_mcl_hoppers_on_after_push = function(pos)
minetest.get_node_timer(pos):start(1.0)
end,
})
minetest.register_node("mcl_furnaces:furnace_active", {
@ -552,7 +584,7 @@ minetest.register_node("mcl_furnaces:furnace_active", {
paramtype = "light",
light_source = LIGHT_ACTIVE_FURNACE,
drop = "mcl_furnaces:furnace",
groups = { pickaxey = 1, container = 4, deco_block = 1, not_in_creative_inventory = 1, material_stone = 1 },
groups = { pickaxey = 1, container = 2, deco_block = 1, not_in_creative_inventory = 1, material_stone = 1 },
is_ground_content = false,
sounds = mcl_sounds.node_sound_stone_defaults(),
on_timer = furnace_node_timer,
@ -592,6 +624,8 @@ minetest.register_node("mcl_furnaces:furnace_active", {
_mcl_hardness = 3.5,
on_rotate = on_rotate,
after_rotate = after_rotate_active,
_mcl_hoppers_on_try_pull = mcl_furnaces.hoppers_on_try_pull,
_mcl_hoppers_on_try_push = mcl_furnaces.hoppers_on_try_push,
})
minetest.register_craft({

View File

@ -470,31 +470,38 @@ minetest.register_abm({
if entity and entity.name then
--mcl_log("Name of object near: " .. tostring(entity.name))
if entity.name == "mcl_minecarts:hopper_minecart" or entity.name == "mcl_minecarts:chest_minecart" then
if entity.name == "mcl_minecarts:hopper_minecart" or entity.name == "mcl_minecarts:chest_minecart" or entity.name == "mcl_boats:chest_boat" then
local hm_pos = entity.object:get_pos()
mcl_log("We have a minecart with inventory close: " .. minetest.pos_to_string(hm_pos))
--if hm_pos.y == pos.y + 1 then mcl_log("y is correct") end
local ent_pos_y
if entity.name == "mcl_minecarts:hopper_minecart" or entity.name == "mcl_minecarts:chest_minecart" then
ent_pos_y = hm_pos.y
elseif entity.name == "mcl_boats:chest_boat" then
ent_pos_y = math.floor(hm_pos.y + 0.8)
end
local DIST_FROM_MC = 1.5
--if ent_pos_y == pos.y - 1 then mcl_log("y is correct") end
--if (hm_pos.x >= pos.x - DIST_FROM_MC and hm_pos.x <= pos.x + DIST_FROM_MC) then mcl_log("x is within range") end
--if (hm_pos.z >= pos.z - DIST_FROM_MC and hm_pos.z <= pos.z + DIST_FROM_MC) then mcl_log("z is within range") end
local DIST_FROM_MC = 1.5
if (hm_pos.y == pos.y + 1)
if (ent_pos_y == pos.y + 1)
and (hm_pos.x >= pos.x - DIST_FROM_MC and hm_pos.x <= pos.x + DIST_FROM_MC)
and (hm_pos.z >= pos.z - DIST_FROM_MC and hm_pos.z <= pos.z + DIST_FROM_MC) then
mcl_log("Minecart close enough")
if entity.name == "mcl_minecarts:hopper_minecart" then
hopper_pull_from_mc(entity, pos, 5)
elseif entity.name == "mcl_minecarts:chest_minecart" then
elseif entity.name == "mcl_minecarts:chest_minecart" or entity.name == "mcl_boats:chest_boat" then
hopper_pull_from_mc(entity, pos, 27)
end
elseif (hm_pos.y == pos.y - 1)
elseif (ent_pos_y == pos.y - 1)
and (hm_pos.x >= pos.x - DIST_FROM_MC and hm_pos.x <= pos.x + DIST_FROM_MC)
and (hm_pos.z >= pos.z - DIST_FROM_MC and hm_pos.z <= pos.z + DIST_FROM_MC) then
mcl_log("Minecart close enough")
if entity.name == "mcl_minecarts:hopper_minecart" then
hopper_push_to_mc(entity, pos, 5)
elseif entity.name == "mcl_minecarts:chest_minecart" then
elseif entity.name == "mcl_minecarts:chest_minecart" or entity.name == "mcl_boats:chest_boat" then
hopper_push_to_mc(entity, pos, 27)
end
end
@ -545,25 +552,7 @@ minetest.register_abm({
end,
})
---Returns true if itemstack is fuel, but not for lava bucket if destination already has one
---@param itemstack ItemStack
---@param src_inventory InvRef
---@param src_list string
---@param dst_inventory InvRef
---@param dst_list string
---@return boolean
local function is_transferrable_fuel(itemstack, src_inventory, src_list, dst_inventory, dst_list)
if mcl_util.is_fuel(itemstack) then
if itemstack:get_name() == "mcl_buckets:bucket_lava" then
return dst_inventory:is_empty(dst_list)
else
return true
end
else
return false
end
end
-- Register push/pull for "straight" hopper
minetest.register_abm({
label = "Hopper/container item exchange",
nodenames = { "mcl_hoppers:hopper" },
@ -571,31 +560,26 @@ minetest.register_abm({
interval = 1.0,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
-- Get node pos' for item transfer
local uppos = vector.offset(pos, 0, 1, 0)
local downpos = vector.offset(pos, 0, -1, 0)
-- Suck an item from the container above into the hopper
local upnode = minetest.get_node(uppos)
if not minetest.registered_nodes[upnode.name] then return end
local g = minetest.get_item_group(upnode.name, "container")
local sucked = mcl_util.move_item_container(uppos, pos)
-- Also suck in non-fuel items from furnace fuel slot
if not sucked and g == 4 then
local finv = minetest.get_inventory({type = "node", pos = uppos})
if finv and not mcl_util.is_fuel(finv:get_stack("fuel", 1)) then
mcl_util.move_item_container(uppos, pos, "fuel")
end
if minetest.get_node_timer(pos):is_started() then
return
end
-- Move an item from the hopper into container below
local downnode = minetest.get_node(downpos)
if not minetest.registered_nodes[downnode.name] then return end
mcl_util.move_item_container(pos, downpos)
-- Move from internal inventory to dst first
local dst_pos = vector.offset(pos, 0, -1, 0)
local pushed = mcl_util.hopper_push(pos, dst_pos)
local src_pos = vector.offset(pos, 0, 1, 0)
mcl_util.hopper_pull(pos, src_pos)
local dst_node = minetest.get_node(dst_pos)
if pushed and (dst_node.name == "mcl_hoppers:hopper" or dst_node.name == "mcl_hoppers:hopper_side") then
--Pause destination hopper
minetest.get_node_timer(dst_pos):start(1.0)
end
end,
})
-- Register push/pull for "bent" hopper
minetest.register_abm({
label = "Side-hopper/container item exchange",
nodenames = { "mcl_hoppers:hopper_side" },
@ -603,164 +587,36 @@ minetest.register_abm({
interval = 1.0,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
if minetest.get_node_timer(pos):is_started() then
--Pause if already recived item this tick
return
end
-- Determine to which side the hopper is facing, get nodes
local face = minetest.get_node(pos).param2
local front = {}
local dst_pos = {}
if face == 0 then
front = vector.offset(pos, -1, 0, 0)
dst_pos = vector.offset(pos, -1, 0, 0)
elseif face == 1 then
front = vector.offset(pos, 0, 0, 1)
dst_pos = vector.offset(pos, 0, 0, 1)
elseif face == 2 then
front = vector.offset(pos, 1, 0, 0)
dst_pos = vector.offset(pos, 1, 0, 0)
elseif face == 3 then
front = vector.offset(pos, 0, 0, -1)
dst_pos = vector.offset(pos, 0, 0, -1)
end
local above = vector.offset(pos, 0, 1, 0)
local pushed = mcl_util.hopper_push(pos, dst_pos)
local frontnode = minetest.get_node(front)
if not minetest.registered_nodes[frontnode.name] then return end
local src_pos = vector.offset(pos, 0, 1, 0)
mcl_util.hopper_pull(pos, src_pos)
-- Suck an item from the container above into the hopper
local abovenode = minetest.get_node(above)
if not minetest.registered_nodes[abovenode.name] then return end
local g = minetest.get_item_group(abovenode.name, "container")
local sucked = mcl_util.move_item_container(above, pos)
-- Also suck in non-fuel items from furnace fuel slot
if not sucked and g == 4 then
local finv = minetest.get_inventory({type = "node", pos = above})
if finv and not mcl_util.is_fuel(finv:get_stack("fuel", 1)) then
mcl_util.move_item_container(above, pos, "fuel")
end
local dst_node = minetest.get_node(dst_pos)
if pushed and (dst_node.name == "mcl_hoppers:hopper" or dst_node.name == "mcl_hoppers:hopper_side") then
--Pause destination hopper
minetest.get_node_timer(dst_pos):start(1.0)
end
-- Move an item from the hopper into the container to which the hopper points to
local g = minetest.get_item_group(frontnode.name, "container")
if g == 2 or g == 3 or g == 5 or g == 6 then
mcl_util.move_item_container(pos, front)
elseif g == 4 then
-- Put fuel into fuel slot
local sinv = minetest.get_inventory({type = "node", pos = pos})
local dinv = minetest.get_inventory({type = "node", pos = front})
local slot_id, _ = mcl_util.get_eligible_transfer_item_slot(sinv, "main", dinv, "fuel", is_transferrable_fuel)
if slot_id then
mcl_util.move_item_container(pos, front, nil, slot_id, "fuel")
end
end
end
end,
})
if minetest.get_modpath("mcl_composters") then
minetest.register_abm({
label = "Bonemeal extraction from composter",
nodenames = {"mcl_hoppers:hopper", "mcl_hoppers:hopper_side"},
neighbors = {"mcl_composters:composter_ready"},
interval = 1.0,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
local uppos = vector.offset(pos, 0, 1, 0)
--local downpos = vector.offset(pos, 0, -1, 0)
-- Get bonemeal from composter above
local upnode = minetest.get_node(uppos)
if upnode.name == "mcl_composters:composter_ready" then
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
minetest.swap_node(uppos, {name = "mcl_composters:composter"})
inv:add_item("main", "mcl_bone_meal:bone_meal")
end
end,
})
---@param node node
---@return integer?
---@nodiscard
local function composter_level(node)
local nn = node.name
if nn == "mcl_composters:composter" then
return 0
elseif nn == "mcl_composters:composter_1" then
return 1
elseif nn == "mcl_composters:composter_2" then
return 2
elseif nn == "mcl_composters:composter_3" then
return 3
elseif nn == "mcl_composters:composter_4" then
return 4
elseif nn == "mcl_composters:composter_5" then
return 5
elseif nn == "mcl_composters:composter_6" then
return 6
elseif nn == "mcl_composters:composter_7" then
return 7
else
return nil
end
end
for i = 1, 7 do
assert(composter_level({name = "mcl_composters:composter_" .. i}) == i)
end
assert(composter_level({name = "mcl_composters:composter"}) == 0)
assert(composter_level({name = "mcl_composters:some_other_node"}) == nil)
minetest.register_abm({
label = "Add compostable items on composter",
nodenames = {"mcl_hoppers:hopper"},
neighbors = {
"mcl_composters:composter",
"mcl_composters:composter_1",
"mcl_composters:composter_2",
"mcl_composters:composter_3",
"mcl_composters:composter_4",
"mcl_composters:composter_5",
"mcl_composters:composter_6",
"mcl_composters:composter_7",
},
interval = 1.0,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
--local uppos = vector.offset(pos, 0, 1, 0)
local downpos = vector.offset(pos, 0, -1, 0)
local downnode = minetest.get_node(downpos)
---@type integer|string|nil
local level = composter_level(downnode)
--Consume compostable items and update composter below
if level then
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
for i = 1, 5 do
local stack = inv:get_stack("main", i)
local compchance = minetest.get_item_group(stack:get_name(), "compostability")
if compchance > 0 then
stack:take_item()
inv:set_stack("main", i, stack)
if compchance >= math.random(0, 100) then
mcl_dye.add_bone_meal_particle(vector.offset(downpos, 0, level / 8, 0))
if level < 7 then
level = level + 1
else
level = "ready"
end
minetest.swap_node(downpos, {name = "mcl_composters:composter_" .. level})
end
break
end
end
end
end,
})
end
minetest.register_craft({
output = "mcl_hoppers:hopper",
recipe = {

View File

@ -638,7 +638,7 @@ function mcl_itemframes.create_base_definitions()
paramtype = "light",
paramtype2 = "facedir",
sunlight_propagates = true,
groups = { dig_immediate = 3, deco_block = 1, dig_by_piston = 1, container = 7, }, -- attached_node_facedir = 1 }, -- allows for more placement options.
groups = { dig_immediate = 3, deco_block = 1, dig_by_piston = 1, container = 1, }, -- attached_node_facedir = 1 }, -- allows for more placement options.
sounds = mcl_sounds.node_sound_defaults(),
node_placement_prediction = "",

View File

@ -121,7 +121,7 @@ minetest.register_node("mcl_jukebox:jukebox", {
_doc_items_usagehelp = S("Place a music disc into an empty jukebox to insert the music disc and play music. If the jukebox already has a music disc, you will retrieve this music disc first. The music can only be heard by you, not by other players."),
tiles = {"mcl_jukebox_top.png", "mcl_jukebox_side.png", "mcl_jukebox_side.png"},
sounds = mcl_sounds.node_sound_wood_defaults(),
groups = {handy=1,axey=1, container=7, deco_block=1, material_wood=1, flammable=-1},
groups = {handy=1,axey=1, container=1, deco_block=1, material_wood=1, flammable=-1},
is_ground_content = false,
on_construct = function(pos)
local meta = minetest.get_meta(pos)

View File

@ -256,7 +256,7 @@ minetest.register_craftitem("mcl_potions:water", {
stack_max = 1,
inventory_image = potion_image("#0022FF"),
wield_image = potion_image("#0022FF"),
groups = {brewitem=1, food=3, can_eat_when_full=1, water_bottle=1},
groups = {brewitem=1, food=3, can_eat_when_full=1, water_bottle=1, bottle=1},
on_place = water_bottle_on_place,
_on_dispense = dispense_water_bottle,
_dispense_into_walkable = true,
@ -273,7 +273,7 @@ minetest.register_craftitem("mcl_potions:river_water", {
stack_max = 1,
inventory_image = potion_image("#0044FF"),
wield_image = potion_image("#0044FF"),
groups = {brewitem=1, food=3, can_eat_when_full=1, water_bottle=1},
groups = {brewitem=1, food=3, can_eat_when_full=1, water_bottle=1, bottle=1},
on_place = water_bottle_on_place,
_on_dispense = dispense_water_bottle,
_dispense_into_walkable = true,

View File

@ -102,7 +102,7 @@ function mcl_potions.register_lingering(name, descr, color, def)
_doc_items_longdesc = longdesc,
_doc_items_usagehelp = S("Use the “Punch” key to throw it."),
inventory_image = lingering_image(color),
groups = {brewitem=1, not_in_creative_inventory=0},
groups = {brewitem=1, not_in_creative_inventory=0, bottle=1},
on_use = function(item, placer, pointed_thing)
local velocity = 10
local dir = placer:get_look_dir();

View File

@ -163,7 +163,7 @@ local function register_potion(def)
stack_max = def.stack_max or 1,
inventory_image = def.image or potion_image(def.color),
wield_image = def.image or potion_image(def.color),
groups = def.groups or {brewitem=1, food=3, can_eat_when_full=1 },
groups = def.groups or {brewitem=1, food=3, can_eat_when_full=1, bottle=1},
on_place = on_use,
on_secondary_use = on_use,
})
@ -260,7 +260,7 @@ local function register_potion(def)
stack_max = def.stack_max or 1,
inventory_image = def.image or potion_image(def.color),
wield_image = def.image or potion_image(def.color),
groups = def.groups or {brewitem=1, food=3, can_eat_when_full=1},
groups = def.groups or {brewitem=1, food=3, can_eat_when_full=1, bottle=1},
on_place = on_use,
on_secondary_use = on_use,
})
@ -343,7 +343,7 @@ local function register_potion(def)
stack_max = 1,
inventory_image = def.image or potion_image(def.color),
wield_image = def.image or potion_image(def.color),
groups = def.groups or {brewitem=1, food=3, can_eat_when_full=1},
groups = def.groups or {brewitem=1, food=3, can_eat_when_full=1, bottle=1},
on_place = on_use,
on_secondary_use = on_use,
})
@ -411,7 +411,7 @@ local awkward_def = {
_tt = S("No effect"),
_longdesc = S("Has an awkward taste and is used for brewing potions."),
color = "#0000FF",
groups = {brewitem=1, food=3, can_eat_when_full=1},
groups = {brewitem=1, food=3, can_eat_when_full=1, bottle=1},
on_use = minetest.item_eat(0, "mcl_potions:glass_bottle"),
}
@ -450,7 +450,7 @@ local dragon_breath_def = {
no_effect = true,
_longdesc = S("This item is used in brewing and can be combined with splash potions to create lingering potions."),
image = "mcl_potions_dragon_breath.png",
groups = { brewitem = 1 },
groups = { brewitem = 1, bottle = 1 },
on_use = nil,
stack_max = 64,
}

View File

@ -26,7 +26,7 @@ function mcl_potions.register_splash(name, descr, color, def)
_doc_items_longdesc = longdesc,
_doc_items_usagehelp = S("Use the “Punch” key to throw it."),
inventory_image = splash_image(color),
groups = {brewitem=1, not_in_creative_inventory=0},
groups = {brewitem=1, not_in_creative_inventory=0, bottle=1},
on_use = function(item, placer, pointed_thing)
local velocity = 10
local dir = placer:get_look_dir();

View File

@ -453,7 +453,7 @@ minetest.register_node("mcl_smoker:smoker", {
"smoker_side.png", "smoker_front.png"
},
paramtype2 = "facedir",
groups = { pickaxey = 1, container = 4, deco_block = 1, material_stone = 1 },
groups = { pickaxey = 1, container = 2, deco_block = 1, material_stone = 1 },
is_ground_content = false,
sounds = mcl_sounds.node_sound_stone_defaults(),
@ -519,6 +519,11 @@ minetest.register_node("mcl_smoker:smoker", {
_mcl_blast_resistance = 3.5,
_mcl_hardness = 3.5,
on_rotate = on_rotate,
_mcl_hoppers_on_try_pull = mcl_furnaces.hoppers_on_try_pull,
_mcl_hoppers_on_try_push = mcl_furnaces.hoppers_on_try_push,
_mcl_hoppers_on_after_push = function(pos)
minetest.get_node_timer(pos):start(1.0)
end,
})
minetest.register_node("mcl_smoker:smoker_active", {
@ -536,7 +541,7 @@ minetest.register_node("mcl_smoker:smoker_active", {
paramtype = "light",
light_source = LIGHT_ACTIVE_FURNACE,
drop = "mcl_smoker:smoker",
groups = { pickaxey = 1, container = 4, deco_block = 1, not_in_creative_inventory = 1, material_stone = 1 },
groups = { pickaxey = 1, container = 2, deco_block = 1, not_in_creative_inventory = 1, material_stone = 1 },
is_ground_content = false,
sounds = mcl_sounds.node_sound_stone_defaults(),
on_timer = smoker_node_timer,
@ -579,6 +584,8 @@ minetest.register_node("mcl_smoker:smoker_active", {
_mcl_hardness = 3.5,
on_rotate = on_rotate,
after_rotate = after_rotate_active,
_mcl_hoppers_on_try_pull = mcl_furnaces.hoppers_on_try_pull,
_mcl_hoppers_on_try_push = mcl_furnaces.hoppers_on_try_push,
})
minetest.register_craft({