Add spawn function and improve chatcommand feedback

This commit is contained in:
stujones11 2013-10-08 19:13:17 +01:00
parent f4428b9d1f
commit f4dfba13b4
3 changed files with 177 additions and 105 deletions

View File

@ -84,12 +84,20 @@ Permanently unload and delete named NPC. (requires server priv)
Loads the NPC at the specified postion. (requires ownership or server priv)
/npcf load npc_name 0, 5, 0
/npcf load npc_name 0, 1.5, 0
Use 'here' to load the NPC at the player's current position.
/npcf load npc_name here
### setpos npc_name pos | here
Set named NPC location. (x, y, z)
/npcf setpos npc_name 0, 1.5, 0
Use 'here' to load the NPC at the player's current position.
### setskin npc_name skin_filename | random
Set the skin texture of the named NPC. (requires server priv)
@ -100,12 +108,6 @@ If you have Zeg9's skins mod installed you can select a random texture from said
/npcf setskin npc_name random
### setpos npc_name pos
Set named NPC location. (x, y, z)
/npcf setpos npc_name 0, 5, 0
API Reference
-------------
@ -142,7 +144,7 @@ Additional properties included by the framework. (defaults)
armor_groups = {immortal=1},
inventory_image = "npcf_info_inv.png",
show_nametag = true,
nametag_color = "white",
nametag_color = "white", --textcolors mod adds red, blue, green, cyan, yellow and magenta
metadata = {},
var = {},
timer = 0,
@ -165,10 +167,7 @@ where it may be desireable update the statically saved position.
Callbacks
---------
Additional callbacks included by the framework.
### on_registration = function(self, pos, sender)
Only ever called once by the framework upon successful NPC registration.
Additional callbacks provided by the framework.
### on_construct = function(self)
This is called before the slightly delayed inbuilt on_activate callback.
@ -176,12 +175,19 @@ Please note that the self.npc_name, self.owner and self.origin properties
may not be available or nil at the time of registration.
### on_receive_fields = function(self, fields, sender)
Called when a button is pressed in the NPC's formspec.
Called when a button is pressed in the NPC's formspec. text fields, dropdown,
list and checkbox selections are automatically stored in the self.metadata table.
### on_registration = function(self, pos, sender)
Only ever called once upon successful NPC registration using a spawner.
Currently not used anywhere and may be removed from future version.
npcf
----
The global NPC framework namespace.
The global NPC framework namespace. Other global variables include.
### Global Constants
NPCF_ANIM_STAND = 1
NPCF_ANIM_SIT = 2
@ -210,21 +216,42 @@ The metadata table is persistent following a reload and automatically stores sub
The var table should be used for non-persistent data storage only. Note that self.timer is
automatically incremented by the framework but should be reset externally.
### npcf:clear_npc(npc_name)
### npcf:spawn(pos, name, def)
Spawns and registers a NPC entity at the specified position. Returns a minetest ObjectRef on success.
local pos = player:getpos()
local yaw = player:get_look_yaw()
local player_name = player:get_player_name()
local luaentity = npcf:spawn(pos, "npcf:guard_npc", {
owner = player_name,
npc_name = "Sam",
origin = {pos=pos, yaw=yaw}, --optional
})
Note that the on_registration callback will not be issued when spawning NPC's this way.
### npcf:clear(npc_name)
Clear (unload) named NPC.
### npcf:load_npc(npc_name, pos)
### npcf:load(npc_name, pos)
Loads the NPC at the specified postion. If pos is nil then the NPC is loaded at the last saved origin.
### npcf:save_npc(luaentity)
### npcf:save(luaentity)
Save current NPC state to file.
on_receive_fields = function(self, fields, sender)
if fields.save then
npcf:save(self)
end
end,
### npcf:set_animation(luaentity, state)
Sets the entity animation state.
Sets the NPC's animation state.
on_activate = function(self, staticdata, dtime_s)
npcf:set_animation(self, NPCF_ANIM_STAND)
@ -238,7 +265,7 @@ Returns a table of all registered NPCs. (loaded or unloaded)
### npcf:get_luaentity(npc_name)
Returns a minetest ObjectRef of the npc entity.
Returns a minetest ObjectRef of the NPC entity.
### npcf:get_face_direction(v1, v2)
@ -254,6 +281,6 @@ Returns a velocity vector for the given speed, y velocity and yaw.
Shows a formspec, similar to minetest.show_formspec() but with the npc_name included.
Submitted data can then be captured in the NPC's own 'on_receive_fields' callback.
Note that Form text fields, dropdown, list and checkbox selections are automatically
Note that form text fields, dropdown, list and checkbox selections are automatically
stored in the NPC's metadata table. Image/Button clicks, however, are not.

View File

@ -9,6 +9,12 @@ minetest.register_chatcommand("npcf", {
if cmd == "setpos" then
if admin or name == index[npc_name] then
local pos = minetest.string_to_pos(args)
if args == "here" then
local player = minetest.get_player_by_name(name)
if player then
pos = player:getpos()
end
end
if pos then
pos.y = pos.y + 1
local luaentity = npcf:get_luaentity(npc_name)
@ -16,9 +22,13 @@ minetest.register_chatcommand("npcf", {
if admin or luaentity.owner == name then
luaentity.object:setpos(pos)
luaentity.origin.pos = pos
npcf:save_npc(luaentity)
npcf:save(luaentity)
pos = minetest.pos_to_string(pos)
minetest.log("action", name.." moves NPC "..npc_name.." to "..pos)
end
end
else
minetest.chat_send_player(name, "Invalid position "..args)
end
end
elseif cmd == "load" then
@ -32,11 +42,13 @@ minetest.register_chatcommand("npcf", {
end
if pos then
pos.y = pos.y + 1
if npcf:load_npc(npc_name, pos) then
if npcf:load(npc_name, pos) then
minetest.after(1, function()
local luaentity = npcf:get_luaentity(npc_name)
if luaentity then
npcf:save_npc(luaentity)
npcf:save(luaentity)
pos = minetest.pos_to_string(pos)
minetest.log("action", name.." loads NPC "..npc_name.." at "..pos)
else
minetest.chat_send_player(name, "Unable to load "..npc_name)
end
@ -74,7 +86,8 @@ minetest.register_chatcommand("npcf", {
cmd, npc_name = string.match(param, "([^ ]+) (.+)")
if cmd and npc_name then
if cmd == "delete" and admin then
npcf:clear_npc(npc_name)
npcf:clear(npc_name)
minetest.log("action", name.." deletes NPC "..npc_name)
local input = io.open(NPCF_DATADIR.."/"..npc_name..".npc", "r")
if input then
io.close(input)
@ -90,11 +103,14 @@ minetest.register_chatcommand("npcf", {
end
elseif cmd == "clear" then
if admin or name == index[npc_name] then
npcf:clear_npc(npc_name)
npcf:clear(npc_name)
minetest.log("action", name.." clears NPC "..npc_name)
end
elseif cmd == "reload" then
if admin or name == index[npc_name] then
if not npcf:load_npc(npc_name, nil) then
if npcf:load(npc_name, nil) then
minetest.log("action", name.." reloads NPC "..npc_name)
else
minetest.chat_send_player(name, "Unable to reload "..npc_name)
end
end
@ -103,8 +119,10 @@ minetest.register_chatcommand("npcf", {
local saved = false
local luaentity = npcf:get_luaentity(npc_name)
if luaentity then
if npcf:save_npc(luaentity) then
if npcf:save(luaentity) then
saved = true
minetest.chat_send_player(name, npc_name.." has been saved")
minetest.log("action", name.." saves NPC "..npc_name)
end
end
if saved == false then
@ -147,14 +165,16 @@ minetest.register_chatcommand("npcf", {
end
minetest.chat_send_player(name, "NPC List: "..msg)
elseif cmd == "clearobjects" and admin then
minetest.log("action", name.." clears all NPC objects")
for _,ref in pairs(minetest.luaentities) do
if ref.object and ref.npcf_id then
ref.object:remove()
end
end
elseif cmd == "loadobjects" and admin then
minetest.log("action", name.." reloads all NPC objects")
for npc_name,_ in pairs(index) do
npcf:load_npc(npc_name, nil)
npcf:load(npc_name, nil)
end
end
end

View File

@ -104,6 +104,10 @@ local function get_valid_player_name(player)
end
end
local function get_valid_npc_name(npc_name)
return npc_name:len() <= 12 and npc_name:match("^[A-Za-z0-9%_%-]+$")
end
local function get_valid_entity(luaentity)
if luaentity then
if luaentity.name and luaentity.owner and luaentity.origin then
@ -326,10 +330,8 @@ function npcf:register_npc(name, def)
local owner = meta:get_string("owner")
local player_name = sender:get_player_name()
if player_name == owner then
if fields.name:len() <= 12 and fields.name:match("^[A-Za-z0-9%_%-]+$") then
local input = io.open(NPCF_DATADIR.."/"..fields.name..".npc", "r")
if input then
io.close(input)
if get_valid_npc_name(fields.name) then
if index[fields.name] then
minetest.chat_send_player(player_name, "Error: Name Already Taken!")
return
end
@ -339,70 +341,89 @@ function npcf:register_npc(name, def)
end
minetest.dig_node(pos)
local npc_pos = {x=pos.x, y=pos.y + 0.5, z=pos.z}
local npc = minetest.add_entity(npc_pos, name)
if npc then
local luaentity = npc:get_luaentity()
if luaentity then
luaentity.owner = player_name
luaentity.npc_name = fields.name
luaentity.origin = {pos=npc_pos, yaw=luaentity.object:getyaw()}
save_npc(luaentity)
index[fields.name] = owner
local output = io.open(NPCF_DATADIR.."/index.txt", 'w')
if output then
output:write(minetest.serialize(index))
io.close(output)
else
minetest.log("error", "Failed to add "..fields.name.." to npc index")
end
if luaentity.show_nametag then
add_nametag(luaentity)
end
if type(def.on_registration) == "function" then
def.on_registration(luaentity, pos, sender)
end
end
local luaentity = npcf:spawn(npc_pos, name, {
owner = player_name,
npc_name = fields.name,
})
if luaentity and type(def.on_registration) == "function" then
def.on_registration(luaentity, pos, sender)
end
end
end,
})
end
function npcf:clear_npc(npc_name)
for _,ref in pairs(minetest.luaentities) do
if ref.object and ref.npc_name == npc_name then
ref.object:remove()
function npcf:spawn(pos, name, def)
if pos and name and def.npc_name and def.owner then
if get_valid_npc_name(def.npc_name) and index[def.npc_name] == nil then
local entity = minetest.add_entity(pos, name)
if entity then
local luaentity = entity:get_luaentity()
if luaentity then
luaentity.owner = def.owner
luaentity.npc_name = def.npc_name
luaentity.origin = def.origin or {pos=pos, yaw=0}
index[def.npc_name] = def.owner
if npcf:save(luaentity) then
local output = io.open(NPCF_DATADIR.."/index.txt", 'w')
if output then
output:write(minetest.serialize(index))
io.close(output)
if NPCF_SHOW_NAMETAGS == true and luaentity.show_nametag == true then
add_nametag(luaentity)
end
return luaentity
else
minetest.log("error", "Failed to add "..def.npc_name.." to NPC index")
end
else
minetest.log("error", "Failed to save NPC "..def.npc_name)
end
end
end
end
end
end
function npcf:load_npc(npc_name, pos)
npcf:clear_npc(npc_name)
local input = io.open(NPCF_DATADIR.."/"..npc_name..".npc", "r")
if input then
local data = minetest.deserialize(input:read('*all'))
io.close(input)
if data then
if pos and data.origin then
data.origin.pos = pos
function npcf:clear(npc_name)
if get_valid_npc_name(npc_name) then
for _,ref in pairs(minetest.luaentities) do
if ref.object and ref.npc_name == npc_name then
ref.object:remove()
end
if data.origin.pos then
local npc = minetest.add_entity(data.origin.pos, data.name)
if npc then
local luaentity = npc:get_luaentity()
if luaentity then
luaentity.owner = data.owner
luaentity.npc_name = npc_name
luaentity.origin = data.origin
luaentity.animation = data.animation
luaentity.metadata = data.metadata
luaentity.object:setyaw(data.origin.yaw)
luaentity.properties = data.properties
luaentity.object:set_properties(data.properties)
if NPCF_SHOW_NAMETAGS == true and luaentity.show_nametag == true then
add_nametag(luaentity)
end
end
end
function npcf:load(npc_name, pos)
if get_valid_npc_name(npc_name) then
npcf:clear(npc_name)
local input = io.open(NPCF_DATADIR.."/"..npc_name..".npc", "r")
if input then
local data = minetest.deserialize(input:read('*all'))
io.close(input)
if data then
if pos and data.origin then
data.origin.pos = pos
end
if data.origin.pos then
local npc = minetest.add_entity(data.origin.pos, data.name)
if npc then
local luaentity = npc:get_luaentity()
if luaentity then
luaentity.owner = data.owner
luaentity.npc_name = npc_name
luaentity.origin = data.origin
luaentity.animation = data.animation
luaentity.metadata = data.metadata
luaentity.object:setyaw(data.origin.yaw)
luaentity.properties = data.properties
luaentity.object:set_properties(data.properties)
if NPCF_SHOW_NAMETAGS == true and luaentity.show_nametag == true then
add_nametag(luaentity)
end
return 1
end
return 1
end
end
end
@ -411,27 +432,29 @@ function npcf:load_npc(npc_name, pos)
minetest.log("error", "Failed to load "..npc_name)
end
function npcf:save_npc(luaentity)
local npc = {
name = luaentity.name,
owner = luaentity.owner,
origin = luaentity.origin,
animation = luaentity.animation,
metadata = luaentity.metadata,
properties = luaentity.properties,
}
local npc_name = luaentity.npc_name
local output = io.open(NPCF_DATADIR.."/"..npc_name..".npc", 'w')
if output then
output:write(minetest.serialize(npc))
io.close(output)
return 1
function npcf:save(luaentity)
if get_valid_entity(luaentity) then
local npc = {
name = luaentity.name,
owner = luaentity.owner,
origin = luaentity.origin,
animation = luaentity.animation,
metadata = luaentity.metadata,
properties = luaentity.properties,
}
local npc_name = luaentity.npc_name
local output = io.open(NPCF_DATADIR.."/"..npc_name..".npc", 'w')
if output then
output:write(minetest.serialize(npc))
io.close(output)
return 1
end
end
minetest.log("error", "Failed to save "..npc_name)
end
function npcf:set_animation(luaentity, state)
if luaentity and state then
if get_valid_entity(luaentity) and state then
if state ~= luaentity.state then
local speed = luaentity.animation_speed
local anim = luaentity.animation
@ -460,7 +483,7 @@ function npcf:get_index()
end
function npcf:get_luaentity(npc_name)
if npc_name then
if get_valid_npc_name(npc_name) then
for _,ref in pairs(minetest.luaentities) do
if ref.object then
if ref.npcf_id == "npc" and ref.npc_name == npc_name then
@ -472,10 +495,12 @@ function npcf:get_luaentity(npc_name)
end
function npcf:get_face_direction(v1, v2)
if v1.x and v2.x and v1.z and v2.z then
dx = v1.x - v2.x
dz = v2.z - v1.z
return math.atan2(dx, dz)
if v1 and v2 then
if v1.x and v2.x and v1.z and v2.z then
dx = v1.x - v2.x
dz = v2.z - v1.z
return math.atan2(dx, dz)
end
end
end