dw_fluffos_v2/
dw_fluffos_v2/fluffos-2.9-ds2.05/
dw_fluffos_v2/fluffos-2.9-ds2.05/ChangeLog.old/
dw_fluffos_v2/fluffos-2.9-ds2.05/Win32/
dw_fluffos_v2/fluffos-2.9-ds2.05/compat/
dw_fluffos_v2/fluffos-2.9-ds2.05/compat/simuls/
dw_fluffos_v2/fluffos-2.9-ds2.05/include/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/clone/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/command/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/data/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/etc/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/include/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/inherit/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/inherit/master/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/log/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/compiler/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/efuns/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/operators/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/u/
dw_fluffos_v2/fluffos-2.9-ds2.05/tmp/
dw_fluffos_v2/fluffos-2.9-ds2.05/windows/
dw_fluffos_v2/lib/
dw_fluffos_v2/lib/binaries/cmds/
dw_fluffos_v2/lib/binaries/cmds/creator/
dw_fluffos_v2/lib/binaries/cmds/living/
dw_fluffos_v2/lib/binaries/cmds/player/
dw_fluffos_v2/lib/binaries/d/admin/obj/
dw_fluffos_v2/lib/binaries/d/liaison/
dw_fluffos_v2/lib/binaries/global/virtual/
dw_fluffos_v2/lib/binaries/global/virtual/setup_compiler/
dw_fluffos_v2/lib/binaries/obj/handlers/autodoc/
dw_fluffos_v2/lib/binaries/obj/handlers/terrain_things/
dw_fluffos_v2/lib/binaries/obj/misc/
dw_fluffos_v2/lib/binaries/obj/misc/buckets/
dw_fluffos_v2/lib/binaries/obj/monster/
dw_fluffos_v2/lib/binaries/obj/reactions/
dw_fluffos_v2/lib/binaries/obj/reagents/
dw_fluffos_v2/lib/binaries/secure/cmds/creator/
dw_fluffos_v2/lib/binaries/secure/master/
dw_fluffos_v2/lib/binaries/std/
dw_fluffos_v2/lib/binaries/std/dom/
dw_fluffos_v2/lib/binaries/std/effects/object/
dw_fluffos_v2/lib/binaries/std/guilds/
dw_fluffos_v2/lib/binaries/std/languages/
dw_fluffos_v2/lib/binaries/std/races/
dw_fluffos_v2/lib/binaries/std/room/
dw_fluffos_v2/lib/binaries/std/room/basic/
dw_fluffos_v2/lib/binaries/std/shops/
dw_fluffos_v2/lib/binaries/std/shops/inherit/
dw_fluffos_v2/lib/binaries/www/
dw_fluffos_v2/lib/cmds/guild-race/
dw_fluffos_v2/lib/cmds/guild-race/crafts/
dw_fluffos_v2/lib/cmds/guild-race/other/
dw_fluffos_v2/lib/cmds/playtester/
dw_fluffos_v2/lib/cmds/playtester/senior/
dw_fluffos_v2/lib/d/admin/
dw_fluffos_v2/lib/d/admin/log/
dw_fluffos_v2/lib/d/admin/mapper/31-10-01/mapmaker/event/
dw_fluffos_v2/lib/d/admin/meetings/
dw_fluffos_v2/lib/d/admin/obj/
dw_fluffos_v2/lib/d/admin/room/we_care/
dw_fluffos_v2/lib/d/admin/save/
dw_fluffos_v2/lib/d/dist/
dw_fluffos_v2/lib/d/dist/mtf/
dw_fluffos_v2/lib/d/dist/pumpkin/
dw_fluffos_v2/lib/d/dist/pumpkin/chars/
dw_fluffos_v2/lib/d/dist/pumpkin/desert/
dw_fluffos_v2/lib/d/dist/pumpkin/gumboot/
dw_fluffos_v2/lib/d/dist/pumpkin/hospital/
dw_fluffos_v2/lib/d/dist/pumpkin/inherit/
dw_fluffos_v2/lib/d/dist/pumpkin/map/
dw_fluffos_v2/lib/d/dist/pumpkin/plain/
dw_fluffos_v2/lib/d/dist/pumpkin/pumpkin/
dw_fluffos_v2/lib/d/dist/pumpkin/save/
dw_fluffos_v2/lib/d/dist/pumpkin/squash/
dw_fluffos_v2/lib/d/dist/pumpkin/terrain/
dw_fluffos_v2/lib/d/dist/pumpkin/woods/
dw_fluffos_v2/lib/d/dist/start/
dw_fluffos_v2/lib/d/learning/TinyTown/buildings/
dw_fluffos_v2/lib/d/learning/TinyTown/map/
dw_fluffos_v2/lib/d/learning/TinyTown/roads/
dw_fluffos_v2/lib/d/learning/add_command/
dw_fluffos_v2/lib/d/learning/arms_and_weps/
dw_fluffos_v2/lib/d/learning/chars/
dw_fluffos_v2/lib/d/learning/cutnpaste/
dw_fluffos_v2/lib/d/learning/examples/npcs/
dw_fluffos_v2/lib/d/learning/examples/player_houses/npcs/
dw_fluffos_v2/lib/d/learning/examples/terrain_map/basic/
dw_fluffos_v2/lib/d/learning/functions/
dw_fluffos_v2/lib/d/learning/handlers/
dw_fluffos_v2/lib/d/learning/help_topics/npcs/
dw_fluffos_v2/lib/d/learning/help_topics/objects/
dw_fluffos_v2/lib/d/learning/help_topics/rcs_demo/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/crowd/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/situations/
dw_fluffos_v2/lib/d/learning/items/
dw_fluffos_v2/lib/d/learning/save/
dw_fluffos_v2/lib/d/liaison/
dw_fluffos_v2/lib/d/liaison/NEWBIE/doc/
dw_fluffos_v2/lib/d/liaison/NEWBIE/save/oldlog/
dw_fluffos_v2/lib/db/
dw_fluffos_v2/lib/doc/
dw_fluffos_v2/lib/doc/creator/
dw_fluffos_v2/lib/doc/creator/autodoc/include/reaction/
dw_fluffos_v2/lib/doc/creator/autodoc/include/ritual_system/
dw_fluffos_v2/lib/doc/creator/autodoc/include/talker/
dw_fluffos_v2/lib/doc/creator/autodoc/include/terrain_map/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/baggage/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/clock/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/clothing/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/cont_save/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/corpse/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/money/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/monster/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/scabbard/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/service_provider/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/state_changer/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/wand/
dw_fluffos_v2/lib/doc/creator/autodoc/std/book_dir/
dw_fluffos_v2/lib/doc/creator/autodoc/std/key/
dw_fluffos_v2/lib/doc/creator/autodoc/std/learning/
dw_fluffos_v2/lib/doc/creator/autodoc/std/map/
dw_fluffos_v2/lib/doc/creator/autodoc/std/race/
dw_fluffos_v2/lib/doc/creator/autodoc/std/weapon_logic/
dw_fluffos_v2/lib/doc/creator/files/
dw_fluffos_v2/lib/doc/creator/policy/
dw_fluffos_v2/lib/doc/creator/room/
dw_fluffos_v2/lib/doc/effects/
dw_fluffos_v2/lib/doc/ideas/
dw_fluffos_v2/lib/doc/known_command/
dw_fluffos_v2/lib/doc/lpc/basic_manual/
dw_fluffos_v2/lib/doc/lpc/intermediate/
dw_fluffos_v2/lib/doc/new/add_command/
dw_fluffos_v2/lib/doc/new/handlers/
dw_fluffos_v2/lib/doc/new/living/
dw_fluffos_v2/lib/doc/new/living/race/
dw_fluffos_v2/lib/doc/new/living/spells/
dw_fluffos_v2/lib/doc/new/player/
dw_fluffos_v2/lib/doc/new/room/guild/
dw_fluffos_v2/lib/doc/new/room/outside/
dw_fluffos_v2/lib/doc/new/room/storeroom/
dw_fluffos_v2/lib/doc/object/
dw_fluffos_v2/lib/doc/playtesters/
dw_fluffos_v2/lib/doc/policy/
dw_fluffos_v2/lib/doc/weapons/
dw_fluffos_v2/lib/global/handlers/
dw_fluffos_v2/lib/global/virtual/setup_compiler/
dw_fluffos_v2/lib/include/
dw_fluffos_v2/lib/include/cmds/
dw_fluffos_v2/lib/include/effects/
dw_fluffos_v2/lib/include/npc/
dw_fluffos_v2/lib/include/shops/
dw_fluffos_v2/lib/net/daemon/chars/
dw_fluffos_v2/lib/net/inherit/
dw_fluffos_v2/lib/net/intermud3/
dw_fluffos_v2/lib/net/intermud3/services/
dw_fluffos_v2/lib/net/obj/
dw_fluffos_v2/lib/net/save/
dw_fluffos_v2/lib/net/smnmp/
dw_fluffos_v2/lib/net/snmp/
dw_fluffos_v2/lib/obj/amulets/
dw_fluffos_v2/lib/obj/b_day/
dw_fluffos_v2/lib/obj/examples/
dw_fluffos_v2/lib/obj/food/alcohol/
dw_fluffos_v2/lib/obj/food/chocolates/
dw_fluffos_v2/lib/obj/food/fruits/
dw_fluffos_v2/lib/obj/food/meat/
dw_fluffos_v2/lib/obj/food/nuts/
dw_fluffos_v2/lib/obj/food/seafood/
dw_fluffos_v2/lib/obj/food/vegetables/
dw_fluffos_v2/lib/obj/fungi/
dw_fluffos_v2/lib/obj/furnitures/artwork/
dw_fluffos_v2/lib/obj/furnitures/bathroom/
dw_fluffos_v2/lib/obj/furnitures/beds/
dw_fluffos_v2/lib/obj/furnitures/cabinets/
dw_fluffos_v2/lib/obj/furnitures/chairs/
dw_fluffos_v2/lib/obj/furnitures/chests/
dw_fluffos_v2/lib/obj/furnitures/clocks/
dw_fluffos_v2/lib/obj/furnitures/crockery/
dw_fluffos_v2/lib/obj/furnitures/cupboards/
dw_fluffos_v2/lib/obj/furnitures/cushions/
dw_fluffos_v2/lib/obj/furnitures/fake_plants/
dw_fluffos_v2/lib/obj/furnitures/lamps/
dw_fluffos_v2/lib/obj/furnitures/mirrors/
dw_fluffos_v2/lib/obj/furnitures/outdoor/
dw_fluffos_v2/lib/obj/furnitures/safes/
dw_fluffos_v2/lib/obj/furnitures/shelves/
dw_fluffos_v2/lib/obj/furnitures/sideboards/
dw_fluffos_v2/lib/obj/furnitures/sofas/
dw_fluffos_v2/lib/obj/furnitures/stoves/
dw_fluffos_v2/lib/obj/furnitures/tables/
dw_fluffos_v2/lib/obj/furnitures/wardrobes/
dw_fluffos_v2/lib/obj/handlers/
dw_fluffos_v2/lib/obj/handlers/autodoc/
dw_fluffos_v2/lib/obj/jewellery/anklets/
dw_fluffos_v2/lib/obj/jewellery/bracelets/
dw_fluffos_v2/lib/obj/jewellery/earrings/
dw_fluffos_v2/lib/obj/jewellery/misc/
dw_fluffos_v2/lib/obj/jewellery/necklaces/
dw_fluffos_v2/lib/obj/jewellery/rings/
dw_fluffos_v2/lib/obj/media/
dw_fluffos_v2/lib/obj/misc/buckets/
dw_fluffos_v2/lib/obj/misc/jars/
dw_fluffos_v2/lib/obj/misc/papers/
dw_fluffos_v2/lib/obj/misc/player_shop/
dw_fluffos_v2/lib/obj/misc/shops/
dw_fluffos_v2/lib/obj/misc/traps/
dw_fluffos_v2/lib/obj/monster/
dw_fluffos_v2/lib/obj/monster/godmother/
dw_fluffos_v2/lib/obj/monster/transport/
dw_fluffos_v2/lib/obj/plants/inherit/
dw_fluffos_v2/lib/obj/potions/
dw_fluffos_v2/lib/open/boards/
dw_fluffos_v2/lib/save/autodoc/
dw_fluffos_v2/lib/save/bank_accounts/
dw_fluffos_v2/lib/save/boards/frog/
dw_fluffos_v2/lib/save/books/bed_catalog/
dw_fluffos_v2/lib/save/creators/
dw_fluffos_v2/lib/save/mail/
dw_fluffos_v2/lib/save/mail/p/
dw_fluffos_v2/lib/save/soul/data/
dw_fluffos_v2/lib/save/tasks/
dw_fluffos_v2/lib/save/vaults/
dw_fluffos_v2/lib/secure/cmds/lord/
dw_fluffos_v2/lib/secure/config/
dw_fluffos_v2/lib/secure/items/
dw_fluffos_v2/lib/secure/player/
dw_fluffos_v2/lib/soul/
dw_fluffos_v2/lib/soul/i/
dw_fluffos_v2/lib/soul/j/
dw_fluffos_v2/lib/soul/k/
dw_fluffos_v2/lib/soul/o/
dw_fluffos_v2/lib/soul/q/
dw_fluffos_v2/lib/soul/to_approve/
dw_fluffos_v2/lib/soul/u/
dw_fluffos_v2/lib/soul/v/
dw_fluffos_v2/lib/soul/wish_list/
dw_fluffos_v2/lib/soul/y/
dw_fluffos_v2/lib/soul/z/
dw_fluffos_v2/lib/std/creator/
dw_fluffos_v2/lib/std/effects/
dw_fluffos_v2/lib/std/effects/attached/
dw_fluffos_v2/lib/std/effects/external/
dw_fluffos_v2/lib/std/effects/fighting/
dw_fluffos_v2/lib/std/effects/other/
dw_fluffos_v2/lib/std/environ/
dw_fluffos_v2/lib/std/guilds/
dw_fluffos_v2/lib/std/hospital/
dw_fluffos_v2/lib/std/house/
dw_fluffos_v2/lib/std/house/onebedhouse/
dw_fluffos_v2/lib/std/house/onebedhut/
dw_fluffos_v2/lib/std/house/tworoomflat/
dw_fluffos_v2/lib/std/languages/
dw_fluffos_v2/lib/std/liquids/
dw_fluffos_v2/lib/std/nationality/
dw_fluffos_v2/lib/std/nationality/accents/
dw_fluffos_v2/lib/std/nationality/accents/national/
dw_fluffos_v2/lib/std/nationality/accents/regional/
dw_fluffos_v2/lib/std/npc/goals/
dw_fluffos_v2/lib/std/npc/goals/basic/
dw_fluffos_v2/lib/std/npc/goals/misc/
dw_fluffos_v2/lib/std/npc/inherit/
dw_fluffos_v2/lib/std/npc/plans/
dw_fluffos_v2/lib/std/npc/plans/basic/
dw_fluffos_v2/lib/std/outsides/
dw_fluffos_v2/lib/std/races/shadows/
dw_fluffos_v2/lib/std/room/basic/topography/
dw_fluffos_v2/lib/std/room/controller/
dw_fluffos_v2/lib/std/room/controller/topography/
dw_fluffos_v2/lib/std/room/furniture/games/
dw_fluffos_v2/lib/std/room/furniture/inherit/
dw_fluffos_v2/lib/std/room/inherit/carriage/
dw_fluffos_v2/lib/std/room/inherit/topography/
dw_fluffos_v2/lib/std/room/punishments/
dw_fluffos_v2/lib/std/room/topography/area/
dw_fluffos_v2/lib/std/room/topography/iroom/
dw_fluffos_v2/lib/std/room/topography/milestone/
dw_fluffos_v2/lib/std/shadows/
dw_fluffos_v2/lib/std/shadows/attached/
dw_fluffos_v2/lib/std/shadows/curses/
dw_fluffos_v2/lib/std/shadows/disease/
dw_fluffos_v2/lib/std/shadows/fighting/
dw_fluffos_v2/lib/std/shadows/room/
dw_fluffos_v2/lib/std/shops/controllers/
dw_fluffos_v2/lib/std/shops/objs/
dw_fluffos_v2/lib/std/shops/player_shop/
dw_fluffos_v2/lib/std/shops/player_shop/office_code/
dw_fluffos_v2/lib/std/socket/
dw_fluffos_v2/lib/www/
dw_fluffos_v2/lib/www/external/autodoc/
dw_fluffos_v2/lib/www/external/java/telnet/Documentation/
dw_fluffos_v2/lib/www/external/java/telnet/Documentation/images/
dw_fluffos_v2/lib/www/external/java/telnet/examples/
dw_fluffos_v2/lib/www/external/java/telnet/tools/
dw_fluffos_v2/lib/www/pics/
dw_fluffos_v2/lib/www/secure/creator/
dw_fluffos_v2/lib/www/secure/editors/
dw_fluffos_v2/lib/www/secure/survey_results/
dw_fluffos_v2/win32/
/*  -*- LPC -*-  */
/*
 * $Locker:  $
 * $Id: response_mon.c,v 1.31 2003/04/01 21:39:38 ceres Exp $
 *
*/
/**
 * This file contains the methods to make the npcs in game respond
 * to soul and says.  Allows for a level of npc interation.
 * <p>
 * Code originaly by nimmox@igor.  Modified for discworld by Pinkfish.
 * <p>
 * @change Olorin Sep 97
 * event_soul does a convert_message on the mess first,
 * to avoid spurious matches caused by a message like eg.
 * "$the_short:/global/lord#64387$ $V$0=smiles,smile$V$ .... in souln
 * @change who knows when
 * changed to stop the responses when the npc is in /room/rubbish
 * @author Pinkfish
 * @see /obj/monster.c
 */
#include <soul.h>
#include <language.h>

#define SPECIAL_DAY "/obj/handlers/special_day"

class response_data {
   object last_ob;
   string last_mess;
   int stop_responding;
   int use_regexp;
   int last_count;
   int last_time;
   int understand_anything;
   object* only_respond_to;
}

mixed *_respond_to_with;
nosave class response_data _response_data;

int match(string str, mixed str2);
void event_soul(object per, string mess, object *avoid, string verb,
                string last, mixed at) ;

void create() {
  _respond_to_with = ({ });
  _response_data = new(class response_data);
  _response_data->only_respond_to = ({ });
} /* create() */

/**
 * This method sets or clears the flag that allows the npc to understand
 * any language at all.  THis is useful for those times when you need an
 * npc that speaks/understands any language.
 * @param flag the new value of the flag
 */
void set_response_mon_understand_anything(int flag) {
   _response_data->understand_anything = flag;
}


/**
 * This method sets or clears the flag to use regular expressions instead of
 * the traditional arrays of words.  In addition, this flag inhibits
 * the usual stripping of non-alphanumerics from the input strings.
 * @param flag the new value of the flag: 1 says to use regular expressions,
 * 0 to not use them
 */
void set_response_mon_regexp(int flag) { _response_data->use_regexp = flag; }

/**
 * This method returns the current state of the flag that indicates whether
 * to use regular expressions instead of the traditional array of words.
 * @return 1 if using regular expressions, 0 if not
 */
int query_response_mon_regexp() { return _response_data->use_regexp; }

/**
 * This method checks to see if the response monster code is in
 * debug mode.
 * @return 1 if in debug mode, 0 if not
 * @see set_response_mon_debug_mode()
 */
int query_response_mon_debug_mode() {
  return this_object()->query_property("respon mon debug");
} /* query_response_mon_debug_mode() */

/**
 * This method sets the current debug flag for the response monster
 * code.
 * @param flag the new value of the flag, 1 on, 0 off
 * @see query_response_mon_debug_mode()
 */
void set_response_mon_debug_mode(int flag) {
  this_object()->add_property("respon mon debug", flag);
} /* set_response_mon_debug_mode() */

/**
 * This method allows the toggling of the responses of the npc.
 * If this is set to 1, then the npc will stop responding to
 * messages.
 * @param i 1 to make the npc not respond, 0 to make it respond again
 * @see query_stop_responding()
 */
void set_stop_responding(int i) { _response_data->stop_responding = i; }
/**
 * This method returns the flag determining the toggling of the responses
 * of the npc.
 * If this is set to 1, then the npc will stop responding to
 * messages.
 * @return 1 to the npc will not respond, 0 it is responding
 * @see query_stop_responding()
 */
int query_stop_responding() { return _response_data->stop_responding; }

/**
 * This method checks to see if the response is allowed for this object.
 * NB: objects to which this_object is not visible are disallowed.
 * @param ob the object to check
 * @param response the response that is matched
 * @return 1 if the object is allowed, 0 if it is not
 */
int query_response_allowed(object ob,
                           string *response) {
  if (!this_object()->query_visible(ob)) {
    return 0;
  }
  return 1;
} /* query_response_allowed() */

/**
 * This method sets the current responses for the npc.
 * This will overwrite the current responses in the npc.
 * Please use add_respond_to_with in your NPC's instead of this function
 * call, as it requires prior knowledge as to the internal structure of
 * the respond_to_with code that add_respond_to_with handles nicely.
 * The array is of the formant:<pre>
 *   ({
 *      trigger1,
 *      response1,
 *      trigger2,
 *      response2,
 *      ...
 *    })
 * </pre>
 * @see add_respond_to_with()
 * @see query_respond_to_with()
 */
void set_respond_to_with( mixed map ) {
  mixed *old_keys;
  int i;

  if ( mapp( map ) ) {
#ifdef DEBUG
    log_file("BAD_RESPONSE", "set_respond_to_with() called by %O (%s)\n",
             previous_object(), previous_object()->query_name());
#endif    
    old_keys = keys(map);
    _respond_to_with = allocate(sizeof(old_keys) * 2);
    for (i = sizeof(old_keys); i--; ) {
      _respond_to_with[2*i] = old_keys[i];
      _respond_to_with[2*i+1] = map[old_keys[i]];
    }
    return;
  }
  _respond_to_with = map;
} /* set_respond_to_with() */

/**
 * This method returns the current responding to with array.
 * The array is of the formant:<pre>
 *   ({
 *      trigger1,
 *      response1,
 *      trigger2,
 *      response2,
 *      ...
 *    })
 * </pre>
 * @see add_respond_to_with()
 */
mixed *query_respond_to_with() { return _respond_to_with; }

/**
 * This method adds a respond message to respond to into the
 * current array of responses.
 * <p>
 * If response_mon_regexp is not set, the trigger consists of an
 * array of words to be matched
 * (in order) in the string.  If there is an array instead of
 * a single word at one point then any of the words in the
 * array will be matched.  If response_mon_regexp is set, the
 * trigger is a single string, representing the regular expression
 * to be matched in the string.
 * <p>
 * The response is either an array of things to execute
 * (in which case a random one will be chosen each time) or
 * a function pointer or a string.  If it is a string then
 * the command of that name will be executed, if the
 * string starts with a '#' then the function named after that
 * will be called on the npc.  That was exciting wasn't it?
 * If the string has a $hname$ name in it and it is not a
 * function call (starts with a #) then the $hname$ will be replaced
 * with the name of the triggering object. $hcname$ or $short$ will be
 * replaced with the short of the triggering object.
 * <p>
 * In the case of a function call, two arguments are passed into
 * the function: the first is the person which triggered the effect
 * and the second in the message which triggered the effect.
 * <p>
 * NOTE: The NPC must explicitly know the language being spoken, even
 * if it's "common".  I don't know why.  That's just the way it is.  See
 * add_language().
 * @example
 * // Simple response
 * add_respond_to_with(({ "@say", "bing" }), "'Yes!  Bing bing bing!");
 * @example
 * // respond to someone saying 'frog' or 'toad'
 * add_respond_to_with(({ "@say", ({ "frog", "toad" }) }),
 *                     "'Frogs and toads are nice.");
 * @example
 * // Randomly say something or bing back at them
 * add_respond_to_with(({ "@say", "bing" }),
 *                     ({ "'Yes!  Bing bing bing!", "bing $hname$" }));
 * @example
 * // Call the function 'rabbit' on the npc.
 * add_respond_to_with(({ "@say", "bing" }), "#rabbit");
 * @example
 * // Do something cute with a function pointer
 * add_respond_to_with(({ "@bing" }),
 *                  (: do_command("'something wild for " + $1->a_short()) :));
 * @param trigger the trigger to trigger the action on
 * @param response the response to the action
 * @see query_respond_to_with()
 * @see query_response_allowed()
 * @see set_response_mon_regexp()
 * @see query_response_mon_regexp()
 * @see regexp()
 * @see add_language()
 */
void add_respond_to_with( mixed *trigger, mixed response ) {
  _respond_to_with += ({ trigger, response });
} /* add_response() */

/**
 * This adds an object into the list of objects that we will only respond
 * to.  If this array is empty then we will respond to anyone, if it has
 * something in it then we will only respond to them.
 * @param person the person to add to the response array
 */
void add_only_respond_to(object person) {
  _response_data->only_respond_to += ({ person });
} /* add_only_respond_to() */

/**
 * This method removes an object from the list of people to respond to.
 * @param person the person to remove
 */
void remove_only_respond_to(object person) {
   _response_data->only_respond_to -= ({ person });
} /* remove_only_respond_to() */

/**
 * This method returns the current list of people we are only responding
 * to.
 * @return the array of people we are responding to
 */
object* query_only_respond_to() {
   return _response_data->only_respond_to;
} /* query_only_respond_to() */

/* Check_sub_sequence checks whether the array (words) contains a subsequence
   as specified in the respond_to_with array */
/**
 * This method checks the subsequence of words to see if it matches
 * any of our current response sets.
 * @param words the words which are to be tested
 * @return the response to be executed
 * @see add_respond_to_with()
 */
protected mixed *check_sub_sequence( mixed *words ) {
  int word_index;
  int format_index; /* what do you know....Meaningful names :-) */
  int format_size;
  int word_size;
  int match_counter;
  int cu_format_size;
  string word_str;
  mixed data;
  string *patterns;
  string pattern;
  string extra;

  format_index = 0;
  word_size = sizeof( words );
  word_str = implode(words, " ");
  format_size = sizeof( _respond_to_with );
  while (format_index < format_size) {
    if ( _response_data->use_regexp ) {
        data = _respond_to_with[ format_index ];

        if ( arrayp( data ) && sizeof( data ) > 1 ) {
            // Excellent. Now, let's add our @say data to the front of
            // the pattern.

            if ( arrayp( data[0] ) ) {
                // Combine the first part into the array, separated
                // by or's, with at least one match.
                extra = "(" + implode( data, "|" ) + ")+";
            }
            else {
                extra = data[0];
            }

            patterns = map( data[1], (: $(extra) + " " + $1 :) );
        }
        else {
            patterns = ({ data });
        }

        //tell_creator( "taffyd", "%O, %O\n", word_str, patterns );

        // Check all of the possible responses.
        foreach( pattern in patterns ) {
            // tell_creator( "taffyd", "%O\n", pattern );
            if ( regexp( word_str, pattern ) ) {
                return _respond_to_with[ format_index + 1 ];
            }
        }
    }
    else {
        word_index = 0;
        match_counter = 0;
        cu_format_size = sizeof(_respond_to_with[format_index]);
        while ((word_index < word_size) && (match_counter < cu_format_size)) {
          match_counter += match(words[word_index],
                                 _respond_to_with[format_index][match_counter]);
          word_index++;
        }
        if (match_counter == (cu_format_size)) {
          return _respond_to_with[format_index + 1];
        }
    }

    format_index += 2;
  }
  return 0;
} /* check_sub_sequence() */

/* match returns 1 if str matches the format.... see also the definitions in
   the example file... */
/** @ignore yes */
protected int match(string str, mixed format) {
  if (pointerp(format)) {
    return (member_array(str, format) != -1);
  }
  if (str == format) {
    return 1;
  }
  return 0;
} /* match() */

/* removing annoying readmarks....  Like .'s and ?'s */
/**
 * This method removes annoying read marks to make the string easier to
 * parse.  Basically it strips puncutation.
 * @param str the string to remove the punctuation from
 * @return the string without any punctuation
 */
protected string remove_read_marks(string str) {
  int blij;
  string result;
  int size;
  string temp;

  size = strlen(str);
  blij = 0;
  result = "";
  while (blij < size) {
    temp = str[blij..blij];
    if (((temp >= "a") && (temp <= "z")) ||
        ((temp >= "0") && (temp <= "9")) ||
        (temp == " ")) {
      result += temp;
    } else {
      result += " ";
    }
    blij++;
  }
  return result;
} /* remove_read_marks() */

/**
 * This method runs the command passed in, doing some substitution.
 * @param str the string to execute
 * @param per the person who triggered the command
 */
protected void senddstr(string str, object per) {
  if ( environment( this_object() ) ) {
    if ( per ) {
      command(replace(str, ({ "$hname$", per->query_name(), "$hcname$",
                                per->query_short(),
                              "$hshort$", per->query_short() })));
    } else {
      command( str );
    }
  }
} /* sendstr() */

/* Take care not to send to other monsters.... Infinite recursion problem...*/

/* Imagine two monsters chatting with each other......*/
/**
 * This method executes the response to the matched string.
 * @param rep the response to execute
 * @param per the person who initiated the event
 * @param mess the message that was matched
 * @see add_respond_to_with()
 */
protected void exec_response(mixed rep, object per, string mess) {
  string *rabbit;

  if (pointerp(rep)) {
    return exec_response(rep[random(sizeof(rep))], per, mess);
  }

  if (functionp(rep)) {
    evaluate(rep, per, mess);
  } else if (stringp(rep)) {
    if (rep[0..0] == "#") {
      rabbit = explode(rep[1..], "#");
      if (sizeof(rabbit) > 1) {
        call_out((: call_other($1, $2, $3, $4) :), 0,
                 rabbit[0], rabbit[1], per, mess);
      } else {
        call_out(rabbit[0], 0, per, mess);
      }
    } else {
      call_out("senddstr", 0, rep, per);
    }
  }
} /* exec_response() */

/** @ignore yes */
private void do_delay_thingy(string *extra, string mess, object per) {
  mixed *response;

  // It does lower case stuff sin...
  mess = lower_case(mess);
  if (!_response_data->use_regexp) {
    mess = remove_read_marks(mess);
  }
  if (query_response_mon_debug_mode()) {
    tell_object(per, "Parsing the text: " + implode(extra, " ") + " " + mess + "\n");
  }
  response = check_sub_sequence(extra + explode(mess, " "));
  if (query_response_mon_debug_mode()) {
    tell_object(per, sprintf("Responding with %O\n", response));
  }

  if (response) {
    if (query_response_allowed(per, response)) {
      exec_response(response, per, mess);
    }
  }
} /* do_delay_thingy() */

/** @ignore yes
 * Some new loop detection. Track the last object, message we responded
 * to and when. Don't respond to the same object & message more than twice in
 * a suitable period.
 * Clearly this won't detect three-way, or other complex, loops.
 */
int check_loop(object per, string mess) {

  // We'll assume players can't get into loops. :)
  if(interactive(per)) {
    return 0;
  }

  if (_response_data->last_ob == per &&
      (_response_data->last_mess = mess) &&
      _response_data->last_time > time() - 10)
  {
    if(_response_data->last_count > 1) {
      return 1;
    } else {
      _response_data->last_count++;
    }
    _response_data->last_time = time();
  } else {
    _response_data->last_ob = per;
    _response_data->last_mess = mess;
    _response_data->last_count = 1;
    _response_data->last_time = time();
  }

  return 0;
}

/** @ignore yes */
private void do_response(string *extra, string mess, object per) {
  if (sizeof(_response_data->only_respond_to)) {
     _response_data->only_respond_to -= ({ 0 });
     if (sizeof(_response_data->only_respond_to) &&
         member_array(per, _response_data->only_respond_to) == -1) {
        return ;
     }
  }
  call_out((: do_delay_thingy($1, $2, $3) :), 2, extra, mess, per);
} /* do_response() */

/*
 * respond as defined in respond_to_with array...See also
 * the definitions in the example monster
 */

/**
 * This method is called on the npcs to help in recognising saytos.
 * Beware...  A @say message will also be added, so you need to make sure
 * you respond to the correct message and not to both.
 */
void event_person_sayto(object per, string mess, string lang, object* targets) {
  string skill;

  if(_response_data->stop_responding ||
     !sizeof(_respond_to_with) ||
     environment() == find_object( "/room/rubbish" ) ||
     environment() == find_object( "/room/void" ) ||
     !per || per == this_object()) {
    return ;
  }

  if(check_loop(per, mess)) {
    return;
  }

  skill = LANGUAGES->query_language_spoken_skill(lang);
  if (this_object()->query_skill(skill) < 90 ||
      per->query_skill(skill) < 60) {
    return;
  }

  do_response(({ "@sayto" }), mess, per);
} /* event_person_say() */

/** @ignore yes */
void event_person_say(object per, string start, string mess, string lang) {
  string skill;

  if(_response_data->stop_responding ||
     !sizeof(_respond_to_with) ||
     environment() == find_object( "/room/rubbish" ) ||
     environment() == find_object( "/room/void" ) ||
     !per || per == this_object()) {
    return ;
  }

  if(check_loop(per, mess))
    return;

  if (!_response_data->understand_anything) {
     skill = LANGUAGES->query_language_spoken_skill(lang);
     if (this_object()->query_skill(skill) < 90 ||
         per->query_skill(skill) < 60) {
       return;
     }
  }

  do_response(({ "@say" }), mess, per);
} /* event_person_say() */

/** @ignore yes */
varargs void event_soul(object per, string mess, object *avoid, string verb,
                        string last, mixed at) {
  if (per == find_object(SOUL_OBJECT))
    per = previous_object(2);

  if(!interactive(per) ||
     _response_data->stop_responding ||
     environment() == find_object( "/room/rubbish" ) ||
     environment() == find_object( "/room/void" ) ||
      member_array(this_object(), avoid) != -1 ||
     !sizeof(_respond_to_with)) {
    return ;
  }

  if((!objectp(at) || (objectp(at) && at != this_object())) &&
     environment(per) != environment() ) {
    return;
  }

  mess = this_object()->convert_message( mess );

  if(check_loop(per, mess))
    return;
  if (objectp(at)) {
    do_response(({ "@" + verb, "#" + last, at->query_name() }), mess, per);
  } else {
    do_response(({ "@" + verb, "#" + last }), mess, per);
  }
} /* event_soul() */

/** @ignore yes */
varargs void event_whisper(object per, string mess, object *obs, string lang,
                           object me) {
  string skill;
  
  if (!interactive(per) || _response_data->stop_responding ||
      environment() == find_object( "/room/rubbish" ) ||
      !sizeof(_respond_to_with)) {
    return ;
  }

  if(check_loop(per, mess)) {
    return;
  }

  if (!_response_data->understand_anything) {
    skill = LANGUAGES->query_language_spoken_skill(lang);
    if (this_object()->query_skill(skill) < 90 ||
        per->query_skill(skill) < 60) {
      return;
    }
  }

  // Don't let them see the message unless it is actually directed at them.
  if (member_array(this_object(), obs) == -1) {
    do_response(({ "@whisper", map(obs, (: $1->query_name() :)) }),
                "", per);
  } else {
    mess = this_object()->convert_message( mess );
    do_response(({ "@whisper", map(obs, (: $1->query_name() :)) }),
                mess, per);
  }
}