123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456 |
- --[[
- 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 not node.name:match("^drawers:") 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 not node.name:match("^drawers:") 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 = self:take_items(add_stack)
- 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, take_stack)
- local meta = core.get_meta(self.drawer_pos)
- if self.count <= 0 then
- return
- end
- local removeCount = 1
- if take_stack then
- removeCount = ItemStack(self.itemName):get_stack_max()
- 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,
- try_insert_stack = function(self, itemstack, insert_stack)
- local stackCount = itemstack:get_count()
- local stackName = itemstack:get_name()
- -- if nothing to be added, return
- if stackCount <= 0 then return itemstack end
- -- if no itemstring, return
- if stackName == "" then return itemstack end
- -- only add one, if player holding sneak key
- if not insert_stack then
- stackCount = 1
- end
- -- if current itemstring is not empty
- if self.itemName ~= "" then
- -- check if same item
- if stackName ~= self.itemName then return itemstack end
- else -- is empty
- self.itemName = stackName
- self.count = 0
- -- get new stack max
- self.itemStackMax = ItemStack(self.itemName):get_stack_max()
- self.maxCount = self.itemStackMax * self.stackMaxFactor
- end
- -- Don't add items stackable only to 1
- if self.itemStackMax == 1 then
- self.itemName = ""
- return itemstack
- end
- -- set new counts:
- -- if new count is more than max_count
- if (self.count + stackCount) > self.maxCount then
- itemstack:set_count(self.count + stackCount - self.maxCount)
- self.count = self.maxCount
- else -- new count fits
- self.count = self.count + stackCount
- -- this is for only removing one
- itemstack:set_count(itemstack:get_count() - stackCount)
- end
- -- update infotext, texture
- self:updateInfotext()
- self:updateTexture()
- self:saveMetaData()
- if itemstack:get_count() == 0 then itemstack = 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.59)
- 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
- })
|