• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
  • entries
    206
  • comments
    217
  • views
    222225

About this blog

Merry Prankster Games: Temple of the Abyssal Winds

Entries in this blog

gdunbar
I've been working on icons for SENG lately; almost done.

One of my philosophies for SENG is to have a minimum of art content, but display it in many ways. In terms of icons, this means using one icon for a variety of different representations. For instance, inventory. Instead of making two separate icons for "Long Sword" and "Mighty Sword of the Wild Hunt", I use the same icon, but color it (in the game engine) differently.

For instance, here are the new inventory icons:



You can see three different versions of the "heavy blade" icon in the middle, one gray, one white, and another green. So, for each item, instead of having to draw up a new icon, instead I can just pick a color.

I should note that the colors in that particular screenshot are not well done; the bright colored items at the bottom especially. Saturated colors with a slight hue seem to look best in-engine. When creating "real" content I'll keep that in mind.

Another issue is making icons that look decent on both a dark background and a light background. SENG typically has a dark background, but when the user hovers on an item, the item is highlighted, changing the icon background to a light color. My best solution for this was to make a black shadow behind the main iconography. On a dark background, the shadow is virtually invisible, whereas on a light background it enables the icon to be seen.

Here are the new spell icons (note the highlighted spell):



Note that these icons are pretty muted because they represent low-level spells; the icons get brighter as the spell-level increases.

So, just wrapping up the remaining miscellaneous icons, and then some work on graphic effects.

gdunbar

Links: Assorted

Various links this week, all courtesy of Coyote (http://rampantgames.com/blog). Hey, maybe you should just read his blog instead of mine! (OK, you can read both).

First, a postmortem on Persona 4. Interesting for me to read about the trials and tribulations that another small RPG creator went through in developing their game:

http://www.gamesetwatch.com/2009/10/indepth_behind_the_scenes_of_a.php

Second, a discourse on the random dungeons in Din's Curse, Soldak's forthcoming RPG:

http://www.soldak.com/Blogs/Steven/Din-s-Curse-random-dungeons.html

Man, this makes me want to add random dungeon creation to SENG. However, a big part of my vision for a SENG game is to have well developed quests as part of the game. How about adding random quest generation too? Hmm... Maybe later.

Last, Destructoid on "The little things that every RPG should have":

http://www.destructoid.com/the-little-things-that-every-rpg-should-have-151174.phtml

This is one of my big points with SENG; support the little things that really should be there, to remove player frustration from the game. Let's see how my vision of SENG stacks up:

Enemies that telegraph when their HP is low

Agree. SENG displays an HP indicator for enemies; it's just annoying to hide this stuff, even if there is some "realism" justification.

Equipped weapons and armor visible on characters

He's talking specifically about NPCs. Borderline agree. SENG does this for "human-like" enemies, but not for more exotic enemies. Too much potential art to make.

Bestiaries

Agree. SENG has accessible information on every monster you encounter. How informative the info is depends on your party's Observation skill. I could probably do better at putting this information into a comprehensive, accessible documentation system; right now it is only accessible if you can actually see the monster.

Useful merchant screens

Agree. SENG is pretty good here (you can view item info for stuff you are planning to buy or sell), but doesn't let you access a character's inventory from the merchant screen. This could be better.

That bit when the party disbands in a town

I'm not really sure what he's getting at here.

"Story so far" summaries

Agree. SENG supports this with a robust journal, both of quests and of all dialogue with NPCs.

HP & MP restoration with level increases

Disagree. I think this feature encourages players having to be "gamey", and trying to time their level gains to correspond to difficult places in the game. Save HP and MP restoration for resting (or potions), where it belongs.

Status spells that actually WORK

Agree. The boss monsters should be vulnerable to spells, same as any other monster (or player). SENG has a save system that guarantees that all monsters will be more vulnerable to certain types of spells, so a clever player can exploit this to his advantage.


gdunbar


In the last couple of days, I wrapped up some last RPG tweaks. Made rogues a little more powerful, adjusted the numbers a little bit; nothing too big.

Next up on my list is to add some AI functionality to the monsters you run into in SENG. Right now they basically just charge at the closest party member and attack. My main goal here is to enable some more interesting tactical combat scenarios for the level designer. (Not totally true; I also want to enable some non-combat scenarios, like having monsters patrol an area, but that is a secondary goal).

However, as I've been thinking about these AI improvements, I got to thinking about the combat scenarios that I wanted to enable. I made a small list of these, and then thought about how I was going test these out, both the AI behavior and also the play balance. Thinking about making little test areas for the scenarios made me think about the next big milestone, where I make a series of prototypes for playtesting. And, "Eureka!", I should just merge these ideas, and make a series of test encounters as part of a prototype. Temporary code name: "Ten Interesting Fights".

Here are the 10 fights:

  1. "Warm-up" - The player controls one actor, a basic fighter. He fights a couple of really wimpy monsters, one at a time.

  2. "The Classic" - I add a couple of new party members, a rogue and a spellcaster. They fight a classic fantasy battle, against some brutes in the front with archers behind.

  3. "Summoners" - Same party as before. Now the enemies are a set of spellcasters that summon minions to fight the party.

  4. "Archers" - Same party. The enemies are a set of archers across a chasm, difficult to approach across a narrow, exposed bridge.

  5. "The Horde" - Now I introduce a full gamut of potential party members for the player to pick from. The first battle for this party is against a whole bunch of wimpy monsters. This is something of a test for how the engine scales. 20? 50? 100?

  6. "Heavy Ordnance" - Something like "The Classic", except now in the back are spellcasters that focus on damaging spells.

  7. "Sneaks" - The party faces a bunch of stealthy rogues who try to sneak behind the party and attack the soft spellcasters in the back, or use shoot-and-run tactics.

  8. "It's a Trap!" - Spellcasters and archers take cover behind a set of traps, some visible (lava pits), some not.

  9. "Helping Hand" - The party faces a set of tough soldiers, with healing/buffing spellcasters in the rear.

  10. "The Boss" - One big, mean sumbitch.


I figure I can advance the party 2 levels between each encounter, giving full exploration of levels 1 through 20.

In planning this, though, I've realized I need to make a bunch of content (levels, monsters, etc), before I can really get to work on the AI improvements. So I'm going to postpone the AI work just briefly to wrap up the remaining engine work. Which is:

  • A few graphics tweaks - adding "aegis" effects (which previously I had to manually put into the graphics), and a couple of others.

  • I never updated all of my icons to the new UI; they are all too dark, and I need some more, for instance for different weapon types.


So, probably about a week to work on those, at which point I'll declare the "Engine Updates" milestone done, and begin work on the "Ten Interesting Fights" Prototype. The goal with "TIF" is to actually produce a prototype that I can have some people test and give feedback.

Oh, and if someone has a thought on a better name than "Ten Interesting Fights", I'm all ears.
gdunbar
A couple of updates on the game engine. First, I added support for footstep sounds and ambient audio. One of the critiques that I got from the To The World Tree demo (http://www.prankster.com/ttwt) was that the game was virtually silent. I'm a little resistant to having a full background music track, but I can appreciate that some sound can help to add to the atmosphere.

The support I added is very basic indeed; when someone in your party is walking, I play a little loop of footstep sounds. And different areas in a level can have a little background loop that they play. So, for instance, if your party is walking through a mountain pass, the game can play a little loop of a wind sound.

I used audiere (http://audiere.sourceforge.net/) to provide the audio support, basically because the API is dirt simple to use, and that's what I wanted. It works well enough, though I am a little disappointed with the performance of MP3 playback; it introduced a little glitch in the game when I used an MP3 file. No big deal; I'll just use WAV files for the small audio that I have, but just a little warning.

Hard to provide a screenshot for audio, but here you can see my test character standing next to a fire, which does indeed play a little burning sound effect:



Second, I added support for what I call party chatter. Every 5 seconds or so, the game calls a "heartbeat" script on a randomly selected party member. This script is intended to allow party members to interact, unprompted, with the player or even other party members. So, if party members don't like the actions the player is taking, or don't like each other, or whatever, they can have a little conversation (or anything else that the scripting engine supports). In this screenshot, the honest Grumpy has gotten so fed up with the player that he is leaving the party altogether:



Next up, I'm working on some last little tweaks to the RPG system, followed by implementing a little more functionality in the AI system.
gdunbar
First, Greg Costikyan has a detailed article on randomness in games (video and otherwise), based on a presentation he made at Austin GDC 2009. Highly recommended; really informative and interesting:

http://playthisthing.com/randomness-blight-or-bane

Next, Coyote waxes poetic about his favorite game Ultima VII:

http://rampantgames.com/blog/2009/09/ultima-and-knee-trauma.html

I've started U7 a number of times but never gotten very far. I do still have a copy (part of some Ultima collection or other); I should dig it out, download Exult (http://exult.sourceforge.net/), and give it another go.
gdunbar
So I finished up the updates to the level editor. Now, let me show off what it can do!

To do this, I'll show how easy it is to make a tower in a grassy field. Let me put a caveat that I don't have any real art or anything, and I don't have a full tileset to work with. (I did actually spend a bit of time getting the tiles together, but that's more because of my incompetence than anything else). Here are the tiles I'm going to work with (pictured in the level editor):



Another caveat; the level editor is an internal-only tool. As such, it should be quick and easy to use, but it's not pretty, or necessarily intuitive.

OK, here's a blank level, with the grid turned on (note that at 40x40 tiles, this level is only 100 feet across; the editor handles much larger levels but that's as big as I need for this demo):



First I'll draw in the tower area. To do this, I enter layout mode, highlight an area:



Then, one click fills in dummy tiles. I'll make an L-shaped tower (so a couple more clicks):



Now, we need walls. To do this (still in Layout mode), I highlight the whole area, select the "Wall" tool, and "Apply" then draws walls around all the tiles (notice the walls all face nicely inwards):



I'll also manually draw in a wall to split the tower into two rooms; not pictured, but only a couple of clicks.

Now, time to "skin" the tower, replacing the dummy tiles with the real tiles. Enter "Skin" mode, pick the "towerskin", highlight the whole area, and hit Apply. Voila:



Next, I use the Layout mode again to put dummy floor tiles all around the tower, and use the Skin mode again ("grassskin" this time) to put grass all around the tower:



And there you have it!

Now, to polish the level, I'd have to go through manually and do stuff like add doors, furniture, and other adornments. But I don't have graphics ready for all that stuff, and besides, I didn't make the polishing any easier; unfortunately that stuff always takes awhile.

I am very happy with these changes; making levels is way easier than it was in the To The World Tree days. Next, working on sound effects and ambient sounds.

gdunbar
Couple of links today:

Andrew Vanden Bossche from GameSetWatch has article about storytelling through cutscenes or text exposition, and the often-superfluous nature of stories told in this manner. Good article:

http://www.gamesetwatch.com/2009/09/column_design_diversions_press.php

Shamus has a nice article where he defines "procedural content". He points out that there can be a difference between procedural content and "totally random content", and writes that Fuel does a nice job of having procedural content that is driven by an artistic designer. Having never played Fuel, I can't comment on that, but I did like the article:

http://www.shamusyoung.com/twentysidedtale/?p=5134
gdunbar
Just a couple of interesting links for the RPG designer today.

First, the Ink Knight finally continues his interesting series on interesting Cursed Items. Good stuff:

http://inkknight.blogspot.com/2009/09/give-your-cursed-items-reason-to-exist.html

Coyote has a list of ways to make a simple combat encounter more interesting. This is really good stuff for me; I think variety in combat strategies is something that can make an RPG so much more interesting for the player. Here it is:

http://rampantgames.com/blog/2009/09/rpg-design-seventeen-combat-encounters.html

That's all for now.
gdunbar
I've been thinking a bit about the overall structure, plot, and story fit together in CRPGs. That is, how does the game designer arrange the progress through the game. This includes things like what locations the game includes, and when the player will have access to them. What overall quest or quests the player is pursuing, and when these quests are revealed to him. How the story unfolds, and is revealed to the player.

The Adventure

Old school pencil-and-paper RPGs (Dungeons & Dragons and its ilk) have the unit of a game as an "adventure", maybe known as a "module". An adventure takes a few hours to play through, and generally advances your character a level or two. The theory being, you'll get some gaming buddies together for a couple of evenings of play, and play through the adventure. If, after that, you got together with a different group of buddies, you can still use your beloved character, and just play a different adventure.

Classic D&D module "Tomb of Horrors" (http://home.flash.net/~brenfrow/dd1/s1.htm):



Typically adventures are completely self-contained; they have a location or two (maybe a town and a dungeon), some sort of overall objective, and once the players obtain that objective, the adventure is over, and time to move on to the next one. Often multiple adventures share a setting (Greyhawk is the old-school D&D setting, but there are many others), but nothing more; no continued story or shared antagonists or anything (but see further down for exceptions).

This type of structure is rare in the CRPG world, for the simple reason that adventures are generally too short and unsatisfying for a full game. I played through "Witchs Wake", a Neverwinter Nights module that more or less fits the Adventure definition, and definitely felt unsatisfied and wanting more when it ended. (In that particular case, it wasn't helped that the story was clearly meant to continue but was never completed).

Witch's Wake (http://nwn.bioware.com/gallery/index.html?galleryID=7&screensize=2&screenimage=8):



Bag of Adventures

One thing a game designer could do is to simply release a "game" with a bunch of adventures, rated for different levels. The player could then pick and choose which adventures to play, based on his current character. There are actually some advantages to this, such as player-controlled difficulty scaling; if the player wants a less challenging adventure, he just picks a lower level, or maybe shorter adventure for his next task. However, you really lose the sense of continuity by just presenting the game as a series of unrelated adventures, and I've never seen a game take this approach.

One place that this might work well is as a multiplayer-only, online game. You get together with some buddies and play through an hour long adventure in an evening. The next night, you get together with some different buddies, and play a different adventure. I suppose this is something like playing an MMO that uses instanced dungeons, although MMOs have a whole extra set of interaction that goes far beyond this idea. Playing a Diablo game online is more what I'm thinking of, except of course Diablo has an overall structure to the game that is not broken down into nicely quantized pieces of evening-long gaming.

Diablo 2 (http://www.gamespot.com/pc/rpg/diablo2/images/0/2/):



Adventure Path

So, an adventure is too short to be satisfying; why don't we just link up a series of adventures to make the overall game? And in fact, this has been done, both in the pencil and paper world, and the CRPG world.

A few years ago, Dungeon magazine (the D&D adventures magazine) came up with the idea to link together a series of episodic adventures, published once a month, into an overall storyline; they called this an Adventure Path. The first one was called "The Shackled City", but it's been so popular that this practice has continued, both in the online Dungeon magazine and with Paizo's Pathfinder Adventure Paths. (Paizo was the old publisher of Dungeon magazine, which ended for reason too complicated to be interesting here, but readily available online).

Shackled City Hardcover (http://paizo.com/image/product/catalog/PZO/PZO1000_500.jpeg):



This format has clear advantages. The episodic nature means that the story is tightly controlled, so the game designer can tell a dramatic, well-formed story without worrying too much about the player wandering off track. The designer has a good idea of what level the player is at each step, so he can tailor the difficulty appropriately. And the players get the satisfaction of recurring characters, long-term objectives, and all those other things that are possible in a longer, more-developed campaign.

I find this format basically equivalent to some of the linear RPGs that have come out for computers; something like Icewind Dale. Now, Icewind Dale didn't explicitly sub-divide the game into a series of adventures, but it effectively did so; as a player you progress from one location or objective to the next, with no choice in the matter.

Icewind Dale (http://www.gamespot.com/pc/rpg/icewinddale/images/0/2/?tag=screenshot):



Super Adventures

There are drawbacks to the Adventure Path format. The player may feel railroaded, with little choice about what course he progresses on through the game. It is difficult to present a good exploration interface in an Adventure Path, because the game typically doesn't want the player to have choices about which location to go to; if there is an exploration phase, it's usually just an illusion where the player "explores" but there is only one location to find.

The game designer can take a somewhat more flexible approach; I saw this referred to as a "Super Adventure" in the latest D&D Dungeon Master's Guide. To quote "A super adventure is a type of short campaign- really one long adventure- that focuses on a single, limited setting." Some of the characteristics are:

  • The adventure takes place in a single setting (likely an extensive one).

  • The adventure allows some non-linear exploration.

  • The adventure involves different quests, objectives, and possibly even expeditions.


A classic old Super Adventure is the old adventure "Temple of Elemental Evil":

Temple of Elemental Evil (http://home.flash.net/~brenfrow/dd1/t1-4.htm):



Interestingly, some of the old D&D "Super Adventures" like "Queen of the Spiders", I would probably now characterize as an Adventure Path in the parlance of this article; they are much more linear than I see a Super Adventure being.

So, with the Super Adventure, we're giving the player a much freer reign in which actions and choices he makes; at its best he has complete control over what options and directions he takes. BUT, he can only do so within a limited setting with limited characters. This allows the game designer the ability to create a detailed setting to work in; he won't end up creating whole cities or dungeons that the player will never see.

A computer based example of this is "Pool of Radiance"; the old one, not the remake. This is a big game, with exploration and non-linear choices, but is all set within one city. (Actually I haven't played the remake so maybe that is a Super Adventure too).

Pool of Radiance (http://www.coverbrowser.com/covers/c64-games/24):



Open World Exploration

The very other end of the spectrum from the Adventure Path is for the game designer to give up all control of the player's actions, and just present a world for the player to do whatever he wants. Putting the greatest emphasis on the "role-playing" aspect of the game. Bethesda is the CRPG master of this, with games like Morrowind:

Morrowind (http://www.bethsoft.com/images/games/gamescrn_morrowind_02-B.jpg):



I think the PnP equivalent is not a published adventure at all, but a Dungeon Master who presents the setting, and then lets the players do whatever they want, making up the details as he goes.

Anyways, these games, done well, are incredibly immersive and addictive. They are also a game designer's nightmare; huge amounts of content to create, horrible balancing issues, difficulty in presenting any sort of coherent story to the player. As a player, you have to temper your expectations, sometimes; if you are trying to do something that the designers didn't plan for, you might end up disappointed that you can't achieve your goal, or that doing so ends up being boring or pointless.

Hybrid

To my mind, the very best of CRPGs have taken something of a hybrid approach. The combine a fairly large world or area to explore, along with a more structured plotline to follow. Tricky for the game designer to get right, but very rewarding to play. I would point to the Baldur's Gate Series or some of the Ultima games as examples of the hybrid approach done well.

Baldur's Gate (http://www.bioware.com/gallery/index.html?galleryID=28&screensize=3&screenimage=4):



Wrapup

So, there's a survey of the various structures a designer can take when designing the plotline of a CRPG. In writing this up, I found it really interesting how many great games I could think of in each category. As long as the game designer is careful in fitting his game progression and plotting to the overall structure, he can succeed with any of these approaches.

The RPG Anvil column deals with the design and development of CRPGs, and is published whenever I can convince my lazy ass to type one up.
gdunbar
This week I've been working on some updates with the level editor. Based on my experience with To The World Tree (http://www.prankster.com/ttwt), I knew that I had some issues; the level editor was slowing me down. Rather than muddle through as before, the best thing to do was to revamp the level editor now, saving valuable time in the future.

Here's a screenshot of "Layout mode", a new feature:



The features to do on my list:

  • Streamlined UI - Duh. Every fewer click I have to make is time saved.

  • Layout mode - In TTWT, I would do level layout in an image editor, and then look back and forth between the level image and the level editor as I filled in the details of the level. I may still do rough layout in an image editor, but the editor supports better layout operations.

  • Cut-and-paste - Previously, if I wanted to, say, move a room, even by one tile, I'd have to redo the whole room. Now I can just use the "move" edit operation to move things. This also helps make layout editing more convenient.

  • Skin mode - Given a level with just a raw layout, now I can select a region and apply a "skin", which fills in the proper tiles for that part of the level. Previously tile editing had to be done by hand.

  • Proper save - I'm embarrassed to admit, but the old level editor didn't actually save levels. Instead, it saved partial levels that had to be cut-and-pasted (in a text editor) into the actual level file. Easy to do once; sucky to do a hundred times.


So, I've got "Streamlined UI" almost done (just missing some keyboarding), "Layout mode" is done (see the screenshot above), and "Proper save" is done. "Cut-and-paste" and "Skin mode" are non-trivial to implement, but should be done next week. I can show you some good workflow screenshots once I have those ready.

gdunbar

Links: The Big Question

Good article from Rock, Paper Shotgun:

http://www.rockpapershotgun.com/2009/09/01/the-big-question/

About having, in an RPG, not just interesting quests, but quests that have meaningful consequences on the game and story.

I agree with this sentiment, and want to keep it in mind when designing "Untitled SENG Game". But there are two caveats:

  1. You can't have too many meaningful choices, or you'll end up with a giant game decision tree, and correspondingly, too much game content to make.

  2. When I'm playing a plot-full RPG, I hate it when I make a choice in conversation that has deep meaningful consequences, only it wasn't clear before I made the choice that I was deciding something really important. I prefer it to be crystal clear (maybe even belabored) that the choice I am making is going to effect the path my game takes (like ruling out other quest paths, storylines, or character advancement options).


Anyways, I wanted to point out the article, and personally to keep it in mind when doing my game design.
gdunbar
Time for another update on Untitled SENG Game. I've been squirreling away on engine changes; in the last couple of weeks I've implemented (well, re-implemented) the way items work in combat, and added a conversation journal.

I had a small bullet on my "To-Do" list; make items work with the new RPG system. That quickly ballooned into a bunch of changes, spider-webbing through much of the code. However, by the time I was done, the programming for items was much simpler and less error prone, and as a fortunate side-effect, I got magic rings, amulets, and helmets working; a "To-Do" that has been sticking around since the "To The World Tree" days (http://www.prankster.com/ttwt).

The sexiest thing I implemented- well, look at this screenshot:



You can see that the newly implemented test item "Poison Dagger" has a Spell Attack "Poison Burst". Items can now have a Spell Attack which attacks an actor's Saving Throws instead of their Defense Points (the SENG equivalent of "Armor Class"). A magic item like this will do half the "normal" damage with its standard attack, but then another half with the Spell Attack.

And, in this screenshot:



You can see the Poison Dagger generating a particle effect when it hits! Cool, huh? The particle effect is just the same as from the spell-casting system, but later I'll customize so the particles look different (while using the same code).

As for the conversation journal, this is just a log of conversations that your party has had with "interesting" NPCs. I've often wished for just such a feature in RPGs I've played; most of them by now provide a pretty good journal of Quests, but I'm often frustated by the lack of a conversation log. Plus I included an entry for the last location that you encountered the NPC. I don't know if it's happened to you, but I've had times where I'm playing an RPG intermittantly, and I come back to a Quest log that says "Give Grobnar the Frozzle", but I've completely forgotten where Grobnar was.

Anyways, here's a screenshot of that:



My "To Do" list for this round of engine updates is really shrinking now; things are looking good. A few more weeks to go certainly (and I have a sneaking suspicion that if I did a real schedule it would be at least a month), but definitely getting there.
gdunbar

RPG Anvil: RPG Length

I was reading Rampant Coyote's blog, and stumbled upon this post:

http://rampantgames.com/blog/2009/08/how-long-should-rpg-be.html

Where he asks, "How long should an RPG be?" (Specifically, in hours of play.) Interesting question, certainly. But, say you've already decided how long your RPG should be... how do you actually come up with a game design that fits that time budget?

I figure for "Untitled SENG Game", I'd like the average game to take about 40 hours of play. I can break it down further; I'd like 20 hours for the main plotline (and associated quests), and another 20 hours for optional quests. I also figure there should be about another 20 hours of optional quests that the player doesn't do in any particular game, by choice or because of availability (for instance, if you do the quest to become Chief of the Assassin's Guild, you probably can't also do the quest to be the High Priestess of the God of Law and Order). So, given that budget, how can I design the game to take that long?

This is an especially tricky question, because you need to get it right (or nearly so) _before_ doing extensive level design, and even overall design. Some questions, like game difficulty, you can generally tweak near the end, after doing playtesting. But if you get the game length wrong; say you only have half of the game-time that you wanted, then you are screwed. You need to somehow insert double the gameplay into the existing game. Whether you are making areas bigger, or inserting new areas, or whatever, you are going to be touching virtually the entire game, and it is going to take _forever_. So you really need to get this question right, or mostly so, up front.

Luckily, you don't need to get the answer perfectly right, as you do with other design questions. For example, if you design the game so that the final boss is correct for a level 20 player character, if you are off in your design by just a little, and the player has a level 22 character (just a 10% miscalculation), the final, pivotal encounter may be way too easy. But, for game-time, no-one will much notice the difference between a 20-hour game and a 22-hour game, or probably even a 30-hour game.

So, in a SENG game, I can think of 6 major contributors to game-time:

  1. Initial character creation.

  2. Talking to NPCs; exposition, quest assignment, and the like.

  3. Character development; fooling around with stores, inventory, and level-up choices.

  4. Level exploration.

  5. Combat.

  6. Replaying a failed quest.


NPC conversation in Icewind Dale, from http://www.gamespot.com/pc/rpg/icewinddale/images/0/44/:



Certainly for points 2, 4, and 5, I can directly control how much of each one a player will take part in during the game. So, if I can come up with an estimate of, for example, how long an average combat will take, then I can just put in the design that the player will take part in N combats, and that tells approximately how much that part of the game will contribute to the overall game-time. More on that in a minute.

Point 1 (character creation) probably isn't a big factor in the game-time, so I'll just ignore it. Point 6 (replaying failed quests or encounters) better be pretty minimal, or I've failed in other parts of the design. If the player has to frequently replay areas, I suspect he will become frustrated with the game and give up. Point 3 is more troublesome; certainly in any RPG I spend lots of time agonizing over inventory and character development choices, but it's pretty hard to measure that. Reluctantly, I think I'll just ignore point 3 because I can't think of a good way to measure it.

So, I think the thing to do, before doing the full design of areas, encounters, and quests, is to come up with some small, representative demos of:

  • NPC conversation.

  • Level exploration.

  • Combat.


Level exploration in Morrowind (http://www.elderscrolls.com/art/mw_screenshots_01.htm):



For combat in particular, I think I will need to have a couple of demos, for low, medium, and high-level play; although SENG theoretically scales combat to be somewhat similar across levels, I should make sure that's really true.

Then I need to play through each demo a couple of times, trying to use a couple of different styles ("Mr. Impatient", "Mr. Cautious", etc), and see how long it takes. Note that this is one case where I _don't_ think playtesters would help much (at least for the indie developer), for the simple reason that a playtester running through a level for the first time is going to take much longer than a playtester going through his 10th, or even 2nd, level. The only way to get good data on what an experienced player would do, would be to make a _lot_ of test content, first to train the playtesters, and then to get the good data. I don't think I have the time or patience to make that much test content, and I'm pretty sure free playtesters wouldn't have the patience either. Anyways, I digress.

Then, simple math should give me a game-time estimate, given:

  • Number of NPCs to converse with.

  • How much depth each conversation tree has.

  • Number of Levels.

  • Average size of each Level.

  • Number of combat encounters.


Combat in Neverwinter Nights (http://nwn.bioware.com/shadows/screens.html?galleryID=14):



Thus, I should be able to have a budget of each of these items _before_ performing high-level game design, and definitely before doing the detailed level design. That should give a reasonable approximation of game-time.

After that, it's probably a good idea to do a little more testing during level design; say after 1/4 of the levels have been completed, just to be sure we're in the right ballpark. At this point it will be easier to get a good estimate for gametime; just multiply the time for 1/4 of the levels times 4. If the number is way off, we're kind of screwed, as we're going to have to seriously rework the 1/4 of the levels, but at least we didn't finish them all.

And then final playtesting should definitely include a measure of game-time for each playtester. At this point it will be hard to do more than tweak the number a little bit (one way or the other), but it is important data to have.

So that's my plan for game-time calculation. If a designer gets a good grasp on this issue early on, it shouldn't be much of an onus during the design. On the other hand, if a designer doesn't consider the problem from the start, it could completely derail game development, possibly even in the final playtesting stages.
gdunbar
This article:

http://www.gamasutra.com/view/feature/4111/dirty_coding_tricks.php

Really caught my fancy. It has a set of short features on last minute hack/bugfixes that programmers put in to quickly fix an issue just before shipping, often in horrible ways. Probably incomprehensible to the non-programmers out there, but I enjoyed it!

I don't have any stories quite as good as the ones in the feature, though I have put in my share of hacks in 16 years as a (professional) programmer. Some of that could be because I've been working on more mainstream software; we've generally taken the time to understand and fix bugs properly instead of doing the easiest (and least maintainable) thing.
gdunbar

Links: Beta Testing

Something that has been weighing on my mind for awhile is the question of how to run a decent Beta test (or any sort of test) for an independent, single-player RPG. Specifically, my RPG, still titled "Untitled SENG Game". I'm approaching the point where I'll have some gameplay prototypes ready, and I'd like to have some unbiased people take a look at them. As it turns out, I've seen a couple of decent posts on beta testing, so I thought I'd share.



First, Amanda Fitch of Amaranth Games (http://www.amaranthia.com) posted her guide to selling indie games:

http://www.amaranthia.com/downloads/How-to-Sell-your-Game.pdf

This is a nice, short, nuts and bolts guide to the details of actually selling your game once you've made it, with a short section on Beta testing.



Next, Jeff Vogel (Spiderweb Software - http://www.spiderwebsoftware.com/) has a really nice, detailed blog post (on the Bottom Feeder - http://jeff-vogel.blogspot.com/)on running a Beta test:

http://jeff-vogel.blogspot.com/2009/08/beta-testers-getting-them-keeping-them.html

This is really helpful and worthwhile; the man has done this sort of thing before!



I have two experiences with running a Beta test for an indie game. First, I ran a long and extensive test for my play-by-email game Atlantis (http://www.prankster.com/project/index.htm). I had several hundred people playing, which made for an excellent testing atmosphere, plus two other things really helped. First, as a server-based game, I had complete access to the game as it was going on; I could see what orders the players were issuing, complete reports on their positions, etc. Basically I had all the data (and more) that I could want for doing testing. Second, as a multi-player online game, the players were already used to lots of discussion, with mailing lists, a weekly newsletter, etc. Getting feedback or bug reports wasn't a problem at all; the problem was distilling all the information into something helpful!



My other experience was testing my free SENG demo "To the World Tree" (http://www.prankster.com/ttwt/index.htm). My experience here couldn't have been more different from Atlantis. Since it was just a demo, I did not set up a formal testing program; instead I just released it and asked people to send feedback. This was a mistake; I don't really know how many people played, though it was downloaded thousands of times, who knows who actually played the game? I got a few really detailed feedback forms with lots of helpful information. I got more (but still not a ton) of limited feedback; this feedback could generally be distilled into either "This sucks!" or "Attaboy!". But mostly I just got silence.

So, I have two thoughts going forward as far as testing. The first is definitely to create a small but active community for testing. Only allow the testers access to the game bits (don't just release a demo into the wild), and really strongly try to interact with them (and even get them to interact with each other). I suspect some combination of an announcement mailing list and a forum is the best way to go.

Next, some sort of real, hard data from the game would be nice, too. I don't want to try to get super-high-tech (because that would take a lot of effort), but perhaps the game could periodically offer the player a log file that he could email to me or something. Maybe a web-form would be even easier. This log file wouldn't help much with bug-fixing, but if I logged the right things, I could get a good idea of how the player is approaching the game. Which class he is playing, which quests he is doing at what levels, what monsters are particularly easy or difficult, etc. That kind of detail would be really nice to have, and it is pretty hard to extract from the player directly, without actually watching him play.

Those are my thoughts; if nothing else I really recommend you read Vogel's piece for good solid advice. Depending on my work plans, I may be getting into some sort of testing program this fall.
gdunbar
Just a couple of random links today.

First, this neat site:

http://maps.bpl.org/history/

Has a huge collection of historical maps available online. So, for instance, if you want a map from 1482 of Asia Minor (in Latin), here's your thing:

http://maps.bpl.org/details_M0612036/?mtid=790

Whether or not you can use such a resource for making a game, it is just cool.

Second, Shamus has started a new series on AI programming:

http://www.shamusyoung.com/twentysidedtale/?p=4085

Shamus is usually entertaining and sometimes informative, so I look forward to reading his thoughts. When he's gotten further into his thoughts I may post my own experience from SENG development, but for now, enjoy!

gdunbar

Links: Death and RPGs

Andrew Doull has a nice post on permadeath; that is, where dying in a game means you have to restart the game again from the beginning:

http://roguelikedeveloper.blogspot.com/2009/07/permadeath.html

This is a common feature of roguelike games, Angband, NetHack, and the like (not coincidentally, Andrew is the developer of an Angband variant, UnAngband). You maybe be more familiar with the permadeath game-style in something like Diablo 2, Hardcore mode. To be clear to those who aren't familiar with playing a permadeath game, even going back to an old savegame is not allowed, or at the least, is considered cheating.



In an RPG, there are 2 key requirements for a game to successfully implement the permadeath feature. The first is some degree of randomized, emergent gameplay. That is, even if you are playing the start of the game for the tenth (or hundredth) time, the game is different, with randomized levels, different monster configurations, and so forth. If you're going to be replaying the same part of the game over and over, there probably needs to be some need new and novel aspect each time.

The other requirement is that, from game to game, a player's (not character's) skill increases; he gets more proficient, learns more, and becomes more adept at the game. This allows him to progress farther in the game, and he gets satisfaction that he is improving, with visceral proof in his in-game achievements.

A game that meets these requirements, and implements the permadeath feature, can provide a level of satisfaction and challenge that is unmatched in other games. When I finished, say, Morrowind, I felt happy and satisfied, but when I won Angband - man, I really felt like I had accomplished something.

That said, permadeath games can be extremely frustrating. I would even say that a lengthy permadeath game like Angband has a very limited audience. Diablo 2 works around this issue by making Hardcore mode optional; I would guess that almost all Hardcore players had played the game through on an easier mode first. And, for a story-driven game like an Ultima or a Bioware game, permadeath would be completely inappropriate; who wants to read the first chapter of a book 100 times?

So, the common approach in story-driven games is, at least least, to allow re-load of a game from an old save-file in the event that your character dies. Some games like some of the Ultimas, or Planescape Torment, simply bring your character back to life, and you continue to play; in the case of Planescape Torment, with no penalty at all. With "Untitled SENG Game" (and the SENG engine in general), I take this approach as well; if you die, the engine simply restores your character at a nearby "safe point" and you proceed. This is how most players play anyways; why make the futz around with game saves and restores to achieve this?



How, in this "death means nothing" design, can we maintain the twin senses of achievement when the player attains something, or failure when his character dies? I've put some thought into both of these.

First, in the case of failure, my preferred design is to leave the sense of failure up to the player. That is, there is no in-game penalty for dying, no quest that now can't be completed, nothing. How can I justify this? Well, personally, when I'm playing a long, engrossing role-playing game, and my character dies, I fel bad. I'm invested in my character, I want him to succeed; even in games where I can simply restore an old game, I still hate to die. So, my design is relying on the immersion of the game and its characters to provide a disincentive to death; I can only say that this is personally effective for myself, and can hope that this applies to other players as well.

Secondly, for the sense of challenge, well, I'm afraid I may have lied a little bit. I claimed that there was no penalty for death. While strictly true, there is something of an in-game disincentive; let me explain. A SENG game subdivides the overall challenges of the game into sub-achievements, which I call quests. Encounters in the game are associated with quests, in such a way that if the quest is complete, the encounter is removed from the game. However, if the quest is incomplete, the player resting (to restore characters hit points, etc), or dying, will also restore any encounters associated with that quest, even if the encounter was previously defeated.

Let me give an example. Say "Slay the Goblin King" is a quest. Then, the level "Goblin Warrens" is packed with goblins (subgrouped into encounters), with the Goblin King himself somewhere near the back. If the player works his way into the warrens, killing off half of the goblins, but then retreats to rest, or dies, the goblins are restored to the level. Only if the player can penetrate all the way and kill the King do the goblins stay dead.

In this system, there is are distinct, challenging achievements. Completing a properly levelled quest should be a big deal to the player. Failure means that he has to replay the quest. Note that good level design is imperative to ensure that this system does not lead to frustration; while it is OK for a player to occasionally fail and have to replay a quest, this should not happen too often. The player should be led towards the right quests, steered away from too-difficult quests, and long quests should be broken into sub-quests to avoid long replay sessions.

As an RPG designer, you need to think hard about the death problem. Whether you go for a permadeath approach, a no-penalty approach, or somewhere in between, will heavily influence the player experience. With SENG, I'm trying to provide a more seamless, player-friendly approach than games that have gone before; with your game, you may want to take a different tack.
gdunbar
I've had some time over the past week to do some good coding, and I've got some lingering features done in the SENG engine.

The first of these was "map discovery". Previously, when you entered an area, you could see everything in it, both onscreen and in the map. Even stuff behind closed doors, etc. Now, one of your characters needs to get close enough (100 feet), and you have to have a clear path, in order to see something. Here's an example.

Tester, with the door closed, and the tiles behind obscured:



Tester opens the door, revealing the room behind. Note also that the monsters notice her and attack:



I didn't implement line of sight, or anything complicated; just straight-up breadth-first-search. One obvious benefit is that I can design levels with hidden details behind doors. Less obvious is that previously monsters would "trigger" and try to attack the player, even through walls, leading battles not taking place in the right spot. Now, monsters only attack after a room has been revealed; much nicer.

Implementing map discovery took a little longer than I had hoped because of performance issues; with my first change, I dropped to about 10 frames per second. After some good optimization, I finally realized that in the Release build (as opposed to the Debug build that I always run), there really wasn't much of an issue at all. But I left the optimizations in anyways.

The other changes were more minor. One was to revamp the way traps work. Previously a trap was a property of a tile, which wasn't great, but got even worse when I dropped the tile size from 10 feet to 2.5 feet. For instance, your thief might disarm a trap, but your fighter right next to him would trigger it since the tile size was so small. Now traps are objects, which have arbitrary size, so if the thief disarms the trap, it rightly disarms the whole thing. In the process I fixed up some other trap issues.

The last change was to implement an "Exit" object, to make exits more visible and functional. If you played "To The World Tree" (http://www.prankster.com/ttwt), you probably have some idea of the confusion that this could clear up.

Anyways, good progress for now. I'm really starting to see the light at the end of the tunnel as far as coding changes to the engine. Unfortunately for indie game development, I'm probably going to take a job in a month, which will cramp things. But I suppose my bank account (and wife) will be pleased.
gdunbar


A little while back, Gamasutra (http://www.gamasutra.com) had a feature on Essential RPGs that a game designer should play. Excellent feature:

http://www.gamasutra.com/view/feature/4066/game_design_essentials_20_rpgs.php

Let's see how I stack up! Bold if I've played it, Italics if I haven't.

Bonus:

  1. Dungeons & Dragons (PnP) - Haven't played in, oh, 25 years (not counting computer versions), but definitely where it started for me.


Western:

  1. Wizardry (series) - Not sure why this made it instead of other like minded games like Bard's Tale. I guess the series went on longer. I'm guessing not worth going back to play now.

  2. Ultima (series) - I've completed 4 (a classic), and played 3-7. I really should go back and play 7 all the way through.

  3. Wasteland - Awesome, awesome game. The setting (post apocalyptic) made it really novel.




  4. D&D Gold Box series - I finished "Pool of Radiance", and played another one or two. These were competent, but less engrossing for me than some others.

  5. Quest For Glory - I played a bunch of the Sierra adventure games, but not this one.

  6. Might & Magic - Played an older one, never finished, didn't really make a big impression.

  7. Nethack - Never really caught the Nethack bug; preferred Moria or Angband, or even old-school Rogue.

  8. Elder Scrolls (series) - Finished Morrowind. Fooled around with Daggerfall, but after it was too dated to be much fun. Waiting for a more powerful laptop before I take on Oblivion.

  9. Baldur's Gate (series) - Still the best single player RPG I've played. I'll avoid waxing poetic so as not to bore regular readers, who have probably heard enough about BG already.




  10. World of Warcraft - Can you believe I've never played this? I really should, just to check out the craftmanship if nothing else. But I never have had the time or desire to get sucked in.


Japanese:

Well, I haven't played a single one of these, so no point making comments. Here's the list:

  1. Dragon Quest

  2. Dragon Slayer

  3. Fire Emblem

  4. Final Fantasy

  5. Mother, a.k.a. Earthbound

  6. Pokemon (series)

  7. Mystery Dungeon

  8. Square's 16-bit RPGs

  9. Tales games (series)

  10. Nippon-Ichi RPGs


I suppose I should go back and play one of the Japanese RPGs, just to see what's up. Final Fantasy 7, maybe?

Anyways, 8 for 21. What kind of an RPG designer do I think I am, anyways?
gdunbar

Back from Vacation

I was on vacation in Maine for the past two weeks. Technically I had Internet access, but I stayed away from everything except email. No programming either.

So, not much to report from the past two weeks. I did notice another Dev Journal pop up here at GameDev, which I've added to my list of blogs:

http://members.gamedev.net/EDIGames/journal/

EDI talks about the development of his Selenite Engine; I always enjoy reading about game engine development (and once in awhile, writing about it). While his engine is more general-purpose than my SENG engine, it is neat to see how other developers have attacked some of the problems involved. I may have some more detailed posts on topics that he's posted about, but for now just the link.

Otherwise, back to SENG development.
gdunbar
RPG Anvil: The Lua Tutorial Final

lua.gif

Well, I thought I'd have some final thoughts on the tutorial here, but really at this point, it stands (or falls) on its own.

My Lua tutorial showed:
gdunbar


Today, how to handle errors in your script-called functions.

The previous installments of this tutorial are here:



There is one last important point to consider in integrating Lua into your engine. Since Lua scripts exist somewhere in your game data, it is entirely possible that your scripts will contain some errors; in fact, if you do any significant scripting, it is a virtual certainty. In this case, probably the ideal behavior is to abort the script but keep running the game. You may also want to log the behavior or something for debuggging, but the most important thing is that if an end-user hits the script error, she has a chance to keep playing the game.

The code presented in previous installments of this tutorial properly handled simple things like simple syntax errors in a Lua script. However, there is also the case where the Lua script causes some error in C++ code. As an example, in the lua_GetLockpickLevel function (called directly from Lua), I might have something like this:

        if( pData->m_iLockpickLevel < 0 )
{
throw( std::exception( "logic error" ) );
}


In the tutorial from Part 4, this would cause the program to abort if the exception is thrown. Not so nice for the player! To try this out, you could change the initialization of CGameData in the main function to something like:

        CGameData Data( -1 );


Now, I could just handle this exception after calling Lua (by putting the whole Lua-calling code into a try-catch block), and attempt to continue to run. However, Lua is C-code, with its own error handling mechanism, and firing a C++ exception through that is probably not a great idea. I'm not entirely sure whether this approach would work or not.

Instead, my suggested approach is to handle the exception in the Lua-called C++ function, and translate it into a Lua error. Basically, we enclose the whole function (lua_GetLockpickLevel in the tutorial) in a block like this:

    try
{
...
}
catch( std::exception const &e )
{
lua_pushstring( luaState, e.what() );
lua_error( luaState );
}


Then, your C++ error is just turned into a Lua error, and it appears to the outer code as any other Lua error. This error handling code already exists in the previous tutorial code; basically we check status code when calling luaL_loadstring or lua_pcall.

I should also mention that if your C++ code handles errors in a different mechanisn, say return values (HRESULT, maybe), this same approach applies. Take the error in your Lua-called C++ function, and translate it into a Lua error via lua_pushstring and lua_error. This will enable your code to properly handle this failure as a script error.

Here's the final tutorial code:

// luatest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include
#include

class CGameData
{
public:
CGameData( int i ) { m_iLockpickLevel = i; }
int m_iLockpickLevel;
};

char *szLua =
"function game_door_check( context ) "
" local x = GetLockpickLevel( context ) "
" return ( x > 7 ) "
"end ";

int lua_GetLockpickLevel( lua_State *luaState )
{
try
{
int nArgs = lua_gettop( luaState );
if( nArgs != 1 )
{
throw( std::exception( "Script Error 1" ) );
}
CGameData *pData = (CGameData *) lua_touserdata( luaState, 1 );
if( !pData )
{
throw( std::exception( "Script Error 2" ) );
}
if( pData->m_iLockpickLevel < 0 )
{
throw( std::exception( "logic error" ) );
}
lua_pushinteger( luaState, pData->m_iLockpickLevel );
}
catch( std::exception const &e )
{
lua_pushstring( luaState, e.what() );
lua_error( luaState );
}
return( 1 );
}

int _tmain(int argc, _TCHAR* argv[])
{
lua_State *lState = NULL;

try
{
lState = luaL_newstate();
luaL_openlibs( lState );

lua_register( lState, "GetLockpickLevel", lua_GetLockpickLevel );

int iStatus = luaL_loadstring( lState, szLua );
if( iStatus )
{
std::cout << "Error: " << lua_tostring( lState, -1 );
throw( std::exception( "script error" ) );
}

iStatus = lua_pcall( lState, 0, 0, 0 );
if( iStatus )
{
std::cout << "Error: " << lua_tostring( lState, -1 );
throw( std::exception( "script error" ) );
}

CGameData Data( 8 );
lua_getfield( lState, LUA_GLOBALSINDEX, "game_door_check" );
lua_pushlightuserdata( lState, (void *) &Data );
iStatus = lua_pcall( lState, 1, 1, 0 );
if( iStatus )
{
std::cout << "Error: " << lua_tostring( lState, -1 );
throw( std::exception( "script error" ) );
}

int iRet = (int) lua_toboolean( lState, -1 );
if( iRet )
{
std::cout << "Door opened!" << std::endl;
}
else
{
std::cout << "Door still closed." << std::endl;
}
}
catch( std::exception const &e )
{
std::cout << e.what() << std::endl;
}

if( lState )
{
lua_close( lState );
}

return 0;
}


OK, at this point, the tutorial is done. We are:

  • Linking and initializing Lua.

  • Loading a Lua script from a string.

  • Running a Lua script from our C++ code.

  • Calling a C++ function from Lua.

  • Accessing our C++ game data in Lua.

  • Properly handling scripting errors.


Hopefully this has been helpful! I'll have some final thoughts in the next post.
gdunbar


Today, how to access C++ objects when Lua calls your C++ function.

The previous installments of this tutorial are here:



The goal of the tutorial is for our C++ game code to call into a Lua script to determine whether the player can open a door or not. Last time, we called the Lua script to make the check, and the Lua script called back into C++ code to get the player's Lockpicking Skill Level. However, in a cliffhanger of dramatic proportions, I pointed out that just calling into C++ wasn't enough; we also need to have access to the game's C++ objects. Today, I show how to get access to the game's data from the C++ code that Lua calls.

To frame the problem, we have this Lua script:

char *szLua =
"x = GetLockpickLevel() "
"return ( x > 7 ) ";


And, in C++, we have some game data. For this demo, we'll make the data as simple as possible, though the method I'll describe for getting at the data is universal; we can get any class (or classes) into the Lua-called code. Anyways, a super-simple class that describes the player's Lockpick Level:

class CGameData
{
public:
CGameData( int i ) { m_iLockpickLevel = i; }
int m_iLockpickLevel;
};


The problem is: How can the C++ function GetLockLevel access our game's CGameData object?

You could simple store a pointer to the CGameData in a global (or static) variable. However, unless you are writing the simplest of toy game demos, I advise against it. I don't want to launch into a screed against global variables in C++ here, but suffice to say there are good reasons against using global variables in most cases, both stylistic and practical. I'm sure a Google search can turn up more reading than you'd care to do.

A much better approach is to associate the CGameData pointer with the lua_State; every function called by Lua has access to the lua_State (which basically represents the Lua virtual computer that runs Lua code). Lua even has a nice type for this exact purpose; "light userdata". The functions lua_touserdata and lua_pushlightuserdata support this type, and allow our C++ code to put "light userdata" in and out of the Lua state.

One particular place you could store the CGameData pointer (as "light userdata") is the "Registry". The Registry is a Lua concept for storing data for C code, and is distinct from the Windows Registry or anything else like that. Lookup Registry in the Lua documentation, or the LUA_REGISTRYINDEX constant in the headers. However, I've actually chosen a different mechanism; the Lua Registry is OK, but there are some issues. If you end up using reentrant scripts (where one Lua script ends up calling another Lua script through C++), or re-using Lua states, it's possible you could have conflicts in the Registry, where you end up overwriting one CGameData pointer with another. Even if you aren't doing that now, I'd go a different way, just to keep my code future-safe.

Instead, I prefer the "context" paradigm, common in C code, particularly pseudo-object-oriented C code. Basically, we'll pass a context object pointer to the Lua script on the stack, and when the Lua script calls a C++ function, it will pass the context back to us unchanged. The context contains any data (or pointers to data) that our C++ function will need.

In the case of this demo, I'll just pass the CGameData pointer as the context. However, you can easily imagine more data in the context; if we extended the demo a little further, we might pass a pointer to a CDoor object to the script, with data about the door the player is trying to open. If we had to call Lua reentrantly, we could use a separate context object, with different data in it, so the C++ functions called from Lua can do their work properly.

OK enough chatter. To make the context concept work, we actually have to change our Lua script slightly; here's the new one:

char *szLua =
"function game_door_check( context ) "
" local x = GetLockpickLevel( context ) "
" return ( x > 7 ) "
"end ";


As you see, the Lua script now takes a context parameter, and passes it on to the GetLockpickLevel function. Variables are loosely typed in Lua, so there is no problem passing light userdata around like that, even though the Lua script doesn't understand the type at all.

Here's the simplest possible GetLockpickLevel implementation:

int lua_GetLockpickLevel( lua_State *luaState )
{
CGameData *pData = (CGameData *) lua_touserdata( luaState, 1 );
lua_pushinteger( luaState, pData->m_iLockpickLevel );
return( 1 );
}


In this case, we get the light userdata from the stack (with lua_touserdata) in position 1. Then we push the return value, from pData->m_iLockpickLevel, onto the Lua stack, and return the number of return values; 1 in this case.

However, that is actually a bad implementation. A simple typo in your script will crash your program. I highly recommend that you carefully check for errors, so you can quickly diagnose scripting issues (plus my pet peeve is tutorials that ignore error conditions). Something like this is much better:

int lua_GetLockpickLevel( lua_State *luaState )
{
int nArgs = lua_gettop( luaState );
if( nArgs != 1 )
{
lua_pushstring( luaState, "Script Error 1" );
lua_error( luaState );
}
CGameData *pData = (CGameData *) lua_touserdata( luaState, 1 );
if( !pData )
{
lua_pushstring( luaState, "Script Error 2" );
lua_error( luaState );
}
lua_pushinteger( luaState, pData->m_iLockpickLevel );
return( 1 );
}


Now, astute readers will have noticed that my new Lua script has a function statement that wasn't there before. You can read all about functions in the Lua documentation; in this case I needed to do this so I could pass the context variable (the light userdata) in to Lua. This adds a complication that, from C++ code, I need to first run the script to create the function, and then, as a separate step, call the function. To run the script, after the old call to luaL_loadstring:

    iStatus = lua_pcall( lState, 0, 0, 0 );
if( iStatus )
{
std::cout << "Error: " << lua_tostring( lState, -1 );
return 1;
}


Basically, we call the script, with no parameters or return values.

Now, to call a function, we first have to get the function and put it onto the Lua stack. Note that in Lua, functions are just another kind of object, so we retrieve the function, based on its name, from the Lua global namespace, like so (note that lua_getfield retrieves the function and puts it on top of the stack):

    lua_getfield( lState, LUA_GLOBALSINDEX, "game_door_check" );


Next, we need to put our CGameData pointer on the Lua stack as light userdata. (This will be the context parameter in the Lua script):

    CGameData Data( 8 );
lua_pushlightuserdata( lState, (void *) &Data );


Then, we tell Lua to run. Since the stack is configured with the parameter on the top, and the function next, we just tell Lua to run with one parameter, and it is smart enough to do things properly:

    iStatus = lua_pcall( lState, 1, 1, 0 );


The Lua documentation for lua_call has a more thorough description of how and why this works.

The remainder of the code, retrieving the return value, remains the same. Here is the complete code listing to this point:

// luatest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include
#include

class CGameData
{
public:
CGameData( int i ) { m_iLockpickLevel = i; }
int m_iLockpickLevel;
};

char *szLua =
"function game_door_check( context ) "
" local x = GetLockpickLevel( context ) "
" return ( x > 7 ) "
"end ";

int lua_GetLockpickLevel( lua_State *luaState )
{
int nArgs = lua_gettop( luaState );
if( nArgs != 1 )
{
lua_pushstring( luaState, "Script Error 1" );
lua_error( luaState );
}
CGameData *pData = (CGameData *) lua_touserdata( luaState, 1 );
if( !pData )
{
lua_pushstring( luaState, "Script Error 2" );
lua_error( luaState );
}
lua_pushinteger( luaState, pData->m_iLockpickLevel );
return( 1 );
}

int _tmain(int argc, _TCHAR* argv[])
{
lua_State *lState;

lState = luaL_newstate();
luaL_openlibs( lState );

lua_register( lState, "GetLockpickLevel", lua_GetLockpickLevel );

int iStatus = luaL_loadstring( lState, szLua );
if( iStatus )
{
std::cout << "Error: " << lua_tostring( lState, -1 );
return 1;
}

iStatus = lua_pcall( lState, 0, 0, 0 );
if( iStatus )
{
std::cout << "Error: " << lua_tostring( lState, -1 );
return 1;
}

lua_getfield( lState, LUA_GLOBALSINDEX, "game_door_check" );
CGameData Data( 8 );
lua_pushlightuserdata( lState, (void *) &Data );
iStatus = lua_pcall( lState, 1, 1, 0 );
if( iStatus )
{
std::cout << "Error: " << lua_tostring( lState, -1 );
return 1;
}

int iRet = (int) lua_toboolean( lState, -1 );
if( iRet )
{
std::cout << "Door opened!" << std::endl;
}
else
{
std::cout << "Door still closed." << std::endl;
}

lua_close( lState );

return 0;
}



(Sorry, the code is getting pretty long at this point. I do think properly handling errors is important, which adds some verbosity to the code).

Well, at this point, we have fully functional code. Yay! There is one more thing I want to cover next time, and that is handling C++ exceptions properly. And after that I'll have some last notes and thoughts.
gdunbar


Today, how to call a C++ function from your Lua script.

The previous installments of this tutorial are here:



As you may recall from previous installments of this tutorial, the goal of the tutorial is for our C++ game code to call into a Lua script to determine whether the player can open a door or not. Today, we'll call a Lua script, which will then call back into C++ code to get the player's Lockpicking level, to determine whether or not the player can open the door.

First, let's update our Lua script:

char *szLua =
"x = GetLockpickLevel() "
"return ( x > 7 ) ";


Previously, the script just did "x = 8"; now we call a function to get the player's Lockpicking level. As you'll see below, this is a C++ function.

Here's the C++ function:

int lua_GetLockpickLevel( lua_State *luaState )
{
lua_pushinteger( luaState, 6 );
return( 1 );
}


Lua C++ (or C) functions always get the lua_State as their one parameter; this is the object that allows us to manipulate or examine the Lua virtual machine. As you can see, we use this state to push the return value onto the Lua stack, via lua_pushinteger. (In this case, I'm returning a fixed value of 6). Lua C++ functions return an integer, simply the number of return values. Since I pushed one return value onto the stack, I return 1.

One last step, we need to tell Lua about the lua_GetLockpickLevel function, so Lua knows what C++ function to call. We do this right after initializing the Lua state:

    lua_register( lState, "GetLockpickLevel", lua_GetLockpickLevel );


Build and run the code, and you should get the message "Door still closed", since the player's Lockpick skill of 6 doesn't allow him to open the door (the Lua script requires greater than 7). Here's the complete listing of luatest.cpp:

// luatest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include
#include

char *szLua =
"x = GetLockpickLevel() "
"return ( x > 7 ) ";

int lua_GetLockpickLevel( lua_State *luaState )
{
lua_pushinteger( luaState, 6 );
return( 1 );
}

int _tmain(int argc, _TCHAR* argv[])
{
lua_State *lState;

lState = luaL_newstate();
luaL_openlibs( lState );

lua_register( lState, "GetLockpickLevel", lua_GetLockpickLevel );

int iStatus = luaL_loadstring( lState, szLua );
if( iStatus )
{
std::cout << "Error: " << lua_tostring( lState, -1 );
return 1;
}
iStatus = lua_pcall( lState, 0, 1, 0 );
if( iStatus )
{
std::cout << "Error: " << lua_tostring( lState, -1 );
return 1;
}

int iRet = (int) lua_toboolean( lState, -1 );
if( iRet )
{
std::cout << "Door opened!" << std::endl;
}
else
{
std::cout << "Door still closed." << std::endl;
}

lua_close( lState );

return 0;
}


So, we called a Lua script, and it called C++. We're done, right? Not so fast! Right now, lua_GetLockpickLevel returns a fixed value of 6. In a real game, we would want to look in some structure or class to get that value. Since all it has access to is the lua_State, how can it get the "real" value? Find out in the next thrilling installment of "RPG Anvil: The Lua Tutorial"!