//*****************************************************************************
//
// 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 = newPyChar(ch);
PyDict_SetItemString(dict, "ch", pych);
Py_DECREF(pych);
}
if(room) {
PyObject *pyroom = newPyRoom(room);
PyDict_SetItemString(dict, "room", pyroom);
Py_DECREF(pyroom);
}
if(obj) {
PyObject *pyobj = newPyObj(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 = newPyChar(me); break;
case VARTYPE_OBJ: pyme = newPyObj(me); break;
case VARTYPE_ROOM: pyme = newPyRoom(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 = newPyChar(opt->data); break;
case VARTYPE_OBJ: pyopt = newPyObj(opt->data); break;
case VARTYPE_ROOM: pyopt = newPyRoom(opt->data); break;
}
PyDict_SetItemString(dict, opt->name, pyopt);
Py_XDECREF(pyopt);
} deleteListIterator(opt_i);
}
// run the script, then kill our dictionary
run_script(dict, triggerGetCode(trig), get_key_locale(triggerGetKey(trig)));
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 {
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 {
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(CHAR_DATA *ch, CHAR_DATA *recv, OBJ_DATA *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(CHAR_DATA *ch, OBJ_DATA *obj, void *none) {
do_obj_trigs (obj, "get", ch, NULL);
do_room_trigs(charGetRoom(ch), "get", ch, obj);
}
void do_drop_trighooks(CHAR_DATA *ch, OBJ_DATA *obj, void *none) {
do_obj_trigs (obj, "drop", ch, NULL);
do_room_trigs(charGetRoom(ch), "drop", ch, obj);
}
void do_enter_trighooks(CHAR_DATA *ch, ROOM_DATA *room, void *none) {
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(CHAR_DATA *ch, ROOM_DATA *room, EXIT_DATA *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(CHAR_DATA *ch, CHAR_DATA *listener, char *speech) {
do_char_trigs(listener, "speech", ch, speech);
}
void do_say_trighooks(CHAR_DATA *ch, void *none, char *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);
}
void do_greet_trighooks(CHAR_DATA *ch, CHAR_DATA *greeted, void *none) {
do_char_trigs(greeted, "greet", ch, NULL);
}
void do_wear_trighooks(CHAR_DATA *ch, OBJ_DATA *obj, void *none) {
do_char_trigs(ch, "wear", obj, NULL);
do_obj_trigs (obj, "wear", ch, NULL);
}
void do_remove_trighooks(CHAR_DATA *ch, OBJ_DATA *obj, void *none) {
do_char_trigs(ch, "remove", obj, NULL);
do_obj_trigs (obj, "remove", ch, NULL);
}
void do_reset_trighooks(ZONE_DATA *zone, void *none1, void *none2) {
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);
}
//*****************************************************************************
// 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);
}