tmi2_fluffos_v2/
tmi2_fluffos_v2/bin/
tmi2_fluffos_v2/etc/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/ChangeLog.old/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/Win32/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/compat/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/compat/simuls/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/include/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/clone/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/command/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/data/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/etc/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/include/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/inherit/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/inherit/master/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/log/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/single/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/single/tests/compiler/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/single/tests/efuns/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/single/tests/operators/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/u/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/tmp/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/windows/
tmi2_fluffos_v2/lib/
tmi2_fluffos_v2/lib/adm/
tmi2_fluffos_v2/lib/adm/daemons/languages/
tmi2_fluffos_v2/lib/adm/daemons/network/I3/
tmi2_fluffos_v2/lib/adm/daemons/virtual/
tmi2_fluffos_v2/lib/adm/daemons/virtual/template/
tmi2_fluffos_v2/lib/adm/news/
tmi2_fluffos_v2/lib/adm/obj/
tmi2_fluffos_v2/lib/adm/obj/master/
tmi2_fluffos_v2/lib/adm/priv/
tmi2_fluffos_v2/lib/adm/shell/
tmi2_fluffos_v2/lib/adm/tmp/
tmi2_fluffos_v2/lib/cmds/
tmi2_fluffos_v2/lib/d/
tmi2_fluffos_v2/lib/d/Conf/
tmi2_fluffos_v2/lib/d/Conf/adm/
tmi2_fluffos_v2/lib/d/Conf/boards/
tmi2_fluffos_v2/lib/d/Conf/cmds/
tmi2_fluffos_v2/lib/d/Conf/data/
tmi2_fluffos_v2/lib/d/Conf/logs/
tmi2_fluffos_v2/lib/d/Conf/obj/
tmi2_fluffos_v2/lib/d/Conf/text/help/
tmi2_fluffos_v2/lib/d/Fooland/adm/
tmi2_fluffos_v2/lib/d/Fooland/data/
tmi2_fluffos_v2/lib/d/Fooland/data/attic/
tmi2_fluffos_v2/lib/d/Fooland/items/
tmi2_fluffos_v2/lib/d/TMI/
tmi2_fluffos_v2/lib/d/TMI/adm/
tmi2_fluffos_v2/lib/d/TMI/boards/
tmi2_fluffos_v2/lib/d/TMI/data/
tmi2_fluffos_v2/lib/d/TMI/rooms/
tmi2_fluffos_v2/lib/d/grid/
tmi2_fluffos_v2/lib/d/grid/adm/
tmi2_fluffos_v2/lib/d/grid/data/
tmi2_fluffos_v2/lib/d/std/
tmi2_fluffos_v2/lib/d/std/adm/
tmi2_fluffos_v2/lib/data/adm/
tmi2_fluffos_v2/lib/data/adm/daemons/
tmi2_fluffos_v2/lib/data/adm/daemons/doc_d/
tmi2_fluffos_v2/lib/data/adm/daemons/emoted/
tmi2_fluffos_v2/lib/data/adm/daemons/network/http/
tmi2_fluffos_v2/lib/data/adm/daemons/network/services/mail_q/
tmi2_fluffos_v2/lib/data/adm/daemons/network/smtp/
tmi2_fluffos_v2/lib/data/adm/daemons/news/archives/
tmi2_fluffos_v2/lib/data/attic/connection/
tmi2_fluffos_v2/lib/data/attic/user/
tmi2_fluffos_v2/lib/data/std/connection/b/
tmi2_fluffos_v2/lib/data/std/connection/l/
tmi2_fluffos_v2/lib/data/std/user/a/
tmi2_fluffos_v2/lib/data/std/user/b/
tmi2_fluffos_v2/lib/data/std/user/d/
tmi2_fluffos_v2/lib/data/std/user/f/
tmi2_fluffos_v2/lib/data/std/user/l/
tmi2_fluffos_v2/lib/data/std/user/x/
tmi2_fluffos_v2/lib/data/u/d/dm/working/doc_d/
tmi2_fluffos_v2/lib/data/u/l/leto/doc_d/
tmi2_fluffos_v2/lib/data/u/l/leto/smtp/
tmi2_fluffos_v2/lib/doc/
tmi2_fluffos_v2/lib/doc/driverdoc/applies/
tmi2_fluffos_v2/lib/doc/driverdoc/applies/interactive/
tmi2_fluffos_v2/lib/doc/driverdoc/concepts/
tmi2_fluffos_v2/lib/doc/driverdoc/driver/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/arrays/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/buffers/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/compile/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/ed/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/filesystem/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/floats/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/functions/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/general/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/mappings/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/numbers/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/parsing/
tmi2_fluffos_v2/lib/doc/driverdoc/lpc/constructs/
tmi2_fluffos_v2/lib/doc/driverdoc/lpc/preprocessor/
tmi2_fluffos_v2/lib/doc/driverdoc/lpc/types/
tmi2_fluffos_v2/lib/doc/driverdoc/platforms/
tmi2_fluffos_v2/lib/doc/mudlib/
tmi2_fluffos_v2/lib/ftp/
tmi2_fluffos_v2/lib/include/driver/
tmi2_fluffos_v2/lib/log/
tmi2_fluffos_v2/lib/log/driver/
tmi2_fluffos_v2/lib/obj/net/
tmi2_fluffos_v2/lib/obj/shells/
tmi2_fluffos_v2/lib/obj/tools/
tmi2_fluffos_v2/lib/std/adt/
tmi2_fluffos_v2/lib/std/board/
tmi2_fluffos_v2/lib/std/body/
tmi2_fluffos_v2/lib/std/fun/
tmi2_fluffos_v2/lib/std/living/
tmi2_fluffos_v2/lib/std/object/
tmi2_fluffos_v2/lib/std/shop/
tmi2_fluffos_v2/lib/std/socket/
tmi2_fluffos_v2/lib/std/user/
tmi2_fluffos_v2/lib/std/virtual/
tmi2_fluffos_v2/lib/student/
tmi2_fluffos_v2/lib/student/kalypso/
tmi2_fluffos_v2/lib/student/kalypso/armor/
tmi2_fluffos_v2/lib/student/kalypso/rooms/
tmi2_fluffos_v2/lib/student/kalypso/weapons/
tmi2_fluffos_v2/lib/u/l/leto/
tmi2_fluffos_v2/lib/u/l/leto/cmds/
tmi2_fluffos_v2/lib/www/errors/
tmi2_fluffos_v2/lib/www/gateways/
tmi2_fluffos_v2/lib/www/images/
tmi2_fluffos_v2/old/
tmi2_fluffos_v2/win32/
// File: /std/monster.c
// The monster body is from the TMI-2 lib. Part of the bodies project and
// implemented by Watcher@TMI-2 and Mobydick@TMI-2. The code contained
// in this object is heavily based on code found in the original user.c
// and monster.c at TMI.
//
// The relevant code headers follow:
// Original author of monster.c: Huma@TMI.
// Subsequent work done by Buddha@TMI-2 and Mobydick@TMI-2.
// Original authors of user.c were Sulam@TMI and Buddha@TMI.
// Many other people have added to this as well.
// This file has a long revision history dating back to the hideous
// mudlib.n and is now probably not at all the same.
// This file is part of the TMI distribution mudlib.
// Please keep this header with any code within.
//
// leto might have fixed type checking, might have killed it ;)
 
#include <config.h>
#include <daemons.h>
#include <net/daemons.h>
#include <mudlib.h>
#include <move.h>
#include <money.h>
#include <priv.h>
#include <uid.h>
#include <logs.h>
#include <body.h>

inherit LIVING ;
object tmp ;
static int speed, hb_status;
 
int environment_check();
void spell_cast (string spell, int chance) ;
void move_around() ;
void monster_chat();
void unwield_weapon(object weapon) ;
void unequip_armor(object armor) ;
varargs private void execute_attack(int hit_mod, int dam_mod) ;
static int create_ghost();
static void die();
 
mapping alias ;
 
//  Setup basic and command catch systems
 
void basic_commands() {
   add_action("quit", "quit");
}

static void init_commands() {
   string path;

   add_action( "cmd_hook", "", 1 );
 
   if(link_data("wizard")) {
   enable_wizard();
   path = NEW_WIZ_PATH;
   }
 
   else path = USER_CMDS;
 
      set("PATH", path, MASTER_ONLY);
}
 
//  This function resets the monster's living_name after a user 
//  shells out of the monster body.
 
void reset_monster() {
   if(!interactive(this_object())) {
     set_living_name( query("name") );
     seteuid(getuid(this_object()));
     link = 0;
   }
}
 
void clear_monster() { if(geteuid(previous_object()) == ROOT_UID) seteuid(0); }
 
 
string process_input (string arg) {
        arg = do_alias(arg) ;
        return arg ;
}
 
//   Setup standard user command hook system.  This system interfaces
//   with the cmd bin system, the environment's exits, and feeling entries.

nomask static int cmd_hook(string cmd) {
   string file;
   string verb;

   verb = query_verb();
 
   if (environment() && environment()->valid_exit(verb)) {
      verb = "go";
      cmd = query_verb();
   }

   file = (string)CMD_D->find_cmd(verb, explode(query("PATH"), ":"));
 
   if (file && file != "")
      return (int)call_other(file, "cmd_" + verb, cmd);
 
   if (environment() && environment()->query("quiet")) return 0 ;
   return (int)EMOTE_D->parse(verb, cmd);
}

 
/*
* Move the player to another room. Give the appropriate
* message to on-lookers.
* The optional message describes the message to give when the player
* leaves.
*/
 
varargs int move_player(mixed dest, string message, string dir) {
   object prev;
   int res;
   
   prev = environment( this_object() );
 
   if( res = move(dest) != MOVE_OK ) {
      write("You remain where you are.\n");
      return res;   }
 
 
   if(message == "SNEAK") {
     set_temp("force_to_look", 1);
     command("look");
     set_temp("force_to_look", 0);
   return 0; } 
 
   if(!query("invisible")) {
 
      if (message == 0 || message == "") {

        if(dir && dir != "") {
           tell_room(prev, (string)this_object()->query_mout(dir) + "\n",
                        ({ this_object() }));
           say((string)this_object()->query_min() + "\n", ({ this_object() }));
        } else {
           tell_room(prev, (string)this_object()->query_mmout() + "\n",
                        ({ this_object() }));
           say((string)this_object()->query_mmin() + "\n", ({ this_object() }));
        }
     } else {
        tell_room(prev, message + "\n", ({ this_object() }));
        say((string)this_object()->query_min() + "\n", ({ this_object() }));
     }
   }
 
    set_temp ("force_to_look", 1) ;
    command("look") ;
    set_temp("force_to_look", 0) ;

 
   //	Follow/track mudlib support
 
   if(!this_object()->query("no_follow") && environment() != prev)
   all_inventory(prev)->follow_other(this_object(), environment(), dir);
 
   return 0;
}

private int clean_up_attackers() {
   mixed *attackers_tmp;
   int i;
   attackers_tmp = ({});
   for (i=0; i < sizeof(attackers); i++) {
// If he's dead, then forget about him entirely.
	if (attackers[i] == 0 || !living(attackers[i]))
      {
         continue;
        }
// If he's not here, then we consider two cases.
	if (environment(attackers[i]) != environment(this_player()))
	{
// If this is a monster who is not forgetful, then add this attacker to the
// will_attack, the list of objects to be attacked on sight.
	   if (!query("forgetful")) {
	  	if (!will_attack) will_attack = ({ attackers[i] }) ; else
	       will_attack += ({ attackers[i] }) ;
	   }
// If a forgetful monster, then forget about him.
	    continue ;
	    }
// If he's a ghost, we've done enough to him already :)
      if ((int)attackers[i]->query("ghost")) continue ;
// If we get this far, then we still want to be attacking him
      attackers_tmp += ({ attackers[i] });
   }
// Copy the tmp list over to the attackers list.
   attackers = attackers_tmp;
   if (sizeof(attackers_tmp) == 0)
      any_attack = 0;
   return any_attack;
}

// Continue_attack is called from heart_beat in monster.c and user.c.
// here is where we can try to see if we're dead or in combat.

void continue_attack() {
// Check if this object has just died. if so, do the death stuff.
   if (query("hit_points") < 0 && !query("ghost")) {  die();  return;  }
 
// If there's no one to attack, then we are finished.
   if (!any_attack) return;
// Call the clean_up_attackers function to see who's left. If it returns
// 0, then there's no one left.
   if (clean_up_attackers() == 0) {
      write("The combat is over.\n"); /* No attackers in the room */
      return;
   }
// Check to see if the monster is doing something that prevents him from
// making an attack.
   if (query("stop_attack")>0) {
	write ("You are too busy to make an attack!\n") ;
   	return ;
   }
// Check to see if we're under wimpy, and if so, run away.
   if (query("hit_points")*100/query("max_hp") < this_player()->query("wimpy")) {
       run_away();
       return;
   }
   
// If this is a spellcasting monster, then maybe we ought to throw a spell.
   if (query("spellcaster")==1 && random(100)<query("spell_chance")) {
      if (query("spell_to_cast")=="heal") {
	call_other(this_object(),query("spell_to_cast")+"_cast",this_object());
      } else {
        call_other(this_object(),query("spell_to_cast")+"_cast") ;
      }
   } else {
// If we don't throw a spell, or are not spellcasting, then just make a
// normal weapon attack.
      execute_attack();
   }
}

// This is the big, ugly CPU hogging function where the combat actually
// takes place.
 
static int noise, loop;

varargs void execute_attack (int hit_mod, int dam_mod) {

	int att_sk, def_sk, str, dex, ac, wc, hit_chance ;
	string name, verb1, verb2, vname, damstr, damstr2, wepstr, *verbs;
	mixed *contents;
	object *prots ;
	object weapon ;
	int old_inv ;
	string victim ;
	string combat_str;
	int *damrange ;

// hit_mod and dam_mod are modifiers that can be passed to the attack.
// The heartbeat doesn't add them but you can make special attacks by
// calling execute_attack directly. Be careful if you do so, you'll want
// to also call kill_ob to make sure a fight starts...
 
	//if (!hit_mod) hit_mod=0 ;
	//if (!dam_mod) dam_mod=0 ;
 
// Check to see if primary target is dead, if so move to the next attack
// in the attackers queue. If the attackers queue is empty, stop attack call.

        if(attackers[0]->query("hit_points") < 0) {
          attackers -= ({ attackers[0] });
	  if(!attackers || !sizeof(attackers))  return;
        }
// Is the target being protected? If so, find the alternate target.

	prots = attackers[0]->query("protectors") ;
	if (prots && sizeof(prots)>0) {
// Get rid of all ineligible protectors: dead or not present.
	    prots = filter_array(prots,"valid_protect", this_object()) ;
// If there are eligible protectors, then move the protector to the top
// of the list, adding him if needed.
// Leto has seen the same error in user.c , i prob fixed it ;)
// 94-11-10
                if (sizeof(prots)) {
                      //victim = prots[random(sizeof(prots))] ;
                      weapon = prots[random(sizeof(prots))] ;
// re-using a variable to save memory - sorry bout that :(
                        // weapon = find_player(victim) ;
                        ac = member_array(weapon,attackers) ;
                        if (ac>-1) {
                                attackers[ac]=attackers[0] ;
                                attackers[0]=weapon ;
                        } else {
				attackers = ({ weapon }) + attackers ;
				weapon -> kill_ob (this_object()) ;
                        }
                }
        }
 
// Collect the various statistics needed to get the hit chance and damage.
        str = this_object()->query("stat/strength") ;
	dex = attackers[0] ->query("stat/dexterity") ;
	ac = attackers[0]->query("armor_class") ;
        weapon = this_object()->query("weapon1") ;
// If they don't have a weapon, they get their intrinsic combat skills.
	if (!weapon) {
			wc = query("attack_strength") ;
			damrange = allocate(2) ;
			damrange = query("damage") ;
			dmin = damrange[0] ;
			dmax = damrange[1] ;
			verbs = allocate(2) ;
			verbs = get_verb() ;
			verb1 = verbs[0] ;
			verb2 = verbs[1] ;
			wepstr = query("weapon_name") ;
	} else {
// If we get here, then the monster has a weapon, and we query the weapon
// for its attack properties.
		wc = weapon->query("weapon") ;
        	damrange = weapon->query("damage") ;
        	dmin = damrange[0] ;
        	dmax = damrange[1] ;
		verbs = allocate(2) ;
		verbs = weapon->get_verb() ;
		verb1 = verbs[0] ;
		verb2 = verbs[1] ;
		wepstr = (string)weapon->query("name") ;
		weapontype = capitalize(weapon->query("type")+" weapons") ;
	}
	name = query("cap_name") ;
	vname = attackers[0]->query("cap_name") ;

// Check the attacker's attack skill and the defenders skill(s).

	att_sk = this_object()->query_skill("attack") ;
// Ditto for the defender.
	if (!(int)attackers[0]->query_monster()) {
		if (attackers[0]->query("armor/shield")) {
			def_sk = attackers[0]->query_skill("Shield defense") ;
		} else {
			def_sk = attackers[0]->query_skill("Parrying defense") ;
		}
	} else {
		def_sk = attackers[0]->query_skill("defense") ;
	}

// This is the combat formula.
// If you are using drunkenness, and want it to affect combat, then
// call query("drunk"), which goes 1-25, and subtract it from the
// hit chance.

	hit_chance = 30 + str + att_sk + 3*wc - dex - def_sk - 3*ac + hit_mod ;

// Hitting invisible things is hard.

	if ((int)attackers[0]->query("invisible")>0) hit_chance=hit_chance/5 ;

// The hit chance is constrained to be between 2 and 98 percent.
	if (hit_chance<2) hit_chance = 2 ;
	if (hit_chance>98) hit_chance = 98 ;


// Improve the skills of the defender, if a player.

// The probability of the skill improving depends on the hit chance. If the
// hit chance is 0 or 100, the skill does not improve. If the hit chance is
// 50%, then the skill improves automatically. The closer the hit chance is
// to 50%, the more likely the skill is to improve. This rewards players for
// taking on monsters roughly equal in skill to themselves.

	skill_improve_prob = hit_chance * (100-hit_chance) / 25 ;
	if (random(100)<skill_improve_prob) {
		if (interactive(attackers[0])) {
			if (attackers[0]->query("armor/shield")) {
				attackers[0]->improve_skill("Shield defense",1) ;
			} else 	{
				attackers[0]->improve_skill("Parrying defense",1) ;
			}
		}
	}

// Get a list of who is listening in the room.
 
  	contents = all_inventory(environment(this_object()));
	contents = filter_array(contents, "filter_listening", this_object());
 
// This is the damage formula.
// We have to calculate this first because we don't want to print messages
// of the form "You hit for zero damage." If the damage is less than zero,
// we print a "You miss" message regardless of the hit_chance roll.

	damage = random(dmax-dmin+1)+dmin+str/8-1+att_sk/10-def_sk/5+dam_mod ;

// Before printing any messages, we have to make ourselves visible so that
// the victim definitely gets the message. At the end of the attack we'll
// restore the old invisibility setting.

	old_inv = query("invisible") ;
	set ("invisible", 0) ;

// If positive damage, and the hit lands, then we do damage and print
// the appropriate damage messages.
	if (damage>0 && random(100)<hit_chance) {

		str = attackers[0]->query("hit_points") ;
		if(damage)  attackers[0]->receive_damage(damage);
		qs = objective((string)attackers[0]->query("gender")) ;
 
// We print different damage messages based on how much damage was done.
// You might want to make this system a little more interesting. Go nuts.
 
		switch (damage) {
			case 1: {
				damstr = "scratch "+qs+"." ;
				damstr2 = "scratches "+qs+"." ;
				break ;
			    }
			case 2..3 : {
				damstr = "do light damage." ;
				damstr2 = "does light damage." ;
				break ;
			    }
			case 4..6 : {
				damstr = "hit." ;
				damstr2 = "hits." ;
				break ;
			    }
			case 7..9 : {
				damstr = "deliver a solid blow." ;
				damstr2 = "delivers a solid blow." ;
				break ;
			    }
			case 10..15 : {
				damstr = "hit hard!" ;
				damstr2 = "hits hard!" ;
				break ;
			    }
			case 16..20 : {
				damstr = "inflict massive damage!" ;
				damstr2 = "inflicts massive damage!" ;
				break ;
			    }
			default : {
// Mobydick just ran out of ideas at this point.  :)
				damstr = "whomp "+qs+"!" ;
				damstr2 = "whomps "+qs+"!" ;
			}
		}

	// The routines below check to see if all the listeners really
	// want to hear how the battle is going (Watcher, 4/27/93).
 
	if(!(this_object()->query("noise_level") && damage < 2))
	tell_object(this_object(),"You " + verb1 + " " + vname + " with your " +
	      wepstr + " and " + damstr + "\n");
 
	combat_str = name + " " + verb2 + " " + vname + " with " +
	 possessive(this_object()->query("gender")) + " " + wepstr +
	 " and " + damstr2 + "\n";

	for(loop=0; loop<sizeof(contents); loop++) {
	  if(contents[loop])
		noise = (int)contents[loop]->query("noise_level");
 
	  if(noise && (noise > 1 || (noise == 1 && damage < 2)))
		continue;

	  tell_object(contents[loop], combat_str);

	}
 
	if(attackers[0] && 
	   !(attackers[0]->query("noise_level") && damage < 2))
	tell_object(attackers[0], 
	  name + " " + verb2 + " you with " +
	  possessive(this_object()->query("gender")) + " " + wepstr + 
	  " and " + damstr2 + "\n");

	} else {

// If we got here, it means we missed the hit roll, or did zero damage.

 	if(!this_object()->query("noise_level"))
	  tell_object(this_object(),"You "+verb1+" "+vname+" with your "+wepstr+
		" but you miss.\n");
 
	for(loop=0; loop<sizeof(contents); loop++) 
	  if(contents[loop] && !contents[loop]->query("noise_level"))
	  tell_object(contents[loop],
		name + " " + verb2 + " " + vname + " with " +
		possessive(this_object()->query("gender")) + " " + wepstr +
		" but misses.\n");
 
	if(attackers[0] && !attackers[0]->query("noise_level"))
	  tell_object(attackers[0],
		name + " " + verb2 + " you with " +
		possessive(this_object()->query("gender")) + " " + wepstr +
		" but misses you.\n");
	}

// Restore the old invis setting.

	if (old_inv) set ("invisible", old_inv) ;

return; }

//  This function filters an array returning only living objects.
 
int filter_listening(object obj) {
   if(obj == attackers[0] || obj == this_object())  return 0;
return living(obj); }

int valid_protect (string str) {

	object foo ;

	foo = find_player(str) ;
	if (!foo) return 0 ;
	if (environment(foo)!=environment(this_object())) {
		return 0 ;
	}
        if ((int)foo->query("hit_points")<0) return 0 ;
        return 1 ;
}
 
 
//	If the monster body is inhabited by a user, give the
//	monster's living hash table name as that of the user.
 
void init_setup() {

	if(!query_link())  return 0;
 
	set_heart_beat(1);
	seteuid(getuid(this_object()));
	set_living_name( link_data("name") );
 
	basic_commands();
	init_commands();

}
 
void create() {

// Monsters need euid set so that they can clone corpses if they die.

	seteuid(getuid()) ;

// no default aliases.

   alias = ([ ]);
 
   set("npc", 1, LOCKED);
 
// Monsters default to the same size and carrying ability as humans. You
// might want to change this in non-humanoid monsters.
 
   set("autosave", 500);
   set("volume", 500);
   set("max_vol", 500) ;
   set("capacity", 5000);
   set("max_cap", 5000) ;
   set("mass", 7500) ;
   set("bulk", 1000) ;
   set("time_to_heal",10) ;
   set("short", "@@query_short");
   set("vision", "@@query_vision");
   set("gender", "neuter");
   enable_commands();

	set ("languages", ([ "common" : 100 ]) ) ;
   set_heart_beat(1);

   set ("PATH", "/cmds/std") ;
   basic_commands() ;
   init_commands() ;
}

void remove() {
   mixed *inv;
   int loop;
 
   inv = all_inventory(this_object());
   for(loop=0; loop<sizeof(inv); loop++)
     if(inv[loop]->query("prevent_drop"))
	inv[loop]->remove();
 
//   CMWHO_D->remove_user(this_object());
   if (link) link->remove();
   living::remove();
}
 
int quit(string str) {
   object ob;
   int i, j;

   if(str) {
   notify_fail("Quit what?\n");
   return 0; }
 
#ifdef LOGOUT_MSG
   tell_object(this_object(), LOGOUT_MSG);
#endif
 
#ifdef QUIT_LOG
   if(this_object() && interactive(this_object()))
   log_file(QUIT_LOG, link_data("cap_name") + ": Monster quit from " +
	    query_ip_name(this_object()) + " [" +
	    extract(ctime(time()), 4, 15) + "]\n");
#endif
  
   //   If the connection object is somehow missing ... 
   //	just remove the monster 
 
   if(!query_link()) {
     if(interactive(this_object()))  remove();
     reset_monster();
   return 1; }
 
   ob = new(OBJECT);			// Temporary body
 
   query_link()->set("tmp_body", ob);
   query_link()->switch_body();
   ob->remove();
 
   query_link()->remove();
   reset_monster();
 
return 1; }

void heart_beat() {

// If we're chasing someone, better go after him.

   if (query("in_pursuit")) {
      command("go "+query("in_pursuit")) ;
      if (present((object)query("pursued"),environment(this_object()))) {
         tell_object((object)query("pursued"),this_object()->query("cap_name")+" attacks you!\n") ;
         query("pursued")->kill_ob(this_object()) ;
      }
      delete ("in_pursuit") ;
      delete ("pursued") ;
   }

   continue_attack();
 
   if (query("moving")==1) move_around() ;
   if(random(100)+1 < query("chat_chance"))  monster_chat();
 
   unblock_attack();
   heal_up();
 
   //  If no-one is around and fully healed, shut down the heartbeat
 
   if(!environment_check() && query("hit_points") == query("max_hp")) {
 
   if(query("max_sp") && query("max_sp") != query("spell_points"))
	return;

// If we're whanging on someone, best not to shut it down... :)

	if (sizeof(attackers)) return ;
 
   set_heart_beat(0);  hb_status = 0;  
   }
 
}
 
//  This function allows monsters to talk or give environmental sounds.

void monster_chat() {
   mixed *chats;

   if(attackers && sizeof(attackers))  chats = query("att_chat_output");
   else  chats = query("chat_output");

   if(!chats || !sizeof(chats))  return ;
 
   if(environment())
   tell_room(environment(), chats[ random(sizeof(chats)) ]);

return; }
 
 
// Let's get rid of this somehow.
void set_name (string str) {
	if (!str)  return;
	set ("name", str) ;
	set ("cap_name", capitalize(str)) ;
	set_living_name(str) ;
}
 
static void die() {
   object killer, ghost, corpse, coins, *stuff;
   mapping wealth ;
   string *names ;
   int i, res, totcoins ;
 
   // 	Define the monster's killer

   killer = query("last_attacker");
 
   init_attack() ;
 
   //	Setup corpse with monster's specifics
 
   if(query("alt_corpse"))
  	corpse = clone_object(query("alt_corpse"));
   else corpse = clone_object("/obj/corpse");
 
   corpse->set_name(query("cap_name")) ;
 
   i = query("mass") ;
   if (i>0) corpse->set("mass", i) ;
 
   i = query("bulk") ;
   if (i>0) corpse ->set("bulk", i) ;
 
   i = query("capacity") ;
   if (i>0) corpse ->set ("capacity", i) ;
 
   i = query("volume") ;
   if (i>0) corpse ->set("volume", i) ;
 
   corpse->move(environment(this_object()));
 
   stuff = all_inventory(this_object());
 
   for(i=0;i<sizeof(stuff);i++)
   stuff[i]->move(corpse); 
 
   wealth = query("wealth") ;
   if (wealth) {
      names=keys(wealth) ;
      for (i=0;i<sizeof(wealth);i++) {
	 coins = clone_object(COINS) ;
	 coins->set_type(names[i]) ;
	 coins->set_number(wealth[names[i]]) ;
	 totcoins = totcoins + wealth[names[i]] ;
	 if (coins) coins->move(corpse) ;
      }
   }
   set ("wealth", ([])) ;
 
   res = query("capacity") ;
   set ("capacity", res + totcoins) ;

   //  Set the last_kill variable ... file_name and short of this_object

   if(killer)
   killer->set("last_kill", ({ file_name(this_object()), query("short"),
	       time() }));
 
   // Announce the sad facts of life and non-life.
 
   write("You have died.\n");
   say(query("cap_name") + " staggers and falls to the ground ... dead.\n",
       ({ this_player() }) );

   //  	NOTE:  If you want to give a reward of skill points or xp or
   //	       whatever to the killer, define the following function
   //	       in the monster's inherit file.

   if(killer)  this_object()->kill_reward( killer );
 
   //	If monster is inhabited, switch user to ghost body
 
   if(interactive(this_object())) {

   	ghost = create_ghost();
	if(!ghost)  return;

#ifdef GHOST_START_LOCATION
   if(ghost->move(GHOST_START_LOCATION) != MOVE_OK)
	ghost->move(START);
#else
   ghost->move(START);
#endif
 
   }

   //	Delay the remove call with a call_out to ensure all the 
   //	housekeeping from above is complete.
 
   call_out("remove", 0);
 
}

// These are the hooks for aggressive and killer monsters.
// If a monster turns on the "aggressive" property, then if they receive
// a message of the form "Foo enters" then they attack Foo. This is done
// in receive_messages. Players can get past these monsters by sneaking
// into the room or being invisible. Also, they don't attack if they enter
// the player's environment, only if the player enters theirs. Monsters
// which turn on the "killer" property will attack any player that triggers
// the monster's init. It can't be fooled by invis or sneaking, and it will
// attack you if it moves into your room. Moving killer monsters can be
// pretty nasty things.

void relay_message(string Class, string str) {

   string name ;
   string direction ;
   object victim ;

   if (str && (sscanf(str,"%s enters.",name)==1 ||
	sscanf (str, "Suddenly %s appears as if out of thin air.",name)==1)) {
      name = lower_case(name) ;
      victim = present(name,environment(this_object())) ;
      if (!victim) return ;
 
      if(query("aggressive") && !victim->query("ghost")) {
	tell_object (victim, "Suddenly the "+query("name")+" attacks you!\n") ;
	kill_ob(victim) ;
      }
   }
   if (query("pursuing") && str && sscanf(str,"%s leaves %s.",name,direction)==2) {
      name = lower_case(name) ;
      victim = find_living(name) ;
      if (attackers && member_array(victim,attackers)>-1) {
	set ("in_pursuit", direction) ;
	set ("pursued", victim) ;
      }
   }

}

void init() {
 
   //  If object is not a monster and is visible...think about attacking.
 
   if(!this_player()->query("npc") && !this_player()->query("ghost") &&
      visible(this_player())) {
 
      if (query("killer")) {
      write("As you enter, " + capitalize(query("name")) + " suddenly " +
	    "attacks you!\n");
      kill_ob( this_player() );
      }
 
      if(will_attack && member_array(this_player(), will_attack) != -1) {
      write(capitalize(query("name")) + " attacks you as you return!\n");
      will_attack -= ({ this_player() });
      kill_ob( this_player() );
      }
 
      //  If heartbeat is turned off ... turn it back on.
 
      if(!hb_status) {  hb_status = 1;  set_heart_beat(1);  }
 
   }
}

// This lets a pursuing monster chase you. It's call_out'd from
// relay_message.


int query_monster() { return 1; }
 
int query_npc() { return 1; }
 
int query_hb_status() {  return hb_status;  }

void move_around() {

   mapping doors,exits, tmp, patrol ;
   string foo,tstr ;
   string *nogos ;
   string *dirs ;
   int i ;

   if (query("moving")==1) set("moving",-1) ;
// If we have no environment, then stop moving around. Done for two
// reasons. First, there's nowhere to go anyway, so save CPU. Second,
// this stops the callout loop in master copies of objects.
// We also shut it down if there's no exits: same logic.
   if (!environment(this_object()) ||
		!environment(this_object())->query("exits")) {
	return ;
    }
   call_out("move_around", query("speed")) ;
// If we're in combat, probably oughta stay here.
   if (sizeof(attackers)) return ;

// If we have a patrol pattern to follow, get the direction from
// that. If not, pick a direction at random.

   exits = environment(this_object())->query("exits") ;
   patrol = this_object()->query("patrol") ;
   if (sizeof(patrol)) {
      i = this_object()->query("patrol_step") ;
      foo = patrol[i] ;
   } else {
      dirs = keys(exits) ;
      foo = dirs[random(sizeof(dirs))] ;
      nogos = query("forbidden_rooms") ;
      if (nogos && member_array(exits[foo],nogos)!=-1) {
		return ;
	}
   }
   doors = environment(this_object())->query("doors") ;
   if (doors && sizeof(doors) && doors[foo] &&
		doors[foo]["status"]!="open") {
      command("open "+foo+" door") ;
      return ;
   }
   tmp = environment(this_object())->query("exit_msg");
   if(tmp && mapp(tmp) && tmp[foo]) {
        tstr = replace_string(tmp[foo], "$N", (string)this_object()->query("cap_name"));
         move_player(exits[foo], tstr);
   } else {
      tstr = query_mout(foo);
      move_player(exits[foo], tstr);
   }
    if (sizeof(patrol)) {
      i = i + 1 ;
      if (i==sizeof(patrol)) i=0 ;
      set ("patrol_step", i) ;
    }
}

void spell_cast (string spell, int chance) {
	set ("spellcaster",1) ;
	set ("spell_to_cast",spell) ;
	set ("spell_chance",chance) ;
}

void wield_weapon (object weapon) {

	object foo ;
	string str ;

	foo = query("weapon1") ;
	if (foo) unwield_weapon(foo) ;
	set ("weapon1", weapon) ;
	weapon->set("wielded", 1) ;
	str = weapon->query("wield_func") ;
	if (str) call_other(weapon,str) ;
}

void equip_armor (object armor) {

	string type ;
	object foo ;

	type = armor->query("type") ;
	foo = query("armor/"+type) ;
	if (foo) unequip_armor(foo) ;
	set ("armor/"+type,(int)armor->query("armor")) ;
	armor->set("equipped",1) ;
	calc_armor_class() ;
}

void unwield_weapon (object weapon) {

	string str ;

	set ("weapon1", 0) ;
	weapon->unwield() ;
	str = weapon->query("unwield_func") ;
        if (str) call_other(weapon,str) ;
}

void unequip_armor (object armor) {

	string type ;

	type = armor->query("type") ;
	delete ("armor/"+type) ;
	armor->unequip() ;
	calc_armor_class() ;
}
 
//  This function checks to see if there are any players present in 
//  the monsters room, so heartbeat can be turned off periodically.

int environment_check() {
 
   if(interactive(this_object()))  return 1;
 
   if(environment())
   return sizeof(filter_array(all_inventory(environment()), "filter_env",
		 this_object()));  
return 0; }
 
static int filter_env(object obj) {
   return (interactive(obj) && visible(obj, this_object())); }

/* EOF */