added npc_force_restore command for lost npc

This commit is contained in:
Sokomine 2023-07-26 18:33:17 +02:00
parent 0585dd4b77
commit b9c8e3c345
3 changed files with 161 additions and 2 deletions

View File

@ -79,6 +79,14 @@ and set its name).
NPC, the priv will be considered granted if either the NPC, the priv will be considered granted if either the
executing NPC or the the generic NPC has the priv. executing NPC or the the generic NPC has the priv.
#### `/npc_talk force_restore_npc <id> [<copy_from_id>]` Restore an NPC that got lost.
It may have got lost due to someone having misplaced its egg.
Or it might have been killed somehow.
The optional parameter `<copy_from_id>` is only used when the NPC
is *not* listed in `/npc_talk list`. You won't need it. It's for legacy NPC.
WARNING: If the egg or the NPC turns up elsewhere, be sure to have only
*ONE* NPC with that ID standing around! Else you'll get chaos.
#### `/npc_talk generic` Add or remove NPC from the list of generic dialog providers. #### `/npc_talk generic` Add or remove NPC from the list of generic dialog providers.
`/npc_talk generic list` Lists all generic NPC `/npc_talk generic list` Lists all generic NPC
`/npc_talk generic add n_3` Adds the dialog from NPC as a `/npc_talk generic add n_3` Adds the dialog from NPC as a

View File

@ -33,6 +33,16 @@ yl_speak_up.command_npc_talk = function(pname, param)
end end
-- implemented in add_generic_dialogs.lua: -- implemented in add_generic_dialogs.lua:
return yl_speak_up.command_npc_talk_generic(pname, rest) return yl_speak_up.command_npc_talk_generic(pname, rest)
-- restore an NPC that got lost
elseif(cmd and cmd == "force_restore_npc") then
if(not(minetest.check_player_privs(pname, {npc_talk_admin = true}))) then
minetest.chat_send_player(pname, "This command is used for restoring "..
"NPC that somehow got lost (egg destroyed, killed, ..). You "..
"lack the \"npc_talk_admin\" priv required to run this command.")
return
end
-- implemented in fs_npc_list.lua:
return yl_speak_up.command_npc_force_restore_npc(pname, rest)
elseif(cmd and cmd == "privs") then elseif(cmd and cmd == "privs") then
-- TODO: make this available for npc_talk_admin? -- TODO: make this available for npc_talk_admin?
if(not(minetest.check_player_privs(pname, {privs = true}))) then if(not(minetest.check_player_privs(pname, {privs = true}))) then
@ -54,8 +64,9 @@ yl_speak_up.command_npc_talk = function(pname, param)
" list shows a list of NPC that you can edit\n".. " list shows a list of NPC that you can edit\n"..
" debug debug a particular NPC\n".. " debug debug a particular NPC\n"..
" force_edit forces edit mode for any NPC you talk to\n".. " force_edit forces edit mode for any NPC you talk to\n"..
" generic [requores npc_talk_admin priv] list, add or remove NPC as generic NPC\n".. " generic [requores npc_talk_admin priv] list, add or remove NPC as generic NPC\n"..
" privs [requires privs priv] list, grant or revoke privs for an NPC\n".. " force_restore_npc [requires npc_talk_admin priv] restore NPC that got lost\n"..
" privs [requires privs priv] list, grant or revoke privs for an NPC\n"..
-- reload is fully handled in register_once -- reload is fully handled in register_once
"Note: /npc_talk_reload [requires privs priv] reloads the code of the mod without server ".. "Note: /npc_talk_reload [requires privs priv] reloads the code of the mod without server "..
"restart.") "restart.")

View File

@ -84,6 +84,8 @@ yl_speak_up.update_npc_data = function(self, dialog, force_store)
properties = properties, properties = properties,
created_at = created_at, created_at = created_at,
muted = self.yl_speak_up.talk, muted = self.yl_speak_up.talk,
animation = self.yl_speak_up.animation,
skin = self.yl_speak_up.skin,
} }
-- the current object will change after deactivate; there is no point in storing -- the current object will change after deactivate; there is no point in storing
-- it over server restart -- it over server restart
@ -117,6 +119,144 @@ yl_speak_up.npc_list_store = function()
end end
-- emergency restore NPC that got lost (egg deleted, killed, ...)
yl_speak_up.command_npc_force_restore_npc = function(pname, rest)
if(not(pname)) then
return
end
if(not(minetest.check_player_privs(pname, {npc_talk_admin = true}))) then
minetest.chat_send_player(pname, "This command is used for restoring "..
"NPC that somehow got lost (egg destroyed, killed, ..). You "..
"lack the \"npc_talk_admin\" priv required to run this command.")
return
end
if(not(rest) or rest == "" or rest == "help" or rest == "?") then
minetest.chat_send_player(pname, "This command is used for restoring "..
"NPC that somehow got lost (egg destroyed, killed, ..).\n"..
"WARNING: If the egg is found again later on, make sure that "..
"this restored NPC and the NPC from the egg are not both placed!\n"..
" There can only be one NPC per ID.\n"..
"Syntax: /npc_talk force_restore_npc <id> [<copy_from_id>]\n"..
" <id> is the ID (number! Without \"n_\") of the NPC to be restored.\n"..
" <copy_from_id> is only needed if the NPC is not listed in "..
"\"/npc_talk list\" (=extremly old NPC).")
return
end
local parts = string.split(rest or "", " ", false, 1)
local id = tonumber(parts[1] or "")
if(not(id)) then
minetest.chat_send_player(pname, "Please provide the ID (number!) of the NPC "..
"you wish to restore.")
return
elseif(not(yl_speak_up.number_of_npcs) or yl_speak_up.number_of_npcs < 1
or id > yl_speak_up.number_of_npcs) then
minetest.chat_send_player(pname, "That ID is larger than the amount of existing NPC. "..
"Restoring is for old NPC that got lost.")
return
elseif(id < 1) then
minetest.chat_send_player(pname, "That ID is smaller than 1. Can't restore negative NPC.")
return
end
local player = minetest.get_player_by_name(pname)
if(not(player)) then
return
end
-- if we've seen the NPC before: make sure he's not just unloaded because nobody is where he is
if(yl_speak_up.npc_list[id] and yl_speak_up.npc_list[id].pos
and yl_speak_up.npc_list[id].pos.x
and yl_speak_up.npc_list[id].pos.y
and yl_speak_up.npc_list[id].pos.z) then
local v_npc = vector.new(yl_speak_up.npc_list[id].pos)
local v_pl = vector.new(player:get_pos())
if(vector.distance(v_npc, v_pl) > 6) then
minetest.chat_send_player(pname, "You are more than 6 m away from the last "..
"known position of this NPC at "..
minetest.pos_to_string(yl_speak_up.npc_list[id].pos)..
". Please move closer to make sure the NPC isn't just not loaded "..
"due to nobody beeing near!")
return
end
end
-- check the currently loaded mobs to make sure he wasn't loaded since last update of our list
for k,v in pairs(minetest.luaentities) do
if(v and v.yl_speak_up and v.yl_speak_up.id and v.yl_speak_up.id == id) then
minetest.chat_send_player(pname, "An NPC with the ID "..tostring(id)..
" is currently loaded. No restoring required!")
return
end
end
local data = nil
-- do we need a donator NPC because we have never seen this NPC and have no data?
local copy_from_id = tonumber(parts[2] or "")
if(not(yl_speak_up.npc_list[id])) then
if(not(copy_from_id) or not(yl_speak_up.npc_list[copy_from_id])) then
minetest.chat_send_player(pname, "We have no data on NPC "..tostring(id)..
". Please provide the ID of an EXISTING NPC from which necessary "..
"data can be copied!")
return
end
minetest.chat_send_player(pname, "Will use the data of the NPC with ID "..
tostring(copy_from_id).." to set up the new/restored NPC.")
data = yl_speak_up.npc_list[copy_from_id]
else
data = yl_speak_up.npc_list[id]
end
-- ok..the NPC is not loaded. Perhaps he really got lost.
minetest.chat_send_player(pname, "Will try to restore the NPC with the ID "..tostring(id)..".")
if(not(data.typ) or not(minetest.registered_entities[data.typ or "?"])) then
minetest.chat_send_player(pname, "Error: No NPC entity prototype found for \""..
tostring(data.name).."\". Aborting.")
return
end
-- this is an emergency fallback restore - so it's ok to drop the NPC where the admin is standing
local mob = minetest.add_entity(player:get_pos(), data.typ)
local ent = mob and mob:get_luaentity()
if(not(ent)) then
minetest.chat_send_player(pname, "Failed to create a new NPC entity of type \""..
tostring(data.name).."\". Aborting.")
return
end
-- set up the new NPC
local npc_name = data.name
local npc_desc = data.npc_description
local npc_owner = data.owner
-- the dialog includes the trades, n_may_edit and other data
local dialog = yl_speak_up.load_dialog(id, nil)
-- restore name and description from dialog if possible
if(dialog and dialog.n_npc) then
npc_name = dialog.n_npc
npc_desc = dialog.n_description
npc_owner = dialog.npc_owner or data.owner
end
ent.yl_speak_up = {
id = id,
talk = data.muted,
properties = data.properties,
npc_name = npc_name,
npc_description = npc_desc,
infotext = yl_speak_up.infotext, -- will be set automaticly later
animation = data.animation,
}
-- This is at least useful for mobs_redo. Other mob mods may require adjustments.
ent.owner = npc_owner
ent.tamed = true
-- update nametag, infotext etc.
yl_speak_up.update_nametag(ent)
if(data.animation and ent.object) then
ent.object:set_animation(data.animation)
end
if(data.skin and ent.object) then
ent.object:set_properties({textures = table.copy(data.skin)})
end
-- update the NPC list
yl_speak_up.update_npc_data(ent, dialog, true)
minetest.chat_send_player(pname, "Placed the restored NPC ID "..tostring(id)..
", named "..tostring(data.name)..", right where you stand.")
end
-- provides a list of NPC the player can edit -- provides a list of NPC the player can edit
yl_speak_up.command_npc_talk_list = function(pname, rest) yl_speak_up.command_npc_talk_list = function(pname, rest)
if(not(pname)) then if(not(pname)) then