Conversations in games

General discussion about LÖVE, Lua, game development, puns, and unicorns.
User avatar
S-Rave
Prole
Posts: 39
Joined: Wed Feb 25, 2009 4:41 pm

Conversations in games

Post by S-Rave »

I've been thinking about making an adventure sort of game. Like conversation and puzzle based (doesn't have to be point & click). And I was thinking about conversations and what would make a good conversation format.

I was thinking something like:

Code: Select all

conversation {
  id = 0,
  message = "alksdfklsaf",
  next = # if the npc says the message. alt number to the id of the next message to be displayed if a PC choice
  condition = pc.GotSword() -- a condition to display the message,should support and/or
  script = pc.getItem("amazing ring") 
}
Good idea? Bad idea?
Suggestions?
srejv
User avatar
qubodup
Inner party member
Posts: 775
Joined: Sat Jun 21, 2008 9:21 pm
Location: Berlin, Germany
Contact:

Re: Conversations in games

Post by qubodup »

Looks good. I have (without knowing your requirements) some addition suggestions and a solution for solving 'conditions' (use a list of lists :) )

Code: Select all

smith000 = -- the id is the table name. also needs to be human-readable
{
  charid = "elvis" --so it can be used for drawing portraits...
  message = "Lorem ipsum dolor sit amet! Let me be your guide!",
  next = "smith001" -- next message or next player choice
  conditions = { -- 'or' lists of 'and' conditions
                      {pc.ownsItem("sword003"), pc.relationship("maria", 0, 20)},
                      {npc.dead("frank")},
                      {map.weather(rain)}}
                     }
  script = {pc.getItem("amazing ring")} -- list of results after message display
}
You will need also an object for choices..
lg.newImage("cat.png") -- made possible by lg = love.graphics
-- Don't force fullscreen (it frustrates those who want to try your game real quick) -- Develop for 1280x720 (so people can make HD videos)
User avatar
S-Rave
Prole
Posts: 39
Joined: Wed Feb 25, 2009 4:41 pm

Re: Conversations in games

Post by S-Rave »

The conditions were clever! :D

I was thinking that conversations were an object of it's own. And then attach the conversation to an actor. (So you can use the same conversations to different actors i.e commoners? But maybe your idea is better...)

The "next" option can either be "#" which is to say the NPC says the message (if the right conditions are met, or else the next "#" is chosen). If there's a number (0,1,24,857) this message is shown as an option to continue the conversation (alt close it). :o
srejv
osuf oboys
Party member
Posts: 215
Joined: Sun Jan 18, 2009 8:03 pm

Re: Conversations in games

Post by osuf oboys »

Might I suggest that the conditions and scripts are functions that can be called with suitable arguments (like the world and the two characters)? Unless you wish to make an automated planner or similar AI (but that would be great!), there's no point in writing {{x1, x2}, {x3, x4}} instead of function() return (x1 or x2) and (x3 or x4) end, seeing how the latter is more flexible.
If I haven't written anything else, you may assume that my work is released under the LPC License - the LÖVE Community. See http://love2d.org/wiki/index.php?title=LPC_License.
User avatar
qubodup
Inner party member
Posts: 775
Joined: Sat Jun 21, 2008 9:21 pm
Location: Berlin, Germany
Contact:

Re: Conversations in games

Post by qubodup »

now that you say it, it's probably possible to do it in the same object.

Code: Select all

next = int -- results in the next message showing up. If you want player actions then..
next = {{int, string}, {int, string}} -- et voila, strings give you a choice of messageIDs!
Tables sure are cool, huh?

So for example

Code: Select all

sansa004 =
{
  charid = "sansa"
  message = "Oh, " .. player.name .. " you are so dreamy wet-blue with your mega-supra" ..
                      " delistinguater. I wish I was your object of impertural desire forever!",
  next = {{player004, "Do a dance"}, {player024, "\"Sansa, I love you!\""}, {player032, "Rub your belly"}}
  conditions = {
                       {pc.ownsItem("mega-supra_delistinguater003"), pc.relationship("sansa", 0, 20)},
                       }
}
Would result in:
Image
Sansa: Oh, Iwan you are so dreamy wet-blue with your mega-supra delistinguater. I wish I was your object of impertural desire forever!

* Do a dance
* "Sansa, I love you!"
* Rub your belly
img cc-by: Ausagi
lg.newImage("cat.png") -- made possible by lg = love.graphics
-- Don't force fullscreen (it frustrates those who want to try your game real quick) -- Develop for 1280x720 (so people can make HD videos)
User avatar
S-Rave
Prole
Posts: 39
Joined: Wed Feb 25, 2009 4:41 pm

Re: Conversations in games

Post by S-Rave »

osuf oboys wrote:Might I suggest that the conditions and scripts are functions that can be called with suitable arguments (like the world and the two characters)? Unless you wish to make an automated planner or similar AI (but that would be great!), there's no point in writing {{x1, x2}, {x3, x4}} instead of function() return (x1 or x2) and (x3 or x4) end, seeing how the latter is more flexible.
I don't really understand what you mean by this. :\
the conditions aren't written like "{ pc.hasWeapon("staff"), pc.hasItem("headofagoat")" but

script = function() return (pc.hasWeapon("staff") and pc.hasItem("headofagoat")) ?

Would be much better... yes!
qubodup wrote:now that you say it, it's probably possible to do it in the same object.

Code: Select all

next = int -- results in the next message showing up. If you want player actions then..
next = {{int, string}, {int, string}} -- et voila, strings give you a choice of messageIDs!
Tables sure are cool, huh?

So for example

Code: Select all

sansa004 =
{
  charid = "sansa"
  message = "Oh, " .. player.name .. " you are so dreamy wet-blue with your mega-supra" ..
                      " delistinguater. I wish I was your object of impertural desire forever!",
  next = {{player004, "Do a dance"}, {player024, "\"Sansa, I love you!\""}, {player032, "Rub your belly"}}
  conditions = {
                       {pc.ownsItem("mega-supra_delistinguater003"), pc.relationship("sansa", 0, 20)},
                       }
}
Would result in:
Image
Sansa: Oh, Iwan you are so dreamy wet-blue with your mega-supra delistinguater. I wish I was your object of impertural desire forever!

* Do a dance
* "Sansa, I love you!"
* Rub your belly
It's actually a good idea. But I was more into the face that you have conditions and scripts for the responses as well. And I don't really like the ID system (sansa004), I wouldn't know how to split the files up. But now that I think about it, it's actually a pretty neat idea.

Since it's tables it's not hard to add conditions and scripts for each response as well. :]
srejv
User avatar
hdon
Prole
Posts: 36
Joined: Tue Mar 17, 2009 10:54 pm
Location: Pittsburgh, PA, USA
Contact:

Re: Conversations in games

Post by hdon »

osuf oboys wrote:I don't really understand what you mean by this. :\
the conditions aren't written like "{ pc.hasWeapon("staff"), pc.hasItem("headofagoat")"
Code is data
User avatar
qubodup
Inner party member
Posts: 775
Joined: Sat Jun 21, 2008 9:21 pm
Location: Berlin, Germany
Contact:

Re: Conversations in games

Post by qubodup »

S-Rave wrote:It's actually a good idea. But I was more into the face that you have conditions and scripts for the responses as well. And I don't really like the ID system (sansa004), I wouldn't know how to split the files up. But now that I think about it, it's actually a pretty neat idea.
The response scripts are inside the messages that responses link to! :)

Also, I made a mistake by using variable names in the responses. (sansa004 should be "sansa004") they would be used to look up the message in the same table.

Perhaps it would be better to have one message table per character so instead of sansa004 one would use sansa_messages[004].. before I was thinking to have one table per dialog, which might be problematic to not mix up numbers.

Code: Select all

  conditions = {
                       {pc.ownsItem("mega-supra_delistinguater003"), pc.relationship("sansa", 0, 20)},
                       }
I totally forgot that this doesn't work this way d'oh!
lg.newImage("cat.png") -- made possible by lg = love.graphics
-- Don't force fullscreen (it frustrates those who want to try your game real quick) -- Develop for 1280x720 (so people can make HD videos)
User avatar
S-Rave
Prole
Posts: 39
Joined: Wed Feb 25, 2009 4:41 pm

Re: Conversations in games

Post by S-Rave »

qubodup wrote:
S-Rave wrote:It's actually a good idea. But I was more into the face that you have conditions and scripts for the responses as well. And I don't really like the ID system (sansa004), I wouldn't know how to split the files up. But now that I think about it, it's actually a pretty neat idea.
The response scripts are inside the messages that responses link to! :)

Also, I made a mistake by using variable names in the responses. (sansa004 should be "sansa004") they would be used to look up the message in the same table.

Perhaps it would be better to have one message table per character so instead of sansa004 one would use sansa_messages[004].. before I was thinking to have one table per dialog, which might be problematic to not mix up numbers.

Code: Select all

  conditions = {
                       {pc.ownsItem("mega-supra_delistinguater003"), pc.relationship("sansa", 0, 20)},
                       }
I totally forgot that this doesn't work this way d'oh!
I'm still pondering on any efficient way to add conditions and scripts for the reponses. Just adding them to the response table?
For example you wouldn't tell your comrades that you've gotten a map BEFORE you got it? :)

Also, I was thinking about some way to start the conversation, since after you've spoken to someone they probably wont answear the same way as the first time. Any ideas? :]
srejv
User avatar
hdon
Prole
Posts: 36
Joined: Tue Mar 17, 2009 10:54 pm
Location: Pittsburgh, PA, USA
Contact:

Re: Conversations in games

Post by hdon »

S-Rave wrote:I'm still pondering on any efficient way to add conditions and scripts for the reponses. Just adding them to the response table? For example you wouldn't tell your comrades that you've gotten a map BEFORE you got it? :)

Also, I was thinking about some way to start the conversation, since after you've spoken to someone they probably wont answear the same way as the first time. Any ideas? :]
This is a classic game pattern which is easily programmed using a simple database table including a counter and/or bitmasks for different game conditions representing the state of the game. This pattern is most obvious in games like Ocarina of Time where being at a different point in the linear overall game, a different point in one of many linear subquests, having accomplished any of many individual feats, talking during different times of day, and wearing different masks, all get factored into which dialogue you're able to have with someone. The best thing is that in games that are heavy on this sort of context-sensitive interaction is that a ton of things in the game can use this same system, not just dialogue. What signs say, what a chest contains, whether or not a door can be opened, what kinds of enemies get generated in different areas, and many more things can use this single elegant facility.

Using a modern database solution even as simple as sqlite is a very efficient way to do what you're asking. Where bitmasks were used in the past, today you may simply want to create different named columns for each individual condition. Depending on your storage engine and query planner, however, you might easily end up with gigantic databases or slow queries. For this reason maybe it's better to use the tried and true bitmasking techniques, and simply start out with super-wide binary columns that will never need to be resized.

All integers in sqlite3 are 64-bit signed integers. Other more conventional databases give you integers of arbitrary precision you get to specify ahead of time (you also get to designate that sign bit however you like.) I'll be using sqlite3 in the example code following, but the basic principles are portable to any database system.

Code: Select all

donny@teamspace:~$ sqlite3 conversation.db
SQLite version 3.5.6
Enter ".help" for instructions
sqlite> CREATE TABLE dialogue (npc_id INT, feat_bits INT(64), feat_mask INT(64), response VARCHAR);
sqlite> INSERT INTO dialogue VALUES (1, 0, 1, "Welcome to the game!");
sqlite> INSERT INTO dialogue VALUES (1, 1, 1, "Leave the forest! Hyrule needs your help!");
sqlite> INSERT INTO dialogue VALUES (1, 1<<63, 1<<63, "Congratulations! You saved Hyrule!");
Here I've created a table with some values for a single non-player character (NPC.) "Non-player element" might be a better term, though, since this could also apply to signs and whatnot. You could give more than one element the same NPC ID and they could spit out the same stock dialogue. You could even have more than one database record match the same criteria, and cycle through all matching records in your database each time you talk to someone. To keep things simple, though, I have ruled out the possibility that we might want cross-cutting stock dialogues. An example of that might be that we have some stock dialogue that we want NPCs near the Water Fortress to use, and some stock dialogue we want NPCs near Hyrule to use; cross-cutting would describe whenever we have some NPCs that are near both of those places, and we want those NPCs to spit out stock dialogue associated with both of those places. If you wanted, though, you could always apply different NPC dialogue IDs to your NPCs, then select which ID to perform the database query with before-hand.

The feat_mask bitmask defines which feat bits we are concerned with, while the feat_bits value defines what we want those bits to be. The first feat bit is apparently whether or not we have gotten past the first part of the game so that we can leave the forest and set off to save the kingdom! The last bit is apparently whether or not we have beaten the game! To keep things simple, the response column of our example table is only a single string. In reality, it may be more complicated than that, depending on your needs.

Here is how you might query might go:

Code: Select all

sqlite> INSERT INTO dialogue VALUES (1, 0, 1, "Welcome to the game!");
sqlite> INSERT INTO dialogue VALUES (1, 1, 1, "Leave the forest! Hyrule needs your help!");
sqlite> INSERT INTO dialogue VALUES (1, 1<<63, 1<<63, "Congratulations! You saved Hyrule!");
sqlite> SELECT response FROM dialogue WHERE $CURRENT_FEATS & feat_mask == feat_bits;
Leave the forest! Hyrule needs your help!
If you imagine that $CURRENT_FEATS represents where your application will substitute the appropriate value, you can see that $CURRENT_FEATS has bit #1 set, meaning that it's time to leave for Hyrule. We'll try another one, but this time I won't bother with the $CURRENT_FEATS nonsense:

Code: Select all

sqlite> SELECT response FROM dialogue WHERE (1|(1<<63)) & feat_mask == feat_bits;
Leave the forest! Hyrule needs your help!
Congratulations! You saved Hyrule!
Oh, that probably shouldn't happen. If you'll recall, the first two records we added to our database had a feat_mask value of 1 meaning that we were only concerned with that bit, disregarding the other bit which clearly plays a part in deciding what response this NPC should give. Let's rewrite them a bit:

Code: Select all

sqlite> UPDATE dialogue SET feat_mask = (1|(1<<63)) WHERE rowid == 1 OR rowid == 2;
sqlite> SELECT response FROM dialogue WHERE (1|(1<<63)) & feat_mask == feat_bits;
Congratulations! You saved Hyrule!
There, that's better. Our game must be pretty flawed, though, if we were able to skip from feat #1 all the way to feat #63 and beat the game!

I'm sure you can also imagine that since simply having spoken to a person is a good reason to update a feat bit, you could add column(s) for just that purpose. Or you could simply store some Lua code in the 'response' column.

Anyhow I'm sure you can figure the rest out. INDEXES WOULD BE MUCH BETTER THAN THE TECHNIQUE SHOWN HERE FYI but I don't know of a light weight in-process database that will do this well for our purposes. sqlite can do indexes, but each column is fundamentally huge, even if it only represents one bit! :cry:
Post Reply

Who is online

Users browsing this forum: No registered users and 268 guests