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: pinkfish $
 * $Id: monster.c,v 1.85 2003/07/28 21:05:41 ceres Exp pinkfish $
 */

/**
 * This file contains all the information relevant to creating an
 * npc (or monster).
 * @author Pinkfish
 */
#include <config.h>
#include <living.h>
#include <map.h>
#include <monster.h>
#include <move_failures.h>
#include <player.h>
#include <skills.h>
#include <wander.h>
#include <route.h>
#include <combat.h>
#define SPELL_INCLUDE_CLASS
#include <top_ten_tables.h>

#define SOUL_OBJECT "/obj/handlers/soul"

inherit "/std/living/mon_actions";
inherit "/std/living/living";
inherit "/std/living/response_mon";
inherit "/global/events";
inherit "/global/guild-race";

nosave string cap_name;
nosave mixed *chat_string;
nosave mixed *achat_string;
private nosave mixed *_combat_actions;
nosave mixed _move_after;
nosave mixed *throw_out;
nosave int chat_chance;
nosave int achat_chance;
nosave int aggressive;
nosave int join_fight_type;
nosave int follow_speed;
nosave int virtual_move;
nosave int moves;
nosave string race;
nosave string guild;
nosave string join_fight_mess;
nosave string true_location;
nosave mixed *enter_commands;
nosave string *move_zones;
private nosave mixed *_queued_commands;
private nosave mixed *doing_story;
nosave object last_attacked;
private nosave string *following_route;
private nosave int added_language;
private nosave function cmd_func = 0;

// This is used to make sure players cannot change an npc's positon with
// souls.
private nosave int cannot_change_position;
private nosave int always_return_to_default_position;

private nosave mapping _spell_actions;

void do_move_after(int running_away);
void do_route_move();
private void _next_queued_command();
void start_attack( object who );

#if !efun_defined(add_action)
protected mixed _process_input(string);
protected mixed command(string);
void command_override(function func);
int drunk_check(string str);
#endif



void create() {
    do_setup++;
    events::create();
    living::create();
    mon_actions::create();
    response_mon::create();
    //p  command::create();
    do_setup--;
    reset_get();
    follow_speed = 3;
    doing_story = ({ ({ }), ({ }) });
    chat_string = ({ 0, ({ }) });
    achat_string = ({ 0, ({ }) });
    move_zones = ({ });
    _spell_actions = ([ ]);
    _combat_actions = ({ });
    enter_commands = ({ });
    known_commands = ({ });
    _queued_commands = ({ });
    following_route = ({ });
    enable_commands();
    living_commands();
    parser_commands();
    communicate_commands();
    command_commands();
    add_property("npc",1);
    set_rows( 24 );
    set_cols( 200 );
    // Default to 5 seconds.
    always_return_to_default_position = 5;
#ifdef OLD_SOUL
    add_action("*", "soul_commqz", -2);
#endif
    /*
      add_action("init_race", "init_race");
      race_guild_commands();
    */
    set_con( 13 );
    set_dex( 13 );
    set_int( 13 );
    set_str( 13 );
    set_wis( 13 );
    set_max_hp( 10000 );
    set_hp( 100000 );
    set_max_gp( 10000 );
    set_gp( 10000 );
    if ( !do_setup )
        this_object()->setup();
    if(clonep())
        call_out(function() {
              if(!environment()) {
                  move(find_object("/room/rubbish"));
              }
          }, 60);
} /* create() */

/**
 * This method sets up a nationality and sets up a region in the
 * nationality for the npc.
 * @param nationality the nationality to set
 * @param region the region in the nationality
 */
void setup_nationality(string nationality, string region) {
   set_nationality(nationality);
   set_nationality_region(region);
   if (!load_object(nationality)) {
      debug_printf("Bad nationality, %O\n", nationality);
   } else {
      if (!nationality->query_region_description(region)) {
         debug_printf("Bad region %O in nationality, %O\n", region,
                      nationality);
      }
      add_language(nationality->query_language());
      set_language(nationality->query_language());
      set_default_language(nationality->query_language());
   }
}

/** @ignore yes */
void dest_me() {
    living::dest_me();
} /* dest_me() */

/** @ignore yes */
int soul_commqz(string str) {
    string verb, bit;

    if (sscanf(str, "%s %s", verb, bit) == 2)
        return (int)SOUL_OBJECT->soul_command(verb, bit);
    return (int)SOUL_OBJECT->soul_command(str, "");
} /* soul_commqz() */

/** @ignore yes */
int query_sp() { return 50; }
/** @ignore yes */
int adjust_sp( int number ) { return 50; }

/**
 * This method returns the current capitalized name of the npc.
 * @return the current capitalized name
 * @see set_cap_name()
 */
string query_cap_name() {
    return cap_name;
} /* query_cap_name() */

/**
 * This method set the current capitalized name of the npc.
 * @param s the capitalized name of the npc
 * @see query_cap_name()
 */
void set_cap_name(string s) { cap_name = s; }

/** @ignore yes */
int soul_com_force(string str) {
    //  string str1,str2;

    if (file_name(previous_object()) != SOUL_OBJECT)
        return 0;
    command(str);
    return 1;
} /* soul_com_force() */

/** @ignore yes */
void set_name(string n) {
    if (query_name() && query_name() != "object")
        return;
    ::set_name(n);
    cap_name = capitalize(query_name());
    add_plural(pluralize(query_name()));
    set_short( query_name() );
    set_long("You see nothing special.\n");
    set_living_name(n);
} /* set_name() */

/** @ignore yes */
string long(string str, int dark) {
    string s;

    if (dark < -1) {
      s = "You can only make out a rough shape in the glare.\n";
    } else if (dark > 1) {
      s = "You can only make out a rough shape in the gloom.\n";
    } else {
       s = query_long();
    }
    if(!dark) {
        s += capitalize(query_pronoun())+" "+health_string()+".\n";
        s += capitalize(query_pronoun()) + " is " +
            this_object()->query_position_short() + ".\n";
        s += calc_extra_look();
        s += query_living_contents(0);
    }
    return s;
} /* long() */

/**
 * This function is deprecated.  Use basic_setup() instead.
 *
 * This method sets the race of the npc.  The race should be one of
 * the races listed in the /std/race.c object.
 * This is used in conjuction with the
 * guild when set_level is called to setup the default
 * attributes for the npc.  This should only be
 * called *before* set_level() is called.
 * @param str the race to set
 * @return always returns 1
 * @example
 * ob = clone_object("/obj/monster");
 * ob->set_race("fish");
 * @example
 * inherit "/obj/monster";
 *
 * void setup() {
 *    ...
 *    set_race("fish");
 *    ...
 *    set_level(12);
 * } /\* setup() *\/
 * @see query_race()
 * @see query_guild()
 * @see set_guild()
 * @see set_level()
 * @see basic_setup()
 */
int set_race(string str) {
    race = str;
    return 1;
} /* set_race() */

/**
 * This method returns the current race of the npc.
 * @return the current race of the object
 * @see set_race()
 * @see query_guild()
 * @see set_level()
 */
string query_race() { return race; }
/**
 * This method returns the current guild of the npc.
 * @return this current guild of the npc
 * @see query_race()
 * @see set_guild()
 * @see set_level()
 */
string query_guild() { return guild; }
/**
 * @ignore yes
 * This method should not be used.  It is 'depreciated'.
 */
string query_class() { return guild; }
/**
 * @ignore yes
 * This method should not be used.  It is 'depreciated'.
 */
string query_profession() { return guild; }

/**
 * @ignore yes
 * This method should not be used.  It is 'depreciated'.
 */
int set_class(string str) {
    guild = str;
} /* set_guild() */

/**
 * This method is deprecated.  Use basic_setup() instead.
 *
 * This method sets the current guild of the npc to the
 * passed in value.  The guild should be one of
 * the guilds listed in the /std/race.c object.
 * This is used in conjuction with the
 * race when set_level is called to setup the default
 * attributes for the npc.  This should only be
 * called *before* set_level() is called.
 * @param str the new guild for the npc
 * @example
 * ob = clone_object("/obj/monster");
 * ob->set_guild("fighter");
 * @example
 * inherit "/obj/monster";
 *
 * void setup() {
 *    ...
 *    set_race("fighter");
 *    ...
 *    set_level(12);
 * } /\* setup() *\/
 * @see query_race()
 * @see query_guild()
 * @see set_guild()
 * @see set_level()
 */
int set_guild(string str) {
    guild = str;
} /* set_guild() */

/**
 * @ignore yes
 * This method should not be used.  It is 'depreciated'.
 */
int set_profession(string str) {
    guild = str;
} /* set_profession() */

/**
 * This method makes the npc initialise all their equipment, like hold
 * it and stuff.
 * @see do_command()
 */
void init_equip() { command("equip"); }

/**
 * This method allows you to control the npc and get it to do
 * actions.  This can be used for npc control and inteligence.
 *
 * Be very careful with this command! This does not go through any
 * command queue like players have and so NPCs can end up doing
 * tons of commands in very short order.
 *
 * If you're trying to make your NPC act like a player use
 * eue_command() instead
 *
 * @param words the action to preform
 * @see init_equip()
 * @see init_command()
 * @example
 * ob = clone_object(NICE_HAIRY_APE);
 * ob->do_command("'I am a hairy ape!");
 * ob->do_command("emote apes around the room.");
 */
int do_command( string words ) {
    if ( this_object()->query_property( PASSED_OUT_PROP ) ) {
        return -1;
    }
    if ( stringp( words ) ) {
        return command( words );
    }
    printf( "Invalid parameter to do_command: %O for monster %O in %O.\n",
      words, this_object(), environment() );
    return -1;
} /* do_command() */

/**
 * This method throws away any queued commands.
 * It doesn't remove the call_out however if no
 * new commands are added there will be no effect.
 * @see init_equip()
 * @see init_command()
 * @see delay_command()
 * @see do_command()
 * @see queue_command()
 */
void delete_queued_commands() {
  _queued_commands = ({ });
}

/**
 * This method returns the queued command list.
 * @see init_equip()
 * @see init_command()
 * @see delay_command()
 * @see do_command()
 * @see queue_command()
 */
mixed *query_queued_commands() {
  return _queued_commands;
}

/** @ignore yes */
private void _next_queued_command() {
  mixed next;

  if (!sizeof(_queued_commands)) return;

  // No actions while casting.
  if(this_object()->query_casting_spell()){
    call_out( (: _next_queued_command :), 2 );
    return;
  }
  
  next = _queued_commands[0];
  if (intp( next )) {
    _queued_commands = _queued_commands[1..];
    if (!sizeof(_queued_commands)) return;
    next = _queued_commands[0];
  }
  while (stringp( next )) {
    if (this_object()->queue_commands()) {
      call_out( (: _next_queued_command :), 2 );
      return;
    }
    do_command( next );
    _queued_commands = _queued_commands[1..];
    if (!sizeof(_queued_commands)) return;
    next = _queued_commands[0];
  }
  call_out( (: _next_queued_command :), next );
} /* _next_queued_command */

/**
 * This method allows you to control the npc and get it to do
 * actions where they are queued as for players.   The command
 * is always delayed by delay even if there are no commands pending
 * unlike queue_command(). This function
 * is 100% compatible with queue_command() and init_command().
 * @param words the action to perform
 * @param interval to wait before the command.
 * @see queue_command()
 * @see query_queued_commands()
 * @see init_command()
 * @see do_command()
 * @example
 * ob = clone_object(NICE_HAIRY_APE);
 * ob->delay_command("'I am a hairy ape!",10);
 * ob->delay_command("emote apes around the room.",2);
 * ob->queue_command("emote get banana.",3);
 * ob->queue_command("emote get apple.");
 * After 10 seconds it says "I am a hariy ape",
 * 2 seconds after that it apes around the room,
 * immediately following that it gets a banana
 * and 3 seconds after that it gets an apple.
 */
int delay_command( string words, int interval ) {
    if ( this_object()->query_property( PASSED_OUT_PROP ) ) {
        return -1;
    }
    if ( stringp( words ) ) {
      if (!sizeof(_queued_commands)) {
        call_out( (: _next_queued_command :), interval );
      }
      _queued_commands = _queued_commands + ({ interval, words });
      return 1;
    }
    printf( "Invalid parameter to delay_command: %O for monster %O in %O.\n",
      words, this_object(), environment() );
    return -1;
} /* delay_command() */

/**
 * This method allows you to control the npc and get it to do
 * actions where they are queued as for players.  If there are no
 * commands pending the command is executed immediately.  This function
 * is 100% compatible with delay_command() and init_command().
 * @param words the action to perform
 * @param interval to wait before processing another command.
 * If omitted defaults to 2 seconds as per players
 * @see delay_command()
 * @see query_queued_commands()
 * @see init_command()
 * @see do_command()
 * @example
 * ob = clone_object(NICE_HAIRY_APE);
 * ob->queue_command("'I am a hairy ape!");
 * ob->queue_command("emote apes around the room.",5);
 * ob->queue_command("get banana",10);
 * ob->delay_command("emote get apple.",3);
 * Right away it says "I am a hairy ape",
 * 2 seconds later it apes around the room,
 * 5 seconds after that it gets a banana
 * and 13 seconds (10+3) after that it gets an apple.
 */
varargs int queue_command( string words, int interval ) {
    if ( this_object()->query_property( PASSED_OUT_PROP ) ) {
        return -1;
    }
    if ( stringp( words ) ) {
      if (undefinedp(interval)) interval=2;
      if (!sizeof(_queued_commands)) {
        _queued_commands = ({ words, interval });
        _next_queued_command();
        return 1;
      }
      _queued_commands = _queued_commands + ({ words, interval });
      return 1;
    }
    printf( "Invalid parameter to queue_command: %O for monster %O in %O.\n",
      words, this_object(), environment() );
    return -1;
} /* queue_command() */

/**
 * This method allows you submit delayed commands to the npc
 * via a call_out.
 * @see do_command()
 * @see queue_command()
 * @see delay_command()
 */
varargs void init_command(string str, int tim) {
    call_out("do_command", tim, str);
} /* init_command() */

/** @ignore yes */
void init() {
  set_heart_beat( 1 );
  if (environment() && aggressive && this_player() &&
      file_name( environment() )[1..4] != "room" &&
      !environment()->no_attack() &&
      this_player()->query_visible(this_object()))
    start_attack(this_player());
}

/**
 * This method check to see if the npc should start attacking someone
 * when they enter the npcs environment.  It is called from inside
 * init().  It will only attack if the agressive is set and the
 * person is visible to be attacked.  The property
 * <pre>"no attack"</pre> can be set on the npc (or player) to
 * stop them being attacked.
 * @param who the person to potentially start attacking
 * @see set_aggressive()
 */
void start_attack( object who ) {
  if(!who || !aggressive ) {
    return;
  }

  if ( !who->query_visible( this_object() ) ||
       who->query_auto_loading() ||
       file_name(who) == DEATH ||
       who->query_property( "guest" ) ||
       ( userp( who ) && !interactive( who ) ) ||
       who->query_property( "no attack" ) ) {
    return;
  }

  /*
   * This uses a call_other() just in case there are shadows.
   */
  if(((aggressive > 1) || interactive(who)) &&
     (!interactive(who) || !who->query_auto_loading()))
    this_object()->attack_ob( who );
}

/** @ignore yes */
varargs int adjust_hp( int number, object attacker, object weapon,
                         string attack ) {
    set_heart_beat( 1 );
    return ::adjust_hp( number, attacker, weapon, attack );
} /* adjust_hp() */

/** @ignore yes */
int adjust_gp( int number ) {
    set_heart_beat( 1 );
    return ::adjust_gp( number );
} /* adjust_gp() */

/**
 * This method checks to see if there are any players in the environment
 * of the npc.  This should be used to determine when chats should
 * be turned off and other things which should only work in the
 * presence of players.
 * @return 1 if there is a player in the room, 0 otherwise
 */
int check_anyone_here() {
    object thing;

    if ( !environment() ) {
        return 0;
    }
    if ( file_name( environment() )[1..4] == "room" ) {
        return 0;
    }
    if ( environment()->query_linked() ) {
        return 1;
    }
    foreach( thing in all_inventory( environment() ) ) {
        if ( interactive( thing ) || thing->query_slave() ) {
            return 1;
        }
    }
    return 0;
} /* check_anyone_here() */

/**
 * This method is used to determine when to throw people out of a
 * room.  This is what detritus uses to throw people out of the
 * mended drum when it gets a bit rowdy.
 * <p>
 * The hps is the level of hps at which the npc will start throwing
 * people out with the chance of it occuring (chance is a percentage).
 * <p>
 * People will be thrown into a random room, if the property
 * <pre>"no throw out"</pre> is specified on the room then they
 * will not be thrown into that room.
 * @param hps the number of hps at which to start throwing people out
 * @param chance the percentage chance of being thrown out
 * @param their_mess the message to show them
 * @param everyone_mess the message to show everyone else
 * @see set_aggressive()
 * @see query_throw_out()
 * @see set_join_fights()
 * @see expand_string()
 */
void set_throw_out( int hps, int chance, string their_mess,
  string everyone_mess ) {
    throw_out = ({ hps, chance, their_mess, everyone_mess });
} /* set_throw_out() */

/**
 * This method returns the current throw out array.
 * The array consists of
 * <pre>({
 *   hps,
 *   chance,
 *   their_mess,
 *   everyone_mess
 * })
 * </pre>The parameters are the same as used in the set_throw_out
 * function.
 * @see set_throw_out()
 * @return the throw out array
 */
mixed *query_throw_out() { return throw_out; }

/**
 * This method is used to make the npc run away.  This will be
 * called by the combat code for wimpy when the npc is bellow the
 * number of points used to trigger the wimpy action.
 * @return 1 if successfuly ran away
 */
int run_away() {
    if ( query_property( "run away" ) == -1 ) {
        return 0;
    }
    // Just to make npcs a bit trickier...
    do_command("lose all");
    become_flummoxed();
    if ( sizeof( following_route ) ) {
        do_route_move();
        return 1;
    }
    if ( query_property( "run away" ) ) {
        this_object()->do_move_after( 1 );
        return 1;
    }
    return ::run_away();
} /* run_away() */

/**
 * This method is used to expand the message strings used in the
 * npc messages.  It is used for chat strings and such things like
 * that.  The strings it expands are of the form:<br>
 * $lname$, $mname$, $aname$, ...<br>
 * The first letter determines the type of object being referenced.
 * They are:
 * <dl>
 * <dt>m
 * <dd>Me!  The npc itself.
 * <dt>l
 * <dt>A living object, choose a random living object in the npcs
 *     environment.
 * <dt>a
 * <dd> Chooses a random attacker from those attacking the npc.
 * <dt>o
 * <dd>Choose a random object in the inventory of the npc.
 * </dl>
 * After the first letter is a type of information being request.
 * <dl>
 * <dt>name
 * <dd>The name of the selected object.
 * <dt>cname
 * <dd>The capitalised name of the selected object.
 * <dt>gender
 * <dd>The gender string of the selected object (male, female, neuter).
 * <dt>poss
 * <dd>The possessive string of the selected object.
 * <dt>obj
 * <dd>The objective string of the selected object.
 * <dt>pronoun
 * <dd>The pronoun string of the selected object.
 * <dt>gtitle
 * <dd>The guild title of the selected object (only useful on livings).
 * <dt>ashort
 * <dd>The a_short() call.
 * <dt>possshort
 * <dd>The poss_short() call.
 * <dt>theshort
 * <dd>The the_short() call.
 * <dt>oneshort
 * <dd>The one_short() call.
 * </dl>
 * @see set_chat_string()
 * @see expand_mon_string()
 * @param in_str the input string
 * @param on the object to use for the 'o' matching
 */
string expand_string(string in_str, object on) {
    string *str, ret;
    int i, add_dollar;
    object liv, *obs, ob;

    in_str = "/global/events"->convert_message( in_str );
    str = explode(in_str, "$");
    ret = "";
    for ( i = 0; i < sizeof( str ); i++ ) {
        if ( i % 2 == 0 ) {
            if (add_dollar) {
                ret += "$";
            }
            ret += str[i];
            add_dollar = 1;
            ob = 0;
        } else switch (str[i][0]) {
        case 'm' :
            ob = this_object();
        case 'l' :
          if(!ob) {
            if(!liv) { /* Changed to not loop badly up when no environment */
              obs = all_inventory(environment()) - ({ this_object() });
              obs = filter_array(obs, (: living($1) &&
                                       $1->query_visible(this_object()) :));
              if (sizeof(obs))
                liv = obs[random(sizeof(obs))];
            }
            if (!liv) { /* No one here to see us blow up anyway ;) */
              break;
            }
            ob = liv;
          }
        case 'a' :
            if ( !ob ) {
                obs = (object *)this_object()->query_attacker_list();
                if ( !sizeof( obs ) )
                    break;
                ob = obs[ random( sizeof( obs ) ) ];
            }
        case 'o' :
            if (!ob) {
                if (!on) {
                    obs = all_inventory(environment());
                    obs = filter_array( obs, (: !living( $1 ) :) );
                    if (sizeof(obs)) {
                        on = obs[random(sizeof(obs))];
                    }
                }
                ob = on;
            }
            switch (str[ i ][ 1 .. ]) {
            case "theshort" :
                ret += (string)ob->the_short();
                add_dollar = 0;
                break;
            case "ashort" :
                ret += (string)ob->a_short();
                add_dollar = 0;
                break;
            case "oneshort":
                ret += (string)ob->one_short();
                add_dollar = 0;
                break;
            case "possshort" :
                ret += (string)ob->poss_short();
                add_dollar = 0;
                break;
            case "name" :
                ret += (string)ob->query_name();
                add_dollar = 0;
                break;
            case "cname" :
                ret += (string)ob->query_cap_name();
                add_dollar = 0;
                break;
            case "gender" :
                ret += (string)ob->query_gender_string();
                add_dollar = 0;
                break;
            case "poss" :
                ret += (string)ob->query_possessive();
                add_dollar = 0;
                break;
            case "obj" :
                ret += (string)ob->query_objective();
                add_dollar = 0;
                break;
            case "gtitle" :
                ret += (string)ob->query_gender_title();
                add_dollar = 0;
                break;
            case "pronoun" :
                ret += (string)ob->query_pronoun();
                add_dollar = 0;
                break;
            default :
                if (add_dollar) {
                    ret += "$";
                }
                ret += str[i];
                add_dollar = 1;
                break;
            }
            ob = 0;
            break;
        default :
            if (add_dollar) {
                ret += "$";
            }
            ret += str[i];
            add_dollar = 1;
            ob = 0;
            break;
        }
    }
    if (strlen(ret) && ret[strlen(ret)-1] == '$') {
        return ret[0..strlen(ret)-2];
    }
    return ret;
} /* expand_string() */

/**
 * This method executes the string passed in.  It handles all the
 * stuff which is needed from the chat_string stuff.
 *
 * If the input is a function pointer then it is evaluated with one
 * parameter, being the npc.
 *
 * If the input is a string then the first letter determines what will
 * be done with it.  All these are passed through expand_string
 * so that exciting things can be done.
 * <ul>
 * <li># - A call_other will be generated.  The parameters are separated by
 * ':'s, so "#frog:bing:fred:chicken" would call<pre>
 * this_object()->bing("fred", "chicken");
 * </pre>.
 * <ul>', ", : - These will generate a 'say', 'lsay' or 'emote'.
 * <ul>@ - This will run the passed in command.  Eg: "@frog" would cause the
 *     soul command frog to be used.
 * <ul>Anything else will be used as a message to be sent to everyone in the
 *     room.
 * </ul>
 * @param str the thing to execute
 * @see expand_string()
 */
void expand_mon_string( mixed str ) {
    string *args;

    if ( functionp( str ) ) {
        evaluate( str, this_object() );
    } else {
        if( !stringp( str ) && environment( this_object() ) ) {
            tell_room(environment(this_object()),
              "%^RED%^"+ this_object()->the_short()+
              " says: please bugreport me, I have a bad load_chat.%^RESET%^\n",
              ({ }) );
        }
        switch ( str[ 0 ] ) {
        case '#' :
            args = explode(str[ 1..], ":");
            switch (sizeof(args)) {
            case 1 :
                call_other( this_object(), args[0] );
                break;
            case 2 :
                call_other( this_object(), args[0], args[1] );
                break;
            case 3 :
                call_other( this_object(), args[0], args[1], args[2] );
                break;
            case 4 :
                call_other( this_object(), args[0], args[1], args[2],
                  args[3] );
                break;
            default :
                call_other( this_object(), args[0], args[1], args[2],
                  args[3], args[4] );
                break;
            }
            break;
        case ':' :
        case '\'' :
        case '"' :
            init_command( expand_string( str, 0 ), 1 );
            break;
        case '@' :
            init_command( expand_string( str[ 1 .. ], 0 ), 1 );
            break;
        default :
            tell_room( environment(), expand_string( str, 0 ) +"\n" );
        }
    }
} /* expand_mon_string() */

/**
 * This method returns 1 if it is ok to turn of the npc's heart beat.
 * THis can be overridden for times when the heart beat needs to be
 * kept on for some reason.
 * @return 1 if the heart beat should go off, 0 if it should stay on
 */
int query_ok_turn_off_heart_beat() {
   return 1;
} /* query_ok_turn_off_heart_beat() */

/** @ignore yes */
void heart_beat() {
  int i, j;
  
  if(base_name(environment()) == "/room/rubbish") {
    set_heart_beat(0);
    return;
  }
  
  ::heart_beat();
  RACE_OB->monster_heart_beat( race, guild, race_ob, guild_ob );
  
  if ( ( hp == max_hp ) && ( gp == max_gp ) ) {
    if (query_ok_turn_off_heart_beat()) {
      if ( !check_anyone_here() ||
           ( !sizeof( achat_string ) && !sizeof( chat_string ) ) ) {
        set_heart_beat( 0 );
        return;
      }
    }
  }
  
  /* These may be obsolete. */
  remove_property( "done follow" );
  this_object()->do_spell_effects( 0 );
  
  if ( check_anyone_here() ) {
    if ( this_object()->query_fighting() ) {
      if ( sizeof( doing_story[ 1 ] ) ) {
        if( !intp(doing_story[ 1 ][ 0 ] ) ) {
          expand_mon_string( doing_story[ 1 ][ 0 ] );
          doing_story[ 1 ] = doing_story[ 1 ][ 1 .. ];
        } else if( random( 1000 ) < doing_story[ 1 ] [ 0 ]) {
          expand_mon_string( doing_story[ 1 ][ 1 ] );
          if ( sizeof( doing_story[ 1 ] ) == 2 ) {
            doing_story[ 1 ] = ({});
          } else {
            doing_story[ 1 ] = ({ doing_story[ 1 ] [ 0 ] }) +
              doing_story[ 1 ][ 2 .. ];
          }
        }
      } else if ( ( random( 1000 ) < achat_chance )
                  && sizeof( achat_string[ 1 ] ) ) {
        i = random( achat_string[ 0 ] + 1 );
        while ( ( i -= achat_string[ 1 ][ j ] ) > 0 )
          j += 2;
        if ( pointerp( achat_string[ 1 ][ j + 1 ] ) ) {
          if( intp(achat_string[ 1 ][ j + 1 ][ 0 ]) ) {
            if( random(1000) < achat_string[ 1 ][ j + 1 ][ 0 ] ) {
              expand_mon_string( achat_string[ 1 ][ j + 1 ][ 1 ] );
              doing_story[ 1 ] = ({ achat_string[ 1 ][ j + 1 ][ 0 ] })+
                achat_string[ 1 ][ j + 1 ][ 2 .. ];
            } else {
              doing_story[ 1 ] = achat_string[ 1 ][ j + 1 ];
            }
          } else {
            expand_mon_string( achat_string[ 1 ][ j + 1 ][ 0 ] );
            doing_story[ 1 ] = achat_string[ 1 ][ j + 1 ][ 1 .. ];
          }
        } else
          expand_mon_string( achat_string[ 1 ][ j + 1 ] );
      }
    } else {
      if ( sizeof( doing_story[ 0 ] ) ) {
        if( !intp( doing_story[ 0 ][ 0 ] ) ) {
          expand_mon_string( doing_story[ 0 ][ 0 ] );
          doing_story[ 0 ] = doing_story[ 0 ][ 1 .. ];
        } else if( random( 1000 ) < doing_story[ 0 ][ 0 ]) {
          expand_mon_string( doing_story[ 0 ][ 1 ] );
          if ( sizeof( doing_story[ 0 ] ) == 2 ) {
            doing_story[ 0 ] = ({});
          } else {
            doing_story[ 0 ] = ({ doing_story[ 0 ] [ 0 ] }) +
              doing_story[ 0 ][ 2 .. ];
          }
        }
      } else if ( ( random( 1000 ) < chat_chance )
                  && sizeof( chat_string[ 1 ] ) ) {
        i = random( chat_string[ 0 ] + 1 );
        while ( ( i -= chat_string[ 1 ][ j ] ) > 0 )
          j += 2;
        if ( pointerp( chat_string[ 1 ][ j + 1 ] ) ) {
          if( intp( chat_string[ 1 ][ j + 1 ][ 0 ]) ) {
            if(random( 1000) < chat_string[ 1 ][ j + 1 ][ 0 ]) {
              expand_mon_string( chat_string[ 1 ][ j + 1 ][ 1 ] );
              doing_story[ 0 ] = ({ chat_string[ 1 ][ j + 1 ][ 0 ] }) +
                chat_string[ 1 ][ j + 1 ][ 2 .. ];
            } else {
              doing_story[ 0 ] = chat_string[ 1 ][ j + 1 ];
            }
          } else {
            expand_mon_string( chat_string[ 1 ][ j + 1 ][ 0 ] );
            doing_story[ 0 ] = chat_string[ 1 ][ j + 1 ][ 1 .. ];
          }
        } else
          expand_mon_string( chat_string[ 1 ][ j + 1 ] );
      }
    }
  }
} /* heart_beat() */

/** @ignore yes */
int clean_up( int parent ) {
  if(query_property("unique") || check_anyone_here())
    return 1;

  if(parent) {
    log_file("CLEANUP", "%s %s cleaned up.\n", ctime(time()),
             file_name(this_object()));
  }
  move( "/room/rubbish" );
  return 1;
}

/**
 * This method generates a random number.  It used to setup the random
 * stats.
 * @param no the number of times to roll the dice
 * @param type the size of the dice
 * @see set_random_stats()
 * @return the randomly generate number
 */
int rand_num(int no, int type) {
    int i, val;

    for (i=0;i<no;i++)
        val = random(type)+1;
    return val;
} /* rand_num() */

/**
 * This method sets the stats for the npc to some exciting random
 * values.
 * @param no the number of times to roll the dice
 * @param type the size of the dice
 * @see rand_num()
 */
void set_random_stats(int no, int type) {
    set_str(rand_num(no, type));
    set_dex(rand_num(no, type));
    set_int(rand_num(no, type));
    set_con(rand_num(no, type));
    set_wis(rand_num(no, type));
} /* set_random_stats() */

/**
 * This method sets up the basic abilities and race of the critter.  It
 * is equivalent to calling set_race(), set_guild(), and then set_level()
 * with the same parameters.  But those latter three functions are
 * deprecated and shouldn't be used.
 * @see set_race()
 * @see set_guild()
 * @see set_level()
 * @param race this is the race of the character.  It should be a
 * race that's understood by the /std/race.c
 * @param guild this is the guild, class, or profession of the NPC.
 * @param level this is the base skill level of the NPC.  The
 * number is used by the race object to set ability scores, and
 * base skills.
 */
void basic_setup(string race, string guild, int level)
{
    this_object()->set_race(race);
    this_object()->set_guild(guild);
    this_object()->set_level(level);
}

/**
 * This function is deprecated.  Use basic_setup() instead.
 *
 * This method sets the level of the npc.  This should only be called
 * *after* the race and guild are set.  If this is called before that
 * the results will be unexpected.
 * @see set_race()
 * @see set_guild()
 * @see basic_setup()
 * @param i the level to set the npc to
 */
void set_level( int i ) { RACE_OB->set_level( i, race, guild ); }

/**
 * This method is used to give some startup money to the npc.
 * @param base the base amount of money to give (fixed)
 * @param rand the random amount of money to give
 * @param type the type of money to give (default: "copper")
 * @example
 * ob = clone_object(CLUCKY_CHICKEN);
 * // This will give the chicken 10 + random(10) royals.
 * // It is a royal chicken
 * ob->give_money(10, 10, "royal");
 * @return the return value of adjust_money()
 * @see /std/living/money.c
 */
int give_money(int base, int rand, string type) {
    if (!type)
        type = "copper";
    return adjust_money(base+random(rand), type);
} /* give_money() */

/**
 * This method loads up the chat strings for the npc.  This will be
 * an array containing pairs of elements, the first pair is the
 * weighting of the chat and the second is the chat to use.
 * <p>
 * All the weights in the array are added up and then a random
 * number is chosen in the weighting.  Then that element is looked
 * up in the array.  This way you can control a chat and make it
 * rare.
 * <p>
 * If the chat string is an array then this a story, the story will be
 * executed one after another and no other chats will be executed
 * in between.  If the first parameter of the story array is a number it
 * will be used as a 1/1000 chance of the next story line being displayed. 
 * Special strings can be used which will replace with
 * object names, see expand_mon_string() for further information.
 * <p>
 * The chat chance is a chance (in 1000) of the chat occuring.  You
 * will need to play with this yourself to see which frequency of
 * chatting you wish for your npcs.
 *
 * @example
 * load_chat(60,
 *    ({
 *       1, "'I am a chicken!",
 *       // Make this one more likely to occur.
 *       2, ":clucks like a chicken."
 *       1, ":pecks at $lpossshort$ foot."
 *     }));
 * @example
 * load_chat(100,
 *    ({
 *       1, "'I am a simple farmer."
 *       1, ":waves $mposs$ pitchfork around."
 *       // A story, they will always occur in this order
 *       1, ({
 *           500,
 *            "'Once upon a time there was a rabbit.",
 *            "'It was a nice rabbit and hung around in bars.",
 *            "'It sung little songs about fruit.",
 *           }),
 *      }));
 * @see expand_mon_string()
 * @param chance the chance in 1000 of a chat working every 2 seconds
 * @param c_s the chat string to use
 * @see load_a_chat()
 * @see query_chat_chance()
 * @see query_chat_string()
 */
void load_chat(int chance, mixed *c_s) {
    int i;
    chat_string = ({ 0, ({ }) });
    for (i=0;i<sizeof(c_s);i+=2) {
        chat_string[1] += ({ c_s[i], c_s[i+1] });
        chat_string[0] += c_s[i];
    }
    chat_chance = chance;
} /* load_chat() */

/**
 * This method sets the current chat chance for messages on the
 * npc.
 * @param i the chat chance
 * @see load_chat()
 * @see query_chat_chance()
 */
void set_chat_chance(int i) { chat_chance = i; }
/**
 * This method returns the current chat chance for messages on
 * the npc
 * @return the current chat chance
 * @see set_chat_chance()
 * @see load_chat()
 */
int query_chat_chance() { return chat_chance; }
/**
 * This method sets the current chat string for messages on the
 * npc.  See load_chat() for a longer description of how the
 * chat string is formatted.
 * @param chat the new chat strings.
 * @see load_chat()
 * @see query_chat_string()
 */
void set_chat_string(string *chat) { chat_string = chat; }
/**
 * This method queries the current chat string for messages on the
 * npc.  See load_chat() for a longer description of how the
 * chat string is formatted.
 * @return the current chat string
 * @see load_chat()
 * @see query_chat_string()
 */
string *query_chat_string() { return chat_string; }

/**
 * This method adds a single chat string into the current list of
 * chat strings.  See load_chat() for a longer description of
 * the chat string.
 * @param weight the weight of the chat
 * @param chat the new chat string
 * @see load_chat()
 * @see remove_chat_string()
 */
void add_chat_string(mixed weight, mixed chat) {
    int i;

    if (pointerp(weight)) {
        for (i=0;i<sizeof(weight);i+=2) {
            add_chat_string(weight[i], weight[i+1]);
        }
    } else {
        if (member_array(chat, chat_string[1]) == -1) {
            chat_string[1] += ({ weight, chat });
            chat_string[0] += weight;
        }
    }
} /* add_chat_string() */

/**
 * This method attempts to remove the given chat string from the
 * current list of chat strings.  The chat message is checked
 * to see if it exists in the array, the weighting of the
 * string is ignored.
 * @param chat the chat string to remove
 * @see add_chat_string()
 * @see load_chat()
 */
void remove_chat_string(mixed chat) {
    int i;

    if (pointerp(chat)) {
        for (i=0;i<sizeof(chat);i++) {
            remove_chat_string(chat[i]);
        }
    } else {
        if ((i = member_array(chat, chat_string[1])) != -1) {
            chat_string[0] -= chat_string[1][i-1];
            chat_string[1] = delete(chat_string[1], i-1, 2);
        }
    }
}  /* remove_chat_string() */

/**
 * This method loads up the set of chat strings to use while in combat.
 * @param chance the chance of the chat occuring
 * @param c_s the chat string to use
 * @see load_chat()
 * @see query_achat_chance()
 * @see query_achat_string()
 */
void load_a_chat(int chance, mixed *c_s) {
    int i;

    achat_string = ({ 0, ({ }) });
    for (i=0;i<sizeof(c_s);i+=2) {
        achat_string[1] += ({ c_s[i], c_s[i+1] });
        achat_string[0] += c_s[i];
    }
    achat_chance = chance;
} /* load_a_chat() */

/**
 * This method sets the current chat chance for attack messages on the
 * npc.
 * @param i the attack message chat chance
 * @see load_chat()
 * @see load_achat()
 * @see query_achat_chance()
 */
void set_achat_chance(int i) { achat_chance = i; }
/**
 * This method returns the current chat chance for attack messages on
 * the npc.
 * @return the current attack message chat chance
 * @see set_achat_chance()
 * @see load_chat()
 * @see load_achat()
 */
int query_achat_chance() { return achat_chance; }
/**
 * This method sets the current chat string for attack messages on the
 * npc.  See load_chat() for a longer description of how the
 * chat string is formatted.
 * @param chat the new chat attack message strings.
 * @see load_chat()
 * @see load_achat()
 * @see query_achat_string()
 */
void set_achat_string(string *chat) { achat_string = chat; }
/**
 * This method queries the current chat string for attack messages on the
 * npc.  See load_chat() for a longer description of how the
 * chat string is formatted.
 * @return the current attack message chat string
 * @see load_chat()
 * @see load_achat()
 * @see set_chat_string()
 */
string *query_achat_string() { return achat_string; }
/**
 * This method adds a single chat string into the current list of
 * attack message chat strings.  See load_chat() for a longer description of
 * the chat string.
 * @param weight the weight of the chat
 * @param chat the new chat string
 * @see load_chat()
 * @see load_achat()
 * @see remove_achat_string()
 */
void add_achat_string(mixed weight, mixed chat) {
    int i;

    if (pointerp(weight))
        for (i=0;i<sizeof(weight);i+=2)
            add_achat_string(weight[i], weight[i+1]);
    else
    if (member_array(chat, achat_string[1]) == -1) {
        achat_string[1] += ({ chat });
        achat_string[0] += weight;
    }
} /* add_achat_string() */

/**
 * This method attempts to remove the given chat string from the
 * current list of attack message chat strings.  The chat message is checked
 * to see if it exists in the array, the weighting of the
 * string is ignored.
 * @param chat the chat string to remove
 * @see add_achat_string()
 * @see load_chat()
 * @see load_achat()
 */
void remove_achat_string(mixed chat) {
    int i;

    if (pointerp(chat))
        for (i=0;i<sizeof(chat);i++)
            remove_achat_string(chat[i]);
    else
    if ((i = member_array(chat, achat_string[1])) != -1) {
        achat_string[0] -= achat_string[1][i-1];
        achat_string[1] = delete(achat_string[1], i-1, 1);
    }
} /* remove_achat_string() */

/**
 * This method adds a move zone onto the npc.  The move zones control
 * which areas the npcs will wander into, a move zone is set on the
 * room and the npcs will only enter rooms which have a matching
 * move zone.  If there is no move zone, then the npc will enter
 * any room.
 * <p>
 * If the parameter is an array each of the elements of the array
 * will be added as a move zone.
 * @param zone the zone(s) to add
 * @see remove_move_zone()
 * @see query_move_zones()
 * @see set_move_after()
 */
void add_move_zone(mixed zone) {
    int i;

    if (pointerp(zone))
        for (i=0;i<sizeof(zone);i++)
            add_move_zone(zone[i]);
    else if (member_array(zone, move_zones) != -1)
        return;
    else
        move_zones += ({ zone });
} /* add_move_zone() */

/** @ignore why is this here? */
void set_move_zones(string *zones) {
    int i;

    for (i=0;i<sizeof(zones);i++)
        add_move_zone(zones[i]);
} /* set_move_zones() */

/**
 * This method removes a move zone from the npc.
 * @param zone the zone to remove
 * @see add_move_zone()
 * @see query_move_zones()
 * @see set_move_after()
 */
void remove_move_zone(mixed zone) {
    int i;

    if (pointerp(zone))
        for (i=0;i<sizeof(zone);i++)
            remove_move_zone(zone[i]);
    else {
        if ((i=member_array(zone, move_zones)) == -1)
            return ;
        move_zones = delete(move_zones, i, 1);
    }
} /* remove_move_zone() */

/**
 * This method returns the current list of move zones on the npc
 * @return the current list of move zones
 * @see add_move_zone()
 * @see remove_move_zones()
 * @see set_move_after()
 */
string *query_move_zones() { return move_zones; }

/**
 * This method sets the speed at which the npc will randomly
 * wander around.  The npc will wander at the speed:<pre>
 * speed = after + random(rand)
 * </pre>This is called every time the npc sets up for its next move.
 * <p>
 * The move zones control
 * which areas the npcs will wander into, a move zone is set on the
 * room and the npcs will only enter rooms which have a matching
 * move zone.  If there is no move zone, then the npc will enter
 * any room.
 * @param after the fixed amount of time
 * @param rand the random amount of time
 * @see remove_move_zone()
 * @see query_move_zones()
 * @see add_move_zone()
 */
void set_move_after(int after, int rand) {
    _move_after = ({ after, rand });
    if (after != 0 && rand != 0) {
        do_move_after(0);
    }
} /* set_move_after() */

/**
 * This method returns the current move after values.
 * It returns an array of the form:<pre>
 *    ({
 *      after,
 *      rand,
 *     })
 * </pre>
 * @return the move after values
 * @see set_move_after()
 */
mixed query_move_after() {
   return copy( _move_after );
} /* query_move_after() */


/**
 * This method adds a command to be called whenever the npc enters
 * a room.  If the command is a string, then it will be executed
 * as if they had typed it.  If it is a function then the function
 * will be evaluated and one argument (the npc itself) will be passed
 * in.
 * @param str the enter commands to add
 * @see reset_enter_commands()
 * @see query_enter_commands()
 */
int add_enter_commands(mixed str) {
    if (stringp(str)) {
        enter_commands += ({ str });
    } else if (pointerp(str)) {
        enter_commands += str;
    } else if (functionp(str)) {
        enter_commands += ({ str });
    }
    return 1;
} /* add_enter_commands() */

/**
 * This method returns the current array of enter commands.
 * @return the current array of enter commands
 * @see reset_enter_commands()
 * @see add_enter_commands()
 */
string *query_enter_commands() { return enter_commands; }

/**
 * This method resets the array of enter commands back to nothing.
 * @see add_enter_commands()
 * @see add_enter_commands()
 */
void reset_enter_commands() { enter_commands = ({ }); }

/** @ignore yes */
varargs int move( mixed dest, string messin, string messout ) {
    int result;
    object before;
    before = environment();
    result = living::move( dest, messin, messout );
    if ( result == MOVE_OK ) {
        me_moveing( before );
        if ( virtual_move )
            true_location = file_name( environment() );
    }
    return result;
} /* move() */

/** @ignore yes */
void room_look() {
    int i;

    ::room_look();
    for ( i = 0; i < sizeof( enter_commands ); i++ ) {
        if ( functionp( enter_commands[ i ] ) ) {
            call_out( enter_commands[ i ], 2 * i + 1, this_object() );
            continue;
        }
        if ( enter_commands[ i ][ 0 .. 0 ] == "#" )
            call_out( enter_commands[ i ][ 1 .. 99 ], 2 * i + 1 );
        else
            init_command( enter_commands[ i ], 2 * i + 1 );
    }
} /* room_look() */

/**
 * This is called when the npc decides it must continue down
 * a certain route.  This will be called by the wander handler
 * and can be used to force the npc to wander along a route
 * faster.
 * @param running_away this is 1 if the npc is running away
 * @see set_move_after()
 */
void do_move_after (int running_away ) {
    if (sizeof(following_route)) {
        do_route_move();
    } else {
        WANDER_HANDLER->move_after( running_away );
    }
} /* do_move_after() */

/**
 * This event is called when a fight is in progress.  It will
 * be used for things like joining into currently running
 * fights and initiating combat with spell casters.
 * @param me the person initiating the attack
 * @param him the person being attacked
 */
void event_fight_in_progress(object me, object him) {
  mixed action;
  int i;

  // Run the combat actions.
  if(sizeof(this_object()->query_attacker_list())) {
    // Do combat actions.
    for(i=0; i<sizeof(_combat_actions); i+= 3) {
      if(_combat_actions[i] > random(100)) {
        action = _combat_actions[i+2];
        if(stringp(action))
          this_object()->do_command(action);
        else if(functionp(action))
          evaluate(action, me, him);
        else if(pointerp(action) && sizeof(action) == 1 && stringp(action[0]))
          call_other(this_object(), action[0], me, him);
        else if(pointerp(action) && sizeof(action) == 2)
          call_other(action[0], action[1], this_object(), me, him);
      }
    }
  }

  if(him == this_object() && this_object()->query_property("see_caster") &&
     !userp(me) &&
     (random(him->query_property("see_caster"))) < (him->query_int()))
    this_object()->attack_ob(me->query_owner());
  
  if(!join_fight_mess || !me || !him)
    return;

  if(him == this_object())
    return;

  /* only attack npcs if fight_type is not 0. */
  if(!join_fight_type && !interactive(him))
    return;

  if(member_array(him, (object *)this_object()->query_attacker_list()) == -1) {
    if(!him->query_property("no attack")) {
      if(join_fight_mess)
        expand_mon_string(join_fight_mess);
      this_object()->attack_ob(him);
    }
  }
} /* event_fight_in_progress() */

/**
 * This method sets the message to use when joining into fights.
 * @param str the message to print when joining a fight
 * @see query_join_fights()
 * @see set_join_fight_type()
 */
void set_join_fights(string str) { join_fight_mess = str; }
/**
 * This method returns the message to use when joining into fights.
 * @return the message to print when joining a fight
 * @see set_join_fights()
 * @see set_join_fight_type()
 */
string query_join_fights() { return join_fight_mess; }

/**
 * This method sets the flag which allows the npc to join into fights.
 * If this is set to a non-zero value then the npc will join into
 * fights in progress using the fight joining message.
 * @param i 1 if the npc is to join fights, 0 if not
 * @see set_join_fights()
 * @see query_join_fight_type()
 */
void set_join_fight_type(int i) { join_fight_type = i; }
/**
 * This method returns the flag which allows the npc to join into fights.
 * @return 1 if the npc is to join fights, 0 if not
 * @see set_join_fights()
 * @see query_join_fight_type()
 */
int query_fight_type() { return join_fight_type; }

/** @ignore yes */
void event_exit( object me, string mess, object to ) {
  mixed *bing;
  int i, j, k;
  string *zones, fname;
  object *attacker_list, ob;
  
  events::event_exit( me, mess, to );
  living::event_exit( me, mess, to );

  if (!_move_after || sizeof(following_route)) /* we dont move :( */
    return ;
  
  /* If we're not going anywhere... */ 
  if ( !to || !me->query_visible(this_object()))
    return; 

  /* follow the suckers. */
  attacker_list = this_object()->query_attacker_list();
  
  if(attacker_list && sizeof(attacker_list)) {
    // If we're not fighting them then don't follow them.
    if (member_array(me, attacker_list) == -1)
      return;
    
    // Remove us. 
    attacker_list -= ({ me }); 
    
    // If there are people still here who we're fighting then don't
    // follow the person who left.
    foreach(ob in attacker_list) {
      if(ob && environment(ob) == environment())
        return;
    }
  } else { 
    /* Not being attacked, let's not follow. */ 
    return;
  }
    
  bing = (mixed *)environment()->query_dest_dir( this_object() );

  if( !bing ) {
    return ; /* lost cause */
  }

  fname = file_name( to );
    
  /* Check to see they're going a valid direction for this room. */ 
  
  if ((i = member_array(fname, bing)) == -1) {
    return ; /* lost cause */
  }
  if (!this_object()->query_property("unrestricted follow")) {
    zones = (string *)to->query_zones();
    if (move_zones && (j = sizeof(move_zones))) {
      while (j--) {
        if (member_array(move_zones[j], zones) != -1) {
          k = 1; /* If we are suppose to wander in move_zones.. dont go */
          break;  /* where we are not supposed to... get stuck */
        }
      }
      if (!k)
        return;
    } else
      return;
  }
  remove_call_out("do_follow_move");
  call_out("do_follow_move", 4 + random(follow_speed), bing[i-1]);
}

/**
 * This method is used to make the npcs follow after attackers when they
 * leave the room.
 * @param dir the dirction to follow them in
 */
void do_follow_move(string dir) {
  if (sizeof(following_route))
    return ;
  // NPCs are following & attacking instantly since they don't have
  // time_left. So, use up actions instead.
  this_object()->adjust_action_defecit((ACTIONS_PER_HB / (COMBAT_SPEED + 1)));
  do_command(dir);
}

/**
 * This method adds a combat action to the npc.  This is an action which
 * has a chance of occuring during combat.  The name is an identifier
 * which can be used to remove the action with later.  The action
 * itself can be a string, then that command will be executed.  If
 * the action is a function pointer then it will be evaluated with
 * two arguments, the first being the attacker, the second being the
 * target.
 * <p>
 * If the action is an array, if it is one element then the function
 * specified will be called on the attacked with the same arguements
 * as above.  If the size of the array is two then the function will
 * be called on the specified object with the arguments as above.
 * @see remove_combat_action()
 * @see query_combat_actions()
 * @see /std/effects/fighting/combat.c
 */
void add_combat_action( int chance, string name, mixed action ) {
    int i;
    i = member_array( name, _combat_actions );
    if ( i == -1 ) {
        _combat_actions += ({ chance, name, action });
    } else {
        _combat_actions[ i - 1 ] = chance;
        _combat_actions[ i + 1 ] = action;
    }
} /* add_combat_action() */

/**
 * This method will remove the combat action with the specified name.
 * @return 1 if it is found and removed, 0 if not
 * @see add_combat_action()
 * @see query_combat_actions()
 * @see /std/effects/fighting/combat.c
 */
int remove_combat_action( string name ) {
    int i;

    i = member_array( name, _combat_actions );
    if ( i == -1 )
        return 0;
    _combat_actions = delete( _combat_actions, i - 1, 3 );
    return 1;
} /* remove_combat_action() */

/**
 * This method returns the current array of combat actions on the
 * npc.
 * <p>
 * The array will have the format of:
 * <pre>
 *    ({
 *        action1_chance,
 *        action1_name,
 *        action1_action,
 *        ...
 *     })
 * </pre>
 * @return the combat action array
 * @see add_combat_action()
 * @see remove_combat_actions()
 * @see /std/effects/fighting/combat.c
 */
mixed *query_combat_actions() { return copy( _combat_actions ); }

/**
 * This method does a combat action.
 * @param target the target of the action
 * @param thing
 */
void do_combat_action( object player,
                       object target,
                       mixed action ) {
   object place;

   if ( !target ) {
      return;
   }
   place = environment( target );
   if ( place != environment( player ) ) {
      return;
   }
   if ( stringp( action ) ) {
      do_command( action );
      return;
   }
   if (functionp(action)) {
      evaluate(action, player, target);
   }
   if ( pointerp(action) && sizeof(action) == 1 && stringp( action[ 0 ] ) ) {
      call_other( this_object(), action[ 0 ], player, target );
      return;
   }
   if (pointerp(action) && sizeof(action) == 2) {
      call_other( action[ 0 ], action[ 1 ], this_object(), player, target );
   }
} /* do_combat_action() */

/**
 * This is the call back from the combat effect to do something
 * wonderful and wild.
 * @param player the player
 * @param target the target
 */
void combat_actions_call_back( object player, object target ) {
  int i;
  object thing;
  object *things;
  mixed *actions;

  if ( !player || !target ) {
    return;
  }
  thing = environment( player );
  if ( thing != environment( target ) ) {
    return;
  }

  things = filter_array( all_inventory( thing ), (: living( $1 ) ==
                                                  !userp( $1 ) :) );
  foreach ( thing in things ) {
    actions = (mixed *)thing->query_combat_actions();
    for(i=0; i<sizeof(actions); i += 3) {
      if(actions[i] > random(100)) {
        call_out( "do_combat_action", 1, player, target, actions[ i + 2 ] );
      }
    }
  }
} /* combat_actions_call_back() */



/**
 * This method adds an action to the npc that will happen if a specified
 * spell is cast.  This is an action which
 * has a chance of occuring when a spell is being cast.
 * The name is an identifier
 * which can be used to remove the action with later.  The action
 * itself can be a string, then that command will be executed.  If
 * the action is a function pointer then it will be evaluated with
 * two arguments, the first being the caster, the second being the
 * target(s) array and the third being the magic arguments class.
 * <p>
 * If the action is an array, if it is one element then the function
 * specified will be called on the attacked with the same arguements
 * as above.  If the size of the array is two then the function will
 * be called on the specified object with an extra first argument being
 * the npc which the effect is being called from.
 * @param spell_object the spell to respond to
 * @param chance the chance of it working
 * @param name the name of the thing
 * @param action the action to preform
 */
void add_spell_action(string spell_object, int chance,
                     string name, mixed action) {
   if (!_spell_actions[spell_object]) {
      _spell_actions[spell_object] = ([ ]);
   }
   _spell_actions[spell_object][name] = ({ chance, action });
} /* add_spell_action() */

/**
 * This method removes the specified spell action.
 * @param name the name of the spell to remove
 * @return 1 if successful, 0 if not
 */
int remove_spell_action(string name) {
   string spell;
   mapping bits;
   int ret;

   foreach (spell, bits in _spell_actions) {
      if (bits[name]) {
         map_delete(bits, name);
         ret = 1;
      }
   }
   return ret;
} /* remove_spell_action() */

/**
 * This method returns the list of spell actions present on the
 * npc.
 * @return the list of spell actions
 */
mapping query_spell_actions() {
   return _spell_actions;
} /* query_spell_actions() */

/**
 * This method does a combat action.
 * @param caster the caster of the spell
 * @param targets the targets of the spell
 * @param action the action to do
 * @param args the spell arguements
 */
void do_spell_action( object caster,
                      object* targets,
                      mixed action,
                      mixed args ) {
   if ( stringp( action ) ) {
      do_command( action );
      return;
   }
   if (functionp(action)) {
      evaluate(action, caster, targets);
   }
   if ( pointerp(action) && sizeof(action) == 1 && stringp( action[ 0 ] ) ) {
      call_other( this_object(), action[ 0 ], caster, targets, args );
      return;
   }
   if (pointerp(action) && sizeof(action) == 2) {
      call_other( action[ 0 ], action[ 1 ], this_object(), caster, targets,
                  args );
   }
} /* do_spell_action() */


/**
 * This returns the race object associated with the npc.
 * @return the race object associated with the npc
 * @see set_race()
 * @see set_race_ob()
 * @see set_level()
 */
mixed query_race_ob() { return race_ob; }
/**
 * This method sets the race object associated with the npc.
 * This will probably not do what you expect and cannot be used
 * in conjucton with set_level to set the race.  It is called
 * by /std/race.c when set_level() is called.
 * @see set_level()
 * @see set_race()
 * @see /std/race.c
 * @param r the new race object
 */
void set_race_ob(mixed r) { race_ob = r; }

/**
 * This returns the guild object associated with the npc.
 * @return the guild object associated with the npc
 * @see set_guild()
 * @see set_guild_ob()
 * @see set_level()
 */
mixed query_guild_ob() { return guild_ob; }
/**
 * This method sets the guild object associated with the npc.
 * This will probably not do what you expect and cannot be used
 * in conjucton with set_level to set the guild.  It is called
 * by /std/race.c when set_level() is called.
 * @see set_level()
 * @see set_guild()
 * @see /std/race.c
 * @param g the new guild object
 */
void set_guild_ob(mixed g) { guild_ob = g; }

/**
 * This method queries the speed at which the npc will follow
 * after a player when they leave combat.
 * @return the current follow speed
 * @see set_follow_speed()
 */
int query_follow_speed() { return follow_speed; }
/**
 * This method sets the speed at which the npc will follow
 * after a player when they leave combat.
 * @return the current follow speed
 * @see set_follow_speed()
 */
void set_follow_speed(int f) { follow_speed = f; }

/**
 * This method returns the current aggressive level of the npc.
 * If the aggressive is set to 1, then the npc will attack all players
 * that enter its environment.  If the aggressive is set to 2 then
 * the npc will attack everything (including other npcs).
 * <p>
 * See the function start_attack() for information about things you
 * can do to stop aggressive npcs attacking things.
 * @return the aggressive level of the npc
 * @see set_aggressive()
 * @see start_attack()
 */
int query_aggressive() { return aggressive; }
/**
 * This method sets the current aggressive level of the npc.
 * If the aggressive is set to 1, then the npc will attack all players
 * that enter its environment.  If the aggressive is set to 2 then
 * the npc will attack everything (including other npcs).
 * <p>
 * See the function start_attack() for information about things you
 * can do to stop aggressive npcs attacking things.
 * @see query_aggressive()
 * @see start_attack()
 * @see set_join_fights()
 * @see set_throw_out()
 * @param a the new aggressive level
 */
void set_aggressive(int a) {
    aggressive = a;

    // this added to make aggressive npcs join in fights if they arent
    // already set that way. This is needed coz otherwise you can sneak
    // into a room and fight the aggressive npcs one at a time -- Ceres 10/97
    if (a && !join_fight_mess) {
        join_fight_mess = this_object()->one_short()+" joins in the fight.\n";
    }
}

/**
 * This method returns the current guild level of the npc.  This is
 * a pass through call to a function on the guild object associated
 * with this npc.
 * @return the current guild level of the object
 * @see set_guild()
 */
int query_level() {
    if (!guild_ob) return 1;
    return (int)guild_ob->query_level(this_object());
} /* query_level() */

/**
 * This method returns the amount of death experiecne that would be
 * gained by killing the npc.
 * @return the amount of death experience for the npc
 */
int query_death_xp() {
    int amount;

    if ( query_property( "dead" ) || query_property( "unique" ) ) {
        return 0;
    }
    amount = (int)TOP_TEN_HANDLER->calculate_rating( this_object() );
    //amount += (int)this_object()->query_max_hp();
    //amount *= 120;
    return amount / 2;
} /* query_death_xp() */

/** @ignore yes */
mixed *stats() {
    mixed *bing;
    int i;
    bing = ({ });
    for (i=0;i<sizeof(move_zones);i++)
        bing += ({ ({ "move zone "+i, move_zones[i] }) });
    if (!query_move_after())
        return ::stats() + ({
          ({ "guild", query_guild() }),
          ({ "guild ob", query_guild_ob() }),
          ({ "race", query_race() }),
          ({ "race ob", query_race_ob() }),
          ({ "join_fights", query_join_fights() }),
          ({ "follow speed", query_follow_speed() }),
          ({ "level", query_level() }),
          ({ "chat chance", query_chat_chance() }),
          ({ "achat chance", query_achat_chance() }),
          ({ "aggressive", query_aggressive() }),
          ({ "route", (sizeof(following_route)?implode(following_route, ", "):
              "not going anywhere") }),
        }) + bing;
    return ::stats() + ({
      ({ "race", query_race() }),
      ({ "race ob", query_race_ob() }),
      ({ "guild", query_guild() }),
      ({ "guild ob", query_guild_ob() }),
      ({ "join fights", query_join_fights() }),
      ({ "follow_speed", query_follow_speed() }),
      ({ "level", query_level() }),
      ({ "chat chance", query_chat_chance() }),
      ({ "achat chance", query_achat_chance() }),
      ({ "aggressive", query_aggressive() }),
      ({ "move after-fix", query_move_after()[0] }),
      ({ "move after-rand", query_move_after()[1] }),
      ({ "route", (sizeof(following_route)?implode(following_route, ", "):
          "not following anyone") }),
    }) + bing;
} /* stats() */

/** @ignore yes */
string expand_nickname(string str) { return str; }

/** @ignore yes */
void event_enter( object dest, string mess, object from ) {
    /* stop massive spam in /room/rubbish and /room/void */
    if( environment( this_object() ) &&
      file_name( environment( this_object() ) )[ 1 .. 4 ] == "room" )
        return;
    if(check_anyone_here() && (moves > 2)){
        moves = 0;
        do_move_after(0);
    }
    living::event_enter( dest, mess, from );
    events::event_enter( dest, mess, from );
} /* event_enter() */

/** @ignore yes */
void event_person_say( object thing, string start, string mess, string lang,
                       string accent) {
    response_mon::event_person_say(thing, start, mess, lang);
} /* event_person_say() */

/** @ignore yes */
void event_whisper( object thing, string start, string mess, object *obs,
  string lang, object me) {
    response_mon::event_whisper( thing, mess, obs, lang, me);
} /* event_whisper() */

/** @ignore yes */
varargs void event_soul( object thing, string mess, mixed avoid, string verb,
  string last, mixed at ) {
    response_mon::event_soul( thing, mess, avoid, verb, last, at );
    events::event_soul( thing, mess, avoid );
} /* event_soul() */

/**
 * This method moves the npc to room it is really supposed to be in.
 * This is used with the virtual moving
 * @param check_room the place where it thinks we are
 * @see query_virtual_move()
 */
void real_room( string check_room ) {
    if ( check_room == true_location ) {
        move_object( true_location );
    }
} /* real_room() */

/**
 * This method returns true if we are currently using the
 * virtual move system.
 * @return 1 if we are virtual moving, 0 if not
 * @see set_virtual_move()
 */
#if !efun_defined(add_action)
int query_virtual_move() { return virtual_move; }
#else
int query_virtual_move() { return 0; }
#endif
/**
 * This method sets the current virual move ability of the npc.
 * NB: This is currently disabled virtual moving is not possible
 * at all.
 * @param number 1 for virtual moving, 0 for not
 * @see query_virtual_move()
 */
void set_virtual_move( int number ) {
    if(virtual_move && !number && file_name(environment()) == "/room/virtual"){
        object ob = load_object(true_location);
        if(ob)
            move(ob);
    }
    virtual_move = number;
    if ( virtual_move && environment() )
        true_location = file_name( environment() );
} /* set_virtual_move() */

/**
 * This method returns the true location of the npc.  This is
 * the real room it is in, not the room it is currently in.
 * @return the true location of the npc
 * @see set_true_location()
 * @see set_virtual_move()
 */
string query_true_location() { return true_location; }

/**
 * This method sets the true location of the npc.  This is
 * the real room it is in, not the room it is currently in.
 * @param word the new true location of the npc
 * @see set_true_location()
 * @see set_virtual_move()
 */
void set_true_location( string word ) {
    if(word == "/room/virtual"){
        if(true_location)
            return;
        //we have to be somewhere!
        word = file_name(environment());
        if(word == "/room/virtual"){
            //something is totally wrong give up on the npc
            move("/room/rubbish");
            true_location = "/room/rubbish";
        }
    }
    true_location = word;
}

int cleaning_room() {
    if ( virtual_move ) {
        true_location = file_name( environment() );
        "/room/virtual"->force_load();
        move_object( "/room/virtual" );
        return 1;
    }
    return 0;
} /* cleaning_room() */

/**
 * This method causes the npc to move in the given direction.
 * @param move the direction to move
 */
void do_move( string move ) {
    if (1 || check_anyone_here()) {
        moves = 0;
    }
    if (moves++ < 3) {
        string tmp;
        command(move);
        tmp = file_name(environment());
        if(tmp == "/room/virtual")
            tmp = true_location; //room didn't let us in.
        true_location = tmp;
    } else {
        WANDER_HANDLER->delete_move_after(this_object());
    }
} /* do_move() */

/* Move along a nice route thingy... */
/**
 * This method gets the next direction to go in the route which is
 * currently being followed.  It will remove this direction off the
 * array.
 * @return the next direction to go down
 * @see query_last_route_direction()
 * @see query_following_route()
 * @see do_route_move()
 */
string get_next_route_direction() {
    string direc;

    if (!sizeof(following_route)) {
        return 0;
    }

    direc = following_route[0];
    following_route = following_route[1..];
    return direc;
} /* get_next_route_direction() */

/**
 * This method tells us if the npc is currently following a route.
 * @return 1 if there are route directions to follow still
 * @see get_next_route_direction()
 * @see query_following_route()
 * @see do_route_move()
 */
int query_last_route_direction() { return ( sizeof(following_route) ? 1 : 0 ); }

/**
 * This method returns the current array of directions we are following
 * as a route.
 * @see get_next_route_direction()
 * @see query_last_route_direction()
 * @see do_route_move()
 */
string *query_following_route() { return following_route; }

/**
 * This method moves the npc one more location along the route it
 * is following.
 * @see query_last_route_direction()
 * @see query_following_route()
 * @see get_next_route_direction()
 */
void do_route_move() {
    if (!sizeof(following_route)) {
        return ;
    }

    do_command(get_next_route_direction());
} /* do_route_move() */

/**
 * This method is called by the move_me_to function after the
 * route handler has successfuly discovered the route to follow.
 * @param route the route to follow
 * @param delay the delay to follow it with
 * @param dest route destination
 * @see move_me_to()
 */
protected void got_the_route(string *route, int delay, string dest) {
    following_route = route;
    if (sizeof(route)) {
        WANDER_HANDLER->move_me_please(delay, dest);
        do_route_move();
    } else {
        this_object()->stopped_route();
    }
} /* got_the_route() */

/**
 * This method will move the npc to the specified destination.  The
 * npc will walk from where they currently are to the destination using
 * the time delay specified between the movements.
 * <p>
 * If the location is reached then the function "stopped_route" will
 * be called on the npc.  The first arguement to the function will
 * be 0 if the npc did not reach its destination and 1 if it did.
 * @param dest the destination to go to
 * @param delay the time delay between each move
 * @example
 * inherit "/obj/monster";
 *
 * void go_home() {
 *    move_me_to(HOME_LOCATION, 2);
 * } /\* go_home() *\/
 *
 * void stopped_route(int success) {
 *    if (success) {
 *       do_command("emote jumps for joy.");
 *    } else {
 *       do_command("emote looks sad and lost.");
 *    }
 * } /\* stopped_route() *\/
 * @see get_next_route_direction()
 * @see got_the_route()
 * @see query_last_route_direction()
 * @see query_following_route()
 * @see do_route_move()
 */
varargs void move_me_to(string dest, int delay) {
    string *dest_dir, *start_dir;

    if (!environment() || !file_name(environment()))
        return ;
    if(!delay)
      delay = 10;
    
    if (!MAP->static_query_short( dest ) ||
      !MAP->static_query_short( file_name(environment()) ) ) {
        //very likely to have no route
       if (!sizeof( dest_dir = dest->query_dest_dir( this_object() ) ) ||
           !sizeof( start_dir = 
               environment()->query_dest_dir( this_object() ) ) )
       {
            //one of the rooms have no exits
            move( dest, "$N wanders in.\n", "$N wanders off.\n" );
        } else {
            //both rooms have exits
            move( dest,
              replace_string( query_msgin(), "$F",
                dest_dir[random(sizeof(dest_dir)/2)*2]),
              replace_string( query_msgout(), "$T",
                start_dir[random(sizeof(start_dir)/2)*2]));
        }
        call_out( "stopped_route", 1 );
        return;
    }

    ROUTE_HANDLER->get_route(dest, file_name(environment()),
      (: got_the_route($1, $(delay), $(dest)) :));
} /* move_me_to() */

/** @ignore yes */
string identify( object thing, object *places ) {
    do_command( "'Please bug me!  I'm using identify()!" );
    return file_name( thing );
} /* identify() */

/** @ignore yes */
int query_time_left() { return 1; }

/** @ignore yes */
int ignore_identifier() { return 1; }

/**
 * This method adds a language to the npc. 
 * <p>
 * After the sun has died away<br>
 * The stars come out and glow<br>
 * Lighting the embers of good intentions<br>
 * Ghostly white, unhappily bright<br>
 * Time lost, the day done
 * @param str the language to add
 */
void add_language(string str) {
    ::add_language(str);
    added_language = 1;
} /* add_language() */

/**
 * This method sets the value of the unable to change position flag.
 * This flag will be checked by the soul, and by anything else which
 * deliberatly changes someones position.
 * @param flag the unable to change position flag
 * @see /std/living/living->set_default_position()
 */
void set_cannot_change_position(int flag) {
    cannot_change_position = flag;
} /* set_cannot_change_position() */

/**
 * This method returns the current value of the unable to change
 * position flag.
 * @return the unable to change position flag
 * @see /std/living/living->set_default_position()
 */
int query_cannot_change_position() {
    return cannot_change_position;
} /* query_cannot_change_position() */

/**
 * This method overrides the position code so that if the position
 * is changed we are changed back to the default position.
 * @param new_pos the new position
 * @ignore yes
 */
void set_position(string new_pos) {
    if (always_return_to_default_position) {
        if (new_pos != query_position() &&
          this_player() != this_object()) {
          // Please, do NOT use function pointers unless it's necessary.
          // Took me ages to find this "function" given that all
          // call_stack could tell me was that it was "<function>"
            call_out("return_to_default_position",
              always_return_to_default_position, 0);
        }
    }
    ::set_position(new_pos);
} /* set_position() */

/**
 * This method sets the status of the flag that makes the npc return
 * to the default position if its position is changed.  The flag
 * specified the length of time to wait before causing the
 * default position to be restored.
 * @param tim the time to wait before the position is restored
 * @see /std/living/living->return_to_default_position()
 * @see query_always_return_to_default_position()
 */
void set_always_return_to_default_position(int tim) {
    always_return_to_default_position = tim;
} /* set_always_return_to_default_position() */

/**
 * This method returns the status of the flag that makes the npc return
 * to the default position if its position is changed.  The flag
 * specified the length of time to wait before causing the
 * default position to be restored.
 * @return the time to wait before the position is restored
 * @see /std/living/living->return_to_default_position()
 * @see set_always_return_to_default_position()
 */
int query_always_return_to_default_position() {
    return always_return_to_default_position;
} /* query_always_return_to_default_position() */

/** @ignore yes */
mapping int_query_static_auto_load() {
    return ([
      "::" : ::int_query_static_auto_load(),
      "cap name" : cap_name,
      "race" : race,
      "guild" : guild,
    ]);
}

/** @ignore yes */
mixed query_static_auto_load() {
    if ( base_name(this_object()) + ".c" == __FILE__ )
        return int_query_static_auto_load();
    return ([ ]);
}

mapping query_dynamic_auto_load() {
    return ([
      "::" : ::query_dynamic_auto_load(),
      "chat string"                       : chat_string,
      "achat string"                      : achat_string,
      "combat actions"                    : _combat_actions,
      "move after"                        : _move_after,
      "throw out"                         : throw_out,
      "chat chance"                       : chat_chance,
      "achat chance"                      : achat_chance,
      "aggressive"                        : aggressive,
      "join fight type"                   : join_fight_type,
      "join fight mess"                   : join_fight_mess,
      "follow speed"                      : follow_speed,
      "virtual move"                      : virtual_move,
      "moves"                             : moves,
      "true location"                     : true_location,
      "enter commands"                    : enter_commands,
      "move zones"                        : move_zones,
      "doing story"                       : doing_story,
      "last attacked"                     : last_attacked,
      "following route"                   : following_route,
      "added language"                    : added_language,
      "cannot change position"            : cannot_change_position,
      "always return to default position" : always_return_to_default_position,
      "level"                             : query_level(),
    ]);
}

void init_static_arg(mapping args) {
    if (args["::"])
        ::init_static_arg(args["::"]);
    if (!undefinedp(args["cap name"]))
        cap_name = args["cap name"];
    if (!undefinedp(args["race"]))
        race = args["race"];
    if (!undefinedp(args["guild"]))
        guild = args["guild"];
}

void init_dynamic_arg( mapping args, object ob ) {
    if (args["::"])
        ::init_static_arg(args["::"]);
    if (!undefinedp(args["chat string"]))
        chat_string = args["chat string"];
    if (!undefinedp(args["achat string"]))
        achat_string = args["achat string"];
    if (!undefinedp(args["combat actions"]))
        _combat_actions = args["combat actions"];
    if (!undefinedp(args["move after"]))
        _move_after = args["move after"];
    if (!undefinedp(args["throw out"]))
        throw_out = args["throw out"];
    if (!undefinedp(args["chat chance"]))
        chat_chance = args["chat chance"];
    if (!undefinedp(args["achat chance"]))
        achat_chance = args["achat chance"];
    if (!undefinedp(args["aggressive"]))
        aggressive = args["aggressive"];
    if (!undefinedp(args["join fight type"]))
        join_fight_type = args["join fight type"];
    if (!undefinedp(args["join fight mess"]))
        join_fight_mess = args["join fight mess"];
    if (!undefinedp(args["follow speed"]))
        follow_speed = args["follow speed"];
    if (!undefinedp(args["virtual move"]))
        virtual_move = args["virtual move"];
    if (!undefinedp(args["moves"]))
        moves = args["moves"];
    if (!undefinedp(args["true location"]))
        true_location = args["true location"];
    if (!undefinedp(args["enter commands"]))
        enter_commands = args["enter commands"];
    if (!undefinedp(args["move zones"]))
        move_zones = args["move zones"];
    if (!undefinedp(args["doing story"]))
        doing_story = args["doing story"];
    if (!undefinedp(args["last attacked"]))
        last_attacked = args["last attacked"];
    if (!undefinedp(args["following route"]))
        following_route = args["following route"];
    if (!undefinedp(args["added language"]))
        added_language = args["added language"];
    if (!undefinedp(args["cannot change position"]))
        cannot_change_position = args["cannot change position"];
    if (!undefinedp(args["always return to default position"]))
        always_return_to_default_position =
        args["always return to default position"];
    if (!undefinedp(args["level"]))
        set_level(args["level"]);
}
/**
 *
 * attack_permission function, added for use by the allow_attack simul.
 * @param object The person performing the action
 * @param object The object being acted on, this object usually.
 * @param string Attack type, this will be one of "combat", "theft", or "magic",
 * this lets you give your NPCs different responses for different attacks. As well
 * as make them immune to theft and magic (as an example)
 * @return int 1 if the action is denied, 0 is it can go through.
 * @see efun::allow_attack()

 */
int attack_permission( object ob1, object ob2, string stringy ) { return 0; }

#if !efun_defined(add_action)
/** @ignore yes */
protected mixed _process_input(string str) {
    object ob = this_player();
    if(str == "")
        return 0;
    _notify_fail(0);
    efun::set_this_player(this_object());
    if(!this_object()->drunk_check(str))
        if(!this_object()->exit_command(str))
            if(!this_object()->cmdAll(str))
                if(!this_object()->new_parser(str))
                    if(!this_object()->lower_check(str)){
                        efun::set_this_player(ob);
                        return 0;
                    }
    efun::set_this_player(ob);
    return "bing";
} /* _process_input() */

int drunk_check(string str) {
   if(cmd_func){
     object owner = function_owner(cmd_func);
     if(owner && owner == environment(this_player())){
       int res = evaluate(cmd_func, str);
       if(res)
         return res;
     } else cmd_func = 0;
   }

   return 0;
} /* drunk_check() */

/**
 * Use this function to set a function that is called with the NPCs input
 * before command() gets it, return 1 from the function if the
 * input needs no further parsing (ie the command is handled)
 *
 * @param func = function in the players environment to call.
 */

void command_override(function func) {
  if(!functionp(func))
    error("command_override needs a function!");
  cmd_func = func;
}


/** @ignore yes */
protected mixed command(string cmd){
  int time = eval_cost();
  if(_process_input(cmd))
    return eval_cost() - time + 1; // on v22.2 eval_cost runs up, reverse for v22.1
  return 0;
}

#endif

/** @ignore yes */
int _living(){return 1;}


/**
 * This event is triggered when hide_invis is added or removed from an
 * object.  In this case it's used to make the NPC attack if someone
 * sneaks into the room and comes out of hiding.
 * @param hider The person who's hiding/unhiding.
 * @param adding 1 if the person is hiding, 0 if they are coming out.
 * @param type The type of hide invis.
 * @param quiet The quiet flag that's passed into remove_hide_invis.
 */
void event_hide_invis( object hider, int adding, string type, int quiet ) {
   if ( aggressive &&
        !adding &&
        environment() &&
        file_name( environment() )[1..4] != "room" )
   {
      start_attack(hider);
   }
} /* event_hide_invis() */