Advertisement

how do i do such a thing in Lua ????

Started by July 18, 2004 11:17 PM
14 comments, last by GameDev.net 20 years, 4 months ago
hi, im working on a 2D RPG w / C++, SDL, and OpenGL. im working on adding NPC's to the game. anyway, i finnally figured out how to build a dialogue tree, and allow the player to have conversations with the NPC. also, i have it set up to where the player can walk up the an NPC, press T, and he will enter chat mode. this is when i load the dialogue tree, load the chat GUI, and let the user conversate with the NPC. well right now i have it hard-coded into C++. id like it so that when i pressed T near an NPC, it loaded the Lua script instead. THEN this Lua script would draw the chat window, load the dialogue tree, etc... i would want to take input and draw the GUI from the script, so i dont have to yeild back to the game. this makes things much easier. but i dont understand how to do such a thing. this is because im using my own data types and the new keyword. i can figure out how to call C++ functions from Lua, but how do i do something like build a dialogue tree? this requires me to allocate memory via new, and free it via delete.. how do i do this from inside a Lua script? and how would i go about using my objects in Lua? i know i can use LuaBind for that, but what about allocating memory, and accessing objects through pointers? here is the function which is right now hard-coded into C++, but id like to do everything here in the Lua script instead. so instead of calling this function, ill call the Lua script for whatever NPC i clicked on:

void Messages::Enter_Chat(Npc *npc)
{
	bool done = false;

	map<string,Widget>::iterator chat_gui = widgets.find("Chat_GUI");

	if(chat_gui == widgets.end())
		systm->Log("COULDNT FIND THE CHAT WIDGET INSIDE MESSAGES::ENTER_CHAT !! BAILING OUT !!!",FAILURE);

	//now lets build up the dialogue tree
	
	// Start the conversation
    NPC_Reply *conversation = new NPC_Reply("<A rat-like dwarf is here.\n He stares at you in silence>");
	NPC_Reply::delete_mes.push_back(conversation);

	// Loopback point
	NPC_Reply *loopback = new NPC_Reply("Is there anything else youd like to talk about?");
	NPC_Reply::delete_mes.push_back(loopback);

	NPC_Reply *bail = new NPC_Reply("Null sweat, chum. \n G'luck in your struggles!");
	NPC_Reply::delete_mes.push_back(bail);
	
    // CHOICE 1
    NPC_Reply *b1 = conversation->Expand("Im looking for some information","I usually dont share such things, but.... I guess i could tell you something");

/*1A*/	     b1->Expand("Whats your story, chummer?","I lived in Glow City all my life. I was slummin' in the sewers, but it got me nowhere, because i had no purpose. And because everyone was larger then me")->Add("Lets talk about a different subject.",loopback);
		

/*1B*/       NPC_Reply *b1a = b1->Expand("I need some questions answered","Oh? Like what?");
				b1a->Expand("Tell me about shaman magic.","Shaman get there powers from nature. We use her forces to acheive our ends. This is supereior to mages, who force the sprits to do their bidding")->Expand("continue....","Now here is even more info on mages. this is awesome!! i can make as many pages of stuff i want!!!!")->Add("Lets talk about a different subject.",loopback);
				b1a->Expand("Tell me about the local area.","The Rat's Nest is a havan for squatters and other transients. \n It used to be the city dump untill the city died. \n Even I, followeer of Rat, will not hole up here.")->Add("Lets talk about a different subject.",loopback);
		        b1a->Expand("Do you know of any Johnsons in the local area?","Yeah, sure. Gunderson's a Johnson Ive used when i needed some quick nuyen. \n Works next door at the Jump House, he does. \n His payout is as bad as his smell, but his runs are a piece of cake so long as you have some firepower handy when things go sour")->Add("Lets talk about a different subject.",loopback);

/*1C*/	     b1->Add("Lets talk about a different subject",loopback);

    
	//CHOICE 2
	NPC_Reply *b2 = conversation->Expand("Im lookin' for a good 'runner. \n Care to sign on?","Only if its a simple job. I dont like to fight. I charge 155 for a single run.");
		   b2->Expand("I just need someone for the short-term. Whadda ya say?","Im all yours")->Add("Lets talk about a different subject.",loopback);
		   b2->Expand("I could use a life-long pal for hire. Whadda' ya say?","Take good care of me.")->Add("Lets talk about a different subject.",loopback);
		   b2->Expand("Ill talk to you later, chum.","Null sweat, chum. \n G'luck in your struggles!")->Add("......",bail);
   
    //CHOICE 3
    conversation->Expand("I'll talk to ya later, chum.","Null sweat, chum. \n G'luck in your struggles!")->Add("......",bail);
   
    NPC_Reply *temp = conversation;

  	//make the loopback have the same replies as the convo. so its an exact copy, except its NPC reply is "is there anything else i can do for you?"
	loopback->player_replies = conversation->player_replies;
	
	temp = conversation;

	//do the convo untill we get to the "bail out" node
	while(temp != bail)
	{

		SDL_Event event;
		//first before we do anything we will clear the bit and depth buffers
		glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
				
		while ( SDL_PollEvent(&event) ) //looks for events
		 {

			switch(event.type)
			{
		
				case SDL_QUIT: 
				{
					systm->done = true;
					break;
				}
				break;

				case SDL_KEYDOWN:
				{
					if(event.key.keysym.sym == SDLK_q)
						temp = bail;
					else if(event.key.keysym.sym == SDLK_ESCAPE)
					{
						systm->done = true;
						temp = bail;
					}
					else if(event.key.keysym.sym == SDLK_t)
						temp = bail;
				}
				break;	

				
			}//end of switch event.type
		 
		 }// end of while polling for events
    
		systm->keys = SDL_GetKeyState(NULL);

		//systm->Draw_Quad(chat_gui->second.texture,0,0,800,600);
		Draw_Widget(0,0,chat_gui->second);

	

			// Only wait for user input if there is more
			// conversation to be had
		   if(!temp->player_replies.empty())
		   {
			   Display_Conversation(temp);

		      int option = Get_User_Option(temp);

			  //option will be -1 if the player pressed escape or Q or T
			  if(option == -1)
				  temp = bail;
			  
			  else temp = temp->player_replies[option].npc_reply;
		   }
		   else temp = loopback;

		SDL_GL_SwapBuffers();
		//systm->Draw_Quad(chat_gui->second.texture,0,0,800,600);
	}


	for(int i = 0; i < NPC_Reply::delete_mes.size(); i++)
		delete NPC_Reply::delete_mes;

	NPC_Reply::delete_mes.clear();
}




so will this be possible? thanks for any help!!!
FTA, my 2D futuristic action MMORPG
For such a task, Small is more than adequate. That's what I use for Eternal Lands.
Advertisement
I am working on a Lua-script system also. The method that I am using may not be the best, it is just what I figured out the soonest, and thought would be the easiest.

What you would do is create a function called NewObject(). It would be implemented in C, and exposed to Lua. It would call new Object; and then return the pointer in the form of 'userdata' Then, later, when you need to acces the data, I simply pass the userdata as a parameter to a function to do whatever low-level manipulation that is necessary. In this way, the low-level code such as allocation/delocation/etc is handled in C, and you worry about the high level stuff in the script (what it is for, no?) Anyway, the obvious down side to the scheme is that it is most definately not type safe. If you call NewObject or NewBox, the return type will be the same. In my case, exposing entire classes was a bit overkill, so I am somewhat emulating them by writing functions that expose their members and take a pointer to object as a first parameter (I believe this is how classes are actually handled in C++, although I may be wrong)

Hopefully you were able to make sence of that, but in general I'd say that you should write a script of what you want to be able to execute and then write the C code to make it possible. That generaly helps clear things up for me.

Dwiel
thanks for your replies guys. i have a question. im reading the luabind docs and trying to get a simple program to bind a C++ class into a lua script and then run the script from a C++ program. anyway, its very simple but its giving me this as the output:

"test.lua:6: ')' expected near 'std'"

im not sure why im getting these errors. heres the source to the C++ program:

#include <stdio.h>#include <string>#include <iostream>#pragma comment(lib,"Lua+Lib.lib")extern "C" {	#include "lua.h"	#include "lualib.h"	#include "lauxlib.h"}#include <luabind/luabind.hpp>/* the Lua interpreter */lua_State* L;class testclass{	public:			testclass(const std::string& s): m_string(s) {}		void print_string() { std::cout << m_string << "\n"; }		private:		std::string m_string;};int main ( int argc, char *argv[] ){	/* initialize Lua */	L = lua_open();	/* load Lua base libraries */	lua_baselibopen(L);	/* run the script */	lua_dofile(L, "test.lua");	/* cleanup Lua */	lua_close(L);	system("PAUSE");	return 0;}


now heres the test.lua script
--define the modulemodule(L)[	class_<testclass>("testclass")		.def(constructor<const std::string&>())		.def("print_string", &testclass::print_string)];-- instantiate the classa = testclass('a string')-- call the member function print_string on it-- this will print 'a string' on stdouta:print_string()


so what am i doing wrong? thanks for any help!!
FTA, my 2D futuristic action MMORPG
I'd say it's choking on the stl::string class. I'd try just printing out integers. If that works, then work on getting the string class to work, if it's necessart. If you cant do the same with an int, i'll take a look at it again, although I havnt used luabind, so I am only making an educated guess.

Dwiel
It looks like you're trying to use the module(L) notation in your Lua code! That's not how Luabind works. "module" is boost/template C++ magic that HAS to be called from C++. What you would do is something like
extern "C" {#include "lua.h"}#include <luabind/luabind.hpp>using namespace luabind;#include <string>class testclass {public:    testclass(const std::string& str) : myStr(str) {}    void print_string() {        std::cout << myStr << std::endl;    }private:    std::string myStr;};int main(int argc, char** argv) {    lua_State* L = lua_open();    luabind::open(L);    // this "exposes" the lua state L to your class "testclass"    module(L)    [        class_<testclass>("testclass")            .def(constructor<const std::string&>())            .def("print_string", &testclass::print_string)    ];    lua_dofile(L,"test.lua");    lua_close(L);}

Then your test.lua file would just be
-- instantiate the classa = testclass('a string')-- call the member function print_string on it-- this will print 'a string' on stdouta:print_string()

Hope that helps!
Advertisement
you know.. i thought it looked like C++ code, but i couldnt get it to compile no matter where i put the code! i didnt even think to put it in the body of main.. i was trying to put it in the global namespace =P .. i dunno, it just looked weird with those brackets...

thanks a lot for your help!! it works now!
FTA, my 2D futuristic action MMORPG
i still cant figure out how to build my dialogue tree from inside Lua. OK, so i can allocate memory - thats easy. i just make a function in C++ that looks like this:

NPC_Reply *New(string init){ return new NPC_Reply(init);}

thats simple. so now i can call New() from Lua to allocate new memory. BUT, how do i build the rest of my tree? IE, how do i do this from Lua? is it even possible to use pointers in Lua, and if so how? also, can i de-reference them and do things with them? this is the part i cant figure out! here is the C++ code i want to do from Lua

		// Start the conversation    NPC_Reply *conversation = new NPC_Reply("<A rat-like dwarf is here.\n He stares at you in silence>");	NPC_Reply::delete_mes.push_back(conversation);	// Loopback point	NPC_Reply *loopback = new NPC_Reply("Is there anything else youd like to talk about?");	NPC_Reply::delete_mes.push_back(loopback);	NPC_Reply *bail = new NPC_Reply("Null sweat, chum. \n G'luck in your struggles!");	NPC_Reply::delete_mes.push_back(bail);	    // CHOICE 1    NPC_Reply *b1 = conversation->Expand("Im looking for some information","I usually dont share such things, but.... I guess i could tell you something");/*1A*/	     b1->Expand("Whats your story, chummer?","I lived in Glow City all my life. I was slummin' in the sewers, but it got me nowhere, because i had no purpose. And because everyone was larger then me")->Add("Lets talk about a different subject.",loopback);		/*1B*/       NPC_Reply *b1a = b1->Expand("I need some questions answered","Oh? Like what?");				b1a->Expand("Tell me about shaman magic.","Shaman get there powers from nature. We use her forces to acheive our ends. This is supereior to mages, who force the sprits to do their bidding")->Expand("continue....","Now here is even more info on mages. this is awesome!! i can make as many pages of stuff i want!!!!")->Add("Lets talk about a different subject.",loopback);				b1a->Expand("Tell me about the local area.","The Rat's Nest is a havan for squatters and other transients. \n It used to be the city dump untill the city died. \n Even I, followeer of Rat, will not hole up here.")->Add("Lets talk about a different subject.",loopback);		        b1a->Expand("Do you know of any Johnsons in the local area?","Yeah, sure. Gunderson's a Johnson Ive used when i needed some quick nuyen. \n Works next door at the Jump House, he does. \n His payout is as bad as his smell, but his runs are a piece of cake so long as you have some firepower handy when things go sour")->Add("Lets talk about a different subject.",loopback);/*1C*/	     b1->Add("Lets talk about a different subject",loopback);


so once i give Lua access to the NPC_Reply class, how do i make NPC_Reply pointers, allocate memory, de-reference and make more nodes, etc, like in my example? if you notice, Expand() will create a new pointer, and then as im creating this new pointer, im also de-referncing this new pointer and calling Add() on it. i just cant figure out how to do this in lua, ive searched google and cant find any info on pointers with it. thanks for any help!!
FTA, my 2D futuristic action MMORPG
I would like to submit that you don't want to be creating C++ objects from Lua. That's the wrong level of abstraction for a Lua interface, and will get unwieldy pretty quickly (been there ;-)

Instead, I'd suggest that you define your dialog tree in Lua. A scripting language like that is IDEAL for defining dialog trees, in my opinion; the table objects can contain all kinds of interesting data that you need.

Then you have the choice of using the Lua script as data for a C++ dialog tree driver (what I would do), or coding the actual dialog-ing functions within Lua as well. Because there's only one dialog going on at a time (right?) I'd bind some global functions into Lua to display a specific response and display a specific set of choices, and perhaps some callbacks for what the user actually chooses; it doesn't need to be (and shouldn't be) more complicated than that.
enum Bool { True, False, FileNotFound };
ahh man.. i was hoping someone wouldnt say that..

ive actually come up with a very nice, flexible system to build a dialogue tree and traverse through it in C++. also, i dont plan on yeilding back to the game when the script is run. this is how i want to do it:

User gets in range of an NPC, and presses the chat button. then:

Execute_Script(target_npc->script_path);

i execute this NPC's script! since it is a real PITA to figure out a system which will yeild back to the application, and since my chat system will take over the whole screen anyway and stop the world in its place, i will just draw the chat GUI, take input, and allow the user to have the conversation with the NPC entirely in the script. that way, i dont have to yeild back to the game. i run the script untill the player wants to exit out of the conversation.

that being said, does this still sound like a bad idea for me to try to do? i just want to build the dialogue tree, draw the chat dialogue, and allow the user to traverse through the convo. in fact, most of this stuff can be in in C++, and i would just call the C++ function through the Lua script. the only part that will change script to script is the actual building of the dialogue tree.

so, does anyone know how i could do what im trying to do? thanks for anymore help!!! also, ill look into table objects, but for now im still leaning towards sticking to my system (if possible). thanks again!
FTA, my 2D futuristic action MMORPG

This topic is closed to new replies.

Advertisement