2024-12-04 08:36:56 +00:00

690 lines
16 KiB
Lua

-- rotation and settings
local screwdriver = screwdriver or {}
local tmp = {}
local should_return_item = minetest.settings:get_bool("itemframes.return_item", false)
local log_actions = minetest.settings:get_bool("itemframes.log_actions", false)
local allow_rotate = minetest.settings:get_bool("itemframes.allow_rotate", false)
-- voxelibre/mineclonia support
local mcl = minetest.get_modpath("mcl_sounds")
local a = {
paper = mcl and "mcl_core:paper" or "default:paper",
glass = mcl and "mcl_core:glass" or "default:glass",
stick = "group:stick", stone = "group:stone"
}
local sounds = nil
if minetest.get_modpath("default") then
sounds = default.node_sound_defaults()
elseif mcl then
sounds = mcl_sounds.node_sound_defaults()
end
-- translation support
local S = minetest.get_translator("itemframes")
-- remove entities
local function del_ent(pos, self)
local pos2 = vector.round(pos)
local objs = minetest.get_objects_inside_radius(pos2, 0.5)
for _, obj in pairs(objs) do
if obj and (self and obj ~= self.object or not self) and obj:get_luaentity()
and obj:get_luaentity().name == "itemframes:item" then
obj:remove() ; print("--removed", minetest.pos_to_string(pos2))
end
end
end
-- item entity
minetest.register_entity("itemframes:item", {
initial_properties = {
hp_max = 1,
visual = "wielditem",
visual_size = {x = 0.33, y = 0.33},
collisionbox = {0, 0, 0, 0, 0, 0},
physical = false,
textures = {"air"}
},
on_activate = function(self, staticdata)
local pos = self.object:get_pos() ; if not pos then return end
del_ent(pos, self)
if tmp.nodename and tmp.texture then
self.nodename = tmp.nodename ; tmp.nodename = nil
self.texture = tmp.texture ; tmp.texture = nil
self.glow = tmp.glow ; tmp.glow = nil
else
if staticdata and staticdata ~= "" then
local data = staticdata:split(";")
if data and data[1] and data[2] then
self.nodename = data[1]
self.texture = data[2]
self.glow = data[3]
end
end
end
if self.texture then
local def = minetest.registered_items[self.texture]
if def and def._itemframe_texture
and self.nodename ~= "itemframes:pedestal" then
self.object:set_properties({
textures = {def._itemframe_texture},
visual = "upright_sprite",
visual_size = {x = 0.6, y = 0.6}
})
else
self.object:set_properties({textures = {self.texture}})
end
end
if self.nodename == "itemframes:pedestal" then
self.object:set_properties({automatic_rotate = 1})
end
if self.glow then
self.object:set_properties({glow = self.glow})
end
end,
get_staticdata = function(self)
if self.nodename and self.texture then
return self.nodename .. ";" .. self.texture .. ";" .. (self.glow or "")
end
return ""
end,
on_blast = function(self, damage)
return false, false, {}
end
})
-- helper table
local facedir = {
[0] = {x = 0, y = 0, z = 1, pitch = 0, yaw = 0, roll = 0, nx = 12},
[12] = {x = 0, y = 0, z = 1, pitch = 0, yaw = 0, roll = 3, nx = 16},
[16] = {x = 0, y = 0, z = 1, pitch = 0, yaw = 0, roll = 1, nx = 20},
[20] = {x = 0, y = 0, z = 1, pitch = 0, yaw = 0, roll = 2, nx = 0},
[1] = {x = 1, y = 0, z = 0, pitch = 0, yaw = 1, roll = 0, nx = 5},
[5] = {x = 1, y = 0, z = 0, pitch = 0, yaw = 1, roll = 1, nx = 9},
[9] = {x = 1, y = 0, z = 0, pitch = 0, yaw = 1, roll = 3, nx = 23},
[23] = {x = 1, y = 0, z = 0, pitch = 0, yaw = 1, roll = 2, nx = 1},
[2] = {x = 0, y = 0, z = -1, pitch = 0, yaw = 2, roll = 0, nx = 14},
[14] = {x = 0, y = 0, z = -1, pitch = 0, yaw = 2, roll = 1, nx = 18},
[18] = {x = 0, y = 0, z = -1, pitch = 0, yaw = 2, roll = 3, nx = 22},
[22] = {x = 0, y = 0, z = -1, pitch = 0, yaw = 2, roll = 2, nx = 2},
[3] = {x = -1, y = 0, z = 0, pitch = 0, yaw = 3, roll = 0, nx = 7},
[7] = {x = -1, y = 0, z = 0, pitch = 0, yaw = 3, roll = 3, nx = 11},
[11] = {x = -1, y = 0, z = 0, pitch = 0, yaw = 3, roll = 1, nx = 21},
[21] = {x = -1, y = 0, z = 0, pitch = 0, yaw = 3, roll = 2, nx = 3},
[4] = {x = 0, y = -1, z = 0, pitch = -4.7, yaw = 0, roll = 0, nx = 10},
[10] = {x = 0, y = -1, z = 0, pitch = -4.7, yaw = 2, roll = 0, nx = 13},
[13] = {x = 0, y = -1, z = 0, pitch = -4.7, yaw = 1, roll = 0, nx = 19},
[19] = {x = 0, y = -1, z = 0, pitch = -4.7, yaw = 3, roll = 0, nx = 4},
[8] = {x = 0, y = 1, z = 0, pitch = -4.7, yaw = 0, roll = 0, nx = 6},
[6] = {x = 0, y = 1, z = 0, pitch = -4.7, yaw = 2, roll = 0, nx = 15},
[15] = {x = 0, y = 1, z = 0, pitch = -4.7, yaw = 3, roll = 0, nx = 17},
[17] = {x = 0, y = 1, z = 0, pitch = -4.7, yaw = 1, roll = 0, nx = 8},
}
-- remove item
local function remove_item(pos, ntype)
local ypos = 0
if ntype == "pedestal" then ypos = 1 end
del_ent({x = pos.x, y = pos.y + ypos, z = pos.z})
end
-- update entity
local function update_item(pos, ntype, node)
remove_item(pos, ntype)
local meta = minetest.get_meta(pos) ; if not meta then return end
local item = meta:get_string("item") ; if item == "" then return end
local pitch, yaw, roll = 0, 0, 0
if ntype == "frame" then
local p2 = node.param2
local adjust = facedir[p2] ; if not adjust then return end
local raise = 6.5 / 16 -- itemframe default
if node and node.name == "itemframes:frame_invis" then
raise = 6.5 / 14 -- stops floating effect
end
pos.x = pos.x + adjust.x * raise
pos.y = pos.y + adjust.y * raise
pos.z = pos.z + adjust.z * raise
pitch = adjust.pitch
yaw = 6.28 - adjust.yaw * 1.57 -- math.pi/2
roll = 6.28 - adjust.roll * 1.57
elseif ntype == "pedestal" then
pos.y = pos.y + 1.08
end
tmp.nodename = node.name
tmp.texture = ItemStack(item):get_name()
local def = core.registered_items[item]
tmp.glow = def and def.light_source
local e = minetest.add_entity(pos, "itemframes:item")
if not e then
tmp.nodename = nil ; tmp.texture = nil ; tmp.glow = nil ; return
end
if ntype == "frame" then
e:set_rotation({x = pitch, y = yaw, z = roll})
end
end
-- remove entity and drop as item
local function drop_item(pos, ntype, metadata)
local meta = metadata or minetest.get_meta(pos) ; if not meta then return end
local item = meta:get_string("item")
meta:set_string("item", "")
if item ~= "" then
remove_item(pos, ntype)
meta:set_string("infotext", S("Right-click to add or remove item"))
if ntype == "pedestal" then pos.y = pos.y + 1 end
minetest.add_item(pos, item)
end
end
-- return item to a player's inventory
local function return_item(pos, ntype, metadata, clicker, itemstack)
local meta = metadata or minetest.get_meta(pos) ; if not meta then return end
local item = meta:get_string("item") ; if item == "" then return end
local remaining = itemstack:add_item(item)
if remaining:is_empty() then
meta:set_string("item", "")
remove_item(pos, ntype)
return itemstack
end
local inv = clicker:get_inventory()
if not inv then
drop_item(pos, ntype, metadata)
return
end
remaining = inv:add_item("main", remaining)
if remaining:is_empty() then
meta:set_string("item", "")
remove_item(pos, ntype)
else
drop_item(pos, ntype, metadata)
end
end
-- on_place helper function
local function frame_place(itemstack, placer, pointed_thing)
if pointed_thing.type ~= "node" then return end
local above = pointed_thing.above
local under = pointed_thing.under
local dir = {x = under.x - above.x, y = under.y - above.y, z = under.z - above.z}
local wdir = minetest.dir_to_wallmounted(dir)
local placer_pos = placer:get_pos()
if placer_pos then
dir = {
x = above.x - placer_pos.x,
y = above.y - placer_pos.y,
z = above.z - placer_pos.z
}
end
local fdir = minetest.dir_to_facedir(dir)
local p2 = fdir
if wdir == 0 then
p2 = 8
elseif wdir == 1 then
p2 = 4
end
return minetest.item_place(itemstack, placer, pointed_thing, p2)
end
-- action logging helper
local function show_msg(message)
if log_actions then minetest.log("action", message) end
end
-- helper function to return item description
local function get_desc(itemname)
local def = core.registered_items[itemname]
local desc = def and def.description or ""
return desc
end
-- itemframe node and recipe
minetest.register_node("itemframes:frame",{
description = S("Item frame"),
drawtype = "nodebox",
node_box = {type = "fixed", fixed = {-7/16, -7/16, 7/16, 7/16, 7/16, 0.5}},
selection_box = {type = "fixed", fixed = {-6/16, -6/16, 7/16, 6/16, 6/16, 0.5}},
tiles = {"itemframes_frame.png"},
inventory_image = "itemframes_frame_inv.png",
wield_image = "itemframes_frame_inv.png",
paramtype = "light",
paramtype2 = "facedir",
sunlight_propagates = true,
groups = {choppy = 2, dig_immediate = 2, flammable = 2, handy = 1, axey = 1},
sounds = sounds,
_mcl_hardness = 0.5,
on_place = frame_place,
after_place_node = function(pos, placer, itemstack)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Right-click to add or remove item"))
end,
on_rightclick = function(pos, node, clicker, itemstack)
if not itemstack or minetest.is_protected(pos, clicker:get_player_name()) then
return
end
local meta = minetest.get_meta(pos) ; if not meta then return end
if meta:get_string("item") ~= "" then
show_msg(clicker:get_player_name()
.. " removed " .. meta:get_string("item")
.. " from Itemframe at " .. minetest.pos_to_string(pos))
if should_return_item then
return return_item(pos, "frame", meta, clicker, itemstack)
else
drop_item(pos, "frame", meta)
end
else
local s = itemstack:take_item()
local item = s:to_string()
meta:set_string("item", item)
meta:set_string("infotext", S("Contains: ") .. get_desc(item))
update_item(pos, "frame", node)
show_msg(clicker:get_player_name()
.. " inserted " .. meta:get_string("item")
.. " into Itemframe at " .. minetest.pos_to_string(pos))
return itemstack
end
end,
on_destruct = function(pos)
drop_item(pos, "frame")
end,
on_punch = function(pos, node, puncher)
-- rotate item inside frame when holding sneak and punching
if puncher and puncher:get_player_control().sneak
and (allow_rotate or not minetest.is_protected(pos, puncher:get_player_name())) then
local p2 = node.param2
local nx = facedir[p2].nx
minetest.swap_node(pos, {name = node.name, param2 = nx})
node.param2 = nx
end
update_item(pos, "frame", node)
end,
on_blast = function(pos, intensity)
drop_item(pos, "frame")
minetest.add_item(pos, {name = "itemframes:frame"})
minetest.remove_node(pos)
end,
on_burn = function(pos)
drop_item(pos, "frame")
minetest.remove_node(pos)
end
})
minetest.register_craft({
output = "itemframes:frame",
recipe = {
{ a.stick, a.stick, a.stick },
{ a.stick, a.paper, a.stick },
{ a.stick, a.stick, a.stick }
}
})
-- invisible itemframe node and recipe
minetest.register_node("itemframes:frame_invis",{
description = S("Invisible Item frame"),
drawtype = "nodebox",
node_box = {type = "fixed", fixed = {-7/16, -7/16, 7/16, 7/16, 7/16, 0.5}},
selection_box = {type = "fixed", fixed = {-6/16, -6/16, 7/16, 6/16, 6/16, 0.5}},
tiles = {"itemframes_frame_invis.png"},
inventory_image = "itemframes_frame_invis_inv.png",
wield_image = "itemframes_frame_invis_inv.png",
paramtype = "light",
paramtype2 = "facedir",
sunlight_propagates = true,
use_texture_alpha = "clip",
groups = {choppy = 2, dig_immediate = 2, flammable = 2, handy = 1, axey = 1},
sounds = sounds,
_mcl_hardness = 0.5,
on_place = frame_place,
after_place_node = function(pos, placer, itemstack)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Right-click to add or remove item"))
end,
on_rightclick = function(pos, node, clicker, itemstack)
if not itemstack or minetest.is_protected(pos, clicker:get_player_name()) then
return
end
local meta = minetest.get_meta(pos) ; if not meta then return end
if meta:get_string("item") ~= "" then
show_msg(clicker:get_player_name()
.. " removed " .. meta:get_string("item")
.. " from Itemframe at " .. minetest.pos_to_string(pos))
if should_return_item then
return return_item(pos, "frame", meta, clicker, itemstack)
else
drop_item(pos, "frame", meta)
end
else
local s = itemstack:take_item()
local item = s:to_string()
meta:set_string("item", item)
meta:set_string("infotext", S("Contains: ") .. get_desc(item))
update_item(pos, "frame", node)
show_msg(clicker:get_player_name()
.. " inserted " .. meta:get_string("item")
.. " into Itemframe at " .. minetest.pos_to_string(pos))
return itemstack
end
end,
on_destruct = function(pos)
drop_item(pos, "frame")
end,
on_punch = function(pos, node, puncher)
-- rotate item inside frame when holding sneak and punching
if puncher and puncher:get_player_control().sneak
and (allow_rotate or not minetest.is_protected(pos, puncher:get_player_name())) then
local p2 = node.param2
local nx = facedir[p2].nx
minetest.swap_node(pos, {name = node.name, param2 = nx})
node.param2 = nx
end
update_item(pos, "frame", node)
end,
on_blast = function(pos, intensity)
drop_item(pos, "frame")
minetest.add_item(pos, {name = "itemframes:frame_invis"})
minetest.remove_node(pos)
end,
on_burn = function(pos)
drop_item(pos, "frame")
minetest.remove_node(pos)
end
})
minetest.register_craft({
output = "itemframes:frame_invis",
recipe = {
{ a.glass, a.glass, a.glass },
{ a.glass, a.paper, a.glass },
{ a.glass, a.glass, a.glass }
}
})
-- pedestal node and recipe
minetest.register_node("itemframes:pedestal",{
description = S("Pedestal"),
drawtype = "nodebox",
node_box = {
type = "fixed", fixed = {
{-7/16, -8/16, -7/16, 7/16, -7/16, 7/16}, -- bottom plate
{-6/16, -7/16, -6/16, 6/16, -6/16, 6/16}, -- bottom plate (upper)
{-0.25, -6/16, -0.25, 0.25, 11/16, 0.25}, -- pillar
{-7/16, 11/16, -7/16, 7/16, 12/16, 7/16}, -- top plate
}
},
selection_box = {type = "fixed", fixed = {-7/16, -0.5, -7/16, 7/16, 12/16, 7/16}},
tiles = {
"itemframes_pedestal_top.png",
"itemframes_pedestal_btm.png",
"itemframes_pedestal.png"
},
paramtype = "light",
groups = {cracky = 3, pickaxey = 1},
sounds = sounds,
on_rotate = screwdriver.disallow,
_mcl_hardness = 1.5,
after_place_node = function(pos, placer, itemstack)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Right-click to add or remove item"))
end,
on_rightclick = function(pos, node, clicker, itemstack)
if not itemstack or minetest.is_protected(pos, clicker:get_player_name()) then
return
end
local meta = minetest.get_meta(pos) ; if not meta then return end
if meta:get_string("item") ~= "" then
show_msg(clicker:get_player_name()
.. " removed " .. meta:get_string("item")
.. " from Pedestal at " .. minetest.pos_to_string(pos))
if should_return_item then
return return_item(pos, "pedestal", meta, clicker, itemstack)
else
drop_item(pos, "pedestal", meta)
end
else
local s = itemstack:take_item()
local item = s:to_string()
meta:set_string("item", item)
meta:set_string("infotext", S("Contains: ") .. get_desc(item))
update_item(pos, "pedestal", node)
show_msg(clicker:get_player_name()
.. " inserted " .. meta:get_string("item")
.. " into Pedestal at " .. minetest.pos_to_string(pos))
return itemstack
end
end,
on_destruct = function(pos)
drop_item(pos, "pedestal")
end,
on_punch = function(pos, node, puncher)
update_item(pos, "pedestal", node)
end,
on_blast = function(pos, intensity)
local pos2 = {x = pos.x, y = pos.y, z = pos.z}
drop_item(pos, "pedestal")
minetest.add_item(pos2, {name = "itemframes:pedestal"})
minetest.remove_node(pos2)
end
})
minetest.register_craft({
output = "itemframes:pedestal",
recipe = {
{ a.stone, a.stone, a.stone },
{ "", a.stone, "" },
{ a.stone, a.stone, a.stone }
}
})
-- automatically restore entities lost from frames/pedestals
-- due to /clearobjects or similar
minetest.register_lbm({
label = "Restore itemframe entities",
name = "itemframes:restore_entities",
nodenames = {"itemframes:frame", "itemframes:pedestal", "itemframes:frame_invis"},
run_at_every_load = true,
action = function(pos, node)
local ypos = 0
local ntype = "frame"
if node.name == "itemframes:pedestal" then
ypos = 1
ntype = "pedestal"
end
pos.y = pos.y + ypos
del_ent(pos)
pos.y = pos.y - ypos
update_item(pos, ntype, node)
end
})
-- stop mesecon pistons from pushing itemframe and pedestals
if minetest.get_modpath("mesecons_mvps") then
mesecon.register_mvps_stopper("itemframes:frame")
mesecon.register_mvps_stopper("itemframes:frame_invis")
mesecon.register_mvps_stopper("itemframes:pedestal")
end
print("[MOD] Itemframes loaded")