From 22cbc058080127445b69d2137e346ad52f8b3387 Mon Sep 17 00:00:00 2001 From: DS Date: Sat, 1 Oct 2022 21:21:06 +0200 Subject: [PATCH] Add an item pick up callback (2) (#7712) Co-authored-by: SmallJoker Co-authored-by: Jude Melton-Houghton --- LICENSE.txt | 9 +- builtin/game/item.lua | 26 ++++- builtin/game/item_entity.lua | 33 ++++-- builtin/game/register.lua | 1 + doc/lua_api.txt | 25 ++++ games/devtest/mods/testitems/init.lua | 108 ++++++++++++++++++ .../textures/testitems_callback_1.png | Bin 0 -> 148 bytes .../textures/testitems_callback_2.png | Bin 0 -> 148 bytes 8 files changed, 189 insertions(+), 13 deletions(-) create mode 100644 games/devtest/mods/testitems/textures/testitems_callback_1.png create mode 100644 games/devtest/mods/testitems/textures/testitems_callback_2.png diff --git a/LICENSE.txt b/LICENSE.txt index af4c4c546..0a27f907a 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -14,6 +14,9 @@ https://www.apache.org/licenses/LICENSE-2.0.html Textures by Zughy are under CC BY-SA 4.0 https://creativecommons.org/licenses/by-sa/4.0/ +Media files by DS are under CC BY-SA 4.0 +https://creativecommons.org/licenses/by-sa/4.0/ + textures/base/pack/server_public.png is under CC-BY 4.0, taken from Twitter's Twemoji set https://creativecommons.org/licenses/by/4.0/ @@ -62,7 +65,7 @@ Zughy: appgurueu: textures/base/pack/server_incompatible.png - + erlehmann, Warr1024, rollerozxa: textures/base/pack/no_screenshot.png @@ -73,6 +76,10 @@ SmallJoker: textures/base/pack/cdb_clear.png textures/base/pack/server_favorite_delete.png (based on server_favorite.png) +DS: + games/devtest/mods/testitems/textures/testitems_callback_1.png + games/devtest/mods/testitems/textures/testitems_callback_2.png + License of Minetest source code ------------------------------- diff --git a/builtin/game/item.lua b/builtin/game/item.lua index af3fcb645..2e8fcc343 100644 --- a/builtin/game/item.lua +++ b/builtin/game/item.lua @@ -349,8 +349,26 @@ function core.item_drop(itemstack, dropper, pos) -- environment failed end +function core.item_pickup(itemstack, picker, pointed_thing, ...) + itemstack = ItemStack(itemstack) + -- Invoke global on_item_pickup callbacks. + for _, callback in ipairs(core.registered_on_item_pickups) do + local result = callback(itemstack, picker, pointed_thing, ...) + if result then + return ItemStack(result) + end + end + + -- Pickup item. + local inv = picker and picker:get_inventory() + if inv then + return inv:add_item("main", itemstack) + end + return itemstack +end + function core.do_item_eat(hp_change, replace_with_item, itemstack, user, pointed_thing) - for _, callback in pairs(core.registered_on_item_eats) do + for _, callback in ipairs(core.registered_on_item_eats) do local result = callback(hp_change, replace_with_item, itemstack, user, pointed_thing) if result then return result @@ -589,6 +607,7 @@ core.nodedef_default = { -- Interaction callbacks on_place = redef_wrapper(core, 'item_place'), -- core.item_place on_drop = redef_wrapper(core, 'item_drop'), -- core.item_drop + on_pickup = redef_wrapper(core, 'item_pickup'), -- core.item_pickup on_use = nil, can_dig = nil, @@ -641,6 +660,7 @@ core.craftitemdef_default = { -- Interaction callbacks on_place = redef_wrapper(core, 'item_place'), -- core.item_place on_drop = redef_wrapper(core, 'item_drop'), -- core.item_drop + on_pickup = redef_wrapper(core, 'item_pickup'), -- core.item_pickup on_secondary_use = redef_wrapper(core, 'item_secondary_use'), on_use = nil, } @@ -661,6 +681,7 @@ core.tooldef_default = { on_place = redef_wrapper(core, 'item_place'), -- core.item_place on_secondary_use = redef_wrapper(core, 'item_secondary_use'), on_drop = redef_wrapper(core, 'item_drop'), -- core.item_drop + on_pickup = redef_wrapper(core, 'item_pickup'), -- core.item_pickup on_use = nil, } @@ -677,8 +698,9 @@ core.noneitemdef_default = { -- This is used for the hand and unknown items tool_capabilities = nil, -- Interaction callbacks - on_place = redef_wrapper(core, 'item_place'), + on_place = redef_wrapper(core, 'item_place'), -- core.item_place on_secondary_use = redef_wrapper(core, 'item_secondary_use'), + on_pickup = redef_wrapper(core, 'item_pickup'), -- core.item_pickup on_drop = nil, on_use = nil, } diff --git a/builtin/game/item_entity.lua b/builtin/game/item_entity.lua index 53f98a7c7..f321bb1dd 100644 --- a/builtin/game/item_entity.lua +++ b/builtin/game/item_entity.lua @@ -318,16 +318,29 @@ core.register_entity(":__builtin:item", { end end, - on_punch = function(self, hitter) - local inv = hitter:get_inventory() - if inv and self.itemstring ~= "" then - local left = inv:add_item("main", self.itemstring) - if left and not left:is_empty() then - self:set_item(left) - return - end + on_punch = function(self, hitter, ...) + if self.itemstring == "" then + self.object:remove() + return + end + + -- Call on_pickup callback in item definition. + local itemstack = ItemStack(self.itemstring) + local callback = itemstack:get_definition().on_pickup + + local ret = callback(itemstack, hitter, {type = "object", ref = self.object}, ...) + if not ret then + -- Don't modify (and don't reset rotation) + return + end + itemstack = ItemStack(ret) + + -- Handle the leftover itemstack + if itemstack:is_empty() then + self.itemstring = "" + self.object:remove() + else + self:set_item(itemstack) end - self.itemstring = "" - self.object:remove() end, }) diff --git a/builtin/game/register.lua b/builtin/game/register.lua index 8b6f5b990..d4c876898 100644 --- a/builtin/game/register.lua +++ b/builtin/game/register.lua @@ -607,6 +607,7 @@ core.registered_on_crafts, core.register_on_craft = make_registration() core.registered_craft_predicts, core.register_craft_predict = make_registration() core.registered_on_protection_violation, core.register_on_protection_violation = make_registration() core.registered_on_item_eats, core.register_on_item_eat = make_registration() +core.registered_on_item_pickups, core.register_on_item_pickup = make_registration() core.registered_on_punchplayers, core.register_on_punchplayer = make_registration() core.registered_on_priv_grant, core.register_on_priv_grant = make_registration() core.registered_on_priv_revoke, core.register_on_priv_revoke = make_registration() diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 201f4d123..390bf8b2f 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -5318,6 +5318,13 @@ Call these functions only at load time! * `minetest.register_on_item_eat(function(hp_change, replace_with_item, itemstack, user, pointed_thing))` * Called when an item is eaten, by `minetest.item_eat` * Return `itemstack` to cancel the default item eat response (i.e.: hp increase). +* `minetest.register_on_item_pickup(function(itemstack, picker, pointed_thing, time_from_last_punch, ...))` + * Called by `minetest.item_pickup` before an item is picked up. + * Function is added to `minetest.registered_on_item_pickups`. + * Oldest functions are called first. + * Parameters are the same as in the `on_pickup` callback. + * Return an itemstack to cancel the default item pick-up response (i.e.: adding + the item into inventory). * `minetest.register_on_priv_grant(function(name, granter, priv))` * Called when `granter` grants the priv `priv` to `name`. * Note that the callback will be called twice if it's done by a player, @@ -5964,6 +5971,11 @@ Defaults for the `on_place` and `on_drop` item definition functions * `param2` overrides facedir and wallmounted `param2` * returns `itemstack, position` * `position`: the location the node was placed to. `nil` if nothing was placed. +* `minetest.item_pickup(itemstack, picker, pointed_thing, time_from_last_punch, ...)` + * Runs callbacks registered by `minetest.register_on_item_pickup` and adds + the item to the picker's `"main"` inventory list. + * Parameters are the same as in `on_pickup`. + * Returns the leftover itemstack. * `minetest.item_drop(itemstack, dropper, pos)` * Drop the item * returns the leftover itemstack @@ -8074,6 +8086,19 @@ Used by `minetest.register_node`, `minetest.register_craftitem`, and -- The dropper may be any ObjectRef or nil. -- default: minetest.item_drop + on_pickup = function(itemstack, picker, pointed_thing, time_from_last_punch, ...), + -- Called when a dropped item is punched by a player. + -- Shall pick-up the item and return the leftover itemstack or nil to not + -- modify the dropped item. + -- Parameters: + -- * `itemstack`: The `ItemStack` to be picked up. + -- * `picker`: Any `ObjectRef` or `nil`. + -- * `pointed_thing` (optional): The dropped item (a `"__builtin:item"` + -- luaentity) as `type="object"` `pointed_thing`. + -- * `time_from_last_punch, ...` (optional): Other parameters from + -- `luaentity:on_punch`. + -- default: `minetest.item_pickup` + on_use = function(itemstack, user, pointed_thing), -- default: nil -- When user pressed the 'punch/mine' key with the item in hand. diff --git a/games/devtest/mods/testitems/init.lua b/games/devtest/mods/testitems/init.lua index 33ebf50fc..0c4cc60de 100644 --- a/games/devtest/mods/testitems/init.lua +++ b/games/devtest/mods/testitems/init.lua @@ -53,3 +53,111 @@ minetest.register_craftitem("testitems:overlay_global", { }) +-- +-- Item callbacks +-- + +minetest.register_craftitem("testitems:callback_1", { + description = "Callback test item 1\n(Use/Drop + Sneak to switch to item 2)", + inventory_image = "testitems_callback_1.png", + wield_image = "testitems_callback_1.png", + + on_secondary_use = function(itemstack, user, pointed_thing) + minetest.log("[testitems:callback_1 on_secondary_use] " .. itemstack:get_name()) + local ctrl = user and user:get_player_control() or {} + if ctrl.sneak then + itemstack = ItemStack(itemstack) + itemstack:set_name("testitems:callback_2") + return itemstack + end + end, + + on_drop = function(itemstack, dropper, pos) + minetest.log("[testitems:callback_1 on_drop] " .. itemstack:get_name()) + local ctrl = dropper and dropper:get_player_control() or {} + if ctrl.sneak then + itemstack = ItemStack(itemstack) + itemstack:set_name("testitems:callback_2") + end + + return minetest.item_drop(itemstack, dropper, pos) + end, + + on_pickup = function(itemstack, picker, pointed_thing, ...) + minetest.log("[testitems:callback_1 on_pickup]") + assert(pointed_thing.ref:get_luaentity().name == "__builtin:item") + local ctrl = picker and picker:get_player_control() or {} + if ctrl.aux1 then + -- Debug message + minetest.log(dump({...})) + end + if ctrl.sneak then + -- Pick up one item of the other kind at once + local taken = itemstack:take_item() + taken:set_name("testitems:callback_2") + local leftover = minetest.item_pickup(taken, picker, pointed_thing, ...) + leftover:set_name("testitems:callback_1") + itemstack:add_item(leftover) + return itemstack + elseif ctrl.up then + -- Don't pick up + return + elseif ctrl.left then + -- Eat it + return minetest.do_item_eat(2, nil, itemstack, picker, pointed_thing) + else + -- Normal: pick up everything + return minetest.item_pickup(itemstack, picker, pointed_thing, ...) + end + end, + + on_use = function(itemstack, user, pointed_thing) + minetest.log("[testitems:callback_1 on_use] " .. itemstack:get_name()) + local ctrl = user and user:get_player_control() or {} + if ctrl.sneak then + itemstack = ItemStack(itemstack) + itemstack:set_name("testitems:callback_2") + return itemstack + end + end, + + after_use = function(itemstack, user, node, digparams) -- never called + minetest.log("[testitems:callback_1 after_use]") + local ctrl = user and user:get_player_control() or {} + if ctrl.up then + itemstack = ItemStack(itemstack) + itemstack:set_name("testitems:callback_2") + return itemstack + end + end, +}) + +minetest.register_craftitem("testitems:callback_2", { + description = "Callback test item 2\n(Use to switch to item 1)", + inventory_image = "testitems_callback_2.png", + wield_image = "testitems_callback_2.png", + + on_use = function(itemstack, user, pointed_thing) + minetest.log("[testitems:callback_2 on_use]") + itemstack = ItemStack(itemstack) + itemstack:set_name("testitems:callback_1") + return itemstack + end, +}) + +minetest.register_on_item_pickup(function(itemstack, picker, pointed_thing, time_from_last_punch, ...) + assert(not pointed_thing or pointed_thing.ref:get_luaentity().name == "__builtin:item") + + local item_name = itemstack:get_name() + if item_name ~= "testitems:callback_1" and item_name ~= "testitems:callback_2" then + return + end + minetest.log("["..item_name.." register_on_item_pickup]") + + local ctrl = picker and picker:get_player_control() or {} + if item_name == "testitems:callback_2" and not ctrl.sneak then + -- Same here. Pick up the other item type. + itemstack:set_name("testitems:callback_1") + return picker:get_inventory():add_item("main", itemstack) + end +end) diff --git a/games/devtest/mods/testitems/textures/testitems_callback_1.png b/games/devtest/mods/testitems/textures/testitems_callback_1.png new file mode 100644 index 0000000000000000000000000000000000000000..92cbc1be30bb9e0f9f775da8a07612c91cff4a35 GIT binary patch literal 148 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!93?!50ihlx9JOMr-u8#FU76|s0gam^Ji3tf6$$Ux|B6xWI)ZeR5*bwzMi}&zv`}g(C nuJ