dialog/
//*****************************************************************************
//
// 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);
}