nakedmud-mod/
nakedmud-mod/html/tutorials/
nakedmud-mod/html/tutorials/building_extras/
nakedmud-mod/html/tutorials/c/
nakedmud-mod/html/tutorials/reference/
nakedmud-mod/html/tutorials/scripting/
nakedmud-mod/html/tutorials/scripting_extras/
nakedmud-mod/lib/
nakedmud-mod/lib/help/A/
nakedmud-mod/lib/help/B/
nakedmud-mod/lib/help/C/
nakedmud-mod/lib/help/D/
nakedmud-mod/lib/help/G/
nakedmud-mod/lib/help/H/
nakedmud-mod/lib/help/J/
nakedmud-mod/lib/help/L/
nakedmud-mod/lib/help/M/
nakedmud-mod/lib/help/O/
nakedmud-mod/lib/help/P/
nakedmud-mod/lib/help/R/
nakedmud-mod/lib/help/S/
nakedmud-mod/lib/help/W/
nakedmud-mod/lib/logs/
nakedmud-mod/lib/misc/
nakedmud-mod/lib/players/
nakedmud-mod/lib/pymodules/polc/
nakedmud-mod/lib/txt/
nakedmud-mod/lib/world/
nakedmud-mod/lib/world/zones/examples/
nakedmud-mod/lib/world/zones/examples/mproto/
nakedmud-mod/lib/world/zones/examples/oproto/
nakedmud-mod/lib/world/zones/examples/reset/
nakedmud-mod/lib/world/zones/examples/rproto/
nakedmud-mod/lib/world/zones/examples/trigger/
nakedmud-mod/lib/world/zones/limbo/
nakedmud-mod/lib/world/zones/limbo/room/
nakedmud-mod/lib/world/zones/limbo/rproto/
nakedmud-mod/src/alias/
nakedmud-mod/src/dyn_vars/
nakedmud-mod/src/editor/
nakedmud-mod/src/example_module/
nakedmud-mod/src/help2/
nakedmud-mod/src/set_val/
nakedmud-mod/src/socials/
nakedmud-mod/src/time/
/*
 * This file handles command interpreting
 */
#include <sys/types.h>
#include <stdio.h>

/* include main header file */
#include "mud.h"
#include "utils.h"
#include "character.h"
#include "socket.h"
#include "room.h"
#include "commands.h"
#include "action.h"
#include "hooks.h"



//*****************************************************************************
// mandatory modules
//*****************************************************************************
#include "scripts/scripts.h"



//*****************************************************************************
// optional modules
//*****************************************************************************
#ifdef MODULE_FACULTY
#include "faculty/faculty.h"
#endif
#ifdef MODULE_ALIAS
#include "alias/alias.h"
#endif



//*****************************************************************************
// local variables and functions
//*****************************************************************************
NEAR_MAP *cmd_table = NULL;

void init_commands() {
  cmd_table = newNearMap();

  //***************************************************************************
  // This is for core functions ONLY! If you have a module that adds new
  // functions to the MUD, they should be added in the init_xxx() function
  // associated with your module.
  //***************************************************************************
  add_cmd("back",       NULL, cmd_back,        "player", FALSE);
  add_cmd("commands",   NULL, cmd_commands,    "player", FALSE);
  //add_cmd("compress",   NULL, cmd_compress,    "player", FALSE);
  //add_cmd("groupcmds",  NULL, cmd_groupcmds,   "player", FALSE);
  add_cmd("look",       "l",  cmd_look,        "player", FALSE);
  add_cmd("more",       NULL, cmd_more,        "player", FALSE);
  add_cmd_check("look",        chk_conscious);
}

bool cmd_exists(const char *cmd) {
  return nearMapKeyExists(cmd_table, cmd);
}

CMD_DATA *remove_cmd(const char *cmd) {
  return nearMapRemove(cmd_table, cmd);
}

void add_cmd(const char *cmd, const char *sort_by, COMMAND(func),
	     const char *user_group, bool interrupts) {
  // do we already have an existing command we're just updating?
  CMD_DATA *data = remove_cmd(cmd);

  // is it something that needs to be updated or erased?
  if(data != NULL && !cmdHasFunc(data))
    cmdUpdate(data, func, user_group, interrupts);
  else {
    if(data != NULL)
      deleteCmd(data);
    data = newCmd(cmd, func, user_group, interrupts);
  }

  // add in the new command
  nearMapPut(cmd_table, cmd, sort_by, data);
}

void add_py_cmd(const char *cmd, const char *sort_by, void *pyfunc,
	     const char *user_group, bool interrupts) {
  // do we already have an existing command we're just updating?
  CMD_DATA *data = remove_cmd(cmd);

  // is this something that needs to be updated or erased?
  if(data != NULL && !cmdHasFunc(data))
    cmdPyUpdate(data, pyfunc, user_group, interrupts);
  else {
    if(data != NULL)
      deleteCmd(data);
    data = newPyCmd(cmd, pyfunc, user_group, interrupts);
  }

  // add in the new command
  nearMapPut(cmd_table, cmd, sort_by, data);
}

void add_cmd_check(const char *cmd, CMD_CHK(func)) {
  CMD_DATA *data = nearMapGet(cmd_table, cmd, FALSE);

  // make a temp container for just the checks
  if(data == NULL) {
    data = newCmd(cmd, NULL, "", FALSE);
    nearMapPut(cmd_table, cmd, NULL, data);
  }
    
  cmdAddCheck(data, func);
}

void add_py_cmd_check(const char *cmd, void *pyfunc) {
  CMD_DATA *data = nearMapGet(cmd_table, cmd, FALSE);

  // make a temp container just for the checks
  if(data == NULL) {
    data = newCmd(cmd, NULL, "", FALSE);
    nearMapPut(cmd_table, cmd, NULL, data);
  }

  cmdAddPyCheck(data, pyfunc);
}

// show the character all of the commands in the specified group(s).
void show_commands(CHAR_DATA *ch, const char *user_groups) {
  BUFFER           *buf = newBuffer(MAX_BUFFER);
  NEAR_ITERATOR *near_i = newNearIterator(cmd_table);
  const char    *abbrev = NULL;
  CMD_DATA         *cmd = NULL;
  int               col = 0;

  // go over all of our buckets
  ITERATE_NEARMAP(abbrev, cmd, near_i) {
    if(cmdHasFunc(cmd) && is_keyword(user_groups, cmdGetUserGroup(cmd), FALSE)){
      bprintf(buf, "%-13.13s", cmdGetName(cmd));
      if (!(++col % 6))
	bufferCat(buf, "\r\n");
    }
  } deleteNearIterator(near_i);

  // do room commands as well
  if(roomHasCmds(charGetRoom(ch))) {
    near_i = newNearIterator(roomGetCmdTable(charGetRoom(ch)));
    abbrev = NULL;
    cmd    = NULL;
    bufferCat(buf, "{c");
    ITERATE_NEARMAP(abbrev, cmd, near_i) {
      if(cmdHasFunc(cmd) && is_keyword(user_groups,cmdGetUserGroup(cmd),FALSE)){
	bprintf(buf, "%-13.13s", cmdGetName(cmd));
	if (!(++col % 6))
	  bufferCat(buf, "\r\n");
      }
    } deleteNearIterator(near_i);
    bufferCat(buf, "{n");
  }

  // tag on our last newline if neccessary, and show commands
  if (col % 6) bprintf(buf, "\r\n");
  text_to_char(ch, bufferString(buf));
  deleteBuffer(buf);
}

//
// return whether the command is usable by the character
bool is_usable_cmd(CHAR_DATA *ch, CMD_DATA *cmd) {
  // this is a check, not a command
  if(*cmdGetUserGroup(cmd) == '\0')
    return FALSE;
  return is_keyword(bitvectorGetBits(charGetUserGroups(ch)),
		    cmdGetUserGroup(cmd), FALSE);
}

//
// checks are commands without a specific user group, added to finer-grain 
// command tables (e.g., characters, to rooms, to zones, to the world). This
// is for e.g., allowing for the addition of checks before leaving a room
// (maybe you have to move a boulder out of the way first, etc...)
CMD_DATA *find_check(CHAR_DATA *ch, NEAR_MAP *table, const char *name) {
  CMD_DATA *check = nearMapGet(table, name, FALSE);
  /*
  if(check == NULL) || *cmdGetUserGroup(check) != '\0')
    return NULL;
  */
  return check;
}

//
// tries to find the relevant command, usable by the character
CMD_DATA *find_cmd(CHAR_DATA *ch, NEAR_MAP *table, const char *name, 
		   bool abbrev_ok) {
  if(abbrev_ok == FALSE) {
    CMD_DATA *cmd = nearMapGet(table, name, FALSE);
    // no command exists
    if(cmd == NULL || !is_usable_cmd(ch, cmd))
      return NULL;
    return cmd;
  }
  else {
    // try to look up the possible commands
    LIST *cmdname_list = nearMapGetAllMatches(table, name);
    CMD_DATA  *cmd = NULL;
    if(cmdname_list != NULL) {
      LIST_ITERATOR *cmdname_i = newListIterator(cmdname_list);
      const char      *cmdname = NULL;
      ITERATE_LIST(cmdname, cmdname_i) {
	cmd = nearMapGet(table, cmdname, FALSE);
	if(is_usable_cmd(ch, cmd))
	  break;
	else
	  cmd = NULL;
      } deleteListIterator(cmdname_i);
      deleteListWith(cmdname_list, free);
    }
    return cmd;
  }
}

// tries to pull a usable command from the near-table and use it. Returns
// TRUE if a usable command was found (even if it failed) and false otherwise.
bool try_use_cmd_table(CHAR_DATA *ch, NEAR_MAP *table, const char *command, 
		       char *arg, bool abbrev_ok) {
  if(abbrev_ok == FALSE) {
    CMD_DATA *cmd = nearMapGet(table, command, FALSE);
    if(cmd == NULL)
      return FALSE;
    else if(!*cmdGetUserGroup(cmd) || 
	    is_keyword(bitvectorGetBits(charGetUserGroups(ch)),
		       cmdGetUserGroup(cmd), FALSE)) {
      if(charTryCmd(ch, cmd, arg) == -1)
	return FALSE;
      return TRUE;
    }
    else
      return FALSE;
  }
  else {
    // try to look up the possible commands
    LIST *cmdname_list = nearMapGetAllMatches(table, command);
    bool           ret = FALSE;
    if(cmdname_list != NULL) {
      LIST_ITERATOR *cmdname_i = newListIterator(cmdname_list);
      CMD_DATA            *cmd = NULL;
      const char      *cmdname = NULL;
      ITERATE_LIST(cmdname, cmdname_i) {
	cmd = nearMapGet(table, cmdname, FALSE);
	if(!*cmdGetUserGroup(cmd) ||
	   is_keyword(bitvectorGetBits(charGetUserGroups(ch)), 
		      cmdGetUserGroup(cmd), FALSE)) {
	  if(charTryCmd(ch, cmd, arg) != -1)
	    ret = TRUE;
	  break;
	}
      } deleteListIterator(cmdname_i);
      deleteListWith(cmdname_list, free);
    }
    return ret;
  }
}

void handle_cmd_input(SOCKET_DATA *dsock, char *arg) {
  CHAR_DATA *ch;
  if ((ch = socketGetChar(dsock)) == NULL)
    return;
  do_cmd(ch, arg, TRUE);
}

void do_cmd(CHAR_DATA *ch, char *arg, bool aliases_ok)  {
  char command[MAX_BUFFER];

  // make sure we've got a command to try
  if(!arg || !*arg)
    return;

  // if we are leading with a non-character, we are trying to do a short-form
  // command (e.g. ' for say, " for gossip). Just take the first character
  // and use the rest as the arg
  if(isalpha(*arg) || isdigit(*arg))
    arg = one_arg(arg, command);
  else {
    *command     = *arg;
    *(command+1) = '\0';
    arg++;
    // and skip all spaces
    while(isspace(*arg))
      arg++;
  }

#ifdef MODULE_ALIAS
  if(aliases_ok && try_alias(ch, command, arg))
    return;
#endif

  // check to see if it's a faculty command
#ifdef MODULE_FACULTY
  if(try_use_faculty(ch, command))
    return;
#endif

  // figure out what tables we need to look over
  LIST *cmd_tables = newList();
  // item-specific commands here? <---
  // character-specific commands here? <---
  if(charGetRoom(ch) && roomHasCmds(charGetRoom(ch)))
    listQueue(cmd_tables, roomGetCmdTable(charGetRoom(ch)));
  // zone-specific commands here? <---
  listQueue(cmd_tables, cmd_table);

  // go through each table in the list, in order. Check if the command exists
  // in any of the tables. Only allow abbreviations in the last table (the
  // game commands). Before a command is executed, run checks that might be
  // included on any of the previous tables
  int  i, j, ret;
  bool found = FALSE;
  for(i = 0; i < listSize(cmd_tables); i++) {
    NEAR_MAP *table = listGet(cmd_tables, i);
    CMD_DATA   *cmd = find_cmd(ch, table, command, 
			       (i == listSize(cmd_tables)-1 ? TRUE : FALSE));
    if(cmd != NULL) {
      // first, run checks on all our previous tables
      for(j = 0; j < i; j++) {
	CMD_DATA *check = find_check(ch,listGet(cmd_tables,j),cmdGetName(cmd));
	if(check != NULL) {
	  // run the check
	  if((ret = charTryCmd(ch, check, arg)) != -1) {
	    if(ret == TRUE)
	      hookRun("command", hookBuildInfo("ch str str",
					       ch,cmdGetName(cmd),arg));
 	    found = TRUE;
	    break;
	  }
	}
      }
    }

    if(found == TRUE)
      break;
    else if(cmd != NULL) {
      // execute the command
      if((ret = charTryCmd(ch, cmd, arg)) != -1) {
	if(ret == TRUE)
	  hookRun("command",hookBuildInfo("ch str str",ch,cmdGetName(cmd),arg));
	found = TRUE;
	break;
      }
    }
  }

  // nothing usable was found
  if(found == FALSE)
    text_to_char(ch, "No such command.\r\n");

  // garbage collection
  deleteList(cmd_tables);

  /*
  // try the command
  if(!charGetRoom(ch) || 
     !try_use_cmd_table(ch,roomGetCmdTable(charGetRoom(ch)),command,arg,FALSE))
    if(!try_use_cmd_table(ch, cmd_table, command, arg, TRUE))
      text_to_char(ch, "No such command.\r\n");
  */
}