de-duplicate chest code (#123)

* rewrite compat-chests.lua
add pipeworks.override_chest() to make a existing chest connecting to pipeworks

* move pipeworks.override_chest() to a new file
fix crash with after_place_node() there
let mcl_barrels make use of the new function

* some comments
This commit is contained in:
tour 2024-04-11 10:25:13 +02:00 committed by GitHub
parent 21dbae9962
commit 1169cff163
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 257 additions and 370 deletions

90
chests.lua Normal file
View File

@ -0,0 +1,90 @@
pipeworks.chests = {}
-- register a chest to connect with pipeworks tubes.
-- will autoconnect to tubes and add tube inlets to the textures
-- it is highly recommended to allow the user to change the "splitstacks" int (1 to enable) in the node meta
-- but that can't be done by this function
-- @param override: additional overrides, such as stuff to modify the node formspec
-- @param connect_sides: which directions the chests shall connect to
function pipeworks.override_chest(chestname, override, connect_sides)
local old_def = minetest.registered_nodes[chestname]
local tube_entry = "^pipeworks_tube_connection_wooden.png"
override.tiles = override.tiles or old_def.tiles
-- expand the tiles table if it has been shortened
if #override.tiles < 6 then
for i = #override.tiles, 6 do
override.tiles[i] = override.tiles[#override.tiles]
end
end
-- add inlets to the sides that connect to tubes
local tile_directions = {"top", "bottom", "right", "left", "back", "front"}
for i, direction in ipairs(tile_directions) do
if connect_sides[direction] then
if type(override.tiles[i]) == "string" then
override.tiles[i] = override.tiles[i] .. tube_entry
elseif type(override.tiles[i]) == "table" and not override.tiles[i].animation then
override.tiles[i].name = override.tiles[i].name .. tube_entry
end
end
end
local old_after_place_node = override.after_place_node or old_def.after_place_node or function() end
override.after_place_node = function(pos, placer, itemstack, pointed_thing)
old_after_place_node(pos, placer, itemstack, pointed_thing)
pipeworks.after_place(pos)
end
local old_after_dig = override.after_dig or old_def.after_dig or function() end
override.after_dig_node = function(pos, oldnode, oldmetadata, digger)
old_after_dig(pos, oldnode, oldmetadata, digger)
pipeworks.after_dig(pos, oldnode, oldmetadata, digger)
end
local old_on_rotate
if override.on_rotate ~= nil then
old_on_rotate = override.on_rotate
elseif old_def.on_rotate ~= nil then
old_on_rotate = old_def.on_rotate
else
old_on_rotate = function() end
end
-- on_rotate = false -> rotation disabled, no need to update tubes
-- everything else: undefined by the most common screwdriver mods
if type(old_on_rotate) == "function" then
override.on_rotate = function(pos, node, user, mode, new_param2)
if old_on_rotate(pos, node, user, mode, new_param2) ~= false then
return pipeworks.on_rotate(pos, node, user, mode, new_param2)
else
return false
end
end
end
override.tube = {
insert_object = function(pos, node, stack, direction)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv:add_item("main", stack)
end,
can_insert = function(pos, node, stack, direction)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if meta:get_int("splitstacks") == 1 then
stack = stack:peek_item(1)
end
return inv:room_for_item("main", stack)
end,
input_inventory = "main",
connect_sides = connect_sides
}
-- Add the extra groups
override.groups = override.groups or old_def.groups or {}
override.groups.tubedevice = 1
override.groups.tubedevice_receiver = 1
minetest.override_item(chestname, override)
pipeworks.chests[chestname] = true
end

View File

@ -1,34 +1,11 @@
-- this bit of code modifies the default chests and furnaces to be compatible
-- with pipeworks.
--
-- the formspecs found here are basically copies of the ones from minetest_game
-- plus bits from pipeworks' sorting tubes
-- this bit of code overrides the default chests from common games (mtg, hades, minclone*) to be
-- compatible with pipeworks. Where possible, it overrides their formspec to add a splitstacks switch
-- Pipeworks Specific
local fs_helpers = pipeworks.fs_helpers
local tube_entry = "^pipeworks_tube_connection_wooden.png"
-- Chest Locals
local open_chests = {}
local get_chest_formspec
if minetest.get_modpath("default") then
function get_chest_formspec(pos)
local spos = pos.x .. "," .. pos.y .. "," .. pos.z
local formspec =
"size[8,9]" ..
default.gui_bg ..
default.gui_bg_img ..
default.gui_slots ..
"list[nodemeta:" .. spos .. ";main;0,0.3;8,4;]" ..
"list[current_player;main;0,4.85;8,1;]" ..
"list[current_player;main;0,6.08;8,3;8]" ..
"listring[nodemeta:" .. spos .. ";main]" ..
"listring[current_player;main]" ..
default.get_hotbar_bg(0,4.85)
-- Pipeworks Switch
-- formspec helper to add the splitstacks switch
local function add_pipeworks_switch(formspec, pos)
-- based on the sorting tubes
formspec = formspec ..
fs_helpers.cycling_button(
minetest.get_meta(pos),
@ -39,271 +16,163 @@ if minetest.get_modpath("default") then
pipeworks.button_on
}
)..pipeworks.button_label
return formspec
end
else
local function get_hotbar_bg(x,y)
local out = ""
for i=0,7,1 do
out = out .."image["..x+i..","..y..";1,1;gui_hb_bg.png]"
end
return out
-- helper to add the splitstacks switch to a node-formspec
local function update_node_formspec(pos)
local meta = minetest.get_meta(pos)
local old_fs = meta:get_string("formspec")
local new_fs = add_pipeworks_switch(old_fs, pos)
meta:set_string("formspec", new_fs)
end
function get_chest_formspec(pos)
if minetest.get_modpath("default") then
-- add the pipeworks switch into the default chest formspec
local old_get_chest_formspec = default.chest.get_chest_formspec
-- luacheck: ignore 122
default.chest.get_chest_formspec = function(pos)
local old_fs = old_get_chest_formspec(pos)
local node = minetest.get_node(pos)
-- not all chests using this formspec necessary connect to pipeworks
if pipeworks.chests[node.name] then
local new_fs = add_pipeworks_switch(old_fs, pos)
return new_fs
else
return old_fs
end
end
-- get the fields from the chest formspec, we can do this bc. newest functions are called first
-- https://github.com/minetest/minetest/blob/d4b10db998ebeb689b3d27368e30952a42169d03/doc/lua_api.md?plain=1#L5840
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname == "default:chest" then
local pn = player:get_player_name()
local pos = default.chest.open_chests[pn].pos
local chest = pos and minetest.get_node(pos)
local is_pipeworks_chest = chest and pipeworks.chests[chest]
if is_pipeworks_chest and not fields.quit and pipeworks.may_configure(pos, player) then
-- Pipeworks Switch
fs_helpers.on_receive_fields(pos, fields)
minetest.show_formspec(player:get_player_name(),
"default:chest",
default.chest.get_chest_formspec(pos))
end
-- Do NOT return true here, the callback from default still needs to run
return false
end
end)
local connect_sides = {left = 1, right = 1, back = 1, bottom = 1, top = 1}
local connect_sides_open = {left = 1, right = 1, back = 1, bottom = 1}
pipeworks.override_chest("default:chest", {}, connect_sides)
pipeworks.override_chest("default:chest_open", {}, connect_sides_open)
pipeworks.override_chest("default:chest_locked", {}, connect_sides)
pipeworks.override_chest("default:chest_locked_open", {}, connect_sides_open)
elseif minetest.get_modpath("hades_chests") then
local chest_colors = {"", "white", "grey", "dark_grey", "black", "blue", "cyan", "dark_green", "green", "magenta",
"orange", "pink", "red", "violet", "yellow"}
for _, color in ipairs(chest_colors) do
local chestname = (color == "" and "hades_chests:chest")
or "hades_chests:chest_" .. color
local chestname_protected = (color == "" and "hades_chests:chest_locked")
or "hades_chests:chest_" .. color .. "_locked"
local old_def = minetest.registered_nodes[chestname]
-- chest formspec-creation functions are local, we need to find other ways
-- normal chests use node formspecs, we can hack into these
local old_on_construct = old_def.on_construct
local override = {
on_construct = function(pos)
old_on_construct(pos)
update_node_formspec(pos)
end,
on_receive_fields = function(pos, formname, fields, player)
if not fields.quit and pipeworks.may_configure(pos, player) then
-- Pipeworks Switch
fs_helpers.on_receive_fields(pos, fields)
update_node_formspec(pos)
end
end,
-- chest's on_rotate is "simple", but we assumed the api from the mtg screwdriver mod
-- this will keep the same behavior, but supports the code above
on_rotate = screwdriver.rotate_simple
}
-- locked chests uses local functions to create their formspec - we need to copy these
-- https://codeberg.org/Wuzzy/Hades_Revisited/src/branch/master/mods/hades_chests/init.lua
local function get_locked_chest_formspec(pos)
local spos = pos.x .. "," .. pos.y .. "," ..pos.z
local formspec =
"size[10,9]"..
"background9[8,8;8,9;hades_chests_chestui.png;true;8]"..
"list[nodemeta:" .. spos .. ";main;0,0.3;10,4;]" ..
"list[current_player;main;0,4.85;10,1;]" ..
"list[current_player;main;0,6.08;10,3;10]" ..
"listring[nodemeta:" .. spos .. ";main]" ..
"listring[current_player;main]" ..
get_hotbar_bg(0,4.85)
"list[nodemeta:".. spos .. ";main;0,0;10,4;]"..
"list[current_player;main;0,5;10,4;]"..
"listring[]"..
"background9[8,8;10,9;hades_chests_chestui.png;true;8]"
-- Pipeworks Switch
formspec = formspec ..
fs_helpers.cycling_button(
minetest.get_meta(pos),
pipeworks.button_base,
"splitstacks",
{
pipeworks.button_off,
pipeworks.button_on
}
)..pipeworks.button_label
return formspec
end
-- change from pipeworks
local new_fs = add_pipeworks_switch(formspec, pos)
return new_fs
end
local function chest_lid_obstructed(pos)
local above = { x = pos.x, y = pos.y + 1, z = pos.z }
local def = minetest.registered_nodes[minetest.get_node(above).name]
-- allow ladders, signs, wallmounted things and torches to not obstruct
if not def then return true end
if def.drawtype == "airlike" or
def.drawtype == "signlike" or
def.drawtype == "torchlike" or
(def.drawtype == "nodebox" and def.paramtype2 == "wallmounted") then
local function has_locked_chest_privilege(meta, player)
local name = player:get_player_name()
if name ~= meta:get_string("owner") and not minetest.check_player_privs(name, "protection_bypass") then
return false
end
return true
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname == "pipeworks:chest_formspec" and player then
local pn = player:get_player_name()
if open_chests[pn] then
local pos = open_chests[pn].pos
if fields.quit then
local sound = open_chests[pn].sound
local swap = open_chests[pn].swap
local node = minetest.get_node(pos)
open_chests[pn] = nil
for _, v in pairs(open_chests) do
if v.pos.x == pos.x and v.pos.y == pos.y and v.pos.z == pos.z then
return true
end
end
minetest.after(0.2, function()
if minetest.get_modpath("default") then
local current_node = minetest.get_node(pos)
if current_node.name ~= "default:" .. swap .. "_open" then
-- the chest has already been replaced, don't try to replace what's there.
-- see: https://github.com/minetest/minetest_game/pull/3046
return
end
minetest.swap_node(pos, { name = "default:" .. swap, param2 = node.param2 })
end
-- Pipeworks notification
pipeworks.after_place(pos)
-- store, which chest a formspec submission belongs to
-- {player1 = pos1, player2 = pos2, ...}
local open_chests = {}
minetest.register_on_leaveplayer(function(player)
open_chests[player:get_player_name()] = nil
end)
minetest.sound_play(sound, {gain = 0.3, pos = pos, max_hear_distance = 10})
elseif pipeworks.may_configure(pos, player) then
local override_protected = {
on_rightclick = function(pos, node, clicker)
local meta = minetest.get_meta(pos)
if has_locked_chest_privilege(meta, clicker) then
minetest.show_formspec(
clicker:get_player_name(),
"hades_chests:chest_locked",
get_locked_chest_formspec(pos)
)
open_chests[clicker:get_player_name()] = pos
else
minetest.sound_play({ name = "hades_chests_locked", gain = 0.3 }, { max_hear_distance = 10 }, true)
end
end,
on_rotate = screwdriver.rotate_simple
}
-- get the fields from the chest formspec, we can do this bc. newest functions are called first
-- https://github.com/minetest/minetest/blob/d4b10db998ebeb689b3d27368e30952a42169d03/doc/lua_api.md?plain=1#L5840
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname == "hades_chests:chest_locked" then
local pn = player:get_player_name()
local pos = open_chests[pn]
if not fields.quit and pos and pipeworks.may_configure(pos, player) then
-- Pipeworks Switch
fs_helpers.on_receive_fields(pos, fields)
minetest.show_formspec(player:get_player_name(), "pipeworks:chest_formspec", get_chest_formspec(pos))
end
return true
minetest.show_formspec(pn, "hades_chests:chest_locked", get_locked_chest_formspec(pos))
end
-- Do NOT return true here, the callback from hades still needs to run (if they add one)
return false
end
end)
-- Original Definitions
local old_chest_def, old_chest_open_def, old_chest_locked_def, old_chest_locked_open_def
if minetest.get_modpath("default") then
old_chest_def = table.copy(minetest.registered_items["default:chest"])
old_chest_open_def = table.copy(minetest.registered_items["default:chest_open"])
old_chest_locked_def = table.copy(minetest.registered_items["default:chest_locked"])
old_chest_locked_open_def = table.copy(minetest.registered_items["default:chest_locked_open"])
elseif minetest.get_modpath("hades_chests") then
old_chest_def = table.copy(minetest.registered_items["hades_chests:chest"])
old_chest_open_def = table.copy(minetest.registered_items["hades_chests:chest"])
old_chest_locked_def = table.copy(minetest.registered_items["hades_chests:chest_locked"])
old_chest_locked_open_def = table.copy(minetest.registered_items["hades_chests:chest_locked"])
local connect_sides = {left = 1, right = 1, back = 1, bottom = 1, top = 1}
pipeworks.override_chest(chestname, override, connect_sides)
pipeworks.override_chest(chestname_protected, override_protected, connect_sides)
end
-- Override Construction
local override_protected, override, override_open, override_protected_open
override_protected = {
tiles = {
"default_chest_top.png"..tube_entry,
"default_chest_top.png"..tube_entry,
"default_chest_side.png"..tube_entry,
"default_chest_side.png"..tube_entry,
"default_chest_lock.png",
"default_chest_inside.png"
},
after_place_node = function(pos, placer)
old_chest_locked_def.after_place_node(pos, placer)
pipeworks.after_place(pos)
end,
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
if not default.can_interact_with_node(clicker, pos) then
return itemstack
elseif minetest.get_modpath("mcl_barrels") then
-- TODO: bring splitstacks switch in the formspec
-- with the current implementation of mcl_barrels this would mean to duplicate a lot of code from there...
local connect_sides = {left = 1, right = 1, back = 1, front = 1, bottom = 1}
pipeworks.override_chest("mcl_barrels:barrel_closed", {}, connect_sides)
pipeworks.override_chest("mcl_barrels:barrel_open", {}, connect_sides)
end
minetest.sound_play(old_chest_locked_def.sound_open, {gain = 0.3,
pos = pos, max_hear_distance = 10})
if not chest_lid_obstructed(pos) then
if minetest.get_modpath("default") then
minetest.swap_node(pos,
{ name = "default:" .. "chest_locked" .. "_open",
param2 = node.param2 })
end
end
minetest.after(0.2, minetest.show_formspec,
clicker:get_player_name(),
"pipeworks:chest_formspec", get_chest_formspec(pos))
open_chests[clicker:get_player_name()] = { pos = pos,
sound = old_chest_locked_def.sound_close, swap = "chest_locked" }
end,
groups = table.copy(old_chest_locked_def.groups),
tube = {
insert_object = function(pos, node, stack, direction)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv:add_item("main", stack)
end,
can_insert = function(pos, node, stack, direction)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if meta:get_int("splitstacks") == 1 then
stack = stack:peek_item(1)
end
return inv:room_for_item("main", stack)
end,
input_inventory = "main",
connect_sides = {left = 1, right = 1, back = 1, bottom = 1, top = 1}
},
after_dig_node = pipeworks.after_dig,
on_rotate = pipeworks.on_rotate
}
override = {
tiles = {
"default_chest_top.png"..tube_entry,
"default_chest_top.png"..tube_entry,
"default_chest_side.png"..tube_entry,
"default_chest_side.png"..tube_entry,
"default_chest_front.png",
"default_chest_inside.png"
},
on_rightclick = function(pos, node, clicker)
minetest.sound_play(old_chest_def.sound_open, {gain = 0.3, pos = pos,
max_hear_distance = 10})
if not chest_lid_obstructed(pos) then
if minetest.get_modpath("default") then
minetest.swap_node(pos, {
name = "default:" .. "chest" .. "_open",
param2 = node.param2 })
end
end
minetest.after(0.2, minetest.show_formspec,
clicker:get_player_name(),
"pipeworks:chest_formspec", get_chest_formspec(pos))
open_chests[clicker:get_player_name()] = { pos = pos,
sound = old_chest_def.sound_close, swap = "chest" }
end,
groups = table.copy(old_chest_def.groups),
tube = {
insert_object = function(pos, node, stack, direction)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv:add_item("main", stack)
end,
can_insert = function(pos, node, stack, direction)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if meta:get_int("splitstacks") == 1 then
stack = stack:peek_item(1)
end
return inv:room_for_item("main", stack)
end,
input_inventory = "main",
connect_sides = {left = 1, right = 1, back = 1, bottom = 1, top = 1}
},
after_place_node = pipeworks.after_place,
after_dig_node = pipeworks.after_dig,
on_rotate = pipeworks.on_rotate
}
--[[local override_common = {
}
for k,v in pairs(override_common) do
override_protected[k] = v
override[k] = v
end]]
override_open = table.copy(override)
override_open.groups = table.copy(old_chest_open_def.groups)
override_open.tube = table.copy(override.tube)
override_open.tube.connect_sides = table.copy(override.tube.connect_sides)
override_open.tube.connect_sides.top = nil
override_protected_open = table.copy(override_protected)
override_protected_open.groups = table.copy(old_chest_locked_open_def.groups)
override_protected_open.tube = table.copy(override_protected.tube)
override_protected_open.tube.connect_sides = table.copy(override_protected.tube.connect_sides)
override_protected_open.tube.connect_sides.top = nil
override_protected.tiles = { -- Rearranged according to the chest registration in Minetest_Game.
"default_chest_top.png"..tube_entry,
"default_chest_top.png"..tube_entry,
"default_chest_side.png"..tube_entry.."^[transformFX",
"default_chest_side.png"..tube_entry,
"default_chest_side.png"..tube_entry,
"default_chest_lock.png",
}
override.tiles = {
"default_chest_top.png"..tube_entry,
"default_chest_top.png"..tube_entry,
"default_chest_side.png"..tube_entry.."^[transformFX",
"default_chest_side.png"..tube_entry,
"default_chest_side.png"..tube_entry,
"default_chest_front.png",
}
-- Add the extra groups
for _,v in ipairs({override_protected, override, override_open, override_protected_open}) do
v.groups.tubedevice = 1
v.groups.tubedevice_receiver = 1
end
-- Override with the new modifications.
if minetest.get_modpath("default") then
minetest.override_item("default:chest", override)
minetest.override_item("default:chest_open", override_open)
minetest.override_item("default:chest_locked", override_protected)
minetest.override_item("default:chest_locked_open", override_protected_open)
elseif minetest.get_modpath("hades_chests") then
minetest.override_item("hades_chests:chest", override)
--minetest.override_item("hades_chests:chest_open", override_open)
minetest.override_item("hades_chests:chest_locked", override_protected)
--minetest.override_item("hades_chests:chest_locked_open", override_protected_open)
end

View File

@ -59,6 +59,7 @@ dofile(pipeworks.modpath.."/luaentity.lua")
dofile(pipeworks.modpath.."/item_transport.lua")
dofile(pipeworks.modpath.."/flowing_logic.lua")
dofile(pipeworks.modpath.."/filter-injector.lua")
dofile(pipeworks.modpath.."/chests.lua")
dofile(pipeworks.modpath.."/trashcan.lua")
dofile(pipeworks.modpath.."/wielder.lua")
dofile(pipeworks.modpath.."/tubes/registration.lua")
@ -95,12 +96,11 @@ end
if pipeworks.enable_pipe_devices then
dofile(pipeworks.modpath.."/devices.lua")
end
if pipeworks.enable_redefines and (minetest.get_modpath("default") or minetest.get_modpath("hades_core")) then
if pipeworks.enable_redefines then
dofile(pipeworks.modpath.."/compat-chests.lua")
dofile(pipeworks.modpath.."/compat-furnaces.lua")
end
if pipeworks.enable_redefines and minetest.get_modpath("mcl_barrels") then
dofile(pipeworks.modpath.."/mcl_barrels.lua")
if pipeworks.enable_redefines and (minetest.get_modpath("default") or minetest.get_modpath("hades_core")) then
dofile(pipeworks.modpath.."/compat-furnaces.lua")
end
if pipeworks.enable_redefines and minetest.get_modpath("mcl_furnaces") then
dofile(pipeworks.modpath.."/mcl_furnaces.lua")

View File

@ -1,72 +0,0 @@
-- this bit of code modifies the mcl barrels to be compatible with
-- pipeworks.
-- Pipeworks Specific
local tube_entry = "^pipeworks_tube_connection_wooden.png"
-- Original Definitions
local old_barrel = table.copy(minetest.registered_items["mcl_barrels:barrel_closed"])
local groups = old_barrel.groups
groups["tubedevice"] = 1
groups["tubedevice_receiver"] = 1
local groups_open = table.copy(groups)
groups_open["not_in_creative_inventory"] = 1
-- Override Construction
local override_barrel = {}
override_barrel.tiles = {
"mcl_barrels_barrel_top.png^[transformR270",
"mcl_barrels_barrel_bottom.png"..tube_entry,
"mcl_barrels_barrel_side.png"..tube_entry
}
override_barrel.after_place_node = function(pos, placer, itemstack, pointed_thing)
old_barrel.after_place_node(pos, placer, itemstack, pointed_thing)
pipeworks.after_place(pos, placer, itemstack, pointed_thing)
end
override_barrel.tube = {
insert_object = function(pos, node, stack, direction)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv:add_item("main", stack)
end,
can_insert = function(pos, node, stack, direction)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if meta:get_int("splitstacks") == 1 then
stack = stack:peek_item(1)
end
return inv:room_for_item("main", stack)
end,
input_inventory = "main",
connect_sides = {left = 1, right = 1, back = 1, front = 1, bottom = 1}
}
override_barrel.after_dig_node = function(pos, oldnode, oldmetadata, digger)
old_barrel.after_dig_node(pos, oldnode, oldmetadata, digger)
pipeworks.after_dig(pos)
end
override_barrel.groups = table.copy(old_barrel.groups)
override_barrel.on_rotate = pipeworks.on_rotate
local override_barrel_open = table.copy(override_barrel)
override_barrel_open.tiles = {
"mcl_barrels_barrel_top_open.png",
"mcl_barrels_barrel_bottom.png"..tube_entry,
"mcl_barrels_barrel_side.png"..tube_entry
}
override_barrel_open.groups = groups_open
-- Override with the new modifications.
minetest.override_item("mcl_barrels:barrel_closed", override_barrel)
minetest.override_item("mcl_barrels:barrel_open", override_barrel_open)