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!