• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

Feature LUA code in descriptions of quests in quests.xml

Gesior.pl

Mega Noob&LOL 2012
Senator
Joined
Sep 18, 2007
Messages
2,968
Solutions
99
Reaction score
3,384
Location
Poland
GitHub
gesior
Friend asked me about source edit 'show player storage in Quest Log'. I thought I can make it better. I decided to add 'creatureevent' that returns modified 'mission description from quests.xml'

Few source changes and you can generate quest description (Quest Log) for X player in LUA.
Tested on TFS 0.3.6, should work with 0.4/0.2 after small changes.

In file creatureevent.cpp:
Under:
Code:
else if(tmpStr == "preparedeath")
		m_type = CREATURE_EVENT_PREPAREDEATH;
Paste:
Code:
else if(tmpStr == "questdescription")
		m_type = CREATURE_EVENT_QUEST_DESCRIPTION;

Under:
Code:
case CREATURE_EVENT_PREPAREDEATH:
				return "onPrepareDeath";
Paste:
Code:
case CREATURE_EVENT_QUEST_DESCRIPTION:
				return "getQuestDescription";

Under:
Code:
case CREATURE_EVENT_PREPAREDEATH:
				return "cid, deathList";
Paste:
Code:
case CREATURE_EVENT_QUEST_DESCRIPTION:
				return "cid, description";

At end of file paste:
Code:
uint32_t CreatureEvent::executeGetQuestDescription(Creature* creature, std::string& description)
{
	//getQuestDescription(cid, description)
	if(m_interface->reserveEnv())
	{
		ScriptEnviroment* env = m_interface->getEnv();
		if(m_scripted == EVENT_SCRIPT_BUFFER)
		{
			env->setRealPos(creature->getPosition());
			std::stringstream scriptstream;

			scriptstream << "local cid = " << env->addThing(creature) << std::endl;
			scriptstream << "local description = " << description << std::endl;

			scriptstream << m_scriptData;
			if(m_interface->loadBuffer(scriptstream.str()))
			{
				lua_State* L = m_interface->getState();
				description = m_interface->getGlobalString(L, "_result", "");
				}
			m_interface->releaseEnv();
			return 1;
		}
		else
		{
			#ifdef __DEBUG_LUASCRIPTS__
			std::stringstream desc;
			desc << creature->getName();
			env->setEventDesc(desc.str());
			#endif

			env->setScriptId(m_scriptId, m_interface);
			env->setRealPos(creature->getPosition());

			lua_State* L = m_interface->getState();
			m_interface->pushFunction(m_scriptId);

			lua_pushnumber(L, env->addThing(creature));
			lua_pushstring(L, description.c_str());

			description = m_interface->callFunction2(2);
			m_interface->releaseEnv();
			return 1;
		}
	}
	else
	{
		std::cout << "[Error - CreatureEvent::executeGetQuestDescription] Call stack overflow." << std::endl;
		return 0;
	}
}


In file creatureevent.h:
Under:
Code:
CREATURE_EVENT_DEATH,
Paste:
Code:
CREATURE_EVENT_QUEST_DESCRIPTION,

Under:
Code:
uint32_t executePrepareDeath(Creature* creature, DeathList deathList);
Paste:
Code:
uint32_t executeGetQuestDescription(Creature* creature, std::string& description);


In file luascript.cpp:
Under:
Code:
bool LuaScriptInterface::callFunction(uint32_t params)
{
	int32_t size = lua_gettop(m_luaState), handler = lua_gettop(m_luaState) - params;
	lua_pushcfunction(m_luaState, handleFunction);

	bool result = false;
	lua_insert(m_luaState, handler);
	if(lua_pcall(m_luaState, params, 1, handler))
		LuaScriptInterface::error(NULL, LuaScriptInterface::popString(m_luaState));
	else
		result = (int32_t)LuaScriptInterface::popBoolean(m_luaState);

	lua_remove(m_luaState, handler);
	if((lua_gettop(m_luaState) + (int32_t)params + 1) != size)
		LuaScriptInterface::error(NULL, "Stack size changed!");

	return result;
}
Paste:
Code:
std::string LuaScriptInterface::callFunction2(uint32_t params)
{
	int32_t size = lua_gettop(m_luaState), handler = lua_gettop(m_luaState) - params;
	lua_pushcfunction(m_luaState, handleFunction);

	std::string result = "";
	lua_insert(m_luaState, handler);
	if(lua_pcall(m_luaState, params, 1, handler))
		LuaScriptInterface::error(NULL, LuaScriptInterface::popString(m_luaState));
	else
		result = LuaScriptInterface::popString(m_luaState);

	lua_remove(m_luaState, handler);
	if((lua_gettop(m_luaState) + (int32_t)params + 1) != size)
		LuaScriptInterface::error(NULL, "Stack size changed!");

	return result;
}


In file luascript.h:
Under:
Code:
std::string callFunction2(uint32_t params);
Paste:
Code:
static int32_t handleFunction(lua_State* L);


In file quests.cpp:
Under:
Code:
#include "tools.h"
Paste:
Code:
#include "creatureevent.h"

IN PLACE OF:
Code:
std::string Mission::getDescription(Player* player)
{
	std::string value;
	if(!player->getStorage(storageId, value))
		return "Couldn't retrieve a valid player storage, please report to a gamemaster.";

	if(state.size())
	{
		std::string ret = state;
		replaceString(ret, "|STATE|", value);
		return ret;
	}
 
	if(atoi(value.c_str()) >= endValue)
		return states.rbegin()->second;

	for(int32_t i = endValue; i >= startValue; --i)
	{
		if(!player->getStorage(storageId, value) || atoi(value.c_str()) != i)
			continue;
 
		std::string ret = states[i - startValue];
		replaceString(ret, "|STATE|", value);
		return ret;

	}

	return "Couldn't retrieve any mission description, please report to a gamemaster.";
}
Paste:
Code:
std::string Mission::getDescription(Player* player)
{
	std::string value;
	if(!player->getStorage(storageId, value))
		return "Couldn't retrieve a valid player storage, please report to a gamemaster.";

	if(state.size())
	{
		std::string ret = state;
		replaceString(ret, "|STATE|", value);
		CreatureEventList questDescriptionEvents = player->getCreatureEvents(CREATURE_EVENT_QUEST_DESCRIPTION);
	 	for(CreatureEventList::iterator it = questDescriptionEvents.begin(); it != questDescriptionEvents.end(); ++it)
	 	{
	 		if(player)
				{
					 (*it)->executeGetQuestDescription(player, ret);
				}
	 	}
		return ret;
	}
 
	if(atoi(value.c_str()) >= endValue)
	{
		std::string ret = states.rbegin()->second;
		CreatureEventList questDescriptionEvents = player->getCreatureEvents(CREATURE_EVENT_QUEST_DESCRIPTION);
	 	for(CreatureEventList::iterator it = questDescriptionEvents.begin(); it != questDescriptionEvents.end(); ++it)
	 	{
	 		if(player)
				{
					 (*it)->executeGetQuestDescription(player, ret);
				}
	 	}
		return ret;
	}

	for(int32_t i = endValue; i >= startValue; --i)
	{
		if(!player->getStorage(storageId, value) || atoi(value.c_str()) != i)
			continue;
 
		std::string ret = states[i - startValue];
		replaceString(ret, "|STATE|", value);
		CreatureEventList questDescriptionEvents = player->getCreatureEvents(CREATURE_EVENT_QUEST_DESCRIPTION);
	 	for(CreatureEventList::iterator it = questDescriptionEvents.begin(); it != questDescriptionEvents.end(); ++it)
	 	{
	 		if(player)
			{
				(*it)->executeGetQuestDescription(player, ret);
			}
	 	}
		return ret;
	}

	return "Couldn't retrieve any mission description, please report to a gamemaster.";
}


COMPILE AND THAT PART IS DONE :)


In data/creaturescripts/creaturescripts.xml
Add:
Code:
<event type="login" name="QuestLogin" event="script" value="questdescription.lua"/>
<event type="questdescription" name="QuestDescription" event="script" value="questdescription.lua"/>

Create file data/creaturescripts/scripts/questdescription.lua
Paste in it:
Code:
function onLogin(cid)
	registerCreatureEvent(cid, "QuestDescription")
	return true
end

function getQuestDescription(cid, description)
	if(string.sub(description,1,3) == "LUA") then
		function luaExecuter(...)
			local v = tostring(arg[2])
			if v:len() > 0 then
				return v
			else
				return "LUA DESCRIPTION - NO RETURN VALUE"
			end
		end
		return luaExecuter(pcall(loadstring('local cid = ' .. cid .. ' ' .. string.sub(description,4))))
	else
		return description
	end
end

AND IT'S READY TO USE!

Time for some examples of code in data/XML/quests.xml

Normal descriptions will work as they worked before. If you want use LUA code then first 3 letters of description must be LUA and then LUA code that MUST return string

You must remember that in your LUA code you cannot use "

Show if player has all required items or he still needs some (shows how many left):
Code:
<missionstate id="1" description="LUA if(getCreatureStorage(cid,123) >= 5) then return 'Yes! You collected all required items, now you can go to NPC.' else return 'No! You still need ' .. (5 - getCreatureStorage(cid,123)) .. ' required items' end" />
Show how many dragons player killed (from storage):
Code:
<missionstate id="1" description="LUA return 'You killed ' .. getCreatureStorage(cid,12345) .. ' dragons!' " />
 
Thanks you Gesior for leting me have this awesome feature on my Server.
There is a bug in this code. I have added it to my 0.3.7 distro and its bugs the questlog. The mission description( without any lua code, pure string ) of any mission just doens't show up. Its only show when the mission is finished.

Yamaken~
 
Back
Top