Friday, January 16, 2009

A neat Ask/Tell change

* EDIT * In order to alleviate some reading confusion, the below post is in regards to Risorg2, a sequel to my first game of Interactive Fiction, Risorgimento Represso. Specifically, this post deals with some methods I came up with during development for simplifying Ask/Tell conversation topics for characters within the game.

I struggled with my original Risorg with the idea of being able to ask characters about everything in the game. I go for the complete experience, and it always seems wrong to me when the guy in the shop north of the town square either claims to know nothing about objects in the town square or has some equally silly response of "There's not much I can tell you," or "I don't think you need to worry about it."

Real people don't act like that. Ask a merchant who's got a shop in the market square what he thinks of the market square and darn right he'll have an opinion.

Of course, with Risorg1, trying to do this resulted in LOTS of quoted strings in characters 'Ask' routines, even to the extent of running into Inform limits. I compromised for game objects by having NPC's Ask routines run WordInProperty on the common game objects I wanted to ask them about. This worked okay, but with collisions from time to time, and still meant a lot of coding. There were also holes, as I wouldn't put a WordInProperty for every item into every character's Ask routine.

In between Risorg1 and Risorg2 development, I played around a lot with ways of attaching topic information to the objects themselves. After a fair bit of work, this is working *really* nicely. For objects in scope & held, we now get nice things like this:

>ask old crone about threadbare
Which do you mean, the threadbare red carpet or the threadbare woollen socks?

>socks
"Definitely not interested in buying," says the old crone. "I've got a drawerful of my own."

So we get the regular parser disambiguation for items in scope, which is nice. This ends up calling an item.information() routine within the object, and the parameter passed is the NPC doing the asking.

So each object that I've got of class "Askable" just lists all the NPCs and their response to the item. It makes it really easy to do it this way. Previously, with each NPC, you had to think of all the items in the game and code responses. Now, with each item, I just think of all the NPCs (a much more manageable number) and make sure the ones I want are accounted for in this object.

The other nice part is if you ask about a game object that isn't in scope, WordInProperty is then used to try to find a game object being asked about. If a game object is found, the same information routine is run.

So in the socks/carpet example above... if the carpet had been left outside the shop, asking about 'threadbare' would have resulted in the socks getting matched. Asking about 'threadbare carpet' would result in the carpet getting matched, even though it's not there. A simple TestScope in the carpet's information routine allows me to craft different responses from the shopkeeper if the carpet is not in scope.

Finally, if no match is provided by any object in the game, it falls through to an AskGeneral action that matches quoted words within the NPC, in the same old way.

All in all, it's a pretty neat and tidy solution and lends real verisimilitude to the game, being able to ask NPCs about any object in the game.

Of course, all this being said, I'll release the game and someone will promptly find the one object that they can't ask person X about.