<--Intro ^--programming--^ schema-->

luad20 - a Lua Schema for d20 gaming systems - design

I'm not going to start the planning and design from scratch, having the body of xmld20 to work from. Instead, we're going to dive right in and start converting xmld20 to what we need.
A few things attracted me to using Lua for this project. One was the need for a scripting language, which was at first (un)satisfied by XSLT; then by a short attempt at Xscr, a scripting language in XML; and last, an aborted attempt to move to PHP.

Lua provides the simple syntax that made PHP appealing, and also provides inherent embeddability into other applications, allowing Luad20 to be a library of d20 manipulation routines that can be used by any external application, regardless of their own language of choice.

Additionally, Lua has a few pieces of "syntactic sugar", which make for nicer-read code. One of them in particular, however, was the deciding factor in using Lua for the d20 project. Lua allows you to pass a table into a function in the following way:

Feat{name="Acrobatic",type="General",benefit="jump=jump+2; tumble=tumble+2"}
This is really shorthand for calling the Feat() function with the table, but if we lay it out a little nicer, we get something that I think a non-programmer might feel comfortable with:
Feat{
	name="Acrobatic",
	type="General",
	benefit="jump=jump+2; tumble=tumble+2"
}
There is a lot of glossing-over of what happens with this simple command, which we'll take a look at.

Lua tricks

The above looks very much like a data file that might be describing the feat. This is what attracted me to Lua. Given the parameter names required (name, type, benefit) and optional (prerequisities, special), anyone should be able to add a feat to their d20 system with this format without knowing any programming.

The Feat() function can be thought of a DefineFeat() function, in that it loads a feat into the game system for use by character sheets, items, etc. Because this is the visible side of the system, I figure we'll keep out the techno-babble of "DefineFeat" and just let it look like you're providing a list.

The curly-braces, instead of the parenthesis, are the syntactic sugar for passing in a table. In this case, we're defining the name of our feat, its type (for sorting, searching, etc.), and what to do when a creature has this feat.

Another nice feature of the definition being code, and not just code, but a function call, is that the feat is automatically being passed through some error-checking when it's being loaded: the Feat() function can check if the name is already used, and if so, ask the user what to do (or just inform the user that this new definition is being used/ignored); if the name or type is missing, a message can pop up saying so, asking for one, or the entry gets ignored; if the type isn't known, a warning might pop up (but it continues loading anyway); if the benefit is missing, well... something's wrong.

The type could also be multiple things, perhaps, with [Fighter] and [Metamagic] (no, it doesn't really make sense, but as an example), in which case, we can't have

...
	type="General",
	type="Metamagic",
...
but we can have this instead:
...
	type={"General","Metamagic"},
...
We could require that types are always in their own table, but Lua is nice and flexible in that we can allow either just a string, or the table with strings, and our Feat() function can check for and handle both. In fact, we might decide to handle it without the quotes, to make it even easier for non-programmers to use:
...
	type={General,Metamagic},
...
This can be done if we have variables named General and Metamagic as constants in our environment.

The benefit entry is interesting. It's just a string, but this is no problem for us... Lua has a loadstring() function which takes code in exactly this form and returns a function, which is what we'd probably want. But this way, we don't require the user to know how to write benefit=function() ... end. Even better, we can get rid of the need for quotation marks by using an alternate notation in Lua:

Feat{
	name="Acrobatic",
	type="General",
	benefit=
	[[
		jump=jump+2
		tumble=tumble+2
	]]
}
This even got rid of the need for semi-colons, which we don't need to impose on the user (no, they weren't needed before either, but made for more readable code.) It's unfortunate that the body of the benefit has to be so code-like, but we have to draw the line somewhere, unless we want to start parsing it as data. As of now, the above is legitimate Lua code, and automatically does what we want it to (well, once we write the Feat() function of course).

Probably the best part of this system (and supporting scripting over data in general), is that we don't have to pre-plan the logic that is required to support future additions. I went through this a bit in my design sections for xmld20, but I'll re-iterate here: the fact that the benefit field can be any arbitrary Lua code means that if someone knows enough programming, they could have a feat that does something that me, as the developer, didn't plan for, or couldn't imagine needing. I don't need to support a DoublesCharacterHeight=yes feature, just in case it's needed, but it can still be done. All that's required is a schema of the values that are available for modification.
<--Intro ^--programming--^ schema-->

©2002-2010 Wayne Pearson