quest/
//*****************************************************************************
//
// qedit.c
//
// An OLC tool for quests. Allows builders to create and alter the info for
// quests. Works in a similar fashion to redit, medit, oedit.
//
//*****************************************************************************
#include "../mud.h"
#include "../utils.h"
#include "../world.h"
#include "../zone.h"
#include "../character.h"
#include "../socket.h"
#include "../prototype.h"
#include "../room.h"
#include "quest.h"
#include "qedit.h"



//*****************************************************************************
// mandatory modules
//*****************************************************************************
#include "../olc2/olc.h"
#include "../editor/editor.h"
#include "../scripts/scripts.h"
#include "../scripts/script_editor.h"



//*****************************************************************************
// olc functions for quest objectives
//*****************************************************************************

#define QOEDIT_TYPE             1
#define QOEDIT_DESC             2
#define QOEDIT_KILL_ENEMY       3
#define QOEDIT_KILL_TIMES       4
#define QOEDIT_GIVE_RECEIVER    5
#define QOEDIT_GIVE_ITEM        6
#define QOEDIT_GIVE_COUNT       7
#define QOEDIT_APPROACH_PERSON  8

const char *qoedit_types[] = {
  "kill",
  "give",
  "approach",
  "indefinite",
  NULL,
};

//
// returns the name of the quest objective type for the given number
const char *qoeditGetName(int num) {
  return qoedit_types[num];
}

//
// keeps track of how many quest objective types we can edit in qedit
int num_qoedit_types() {
  static int num = -1;
  if(num == -1) {
    do {
      num++;
    } while(qoedit_types[num] != NULL);
  }
  return num;
}

//
// display the qedit menu for the approach objective
void qoedit_approach_menu(SOCKET_DATA *sock, QUEST_OBJECTIVE *data) {
  send_to_socket(sock,
		 "{g3) person  : {c%s\r\n",
		 questObjectiveGetVar(data, "person"));
}

//
// display the qedit menu for kill objectives
void qoedit_kill_menu(SOCKET_DATA *sock, QUEST_OBJECTIVE *data) {
  send_to_socket(sock, 
		 "{g3) enemy   : {c%s\r\n"
		 "{g4) times   : {c%s\r\n",
		 questObjectiveGetVar(data, "enemy"),
		 questObjectiveGetVar(data, "times"));
}

//
// display the qedit menu for give objectives
void qoedit_give_menu(SOCKET_DATA *sock, QUEST_OBJECTIVE *data) {
  send_to_socket(sock, 
		 "{g3) person  : {c%s\r\n"
		 "{g4) item    : {c%s\r\n"
		 "{g5) count   : {c%s\r\n",
		 questObjectiveGetVar(data, "person"),
		 questObjectiveGetVar(data, "item"),
		 questObjectiveGetVar(data, "count"));
}

void qoedit_menu(SOCKET_DATA *sock, QUEST_OBJECTIVE *data) {
  send_to_socket(sock, 
		 "{g1) type    : {c%s\r\n"  
		 "{g2) desc    : {c%s\r\n", 
		 questObjectiveGetType(data), questObjectiveGetDesc(data));
  if(!strcasecmp("kill", questObjectiveGetType(data)))
    qoedit_kill_menu(sock, data);
  else if(!strcasecmp("give", questObjectiveGetType(data)))
    qoedit_give_menu(sock, data);
  else if(!strcasecmp("approach", questObjectiveGetType(data)))
    qoedit_approach_menu(sock, data);
  send_to_socket(sock, "{n");
}

int qoedit_chooser(SOCKET_DATA *sock, QUEST_OBJECTIVE *data,const char *option){
  switch(toupper(*option)) {
  case '1':
    olc_display_table(sock, qoeditGetName, num_qoedit_types(), 1);
    send_to_socket(sock, "Enter a choice: ");
    return QOEDIT_TYPE;
  case '2':
    send_to_socket(sock, "Enter a description of the objective: ");
    return QOEDIT_DESC;
  case '3':
    if(!strcasecmp("kill", questObjectiveGetType(data))) {
      send_to_socket(sock, "Enter an enemy to kill: ");
      return QOEDIT_KILL_ENEMY;
    }
    else if(!strcasecmp("give", questObjectiveGetType(data))) {
      send_to_socket(sock, "Who must the items be given to: ");
      return QOEDIT_GIVE_RECEIVER;
    }
    else if(!strcasecmp("approach", questObjectiveGetType(data))) {
      send_to_socket(sock, "Who must be approached: ");
      return QOEDIT_APPROACH_PERSON;
    }
    else
      return MENU_CHOICE_INVALID;
  case '4':
    if(!strcasecmp("kill", questObjectiveGetType(data))) {
      send_to_socket(sock, "Enter a number of enemies to kill: ");
      return QOEDIT_KILL_TIMES;
    }
    else if(!strcasecmp("give", questObjectiveGetType(data))) {
      send_to_socket(sock, "Enter the item to give: ");
      return QOEDIT_GIVE_ITEM;
    }
    else
      return MENU_CHOICE_INVALID;
  case '5':
    if(!strcasecmp("give", questObjectiveGetType(data))) {
      send_to_socket(sock, "How many copies of the item must be given: ");
      return QOEDIT_GIVE_COUNT;
    }
    else
      return MENU_CHOICE_INVALID;
  default:
    return MENU_CHOICE_INVALID;
  }
}

bool qoedit_parser(SOCKET_DATA *sock, QUEST_OBJECTIVE *data, int choice, 
		  const char *arg) {
  switch(choice) {
  case QOEDIT_TYPE: {
    if(!isdigit(*arg))
      return FALSE;
    else {
      int val = atoi(arg);
      if(val < 0 || val >= num_qoedit_types())
	return FALSE;
      else {
	questObjectiveClearVars(data);
	questObjectiveSetType(data, qoedit_types[val]);

	// set our default values
	if(!strcasecmp("kill", qoedit_types[val])) {
	  questObjectiveSetVar(data, "enemy", "");
	  questObjectiveSetVar(data, "times", "0");
	}
	else if(!strcasecmp("give", qoedit_types[val])) {
	  questObjectiveSetVar(data, "person", "");
	  questObjectiveSetVar(data, "item",   "");
	  questObjectiveSetVar(data, "count",  "0");
	}
	else if(!strcasecmp("approach", qoedit_types[val])) {
	  questObjectiveSetVar(data, "person", "");
	}
	return TRUE;
      }
    }
  }
  case QOEDIT_DESC:
    questObjectiveSetDesc(data, arg);
    return TRUE;
  case QOEDIT_KILL_ENEMY:
    questObjectiveSetVar(data, "enemy", arg);
    return TRUE;
  case QOEDIT_KILL_TIMES:
    if(!isdigit(*arg))
      return FALSE;
    else {
      questObjectiveSetVar(data, "times", arg);
      return TRUE;
    }
  case QOEDIT_GIVE_RECEIVER:
    questObjectiveSetVar(data, "person", arg);
    return TRUE;
  case QOEDIT_GIVE_ITEM:
    questObjectiveSetVar(data, "item",  arg);
    return TRUE;
  case QOEDIT_GIVE_COUNT:
    if(!isdigit(*arg))
      return FALSE;
    else {
      questObjectiveSetVar(data, "count", arg);
      return TRUE;
    }
  case QOEDIT_APPROACH_PERSON:
    questObjectiveSetVar(data, "person", arg);
    return TRUE;
  default:
    return FALSE;
  }
}



//*****************************************************************************
// olc functions for quest stages
//*****************************************************************************

#define QSEDIT_NAME               1
#define QSEDIT_EDIT_OBJECTIVE     2
#define QSEDIT_DELETE_OBJECTIVE   3

//
// displays info for one objective to a socket
void disp_one_objective(SOCKET_DATA *sock, QUEST_OBJECTIVE *ob) {
  // need to get this, for seeing our locale
  QUEST_DATA *quest = questStageGetQuest(questObjectiveGetStage(ob));
  if(!strcasecmp("kill", questObjectiveGetType(ob))) {
    PROTO_DATA *mproto = 
      worldGetType(gameworld, "mproto", 
		   get_fullkey_relative(questObjectiveGetVar(ob, "enemy"),
					get_key_locale(questGetKey(quest))));
    send_to_socket(sock, "{cKill %s %s times.\r\n",
		   (mproto ? protoGetKey(mproto) : "{rNOBODY{c"),
		   questObjectiveGetVar(ob, "times"));
  }
  else if(!strcasecmp("give", questObjectiveGetType(ob))) {
    PROTO_DATA *mproto = 
      worldGetType(gameworld, "mproto", 
		   get_fullkey_relative(questObjectiveGetVar(ob, "person"),
					get_key_locale(questGetKey(quest))));
    PROTO_DATA *oproto = 
      worldGetType(gameworld, "oproto", 
		   get_fullkey_relative(questObjectiveGetVar(ob, "item"),
					get_key_locale(questGetKey(quest))));
    send_to_socket(sock, "{cGive %s copies of %s to %s.\r\n",
		   questObjectiveGetVar(ob, "count"),
		   (oproto ? protoGetKey(oproto) : "{rNOTHING{c"),
		   (mproto ? protoGetKey(mproto) : "{rNOBODY{c"));
  }
  else if(!strcasecmp("approach", questObjectiveGetType(ob))) {
    PROTO_DATA *mproto = 
      worldGetType(gameworld, "mproto", 
		   get_fullkey_relative(questObjectiveGetVar(ob, "person"),
					get_key_locale(questGetKey(quest))));
    send_to_socket(sock, "{cApproach %s about your deeds.\r\n",
		   (mproto ? protoGetKey(mproto) : "{rNOBODY{c"));
  }
  else if(!strcasecmp("indefinite", questObjectiveGetType(ob))) {
    send_to_socket(sock, "{cNo specific goal for this objective.\r\n");
  }
  else
    send_to_socket(sock, "{cUnknown objective, {r%s{c.\r\n", 
		   questObjectiveGetType(ob));
}

void qsedit_menu(SOCKET_DATA *sock, QUEST_STAGE *data) {
  send_to_socket(sock, 
		 "{g1) Name\r\n"
		 "{c%s\r\n"
		 "{g2) End Script\r\n",
		 questStageGetName(data));
  script_display(sock, questStageGetEndScript(data), FALSE);
  send_to_socket(sock, "\r\n{wObjectives:\r\n");
  LIST_ITERATOR *ob_i = newListIterator(questStageGetObjectives(data));
  QUEST_OBJECTIVE *ob = NULL;
  int i = 0;
  ITERATE_LIST(ob, ob_i) {
    send_to_socket(sock, "  {g%d) ", i);
    disp_one_objective(sock, ob);
    i++;
  } deleteListIterator(ob_i);
  send_to_socket(sock, 
		 "\r\n"
		 "  {gE) Edit objective\r\n"
		 "  {gN) New objective\r\n"
		 "  {gD) Delete objective\r\n");
}

int qsedit_chooser(SOCKET_DATA *sock, QUEST_STAGE *data, const char *option) {
  switch(toupper(*option)) {
  case '1':
    send_to_socket(sock, "Enter the name of the stage: ");
    return QSEDIT_NAME;
  case '2':
    socketStartEditor(sock, script_editor, questStageGetEndScriptBuf(data));
    return MENU_NOCHOICE;
  case 'N': {
    QUEST_OBJECTIVE *ob = newQuestObjective();
    questStageAddObjective(data, ob);
    do_olc(sock, qoedit_menu, qoedit_chooser, qoedit_parser,
	   NULL, NULL, NULL, NULL, ob);
    return MENU_NOCHOICE;
  }
  case 'E':
    send_to_socket(sock, "Which objective do you want to edit: ");
    return QSEDIT_EDIT_OBJECTIVE;
  case 'D':
    if(listSize(questStageGetObjectives(data)) == 0)
      return MENU_CHOICE_INVALID;
    send_to_socket(sock, "Which objective do you want to delete: ");
    return QSEDIT_DELETE_OBJECTIVE;
  default:
    return MENU_CHOICE_INVALID;
  }
}

bool qsedit_parser(SOCKET_DATA *sock, QUEST_STAGE *data, int choice, 
		  const char *arg) {
  switch(choice) {
  case QSEDIT_NAME:
    questStageSetName(data, arg);
    return TRUE;
  case QSEDIT_EDIT_OBJECTIVE:
    if(!isdigit(*arg) || listSize(questStageGetObjectives(data)) == 0)
      return FALSE;
    else {
      QUEST_OBJECTIVE *ob = listGet(questStageGetObjectives(data), atoi(arg));
      if(ob == NULL)
	return FALSE;
      else {
	do_olc(sock, qoedit_menu, qoedit_chooser, qoedit_parser,
	       NULL, NULL, NULL, NULL, ob);
	return TRUE;
      }
    }
  case QSEDIT_DELETE_OBJECTIVE:
    if(!isdigit(*arg))
      return FALSE;
    else {
      QUEST_OBJECTIVE *ob = questStageRemoveObjectiveNum(data, atoi(arg));
      if(ob != NULL)
	deleteQuestObjective(ob);
      return TRUE;
    }
  default:
    return FALSE;
  }
}



//*****************************************************************************
// olc functions for quests
//*****************************************************************************
#define QEDIT_NAME          1
#define QEDIT_EDIT_STAGE    2
#define QEDIT_DELETE_STAGE  3

void qedit_menu(SOCKET_DATA *sock, QUEST_DATA *data) {
  send_to_socket(sock,
		 "{y[{c%s{y]\r\n"
		 "{g1) Name\r\n"
		 "{c%s\r\n"
		 "{g2) Description\r\n"
		 "{c%s\r\n", 
		 questGetKey(data), questGetName(data), questGetDesc(data));
  send_to_socket(sock, "{wStages:\r\n");
  LIST_ITERATOR *st_i = newListIterator(questGetStages(data));
  QUEST_STAGE     *st = NULL;
  int i = 0;
  ITERATE_LIST(st, st_i) {
    send_to_socket(sock, "  {g%d) {c%s\r\n", i, questStageGetName(st));
    i++;
  } deleteListIterator(st_i);
  send_to_socket(sock,
		 "\r\n"
		 "  {gE) Edit Stage\r\n"
		 "  {gN) New Stage\r\n"
		 "  {gD) Delete Stage\r\n");
}

int qedit_chooser(SOCKET_DATA *sock, QUEST_DATA *data, const char *option) {
  switch(toupper(*option)) {
  case '1':
    send_to_socket(sock, "Enter new name for the quest: ");
    return QEDIT_NAME;
  case '2':
    socketStartEditor(sock, text_editor, questGetDescBuf(data));
    return MENU_NOCHOICE;
  case 'E':
    if(listSize(questGetStages(data)) == 0)
      return MENU_CHOICE_INVALID;
    send_to_socket(sock, "Which stage do you want to edit: ");
    return QEDIT_EDIT_STAGE;
  case 'N': {
    QUEST_STAGE *stage = newQuestStage();
    questAddStage(data, stage);
    do_olc(sock, qsedit_menu, qsedit_chooser, qsedit_parser,
	   NULL, NULL, NULL, NULL, stage);
    return MENU_NOCHOICE;
  }
  case 'D':
    if(listSize(questGetStages(data)) == 0)
      return MENU_CHOICE_INVALID;
    send_to_socket(sock, "Which stage do you want to delete: ");
    return QEDIT_DELETE_STAGE;
  default:
    return MENU_CHOICE_INVALID;
  }
}

bool qedit_parser(SOCKET_DATA *sock, QUEST_DATA *data, int choice, 
		  const char *arg) {
  switch(choice) {
  case QEDIT_NAME:
    questSetName(data, arg);
    return TRUE;
  case QEDIT_EDIT_STAGE:
    if(!isdigit(*arg))
      return FALSE;
    else {
      QUEST_STAGE *stage = listGet(questGetStages(data), atoi(arg));
      if(stage == NULL)
	return FALSE;
      else {
	do_olc(sock, qsedit_menu, qsedit_chooser, qsedit_parser,
	       NULL, NULL, NULL, NULL, stage);
	return TRUE;
      }
    }
  case QEDIT_DELETE_STAGE:
    if(!isdigit(*arg))
      return FALSE;
    else {
      QUEST_STAGE *stage = questRemoveStageNum(data, atoi(arg));
      if(stage != NULL)
	deleteQuestStage(stage);
      return TRUE;
    }
  default:
    return FALSE;
  }
}



//*****************************************************************************
// builder commands
//*****************************************************************************
void olc_save_quest(QUEST_DATA *quest) {
  worldSaveType(gameworld, "quest", questGetKey(quest));
}

COMMAND(cmd_qedit) {
  ZONE_DATA    *zone = NULL;
  QUEST_DATA  *quest = NULL;

  // we need a key
  if(!arg || !*arg)
    send_to_char(ch, "What is the name of the quest you want to edit?\r\n");
  else {
    char locale[SMALL_BUFFER];
    char   name[SMALL_BUFFER];
    if(!parse_worldkey_relative(ch, arg, name, locale))
      send_to_char(ch, "Which quest are you trying to edit?\r\n");
    // make sure we can edit the zone
    else if((zone = worldGetZone(gameworld, locale)) == NULL)
      send_to_char(ch, "No such zone exists.\r\n");
    else if(!canEditZone(zone, ch))
      send_to_char(ch, "You are not authorized to edit that zone.\r\n");
    else {
      // try to pull up the prototype
      quest = worldGetType(gameworld, "quest", get_fullkey(name, locale));
      if(quest == NULL) {
	quest = newQuest();
	questSetName(quest, "An unfinished quest");
	questSetDesc(quest, "Something unfinished has happened, "
		     "and now you must fix it.\r\n");
	worldPutType(gameworld, "quest", get_fullkey(name, locale), quest);
      }
      
      do_olc(charGetSocket(ch), qedit_menu, qedit_chooser, qedit_parser,
 	     questCopy, questCopyTo, deleteQuest, olc_save_quest, quest);
    }
  }
}

// this is used for the header when printing out zone proto info
#define QUEST_LIST_HEADER \
"Name                                                  "

const char *qlist_list_name(QUEST_DATA *quest) {
  static char buf[SMALL_BUFFER];
  sprintf(buf, "%-54s", questGetName(quest));
  return buf;
}

COMMAND(cmd_qlist) {
  do_list(ch, (arg&&*arg?arg:get_key_locale(roomGetClass(charGetRoom(ch)))),
	  "quest", QUEST_LIST_HEADER, qlist_list_name);
}

COMMAND(cmd_qrename) {
  char *from = NULL, *to = NULL;
  if(!parse_args(ch, TRUE, cmd, arg, "word word", &from, &to))
    return;
  do_rename(ch, "quest", from, to);
}

COMMAND(cmd_qdelete) {
  char *name = NULL;
  if(!parse_args(ch, TRUE, cmd, arg, "word", &name))
    return;
  do_delete(ch, "quest", deleteQuest, name);
}



//*****************************************************************************
// initialization
//*****************************************************************************
void init_qedit(void) {
  add_cmd("qedit",   NULL, cmd_qedit,   "builder", TRUE);
  add_cmd("qlist",   NULL, cmd_qlist,   "builder", TRUE);
  add_cmd("qrename", NULL, cmd_qrename, "builder", TRUE);
  add_cmd("qdelete", NULL, cmd_qdelete, "builder", TRUE);
}