Sunday, April 29, 2012

Magnum Opus, I: Creating Procedural Dialogue


It may be early to claim what my life's work will be, but I'll do it anyway.  I want to create an RPG system on the complexity level of Dwarf Fortress, but where DF focuses on Fortress building and exploration, I want to focus on creating immersive villages and cities that feel alive.  This system would be open source and portable to other games, alleviating the problem of creating economic and social systems, so the designers could focus more time on creating other aspects of the game world.  

One aspect of this system is that different NPCs (non-playable characters) seamlessly communicate with each other.  I mean full sentences of dialogue where each interprets the other's message.  This doesn't have to be complete AI, it just has to be a Turing-level of convincing.  When a player stumbles upon two characters talking, she'll think the dialogue was prewritten or that the characters are alive.

Example:
Paul: "Good morning, Cindy."
Cindy: "Hello again, Paul."
Paul: "Do you have any fresh bread?"
Cindy: "I'm sorry; I'm all out."
Paul: "Damn, any bagels?"
Cindy: "Yes!  Would you like those toasted?"
Paul: "Please, and with some cinnamon spread."
Cindy: "Here you go.  Is that all?"
Paul: "Yes, thanks!"
Cindy: "Have a great day."

That's on the simple side of transactions, basically the first milestone of a huge undertaking.  So what's required for this?  How am I going to break up a vastly complex system of communication into a doable project for one mere mortal?

Taking the straight forward road, this would be impossible with current tech.  It would require the holy grail of AI.  And game making is anything but direct; it tends to be smoke and mirrors.  So to accomplish the same conversation, I am first looking at the following system.

Split conversation into dialogue and intent.  The dialogue is what's created based on intent.  Intent is the universal language that the game entities speak to each other.  Here's that interaction in pseudo code.

Paul.update(isHungry = True)
Paul.goes(breadstore)
Paul.sees(Cindy)
Paul.greeting().  
#If Cindy isn't busy, conversation starts successfully by her returning:
Cindy.greeting().  
Paul.requests(bread)  
Cindy.checks(bread) #on her inventory.  
Cindy.answers(False)  
Paul.requests(bagel)
Cindy.checks(bagel)
Cindy.answers(True)  
Paul.requests(cinnamon spread).
Cindy.checks(cinnamon spread)
Cindy.answers(True)
Cindy.trade(Paul, [bagel, cinnamon spread], items.cost())
Paul.acceptTrade()
Cindy.requests(MoreRequests)
Paul.answers(False)
Paul.Goodbye()
Cindy.Goodbye()

So there's the metaconversation, the Intent.  That's the simple part.  If you've ever worked with network programming patterns, some of those methods should look pretty familiar.  That conversation could be accomplished with two instances of one class with an inventory attribute and a few different communication methods.  The part I'm most excited about tackling is the conversion of intent to human readable language using grammatical patterns and simplified thesauruses.

So greeting can be "{Identifier}, {Greeting}{Punctuation}" and you could feed in random words like Greeting = ["Hello", "Good Morning", "Hi", "Salutations"]  and Identifier = [Target.Name, "Stranger", "Sir", "Friend"].  Ideally, I would feed in nonrandom words.  So stranger would be use in the condition that Speaker does not know Target, i.e. Cindy does not know Paul and is meeting him for the first time.    The request would be "{Question {item}}?", as in ["Do you have %item", "I would like a %item", "May I have a %item"].

The next time I write about this, I want to have a small, working prototype.  The conversation it outputs may look very familiar...

No comments:

Post a Comment