//***************************************************************************** // // dialog.c // // This is a module that allows NPCs to carry on basic conversation with // players. Dialogs are initiated by approaching (or greeting) a mob that has // an attached dialog. Players are presented a list of questions they can ask // the NPC, and the NPC will respond in fashion to each one. Questions can have // checks to make sure a PC can ask the question, and they can also have // executed scripts at the end of each question. // // Dialogs can also be a means for interacting with NPCs in other ways. For // instance, training skills or buying/selling items. // // Known bugs: // * if we try to end a dialog while we've pushed on another input handler // (e.g. while training, shopping) undefined behavior will result. // //***************************************************************************** #include "../mud.h" #include "../utils.h" #include "../character.h" #include "../world.h" #include "../storage.h" #include "../auxiliary.h" #include "../hooks.h" #include "../socket.h" #include "../inform.h" #include "dialog.h" #include "dedit.h" //***************************************************************************** // mandatory modules //***************************************************************************** #include "../scripts/scripts.h" #include "../scripts/pychar.h" #include "../scripts/pyplugs.h" //***************************************************************************** // auxiliary data //***************************************************************************** typedef struct { CHAR_DATA *talker; // who are we talking with? char *panel; // which discussion panel are we on? char *dialog; // the key for the dialog we use? LIST *dialogers; // people reading our dialog LIST *extensions; // extra questions we support bool dmenu; // do we need a dialog menu? } DIALOG_AUX_DATA; DIALOG_AUX_DATA *newDialogAuxData(void) { DIALOG_AUX_DATA *data = malloc(sizeof(DIALOG_AUX_DATA)); data->talker = NULL; data->panel = strdup(""); data->dialog = strdup(""); data->dialogers = newList(); data->extensions = newList(); data->dmenu = FALSE; return data; } void deleteDialogAuxData(DIALOG_AUX_DATA *data) { if(data->panel) free(data->panel); if(data->dialog) free(data->dialog); if(data->dialogers) free(data->dialogers); if(data->extensions) deleteListWith(data->extensions, deleteDialogQuestion); free(data); } void dialogAuxDataCopyTo(DIALOG_AUX_DATA *from, DIALOG_AUX_DATA *to) { if(to->panel) free(to->panel); if(to->dialog) free(to->dialog); if(to->dialogers) deleteList(to->dialogers); if(to->extensions) deleteListWith(to->extensions, deleteDialogQuestion); to->talker = from->talker; to->panel = strdupsafe(from->panel); to->dialog = strdupsafe(from->dialog); to->dialogers = listCopyWith(from->dialogers, identity_func); to->extensions = listCopyWith(from->extensions, dialogQuestionCopy); } DIALOG_AUX_DATA *dialogAuxDataCopy(DIALOG_AUX_DATA *data) { DIALOG_AUX_DATA *newdata = newDialogAuxData(); dialogAuxDataCopyTo(data, newdata); return newdata; } DIALOG_AUX_DATA *dialogAuxDataRead(STORAGE_SET *set) { DIALOG_AUX_DATA *data = newDialogAuxData(); if(data->dialog) free(data->dialog); if(data->extensions) deleteListWith(data->extensions, deleteDialogQuestion); data->dialog = strdup(read_string(set, "dialog")); data->extensions = gen_read_list(read_list(set, "extensions"), dialogQuestionRead); return data; } STORAGE_SET *dialogAuxDataStore(DIALOG_AUX_DATA *data) { STORAGE_SET *set = new_storage_set(); store_string(set, "dialog", data->dialog); store_list(set, "extensions", gen_store_list(data->extensions, dialogQuestionStore)); return set; } void charSetDialogPanel(CHAR_DATA *ch, const char *panel) { DIALOG_AUX_DATA *data = charGetAuxiliaryData(ch, "dialog_data"); if(data->panel) free(data->panel); data->panel = strdupsafe(panel); } const char *charGetDialogPanel(CHAR_DATA *ch) { DIALOG_AUX_DATA *data = charGetAuxiliaryData(ch, "dialog_data"); return data->panel; } void charSetDialogTalker(CHAR_DATA *ch, CHAR_DATA *talker) { DIALOG_AUX_DATA *data = charGetAuxiliaryData(ch, "dialog_data"); data->talker = talker; } CHAR_DATA *charGetDialogTalker(CHAR_DATA *ch) { DIALOG_AUX_DATA *data = charGetAuxiliaryData(ch, "dialog_data"); return data->talker; } void charSetDialog(CHAR_DATA *ch, const char *key) { DIALOG_AUX_DATA *data = charGetAuxiliaryData(ch, "dialog_data"); if(data->dialog) free(data->dialog); data->dialog = strdupsafe(key); } const char *charGetDialog(CHAR_DATA *ch) { DIALOG_AUX_DATA *data = charGetAuxiliaryData(ch, "dialog_data"); return data->dialog; } LIST *charGetDialogers(CHAR_DATA *ch) { DIALOG_AUX_DATA *data = charGetAuxiliaryData(ch, "dialog_data"); return data->dialogers; } bool charNeedsDialogMenu(CHAR_DATA *ch) { DIALOG_AUX_DATA *data = charGetAuxiliaryData(ch, "dialog_data"); return data->dmenu; } void charSetNeedsDialogMenu(CHAR_DATA *ch, bool val) { DIALOG_AUX_DATA *data = charGetAuxiliaryData(ch, "dialog_data"); data->dmenu = val; } void charExtendDialog(CHAR_DATA *ch, DIALOG_QUESTION *qst) { DIALOG_AUX_DATA *data = charGetAuxiliaryData(ch, "dialog_data"); listQueue(data->extensions, qst); } //***************************************************************************** // local variables, datastructures, functions //***************************************************************************** // // neeed by a couple functions before them void charStartDialog(CHAR_DATA *ch, CHAR_DATA *talker); void charEndDialog(CHAR_DATA *ch); // // returns whether or not the character can ask the given dialog question bool charCanAskDialogQuestion(CHAR_DATA *ch, DIALOG_QUESTION *qst) { // make sure we're in the right panel if(!is_keyword(dialogQuestionGetPanels(qst), charGetDialogPanel(ch), FALSE)) return FALSE; // make sure our check is alright else if(!*dialogQuestionGetCheck(qst)) return TRUE; else { const char *locale = get_key_locale(charGetDialog(charGetDialogTalker(ch))); const char *check = dialogQuestionGetCheck(qst); PyObject *pych = charGetPyForm(ch); PyObject *pyme = charGetPyForm(charGetDialogTalker(ch)); PyObject *dict = restricted_script_dict(); PyDict_SetItemString(dict, "me", pyme); PyDict_SetItemString(dict, "ch", pych); bool ret = TRUE; // evaluate the code PyObject *retval = eval_script(dict, check, locale); // did we encounter an error? if(retval == NULL) ret = FALSE; // append the output else if(PyInt_Check(retval)) ret = (PyInt_AsLong(retval) ? TRUE : FALSE); else if(retval == Py_None) ret = FALSE; // invalid return type... else { log_string("dialog check had invalid evaluation: %s", check); ret = FALSE; } // garbage collection Py_XDECREF(dict); Py_XDECREF(pyme); Py_XDECREF(pych); return ret; } } // // compares two dialog questions based on their query text int dqquerycmp(DIALOG_QUESTION *dq1, DIALOG_QUESTION *dq2) { return strcasecmp(dialogQuestionGetQuery(dq1), dialogQuestionGetQuery(dq2)); } // // returns a list of the dialog questions we can use at the moment. List but not // contents must be deleted after use. LIST *charGetDialogQuestions(CHAR_DATA *ch) { CHAR_DATA *pers = charGetDialogTalker(ch); DIALOG_DATA *dialog = worldGetType(gameworld, "dialog",charGetDialog(pers)); LIST *questions = newList(); if(dialog != NULL) { LIST_ITERATOR *q_i = newListIterator(dialogGetQuestions(dialog)); DIALOG_QUESTION *q = NULL; ITERATE_LIST(q, q_i) { if(charCanAskDialogQuestion(ch, q)) listPutWith(questions, q, dqquerycmp); } deleteListIterator(q_i); } // find all of our extended questions DIALOG_AUX_DATA *pauxdialog = charGetAuxiliaryData(pers, "dialog_data"); if(listSize(pauxdialog->extensions) > 0) { LIST_ITERATOR *q_i = newListIterator(pauxdialog->extensions); DIALOG_QUESTION *q = NULL; ITERATE_LIST(q, q_i) { if(charCanAskDialogQuestion(ch, q)) listQueue(questions, q); } deleteListIterator(q_i); } // return the questions we found return questions; } // // the function that displays a prompt to a character when in dialog mode void show_dialog_prompt(SOCKET_DATA *sock) { CHAR_DATA *ch = socketGetChar(sock); // if our character has died, pop our input handler if(ch == NULL) socketPopInputHandler(sock); // we entered an actual command, and not just a newline else if(charNeedsDialogMenu(ch)) { BUFFER *buf = newBuffer(MAX_BUFFER); LIST *questions = charGetDialogQuestions(ch); int count = 1; // if we have no questions and we're not at start, put us back at start if(listSize(questions) == 0 && strcasecmp("start", charGetDialogPanel(ch))){ charSetDialogPanel(ch, "start"); deleteList(questions); questions = charGetDialogQuestions(ch); } // list all of our responses if(listSize(questions) > 0) { bprintf(buf, "\r\n{wResponses:{n\r\n"); LIST_ITERATOR *q_i = newListIterator(questions); DIALOG_QUESTION *q = NULL; ITERATE_LIST(q, q_i) { bprintf(buf, "{g %2d) %s\r\n", count, dialogQuestionGetQuery(q)); count++; } deleteListIterator(q_i); } // append our end option bprintf(buf, "{g %2d) Goodbye\r\n", count); // send the prompt out message(ch, charGetDialogTalker(ch), NULL, NULL, FALSE, TO_CHAR, bufferString(buf)); // garbage collection deleteList(questions); deleteBuffer(buf); } send_to_char(ch, "{gEnter a choice, or Q to say goodbye: "); } // // a function that handles our commands in dialog mode void dialog_input_handler(SOCKET_DATA *sock, char *input) { CHAR_DATA *ch = socketGetChar(sock); if(ch == NULL) return; CHAR_DATA *talker = charGetDialogTalker(ch); charSetNeedsDialogMenu(ch, FALSE); switch(toupper(*input)) { // we're trying to break out of the input handler case 'Q': charEndDialog(ch); return; // are we trying to do one of our responses? default: { // make sure it's a numeric choice if(!isdigit(*input)) return; // get our options and choice int choice = atoi(input) - 1; LIST *options = charGetDialogQuestions(ch); DIALOG_QUESTION *question = listGet(options, choice); const char *dialog_key = charGetDialog(talker); bool menu = TRUE; bool end = FALSE; // if we have a question, display the response if(question == NULL) { // are we trying to terminate the dialog? if(choice == listSize(options)) end = TRUE; menu = FALSE; } else { BUFFER *resp = bufferCopy(dialogQuestionGetResponseBuf(question)); PyObject *pyme = charGetPyForm(talker); char *locale = strdupsafe(get_key_locale(dialog_key)); // expand our dynamic descriptions expand_dynamic_descs(resp, pyme, ch, locale); // format our response bufferFormat(resp, SCREEN_WIDTH, PARA_INDENT); // send the response if(*bufferString(resp)) send_to_socket(sock, "%s responds:\r\n{c%s", see_char_as(ch, talker), bufferString(resp)); else send_to_socket(sock, "%s has no response.\r\n", see_char_as(ch,talker)); // set our new panel if(*dialogQuestionGetDestPanel(question)) charSetDialogPanel(ch, dialogQuestionGetDestPanel(question)); // run any scripts we might have if(*dialogQuestionGetScript(question)) { PyObject *dict = restricted_script_dict(); PyObject *pych = charGetPyForm(ch); PyDict_SetItemString(dict, "me", pyme); PyDict_SetItemString(dict, "ch", pych); // run the script run_script(dict, dialogQuestionGetScript(question), locale); // garbage collection Py_XDECREF(dict); Py_XDECREF(pych); } // do our garbage collection deleteBuffer(resp); Py_XDECREF(pyme); free(locale); } // set our menu status charSetNeedsDialogMenu(ch, menu); // are we trying to exit the dialog? if(end == TRUE) charEndDialog(ch); // garbage collection deleteList(options); return; } } } // // stop a character conversing with another person void charEndDialog(CHAR_DATA *ch) { // run the ending script, if we have one CHAR_DATA *talker = charGetDialogTalker(ch); DIALOG_DATA *dialog = worldGetType(gameworld, "dialog",charGetDialog(talker)); if(dialog && *dialogGetEndScript(dialog)) { PyObject *dict = restricted_script_dict(); PyObject *pyme = charGetPyForm(talker); PyObject *pych = charGetPyForm(ch); PyDict_SetItemString(dict, "me", pyme); PyDict_SetItemString(dict, "ch", pych); // run the end script run_script(dict, dialogGetEndScript(dialog), get_key_locale(dialogGetKey(dialog))); // garbage collection Py_XDECREF(dict); Py_XDECREF(pyme); Py_XDECREF(pych); } // sever the connection between the two characters listRemove(charGetDialogers(talker), ch); charSetDialogTalker(ch, NULL); charSetDialogPanel(ch, NULL); if(charGetSocket(ch)) socketPopInputHandler(charGetSocket(ch)); } // // start a character conversing with another person void charStartDialog(CHAR_DATA *ch, CHAR_DATA *talker) { listQueue(charGetDialogers(talker), ch); charSetDialogPanel(ch, "start"); charSetDialogTalker(ch, talker); charSetNeedsDialogMenu(ch, TRUE); if(charGetSocket(ch)) socketPushInputHandler(charGetSocket(ch), dialog_input_handler, show_dialog_prompt, "dialog"); } // // stops our dialog and anyone having dialog with us void stop_dialogs_with(CHAR_DATA *ch) { if(charGetDialogTalker(ch)) charEndDialog(ch); LIST *dialogers = charGetDialogers(ch); if(listSize(dialogers) > 0) { LIST_ITERATOR *pers_i = newListIterator(dialogers); CHAR_DATA *pers = NULL; ITERATE_LIST(pers, pers_i) { charEndDialog(pers); } deleteListIterator(pers_i); } } // // a hook that tries to stop all the dialogs with a character void stop_dialogs_hook(const char *info) { CHAR_DATA *ch = NULL; hookParseInfo(info, &ch); stop_dialogs_with(ch); } // // whenever we move, stop any dialog we have going on void stop_dialogs_move_hook(const char *info) { CHAR_DATA *ch = NULL; ROOM_DATA *room = NULL; EXIT_DATA *exit = NULL; hookParseInfo(info, &ch, &room, &exit); stop_dialogs_with(ch); } // // same as try_start_dialog, but users can also supply a custom greeting message void try_start_dialog_full(CHAR_DATA *ch, CHAR_DATA *other, const char *greet) { DIALOG_DATA *dialog = worldGetType(gameworld, "dialog",charGetDialog(other)); if(dialog != NULL) { // figure out what we'll be using as a greeting messg: custom or default BUFFER *gbuf = newBuffer(1); bufferCat(gbuf, (greet ? greet : dialogGetGreet(dialog))); // figure out our locale char *locale = strdup(get_key_locale(dialogGetKey(dialog))); // send out our greet message if neccessary PyObject *pyme = charGetPyForm(other); // expand any dynamic descriptions the buffer might have expand_dynamic_descs(gbuf, pyme, ch, locale); // format the buffer bufferFormat(gbuf, SCREEN_WIDTH, PARA_INDENT); // send it out send_to_char(ch, "%s acknowledges you%s:\r\n{c%s", see_char_as(ch, other), (*bufferString(gbuf) ? " and responds" : ""), bufferString(gbuf)); // start the dialog charStartDialog(ch, other); // garbage collection deleteBuffer(gbuf); Py_XDECREF(pyme); free(locale); } } // // tries to start up a dialog between the two persons void try_start_dialog(CHAR_DATA *ch, CHAR_DATA *other) { try_start_dialog_full(ch, other, NULL); } // // a greet hook that tries to start a dialog between two persons void try_start_dialog_hook(const char *info) { CHAR_DATA *ch = NULL; CHAR_DATA *other = NULL; hookParseInfo(info, &ch, &other); try_start_dialog(ch, other); } //***************************************************************************** // implementation of dialog.h //***************************************************************************** struct dialog_data { char *key; // our unique identifier in the world database char *name; // the name of our dialog for reference by builders BUFFER *greet; // the message shown when the dialog first starts BUFFER *endscript; // a script run when the dialog is terminated LIST *questions; // a list of questions that can be asked }; struct dialog_question { char *panels; // the list of panels we belong to char *to_panel; // the panel we lead to char *pycheck; // a python statement of whether we can ask the question char *query; // the question ask BUFFER *response; // what the person says in response BUFFER *script; // the script that's executed after asking }; DIALOG_DATA *newDialog(void) { DIALOG_DATA *data = malloc(sizeof(DIALOG_DATA)); data->key = strdup(""); data->name = strdup(""); data->questions = newList(); data->endscript = newBuffer(1); data->greet = newBuffer(1); return data; } void deleteDialog(DIALOG_DATA *data) { if(data->key) free(data->key); if(data->name) free(data->name); if(data->questions) deleteListWith(data->questions, deleteDialogQuestion); if(data->greet) deleteBuffer(data->greet); if(data->endscript) deleteBuffer(data->endscript); free(data); } void dialogCopyTo(DIALOG_DATA *from, DIALOG_DATA *to) { if(to->key) free(to->key); if(to->name) free(to->name); if(to->questions) deleteListWith(to->questions, deleteDialogQuestion); to->key = strdupsafe(from->key); to->name = strdupsafe(from->name); to->questions = listCopyWith(from->questions, dialogQuestionCopy); bufferCopyTo(from->greet, to->greet); bufferCopyTo(from->endscript, to->endscript); } DIALOG_DATA *dialogCopy(DIALOG_DATA *data) { DIALOG_DATA *newdata = newDialog(); dialogCopyTo(data, newdata); return newdata; } STORAGE_SET *dialogStore(DIALOG_DATA *data) { STORAGE_SET *set = new_storage_set(); store_string(set, "name", data->name); store_string(set, "greet", bufferString(data->greet)); store_string(set, "endscript", bufferString(data->endscript)); store_list(set, "questions", gen_store_list(data->questions, dialogQuestionStore)); return set; } DIALOG_DATA *dialogRead(STORAGE_SET *set) { DIALOG_DATA *data = newDialog(); dialogSetName(data, read_string(set, "name")); dialogSetGreet(data, read_string(set, "greet")); dialogSetEndScript(data, read_string(set, "endscript")); LIST *qstns = gen_read_list(read_list(set, "questions"), dialogQuestionRead); DIALOG_QUESTION *q = NULL; while( (q = listPop(qstns)) != NULL) dialogAddQuestion(data, q); deleteList(qstns); return data; } void dialogSetKey(DIALOG_DATA *dialog, const char *key) { if(dialog->key) free(dialog->key); dialog->key = strdupsafe(key); } const char *dialogGetKey(DIALOG_DATA *dialog) { return dialog->key; } const char *dialogGetName(DIALOG_DATA *dialog) { return dialog->name; } void dialogSetName(DIALOG_DATA *dialog, const char *name) { if(dialog->name) free(dialog->name); dialog->name = strdupsafe(name); } void dialogSetEndScript(DIALOG_DATA *dialog, const char *script) { bufferClear(dialog->endscript); bufferCat(dialog->endscript, script); } void dialogSetGreet(DIALOG_DATA *dialog, const char *greet) { bufferClear(dialog->greet); bufferCat(dialog->greet, greet); } const char *dialogGetEndScript(DIALOG_DATA *dialog) { return bufferString(dialog->endscript); } BUFFER *dialogGetEndScriptBuf(DIALOG_DATA *dialog) { return dialog->endscript; } const char *dialogGetGreet(DIALOG_DATA *dialog) { return bufferString(dialog->greet); } BUFFER *dialogGetGreetBuf(DIALOG_DATA *dialog) { return dialog->greet; } LIST *dialogGetQuestions(DIALOG_DATA *dialog) { return dialog->questions; } void dialogAddQuestion(DIALOG_DATA *dialog, DIALOG_QUESTION *question) { listQueue(dialog->questions, question); } DIALOG_QUESTION *newDialogQuestion(void) { DIALOG_QUESTION *data = malloc(sizeof(DIALOG_QUESTION)); data->panels = strdup(""); data->to_panel = strdup(""); data->query = strdup(""); data->pycheck = strdup(""); data->response = newBuffer(1); data->script = newBuffer(1); return data; } void deleteDialogQuestion(DIALOG_QUESTION *qst) { if(qst->panels) free(qst->panels); if(qst->to_panel) free(qst->to_panel); if(qst->query) free(qst->query); if(qst->pycheck) free(qst->pycheck); if(qst->response) deleteBuffer(qst->response); if(qst->script) deleteBuffer(qst->script); free(qst); } void dialogQuestionCopyTo(DIALOG_QUESTION *from, DIALOG_QUESTION *to) { if(to->panels) free(to->panels); if(to->to_panel) free(to->to_panel); if(to->query) free(to->query); if(to->pycheck) free(to->pycheck); bufferClear(to->response); bufferClear(to->script); to->panels = strdupsafe(from->panels); to->to_panel = strdupsafe(from->to_panel); to->query = strdupsafe(from->query); to->pycheck = strdupsafe(from->pycheck); bufferCat(to->response, bufferString(from->response)); bufferCat(to->script, bufferString(from->script)); } DIALOG_QUESTION *dialogQuestionCopy(DIALOG_QUESTION *qst) { DIALOG_QUESTION *newqst = newDialogQuestion(); dialogQuestionCopyTo(qst, newqst); return newqst; } STORAGE_SET *dialogQuestionStore(DIALOG_QUESTION *qst) { STORAGE_SET *set = new_storage_set(); store_string(set, "panels", qst->panels); store_string(set, "to_panel", qst->to_panel); store_string(set, "query", qst->query); store_string(set, "pycheck", qst->pycheck); store_string(set, "response", bufferString(qst->response)); store_string(set, "script", bufferString(qst->script)); return set; } DIALOG_QUESTION *dialogQuestionRead(STORAGE_SET *set) { DIALOG_QUESTION *data = newDialogQuestion(); dialogQuestionSetPanels(data, read_string(set, "panels")); dialogQuestionSetDestPanel(data, read_string(set, "to_panel")); dialogQuestionSetQuery(data, read_string(set, "query")); dialogQuestionSetCheck(data, read_string(set, "pycheck")); dialogQuestionSetResponse(data, read_string(set, "response")); dialogQuestionSetScript(data, read_string(set, "script")); return data; } void dialogQuestionSetQuery(DIALOG_QUESTION *qst, const char *query) { if(qst->query) free(qst->query); qst->query = strdupsafe(query); } void dialogQuestionSetResponse(DIALOG_QUESTION *qst, const char *response) { bufferClear(qst->response); bufferCat(qst->response, response); } void dialogQuestionSetCheck(DIALOG_QUESTION *qst,const char *pycheck) { if(qst->pycheck) free(qst->pycheck); qst->pycheck = strdupsafe(pycheck); } void dialogQuestionSetScript(DIALOG_QUESTION *qst, const char *script) { bufferClear(qst->script); bufferCat(qst->script, script); } void dialogQuestionSetPanels(DIALOG_QUESTION *qst, const char *panels) { if(qst->panels) free(qst->panels); qst->panels = strdupsafe(panels); } void dialogQuestionSetDestPanel(DIALOG_QUESTION *qst, const char *panel) { if(qst->to_panel) free(qst->to_panel); qst->to_panel = strdup(panel); } const char *dialogQuestionGetQuery(DIALOG_QUESTION *qst) { return qst->query; } const char *dialogQuestionGetResponse(DIALOG_QUESTION *qst) { return bufferString(qst->response); } BUFFER *dialogQuestionGetResponseBuf(DIALOG_QUESTION *qst) { return qst->response; } const char *dialogQuestionGetCheck(DIALOG_QUESTION *qst) { return qst->pycheck; } const char *dialogQuestionGetScript(DIALOG_QUESTION *qst) { return bufferString(qst->script); } BUFFER *dialogQuestionGetScriptBuf(DIALOG_QUESTION *qst) { return qst->script; } const char *dialogQuestionGetPanels(DIALOG_QUESTION *qst) { return qst->panels; } const char *dialogQuestionGetDestPanel(DIALOG_QUESTION *qst) { return qst->to_panel; } //***************************************************************************** // Python extensions //***************************************************************************** PyObject *PyChar_GetDialog(PyObject *self, void *closure) { CHAR_DATA *ch = PyChar_AsChar(self); if(ch == NULL) return NULL; else return Py_BuildValue("s", charGetDialog(ch)); } int PyChar_SetDialog(PyObject *self, PyObject *arg, void *closure) { CHAR_DATA *ch = PyChar_AsChar(self); if(ch == NULL) { PyErr_Format(PyExc_StandardError, "Character uid %d does not exist.", PyChar_AsUid(self)); return -1; } if(arg == Py_None) { charSetDialog(ch, NULL); return 0; } else if(PyString_Check(arg)) { const char *key = get_fullkey_relative(PyString_AsString(arg), get_script_locale()); DIALOG_DATA *dialog = worldGetType(gameworld, "dialog", key); if(dialog == NULL) { PyErr_Format(PyExc_StandardError, "dialog, %s, does not exist!\r\n", key); return -1; } else { charSetDialog(ch, key); return 0; } } else { PyErr_Format(PyExc_TypeError, "Character dialog must be a string key"); return -1; } } // // if a character is in dialog, terminates the dialog PyObject *PyChar_EndDialog(PyObject *self, void *closure) { // get our character represenetation CHAR_DATA *ch = PyChar_AsChar(self); if(ch != NULL) { if(charGetDialogTalker(ch) != NULL) charEndDialog(ch); return Py_BuildValue("i", 1); } else { PyErr_Format(PyExc_StandardError, "tried to end dialog for nonexistant " "Char, %d", PyChar_AsUid(self)); return NULL; } } // // starts a dialog with someone (the person with the dialog on them). Sends an // optional start message to the person. If the optional message is not // provided, then the dialoger's default greet message is displayed. PyObject *PyChar_TryDialog(PyObject *self, PyObject *args, void *closure) { // try to parse our arguments PyObject *pydialoger = NULL; char *mssg = NULL; CHAR_DATA *dialoger = NULL; if(!PyArg_ParseTuple(args, "O|s", &pydialoger, &mssg)) { PyErr_Format(PyExc_TypeError,"Improper arguments supplied to dialog_start"); return NULL; } // parse out our character CHAR_DATA *ch = PyChar_AsChar(self); if(ch == NULL) { PyErr_Format(PyExc_StandardError, "Character uid %d does not exist.", PyChar_AsUid(self)); return NULL; } // parse out our dialoger if(PyChar_Check(pydialoger)) { dialoger = PyChar_AsChar(pydialoger); if(dialoger == NULL) { PyErr_Format(PyExc_StandardError, "Dialog char uid %d does not exist.", PyChar_AsUid(pydialoger)); return NULL; } } else { PyErr_Format(PyExc_TypeError, "The first arg supplied to dialog must be a " "character with a dialog!"); return NULL; } // try doing the dialog try_start_dialog_full(ch, dialoger, mssg); return Py_BuildValue("i", 1); } // // extends the character's dialog with a new question specific to the character PyObject *PyChar_ExtendDialog(PyObject *self, PyObject *args) { CHAR_DATA *ch = PyChar_AsChar(self); char *query = NULL; char *panel = NULL; char *to_panel = NULL; char *check = NULL; char *response = NULL; char *script = NULL; if(!PyArg_ParseTuple(args, "s|sssss", &query, &panel, &to_panel, &check, &response, &script)) { PyErr_Format(PyExc_TypeError,"Improper args supplied to extend_dialog."); return NULL; } // make sure we exist if(ch == NULL) { PyErr_Format(PyExc_StandardError, "character to extend does not exist."); return NULL; } // make the dialog question DIALOG_AUX_DATA *aux = charGetAuxiliaryData(ch, "dialog_data"); DIALOG_QUESTION *question = newDialogQuestion(); dialogQuestionSetQuery(question, query); dialogQuestionSetPanels(question, (panel ? panel : "start")); dialogQuestionSetDestPanel(question, (to_panel ? to_panel : "")); dialogQuestionSetCheck(question, (check ? check : "")); dialogQuestionSetResponse(question, (response ? response : "")); dialogQuestionSetScript(question, (script ? script : "")); listQueue(aux->extensions, question); return Py_BuildValue("i", 1); } //***************************************************************************** // initialization //***************************************************************************** void init_dialogs(void) { // set up our olc init_dedit(); // set up our auxiliary data auxiliariesInstall("dialog_data", newAuxiliaryFuncs(AUXILIARY_TYPE_CHAR, newDialogAuxData, deleteDialogAuxData, dialogAuxDataCopyTo, dialogAuxDataCopy, dialogAuxDataStore, dialogAuxDataRead)); // add our new world type worldAddType(gameworld, "dialog", dialogRead, dialogStore, deleteDialog, dialogSetKey); // set up our hooks hookAdd("char_from_game", stop_dialogs_hook); hookAdd("exit", stop_dialogs_move_hook); hookAdd("greet", try_start_dialog_hook); // set up our Python extensions PyChar_addGetSetter("dialog", PyChar_GetDialog, PyChar_SetDialog, NULL); PyChar_addMethod ("dialog_start", PyChar_TryDialog, METH_VARARGS, NULL); PyChar_addMethod ("dialog_end", PyChar_EndDialog, METH_NOARGS, NULL); PyChar_addMethod ("dialog_extend",PyChar_ExtendDialog, METH_VARARGS, NULL); }