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/
//*****************************************************************************
//
// command.c
//
// A structure that holds all of the data tied to player commands. 
// Specifically, the function that is executed and all of the restrictions on
// its execution.
//
//*****************************************************************************

#include <Python.h>
#include <structmember.h>

#include "mud.h"
#include "utils.h"
#include "action.h"
#include "character.h"



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



//*****************************************************************************
// local structs, defines, and functions
//*****************************************************************************
struct cmd_data {
  char       *name;
  CMD_PTR(func);
  PyObject *pyfunc;
  char *user_group;
  bool  interrupts;
  LIST     *checks;
};

typedef struct cmd_check_data {
  CMD_CHK_PTR(func);
  PyObject *pyfunc;
} CMD_CHK_DATA;



//*****************************************************************************
// functions for manipulating CMD_CHK_DATA
//*****************************************************************************
CMD_CHK_DATA *newCmdCheck(CMD_CHK(func)) {
  CMD_CHK_DATA *data = malloc(sizeof(CMD_CHK_DATA));
  data->func   = func;
  data->pyfunc = NULL;
  return data;
}

CMD_CHK_DATA *newPyCmdCheck(PyObject *pyfunc) {
  CMD_CHK_DATA *data = malloc(sizeof(CMD_CHK_DATA));
  data->func   = NULL;
  data->pyfunc = pyfunc;
  Py_XINCREF(data->pyfunc);
  return data;
}

void deleteCmdCheck(CMD_CHK_DATA *data) {
  if(data->pyfunc) { Py_XDECREF(data->pyfunc); }
  free(data);
}

CMD_CHK_DATA *cmdCheckCopy(CMD_CHK_DATA *data) {
  if(data->func)
    return newCmdCheck(data->func);
  else
    return newPyCmdCheck(data->pyfunc);
}

void cmdCheckCopyTo(CMD_CHK_DATA *from, CMD_CHK_DATA *to) {
  Py_XDECREF(to->pyfunc);
  to->func   = from->func;
  to->pyfunc = from->pyfunc;
  Py_XINCREF(to->pyfunc);
}



//*****************************************************************************
// implementation of command.h
//*****************************************************************************
CMD_DATA *newCmd(const char *name, COMMAND(func), const char *user_group, 
		 bool interrupts) {
  CMD_DATA *cmd = calloc(1, sizeof(CMD_DATA));
  cmd->name     = strdupsafe(name);
  cmd->checks   = newList();
  cmdUpdate(cmd, func, user_group, interrupts);
  return cmd;
}

CMD_DATA *newPyCmd(const char *name, void *pyfunc, const char *user_group, 
		   bool interrupts) {
  CMD_DATA *cmd   = calloc(1, sizeof(CMD_DATA));
  cmd->name       = strdupsafe(name);
  cmd->checks     = newList();
  cmdPyUpdate(cmd, pyfunc, user_group, interrupts);
  return cmd;
}

void cmdUpdate(CMD_DATA *cmd, COMMAND(func), const char *user_group, 
	       bool interrupts) {
  if(cmd->pyfunc)     { Py_DECREF(cmd->pyfunc); cmd->pyfunc = NULL; }
  if(cmd->user_group) free(cmd->user_group);
  cmd->func       = func;
  cmd->user_group = strdupsafe(user_group);
  cmd->interrupts = interrupts;
}

void cmdPyUpdate(CMD_DATA *cmd, void *pyfunc, const char *user_group,
		 bool interrupts) {
  if(cmd->pyfunc)     { Py_DECREF(cmd->pyfunc); cmd->pyfunc = NULL; }
  if(cmd->user_group) free(cmd->user_group);
  cmd->func       = NULL;
  cmd->user_group = strdupsafe(user_group);
  cmd->interrupts = interrupts;
  cmd->pyfunc     = pyfunc;
  Py_XINCREF(cmd->pyfunc);
}

void deleteCmd(CMD_DATA *cmd) {
  if(cmd->name)       free(cmd->name);
  if(cmd->user_group) free(cmd->user_group);
  if(cmd->pyfunc)     { Py_DECREF(cmd->pyfunc); }
  if(cmd->checks)     deleteListWith(cmd->checks, deleteCmdCheck);
  free(cmd);
}

CMD_DATA *cmdCopy(CMD_DATA *cmd) {
  CMD_DATA *newcmd = NULL;
  if(cmd->pyfunc)
    newcmd = newPyCmd(cmd->name, cmd->pyfunc, cmd->user_group, cmd->interrupts);
  else
    newcmd = newCmd(cmd->name, cmd->func, cmd->user_group, cmd->interrupts);
  
  // copy over the checks
  deleteList(newcmd->checks);
  newcmd->checks = listCopyWith(cmd->checks, cmdCheckCopy);
  return newcmd;
}

void cmdCopyTo(CMD_DATA *from, CMD_DATA *to) {
  if(to->name)       free(to->name);
  if(to->user_group) free(to->user_group);
  if(to->pyfunc)     { Py_DECREF(to->pyfunc); }
  to->name         = strdup(from->name);
  to->user_group   = strdup(from->user_group);
  to->pyfunc       = from->pyfunc;
  if(to->pyfunc)     { Py_INCREF(to->pyfunc); }
  to->func         = from->func;
  to->interrupts   = from->interrupts;
  if(to->checks)     { deleteListWith(to->checks, deleteCmdCheck); }
  to->checks       = listCopyWith(from->checks, cmdCheckCopy);
}

const char *cmdGetName(CMD_DATA *cmd) {
  return cmd->name;
}

const char *cmdGetUserGroup(CMD_DATA *cmd) {
  return cmd->user_group;
}

bool cmdGetInterrupts(CMD_DATA *cmd) {
  return cmd->interrupts;
}

LIST *cmdGetChecks(CMD_DATA *cmd) {
  return cmd->checks;
}

void cmdAddCheck(CMD_DATA *cmd, CMD_CHK(func)) {
  listPut(cmdGetChecks(cmd), newCmdCheck(func));
}

void cmdAddPyCheck(CMD_DATA *cmd, void *pyfunc) {
  listPut(cmdGetChecks(cmd), newPyCmdCheck(pyfunc));
}

//
// make sure the character is in a position where this can be performed
bool min_pos_ok(CHAR_DATA *ch, int minpos) {
  if(poscmp(charGetPos(ch), minpos) >= 0)
    return TRUE;
  else {
    switch(charGetPos(ch)) {
    case POS_UNCONSCIOUS:
      send_to_char(ch, "You cannot do that while unconscious!\r\n");
      break;
    case POS_SLEEPING:
      send_to_char(ch, "Not while sleeping, you won't!\r\n");
      break;
    case POS_SITTING:
      send_to_char(ch, "You cannot do that while sitting!\r\n");
      break;
    case POS_STANDING:
      // flying is the highest position... we can deduce this message
      send_to_char(ch, "You must be flying to try that.\r\n");
      break;
    case POS_FLYING:
      send_to_char(ch, "That is not possible in any position you can think of.\r\n");
      break;
    default:
      send_to_char(ch, "Your position is all wrong!\r\n");
      log_string("Character, %s, has invalid position, %d.",
		 charGetName(ch), charGetPos(ch));
      break;
    }
    return FALSE;
  }
}


//
// make sure the character is in a position where this can be performed
bool max_pos_ok(CHAR_DATA *ch, int minpos) {
  if(poscmp(charGetPos(ch), minpos) <= 0)
    return TRUE;
  else {
    switch(charGetPos(ch)) {
    case POS_UNCONSCIOUS:
      send_to_char(ch, "You're still too alive to try that!\r\n");
      break;
    case POS_SLEEPING:
      send_to_char(ch, "Not while sleeping, you won't!\r\n");
      break;
    case POS_SITTING:
      send_to_char(ch, "You cannot do that while sitting!\r\n");
      break;
    case POS_STANDING:
      send_to_char(ch, "You cannot do that while standing.\r\n");
      break;
    case POS_FLYING:
      send_to_char(ch, "You must land first.\r\n");
      break;
    default:
      send_to_char(ch, "Your position is all wrong!\r\n");
      log_string("Character, %s, has invalid position, %d.",
		 charGetName(ch), charGetPos(ch));
      break;
    }
    return FALSE;
  }
}

bool cmdTryChecks(CHAR_DATA *ch, CMD_DATA *cmd) {
  bool cmd_ok = TRUE;
  if(listSize(cmd->checks) > 0) {
    LIST_ITERATOR *chk_i = newListIterator(cmd->checks);
    CMD_CHK_DATA    *chk = NULL;
    ITERATE_LIST(chk, chk_i) {
      if(chk->func)
	cmd_ok = (chk->func)(ch, cmd->name);
      else {
	PyObject *arglist = Py_BuildValue("Os", charGetPyFormBorrowed(ch), 
					  cmd->name);
	PyObject *retval  = PyEval_CallObject(chk->pyfunc, arglist);
	// check for an error:
	if(retval == NULL)
	  log_pyerr("Error running Python command check, %s:", cmd->name);
	else if(retval == Py_False)
	  cmd_ok = FALSE;

	// garbage collection
	Py_XDECREF(retval);
	Py_XDECREF(arglist);
      }

      if(cmd_ok == FALSE)
	break;
    } deleteListIterator(chk_i);
  }
  return cmd_ok;
}

bool cmdHasFunc(CMD_DATA *cmd) {
  return (cmd->func != NULL || cmd->pyfunc != NULL);
}

int charTryCmd(CHAR_DATA *ch, CMD_DATA *cmd, char *arg) {
  // first, go through all of our checks
  if(!cmdTryChecks(ch, cmd))
    return FALSE;
  /*
  if(!min_pos_ok(ch, cmd->min_pos) || !max_pos_ok(ch,cmd->max_pos) ||
     (charIsNPC(ch) && !cmd->mob_ok))
    return FALSE;
  */
  else {
    if(cmd->interrupts) {
#ifdef MODULE_FACULTY
      interrupt_action(ch, FACULTY_ALL);
#else
      interrupt_action(ch, 1);
#endif
    }
    if(cmd->func) {
      (cmd->func)(ch, cmd->name, arg);
      return TRUE;
    }
    else if(cmd->pyfunc) {
      PyObject *arglist = Py_BuildValue("Oss", charGetPyFormBorrowed(ch), 
					cmd->name, arg);
      PyObject *retval  = PyEval_CallObject(cmd->pyfunc, arglist);
      // check for an error:
      if(retval == NULL)
	log_pyerr("Error running Python command, %s:", cmd->name);

      // garbage collection
      Py_XDECREF(retval);
      Py_XDECREF(arglist);
      return TRUE;
    }
    // command is null (but there might have been checks)
    else
      return -1;
  }
}

CMD_CHK(chk_can_move) {
  return min_pos_ok(ch, POS_STANDING);
}

CMD_CHK(chk_conscious) {
  return min_pos_ok(ch, POS_SITTING);
}

CMD_CHK(chk_grounded) {
  return max_pos_ok(ch, POS_STANDING);
}

CMD_CHK(chk_not_mob) {
  if(charIsNPC(ch))
    send_to_char(ch, "NPCs may not use this command.\r\n");
  return !charIsNPC(ch);
}