nakedmudv3.6/
nakedmudv3.6/lib/
nakedmudv3.6/lib/help/A/
nakedmudv3.6/lib/help/B/
nakedmudv3.6/lib/help/C/
nakedmudv3.6/lib/help/D/
nakedmudv3.6/lib/help/G/
nakedmudv3.6/lib/help/H/
nakedmudv3.6/lib/help/J/
nakedmudv3.6/lib/help/L/
nakedmudv3.6/lib/help/M/
nakedmudv3.6/lib/help/O/
nakedmudv3.6/lib/help/P/
nakedmudv3.6/lib/help/R/
nakedmudv3.6/lib/help/S/
nakedmudv3.6/lib/help/W/
nakedmudv3.6/lib/logs/
nakedmudv3.6/lib/misc/
nakedmudv3.6/lib/players/
nakedmudv3.6/lib/txt/
nakedmudv3.6/lib/world/
nakedmudv3.6/lib/world/examples/
nakedmudv3.6/lib/world/examples/mproto/
nakedmudv3.6/lib/world/examples/oproto/
nakedmudv3.6/lib/world/examples/reset/
nakedmudv3.6/lib/world/examples/rproto/
nakedmudv3.6/lib/world/examples/trigger/
nakedmudv3.6/lib/world/limbo/
nakedmudv3.6/lib/world/limbo/room/
nakedmudv3.6/lib/world/limbo/rproto/
nakedmudv3.6/src/alias/
nakedmudv3.6/src/dyn_vars/
nakedmudv3.6/src/editor/
nakedmudv3.6/src/example_module/
nakedmudv3.6/src/help2/
nakedmudv3.6/src/set_val/
nakedmudv3.6/src/socials/
nakedmudv3.6/src/time/
//*****************************************************************************
//
// trighooks.c
//
// Triggers attach on to rooms, objects, and mobiles as hooks. When a hook
// event occurs, all of the triggers of the right type will run. This header is
// just to allow scripts to initialize the hooks into the game. The init 
// function here should not be touched by anything other than scripts.c
//
//*****************************************************************************

#include "../mud.h"
#include "../utils.h"
#include "../hooks.h"
#include "../character.h"
#include "../room.h"
#include "../object.h"
#include "../world.h"
#include "../zone.h"

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



//*****************************************************************************
// local datastructures and defines
//*****************************************************************************

// values for figuring out what "me" and optional variables are in gen_do_trig
#define VARTYPE_CHAR      0
#define VARTYPE_OBJ       1
#define VARTYPE_ROOM      2

// used for providing additional variables to gen_do_trig that are not standard
typedef struct {
  char *name;
  void *data;
  int   type;
} OPT_VAR;

OPT_VAR *newOptVar(const char *name, void *data, int type) {
  OPT_VAR *var = malloc(sizeof(OPT_VAR));
  var->name    = strdupsafe(name);
  var->data    = data;
  var->type    = type;
  return var;
}

void deleteOptVar(OPT_VAR *var) {
  if(var->name) free(var->name);
  free(var);
}



//*****************************************************************************
// trigger handlers
//*****************************************************************************

//
// generalized function for setting up a dictionary and running a trigger. The
// common types of variables can be supplied in the function. Additional ones
// can be added in the optional list, which must be deleted after use
void gen_do_trig(TRIGGER_DATA *trig, 
		 void *me, int me_type, CHAR_DATA *ch, OBJ_DATA *obj,
		 ROOM_DATA *room, EXIT_DATA *exit, const char *command,
		 const char *arg, LIST *optional) {
  // make our basic dictionary, and fill it up with these new variables
  PyObject *dict = restricted_script_dict();
  // now, import all of our variables
  if(command) {
    PyObject *pycmd = PyString_FromString(command);
    PyDict_SetItemString(dict, "cmd", pycmd);
    Py_DECREF(pycmd);
  }
  if(arg) {
    PyObject *pyarg = PyString_FromString(arg);
    PyDict_SetItemString(dict, "arg", pyarg);
    Py_DECREF(pyarg);
  }
  if(ch) {
    PyObject *pych = charGetPyForm(ch);
    PyDict_SetItemString(dict, "ch", pych);
    Py_DECREF(pych);
  }
  if(room) {
    PyObject *pyroom = roomGetPyForm(room);
    PyDict_SetItemString(dict, "room", pyroom);
    Py_DECREF(pyroom);
  }    
  if(obj) {
    PyObject *pyobj = objGetPyForm(obj);
    PyDict_SetItemString(dict, "obj", pyobj);
    Py_DECREF(pyobj);
  }
  if(exit) {
    PyObject *pyexit = newPyExit(exit);
    PyDict_SetItemString(dict, "ex", pyexit);
    Py_DECREF(pyexit);
  }

  // add the thing the tirgger is attached to
  if(me) {
    PyObject *pyme = NULL;
    switch(me_type) {
    case VARTYPE_CHAR:  pyme = charGetPyForm(me); break;
    case VARTYPE_OBJ:   pyme = objGetPyForm(me);  break;
    case VARTYPE_ROOM:  pyme = roomGetPyForm(me); break;
    }
    PyDict_SetItemString(dict, "me", pyme);
    Py_DECREF(pyme);
  }

  // now, add any optional variables
  if(optional) {
    LIST_ITERATOR *opt_i = newListIterator(optional);
    OPT_VAR         *opt = NULL;
    PyObject      *pyopt = NULL;
    ITERATE_LIST(opt, opt_i) {
      pyopt = NULL;
      switch(opt->type) {
      case VARTYPE_CHAR:  pyopt = charGetPyForm(opt->data); break;
      case VARTYPE_OBJ:   pyopt = objGetPyForm(opt->data);  break;
      case VARTYPE_ROOM:  pyopt = roomGetPyForm(opt->data); break;
      }
      PyDict_SetItemString(dict, opt->name, pyopt);
      Py_XDECREF(pyopt);
    } deleteListIterator(opt_i);
  }

  // run the script, then kill our dictionary
  triggerRun(trig, dict);
  Py_DECREF(dict);
}


//
// handles all of a character's triggers
void do_char_trigs(CHAR_DATA *ch, const char *type, void *thing, void *arg) {
  if(listSize(charGetTriggers(ch)) > 0) {
    // first, build a list of all our triggers of this type
    LIST           *trigs = newList();
    LIST_ITERATOR *trig_i = newListIterator(charGetTriggers(ch));
    char             *key = NULL;
    TRIGGER_DATA    *trig = NULL;
    ITERATE_LIST(key, trig_i) {
      if((trig = worldGetType(gameworld, "trigger", key)) != NULL &&
	 !strcasecmp(triggerGetType(trig), type))
	listPut(trigs, trig);
    } deleteListIterator(trig_i);

    // did we find any triggers?
    if(listSize(trigs) > 0) {
      trig_i = newListIterator(trigs);
      ITERATE_LIST(trig, trig_i) {
	if(!strcasecmp(type, "speech"))
	  gen_do_trig(trig,ch,VARTYPE_CHAR,thing,NULL,NULL,NULL,NULL,arg,NULL);
	else if(!strcasecmp(type, "move"))
	  gen_do_trig(trig,ch,VARTYPE_CHAR,NULL,NULL,NULL,thing,NULL,NULL,NULL);
	else if(!strcasecmp(type, "enter"))
	  gen_do_trig(trig,ch,VARTYPE_CHAR,thing,NULL,NULL,NULL,NULL,NULL,NULL);
	else if(!strcasecmp(type, "exit"))
	  gen_do_trig(trig,ch,VARTYPE_CHAR,thing,NULL,NULL,arg,NULL,NULL,NULL);
	else if(!strcasecmp(type, "greet"))
	  gen_do_trig(trig,ch,VARTYPE_CHAR,thing,NULL,NULL,NULL,NULL,NULL,NULL);
	else if(!strcasecmp(type, "give"))
	  gen_do_trig(trig,ch,VARTYPE_CHAR,thing,arg,NULL,NULL,NULL,NULL,NULL);
	else if(!strcasecmp(type, "receive"))
	  gen_do_trig(trig,ch,VARTYPE_CHAR,thing,arg,NULL,NULL,NULL,NULL,NULL);
	else if(!strcasecmp(type, "wear"))
	  gen_do_trig(trig,ch,VARTYPE_CHAR,NULL,thing,NULL,NULL,NULL,NULL,NULL);
	else if(!strcasecmp(type, "remove"))
	  gen_do_trig(trig,ch,VARTYPE_CHAR,NULL,thing,NULL,NULL,NULL,NULL,NULL);
	else {
	  log_string("Unrecognized trigger type %s attached to %s, uid %d.\r\n",
		     type, charGetClass(ch), charGetUID(ch));
	}
      } deleteListIterator(trig_i);
    }
    
    // clean up our mess
    deleteList(trigs);
  }
}

//
// handles all of an object's triggers
void do_obj_trigs(OBJ_DATA *obj, const char *type, void *thing, void *arg) {
  if(listSize(objGetTriggers(obj)) > 0) {
    // first, build a list of all our triggers of this type
    LIST           *trigs = newList();
    LIST_ITERATOR *trig_i = newListIterator(objGetTriggers(obj));
    char             *key = NULL;
    TRIGGER_DATA    *trig = NULL;
    ITERATE_LIST(key, trig_i) {
      if((trig = worldGetType(gameworld, "trigger", key)) != NULL &&
	 !strcasecmp(triggerGetType(trig), type))
	listPut(trigs, trig);
    } deleteListIterator(trig_i);

    // did we find any triggers?
    if(listSize(trigs) > 0) {
      trig_i = newListIterator(trigs);
      ITERATE_LIST(trig, trig_i) {
	if(!strcasecmp(type, "give")) {
	  // set up the optional "receiver" variable
	  LIST *opts = newList();
	  listPut(opts, newOptVar("recv", arg, VARTYPE_CHAR));
	  gen_do_trig(trig,obj,VARTYPE_OBJ,thing,NULL,NULL,NULL,NULL,NULL,opts);
	  deleteListWith(opts, deleteOptVar);
	}
	else if(!strcasecmp(type, "get"))
	  gen_do_trig(trig,obj,VARTYPE_OBJ,thing,NULL,NULL,NULL,NULL,NULL,NULL);
	else if(!strcasecmp(type, "drop"))
	  gen_do_trig(trig,obj,VARTYPE_OBJ,thing,NULL,NULL,NULL,NULL,NULL,NULL);
	else if(!strcasecmp(type, "wear"))
	  gen_do_trig(trig,obj,VARTYPE_OBJ,thing,NULL,NULL,NULL,NULL,NULL,NULL);
	else if(!strcasecmp(type, "remove"))
	  gen_do_trig(trig,obj,VARTYPE_OBJ,thing,NULL,NULL,NULL,NULL,NULL,NULL);
	else if(!strcasecmp(type, "open"))
	  gen_do_trig(trig,obj,VARTYPE_OBJ,thing,NULL,NULL,NULL,NULL,NULL,NULL);
	else {
	  log_string("Unrecognized trigger type %s attached to %s, uid %d.\r\n",
		     type, objGetClass(obj), objGetUID(obj));
	}
      } deleteListIterator(trig_i);
    }

    // clean up our mess
    deleteList(trigs);
  }
}

//
// handles all of a room's triggers
void do_room_trigs(ROOM_DATA *rm, const char *type, void *thing, void *arg){
  if(listSize(roomGetTriggers(rm)) > 0) {
    // first, build a list of all our triggers of this type
    LIST           *trigs = newList();
    LIST_ITERATOR *trig_i = newListIterator(roomGetTriggers(rm));
    char             *key = NULL;
    TRIGGER_DATA    *trig = NULL;
    ITERATE_LIST(key, trig_i) {
      if((trig = worldGetType(gameworld, "trigger", key)) != NULL &&
	 !strcasecmp(triggerGetType(trig), type))
	listPut(trigs, trig);
    } deleteListIterator(trig_i);

    // did we find any triggers?
    if(listSize(trigs) > 0) {
      trig_i = newListIterator(trigs);
      ITERATE_LIST(trig, trig_i) {
	if(!strcasecmp(type, "get"))
	  gen_do_trig(trig,rm,VARTYPE_ROOM,thing,arg,NULL,NULL,NULL,NULL,NULL);
	else if(!strcasecmp(type, "drop"))
	  gen_do_trig(trig,rm,VARTYPE_ROOM,thing,arg,NULL,NULL,NULL,NULL,NULL);
	else if(!strcasecmp(type, "enter"))
	  gen_do_trig(trig,rm,VARTYPE_ROOM,thing,NULL,NULL,NULL,NULL,NULL,NULL);
	else if(!strcasecmp(type, "exit"))
	  gen_do_trig(trig,rm,VARTYPE_ROOM,thing,NULL,NULL,arg,NULL,NULL,NULL);
	else if(!strcasecmp(type, "speech"))
	  gen_do_trig(trig,rm,VARTYPE_ROOM,thing,NULL,NULL,NULL,NULL,arg,NULL);
	else if(!strcasecmp(type, "reset"))
	  gen_do_trig(trig,rm,VARTYPE_ROOM,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
	else if(!strcasecmp(type, "open"))
	  gen_do_trig(trig,rm,VARTYPE_ROOM,thing,NULL,NULL,arg,NULL,NULL,NULL);
	else {
	  log_string("Unrecognized trigger type %s attached to %s, uid %d.\r\n",
		     type, roomGetClass(rm), roomGetUID(rm));
	}
      } deleteListIterator(trig_i);
    }
    
    // clean up our mess
    deleteList(trigs);
  }
}



//*****************************************************************************
// trighooks
//*****************************************************************************
void do_give_trighooks(const char *info) {
  CHAR_DATA   *ch = NULL;
  CHAR_DATA *recv = NULL;
  OBJ_DATA   *obj = NULL;
  hookParseInfo(info, &ch, &recv, &obj);

  do_char_trigs(ch,   "give",    recv, obj);
  do_char_trigs(recv, "receive", ch,   obj);
  do_obj_trigs (obj,  "give",    ch,  recv);
}

void do_get_trighooks(const char *info) {
  CHAR_DATA *ch = NULL;
  OBJ_DATA *obj = NULL;
  hookParseInfo(info, &ch, &obj);

  do_obj_trigs (obj,             "get", ch, NULL);
  do_room_trigs(charGetRoom(ch), "get", ch, obj);
}

void do_drop_trighooks(const char *info) {
  CHAR_DATA *ch = NULL;
  OBJ_DATA *obj = NULL;
  hookParseInfo(info, &ch, &obj);

  do_obj_trigs (obj,             "drop", ch, NULL);
  do_room_trigs(charGetRoom(ch), "drop", ch,  obj);
}

void do_enter_trighooks(const char *info) {
  CHAR_DATA   *ch = NULL;
  ROOM_DATA *room = NULL;
  hookParseInfo(info, &ch, &room);

  LIST_ITERATOR *mob_i = newListIterator(roomGetCharacters(room));
  CHAR_DATA       *mob = NULL;
  ITERATE_LIST(mob, mob_i) {
    if(ch != mob)
      do_char_trigs(mob, "enter", ch, NULL);
  } deleteListIterator(mob_i);
  do_room_trigs(room, "enter", ch, NULL);
}

void do_exit_trighooks(const char *info) {
  CHAR_DATA   *ch = NULL;
  ROOM_DATA *room = NULL;
  EXIT_DATA *exit = NULL;
  hookParseInfo(info, &ch, &room, &exit);

  LIST_ITERATOR *mob_i = newListIterator(roomGetCharacters(room));
  CHAR_DATA       *mob = NULL;
  ITERATE_LIST(mob, mob_i) {
    if(ch != mob)
      do_char_trigs(mob, "exit", ch, exit);
  } deleteListIterator(mob_i);
  do_room_trigs(room, "exit", ch,   exit);
  do_char_trigs(ch,   "move", exit, NULL);
}

void do_ask_trighooks(const char *info) {
  CHAR_DATA       *ch = NULL;
  CHAR_DATA *listener = NULL;
  char        *speech = NULL;
  hookParseInfo(info, &ch, &listener, &speech);
  do_char_trigs(listener, "speech", ch, speech);
  if(speech) free(speech);
}

void do_say_trighooks(const char *info) {
  CHAR_DATA *ch = NULL;
  char  *speech = NULL;
  hookParseInfo(info, &ch, &speech);

  LIST_ITERATOR *mob_i = newListIterator(roomGetCharacters(charGetRoom(ch)));
  CHAR_DATA       *mob = NULL;
  ITERATE_LIST(mob, mob_i) {
    if(ch != mob)
      do_char_trigs(mob, "speech", ch, speech);
  } deleteListIterator(mob_i);
  do_room_trigs(charGetRoom(ch), "speech", ch, speech);
  if(speech) free(speech);
}

void do_greet_trighooks(const char *info) {
  CHAR_DATA      *ch = NULL;
  CHAR_DATA *greeted = NULL;
  hookParseInfo(info, &ch, &greeted);
  do_char_trigs(greeted, "greet", ch, NULL);
}

void do_wear_trighooks(const char *info) {
  CHAR_DATA *ch = NULL;
  OBJ_DATA *obj = NULL;
  hookParseInfo(info, &ch, &obj);

  do_char_trigs(ch,  "wear", obj, NULL);
  do_obj_trigs (obj, "wear", ch,  NULL);
}

void do_remove_trighooks(const char *info) {
  CHAR_DATA *ch = NULL;
  OBJ_DATA *obj = NULL;
  hookParseInfo(info, &ch, &obj);
  do_char_trigs(ch,  "remove", obj, NULL);
  do_obj_trigs (obj, "remove", ch,  NULL);
}

void do_reset_trighooks(const char *info) {
  char  *zone_key = NULL;
  hookParseInfo(info, &zone_key);
  ZONE_DATA *zone = worldGetZone(gameworld, zone_key);
  free(zone_key);

  LIST_ITERATOR *res_i = newListIterator(zoneGetResettable(zone));
  char           *name = NULL;
  const char   *locale = zoneGetKey(zone);
  ROOM_DATA      *room = NULL;
  ITERATE_LIST(name, res_i) {
    room = worldGetRoom(gameworld, get_fullkey(name, locale));
    if(room != NULL) do_room_trigs(room, "reset", NULL, NULL);
  } deleteListIterator(res_i);
}

void do_open_door_trighooks(const char *info) {
  CHAR_DATA *ch = NULL;
  EXIT_DATA *ex = NULL;
  hookParseInfo(info, &ch, &ex);
  do_room_trigs(charGetRoom(ch), "open", ch, ex);
}

void do_open_obj_trighooks(const char *info) {
  CHAR_DATA *ch = NULL;
  OBJ_DATA *obj = NULL;
  hookParseInfo(info, &ch, &obj);
  do_obj_trigs(obj, "open", ch, NULL);
}



//*****************************************************************************
// implementation of trighooks.h
//*****************************************************************************
void init_trighooks(void) {
  // add all of our hooks to the game
  hookAdd("give",      do_give_trighooks);
  hookAdd("get",       do_get_trighooks);
  hookAdd("drop",      do_drop_trighooks);
  hookAdd("enter",     do_enter_trighooks);
  hookAdd("exit",      do_exit_trighooks);
  hookAdd("ask",       do_ask_trighooks);
  hookAdd("say",       do_say_trighooks);
  hookAdd("greet",     do_greet_trighooks);
  hookAdd("wear",      do_wear_trighooks);
  hookAdd("remove",    do_remove_trighooks);
  hookAdd("reset",     do_reset_trighooks);
  hookAdd("open_door", do_open_door_trighooks);
  hookAdd("open_obj",  do_open_obj_trighooks);
}