Archived

This topic is now archived and is closed to further replies.

phil05

Expert C++ question in writing keyword questions for NPC's

Recommended Posts

I''m looking for a programmer to make me a script for my game. Like in Everquest, the player can type his own questions, and the NPC would respond to certain keywords in a database. I''m interested in the same thing for my C++ text-based game. Can you share how that might be done? Example: Player: "Do you know where I can buy a sword?" NPC: (sees the word ''sword'') "Yes, in fact, I have one right here..."

Share this post


Link to post
Share on other sites
Searching for individual words in strings is easy in C++. Just dump the text in a std::string object and use std::string::find(). You can get fancier by storing the strings and reply text in an a separate file, or running a full blown parser. But if you want to just trigger on inidividual keywords, std::string::find() should be a good place to start.

Share this post


Link to post
Share on other sites

void bobs_dialog(std::string player_text) {
if (player_text.find("sword") != std::string::npos) {
std::cout << "Yes, in fact, I have one right here..." << std::endl;
} else {
std::cout << "*sigh* Times are tough." << std::endl;
}
}

int main(int, char **) {
bobs_dialog("Do you know where I can buy a sword?");
bobs_dialog("I like cows.");
return 0;
}


[edited by - SiCrane on March 19, 2004 10:00:01 PM]

Share this post


Link to post
Share on other sites
I tried out the code just now. Sorry for not being specific, but the player needs to ask the questions aka typing them in, instead of static questions. How can that be redone on the code above?

Share this post


Link to post
Share on other sites
One method would be to replace main() with:

int main(int, char **) {
char buffer[512];
std::cin.getline(buffer, sizeof(buffer));
bobs_dialog(buffer);
return 0;
}

Share this post


Link to post
Share on other sites
I tried this. It worked but it didn''t allow me to continue a question like typing "Do you have a sword?"


#include <iostream>

void bobs_dialog(std::string player_text) {
if (player_text.find("sword") != std::string::npos) {
std::cout << "Yes, in fact, I have offe right here..." << std::endl;
} else {
std::cout << "*sigh* Times are tough." << std::endl;
}
}

int main(int, char **) {
char player_text[200];
std::cin >> player_text;
bobs_dialog(player_text);
//bobs_dialog("I like cows.");
return 0;
}

Share this post


Link to post
Share on other sites
yay it works great. Thanks For a database, would this be set for each NPC? Or one big database? It seems like this could work for each NPC. I''m new to the database fun stuff.

Share this post


Link to post
Share on other sites
That would depend on how you structure the rest of your program. Depending on how things work you migh include all the dialog information as part of a map or room file. Or all the dialog information in a big file. Or each NPC in a different file. I don''t know how your program works so I wouldn''t be the best one to say how you should organize your data files.

Share this post


Link to post
Share on other sites
edit: nevermind.

Thanks for your help dude. Means a lot

[edited by - philvaira on March 19, 2004 12:25:13 AM]

Share this post


Link to post
Share on other sites
also, look at spliting the incoming text string into words .. and "normalizing" it (such as converting all characters to lower case or something), to allow more efficient and correct matching.

then, instead of doing a thisInput.find("someString"), you could do many more advanved search, usings sets of strings (or more likely, maps of strings).

for instance:

here are my assumptions:

typedef std::deque StringList; // can be any container you like.

void split(const std::string &inputText, StringList &words); // seperate the inputText into individual words, appending them into the provided string list.

std::string Normalize(const std::string &word);
// or
void NormalizeInPlace(std::string &word);
// these get some form of normalized string, I''m assume just a lower case version for the example.

typedef std::map KeywordMap;
// or
typedef std::set WordSet;

then you can do stuff like (in pseudocode):

for a given input string
split the string into words
normalize the words
for each word
if(npcWordSet.find(current word))
resultSet.insert(current word)

if(resultSet !empty)
build or choose some cool response string based on the matched word(s)

Share this post


Link to post
Share on other sites
Cool. I''ll look into that. Here''s my code so far, but after the Armorer says his line, the program quits. I want a continuing conversation going. I did it before, but now I can''t figure out how I did that.
And making the if/elses into switch is a great interest of mine...


#include <iostream>

////////////////////////////////////////////////////////////////////////////

// Armorer
void bobs_dialog(std::string player_text)
{
if (player_text.find("sword") || (player_text.find("swords")) != std::string::npos) {
std::cout << "Yes, in fact, I have offe right here..." << std::endl;
}
if (player_text.find("sheild") != std::string::npos) {
std::cout << "I think I have offe lying around." << std::endl;
}
else {
std::cout << "*sigh* Times are tough." << std::endl;
}
}

////////////////////////////////////////////////////////////////////////////

int main(int, char **)
{
// Gets Player''s Name
char player_name[40];
std::cout << "Name: ";
std::cin >> player_name;
std::cout << "\n" << std::endl;
std::cin.ignore(1, ''\n'');

char buffer[512];
std::cout << player_name << " -> ";
std::cin.getline(buffer, sizeof(buffer));
bobs_dialog(buffer);


return 0;
}



/////////////////////////////////////////////////////////////////////////////


Share this post


Link to post
Share on other sites
I don''t think you want to do it quite that way. If you ask him about a password, he''ll try to sell you a sword.
Look into tokenising the input string. I think boost has a class for that somewhere.

Share this post


Link to post
Share on other sites
The reason he tries to sell you a sword and a shield is because your use of the || operator in the first condition is incorrect. It should be if ((player_text.find("sword") != std::string::npos) || (player_text.find("swords") != std::string::npos)).

(And having the "sword" match "password" has a fine pedigree in early RPG games. That kind of thing would happen all the time in things like Ultima.)

Share this post


Link to post
Share on other sites
Ah ok.. I found this to work too... else if... And still, I''m having trouble to have a continuing conversation. After saying something, he responds, and it''s the end of the game. Kinda upsetting.


// Armorer
void bobs_dialog(std::string player_text)
{
if (player_text.find("sword") != std::string::npos) {
std::cout << "\n\"Yes, in fact, I have offe right here...\"\n" << std::endl;
}
else if (player_text.find("sheild") != std::string::npos) {
std::cout << "\n\"I think I have offe lying around.\"\n" << std::endl;
}
else if (player_text.find("armor") != std::string::npos) {
std::cout << "\n\"No, I sell food products. Dumbass...\" * rolls eyes *\n" << std::endl;
}
else {
std::cout << "*sigh* Times are tough." << std::endl;
}
}

Share this post


Link to post
Share on other sites
If you want the game to do something else, you need to put code to do that other thing in. If Bob is going to open a shop when the player says "sword" then put in a function to open the shop.

(By the way, why is your guy saying "offe" all the time?)

Share this post


Link to post
Share on other sites
typo. too lazy to fix... alright fine.

Strange as it is, I can''t get back to main when in the bob function. I''m trying to this so I can ask him more than one question instead of it quitting the program.

Share this post


Link to post
Share on other sites
How are you invoking the bob function? Maybe there''s a problem with the way you integrated it with the rest of your code.

Share this post


Link to post
Share on other sites
Here's the code as is. I figure it's more easy to show you than describe it. "offe" is convered wrongly on this board. It's "one" without being copied here.


#include <iostream>

char player_name[40];
////////////////////////////////////////////////////////////////////////////
// Armorer
void armorer_dialog(std::string player_text)
{
// Have sword?
if ((player_text.find("sword") != std::string::npos) ||
(player_text.find("swords") != std::string::npos)) {
std::cout << "\n\"Yes, in fact, I have offe right here...\"\n" << std::endl;
}
// Have shield?
else if ((player_text.find("shield") != std::string::npos) ||
(player_text.find("shields") != std::string::npos)) {
std::cout << "\n\"I think I have offe lying around.\"\n" << std::endl;
}
// Have armor?
else if (player_text.find("armor") != std::string::npos) {
std::cout << "\n\"No, I sell food products. Dumbass...\" *rolls eyes*\n" << std::endl;
}
else {
std::cout << "*sigh* Times are tough." << std::endl;
}
}

////////////////////////////////////////////////////////////////////////////

int main(int, char **)
{
// Gets Player's Name
std::cout << "Name: ";
std::cin >> player_name;
std::cout << "\n" << std::endl;
std::cin.ignore(1, '\n');

// Introduces Player's Surroundings
std::cout << "The sun shines through the dense woods, making its way offto a small ";
std::cout << "village \nof where you are. You stand near an armorer who greets you warmly.\n" << std::endl;


// Player Input
char buffer[512];
std::cout << player_name << " -> ";
std::cin.getline(buffer, sizeof(buffer));
armorer_dialog(buffer);


return 0;
}



/////////////////////////////////////////////////////////////////////////////



[edited by - philvaira on March 20, 2004 2:46:11 AM]

[edited by - philvaira on March 20, 2004 2:47:02 AM]

Share this post


Link to post
Share on other sites
I''m confused. What do you expect to happen? You don''t have any code to do anything else.

Share this post


Link to post
Share on other sites
Well, I want the conversation to loop til the player says goodbye. I guess that gives me an idea of waht to do..

Share this post


Link to post
Share on other sites
then add a LOOP

the:

while()
for()
do / while()

statements are the loop constructs in C++ ... you MUST use one (or recurison ... which you don''t want to do here) ... to cause repeating behavior.

for example:

if(x == 5)
printf("hi");

prints "hi" ONCE

bool quitRequested = false;
while(!quitRequested)
{
printf("hi");
}

will just print "hi" forever ...

but if you ADD CODE INSIDE THE WHILE, to set quitRequested to true when the user enters a command like "quit" or "exit" then it will keep running until the type "quit" or "exit" ...

Share this post


Link to post
Share on other sites
so for your case, place the code which gets the input from the user in a function ... like GetUserInput() or something ... and place the code which responds to the input in another ... then do something like this:


bool quitRequested = false;

PrintInitialMessage();

while(!quitRequested)
{
string userInput = GetUserInput();
quitRequested = RespondToInput(userInput);
}

inside GetUserInput just read the line the user types
inside RepondToInput, do your processing of the line, and compare to words you are looking for, then output the response AND RETURN true if the user typed quit or exit, and false otherwise.

the expanded version is somethng like this:

bool quitRequested = false;

PrintInitialMessage();

while(!quitRequested)
{
string userInput;
userInput = GetUserInput(); // get the next line from the keyboard
StringList inputWords;
split(userInput, inputWords); // splits string into words
NormalizeInPlace(inputWords); // makes all words lower case
string responseString;
quitRequested = GenerateResponseString(responseString, inputWords); // sets responseString, also returns true if quiting, false otherwise
OutputResponse(responseString); // prints the response to the user
}

Share this post


Link to post
Share on other sites