allow NPC to use tools when punching and right-clicking blocks

This commit is contained in:
Sokomine 2024-07-20 01:20:08 +02:00
parent 1323f3d8fc
commit b9bebe82bd
3 changed files with 122 additions and 24 deletions

View File

@ -159,6 +159,8 @@ yl_speak_up.blacklist_effect_on_block_right_click = {}
-- taking something out of the inventory of a block or putting something in
yl_speak_up.blacklist_effect_on_block_put = {}
yl_speak_up.blacklist_effect_on_block_take = {}
-- tools the NPC shall not be able to use (covers both punching and right-click):
yl_speak_up.blacklist_effect_tool_use = {}
-- If some items are for some reasons not at all acceptable as quest items,
-- blacklist them here. The data structure is the same as for the tables above.

View File

@ -19,7 +19,11 @@ yl_speak_up.dropdown_values_deal_with_offered_item = {
-- how how to interact with the node
-- node_name the node to place
-- node_there the node that can currently be found at that position
yl_speak_up.check_blacklisted = function(how, node_name, node_there)
-- tool_name the name of the tool the NPC wants to use (punch or right-click with)
yl_speak_up.check_blacklisted = function(how, node_name, node_there, tool_name)
if(tool_name) then
return yl_speak_up.blacklist_effect_tool_use[ tool_name ]
end
return yl_speak_up.blacklist_effect_on_block_interact[ node_name ]
or yl_speak_up.blacklist_effect_on_block_interact[ node_there ]
or (how == "place" and yl_speak_up.blacklist_effect_on_block_place[ node_name ])
@ -31,6 +35,79 @@ yl_speak_up.check_blacklisted = function(how, node_name, node_there)
end
-- create fake playerdata so that the NPC can interact with inventories, punch and right-click blocks
yl_speak_up.get_fake_player = function(owner_name, wielded_item)
return {
get_player_name = function()
return owner_name
end,
is_player = function()
return true
end,
is_fake_player = true,
get_wielded_item = function(self, item)
return ItemStack(wielded_item)
end,
get_player_control = function()
-- NPC is not sneaking
return {}
end,
}
end
-- shall the NPC wield and use a tool? if so that tools' on_use or on_place
-- function takes precedence over the block it's used on
yl_speak_up.use_tool_on_block = function(r, fun_name, player, n_id, o_id)
if(not(r.r_wielded) or r.r_wielded == "") then
return false
end
-- we need the owner_name for creating the fake player
local owner_name = yl_speak_up.npc_owner[ n_id ]
if(not(owner_name) or owner_name == "") then
yl_speak_up.debug_msg(player, n_id, o_id, tostring(r.r_id).." "..
r.r_type..": NPC does not have an owner. Aborting.")
return false
end
local npc_inv = minetest.get_inventory({type="detached", name="yl_speak_up_npc_"..tostring(n_id)})
-- can the tool be used?
local tool_err_msg = nil
if(not(minetest.registered_items[r.r_wielded])) then
tool_err_msg = "Tool not defined"
elseif(not(minetest.registered_items[r.r_wielded][fun_name])) then
tool_err_msg = "Tool does not support "..tostring(r.r_value).."ing"
-- do not use forbidden tools
elseif(yl_speak_up.check_blacklisted(nil, nil, nil, r.r_wielded)) then
tool_err_msg = "NPC are not allowed to use this tool"
-- does the NPC have the item he's supposed to wield?
elseif(not(npc_inv:contains_item("npc_main", r.r_wielded, false))) then
tool_err_msg = "NPC lacks tool"
end
if(tool_err_msg) then
yl_speak_up.debug_msg(player, n_id, o_id, tostring(r.r_id)..
" block: "..tostring(r.r_value).." - "..tool_err_msg.. ": \""..tostring(r.r_wielded).."\".")
return false
end
-- act in the name of the owner when accessing inventories
local fake_player = yl_speak_up.get_fake_player(owner_name, r.r_wielded)
local itemstack = fake_player:get_wielded_item()
local pointed_thing = {
type = "node",
under = r.r_pos,
above = {x=r.r_pos.x, y=r.r_pos.y+1, z=r.r_pos.z}
}
local new_itemstack = minetest.registered_items[r.r_wielded][fun_name](
itemstack, fake_player, pointed_thing)
minetest.chat_send_player("singleplayer", "Did the rightclicking. Result: "..new_itemstack:get_name().." fun_name: "..tostring(fun_name))
if(new_itemstack) then
-- apply any itemstack changes
npc_inv:remove_item("npc_main", itemstack)
npc_inv:add_item("npc_main", new_itemstack)
end
return true
end
-- called by yl_speak_up.input_talk(..)
-- and also by yl_speak_up.get_fs_trade_list(..)
--
@ -318,7 +395,8 @@ yl_speak_up.execute_effect = function(player, n_id, o_id, r)
return false
end
-- do not interact with nodes on the blacklist
if(yl_speak_up.check_blacklisted(how_to_interact, node.name, node.name)) then
-- (this here is inventory interaction, so no need to check for tools)
if(yl_speak_up.check_blacklisted(how_to_interact, node.name, node.name, nil)) then
yl_speak_up.debug_msg(player, n_id, o_id, tostring(r.r_id).." "..
r.r_type..": Blocks of type \""..tostring(node.name).."\" do not allow "..
"interaction of type \""..tostring(r.r_value).."\" for NPC.")
@ -332,18 +410,7 @@ yl_speak_up.execute_effect = function(player, n_id, o_id, r)
return false
end
-- act in the name of the owner when accessing inventories
local fake_player = {
get_player_name = function() return owner_name end,
is_player = function() return true end,
is_fake_player = true,
get_wielded_item = function(self, item)
if(self._inventory and def.wield_list) then
return self._inventory:get_stack(def.wield_list, self._wield_index)
end
return ItemStack(self._wielded_item)
end,
}
-- TODO: get the fake player from pipeworks?
local fake_player = yl_speak_up.get_fake_player(owner_name, "")
local def = minetest.registered_nodes[ node.name ]
if(def and def[ "allow_metadata_inventory_"..how_to_interact ]) then
local res = def[ "allow_metadata_inventory_"..how_to_interact ](
@ -618,7 +685,7 @@ yl_speak_up.execute_effect = function(player, n_id, o_id, r)
return false
end
-- do not interact with nodes on the blacklist
if(yl_speak_up.check_blacklisted(r.r_value, r.r_node, node.name)) then
if(yl_speak_up.check_blacklisted(r.r_value, r.r_node, node.name, nil)) then
-- construct the right text for the error message
local nname = node.name
if(r.r_value == "place") then
@ -640,6 +707,14 @@ yl_speak_up.execute_effect = function(player, n_id, o_id, r)
" and thus cannot interact with it.")
return false
end
-- create a fake player and a suitable itemstack
local owner_name = yl_speak_up.npc_owner[ n_id ]
if(not(owner_name) or owner_name == "") then
yl_speak_up.debug_msg(player, n_id, o_id, tostring(r.r_id).." "..
r.r_type..": NPC does not have an owner. Aborting.")
return false
end
-- "If there is air: Place a block so that it looks like now.", -- 2
if(r.r_value and r.r_value == "place") then
if(is_protected) then
@ -710,11 +785,22 @@ yl_speak_up.execute_effect = function(player, n_id, o_id, r)
return true
-- "Punch the block.", -- 4
elseif(r.r_value and r.r_value == "punch") then
-- shall the NPC wield and use an item? if so that items' on_use function takes
-- precedence
if(r.r_wielded and r.r_wielded ~= "") then
return yl_speak_up.use_tool_on_block(r, "on_use", player, n_id, o_id)
end
-- even air can be punched - even if that is pretty pointless
minetest.punch_node(r.r_pos)
minetest.punch_node(r.r_pos, nil)
return true
-- "Right-click the block.", -- 5
elseif(r.r_value and r.r_value == "right-click") then
-- shall the NPC wield and use an item? if so that items' on_use function takes
-- precedence
if(r.r_wielded and r.r_wielded ~= "") then
return yl_speak_up.use_tool_on_block(r, "on_place", player, n_id, o_id)
end
-- with a tool, clicking on air might make sense; without a tool it doesn't
if(not(node) or not(node.name) or not(minetest.registered_nodes[node.name])) then
return false
end
@ -748,14 +834,18 @@ yl_speak_up.execute_effect = function(player, n_id, o_id, r)
yl_speak_up.debug_msg(player, n_id, o_id, tostring(r.r_id).." "..
"block: Opened/closed trapdoor at "..pos_str..".")
elseif(minetest.registered_nodes[node.name]
and minetest.registered_nodes[node.name].on_rightclick
and minetest.registered_nodes[node.name].on_rightclick(r.r_pos, node, nil)) then
minetest.registered_nodes[node.name].on_rightclick(r.r_pos, node, nil)
yl_speak_up.debug_msg(player, n_id, o_id, tostring(r.r_id).." "..
"block: right-clicked at at pos "..pos_str..".")
else
yl_speak_up.debug_msg(player, n_id, o_id, tostring(r.r_id).." "..
"block: right-click at at pos "..pos_str.." had no effect.")
and minetest.registered_nodes[node.name].on_rightclick) then
local fake_player = yl_speak_up.get_fake_player(owner_name, "")
local itemstack = ItemStack("")
local pointed_thing = nil -- TODO
if(minetest.registered_nodes[node.name].on_rightclick(
r.r_pos, node, fake_player, itemstack, pointed_thing)) then
yl_speak_up.debug_msg(player, n_id, o_id, tostring(r.r_id).." "..
"block: right-clicked at at pos "..pos_str..".")
else
yl_speak_up.debug_msg(player, n_id, o_id, tostring(r.r_id).." "..
"block: right-click at at pos "..pos_str.." had no effect.")
end
end
end
return false

View File

@ -728,6 +728,12 @@ contain all the blocks which do not allow the NPCs this kind of interaction.
<br>You may i.e. set the `put` and `take` tables for blocks that do extensive
checks on the player object which the NPC simply can't provide.
```
yl_speak_up.blacklist_effect_tool_use
```
is a similar table. Please let it contain a list of all the tool item names that NPC are _not_ allowed to u se.
_Note_: The best way to deal with local adjustments may be to create your
own mod, i.e. `yl_speak_up_addons`, and let that mod depend on this
one, `yl_speak_up`, and do the necessary calls. This is very useful