make data persistant for idlers and fix a multi-spawn bug

master
xisd 2020-01-14 00:32:28 +01:00
parent 25dec928ca
commit 1b3995345b
2 changed files with 233 additions and 78 deletions

View File

@ -197,6 +197,7 @@ mobs.npcs.messages.get_list_from_file = function(basename)
end
-- Fill messages array when server start.
--
local default_messages_file_name = "mobs_npc_messages"
local messages_default = mobs.npcs.messages.get_list_from_file("mobs_npc_messages")
@ -227,6 +228,6 @@ mobs.npcs.messages.get_random = function()
end
mobs.npcs.messages.get_random_from_string = function(str)
arr = string.lines_to_table(str)
local arr = string.lines_to_table(str)
return table.random(arr)
end

View File

@ -6,6 +6,7 @@
local S = mobs.translator
local vlog = mobs.vlog or "action" --"info"
local vlog = "action" --"info"
-- Mod related infos
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
@ -13,7 +14,16 @@ local modpath = minetest.get_modpath(modname)
local mob_name = "npc_idler"
local icprefix=modname..'_icon_'
mobs.npcs.mob_data = {}
local mod_storage = minetest.get_mod_storage()
local serialized_mob_data = mod_storage:get_string('mob_data')
mobs.npcs.mob_data = minetest.deserialize(serialized_mob_data) or {}
-- ------------------
--
-- helper function
--
local function find_in_array(needle,stack)
if needle and stack then
@ -25,16 +35,78 @@ local function find_in_array(needle,stack)
end
return nil
end
data_values = {
"nametag_color",
"nametag",
"infotext",
"textures",
"infotext",
"wield_item",
"visual_size",
"action",
}
data_serialize = {
textures = true,
action = true,
}
local function save_node_meta(pos,data)
local meta = minetest.get_meta(pos)
for _,v in ipairs(data_values) do
if data_serialize[v] then
local szv = minetest.serialize(data[v])
meta:set_string(v,szv)
else
meta:set_string(v,data[v])
end
end
end
local function get_data_from_node_meta(pos)
local meta = minetest.get_meta(pos)
local data = {}
for _,v in ipairs(data_values) do
if data_serialize[v] then
data[v] = minetest.deserialize(meta:get_string(v))
else
data[v] = meta:get_string(v)
end
end
return data
end
local function get_mob_data(pos)
local key = minetest.pos_to_string(pos)
return mobs.npcs.mob_data[key]
return mobs.npcs.mob_data[key] or get_data_from_node_meta(pos)
end
local function set_mob_data(pos,data)
local key = minetest.pos_to_string(pos)
mobs.npcs.mob_data[key] = data
save_node_meta(pos,data)
-- local sz = minetest.serialize(mobs.npcs.mob_data)
-- mod_storage:set_string('mob_data',sz)
end
-- ------------------
--
-- helper function
--
-- ------------------
--
-- helper function
--
mobs.npcs.interactions = {
default = {
icon = icprefix..'chat_random.png',
@ -103,33 +175,6 @@ if textures then
mob_def.textures = textures
end
-- Set on_right_click interaction
mob_def.on_rightclick = function(self, clicker)
-- Look at the player
mobs.npcs.turn_to_player(self, clicker)
-- Grab specific data from spawner
local spawner_pos = self._spawner_pos
if spawner_pos then
-- Do custom action
local data = get_mob_data(spawner_pos) or {}
if data.action and data.action.id then
local id = data.action.id
local ref = mobs.npcs.interactions
if ref[id] and ref[id].on_rightclick then
return ref[id].on_rightclick(self, clicker, data)
end
end
end
-- Or at least say something
local mobname = mobs.npcs.get_mobname(self)
local clickername = clicker:get_player_name()
local msg = mobs.npcs.messages.get_random()
mobs.npcs.send_chat_message(clickername,msg,mobname)
end
@ -270,6 +315,7 @@ local function get_npc_formspec(pos,n)
forms[4] =
'textarea[0.3,'.. iy+1.6 ..';'..fw..',1.6;infotext;'..S('InfoText')..';'..dv.infotext..']'..
'button_exit['.. ww - 1 ..','.. wh - 1 ..';1,1;ok;ok]'..
''
@ -302,19 +348,27 @@ local function get_npc_formspec(pos,n)
return formspec
end
-- Function to initialize spawner : add entity and update metadata
-- Call after place node and on rightclick
-- Called after place node and on rightclick
mobs.npcs.update_cmob_properties = function(pos, player, mob)
minetest.log(vlog, '['..modname..'] '..'Updating mob properties from data at '..minetest.pos_to_string(pos))
local data = get_mob_data(pos) or {}
if not mob and data.obj then mob = data.obj end
if data.obj and not mob then mob = data.obj end
--- -- FIXEME: Seem to return true even when entity isn't there
--- -- Probably because it's there just before being removed by the do_custom funtion
local self
if mob then self = mob:get_luaentity() end
if not self then return false end
-- mob:set_pos({ x= pos.x, y= pos.y + 1, z= pos.z, })
minetest.log(vlog, '['..modname..'] '..'Mob entity exist')
@ -322,8 +376,12 @@ mobs.npcs.update_cmob_properties = function(pos, player, mob)
-- Set owner
-- TODO: check that node is the correct node
local player_name = player:get_player_name()
local player_name
if not player then
player_name = data.owner
else
player_name = player:get_player_name()
end
-- Set node meta
local meta = minetest.get_meta(pos)
local owner_name = data.owner or player_name
@ -347,6 +405,7 @@ mobs.npcs.update_cmob_properties = function(pos, player, mob)
wield_item = data.wield_item or self.wield_item,
visual_size = data.visual_size or self.visual_size,
})
-- self = mob:get_luaentity()
self._mobname = data.nametag or nil
self._owner = owner_name
self._spawner_pos = pos
@ -354,6 +413,8 @@ mobs.npcs.update_cmob_properties = function(pos, player, mob)
data.obj = mob
-- Register to global table
set_mob_data(pos,data)
print(dump(self))
end
@ -391,6 +452,8 @@ end
mobs.npcs.add_cmob = function(pos, player, _)
minetest.log(vlog, '['..modname..'] '..'Adding new mob at :'..minetest.pos_to_string(pos))
-- Place mob
local above = { x=pos.x, y=pos.y+1, z=pos.z }
local mob = minetest.add_entity(above, modname..':'..mob_name,minetest.write_json({_spawner_pos = pos}))
@ -419,6 +482,52 @@ mobs.npcs.add_cmob = function(pos, player, _)
end
local function cmob_data_update(pos,player,itemstack,mob)
-- Update mob properties
mobs.npcs.update_cmob_properties(pos, player, mob)
local data = mobs.npcs.update_cmob_data(pos)
local meta = minetest.get_meta(pos)
local owner = meta:get_string('owner')
-- Re-call the after_place_node function if needed
if not ( data and data.obj ) then
-- Get infos about the owner
local powner = minetest.get_player_by_name(owner)
if player and (not powner) then
powner = player
end
-- Call the function
--~ local spw_init = minetest.registered_nodes[node.name].after_place_node
--~ if spw_init and player then
--~ spw_init(pos, player, itemstack)
--~ end
for _,obj in ipairs(minetest.get_objects_inside_radius(pos, 1)) do
if obj:get_luaentity().name == mob_id then
obj:remove()
end
end
mobs.npcs.add_cmob(pos, powner, itemstack)
end
return data
end
local function cmob_show_formspec(pos,player,data)
local tab = 1
if data and data.last_tab then
tab = data.last_tab
end
minetest.after(0.2,
minetest.show_formspec,
player:get_player_name(),
modname..':'..mob_name..'_form0'..tab,
get_npc_formspec(pos,tab))
end
--
-- Register Npc
--
@ -426,24 +535,95 @@ end
mob_def.walk_chance = 0
mob_def.jump = false
mob_def.do_custom = function(self, dtime)
-- Get mob pos
--- Get mob pos
local pos = self.object:get_pos()
-- Make sure that it is still on the spawner (or at least 'in' it, or 'above' it)
for i=-1,1 do
local spos = { x= pos.x, y= pos.y + i, z= pos.z, }
if ( minetest.get_node(spos).name == spawner_id ) then
-- Stop function when spawner is found
return false
end
local spawner_pos = self._spawner_pos
--- Look for reasons to remove the mob:
--- Spawner pos is not set...
--- ... try to find it
-- if not spawner_pos then
-- for i=-1,1 do
-- local spos = { x= pos.x, y= pos.y + i, z= pos.z, }
-- if ( minetest.get_node(spos).name == spawner_id ) then
-- spawner_pos = spos
-- break
-- end
-- end
-- end
--- Spawner pos is not set...
if ( not spawner_pos )
--- Or mob if too far from spawner
or ( vector.distance(pos, spawner_pos) > 2 )
--- Or spawner has been removed
or ( minetest.get_node(spawner_pos).name ~= spawner_id )
then
self.object:remove()
return false
end
-- local meta = minetest.get_meta(spawner_pos)
local data = get_mob_data(spawner_pos)
if not data.obj then
self.object:remove()
mobs.npcs.add_cmob(spawner_pos, data.owner)
end
-- set_mob_data(spawner_pos,data)
-- mobs.npcs.update_cmob_properties(spawner_pos, _, self.obj)
-- Make sure that it is still on the spawner (or at least 'in' it, or 'above' it)
-- for i=-1,1 do
-- local spos = { x= pos.x, y= pos.y + i, z= pos.z, }
-- if ( minetest.get_node(spos).name == spawner_id ) then
-- --- Stop function when spawner is found
-- mobs.npcs.update_cmob_properties(pos, _, self.obj)
-- return false
-- end
-- end
-- Remove mob if it is away from spawner
self.object:remove()
-- self.object:remove()
return false
end
--~ mob_def.on_spawn = function(self,pos)
--~ local data =
--~ end
-- Set on_right_click interaction
mob_def.on_rightclick = function(self, clicker)
-- Look at the player
mobs.npcs.turn_to_player(self, clicker)
-- Find spawner
local spawner_pos = self._spawner_pos
-- Edit and update
local edit_key = 'sneak'
if spawner_pos and clicker:get_player_control()[edit_key] then
data = cmob_data_update(spawner_pos,clicker,nil,self.object)
cmob_show_formspec(spawner_pos,clicker,data)
return
end
if spawner_pos then
-- Do custom action
local data = get_mob_data(spawner_pos) or {}
if data.action and data.action.id then
local id = data.action.id
local ref = mobs.npcs.interactions
if ref[id] and ref[id].on_rightclick then
return ref[id].on_rightclick(self, clicker, data)
end
end
end
-- Or at least say something
local mobname = mobs.npcs.get_mobname(self)
local clickername = clicker:get_player_name()
local msg = mobs.npcs.messages.get_random()
mobs.npcs.send_chat_message(clickername,msg,mobname)
end
mobs:register_mob(mob_id, mob_def)
@ -482,27 +662,10 @@ minetest.register_node(spawner_id, {
end,
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
-- --
-- Check that the npc is still there
-- --
local data = mobs.npcs.update_cmob_data(pos)
local meta = minetest.get_meta(pos)
local owner = meta:get_string('owner')
-- Re-call the after_place_node function if needed
if not ( data and data.obj ) then
-- Get infos about the owner
local powner = minetest.get_player_by_name(owner)
if not powner then powner = clicker end
-- Call the function
--~ local spw_init = minetest.registered_nodes[node.name].after_place_node
--~ if spw_init and player then
--~ spw_init(pos, player, itemstack)
--~ end
mobs.npcs.add_cmob(pos, powner, itemstack)
end
local data = cmob_data_update(pos, clicker, itemstack)
-- --
-- Stop there if not allowed to interact
@ -512,16 +675,7 @@ minetest.register_node(spawner_id, {
end
-- Show customisation form
-- minetest.show_formspec(playername, formname, formspec)
local tab = 1
if data and data.last_tab then
tab = data.last_tab
end
minetest.after(0.2,
minetest.show_formspec,
clicker:get_player_name(),
modname..':'..mob_name..'_form0'..tab,
get_npc_formspec(pos,tab))
cmob_show_formspec(pos,clicker,data)
end,
-- on_receive_fields = function(pos, formname, fields, sender)
@ -530,7 +684,7 @@ minetest.register_node(spawner_id, {
-- Register egg
mobs:register_egg(mob_id, "npc", "character_lambda_inv.png", 0)
-- mobs:register_egg(mob_id, "npc", "character_lambda_inv.png", 0)
--~ mobs:doc_identifier_compat(mob_id, longdesc, usagehelp)
mobs:alias_mob("mobs:"..mob_name, mob_id) -- compatibility
@ -553,7 +707,7 @@ local on_receive_fields = function(player, formname, fields)
if not pos then return false end
local data = get_mob_data(pos) or {}
-- Update data without overrinding it
-- Update data without overiding it
if fields.texture then data.textures = { fields.texture } end
if fields.action_id then
-- if fields.action_id and (( not data.action ) or ( fields.action_id ~= data.action.id )) then
@ -584,7 +738,7 @@ local on_receive_fields = function(player, formname, fields)
set_mob_data(pos,data)
mobs.npcs.update_cmob_properties(pos,player, data.obj)
mobs.npcs.update_cmob_properties(pos,player,data.obj)
local tb = data.last_tab
if ( fields.tab or fields.action_id ) and not fields.quit then