summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillaume Melquiond <guillaume.melquiond@gmail.com>2007-08-16 14:53:25 +0000
committerGuillaume Melquiond <guillaume.melquiond@gmail.com>2007-08-16 14:53:25 +0000
commit5560e259363a41e39f18dcbd8f3109e253d1fd0d (patch)
treeb6ca579d6a5dec5474aafdf2e437bd9ea688ad5a
parentd5a67c771f3da87ee22cbb02ebe2985e6df1f5b7 (diff)
downloadmanaserv-5560e259363a41e39f18dcbd8f3109e253d1fd0d.tar.gz
manaserv-5560e259363a41e39f18dcbd8f3109e253d1fd0d.tar.bz2
manaserv-5560e259363a41e39f18dcbd8f3109e253d1fd0d.tar.xz
manaserv-5560e259363a41e39f18dcbd8f3109e253d1fd0d.zip
Improved NPC state machine, so that the engine avoids waiting, once the last message has been sent.
-rw-r--r--ChangeLog5
-rw-r--r--data/scripts/libtmw.lua85
2 files changed, 73 insertions, 17 deletions
diff --git a/ChangeLog b/ChangeLog
index e499a94b..49b701db 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2007-08-16 Guillaume Melquiond <guillaume.melquiond@gmail.com>
+
+ * data/scripts/libtmw.lua: Improved NPC state machine, so that the
+ engine avoids waiting, once the last message has been sent.
+
2007-08-15 Guillaume Melquiond <guillaume.melquiond@gmail.com>
* src/scripting/script.cpp, src/scripting/script.hpp: Added helper
diff --git a/data/scripts/libtmw.lua b/data/scripts/libtmw.lua
index 441ec26f..158befe3 100644
--- a/data/scripts/libtmw.lua
+++ b/data/scripts/libtmw.lua
@@ -7,29 +7,71 @@ local states = {}
local init_fun = {}
local timer
+-- Creates an NPC and associates the given handler.
+-- Note: Cannot be called until map initialization has started.
function create_npc(id, x, y, handler)
local npc = tmw.obj_create_npc(id, x, y)
npcs[npc] = handler
end
+-- Sends an npc message to a player.
+-- Note: Does not wait for the player to acknowledge the message.
function do_message(npc, ch, msg)
+ -- Wait for the arrival of a pending acknowledgment, if any.
+ coroutine.yield(0)
tmw.msg_npc_message(npc, ch, msg)
+ -- An acknowledgment is pending, but do not wait for its arrival.
coroutine.yield(1)
end
+-- Sends an NPC question to a player and waits for its answer.
function do_choice(npc, ch, ...)
+ -- Wait for the arrival of a pending acknowledgment, if any.
+ coroutine.yield(0)
tmw.msg_npc_choice(npc, ch, ...)
+ -- Wait for player choice.
return coroutine.yield(2)
end
--- Called whenever a player starts talking to an NPC.
--- Creates a coroutine based on the register NPC handler.
+-- Processes as much of an NPC handler as possible.
+local function process_npc(co, ...)
+ -- First, resume with the arguments the coroutine was waiting for.
+ local b, v = coroutine.resume(co, ...)
+ if not b or not v then
+ return
+ end
+ if v == 2 then
+ return 2
+ end
+ -- Then, keep resuming until the coroutine expects the result of a choice
+ -- or an acknowledgment to a message.
+ local pending = (v == 1)
+ while true do
+ b, v = coroutine.resume(co)
+ if not b or not v then
+ return
+ end
+ if v == 2 then
+ return 2
+ end
+ if pending then
+ return 1
+ end
+ if v == 1 then
+ pending = true
+ end
+ end
+end
+
+-- Called by the game whenever a player starts talking to an NPC.
+-- Creates a coroutine based on the registered NPC handler.
function npc_start(npc, ch)
+ states[ch] = nil
local h = npcs[npc]
if not h then return end
local co = coroutine.create(h)
- local b, v = coroutine.resume(co, npc, ch)
- if b and v then
+ local v = process_npc(co, npc, ch)
+ if v then
states[ch] = {npc, co, v, 5}
if not timer then
timer = 600
@@ -37,43 +79,46 @@ function npc_start(npc, ch)
end
end
--- Called whenever a player keeps talking to an NPC.
--- Checks that the NPC expects it, and resumes the respective coroutine.
+-- Called by the game whenever a player keeps talking to an NPC.
+-- Checks that the NPC expects it, and processes the respective coroutine.
function npc_next(npc, ch)
local w = states[ch]
if w and w[1] == npc and w[3] == 1 then
- local b, v = coroutine.resume(w[2])
- if b and v then
+ local v = process_npc(w[2])
+ if v then
w[3] = v
w[4] = 5
- else
- states[ch] = nil
+ return
end
end
+ states[ch] = nil
end
--- Called whenever a player selects a particular reply.
--- Checks that the NPC expects it, and resumes the respective coroutine.
+-- Called by the game whenever a player selects a particular reply.
+-- Checks that the NPC expects it, and processes the respective coroutine.
function npc_choose(npc, ch, u)
local w = states[ch]
if w and w[1] == npc and w[3] == 2 then
- local b, v = coroutine.resume(w[2], u)
- if b and v then
+ local v = process_npc(w[2], u)
+ if v then
w[3] = v
w[4] = 5
- else
- states[ch] = nil
+ return
end
end
+ states[ch] = nil
end
+-- Called by the game every tick for each NPC.
function npc_update(npc)
end
+-- Called by the game every tick.
+-- Cleans obsolete connections.
function update()
-- Run every minute only, in order not to overload the server.
if not timer then return end
- timer = timer - 10
+ timer = timer - 1
if timer ~= 0 then return end
-- Free connections that have been inactive for 3-4 minutes.
for k, w in pairs(states) do
@@ -92,10 +137,14 @@ function update()
end
end
+-- Registers a function so that is is executed during map initialization.
function atinit(f)
init_fun[#init_fun + 1] = f
end
+-- Called by the game for creating NPCs embedded into maps.
+-- Delays the creation until map initialization is performed.
+-- Note: Assumes that the "npc_handler" global field contains the NPC handler.
function create_npc_delayed(id, x, y)
-- Bind the name to a local variable first, as it will be reused.
local h = npc_handler
@@ -103,6 +152,8 @@ function create_npc_delayed(id, x, y)
npc_handler = nil
end
+-- Called during map initialization.
+-- Executes all the functions registered by atinit.
function initialize()
for i,f in ipairs(init_fun) do
f()