mirror of
https://github.com/minetest-mods/drawers.git
synced 2024-11-25 16:13:45 +01:00
c490b1fc12
Co-authored-by: jessicara <chi@chi.hakurei.shrine>
466 lines
13 KiB
Lua
Executable File
466 lines
13 KiB
Lua
Executable File
--[[
|
|
Minetest Mod Storage Drawers - A Mod adding storage drawers
|
|
|
|
Copyright (C) 2017-2019 Linus Jahn <lnj@kaidan.im>
|
|
|
|
MIT License
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
]]
|
|
|
|
-- Load support for intllib.
|
|
local MP = core.get_modpath(core.get_current_modname())
|
|
local S, NS = dofile(MP.."/intllib.lua")
|
|
|
|
core.register_entity("drawers:visual", {
|
|
initial_properties = {
|
|
hp_max = 1,
|
|
physical = false,
|
|
collide_with_objects = false,
|
|
collisionbox = {-0.4374, -0.4374, 0, 0.4374, 0.4374, 0}, -- for param2 0, 2
|
|
visual = "upright_sprite", -- "wielditem" for items without inv img?
|
|
visual_size = {x = 0.6, y = 0.6},
|
|
textures = {"blank.png"},
|
|
spritediv = {x = 1, y = 1},
|
|
initial_sprite_basepos = {x = 0, y = 0},
|
|
is_visible = true,
|
|
},
|
|
|
|
get_staticdata = function(self)
|
|
return core.serialize({
|
|
drawer_posx = self.drawer_pos.x,
|
|
drawer_posy = self.drawer_pos.y,
|
|
drawer_posz = self.drawer_pos.z,
|
|
texture = self.texture,
|
|
drawerType = self.drawerType,
|
|
visualId = self.visualId
|
|
})
|
|
end,
|
|
|
|
on_activate = function(self, staticdata, dtime_s)
|
|
-- Restore data
|
|
local data = core.deserialize(staticdata)
|
|
if data then
|
|
self.drawer_pos = {
|
|
x = data.drawer_posx,
|
|
y = data.drawer_posy,
|
|
z = data.drawer_posz,
|
|
}
|
|
self.texture = data.texture
|
|
self.drawerType = data.drawerType or 1
|
|
self.visualId = data.visualId or ""
|
|
|
|
-- backwards compatibility
|
|
if self.texture == "drawers_empty.png" then
|
|
self.texture = "blank.png"
|
|
end
|
|
else
|
|
self.drawer_pos = drawers.last_drawer_pos
|
|
self.texture = drawers.last_texture or "blank.png"
|
|
self.visualId = drawers.last_visual_id
|
|
self.drawerType = drawers.last_drawer_type
|
|
end
|
|
|
|
local node = minetest.get_node(self.object:get_pos())
|
|
if core.get_item_group(node.name, "drawer") == 0 then
|
|
self.object:remove()
|
|
return
|
|
end
|
|
|
|
-- add self to public drawer visuals
|
|
-- this is needed because there is no other way to get this class
|
|
-- only the underlying LuaEntitySAO
|
|
-- PLEASE contact me, if this is wrong
|
|
local vId = self.visualId
|
|
if vId == "" then vId = 1 end
|
|
local posstr = core.serialize(self.drawer_pos)
|
|
if not drawers.drawer_visuals[posstr] then
|
|
drawers.drawer_visuals[posstr] = {[vId] = self}
|
|
else
|
|
drawers.drawer_visuals[posstr][vId] = self
|
|
end
|
|
|
|
-- get meta
|
|
self.meta = core.get_meta(self.drawer_pos)
|
|
|
|
-- collisionbox
|
|
node = core.get_node(self.drawer_pos)
|
|
local colbox
|
|
if self.drawerType ~= 2 then
|
|
if node.param2 == 1 or node.param2 == 3 then
|
|
colbox = {0, -0.4374, -0.4374, 0, 0.4374, 0.4374}
|
|
else
|
|
colbox = {-0.4374, -0.4374, 0, 0.4374, 0.4374, 0} -- for param2 = 0 or 2
|
|
end
|
|
-- only half the size if it's a small drawer
|
|
if self.drawerType > 1 then
|
|
for i,j in pairs(colbox) do
|
|
colbox[i] = j * 0.5
|
|
end
|
|
end
|
|
else
|
|
if node.param2 == 1 or node.param2 == 3 then
|
|
colbox = {0, -0.2187, -0.4374, 0, 0.2187, 0.4374}
|
|
else
|
|
colbox = {-0.4374, -0.2187, 0, 0.4374, 0.2187, 0} -- for param2 = 0 or 2
|
|
end
|
|
end
|
|
|
|
-- visual size
|
|
local visual_size = {x = 0.6, y = 0.6}
|
|
if self.drawerType >= 2 then
|
|
visual_size = {x = 0.3, y = 0.3}
|
|
end
|
|
|
|
|
|
-- drawer values
|
|
local vid = self.visualId
|
|
self.count = self.meta:get_int("count"..vid)
|
|
self.itemName = self.meta:get_string("name"..vid)
|
|
self.maxCount = self.meta:get_int("max_count"..vid)
|
|
self.itemStackMax = self.meta:get_int("base_stack_max"..vid)
|
|
self.stackMaxFactor = self.meta:get_int("stack_max_factor"..vid)
|
|
|
|
|
|
-- infotext
|
|
local infotext = self.meta:get_string("entity_infotext"..vid) .. "\n\n\n\n\n"
|
|
|
|
self.object:set_properties({
|
|
collisionbox = colbox,
|
|
infotext = infotext,
|
|
textures = {self.texture},
|
|
visual_size = visual_size
|
|
})
|
|
|
|
-- make entity undestroyable
|
|
self.object:set_armor_groups({immortal = 1})
|
|
end,
|
|
|
|
on_rightclick = function(self, clicker)
|
|
if core.is_protected(self.drawer_pos, clicker:get_player_name()) then
|
|
core.record_protection_violation(self.drawer_pos, clicker:get_player_name())
|
|
return
|
|
end
|
|
|
|
-- used to check if we need to play a sound in the end
|
|
local inventoryChanged = false
|
|
|
|
-- When the player uses the drawer with their bare hand all
|
|
-- stacks from the inventory will be added to the drawer.
|
|
if self.itemName ~= "" and
|
|
clicker:get_wielded_item():get_name() == "" and
|
|
not clicker:get_player_control().sneak then
|
|
-- try to insert all items from inventory
|
|
local i = 0
|
|
local inv = clicker:get_inventory()
|
|
|
|
while i <= inv:get_size("main") do
|
|
-- set current stack to leftover of insertion
|
|
local leftover = self.try_insert_stack(
|
|
self,
|
|
inv:get_stack("main", i),
|
|
true
|
|
)
|
|
|
|
-- check if something was added
|
|
if leftover:get_count() < inv:get_stack("main", i):get_count() then
|
|
inventoryChanged = true
|
|
end
|
|
|
|
-- set new stack
|
|
inv:set_stack("main", i, leftover)
|
|
i = i + 1
|
|
end
|
|
else
|
|
-- try to insert wielded item only
|
|
local leftover = self.try_insert_stack(
|
|
self,
|
|
clicker:get_wielded_item(),
|
|
not clicker:get_player_control().sneak
|
|
)
|
|
|
|
-- check if something was added
|
|
if clicker:get_wielded_item():get_count() > leftover:get_count() then
|
|
inventoryChanged = true
|
|
end
|
|
-- set the leftover as new wielded item for the player
|
|
clicker:set_wielded_item(leftover)
|
|
end
|
|
|
|
if inventoryChanged then
|
|
self:play_interact_sound()
|
|
end
|
|
end,
|
|
|
|
on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
|
|
local node = minetest.get_node(self.object:get_pos())
|
|
|
|
if core.get_item_group(node.name, "drawer") == 0 then
|
|
self.object:remove()
|
|
return
|
|
end
|
|
local add_stack = not puncher:get_player_control().sneak
|
|
if core.is_protected(self.drawer_pos, puncher:get_player_name()) then
|
|
core.record_protection_violation(self.drawer_pos, puncher:get_player_name())
|
|
return
|
|
end
|
|
local inv = puncher:get_inventory()
|
|
if inv == nil then
|
|
return
|
|
end
|
|
local spaceChecker = ItemStack(self.itemName)
|
|
if add_stack then
|
|
spaceChecker:set_count(spaceChecker:get_stack_max())
|
|
end
|
|
if not inv:room_for_item("main", spaceChecker) then
|
|
return
|
|
end
|
|
|
|
local stack
|
|
if add_stack then
|
|
stack = self:take_stack()
|
|
else
|
|
stack = self:take_items(1)
|
|
end
|
|
|
|
if stack ~= nil then
|
|
-- add removed stack to player's inventory
|
|
inv:add_item("main", stack)
|
|
|
|
-- play the interact sound
|
|
self:play_interact_sound()
|
|
end
|
|
end,
|
|
|
|
take_items = function(self, removeCount)
|
|
local meta = core.get_meta(self.drawer_pos)
|
|
|
|
if self.count <= 0 then
|
|
return
|
|
end
|
|
|
|
if removeCount > self.count then
|
|
removeCount = self.count
|
|
end
|
|
|
|
local stack = ItemStack(self.itemName)
|
|
stack:set_count(removeCount)
|
|
|
|
-- update the drawer count
|
|
self.count = self.count - removeCount
|
|
|
|
self:updateInfotext()
|
|
self:updateTexture()
|
|
self:saveMetaData()
|
|
|
|
-- return the stack that was removed from the drawer
|
|
return stack
|
|
end,
|
|
|
|
take_stack = function(self)
|
|
return self:take_items(ItemStack(self.itemName):get_stack_max())
|
|
end,
|
|
|
|
can_insert_stack = function(self, stack)
|
|
if stack:get_name() == "" or stack:get_count() <= 0 then
|
|
return 0
|
|
end
|
|
|
|
-- don't allow unstackable stacks
|
|
if self.itemName == "" and stack:get_stack_max() ~= 1 then
|
|
return stack:get_count()
|
|
end
|
|
|
|
if self.itemName ~= stack:get_name() then
|
|
return 0
|
|
end
|
|
|
|
if (self.count + stack:get_count()) <= self.maxCount then
|
|
return stack:get_count()
|
|
end
|
|
return self.maxCount - self.count
|
|
end,
|
|
|
|
try_insert_stack = function(self, itemstack, insert_all)
|
|
local stackCount = itemstack:get_count()
|
|
local stackName = itemstack:get_name()
|
|
|
|
local insertCount = self:can_insert_stack(itemstack)
|
|
|
|
if insertCount == 0 then
|
|
return itemstack
|
|
end
|
|
|
|
-- only add one, if player holding sneak key
|
|
if not insert_all then
|
|
insertCount = 1
|
|
end
|
|
|
|
-- in case the drawer was empty, initialize count, itemName, maxCount
|
|
if self.itemName == "" then
|
|
self.count = 0
|
|
self.itemName = itemstack:get_name()
|
|
self.maxCount = itemstack:get_stack_max() * self.stackMaxFactor
|
|
self.itemStackMax = itemstack:get_stack_max()
|
|
end
|
|
|
|
-- update everything
|
|
self.count = self.count + insertCount
|
|
self:updateInfotext()
|
|
self:updateTexture()
|
|
self:saveMetaData()
|
|
|
|
-- return leftover
|
|
itemstack:take_item(insertCount)
|
|
if itemstack:get_count() == 0 then
|
|
return ItemStack("")
|
|
end
|
|
return itemstack
|
|
end,
|
|
|
|
updateInfotext = function(self)
|
|
local itemDescription = ""
|
|
if core.registered_items[self.itemName] then
|
|
itemDescription = core.registered_items[self.itemName].description
|
|
end
|
|
|
|
if self.count <= 0 then
|
|
self.itemName = ""
|
|
self.meta:set_string("name"..self.visualId, self.itemName)
|
|
self.texture = "blank.png"
|
|
itemDescription = S("Empty")
|
|
end
|
|
|
|
local infotext = drawers.gen_info_text(itemDescription,
|
|
self.count, self.stackMaxFactor, self.itemStackMax)
|
|
self.meta:set_string("entity_infotext"..self.visualId, infotext)
|
|
|
|
self.object:set_properties({
|
|
infotext = infotext .. "\n\n\n\n\n"
|
|
})
|
|
end,
|
|
|
|
updateTexture = function(self)
|
|
-- texture
|
|
self.texture = drawers.get_inv_image(self.itemName)
|
|
|
|
self.object:set_properties({
|
|
textures = {self.texture}
|
|
})
|
|
end,
|
|
|
|
dropStack = function(self, itemStack)
|
|
-- print warning if dropping higher stack counts than allowed
|
|
if itemStack:get_count() > itemStack:get_stack_max() then
|
|
core.log("warning", "[drawers] Dropping item stack with higher count than allowed")
|
|
end
|
|
-- find a position containing air
|
|
local dropPos = core.find_node_near(self.drawer_pos, 1, {"air"}, false)
|
|
-- if no pos found then drop on the top of the drawer
|
|
if not dropPos then
|
|
dropPos = self.pos
|
|
dropPos.y = dropPos.y + 1
|
|
end
|
|
-- drop the item stack
|
|
core.item_drop(itemStack, nil, dropPos)
|
|
end,
|
|
|
|
dropItemOverload = function(self)
|
|
-- drop stacks until there are no more items than allowed
|
|
while self.count > self.maxCount do
|
|
-- remove the overflow
|
|
local removeCount = self.count - self.maxCount
|
|
-- if this is too much for a single stack, only take the
|
|
-- stack limit
|
|
if removeCount > self.itemStackMax then
|
|
removeCount = self.itemStackMax
|
|
end
|
|
-- remove this count from the drawer
|
|
self.count = self.count - removeCount
|
|
-- create a new item stack having the size of the remove
|
|
-- count
|
|
local stack = ItemStack(self.itemName)
|
|
stack:set_count(removeCount)
|
|
print(stack:to_string())
|
|
-- drop the stack
|
|
self:dropStack(stack)
|
|
end
|
|
end,
|
|
|
|
setStackMaxFactor = function(self, stackMaxFactor)
|
|
self.stackMaxFactor = stackMaxFactor
|
|
self.maxCount = self.stackMaxFactor * self.itemStackMax
|
|
|
|
-- will drop possible overflowing items
|
|
self:dropItemOverload()
|
|
self:updateInfotext()
|
|
self:saveMetaData()
|
|
end,
|
|
|
|
play_interact_sound = function(self)
|
|
core.sound_play("drawers_interact", {
|
|
pos = self.object:get_pos(),
|
|
max_hear_distance = 6,
|
|
gain = 2.0
|
|
})
|
|
end,
|
|
|
|
saveMetaData = function(self, meta)
|
|
self.meta:set_int("count"..self.visualId, self.count)
|
|
self.meta:set_string("name"..self.visualId, self.itemName)
|
|
self.meta:set_int("max_count"..self.visualId, self.maxCount)
|
|
self.meta:set_int("base_stack_max"..self.visualId, self.itemStackMax)
|
|
self.meta:set_int("stack_max_factor"..self.visualId, self.stackMaxFactor)
|
|
end
|
|
})
|
|
|
|
core.register_lbm({
|
|
name = "drawers:restore_visual",
|
|
nodenames = {"group:drawer"},
|
|
run_at_every_load = true,
|
|
action = function(pos, node)
|
|
local meta = core.get_meta(pos)
|
|
-- create drawer upgrade inventory
|
|
meta:get_inventory():set_size("upgrades", 5)
|
|
-- set the formspec
|
|
meta:set_string("formspec", drawers.drawer_formspec)
|
|
|
|
-- count the drawer visuals
|
|
local drawerType = core.registered_nodes[node.name].groups.drawer
|
|
local foundVisuals = 0
|
|
local objs = core.get_objects_inside_radius(pos, 0.56)
|
|
if objs then
|
|
for _, obj in pairs(objs) do
|
|
if obj and obj:get_luaentity() and
|
|
obj:get_luaentity().name == "drawers:visual" then
|
|
foundVisuals = foundVisuals + 1
|
|
end
|
|
end
|
|
end
|
|
-- if all drawer visuals were found, return
|
|
if foundVisuals == drawerType then
|
|
return
|
|
end
|
|
|
|
-- not enough visuals found, remove existing and create new ones
|
|
drawers.remove_visuals(pos)
|
|
drawers.spawn_visuals(pos)
|
|
end
|
|
})
|