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/
//*****************************************************************************
//
// 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"
#include "pymudsys.h"
#include "trighooks.h"



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

// a table matching trigger types to what they can attach to (obj, mob, room)
HASHTABLE *tedit_opts = NULL;

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

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);
}

HASHTABLE *get_tedit_opts(void) {
  return tedit_opts;
}

void register_tedit_opt(const char *type, const char *desc) {
  if(tedit_opts == NULL)
    tedit_opts = newHashtable();
  hashPut(tedit_opts, type, strdupsafe(desc));
}



//*****************************************************************************
// 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();
  LIST *varnames = newList(); 
  // now, import all of our variables
  if(command) {
    PyObject *pycmd = PyString_FromString(command);
    PyDict_SetItemString(dict, "cmd", pycmd);
    listPut(varnames, strdup("cmd"));
    Py_DECREF(pycmd);
  }
  if(arg) {
    PyObject *pyarg = PyString_FromString(arg);
    PyDict_SetItemString(dict, "arg", pyarg);
    listPut(varnames, strdup("arg"));
    Py_DECREF(pyarg);
  }
  if(ch) {
    PyObject *pych = charGetPyForm(ch);
    PyDict_SetItemString(dict, "ch", pych);
    listPut(varnames, strdup("ch"));
    Py_DECREF(pych);
  }
  if(room) {
    PyObject *pyroom = roomGetPyForm(room);
    PyDict_SetItemString(dict, "room", pyroom);
    listPut(varnames, strdup("room"));
    Py_DECREF(pyroom);
  }    
  if(obj) {
    PyObject *pyobj = objGetPyForm(obj);
    PyDict_SetItemString(dict, "obj", pyobj);
    listPut(varnames, strdup("obj"));
    Py_DECREF(pyobj);
  }
  if(exit) {
    PyObject *pyexit = newPyExit(exit);
    PyDict_SetItemString(dict, "ex", pyexit);
    listPut(varnames, strdup("ex"));
    Py_DECREF(pyexit);
  }

  // add the thing the trigger is attached to
  if(me) {
    PyObject *pyme = NULL;
    switch(me_type) {
    case TRIGVAR_CHAR:  pyme = charGetPyForm(me); break;
    case TRIGVAR_OBJ:   pyme = objGetPyForm(me);  break;
    case TRIGVAR_ROOM:  pyme = roomGetPyForm(me); break;
    }
    PyDict_SetItemString(dict, "me", pyme);
    listPut(varnames, strdup("me"));
    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 TRIGVAR_CHAR:  pyopt = charGetPyForm(opt->data); break;
      case TRIGVAR_OBJ:   pyopt = objGetPyForm(opt->data);  break;
      case TRIGVAR_ROOM:  pyopt = roomGetPyForm(opt->data); break;
      }
      PyDict_SetItemString(dict, opt->name, pyopt);
      listPut(varnames, strdup(opt->name));
      Py_XDECREF(pyopt);
    } deleteListIterator(opt_i);
  }

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

  // if triggers create methods, it increases the reference count on our
  // dictionary. This is a known memory leak. We'll try to mitigate it by
  // cleaning out some of the contents. A better solution is needed, that
  // makes sure the dictionary itself is not leaked
  LIST_ITERATOR *vname_i = newListIterator(varnames);
  char            *vname = NULL;
  ITERATE_LIST(vname, vname_i) {
    PyDict_DelItemString(dict, vname);
  } deleteListIterator(vname_i);
  deleteListWith(varnames, free);
  Py_XDECREF(dict);
}

//
// generalized function for running all triggers of a specified type.
void gen_do_trigs(void *me, int me_type, const char *type,
		  CHAR_DATA *ch,OBJ_DATA *obj, ROOM_DATA *room, EXIT_DATA *exit,
		  const char *command, const char *arg, LIST *optional) {
  // find our list of triggers
  LIST *trig_keys = NULL;
  if(me_type == TRIGVAR_CHAR)
    trig_keys = charGetTriggers(me);
  else if(me_type == TRIGVAR_OBJ)
    trig_keys = objGetTriggers(me);
  else if(me_type == TRIGVAR_ROOM)
    trig_keys = roomGetTriggers(me);

  if(trig_keys == NULL || listSize(trig_keys) == 0)
    return;
  
  char        *trig_key = NULL;
  LIST_ITERATOR *trig_i = newListIterator(trig_keys);
  TRIGGER_DATA    *trig = NULL;
  ITERATE_LIST(trig_key, trig_i) {
    if((trig = worldGetType(gameworld, "trigger", trig_key)) != NULL &&
       !strcasecmp(triggerGetType(trig), type))
      gen_do_trig(trig,me,me_type,ch,obj,room,exit,command,arg,optional);
  } deleteListIterator(trig_i);
}



//*****************************************************************************
// 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);

  gen_do_trigs(ch,TRIGVAR_CHAR,"give",recv,obj,NULL,NULL,NULL,NULL,NULL);
  gen_do_trigs(recv,TRIGVAR_CHAR,"receive",ch,obj,NULL,NULL,NULL,NULL,NULL);

  LIST *opts = newList();
  listPut(opts, newOptVar("vict", recv, TRIGVAR_CHAR));
  gen_do_trigs(obj,TRIGVAR_OBJ,"give",ch,NULL,NULL,NULL,NULL,NULL,opts);

  // garbage collection
  deleteListWith(opts, deleteOptVar);
}

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

  gen_do_trigs(obj,TRIGVAR_OBJ,"get",ch,NULL,NULL,NULL,NULL,NULL,NULL);
  gen_do_trigs(charGetRoom(ch),TRIGVAR_ROOM,"get",ch,obj,NULL,NULL,NULL,NULL,NULL);
}

void do_drop_trighooks(const char *info) {
  CHAR_DATA *ch = NULL;
  OBJ_DATA *obj = NULL;
  hookParseInfo(info, &ch, &obj);
  gen_do_trigs(obj,TRIGVAR_OBJ,"drop",ch,NULL,NULL,NULL,NULL,NULL,NULL);
  gen_do_trigs(charGetRoom(ch),TRIGVAR_ROOM,"drop",ch,obj,NULL,NULL,NULL,NULL,NULL);
}

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)
      gen_do_trigs(mob,TRIGVAR_CHAR,"enter",ch,NULL,NULL,NULL,NULL,NULL,NULL);
  } deleteListIterator(mob_i);
  gen_do_trigs(room,TRIGVAR_ROOM,"enter",ch,NULL,NULL,NULL,NULL,NULL,NULL);
  gen_do_trigs(ch,TRIGVAR_CHAR,"self enter",NULL,NULL,NULL,NULL,NULL,NULL,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)
      gen_do_trigs(mob,TRIGVAR_CHAR,"exit",ch,NULL,NULL,exit,NULL,NULL,NULL);
  } deleteListIterator(mob_i);
  gen_do_trigs(room,TRIGVAR_ROOM,"exit",ch,NULL,NULL,exit,NULL,NULL,NULL);
  gen_do_trigs(ch,TRIGVAR_CHAR,"self exit",NULL,NULL,NULL,exit,NULL,NULL,NULL);
}

void do_ask_trighooks(const char *info) {
  CHAR_DATA       *ch = NULL;
  CHAR_DATA *listener = NULL;
  char        *speech = NULL;
  hookParseInfo(info, &ch, &listener, &speech);
  gen_do_trigs(listener,TRIGVAR_CHAR,"speech",ch,NULL,NULL,NULL,NULL,speech,NULL);

  // garbage collection
  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)
     gen_do_trigs(mob,TRIGVAR_CHAR,"speech",ch,NULL,NULL,NULL,NULL,speech,NULL);
  } deleteListIterator(mob_i);
  gen_do_trigs(charGetRoom(ch),TRIGVAR_ROOM,"speech",ch,NULL,NULL,NULL,NULL,speech,NULL);

  // garbage collection
  free(speech);
}

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

void do_wear_trighooks(const char *info) {
  CHAR_DATA *ch = NULL;
  OBJ_DATA *obj = NULL;
  hookParseInfo(info, &ch, &obj);
  gen_do_trigs(ch,TRIGVAR_CHAR,"wear",NULL,obj,NULL,NULL,NULL,NULL,NULL);
  gen_do_trigs(obj,TRIGVAR_OBJ,"wear",ch,NULL,NULL,NULL,NULL,NULL,NULL);
}

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

void do_reset_trighooks(const char *info) {
  char  *zone_key = NULL;
  hookParseInfo(info, &zone_key);
  ZONE_DATA *zone = worldGetZone(gameworld, 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)
     gen_do_trigs(room,TRIGVAR_ROOM,"reset",NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  } deleteListIterator(res_i);

  // garbage collection
  free(zone_key);
}

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

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

void do_close_door_trighooks(const char *info) {
  CHAR_DATA *ch = NULL;
  EXIT_DATA *ex = NULL;
  hookParseInfo(info, &ch, &ex);
  gen_do_trigs(charGetRoom(ch),TRIGVAR_ROOM,"close",ch,NULL,NULL,ex,NULL,NULL,NULL);
}

void do_close_obj_trighooks(const char *info) {
  CHAR_DATA *ch = NULL;
  OBJ_DATA *obj = NULL;
  hookParseInfo(info, &ch, &obj);
  gen_do_trigs(obj,TRIGVAR_OBJ,"close",ch,NULL,NULL,NULL,NULL,NULL,NULL);
}

void do_look_at_obj_trighooks(const char *info) {
  OBJ_DATA     *obj = NULL;
  CHAR_DATA *looker = NULL;
  hookParseInfo(info, &obj, &looker);
  gen_do_trigs(obj,TRIGVAR_OBJ,"look",looker,NULL,NULL,NULL,NULL,NULL,NULL);
}

void do_look_at_room_trighooks(const char *info) {
  ROOM_DATA   *room = NULL;
  CHAR_DATA *looker = NULL;
  hookParseInfo(info, &room, &looker);
  gen_do_trigs(room,TRIGVAR_ROOM,"look",looker,NULL,NULL,NULL,NULL,NULL,NULL);
}

void do_look_at_char_trighooks(const char *info) {
  CHAR_DATA     *ch = NULL;
  CHAR_DATA *looker = NULL;
  hookParseInfo(info, &ch, &looker);
  gen_do_trigs(ch,TRIGVAR_CHAR,"look",looker,NULL,NULL,NULL,NULL,NULL,NULL);
}

void do_obj_to_game_trighooks(const char *info) {
  OBJ_DATA *obj = NULL;
  hookParseInfo(info, &obj);
  gen_do_trigs(obj,TRIGVAR_OBJ,"to_game",NULL,NULL,NULL,NULL,NULL,NULL,NULL);
}

void do_char_to_game_trighooks(const char *info) {
  CHAR_DATA *ch = NULL;
  hookParseInfo(info, &ch);
  gen_do_trigs(ch, TRIGVAR_CHAR,"to_game",NULL,NULL,NULL,NULL,NULL,NULL,NULL);
}

void do_room_to_game_trighooks(const char *info) {
  ROOM_DATA *rm = NULL;
  hookParseInfo(info, &rm);
  gen_do_trigs(rm,TRIGVAR_ROOM,"to_game",NULL,NULL,NULL,NULL,NULL,NULL,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);
  hookAdd("close_door",     do_close_door_trighooks);
  hookAdd("close_obj",      do_close_obj_trighooks);
  hookAdd("look_at_obj",    do_look_at_obj_trighooks);
  hookAdd("look_at_char",   do_look_at_char_trighooks);
  hookAdd("look_at_room",   do_look_at_room_trighooks);
  hookAdd("obj_to_game",    do_obj_to_game_trighooks);
  hookAdd("char_to_game",   do_char_to_game_trighooks);
  hookAdd("room_to_game",   do_room_to_game_trighooks);

  // add our trigger displays
  register_tedit_opt("speech",         "mob, room" );
  register_tedit_opt("greet",          "mob"       );
  register_tedit_opt("enter",          "mob, room" );
  register_tedit_opt("exit",           "mob, room" );
  register_tedit_opt("self enter",     "mob"       ),
  register_tedit_opt("self exit",      "mob"       );
  register_tedit_opt("drop",           "obj, room" );
  register_tedit_opt("get",            "obj, room" );
  register_tedit_opt("give",           "obj, mob"  );
  register_tedit_opt("receive",        "mob"       );
  register_tedit_opt("wear",           "obj, mob"  );
  register_tedit_opt("remove",         "obj, mob"  );
  register_tedit_opt("reset",          "room"      );
  register_tedit_opt("look",           "obj, mob, room" );
  register_tedit_opt("open",           "obj, room" );
  register_tedit_opt("close",          "obj, room" );
  register_tedit_opt("to_game",        "obj, mob, room" );
}