Skip to content

bogu9821/BetterDaedalusExternals

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

57 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

BetterDaedalusExternals

Improved method of creating C++ externals for the Daedalus scripting language.
Thanks to C++ templates and constexpr, you no longer need to define external types yourself or call parser methods to handle arguments from the stack or return values.
This simplifies code writing and reduces the errors. I most cases the call to an external will be fully inlined and it won't have any overhead. There are many errors in the engine with returning values in void functions, which may cause stack overflow.
Additionally, zSTRING externals have a pooled return value and you don't have to return static variables.
zSTRINGs can be also taken from the stack as const references, eliminating the need for copying.
The name of the external function will be taken from the name of the function/lambda.
Compile time errors occur if you attempt to redefine an external.

Requires C++23 and is designed to work with Union 1.0m SDK.

Example

Include BetterDaedalusExternals.h and use macros for externals definition e.g.

int EXT_Log_GetTopicStatus(const zSTRING& t_topicName)
{
	auto list = oCLogManager::GetLogManager().m_lstTopics.next;
	while (list)
	{
		auto const log = list->data;
		if (log->m_strDescription == t_topicName)
		{
			return static_cast<int>(log->m_enuStatus);
		}

		list = list->next;
	}

	return static_cast<int>(oCLogTopic::zELogTopicStatus_Free);
}

void Npc_DoTakeItem(oCNpc* t_npc, oCItem* t_item)
{
	if (!t_npc || !t_item)
	{
		return;
	}

	t_npc->DoTakeVob(t_item);
}

int EXT_RGBA(const int r, const int g, const int b, const int a)
{
	const zCOLOR color
	{
		static_cast<unsigned char>(r),
		static_cast<unsigned char>(g),
		static_cast<unsigned char>(b),
		static_cast<unsigned char>(a)
	};

	return static_cast<int>(color.dword);
}

zSTRING GetHeroName()
{
	return player->name[0];
}

BetterExternalDefinition(parser,
	BetterDaedalusExternal(EXT_Log_GetTopicStatus),
	BetterDaedalusExternal(GetHeroName),
	//external is added to a parser only if condition is meet
	BetterDaedalusExternalWithCondition(Npc_DoTakeItem, []() -> bool { return (rand() % 2) == 1; })
);

BetterExternalDefinition(parserMenu, BetterDaedalusExternal(EXT_RGBA));

How it looked before

Here's how you typically add externals without using my library:

//zCParser::DefineExternal accepts only int function pointers
int Log_GetTopicStatus()
{
	zCParser* par = zCParser::GetParser();

	//copy zSTRING from the stack
	zSTRING logName;
	par->GetParameter(logName);

	auto list = oCLogManager::GetLogManager().m_lstTopics.next;
	while (list)
	{
		auto const log = list->data;
		if (log->m_strDescription == logName)
		{
			par->SetReturn(static_cast<int>(log->m_enuStatus));
			return 0;
		}

		list = list->next;
	}

	//you have to remember to call zCParser::SetReturn before every return of a C++ function.
	par->SetReturn(static_cast<int>(oCLogTopic::zELogTopicStatus_Free));
	return 0;
};

int Npc_DoTakeItem()
{
	auto const par = zCParser::GetParser();

	oCNpc* npc;
	oCItem* item;

	//all arguments must be popped in a reverse order
	item = static_cast<oCItem*>(par->GetInstance());
	npc = static_cast<oCNpc*>(par->GetInstance());

	//you have to pop every argument before return
	if (!item || !npc)
	{
		return 0;
	}

	npc->DoTakeVob(item);

	return 0;
}

int EXT_RGBA()
{
	auto const par = zCParser::GetParser();

	int r, g, b, a;

	par->GetParameter(a);
	par->GetParameter(b);
	par->GetParameter(g);
	par->GetParameter(r);

	const zCOLOR color
	{
		static_cast<unsigned char>(r),
		static_cast<unsigned char>(g),
		static_cast<unsigned char>(b),
		static_cast<unsigned char>(a)
	};

	par->SetReturn(static_cast<int>(color.dword));

	return 0;
}

//hooked engine function
void Game_DefineExternals()
{
	//for every parser you have to define name, all script function types and return value
	//it uses C va args so at the end must be 0
	//if you will pop different type in your external you will get a crash
	parser->DefineExternal("Log_GetTopicStatus", Log_GetTopicStatus, zPAR_TYPE_INT, zPAR_TYPE_STRING, 0);

	if ((rand() % 2) == 1)
	{
		parser->DefineExternal("Npc_DoTakeItem", Npc_DoTakeItem, zPAR_TYPE_VOID, zPAR_TYPE_INSTANCE, zPAR_TYPE_INSTANCE, 0);
	}

	menuParser->DefineExternal("EXT_RGBA", EXT_RGBA, zPAR_TYPE_INT, zPAR_TYPE_INT, zPAR_TYPE_INT, zPAR_TYPE_INT, zPAR_TYPE_INT, 0);
}

About

Improvements way to create c++ externals for Daedalus scripting language. Requires C++23.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages