diff options
author | Guillaume Melquiond <guillaume.melquiond@gmail.com> | 2007-08-28 17:03:44 +0000 |
---|---|---|
committer | Guillaume Melquiond <guillaume.melquiond@gmail.com> | 2007-08-28 17:03:44 +0000 |
commit | e678c7cc1574d5739f83da2b0d493b44e3a7a42e (patch) | |
tree | a184372b02622d82bc85a9827a835de186b0e2ed | |
parent | c4a2cd54d72f776d6e37eae7a8c67caa81269f4f (diff) | |
download | manaserv-e678c7cc1574d5739f83da2b0d493b44e3a7a42e.tar.gz manaserv-e678c7cc1574d5739f83da2b0d493b44e3a7a42e.tar.bz2 manaserv-e678c7cc1574d5739f83da2b0d493b44e3a7a42e.tar.xz manaserv-e678c7cc1574d5739f83da2b0d493b44e3a7a42e.zip |
Implemented quest variables.
-rw-r--r-- | ChangeLog | 17 | ||||
-rw-r--r-- | data/scripts/libtmw.lua | 110 | ||||
-rw-r--r-- | data/test.lua | 5 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/account-server/dalstorage.cpp | 56 | ||||
-rw-r--r-- | src/account-server/dalstorage.hpp | 11 | ||||
-rw-r--r-- | src/account-server/dalstoragesql.hpp | 24 | ||||
-rw-r--r-- | src/account-server/serverhandler.cpp | 21 | ||||
-rw-r--r-- | src/account-server/storage.hpp | 11 | ||||
-rw-r--r-- | src/defines.h | 3 | ||||
-rw-r--r-- | src/game-server/accountconnection.cpp | 31 | ||||
-rw-r--r-- | src/game-server/accountconnection.hpp | 13 | ||||
-rw-r--r-- | src/game-server/character.hpp | 7 | ||||
-rw-r--r-- | src/game-server/quest.cpp | 138 | ||||
-rw-r--r-- | src/game-server/quest.hpp | 63 | ||||
-rw-r--r-- | src/scripting/lua.cpp | 71 |
16 files changed, 537 insertions, 46 deletions
@@ -1,3 +1,20 @@ +2007-08-28 Guillaume Melquiond <guillaume.melquiond@gmail.com> + + * src/account-server/storage.hpp, src/account-server/dalstoragesql.hpp, + src/account-server/dalstorage.cpp, src/account-server/dalstorage.hpp: + Implemented storing and recovering quest variables from database. + * src/account-server/serverhandler.cpp, src/defines.h, + src/game-server/accountconnection.cpp, + src/game-server/accountconnection.hpp: Added protocol for sending quest + variables between servers. + * src/game-server/quest.cpp, src/game-server/quest.hpp, + src/game-server/character.hpp, src/Makefile.am: Added async handling of + quest variables. + * src/scripting/lua.cpp, data/scripts/libtmw.lua: Added helper + functions for querying quest variables from Lua scripts. Reworked state + machine of the NPC support library. + * data/test.lua: Modified for testing quest variables. + 2007-08-27 Eugenio Favalli <elvenprogrammer@gmail.com> * accountserver.cbp, gameserver.cbp, src/game-server/mapreader.cpp, diff --git a/data/scripts/libtmw.lua b/data/scripts/libtmw.lua index bb724799..3189da6a 100644 --- a/data/scripts/libtmw.lua +++ b/data/scripts/libtmw.lua @@ -8,10 +8,12 @@ local npcs = {} -- Table that associates to each Character pointer its state with respect to -- NPCs (only one at a time). A state is an array with four fields: --- . 1: pointer of the NPC the player is currently talking to. +-- . 1: pointer to the NPC the player is currently talking to. -- . 2: coroutine running the NPC handler. --- . 3: next query the NPC expects from a player (1 = next, 2 = choice). +-- . 3: next event the NPC expects from the server. +-- (1 = npc_next, 2 = npc_choose, 3 = quest_reply, 4 = 1+3) -- . 4: countdown (in minutes) before the state is deleted. +-- . 5: name of the expected quest variable. (optional) local states = {} -- Array containing the function registered by atinit. @@ -46,34 +48,68 @@ function do_choice(npc, ch, ...) return coroutine.yield(2) end +-- Gets the value of a quest variable. +-- Calling this function while an acknowledment is pending is desirable, so +-- that lag cannot be perceived by the player. +function get_quest_var(npc, ch, name) + -- Query the server and return immediatly if a value is available. + local value = tmw.chr_get_quest(ch, name) + if value then return value end + -- Wait for database reply. + return coroutine.yield(3, name) +end + -- 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) +local function process_npc(w, ...) + local co = w[2] + local pending = (w[3] == 4) + local first = true while true do - b, v = coroutine.resume(co) + local b, v, u + if first then + -- First time, resume with the arguments the coroutine was waiting for. + b, v, u = coroutine.resume(co, ...) + first = false + else + -- Otherwise, simply resume. + b, v, u = coroutine.resume(co) + end if not b or not v then + -- Either there was an error, or the handler just finished its work. return end if v == 2 then - return 2 + -- The coroutine needs a user choice from the server, so wait for it. + w[3] = 2 + break + end + if v == 3 then + -- The coroutine needs the value of a quest variable from the server. + w[5] = u + if pending then + -- The coroutine has also sent a message to the user, so do not + -- forget about it, as it would flood the user with new messages. + w[3] = 4 + else + w[3] = 3 + end + break end if pending then - return 1 + -- The coroutine is about to interact with the user. But the previous + -- action has not been acknowledged by the user yet, so wait for it. + w[3] = 1 + break end if v == 1 then + -- A message has just been sent. But the coroutine can keep going in case + -- there is still some work to do while waiting for user acknowledgment. pending = true end end + -- Restore the countdown, as there was some activity. + w[4] = 5 + return true end -- Called by the game whenever a player starts talking to an NPC. @@ -82,10 +118,9 @@ function npc_start(npc, ch) states[ch] = nil local h = npcs[npc] if not h then return end - local co = coroutine.create(h) - local v = process_npc(co, npc, ch) - if v then - states[ch] = {npc, co, v, 5} + local w = { npc, coroutine.create(h) } + if process_npc(w, npc, ch) then + states[ch] = w if not timer then timer = 600 end @@ -96,27 +131,32 @@ end -- 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 v = process_npc(w[2]) - if v then - w[3] = v - w[4] = 5 - return - end + if not (w and w[1] == npc and w[3] == 1 and process_npc(w)) then + states[ch] = nil end - states[ch] = nil end -- 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 v = process_npc(w[2], u) - if v then - w[3] = v - w[4] = 5 - return + if not (w and w[1] == npc and w[3] == 2 and process_npc(w, u)) then + states[ch] = nil + end +end + +-- Called by the game whenever the value of a quest variable is known. +-- Checks that the NPC expects it, and processes the respective coroutine. +-- Note: the check for NPC correctness is missing, but it should never matter. +function quest_reply(ch, name, value) + local w = states[ch] + if w then + local w3 = w[3] + if (w3 == 3 or w3 == 4) and w[5] == name then + w[5] = nil + if process_npc(w, value) then + return + end end end states[ch] = nil diff --git a/data/test.lua b/data/test.lua index 6254225a..47b06519 100644 --- a/data/test.lua +++ b/data/test.lua @@ -36,6 +36,11 @@ function my_npc1(npc, ch) elseif v == 5 then if tmw.chr_money_change(ch, -100) then do_message(npc, ch, string.format("Thank you for you patronage! You are left with %d gil.", tmw.chr_money(ch))) + local g = tonumber(get_quest_var(npc, ch, "001_donation")) + if not g then g = 0 end + g = g + 100 + tmw.chr_set_quest(ch, "001_donation", g) + do_message(npc, ch, string.format("As of today, you have donated %d gil.", g)) else do_message(npc, ch, "I would feel bad taking money from someone that poor.") end diff --git a/src/Makefile.am b/src/Makefile.am index 6bc88be8..a2d22615 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -114,6 +114,8 @@ tmwserv_game_SOURCES = \ game-server/npc.hpp \ game-server/npc.cpp \ game-server/object.hpp \ + game-server/quest.hpp \ + game-server/quest.cpp \ game-server/spawnarea.hpp \ game-server/spawnarea.cpp \ game-server/state.hpp \ diff --git a/src/account-server/dalstorage.cpp b/src/account-server/dalstorage.cpp index e9887fad..612d0565 100644 --- a/src/account-server/dalstorage.cpp +++ b/src/account-server/dalstorage.cpp @@ -184,6 +184,7 @@ void DALStorage::open() createTable(CHANNELS_TBL_NAME, SQL_CHANNELS_TABLE); createTable(GUILDS_TBL_NAME, SQL_GUILDS_TABLE); createTable(GUILD_MEMBERS_TBL_NAME, SQL_GUILD_MEMBERS_TABLE); + createTable(QUESTS_TBL_NAME, SQL_QUESTS_TABLE); } catch (const DbConnectionFailure& e) { LOG_ERROR("(DALStorage::open #1) Unable to connect to the database: " @@ -1231,3 +1232,58 @@ std::list<Guild*> DALStorage::getGuildList() return guilds; } + +std::string DALStorage::getQuestVar(int id, std::string const &name) +{ + // connect to the database (if not connected yet). + open(); + + using namespace dal; + + try + { + std::ostringstream query; + query << "select value from " << QUESTS_TBL_NAME + << " where owner_id = '" << id << "' and name = '" + << name << "';"; + RecordSet const &info = mDb->execSql(query.str()); + + if (!info.isEmpty()) return info(0, 0); + } + catch (DbSqlQueryExecFailure const &e) + { + LOG_ERROR("(DALStorage::getQuestVar) SQL query failure: " << e.what()); + } + + return std::string(); +} + +void DALStorage::setQuestVar(int id, std::string const &name, + std::string const &value) +{ + // connect to the database (if not connected yet). + open(); + + using namespace dal; + + try + { + std::ostringstream query1; + query1 << "delete from " << QUESTS_TBL_NAME + << " where owner_id = '" << id << "' and name = '" + << name << "';"; + mDb->execSql(query1.str()); + + if (value.empty()) return; + + std::ostringstream query2; + query2 << "insert into " << QUESTS_TBL_NAME + << " (owner_id, name, value) values ('" + << id << "', '" << name << "', '" << value << "');"; + mDb->execSql(query2.str()); + } + catch (DbSqlQueryExecFailure const &e) + { + LOG_ERROR("(DALStorage::setQuestVar) SQL query failure: " << e.what()); + } +} diff --git a/src/account-server/dalstorage.hpp b/src/account-server/dalstorage.hpp index 00a5b69b..c25b3fc2 100644 --- a/src/account-server/dalstorage.hpp +++ b/src/account-server/dalstorage.hpp @@ -203,6 +203,17 @@ class DALStorage: public Storage void flushAll(); void flush(AccountPtr const &); + /** + * Gets the value of a quest variable. + */ + std::string getQuestVar(int id, std::string const &); + + /** + * Sets the value of a quest variable. + */ + void setQuestVar(int id, std::string const &, std::string const &); + + private: /** * Constructor. diff --git a/src/account-server/dalstoragesql.hpp b/src/account-server/dalstoragesql.hpp index 81ceaf46..c4d8ad7d 100644 --- a/src/account-server/dalstoragesql.hpp +++ b/src/account-server/dalstoragesql.hpp @@ -64,13 +64,6 @@ /** * TABLE: tmw_accounts. - * - * Notes: - * - the user levels are: - * 0: normal user - * 1: moderator (has medium level rights) - * 2: administrator (i am god :)) - * - the 'banned' field contains the UNIX time of unban (default = 0) */ static char const *ACCOUNTS_TBL_NAME = "tmw_accounts"; static char const *SQL_ACCOUNTS_TABLE = @@ -294,5 +287,22 @@ static char const *SQL_GUILD_MEMBERS_TABLE = #endif ");"; +/** + * TABLE: tmw_quests. + */ +static char const *QUESTS_TBL_NAME = "tmw_quests"; +static char const *SQL_QUESTS_TABLE = + "CREATE TABLE tmw_quests (" +#if defined (MYSQL_SUPPORT) +#error "Missing definition. Please fill the blanks." +#elif defined (SQLITE_SUPPORT) + "owner_id INTEGER NOT NULL," + "name TEXT NOT NULL," + "value TEXT NOT NULL," + "FOREIGN KEY (owner_id) REFERENCES tmw_characters(id)" +#elif defined (POSTGRESQL_SUPPORT) +#error "Missing definition. Please fill the blanks." +#endif + ");"; #endif // _TMWSERV_DALSTORAGE_SQL_H_ diff --git a/src/account-server/serverhandler.cpp b/src/account-server/serverhandler.cpp index 9e53356f..6c428e4a 100644 --- a/src/account-server/serverhandler.cpp +++ b/src/account-server/serverhandler.cpp @@ -189,6 +189,27 @@ void ServerHandler::processMessage(NetComputer *comp, MessageIn &msg) } break; + case GAMSG_GET_QUEST: + { + int id = msg.readLong(); + std::string name = msg.readString(); + Storage &store = Storage::instance("tmw"); + std::string value = store.getQuestVar(id, name); + result.writeShort(AGMSG_GET_QUEST_RESPONSE); + result.writeLong(id); + result.writeString(name); + result.writeString(value); + } break; + + case GAMSG_SET_QUEST: + { + int id = msg.readLong(); + std::string name = msg.readString(); + std::string value = msg.readString(); + Storage &store = Storage::instance("tmw"); + store.setQuestVar(id, name, value); + } break; + #if 0 case GAMSG_GUILD_CREATE: { diff --git a/src/account-server/storage.hpp b/src/account-server/storage.hpp index 55a82abf..d436fa1f 100644 --- a/src/account-server/storage.hpp +++ b/src/account-server/storage.hpp @@ -319,6 +319,17 @@ class Storage */ virtual void flush(AccountPtr const &account) = 0; + /** + * Gets the value of a quest variable. + */ + virtual std::string getQuestVar(int id, std::string const &) = 0; + + /** + * Sets the value of a quest variable. + */ + virtual void setQuestVar(int id, std::string const &, + std::string const &) = 0; + protected: /** diff --git a/src/defines.h b/src/defines.h index 03eb60d0..fff197c5 100644 --- a/src/defines.h +++ b/src/defines.h @@ -235,6 +235,9 @@ enum { GAMSG_REDIRECT = 0x0530, // L id AGMSG_REDIRECT_RESPONSE = 0x0531, // L id, B*32 token, S game address, W game port GAMSG_PLAYER_RECONNECT = 0x0532, // L id, B*32 token + GAMSG_SET_QUEST = 0x0540, // L id, S name, S value + GAMSG_GET_QUEST = 0x0541, // L id, S name + AGMSG_GET_QUEST_RESPONSE = 0x0542, // L id S name, S value #if 0 GAMSG_GUILD_CREATE = 0x0550, // S name diff --git a/src/game-server/accountconnection.cpp b/src/game-server/accountconnection.cpp index 8a03bb24..b351890b 100644 --- a/src/game-server/accountconnection.cpp +++ b/src/game-server/accountconnection.cpp @@ -29,6 +29,7 @@ #include "game-server/map.hpp" #include "game-server/mapcomposite.hpp" #include "game-server/mapmanager.hpp" +#include "game-server/quest.hpp" #include "game-server/state.hpp" #include "net/messagein.hpp" #include "net/messageout.hpp" @@ -94,6 +95,14 @@ void AccountConnection::processMessage(MessageIn &msg) gameHandler->completeServerChange(id, token, address, port); } break; + case AGMSG_GET_QUEST_RESPONSE: + { + int id = msg.readLong(); + std::string name = msg.readString(); + std::string value = msg.readString(); + recoveredQuestVar(id, name, value); + } break; + // The client should directly talk with the chat server and not go through the game server. #if 0 case AGMSG_GUILD_CREATE_RESPONSE: @@ -216,15 +225,33 @@ void AccountConnection::processMessage(MessageIn &msg) } } -void AccountConnection::playerReconnectAccount(int id, const std::string magic_token) +void AccountConnection::playerReconnectAccount(int id, std::string const &magic_token) { - LOG_INFO("Send GAMSG_PLAYER_RECONNECT."); + LOG_DEBUG("Send GAMSG_PLAYER_RECONNECT."); MessageOut msg(GAMSG_PLAYER_RECONNECT); msg.writeLong(id); msg.writeString(magic_token, MAGIC_TOKEN_LENGTH); send(msg); } +void AccountConnection::requestQuestVar(Character *ch, std::string const &name) +{ + MessageOut msg(GAMSG_GET_QUEST); + msg.writeLong(ch->getDatabaseID()); + msg.writeString(name); + send(msg); +} + +void AccountConnection::updateQuestVar(Character *ch, std::string const &name, + std::string const &value) +{ + MessageOut msg(GAMSG_SET_QUEST); + msg.writeLong(ch->getDatabaseID()); + msg.writeString(name); + msg.writeString(value); + send(msg); +} + #if 0 void AccountConnection::playerCreateGuild(int id, const std::string &guildName) { diff --git a/src/game-server/accountconnection.hpp b/src/game-server/accountconnection.hpp index ba9abe06..9899db14 100644 --- a/src/game-server/accountconnection.hpp +++ b/src/game-server/accountconnection.hpp @@ -48,7 +48,18 @@ class AccountConnection : public Connection /** * Prepares the account server for a reconnecting player */ - void playerReconnectAccount(int id, const std::string magic_token); + void playerReconnectAccount(int id, std::string const &magic_token); + + /** + * Requests the value of a quest variable from the database. + */ + void requestQuestVar(Character *, std::string const &); + + /** + * Pushes a new quest value to the database. + */ + void updateQuestVar(Character *, std::string const &name, + std::string const &value); #if 0 /** diff --git a/src/game-server/character.hpp b/src/game-server/character.hpp index 2cf4500c..eff99286 100644 --- a/src/game-server/character.hpp +++ b/src/game-server/character.hpp @@ -23,6 +23,7 @@ #ifndef _TMWSERV_CHARACTER_HPP_ #define _TMWSERV_CHARACTER_HPP_ +#include <map> #include <string> #include <vector> @@ -207,6 +208,12 @@ class Character : public Being */ void modifiedAttribute(int); + /** + * Associative array containing all the quest variables known by the + * server. + */ + std::map< std::string, std::string > questCache; + private: Character(Character const &); Character &operator=(Character const &); diff --git a/src/game-server/quest.cpp b/src/game-server/quest.cpp new file mode 100644 index 00000000..f6675127 --- /dev/null +++ b/src/game-server/quest.cpp @@ -0,0 +1,138 @@ +/* + * The Mana World Server + * Copyright 2007 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include <algorithm> +#include <list> +#include <map> +#include <string> + +#include "game-server/quest.hpp" + +#include "defines.h" +#include "game-server/accountconnection.hpp" +#include "game-server/character.hpp" +#include "game-server/deathlistener.hpp" +#include "utils/logger.h" + +typedef std::list< QuestCallback > QuestCallbacks; +typedef std::map< std::string, QuestCallbacks > PendingVariables; + +struct PendingQuest +{ + Character *character; + PendingVariables variables; +}; + +typedef std::map< int, PendingQuest > PendingQuests; + +static PendingQuests pendingQuests; + +bool getQuestVar(Character *ch, std::string const &name, std::string &value) +{ + std::map< std::string, std::string >::iterator + i = ch->questCache.find(name); + if (i == ch->questCache.end()) return false; + value = i->second; + return true; +} + +void setQuestVar(Character *ch, std::string const &name, + std::string const &value) +{ + std::map< std::string, std::string >::iterator + i = ch->questCache.lower_bound(name); + if (i == ch->questCache.end() || i->first != name) + { + ch->questCache.insert(i, std::make_pair(name, value)); + } + else if (i->second == value) + { + return; + } + else + { + i->second = value; + } + accountHandler->updateQuestVar(ch, name, value); +} + +/** + * Listener for deleting related quests when a character disappears. + */ +struct QuestDeathListener: DeathListener +{ + void deleted(Being *b) + { + int id = static_cast< Character * >(b)->getDatabaseID(); + pendingQuests.erase(id); + } +}; + +static QuestDeathListener questDeathListener; + +void recoverQuestVar(Character *ch, std::string const &name, + QuestCallback const &f) +{ + int id = ch->getDatabaseID(); + PendingQuests::iterator i = pendingQuests.lower_bound(id); + if (i == pendingQuests.end() || i->first != id) + { + i = pendingQuests.insert(i, std::make_pair(id, PendingQuest())); + i->second.character = ch; + /* Register a listener, because we cannot afford to get invalid + pointers, when we finally recover the variable. */ + ch->addDeathListener(&questDeathListener); + } + i->second.variables[name].push_back(f); + accountHandler->requestQuestVar(ch, name); +} + +void recoveredQuestVar(int id, std::string const &name, + std::string const &value) +{ + PendingQuests::iterator i = pendingQuests.find(id); + if (i == pendingQuests.end()) return; + PendingVariables &variables = i->second.variables; + PendingVariables::iterator j = variables.find(name); + if (j == variables.end()) + { + LOG_ERROR("Account server recovered an unexpected quest variable."); + return; + } + + Character *ch = i->second.character; + ch->questCache[name] = value; + + // Call the registered callbacks. + for (QuestCallbacks::const_iterator k = j->second.begin(), + k_end = j->second.end(); k != k_end; ++k) + { + k->handler(ch, name, value, k->data); + } + + variables.erase(j); + if (variables.empty()) + { + pendingQuests.erase(i); + } +} diff --git a/src/game-server/quest.hpp b/src/game-server/quest.hpp new file mode 100644 index 00000000..720d79ae --- /dev/null +++ b/src/game-server/quest.hpp @@ -0,0 +1,63 @@ +/* + * The Mana World Server + * Copyright 2007 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMWSERV_GAMESERVER_QUEST_ +#define _TMWSERV_GAMESERVER_QUEST_ + +#include <string> + +class Character; + +struct QuestCallback +{ + void (*handler)(Character *, std::string const &name, + std::string const &value, void *data); + void *data; +}; + +/** + * Gets the value associated to a quest variable. + * @return false if no value was in cache. + */ +bool getQuestVar(Character *, std::string const &name, std::string &value); + +/** + * Sets the value associated to a quest variable. + */ +void setQuestVar(Character *, std::string const &name, + std::string const &value); + +/** + * Starts the recovery of a variable and returns immediatly. The callback will + * be called once the value has been recovered. + */ +void recoverQuestVar(Character *, std::string const &name, + QuestCallback const &); + +/** + * Called by the handler of the account server when a value is received. + */ +void recoveredQuestVar(int id, std::string const &name, + std::string const &value); + +#endif diff --git a/src/scripting/lua.cpp b/src/scripting/lua.cpp index 50d36349..3eb3ecff 100644 --- a/src/scripting/lua.cpp +++ b/src/scripting/lua.cpp @@ -37,6 +37,7 @@ extern "C" { #include "game-server/itemmanager.hpp" #include "game-server/mapmanager.hpp" #include "game-server/npc.hpp" +#include "game-server/quest.hpp" #include "game-server/state.hpp" #include "net/messageout.hpp" #include "scripting/script.hpp" @@ -68,6 +69,9 @@ class LuaScript: public Script int execute(); + static void getQuestCallback(Character *, std::string const &, + std::string const &, void *); + private: lua_State *mState; @@ -320,7 +324,7 @@ static int LuaChr_InvCount(lua_State *s) } /** - * Callback for trading between a a player and an NPC. + * Callback for trading between a player and an NPC. * tmw.npc_trade(npc, character, bool sell, table items) */ static int LuaNpc_Trade(lua_State *s) @@ -362,6 +366,69 @@ static int LuaNpc_Trade(lua_State *s) return 0; } +/** + * Called when the server has recovered the value of a quest variable. + */ +void LuaScript::getQuestCallback(Character *q, std::string const &name, + std::string const &value, void *data) +{ + LuaScript *s = static_cast< LuaScript * >(data); + assert(s->nbArgs == -1); + lua_getglobal(s->mState, "quest_reply"); + lua_pushlightuserdata(s->mState, q); + lua_pushstring(s->mState, name.c_str()); + lua_pushstring(s->mState, value.c_str()); + s->nbArgs = 3; + s->execute(); +} + +/** + * Callback for getting a quest variable. Starts a recovery and returns + * immediatly, if the variable is not known yet. + * tmw.chr_get_chest(character, string): nil or string + */ +static int LuaChr_GetQuest(lua_State *s) +{ + Character *q = getCharacter(s, 1); + char const *m = lua_tostring(s, 2); + if (!m || m[0] == 0) + { + LOG_WARN("LuaChr_GetQuest called with incorrect parameters."); + return 0; + } + std::string value, name = m; + bool res = getQuestVar(q, name, value); + if (res) + { + lua_pushstring(s, value.c_str()); + return 1; + } + lua_pushlightuserdata(s, (void *)®istryKey); + lua_gettable(s, LUA_REGISTRYINDEX); + Script *t = static_cast<Script *>(lua_touserdata(s, -1)); + QuestCallback f = { &LuaScript::getQuestCallback, t }; + recoverQuestVar(q, name, f); + return 0; +} + +/** + * Callback for setting a quest variable. + * tmw.chr_set_chest(character, string, string) + */ +static int LuaChr_SetQuest(lua_State *s) +{ + Character *q = getCharacter(s, 1); + char const *m = lua_tostring(s, 2); + char const *n = lua_tostring(s, 3); + if (!m || !n || m[0] == 0) + { + LOG_WARN("LuaChr_SetQuest called with incorrect parameters."); + return 0; + } + setQuestVar(q, m, n); + return 0; +} + LuaScript::LuaScript(): nbArgs(-1) { @@ -377,6 +444,8 @@ LuaScript::LuaScript(): { "chr_warp", &LuaChr_Warp }, { "chr_inv_change", &LuaChr_InvChange }, { "chr_inv_count", &LuaChr_InvCount }, + { "chr_get_quest", &LuaChr_GetQuest }, + { "chr_set_quest", &LuaChr_SetQuest }, { NULL, NULL } }; luaL_register(mState, "tmw", callbacks); |