Friday, July 08, 2005

Advanced LUA

NOTE: This has been updated for LuaInterface 1.3 see this link.

In the first LUA tutorial I did - I showed you how to call C# functions from LUA. All well and good ... but what about using classes? That's kinda of important isn't it! You want to interact with your NPC classes and all that kind of cool stuff. This is what we'll be covering here.

Let There Be Classes!

I assume you can set up a LUA enabled C# project - we're going to do another nice console window one.

First thing to note:
Classes, enumerations, structs, what have you, . . . are all "CTS types".
Q: "What does CTS stand for?"
A: "Shutup"

Right, so how do we use these CTS types inside LUA. How how how?
Well there are two important functions that we must use.
1. load_assembly
2. import_type
While you're commiting those to memory - let's start creating a demo, framework, example whatever! Let's make one. So we make a new console application.

When I type you . . . type. Just like that.

Using the previous tutorial we created a LUA enabled C# program. You should end up with the following and should be able to compile it without errors (if you fail in this hang your head in shame and go read the first tutorial again)

using System;
using LuaInterface;

namespace LuaClasses
{
///
/// A Room
///
class Room
{
Lua lua = new Lua();
///
/// The main entry point for the application.
///
[STAThread]
static void Main(string[] args)
{
//
// TODO: Add code to start application here
//
}

}
}


Now we're going to add a class, a marvel of object orientated design and programming.

class Person
{
public string description = "";
}


Amazing! Well let's say we want a few of these people. We need the collections namespace. So, at the top, we'll whack in the relevant line of code.

using System.Collections;

The class "Room" is where we'll be having the party so we need to add some code there, too. We'll have the party data-structure itself and a look function that will tells us what's happening.

ArrayList party = new ArrayList();

//Look at the party
public void Look()
{
foreach(Person p in party)
Console.Write(p.description);
}


We need one last function then we can start our LUA stuff. Can you guess what function? No? Well it's an AddPersonToParty function, doesn't exactly roll off the tongue but it will do.

public void AddPersonToParty(Person p)
{
party.Add(p);
}


Making Use of Lua

Let's stick to tried and true ground first and register the above function. Well kind of tried and true - it has a type in that's not basic to LUA! But if you're not afraid, I'm not afraid, let's continue! We're going to jazz up the main function like so:

static void Main(string[] args)
{
Room room = new Room();
room.lua.RegisterFunction("JoinParty" , room,
room.GetType().GetMethod("AddPersonToParty" ));

room.Look();

Console.WriteLine("Press enter key to exit");
Console.ReadLine(); //wait for keypress
}

Hmm sweet jazzy jazz. Here we create an object that represents a room. Then we register a function that will let us add people to the party from a Lua script (The room also contains a Lua reference). Then we call the look function of the room!

This should list the people in the party.

Then finally we tell the user that to exit this program she must merely press the enter key. Right, are you ready to be amazed? Okay, one last line of C# code and then we'll do some more amazing.

In the main function before the look command we must add a run script function. Remember this won't work until we write the script itself so calm down.

room.lua.DoFile("party.txt");

room.Look();


Coding In Lua

Ready? Okay go to the bin directory where the executable is being generated. You know where you dumped all those Lua dll files? Yeah? Good. Go there. Now create a text file named "party.txt". Now open it and prepare to type!

--Grab the class info

load_assembly("LuaClasses"); --our misleading namespace yay!
Person = import_type("LuaClasses.Person");

--Create some people and add them to the party

--Steve

steve = Person();
steve.description = "There's Steve drinking something brown and frothy from a shoe\r\n";
JoinParty(steve);


For now Steve is a one man party. Okay now run the executable and try not to look alarmed. We should get the output:


There's Steve drinking something brown and frothy from a shoe
Press any key to exit.


And there you go. Remember what CTS stands for and you'll be fine! Now you can add classes to your Lua files - and you now what classes mean - prizes!

Let's Do Functions!

We have to make use of the Lua libraries to do this. We're going to use the base library. Let's go back to our person class and add a few bits.

class Person
{
public string description = "";
public string name = "";


public void GiveName(string n)
{
name = n;
}

}

Now let's check out that look function.

public void Look()
{
foreach(Person p in party)
Console.Write(p.name + ": " + p.description);
}


In our main class we need to say we're going to be using the base library. Here's how:

static void Main(string[] args)
{
Room room = new Room();
room.lua.OpenBaseLib();

Very nice. Now we add a call to this method in the actual text file.

--Steve
steve = Person();
steve.description = "There's Steve drinking something brown and frothy from a shoe.\r\n\r\n";
steve:GiveName("Steve");
JoinParty(steve);


Notice the little colon rather than a peroid. Be careful about that otherwise it will trip you up. Okay that should be enough to keep you going.

If you're going to use ints or numbers as function arguments remember all Lua's numbers are doubles therefore they need to be converted. This is done automatically yah! But in order to do it automatically you must include the math library. Using this: room.lua.OpenMathLib(); will sort you out.

Next time I might do delegates, of course consider the amount of time that seems to pass between tutorials before you go betting your deadline on me. You may also be wondering about inheritence and all that ... the answer is yes you can but it's not something I think I'm going to need at the moment so I'm leaving it alone.

7 comments:

Anonymous said...

SInce 1.3.0, LuaInterface works with a dll called luanet.dll. Unfortunately, I don't get the sample to work with it.

"party.txt:1: could not load package `luanet' from path `?;?.lua'" is, what I get on the line saying "require("luanet")".

Does anyone know what to do?

Anonymous said...

the solution is to replace:
load_assembly("LuaClasses");
Person = import_type("LuaClasses.Person");
with:
luanet.load_assembly("LuaClasses");
Person = luanet.import_type("LuaClasses.Person");

Anonymous said...

hoo yes and I also noticed that:
room.lua.OpenBaseLib();
is not necessary anymore, in fact I didn't checked with a previous version of the lua interface but with 1.3.0 I can call the Steve:GiveName("Steve"); without it.

and just to make things more clear: I said to prefix the 2 function calls with 'luanet.' nothing more ... this means that the 'require "luanet"' is not necessary and indeed gives some package loading error if you use it.

balaam said...

Thanks for all the info. I'll be looking to update this soon.

Anonymous said...

I've done everything in the tutorial and the comments but when I try to run it I get an exception:

"party.txt:9: attempt to call global `Person' (a nil value)"

Anyone know how to fix this?

Anonymous said...

This may be a version conflict or something I also have the same issue. First time with LUA (obvsiously).

balaam said...

Yes, that's more than likely considering I wrote this with the last version of Lua interface.

I'm going to scrapbook it in firefox now and I'll redo sometime during the week when I bored at school :D