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
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 list` Lists all generic NPC
`/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
-- implemented in add_generic_dialogs.lua:
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
-- TODO: make this available for npc_talk_admin?
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"..
" debug debug a particular NPC\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"..
" privs [requires privs priv] list, grant or revoke privs for an NPC\n"..
" generic [requores npc_talk_admin priv] list, add or remove NPC as generic 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
"Note: /npc_talk_reload [requires privs priv] reloads the code of the mod without server "..
"restart.")

View File

@ -84,6 +84,8 @@ yl_speak_up.update_npc_data = function(self, dialog, force_store)
properties = properties,
created_at = created_at,
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
-- it over server restart
@ -117,6 +119,144 @@ yl_speak_up.npc_list_store = function()
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
yl_speak_up.command_npc_talk_list = function(pname, rest)
if(not(pname)) then