nakedmudv3.3/
nakedmudv3.3/lib/
nakedmudv3.3/lib/logs/
nakedmudv3.3/lib/misc/
nakedmudv3.3/lib/players/
nakedmudv3.3/lib/txt/
nakedmudv3.3/lib/world/
nakedmudv3.3/lib/world/examples/
nakedmudv3.3/lib/world/examples/mproto/
nakedmudv3.3/lib/world/examples/oproto/
nakedmudv3.3/lib/world/examples/reset/
nakedmudv3.3/lib/world/examples/rproto/
nakedmudv3.3/lib/world/examples/trigger/
nakedmudv3.3/lib/world/limbo/
nakedmudv3.3/lib/world/limbo/room/
nakedmudv3.3/lib/world/limbo/rproto/
nakedmudv3.3/src/alias/
nakedmudv3.3/src/char_vars/
nakedmudv3.3/src/editor/
nakedmudv3.3/src/example_module/
nakedmudv3.3/src/help/
nakedmudv3.3/src/set_val/
nakedmudv3.3/src/socials/
nakedmudv3.3/src/time/
//*****************************************************************************
//
// pymud.h
//
// a python module that provides some useful utility functions for interacting
// with the MUD. Includes stuff like global variables, messaging functions,
// and a whole bunch of other stuff.
//
// WORK FOR FUTURE: Our use of Py_INCREF is probably creating a
//                  memory leak somewhere.
//
//*****************************************************************************

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

#include "../mud.h"
#include "../utils.h"
#include "../character.h"
#include "../inform.h"
#include "../handler.h"
#include "../parse.h"
#include "../races.h"

#include "scripts.h"
#include "pyroom.h"
#include "pychar.h"
#include "pyobj.h"
#include "pyplugs.h"
#include "pyexit.h"
#include "pysocket.h"



//*****************************************************************************
// local variables and functions
//*****************************************************************************
// global variables we have set.
PyObject  *globals = NULL;

// a list of methods to add to the mud module
LIST *pymud_methods = NULL;

// a placeholder of the movement command, as set by one of our modules
PyObject *py_cmd_move = NULL;



//*****************************************************************************
//
// GLOBAL VARIABLES
//
// the following functions allow scriptors to store/access global variables.
// globals are stored in a python map, that maps two python objects together.
// the functions used to interact with globals are:
//   get_global(key)
//   set_global(key, val)
//   erase_global(key)
//
//*****************************************************************************
PyObject *mud_get_global(PyObject *self, PyObject *args) {
  PyObject *key = NULL;

  // get the key
  if (!PyArg_ParseTuple(args, "O", &key)) {
    PyErr_Format(PyExc_TypeError, 
		 "Could not retrieve global variable - no key provided");
    return NULL;
  }

  PyObject *val = PyDict_GetItem(globals, key);
  if(val == NULL)
    val = Py_None;
  
  Py_INCREF(val);
  return val;
}

PyObject *mud_set_global(PyObject *self, PyObject *args) {
  PyObject *key = NULL, *val = NULL;

  if (!PyArg_ParseTuple(args, "OO", &key, &val)) {
    PyErr_Format(PyExc_TypeError, 
		 "Could not set global variable - need key and value");
    return NULL;
  }

  PyDict_SetItem(globals, key, val);
  return Py_BuildValue("i", 1);
}


PyObject *mud_erase_global(PyObject *self, PyObject *args) {
  PyObject *key = NULL;

  if (!PyArg_ParseTuple(args, "O", &key)) {
    PyErr_Format(PyExc_TypeError, 
		 "Could not erase global variable - need key");
    return NULL;
  }

  PyDict_SetItem(globals, key, Py_None);
  return Py_BuildValue("i", 1);
}


//
// format a string to be into a typical description style
PyObject *mud_format_string(PyObject *self, PyObject *args) {
  char *string = NULL;
  bool indent  = TRUE;

  // parse all of the values
  if (!PyArg_ParseTuple(args, "s|b", &string, &indent)) {
    PyErr_Format(PyExc_TypeError, 
		 "Can not format non-string values.");
    return NULL;
  }

  // dup the string so we can work with it and not intrude on the PyString data
  BUFFER *buf = newBuffer(MAX_BUFFER);
  bufferCat(buf, string);
  bufferFormat(buf, SCREEN_WIDTH, (indent ? PARA_INDENT : 0));
  PyObject *ret = Py_BuildValue("s", bufferString(buf));
  deleteBuffer(buf);
  return ret;
}


//
// parses arguments for character commands
PyObject *mud_parse_args(PyObject *self, PyObject *args) {
  PyObject   *pych = NULL;
  bool show_errors = FALSE;
  char        *cmd = NULL;
  char     *pyargs = NULL;
  char     *syntax = NULL;
  char *parse_args = NULL;
  CHAR_DATA    *ch = NULL;

  // parse our arguments
  if(!PyArg_ParseTuple(args, "Obsss", &pych, &show_errors, 
		       &cmd, &pyargs, &syntax)) {
    PyErr_Format(PyExc_TypeError, "Invalid arguments to parse_args");
    return NULL;
  }

  // convert the character
  if(!PyChar_Check(pych) || (ch = PyChar_AsChar(pych)) == NULL) {
      PyErr_Format(PyExc_TypeError, 
		   "First argument must be an existent character!");
      return NULL;
  }

  // strdup our py args; they might be edited in the parse function
  parse_args = strdup(pyargs);

  // finish up and garbage collections
  PyObject *retval = Py_parse_args(ch, show_errors, cmd, parse_args, syntax);
  free(parse_args);
  return retval;
}


//
// a wrapper around NakedMud's generic_find() function
PyObject *mud_generic_find(PyObject *self, PyObject *args) {
  PyObject *py_looker = Py_None;     CHAR_DATA *looker = NULL;
  char      *type_str = NULL;        bitvector_t  type = 0; 
  char     *scope_str = NULL;        bitvector_t scope = 0;
  char           *arg = NULL;
  bool         all_ok = TRUE;

  // parse the arguments
  if(!PyArg_ParseTuple(args, "Osss|b", &py_looker, &arg, &type_str, &scope_str,
		       &all_ok)) {
    PyErr_Format(PyExc_TypeError,
		 "Invalid arguments supplied to mud.generic_find()");
    return NULL;
  }

  // convert the looker
  if(py_looker != Py_None) {
    if(!PyChar_Check(py_looker) || (looker = PyChar_AsChar(py_looker)) == NULL){
      PyErr_Format(PyExc_TypeError, 
		   "First argument must be an existent character or None!");
      return NULL;
    }
  }

  // convert the scope
  if(is_keyword(scope_str, "room", FALSE))
    SET_BIT(scope, FIND_SCOPE_ROOM);
  if(is_keyword(scope_str, "inv", FALSE))
    SET_BIT(scope, FIND_SCOPE_INV);
  if(is_keyword(scope_str, "worn", FALSE))
    SET_BIT(scope, FIND_SCOPE_WORN);
  if(is_keyword(scope_str, "world", FALSE))
    SET_BIT(scope, FIND_SCOPE_WORLD);
  if(is_keyword(scope_str, "visible", FALSE))
    SET_BIT(scope, FIND_SCOPE_VISIBLE);
  if(is_keyword(scope_str, "immediate", FALSE))
    SET_BIT(scope, FIND_SCOPE_IMMEDIATE);
  if(is_keyword(scope_str, "all", FALSE))
    SET_BIT(scope, FIND_SCOPE_ALL);

  // convert the types
  if(is_keyword(type_str, "obj", FALSE))
    SET_BIT(type, FIND_TYPE_OBJ);
  if(is_keyword(type_str, "char", FALSE))
    SET_BIT(type, FIND_TYPE_CHAR);
  if(is_keyword(type_str, "exit", FALSE))
    SET_BIT(type, FIND_TYPE_EXIT);
  if(is_keyword(type_str, "in", FALSE))
    SET_BIT(type, FIND_TYPE_IN_OBJ);
  if(is_keyword(type_str, "all", FALSE))
    SET_BIT(type,FIND_TYPE_OBJ  | FIND_TYPE_CHAR | 
	         FIND_TYPE_EXIT | FIND_TYPE_IN_OBJ);

  // do the search
  int found_type = FOUND_NONE;
  void    *found = generic_find(looker, arg, type, scope, all_ok, &found_type);

  if(found_type == FOUND_CHAR) {
    // were we searching for one type, or multiple types?
    if(!strcasecmp("char", type_str))
      return Py_BuildValue("O", charGetPyFormBorrowed(found));
    else
      return Py_BuildValue("Os", charGetPyFormBorrowed(found), "char");
  }
  else if(found_type == FOUND_EXIT) {
    // were we searching for one type, or multiple types?
    PyObject   *exit = newPyExit(found);
    PyObject *retval = NULL;
    if(!strcasecmp("exit", type_str))
      retval = Py_BuildValue("O", exit);
    else
      retval = Py_BuildValue("Os", exit, "obj");
    Py_DECREF(exit);
    return retval;
  }
  else if(found_type == FOUND_OBJ) {
    // were we searching for one type, or multiple types?
    if(!strcasecmp("obj", type_str))
      return Py_BuildValue("O", objGetPyFormBorrowed(found));
    else
      return Py_BuildValue("Os", objGetPyFormBorrowed(found), "obj");
  }
  else if(found_type == FOUND_IN_OBJ) {
    // were we searching for one type, or multiple types?
    if(!strcasecmp("in", type_str))
      return Py_BuildValue("O", objGetPyFormBorrowed(found));
    else
      return Py_BuildValue("Os", objGetPyFormBorrowed(found), "in");
  }
  // now it gets a bit more tricky... we have to see what other bit was set
  else if(found_type == FOUND_LIST) {
    PyObject         *list = PyList_New(0);
    LIST_ITERATOR *found_i = newListIterator(found);
    void        *one_found = NULL;
    if(IS_SET(type, FIND_TYPE_CHAR)) {
      ITERATE_LIST(one_found, found_i)
	PyList_Append(list, charGetPyFormBorrowed(one_found));
    }
    else if(IS_SET(type, FIND_TYPE_OBJ | FIND_TYPE_IN_OBJ)) {
      ITERATE_LIST(one_found, found_i)
	PyList_Append(list, charGetPyFormBorrowed(one_found));
    }
    deleteListIterator(found_i);
    deleteList(found);
    PyObject *retval = Py_BuildValue("Os", list, "list");
    Py_DECREF(list);
    return retval;
  }

  // nothing was found...
  return Py_BuildValue("Os", Py_None, Py_None);
}


//
// execute message() from inform.h
PyObject *mud_message(PyObject *self, PyObject *args) {
  // the python/C representations of the various variables that message() needs
  PyObject    *pych = NULL;     CHAR_DATA     *ch = NULL;
  PyObject  *pyvict = NULL;     CHAR_DATA   *vict = NULL;
  PyObject   *pyobj = NULL;     OBJ_DATA     *obj = NULL;
  PyObject  *pyvobj = NULL;     OBJ_DATA    *vobj = NULL;
  char     *pyrange = NULL;     bitvector_t range = 0;
  char        *mssg = NULL;
  int    hide_nosee = 0;

  // parse all of the arguments
  if(!PyArg_ParseTuple(args, "OOOObss", &pych, &pyvict, &pyobj, &pyvobj,
		       &hide_nosee, &pyrange, &mssg)) {
    PyErr_Format(PyExc_TypeError,"Invalid arguments supplied to mud.message()");
    return NULL;
  }

  // convert the character
  if(pych != Py_None) {
    if(!PyChar_Check(pych) || (ch = PyChar_AsChar(pych)) == NULL) {
      PyErr_Format(PyExc_TypeError, 
		   "First argument must be an existent character or None!");
      return NULL;
    }
  }

  // convert the victim
  if(pyvict != Py_None) {
    if(!PyChar_Check(pyvict) || (vict = PyChar_AsChar(pyvict)) == NULL) {
      PyErr_Format(PyExc_TypeError, 
		   "Second argument must be an existent character or None!");
      return NULL;
    }
  }

  // convert the object
  if(pyobj != Py_None) {
    if(!PyObj_Check(pyobj) || (obj = PyObj_AsObj(pyobj)) == NULL) {
      PyErr_Format(PyExc_TypeError, 
		   "Third argument must be an existent object or None!");
      return NULL;
    }
  }

  // convert the target object
  if(pyvobj != Py_None) {
    if(!PyObj_Check(pyvobj) || (vobj = PyObj_AsObj(pyvobj)) == NULL) {
      PyErr_Format(PyExc_TypeError, 
		   "Fourth argument must be an existent object or None!");
      return NULL;
    }
  }

  // check all of our keywords: char, vict, room
  if(is_keyword(pyrange, "to_char", FALSE))
    SET_BIT(range, TO_CHAR);
  if(is_keyword(pyrange, "to_vict", FALSE))
    SET_BIT(range, TO_VICT);
  if(is_keyword(pyrange, "to_room", FALSE))
    SET_BIT(range, TO_ROOM);
  if(is_keyword(pyrange, "to_world", FALSE))
    SET_BIT(range, TO_WORLD);

  // finally, send out the message
  message(ch, vict, obj, vobj, hide_nosee, range, mssg);
  return Py_BuildValue("i", 1);
}


//
// extracts an mob or object from the game
PyObject *mud_extract(PyObject *self, PyObject *args) {
  PyObject *thing = NULL;

  // parse the value
  if (!PyArg_ParseTuple(args, "O", &thing)) {
    PyErr_Format(PyExc_TypeError, 
		 "extract must be provided with an object or mob to extract!.");
    return NULL;
  }

  // check its type
  if(PyChar_Check(thing)) {
    CHAR_DATA *ch = PyChar_AsChar(thing);
    if(ch != NULL)
      extract_mobile(ch);
    else {
      PyErr_Format(PyExc_StandardError,
		   "Tried to extract nonexistent character!");
      return NULL;
    }
  }
  else if(PyObj_Check(thing)) {
    OBJ_DATA *obj = PyObj_AsObj(thing);
    if(obj != NULL)
      extract_obj(obj);
    else {
      PyErr_Format(PyExc_StandardError,
		   "Tried to extract nonexistent object!");
      return NULL;
    }
  }
  
  // success
  return Py_BuildValue("i", 1);
}

//
// functional form of if/then/else
PyObject *mud_ite(PyObject *self, PyObject *args) {
  PyObject *condition = NULL;
  PyObject  *true_act = NULL;
  PyObject *false_act = Py_None;

  if (!PyArg_ParseTuple(args, "OO|O", &condition, &true_act, &false_act)) {
    PyErr_Format(PyExc_TypeError, "ite must be specified 2 and an optional 3rd "
		 "arg");
    return NULL;
  }

  // check to see if our condition is true
  if( (PyInt_Check(condition)    && PyInt_AsLong(condition) != 0) ||
      (PyString_Check(condition) && strlen(PyString_AsString(condition)) > 0))
    return true_act;
  else
    return false_act;
}

//
// returns whether or not two database keys have the same name. Locale sensitive
PyObject *mud_keys_equal(PyObject *self, PyObject *args) {
  char *key1 = NULL;
  char *key2 = NULL;
  if(!PyArg_ParseTuple(args, "ss", &key1, &key2)) {
    PyErr_Format(PyExc_TypeError, "keys_equal takes two string arguments");
    return NULL;
  }

  char *fullkey1 = strdup(get_fullkey_relative(key1, get_script_locale()));
  char *fullkey2 = strdup(get_fullkey_relative(key2, get_script_locale()));
  bool        ok = !strcasecmp(fullkey1, fullkey2);
  free(fullkey1);
  free(fullkey2);
  return Py_BuildValue("i", ok);
}

//
// returns the mud's message of the day
PyObject *mud_get_motd(PyObject *self, PyObject *args) {
  return Py_BuildValue("s", bufferString(motd));
}

PyObject *mud_log_string(PyObject *self, PyObject *args) {
  char *mssg = NULL;
  if(!PyArg_ParseTuple(args, "s", &mssg)) {
    PyErr_Format(PyExc_TypeError, "a message must be supplied to log_string");
    return NULL;
  }

  // we have to strip all %'s out of this message
  BUFFER *buf = newBuffer(1);
  bufferCat(buf, mssg);
  bufferReplace(buf, "%", "%%", TRUE);
  log_string(bufferString(buf));
  deleteBuffer(buf);
  return Py_BuildValue("i", 1);
}

PyObject *mud_set_cmd_move(PyObject *self, PyObject *args) {
  PyObject *cmd = NULL;
  if(!PyArg_ParseTuple(args, "O", &cmd)) {
    PyErr_Format(PyExc_TypeError, "a command must be suppled");
    return NULL;
  }

  // make sure it's a function
  if(!PyFunction_Check(cmd)) {
    PyErr_Format(PyExc_TypeError, "a command must be suppled");
    return NULL;
  }

  Py_XDECREF(py_cmd_move);
  py_cmd_move = cmd;
  return Py_BuildValue("i", 1);
}

PyObject *mud_is_race(PyObject *self, PyObject *args) {
  char       *race = NULL;
  bool player_only = FALSE;
  if(!PyArg_ParseTuple(args, "s|b", &race, &player_only)) {
    PyErr_Format(PyExc_TypeError, "a string must be supplied");
    return NULL;
  }

  if(player_only)
    return Py_BuildValue("i", raceIsForPC(race));
  else
    return Py_BuildValue("i", isRace(race));
}

PyObject *mud_list_races(PyObject *self, PyObject *args) {
  bool player_only = FALSE;
  if(!PyArg_ParseTuple(args, "|b", &player_only)) {
    PyErr_Format(PyExc_TypeError, "true/false value to list only player races must be provided.");
    return NULL;
  }
  return Py_BuildValue("s", raceGetList(player_only));
}


//*****************************************************************************
// MUD module
//*****************************************************************************
void PyMud_addMethod(const char *name, void *f, int flags, const char *doc) {
  // make sure our list of methods is created
  if(pymud_methods == NULL) pymud_methods = newList();

  // make the Method def
  PyMethodDef *def = calloc(1, sizeof(PyMethodDef));
  def->ml_name     = strdup(name);
  def->ml_meth     = (PyCFunction)f;
  def->ml_flags    = flags;
  def->ml_doc      = (doc ? strdup(doc) : NULL);
  listPut(pymud_methods, def);
}


PyMODINIT_FUNC
init_PyMud(void) {
  // add all of our methods
  PyMud_addMethod("get_global", mud_get_global, METH_VARARGS,
		  "Get the value of a global variable.");
  PyMud_addMethod("set_global", mud_set_global, METH_VARARGS,
		  "Set the value of a global variable.");
  PyMud_addMethod("erase_global",  mud_erase_global, METH_VARARGS,
		  "Erase the value of a global variable.");
  PyMud_addMethod("message", mud_message, METH_VARARGS,
		  "plugs into the message() function from inform.h");
  PyMud_addMethod("format_string", mud_format_string, METH_VARARGS,
		  "format a string to be 80 chars wide and indented.");
  PyMud_addMethod("generic_find",  mud_generic_find, METH_VARARGS,
		  "Python wrapper around the generic_find() function");
  PyMud_addMethod("extract", mud_extract, METH_VARARGS,
		  "extracts an object or character from the game.");
  PyMud_addMethod("keys_equal", mud_keys_equal, METH_VARARGS,
		  "Returns whether or not two db keys are equal, given the ."
		  "locale that the script is running in.");
  PyMud_addMethod("ite", mud_ite, METH_VARARGS,
		  "A functional form of an if-then-else statement. Takes 2 "
		  "arguments (condition, if action) and an optional third "
		  "(else action). If no else action is specified and the "
		  "condition is false, None is returned.");
  PyMud_addMethod("parse_args", mud_parse_args, METH_VARARGS,
		  "equivalent to parse_args written in C");
  PyMud_addMethod("get_motd", mud_get_motd, METH_VARARGS,
		  "returns the mud's message of the day");
  PyMud_addMethod("log_string", mud_log_string, METH_VARARGS,
		  "adds a string to the mudlog");
  PyMud_addMethod("set_cmd_move", mud_set_cmd_move, METH_VARARGS,
		  "sets the movement command");
  PyMud_addMethod("is_race", mud_is_race, METH_VARARGS,
		  "returns whether or not the string is a valid race.");
  PyMud_addMethod("list_races", mud_list_races, METH_VARARGS,
		  "returns a list of all the races available. Can take one "
		  "argument that specifies whether or not to list player "
		  "races.");

  Py_InitModule3("mud", makePyMethods(pymud_methods),
		 "The mud module, for all MUD misc mud utils.");

  globals = PyDict_New();
  Py_INCREF(globals);
}

void *get_cmd_move(void) {
  return py_cmd_move;
}