Combat Stuff

// ----------------------------------------------------------------------------

#pragma strict_types

#define KILL_NEUTRAL_ALIGNMENT 10
#define ADJ_ALIGNMENT(al)      ((-al - KILL_NEUTRAL_ALIGNMENT) / 4)
#define NAME_OF_GHOST          "some mist"
#define WEAPON_CLASS_OF_HANDS  2
#define ARMOUR_CLASS_OF_BARE   0

#define DEBUG_COMBAT           0

#define BRAWL_IN_COMBAT        1

#if DEBUG_COMBAT
#define EV_COST "w/paldin/combat/ev_d"
#endif

#include <version.h>
#include <combat.h>
#include <daemons.h>
#include <defend.h>
#include <body_part.h>
#include <wizards.h>
#include <tune.h>
#include <regeneration.h>
#include <xfun.h>

// ----------------------------------------------------------------------------

inherit "lib/basic/hooks";
inherit "lib/living/defend_attack";
inherit "lib/living/skills";
inherit "lib/living/stats";
inherit "lib/living/claim";

// ----------------------------------------------------------------------------

void set_to_hit_bonus( int s );
void set_damage_bonus( int s );
void set_damage_modifier( int s );
void set_hit_modifier( int s );
varargs int query_to_hit_modifier(object weapon, int target, string dam_type);
int query_weapon_skill( object weapon );
varargs int query_to_hit_bonus(object weapon, int target, string dam_type);
varargs int query_damage_bonus(object weapon, int target, string dam_type);
int query_to_hit_armour( int target, string weap_type );
int query_to_damage_armour( int target, string weap_type );
varargs int query_to_damage_modifier(object weapon, int target,
                                     string dam_type);
int average_ac( int target );
int query_resistance( int dam, string dam_type );
mapping query_hit_armour();
mapping query_dam_armour();
int query_primary_attack();
int query_penalty( object weapon );
int query_second_attack();
int set_attack_method( string s );
string query_attack_method();
mixed query_no_attack( mixed x );
object query_second_weapon();
mapping query_skill_pts();
void set_skill_pts( string cate, int val );
int query_can_hit( object weapon, int weap_sk, int hit_at, string dam_type,
  int penalty, int whit );
int set_hit_messages( string *arr );
varargs int hit_player_damage( int dam, int target, string damage_type );
static void _award_kill_exp( mixed key, int dam, int total_dam );
varargs int hit_player( int dam, int target, string damage_type );
void attacked_by( object ob );
void stop_wielding( object ob );
int check_is_armour( object a );
void apply_armour( object a );
void apply_all_armour();
void wear_armour( object a );
void remove_armour( object a );
void wear_shield( object s );
void remove_shield( object s );
int attack();
int hit_with_weapon( object weapon, int penalty );
int set_defend( string str, int silent );
int fail_wield( object w, int where );
int fail_wear( object w );
void wield( object w );
void second_wield( object w );
void heal_self( int h );
void restore_spell_points( int h );
void reduce_spell_points( int i );
void restore_hit_points( int h );
void attack_object( object ob );
void set_attacker( object ob );
void set_hunted( object ob );
void zap_object( object ob );
void stop_hunter();
void run_away();
void stop_fight();
void set_attack_disabled( int active );
void set_wimpy_disabled( int active );
int reduce_hit_point( int dam );
int pay_spell_points( int arg );
void stop_any_hunt();
int calculate_power();
void set_detect_invisible( status arg );
status see_invisible();
int query_hit_point();
int query_wc();
int query_ac();
int query_head_ac();
int query_body_ac();
int query_arm_ac();
int query_leg_ac();
int query_block_ac();
object query_alt_hunter();
object query_alt_attack();
static void set_wimpy( int w );
int query_wimpy();
#ifdef VERSION_WIMPY_DIR
static void set_wimpy_direction( string direction );
string query_wimpy_direction();
#endif
object query_hunter();
object query_wielded_weapon();
int query_attack_disabled();
static void set_aim_at( int a );
int query_aim_at();
object query_worn_shield();
int query_dead();
object query_hunted();
object query_attack();
int query_parry_ac();
int query_weapon_class();
string query_killed_string();
string query_died_string();
void ks_handle_attack( object who, object attacker, int dam );
void ks_handle_death();

// ----------------------------------------------------------------------------

int query_debug();

string query_name();
string query_real_name();
string query_objective();
int query_body_position();
string query_head_str();
string query_body_str();
string query_arm_str();
string query_leg_str();
string query_hand_str();
string query_foot_str();

int query_level();
string query_real_race();
string query_class();
int query_npc();
int query_is_player();

int query_exp();
void set_exp( int i );
void add_exp( int exp );

int query_ability( string ab );

int query_alignment();

int query_local_weight();
int query_max_carry();

void stop_follow();
varargs void transfer_all_to( object corpse, int flag );
void save_me( int i );
object kill_claimer();

public varargs void message( string mess, object who, object target );

// ----------------------------------------------------------------------------

static string corpsefile;  /* Custom corpse */
int whimpy;         /* Automatically run when low on HP */
string whimpy_direction; /* Which way to run first when low on HP */
int dead;           /* Are we alive or dead? */

static int attack_disabled; /* Disable autoattack - Greyson */
static int wimpy_disabled;  /* Disable run_away - Greyson */
static int armour_class;   /* What armour class of monster */
static object worn_shield; /* This is the shield we're wearing - Biko */
static object attacker_ob; /* Name of player attacking us */
static int weapon_class;   /* How good weapon. Used to calculate damage */
static object head_armour; /* What armour we have */
static int hunting_time;   /* How long we will stay in hunting mode */
static string *hit_messages;  /* Hit messages */
static object hunted, hunter; /* Used in the hunt mode */
static object alt_hunter;     /* Added 19-Nov-93 (Greyson) to stop cheaters */
static object wielded_weapon; /* The weapon we're wielding */
static object alt_attacker_ob;  /* Name of other player also attacking us */
static int block_ac;  /* How good the shield is at blocking */
static int parry_ac;  /* How good your weapon is at parrying */
int aiming_at; /* Limb oriented combat */
static int hit_at;    /* Limb oriented combat */
static int power;
static int dex_wc;    /* How much a weapon needs you to be dexterous */
static int head_ac, body_ac, arm_ac, leg_ac;  /* Limb oriented combat */
static status can_detect_invis; /* True when living detects invisible */
static int alt_hunting_time;  /* see alt_hunter */

 /* New Combat Variables */
static int weapon_skill,hit_bonus,damage_bonus, hit_modifier, damage_modifier;
static mapping hit_armour; /* Mapping of how hard to hit */
static mapping dam_armour; /* Mapping of how damage gets affected */
static object second_weapon; /* object for weapon in secondary hand */
string attack_method; /* How you are trying to attack */
private static int armour_applied; /* Has armour been applied to an npc */
                                   /* To reduce CPU costs */
#if DEBUG_COMBAT
int cost;
#endif

/* Non static Variables */
mapping skill_pts; /* For how much skill you have */

/*
 * The following routines are defined for usage:
 * stop_fight       Stop a fight.
 * hit_player       Called when fighting to damage us.
 * attacked_by      Tells us who is attacking us.
 * stop_wielding    Called when we unwield a weapon.
 * stop_wearing     Called when we remove armour.
 * attack           Should be called from heart_beat.  Will maintain attack.
 * query_attack     Return who we are fighting.
 * wield            Called by weapons.
 * wear             Called by armour.
 * attack_object    Called when starting to attack an object.
 * query_spell_points  Return how much spell points the character has.
 * reduce_hit_point    Reduce hit points, but not below 0.
 */

// ----------------------------------------------------------------------------

 /* For base combat effects.
    hit_bonus       - for making it easier/harder to hit
    damage_bonus    - for dealing more/less damage in a hit
    damage_modifier - for changing the damage done to this object
    hit_modified    - for modifying chance to hit this object
  */

void set_to_hit_bonus( int s ) {
  hit_bonus = s;
}

void set_damage_bonus( int s ) {
  damage_bonus = s;
}

void set_damage_modifier( int s ) {
  damage_modifier = s;
}

void set_hit_modifier( int s ) {
  hit_modifier = s;
}

// Queries call hooks, for outside modification
varargs int query_to_hit_modifier(object weapon, int target, string dam_type) {
  int ret;

  ret = hit_modifier;
  if ( attack_defend == (int)BERSERK )
#ifdef VERSION_INCREASED_MORTAL_LEVEL
    ret -= random( 3 + ((int)query_level()>MORTAL_MAXIMUM?MORTAL_MAXIMUM:(int)query_level()) / 2 );
#else
    ret -= random( 3 + (int)query_level() / 2 );
#endif
  ret += hook( "hit_modifier", ({ hit_modifier, weapon, target, dam_type }) );

  return ret;
}

int query_weapon_skill( object weapon ) {
  string abi;
  int ret;

  if ( objectp( weapon ) ) {
    abi = (string)weapon->query_weapon_type();
  } else {
    abi = "Unarmed";
  }

  ret = query_skill( abi );

  return ret;
}

varargs int query_to_hit_bonus(object weapon, int target, string dam_type) {
  int ret;

  ret = hit_bonus;
  ret += hook( "hit_bonus", ({ hit_bonus, weapon, target, dam_type }) );

  return ret;
}

varargs int query_damage_bonus(object weapon, int target, string dam_type) {
  int ret;

  ret = damage_bonus;
  ret += hook( "damage_bonus", ({ damage_bonus, weapon, target, dam_type }) );

  return ret;
}

int query_to_hit_armour( int target, string weap_type ) {
  // set armour on npcs now that it's needed
  if ( !armour_applied && query_npc() )
    apply_all_armour();

  if ( !hit_armour )
    return 0;

  --target;
  if ( target < 0 || target > 3 )
    return 0;

  if ( hit_armour[weap_type] )
    return hit_armour[weap_type][target];

  return 0;
}

int query_to_damage_armour( int target, string weap_type ) {
  if ( !armour_applied && query_npc() )
    apply_all_armour();

  if ( !dam_armour )
    return 0;

  --target;
  if ( target < 0 || target > 3 )
    return 0;

  if ( dam_armour[weap_type] )
    return dam_armour[weap_type][target];

  return 0;
}

varargs int query_to_damage_modifier(object weapon, int target,
                                     string dam_type) {
  int ret;

  ret = damage_modifier;
  ret += hook( "damage_modifier",
                ({ damage_modifier, weapon, target, dam_type }) );
  return ret;
}

int average_ac( int target ) {
  int av_ac, i, ac_i;
  int tot_ac_pos;
  string *indx;

  av_ac = 0;
  if ( !dam_armour )
    apply_all_armour();

  indx = m_indices( dam_armour );
  if ( sizeof( indx ) ) {
    for( i = 0 ; i < sizeof( indx ) ; ++i ) {
      if ( (ac_i = query_to_damage_armour( target + 1, indx[i] )) != -1 ) {
        ++tot_ac_pos;
        av_ac += ac_i;
      }
    }

    if ( !tot_ac_pos )
      tot_ac_pos = 1;

    av_ac = av_ac / tot_ac_pos;
  }

  return av_ac;
}

int query_resistance( int dam, string dam_type ) {
  string d_type;
  int amt;

  d_type = dam_type;
  if ( d_type == "saving" )
    d_type = "magic";

  if ( d_type == "physical" )
    return (dam - random( average_ac( 1 ) ) / 2);

  amt = query_ability( d_type +" resistance" );
  if ( random( 100 ) < amt ) {
    if ( d_type == "magic" )
      return 0;
    dam -= random( dam );
  }

  return dam;
}

mapping query_hit_armour() {
  return hit_armour ? hit_armour : ([]);
}

mapping query_dam_armour() {
  return dam_armour ? dam_armour : ([]);
}

int query_primary_attack() {
  int ret;

  ret = 1 + hook( "primary_attack", ({}) );

  return ret;
}

int query_penalty( object weapon ) {
  int pen;

  pen = 3 * (int)this_object()->query_intoxination() /
    MAX_OB_INTOX( this_object() );

  if ( second_weapon && weapon ) {
    pen += 2;
    pen -= query_skill( "Two Weapon" )/6;
    pen -= query_weapon_skill( weapon ) / 10;
    pen += 7 * (int)weapon->query_weight();
    pen += (2 * (int)weapon->query_difficulty()) / 4;

/* Old Calc for penalty
    pen += 2;
    pen -= query_skill( "two weapon" ) / 8;
    pen -= query_weapon_skill( weapon ) / 13;
    pen += 5 * (int)weapon->query_weight();
    pen += (3 * (int)weapon->query_difficulty()) / 4;
*/

    if ( weapon == wielded_weapon )
      pen /= 2;
    if ( pen < 0 )
      pen = 0;

    return pen;
  }

  return pen;
}

int query_second_attack() {
  int ret;
  int t_skill;

  t_skill = 5 + query_skill( "Two Weapon" ) / 2;
  if ( BERSERK == attack_defend )
    t_skill += ((int)query_level()>WIZ_ALLOWABLE?WIZ_ALLOWABLE:(int)query_level());

  if ( random( 100 ) < t_skill )
    ret = 1;

  ret += hook( "second_attack", ({ ret }) );

//  Old method
//  ret = 1 + hook( "second_attack", ({}) );

  return ret;
}

int set_attack_method( string s ) {
  if ( member_array( s, ATTACKS ) != -1 ) {
    if ( s == "none" || s == "random" ) // 'random' option Added Jul 07 by Howitzer
      s = "random";

    attack_method = s;
    write( "You change your attack method to "+ s +".\n" );

    return 1;
  }

  notify_fail( "You do not think you can do that.\n" );
  return 0;
}

string query_attack_method() {
  return attack_method ? attack_method : "random";
}

mixed query_no_attack( mixed x ) {
  mixed ret;

  ret = x;
  ret += hook( "no_attack", ({ x }) );

  return ret;
}

object query_second_weapon() {
  return second_weapon;
}

mapping query_skill_pts() {
  return skill_pts;
}

void set_skill_pts( string cate, int val ) {
  if ( !skill_pts )
    skill_pts = ([]);

  skill_pts[cate] = val;
  if ( val )
    set_skill( cate, SKILL_D->calc_skill( this_object(), cate, val ) );
  else
    remove_skill( cate );
}

int query_can_hit( object weapon, int weap_sk, int hit_at, string dam_type,
    int penalty, int whit ) {
  int to_hit;

  if ( weapon ) {
    to_hit = random( 3 * tmp_Dex ) +
             (int)weapon->query_to_hit_bonus(hit_at, dam_type) +
             3 * ((whit + 1) / 2) +
             random( weap_sk / 3 + 5 + weap_sk * weap_sk / 75 ) +
             query_to_hit_bonus(weapon, hit_at, dam_type) -
             (random( (int)attacker_ob->query_dex() )) -
             (int)attacker_ob->query_to_hit_modifier(weapon, hit_at, dam_type) -
             random((int)attacker_ob->query_to_hit_armour(hit_at, dam_type)) -
             5 * (random( (int)weapon->query_weight() - 1 )) -
             (30 * random((int)weapon->query_difficulty())) / 10 - 2 * penalty;
  } else {
    to_hit = random( 3 * tmp_Dex + weap_sk / 2 ) +
             (int)query_to_hit_bonus(0, hit_at, dam_type) -
             random( (int)attacker_ob->query_dex() ) -
             (int)attacker_ob->query_to_hit_modifier(0, hit_at, dam_type) -
             random((int)attacker_ob->query_to_hit_armour(hit_at, dam_type)) +
             3 * ((whit + 1) / 2);
  }

  return to_hit;
}

// End combat queries
// ----------------------------------------------------------------------------

int set_hit_messages( string *arr ) {
  if ( arr && sizeof( arr ) != 14 )
    return 0;

  hit_messages = arr;
  return 1;
}

/*
 * This function is called from other players when they want to damage us.
 * We return how much damage we received, which will change the attacker's
 * score.  This routine is probably called from heart_beat() from another
 * player.  Compare this function to reduce_hit_point(dam).
 */
varargs int hit_player_damage(int dam,int target,string damage_type) {
  object corpse;
  object temp_attack;
  mixed dam_key;
  object cur_claimer;
  int tmp;

  if ( !attacker_ob )
    set_heart_beat( 1 );

  if ( !attacker_ob && this_player() != this_object() ) {
    attacker_ob = this_player();
    this_object()->start_fight();
  }
/* removed 6Jul94 by Dawg, hoping to fix 'friendly fire' bug
  else if(!alt_attacker_ob && attacker_ob != this_player() &&
           this_player() != this_object()) alt_attacker_ob = this_player();
*/

  if ( this_object()->query_is_statue() ) {
    if ( this_player() != this_object() ) // not self-inflicted
      write( "Time has petrified "+ query_objective() +".\n" );

    stop_fight();
    this_player()->stop_fight();

    return 0;
  }

  /* Added at Wrath's request on 8-16-95 by Leoz. You can now stop people from
     dying. */

  if ( dead )
    return 0;

  if ( !target )
    target = BODY;

 /* ***********************************
  if(target == HEAD) ac = head_ac;
  else if(target==BODY) ac = body_ac;
  else if(target==ARM ) ac = arm_ac;
  else if(target==LEG ) ac = leg_ac;
  else log_file("BAD_HIT", this_player()->query_name()+ "\n");
  if(attack_defend == BERSERK) ac -= (4 + query_level() / 2 - level / 4);
  if((dam -= random(((ac < 0)?0:ac) / 2)) < 0) return 0;
  ************************************ */

  if ( !damage_type )
    damage_type = "crush";

  // For each 3 con above level npcs gain 2AC (-1 damage) to physical hits.
  // +1 in random so even a 1 change can get to random(4)/3 so a chance of +/-1
  if (query_npc() && (tmp = query_con() - query_level())) {
    if (tmp > 0) // bonus con reduces damage
      dam -= random(2 * (tmp+1))/3;
    else // low con increases damage
      dam += random(2 * (-1 * tmp + 1))/3;
  }

  dam = query_resistance( dam, damage_type );
  dam += hook( "hit_player", ({ dam, target, damage_type, previous_object() }) );

  if ( dam >= hit_point && environment() && (temp_attack = attacker_ob) &&
       environment()->stop_death( this_object(), attacker_ob ) ) {
    catch( log_file( "STOP_DEATH",
      get_f_string( ctime() +" : "+ this_object()->query_name() +
      " saved from "+ temp_attack->query_name() +" in "+
      file_name( environment() ) +".", 0, 20 ) ) );

    return 0;
  }

  if ( query_level() >= APPRENTICE && !query_npc() && dam >= hit_point ) {
    tell_object( this_object(), "Your wizardhood protects you from death.\n" );
    return 0;
  }

  ks_handle_attack(this_player(),attacker_ob, dam);

  if ( dam <= 0 )
    return 0;

  if ( dam > hit_point + 1 )
    dam = hit_point + 1;

  hook("hit_player_damage_taken", ({ dam, target, damage_type, previous_object() }));

  if ( this_player() && this_player() != this_object() ) {
    int dam_factor;
    dam_factor = 100;

    if ( this_player()->query_is_player() ) {
      dam_key = (string)this_player()->query_real_name();
    } else if (dam_key = (string)this_player()->query_xp_conduit() ) {
      dam_factor = (int)this_player()->query_xp_conduit_factor();
    } else {
      dam_key = this_player();
    }

    add_m_damagers(dam_key, (dam * dam_factor) / 100);

    if ( query_m_damagers(dam_key) <= 0 )
      del_m_damagers(dam_key);
  }

  if ( (hit_point -= dam) > query_max_hp() )
    hit_point = query_max_hp();

  if (hit_point >=0) {
    if ( !query_npc() ) // else if ( reporting ) - changed by Greyson
      this_object()->report_hp_sp();
    return dam;
  }

  // FROM HERE ON HP < 0, so NPC should be dying.

  if ( !query_npc() && !query_ip_number( this_object() ) ) {
    write( query_name() +" is not here. You cannot kill a player who is "
      "not logged in.\n" );

    hit_point = 20;
    stop_fight();
    this_player()->stop_fight();

    return 0;
  }

  stop_follow();

  if ( hunter )
    hunter->stop_hunter();
  if ( alt_hunter )
    alt_hunter->stop_hunter();

  hunter = alt_hunter = hunted = 0;

 /* Added 10-Jul-94 by Ricochet */
 /* this prevents a monster from a plain kill by calling hit_player()
   some monster have a coded "suicide" by hit_player(xyz) like greysons
   there is no attack_ob in that case..  putting in another if...
   03.08.94 Saerdan
  */
  if ( attacker_ob ) {
    if ( attacker_ob->query_npc() && attacker_ob->query_killer_ob() ) {
      if ( attacker_ob->monster_killer( this_object() ) ) {
        // place a log file here if desired
        hit_point = 10;
        attacker_ob->attack_object( 0 );
        attacker_ob = 0;
        return 1;
      }
    }
  }

  dead = 1;

  say( query_name() +" "+ (string)this_object()->query_died_string() +".\n");

  if ( !query_npc() ) {
    "secure/tool"->adm_log( "DIED", query_real_name() +" -> "+
     (query_exp() / 3 ) +"["+
     ((environment()) ? file_name( environment() ) : "???") +"]" );
  } else {
    "adm/tools/watch"->monster_died( this_object(), attacker_ob );
    ks_handle_death();
  }

  // Cap XP Loss to 10 mill.
  // If it comes from Villa, they are leaving rogue class so lose 1/3
  if ((int)this_object()->query_npc() ||
    extract(file_name(previous_object()),0,25) == "w/greyson/rogue/npc/villa")
            this_object()->add_exp( -1 * (int)this_object()->query_exp() / 3 );
  else
    // sub 20M/pi * atan(exp*pi/60M)
    this_object()->add_exp(-1 * (((int)((20000000/3.1415926) * atan((float)((int)this_object()->query_exp()*3.1415926/60000000)))) +
      hook("death_xp_loss",
      ({ ((int)((20000000/3.1415926) * atan((float)((int)this_object()->query_exp()*3.1415926/60000000))))}))) );

  hit_point = 10;

  if ( attacker_ob && attacker_ob->query_party_name() )
    attacker_ob->share_align( ADJ_ALIGNMENT( query_alignment() ) );

  // In claim.c
  award_exp();

  if ( attacker_ob ) {
    if ( attacker_ob->query_party_name() ) {
      //Mordecai Oct09: Added call to query_killed_string() in this object,
      //  to allow a monster to specify a string other than "killed", such
      //  as "defeated".
      attacker_ob->party_killed( query_name(), query_killed_string() );
    } else {
      attacker_ob->add_alignment( ADJ_ALIGNMENT( query_alignment() ) +
                                  (int)attacker_ob->hook("align_bonus", ({ADJ_ALIGNMENT( query_alignment() )})));
      tell_object( attacker_ob, "You "+ (string)query_killed_string() +" "+
        query_name() +".\n" );
    }
    say( attacker_ob->query_name() +" "+ (string)query_killed_string() +" "+
      query_name() +".\n", attacker_ob );
  }

  // Who has done the most damage
  cur_claimer = kill_claimer();
  // If we dont have the highest damager present etc, then set to attacker
  if (!cur_claimer) cur_claimer = attacker_ob;
  clear_m_damagers();

  /* Some undeads vanish when killed - Trixx */
  if ( this_object()->query_vanish() ) {
    string vanish_msg;

    vanish_msg = (string)this_object()->query_vanish_msg();
    if ( vanish_msg ) {
      if ( ('#' == vanish_msg[0]) && ('#' == vanish_msg[1]) )
        call_other( this_object(), vanish_msg[2..] );
      else
        tell_room( environment(), get_f_string( vanish_msg ) );
    }

    transfer_all_to(environment());
  } else {
    if ( corpsefile ) {
      corpse = clone_object( corpsefile );
    } else {
      corpse = clone_object( "obj/corpse" );
      if ( !interactive( this_object() ) )
        corpse->set_name( query_name() );
      else
        corpse->set_name( query_real_name() );

      corpse->set_str( Str );
      corpse->set_int( Int );
      corpse->set_con( Con );
      corpse->set_dex( Dex );
      corpse->set_wis( Wis );
      corpse->set_race( query_real_race() );
      if((int)this_object()->query_undead())
        corpse->set_undead( 1 );
      else
        corpse->set_undead( 0 );
      corpse->set_level( query_level() );
      corpse->set_alignment( query_alignment() );
      corpse->set_head_str( query_head_str() );
      corpse->set_body_str( query_body_str() );
      corpse->set_leg_str( query_leg_str() );
      corpse->set_arm_str( query_arm_str() );
      corpse->set_hand_str( query_hand_str() );
      corpse->set_foot_str( query_foot_str() );
      corpse->set_exp( query_exp() );
      corpse->set_wc( query_wc() );
      corpse->set_max_hp( query_max_hp() );
      if ((mixed)this_object()->query_spells()) {
        corpse->set_spell_damage((mixed)this_object()->query_spells());
      }
    }

    if(corpse) {
      transfer_all_to( corpse, 0 );
      //Note: Move the corpse before setting claim (init in the corpse will
      //reset the claim flag when it comes into contact with the claimer).
      corpse->move( environment() );
      if (corpse && cur_claimer)
        corpse->claimed_by(cur_claimer);
    } else {
      //Note: Some NPCs use destructing corpses.  Let's log it but otherwise
      //end peaceably.
      log_file("COMBAT", sprintf("Bad corpse file: %s in %s\n",
        corpsefile ? corpsefile : "0",
        file_name()));
      transfer_all_to(environment()); // Behave like with vanish. Fir
    }
  }

  if (cur_claimer && cur_claimer->query_is_player()) {
    cur_claimer->set_misc( "kills",
                   (int)cur_claimer->query_misc( "kills" ) + 1 );
  }

  if ( cur_claimer ) {
    object ok;

  // call all versions of owner_killer rather than just the first
    map_array(presents("\nowner_killer",cur_claimer),
        lambda(({'x}), ({#'call_other, 'x, "owner_killer", this_object() }) )); //'
  //      if ( ok = present( "\nowner_killer", attacker_ob ) )
  //        ok->owner_killer( this_object() );
  }

  if ( !this_object()->second_life() )
    destruct( this_object() );

  if ( !query_npc() ) {
    SKILL_D->reduce_skill( this_object() );

    save_me( 0 );
  }

  return dam;
}

static void _award_kill_exp( mixed key, int dam, int total_dam ) {
  if ( query_debug() ) {
    log_file( "NEW_EXP", "_award_kill_exp(" + XFUN->variable_to_string( key ) +
      ", "+ XFUN->variable_to_string( dam ) +", "+
      XFUN->variable_to_string( total_dam ) +") in "+ file_name() +" ("+
      query_exp() +")\n" );
  }

  if ( key && (objectp(key) || (stringp(key) && (key = find_player(key)))) ) {
    if ( dam > 0 ) {
      int exp_share;
      // Darn integer overflow with high exp & npcs with high hps.
      // Theryn, 4 Nov 2006.
      if ( WIZ_ALLOWABLE <= (int)this_object()->query_level() ) {
        exp_share = (int)query_exp() / total_dam * dam / XP_FACTOR;
      } else {
        exp_share = (int)query_exp() * dam;
        if ( exp_share < 0 )
          exp_share = 0x7FFFFFFF;
        exp_share = (exp_share / total_dam) / XP_FACTOR;
      }

      // to prevent a net gain of xp when players kill each other
      if(this_object()->query_is_player())
      exp_share = 0;

      // Share xp with party, but only if they are part of the party xp share
      if ( key->query_party_name() && key->query_party_sharing_xp())
        key->share_exp( exp_share );
      else
        key->add_exp( exp_share );

      if ( (1000 < exp_share) && (query_debug() || LOG_D->query_log_exp()) ) {
        log_file( "NEW_EXP", ctime()[4..15] +" "+
          (string)key->query_real_name() +" "+ exp_share +" for "+
          (string)this_object()->query_real_name() +" ["+
          dam +"/"+ total_dam +" of "+ query_exp() +"]\n" );
      }
    }
  }
}

varargs int hit_player( int dam, int target, string damage_type ) {
  int new_dam;

  new_dam = dam - random( average_ac( target ) );

  return hit_player_damage( new_dam, target, damage_type );
}

/*
 * This routine is called when we are attacked by someone.
 */
void attacked_by(object ob) {
#if 1
  if ( attacker_ob ) {
    if ( ob && (ob != attacker_ob) ) {
      string the_party;

// Obsolete logging ... removed by Adinos - July 2001
//
//      if ( (interactive( ob )) && (interactive( attacker_ob )) &&
//           (environment( attacker_ob ) == environment()) &&
//           ((!(the_party = (string)attacker_ob->query_party_name())) ||
//            (the_party != (string)ob->query_party_name())) ) {
//        log_file( "KILL_STEALS", ctime()[4..15] +" "+
//          (string)ob->query_real_name() +" stole "+
//          (string)this_object()->query_name() +" from "+
//          (string)attacker_ob->query_real_name() +"\n" );
//      }

      if ( !alt_attacker_ob )
        alt_attacker_ob = ob;
    }
  } else {
    attacker_ob = ob;
    set_heart_beat( 1 );
  }
#else
  if ( !attacker_ob ) {
    attacker_ob = ob;
    set_heart_beat( 1 );
    return;
  }

  if ( attacker_ob == ob )
    return; /* added by Dawg 6Jul94 */
  if ( !alt_attacker_ob )
    alt_attacker_ob = ob;
#endif
}

void stop_wielding( object ob ) {
#if BRAWL_IN_COMBAT
 object brawl;
#endif
  if ( !wielded_weapon && !second_weapon )
    return;
  if ( ob && ob != wielded_weapon && ob!= second_weapon )
    return;

  if ( !ob ) {
    if ( wielded_weapon )
      wielded_weapon->un_wield( dead );
    wielded_weapon = 0;

    return;
  }

  if ( ob == wielded_weapon ) {
    wielded_weapon->un_wield( dead );
    wielded_weapon = parry_ac = dex_wc = hit_messages = 0;

#if BRAWL_IN_COMBAT
    brawl = present("\nbrawl_skill",this_object());
    if (brawl)
      hit_messages = (string *)brawl->query_hit_messages();
#endif

    if ( query_is_player() )
      weapon_class = WEAPON_CLASS_OF_HANDS;
  } else {
    second_weapon->un_wield( dead );
    second_weapon = 0;
  }

//  calculate_power();
}

int check_is_armour( object a ) {
  return a->query_worn() ? 1 : 0;
}

/* Apply the armour values to the object */
void apply_armour( object a ) {
  int i, j;
  string *idx;
  mapping a_hit_arm, a_dam_arm;

  a_hit_arm = (mapping)a->query_to_hit_armour();
  if ( !a_hit_arm || !mappingp( a_hit_arm ) )
    return 0;
  a_dam_arm = (mapping)a->query_to_damage_armour();
  if ( !a_dam_arm || !mappingp( a_dam_arm ) )
    return 0;

  if ( !hit_armour )
    hit_armour = ([]);

  idx = m_indices( a_hit_arm );

  for ( i = 0 ; i < sizeof( idx ) ; ++i ) {
    for ( j = 0 ; j < sizeof( a_hit_arm[idx[i]] ) ; j += 2 ) {
      if ( hit_armour[idx[i]] ) {
        hit_armour[idx[i]][j / 2] += (int)a_hit_arm[idx[i]][j];
        hit_armour[idx[i]][j / 2] += (int)a_hit_arm[idx[i]][j + 1];
      } else {
        hit_armour[idx[i]] = ({ 0, 0, 0, 0 });
        hit_armour[idx[i]][j / 2] += (int)a_hit_arm[idx[i]][j];
        hit_armour[idx[i]][j / 2] += (int)a_hit_arm[idx[i]][j + 1];
      }
    }
  }

  if ( !dam_armour )
    dam_armour = ([]);

  idx = m_indices( a_dam_arm );

  for ( i = 0 ; i < sizeof( idx ) ; ++i ) {
    for ( j =  0 ; j < sizeof( a_dam_arm[idx[i]] ) ; j += 2 ) {
      if ( dam_armour[idx[i]] ) {
        dam_armour[idx[i]][j / 2] += a_dam_arm[idx[i]][j];
        dam_armour[idx[i]][j / 2] += a_dam_arm[idx[i]][j + 1];
      } else {
        dam_armour[idx[i]] = ({ 0, 0, 0, 0 });
        dam_armour[idx[i]][j / 2] += a_dam_arm[idx[i]][j];
        dam_armour[idx[i]][j / 2] += a_dam_arm[idx[i]][j + 1];
      }
    }
  }
}

/* Apply all the armour to the object as well as base effects from
   set_<x>_ac()'s */
void apply_all_armour() {
  object *arm;
  string dam_type;
  int i;

  arm = filter_array( all_inventory( this_object() ), "check_is_armour",
    this_object() );

  if ( !hit_armour )
    hit_armour = ([]);
  if ( !dam_armour )
    dam_armour = ([]);

  for ( i = 0 ; i < sizeof( DAMAGE_TYPES ) ; ++i ) {
    dam_type = DAMAGE_TYPES[i];

    if ( !hit_armour[dam_type] )
      hit_armour[dam_type] = ({ 0, 0, 0, 0 });
    if ( !dam_armour[dam_type] )
      dam_armour[dam_type] = ({ 0, 0, 0, 0 });

    hit_armour[dam_type][0] = head_ac;
    dam_armour[dam_type][0] = head_ac;
    hit_armour[dam_type][1] = body_ac;
    dam_armour[dam_type][1] = body_ac;
    hit_armour[dam_type][2] = arm_ac;
    dam_armour[dam_type][2] = arm_ac;
    hit_armour[dam_type][3] = leg_ac;
    dam_armour[dam_type][3] = leg_ac;
  }

  for ( i = 0 ; i < sizeof( arm ) ; ++i ) {
    apply_armour( arm[i] );
  }

  armour_applied = 1;
}

void wear_armour( object a ) {
/* Old Backward Compatibility code removed to use less CPU
  head_ac += ((int)a->query_head_ac()  + (int)a->query_neck_ac());
  body_ac += (a->query_lbody_ac() + a->query_hbody_ac());
  leg_ac  += (a->query_leg_ac()   + a->query_foot_ac());
  arm_ac  += (a->query_arm_ac()   + a->query_hand_ac());
  head_ac += (int)a->query_special_head_ac();
  body_ac += (int)a->query_special_body_ac();
  leg_ac  += (int)a->query_special_leg_ac();
  arm_ac  += (int)a->query_special_arm_ac();
*/

/* Ok only apply armour if it's a player. Otherwise use apply_armour()
   to get an immediate effect. */

  if ( !query_npc() || armour_applied ) {
    apply_armour( a );
  }
}

void remove_armour( object a ) {
  int i, j;
  string *idx;
  mapping a_hit_arm, a_dam_arm;

/* Old remove coded commented out to save some CPU
  head_ac -= (a->query_head_ac()  + a->query_neck_ac());
  body_ac -= (a->query_lbody_ac() + a->query_hbody_ac());
  leg_ac  -= (a->query_leg_ac()   + a->query_foot_ac());
  arm_ac  -= (a->query_arm_ac()   + a->query_hand_ac());
  head_ac -= (int)a->query_special_head_ac();
  body_ac -= (int)a->query_special_body_ac();
  leg_ac  -= (int)a->query_special_leg_ac();
  arm_ac  -= (int)a->query_special_arm_ac();
*/

  if ( !armour_applied && query_npc() )
    apply_all_armour();

  if ( !hit_armour )
    hit_armour = ([]);

  a_hit_arm = (mapping)a->query_to_hit_armour();
  if ( a_hit_arm && mappingp( a_hit_arm ) ) {
    idx = m_indices( a_hit_arm );

    for ( i = 0 ; i < sizeof( idx ) ; ++i ) {
      for ( j = 0 ; j < sizeof( a_hit_arm[idx[i]] ) ; j += 2 ) {
        if ( hit_armour[idx[i]] ) {
          hit_armour[idx[i]][j / 2] -= a_hit_arm[idx[i]][j];
          hit_armour[idx[i]][j / 2] -= a_hit_arm[idx[i]][j + 1];
        } else {
          hit_armour[idx[i]] = ({ 0, 0, 0, 0 });
          hit_armour[idx[i]][j / 2] -= a_hit_arm[idx[i]][j];
          hit_armour[idx[i]][j / 2] -= a_hit_arm[idx[i]][j + 1];
        }
      }
    }
  }

  if ( !dam_armour )
    dam_armour = ([]);

  a_dam_arm = (mapping)a->query_to_damage_armour();
  if ( a_dam_arm && mappingp( a_dam_arm ) ) {
    idx = m_indices( a_dam_arm );

    for ( i = 0 ; i < sizeof( idx ) ; ++i ) {
      for ( j = 0 ; j < sizeof( a_dam_arm[idx[i]] ) ; j += 2 ) {
        if ( dam_armour[idx[i]] ) {
          dam_armour[idx[i]][j / 2] -= a_dam_arm[idx[i]][j];
          dam_armour[idx[i]][j / 2] -= a_dam_arm[idx[i]][j + 1];
        } else {
          dam_armour[idx[i]] = ({ 0, 0, 0, 0 });
          dam_armour[idx[i]][j / 2] -= a_dam_arm[idx[i]][j];
          dam_armour[idx[i]][j / 2] -= a_dam_arm[idx[i]][j + 1];
        }
      }
    }
  }
}

void wear_shield( object s ) {
  block_ac = (int)s->query_block_ac();
  worn_shield = s;
}

void remove_shield( object s ) {
  block_ac = worn_shield = 0;
}

/*
 * Return true if there still is a fight.
 */
int attack() {
  int tmp, penalty, i;
  string name_of_attacker;

  if ( attacker_ob == this_object() ) // added by Greyson, 13-Feb-95
    attacker_ob = 0;

  if ( !attacker_ob ) {
    if ( !alt_attacker_ob )
      return 0;

    attacker_ob = alt_attacker_ob;
    alt_attacker_ob = 0; /* added by Dawg 6Jul94 */
  }

#if DEBUG_COMBAT
  cost = get_eval_cost();
#endif

  name_of_attacker = (string)attacker_ob->query_name();
  if ( !name_of_attacker || name_of_attacker == NAME_OF_GHOST ||
       environment( attacker_ob ) != environment() ) {
    if ( !hunter && name_of_attacker && !attacker_ob->query_ghost() ) {
      set_hunted( attacker_ob );
      tell_object( this_object(), "You are now hunting "+ name_of_attacker +
        ".\n" );
    }

    attacker_ob = 0;
    if ( !alt_attacker_ob )
      return 0;

    attacker_ob = alt_attacker_ob;
    alt_attacker_ob = 0;

    if ( attack() ) {
      if ( attacker_ob ) {
        tell_object( this_object(), "You turn to attack "+
          attacker_ob->query_name() +".\n" );
      }

      return 1;
    }

    return 0;
  }

  if ( query_no_attack( attack_disabled ) )
    return 1;

#if DEBUG_COMBAT
  EV_COST->cost1( cost - get_eval_cost() );
#endif

  tmp = query_primary_attack();
  penalty = query_penalty( wielded_weapon );
  for ( i = 0 ; i < tmp ; ++i ) {
    hit_with_weapon( wielded_weapon, penalty );
  }

  if ( second_weapon ) {
    tmp = query_second_attack();
    penalty = query_penalty( second_weapon );
    for ( i = 0 ; i < tmp ; ++i ) {
      hit_with_weapon( second_weapon, penalty );
    }
  }

#if DEBUG_COMBAT
  EV_COST->cost10( cost - get_eval_cost() );
#endif
}

/* Called for each attack with that weapon */
int hit_with_weapon(object weapon, int penalty) {
  int tmp;
  mixed whit;
  string name_of_attacker, dam_type;
  mapping weap_type;
  int to_hit,i,chce,weap_sk,tmp_int; /* tmp_int will be a reused variable */
  string *idx1;
  int str_dam,weap_dam,armour_dam;

  if ( !attacker_ob )
    return 1; /* Must have died on an earlier hit */

  name_of_attacker = (string)attacker_ob->query_name();

  // Added by Paldin for unarmed attack types
  weap_type =  hook("unarmed_attack_type");

  if ( weapon ) {
    whit = (int)weapon->hit( attacker_ob );
    weap_type = (mapping)weapon->query_damage_type();

    if ( !attacker_ob ) {
      /* allow for weapon_hits to do damage
      tell_object( this_object(), "CRACK!\nYour weapon broke!\n" );
      log_file( "BAD_WEAPON", weapon->short() +", "+ getuid( weapon ) +
        " XX!\n" );
      destruct( wielded_weapon );
      weapon_class = parry_ac = dex_wc = hit_messages = 0;
      calculate_power();
      */
      return 1;
    }
  }

#if DEBUG_COMBAT
  EV_COST->cost2( cost - get_eval_cost() );
#endif

  if ( !mappingp( weap_type ) ) {
    /* Unknown weapon or barehanded */
    weap_type = ([ "crush" : 100 ]);
  }

  weap_sk = query_weapon_skill( weapon );
  if ( (weap_sk - 20) > random( 100 ) && weap_type[attack_method] ) {
    dam_type = attack_method;
  } else {
    /* attack style is random */
    idx1 = m_indices( weap_type );
    chce = 0;
    tmp_int = random( 100 );

    for ( i = sizeof( idx1 ) ; i-- && !dam_type ; ) {
      chce += weap_type[idx1[i]];
      if ( tmp_int < chce ) {
        dam_type = idx1[i];
      }
    }
  }

#if DEBUG_COMBAT
  EV_COST->cost3( cost - get_eval_cost() );
#endif

  if ( whit != "miss" ) {
    if ( aiming_at == RANDOM ) {
      hit_at = random( 4 ) + 1;
    } else {
      if ( random( tmp_Dex ) > 3 )
        hit_at = aiming_at;
      else
        hit_at = (1 + random( 4 ));
    }

    hit_at += hook("aim_modifier", ({ hit_at }));

    if ( (hit_at == (int)HEAD &&
          (int)attacker_ob->query_to_hit_armour( HEAD, "crush" ) == -1) ||
         (hit_at == (int)ARM &&
          (int)attacker_ob->query_to_hit_armour( ARM, "crush" ) == -1) ||
         (hit_at == (int)LEG &&
          (int)attacker_ob->query_to_hit_armour( LEG, "crush" ) == -1) ) {
      if ( aiming_at == hit_at ) {
        tell_object( this_object(), attacker_ob->query_name() +
          " does not have the body part you were aiming at.\n" );
        tell_object( this_object(), "Aiming at: "+
          attacker_ob->query_body_str() +"\n" );

        aiming_at = BODY;
      }

      hit_at = BODY;
    }

#if DEBUG_COMBAT
    EV_COST->cost4( cost - get_eval_cost() );
#endif

    to_hit = query_can_hit( weapon, weap_sk, hit_at, dam_type, penalty, whit );

#if DEBUG_COMBAT
    EV_COST->cost5( cost - get_eval_cost() );
#endif

    tmp_int = (int)query_body_position();
    to_hit -= tmp_int * 2;

    i = (int)attacker_ob->query_body_position(); /* Ok Overloading var i */
    to_hit += i * 2;

    if ( to_hit > 0 )
      to_hit = (int)attacker_ob->defend_attack( to_hit, hit_at );
    else
      to_hit = 0;

#if DEBUG_COMBAT
    EV_COST->cost6( cost - get_eval_cost() );
#endif

    if ( to_hit != 0 ) {
      str_dam = random( tmp_Str ) / 2;

      if ( weapon ) {
        weap_dam = (int)weapon->query_damage( dam_type );
        if ( !(int)query_is_player() )
          weap_dam /= 2;

        armour_dam = 2 *
          random((int)attacker_ob->query_to_damage_armour(hit_at, dam_type))/3;

        tmp = str_dam +
          weap_dam +
          (whit / 2) +
          random( (int)query_weapon_class() ) +
          (int)query_damage_bonus(weapon, hit_at, dam_type) -
          armour_dam -
          (int)attacker_ob->query_to_damage_modifier(weapon, hit_at, dam_type) -
          penalty / 2;
      } else {
        tmp = random( tmp_Str ) / 2 +
          random( (int)query_weapon_class() + 1 ) +
          (int)query_damage_bonus(0, hit_at, dam_type) -
          random(1*(int)attacker_ob->query_to_damage_armour(hit_at,dam_type)/2) -
          (int)attacker_ob->query_to_damage_modifier(0, hit_at, dam_type) +
          (whit / 2) -
          penalty / 2;
      }

#if DEBUG_COMBAT
      EV_COST->cost7( cost - get_eval_cost() );
#endif

      /* Bonus to damage only from berserk .. for now? */
      /* Extra berserk damage if weapon is primary weapon */
      if ( attack_defend == BERSERK && weapon==wielded_weapon)
#ifdef VERSION_INCREASED_MORTAL_LEVEL
          tmp += random( (1 + ((int)query_level()>WIZ_ALLOWABLE?WIZ_ALLOWABLE:(int)query_level()) / 2 + tmp_Str / 5) );
#else
          tmp += random( (1 + (int)query_level() / 2 + tmp_Str / 5) );
#endif

      tmp -= tmp_int * 2; /* Body_position */
      tmp += i * 2;

      if ( tmp < 0 )
        tmp = 0;

      if ( tmp != 0 ) {
        // Need magical weapons on undeads - Trixx
        if ( attacker_ob->query_undead() ) {
          if ( weapon &&
               weapon->query_magical_weapon() ) {
            tmp = tmp * 115 / 100;
          } else {
            tmp = tmp * 85 / 100;
          }
        }

#if DEBUG_COMBAT
        tell_room( environment(), "Damage before defend: "+ tmp +" To hit: "+
          to_hit +"\n" );
#endif

        // Added by Elfling to support changed defend_attack routine 6/9/98
        if ( to_hit != 1 ) // Modifying for partial/bad defending.
	        tmp = tmp * (100 + to_hit) / 100;

#if DEBUG_COMBAT
        tell_room( environment(), "Damage after defend: "+ tmp +"\n" );
#endif

        tmp = (int)attacker_ob->hit_player_damage( 3*tmp/4, hit_at, dam_type );
      }
    }

#if DEBUG_COMBAT
    EV_COST->cost8( cost - get_eval_cost() );
#endif

    hook( "damage_dealt", ({ tmp, weapon }) );

    if ( dead )
      return 0; // no need to continue if we died in defend_attack()
  } else {
    tmp = 0;
  }

 /*
  if ( !query_npc() && weapon && tmp > 20 &&
       random( 100 ) < weapon_class - (int)query_level() * 2 / 3 - 14 ) {
    tell_object( this_object(), "CRACK!\nYour weapon broke!\n" );
    tell_object( this_object(),
      "You are too inexperienced for such a weapon.\n" );
    log_file( "BAD_WEAPON", weapon->short() +", "+ getuid( weapon ) +"\n" );
    destruct( weapon );
    weapon_class = parry_ac = dex_wc = hit_messages = 0;
    weapon_class = WEAPON_CLASS_OF_HANDS;
// calculate_power();
    return 1;
  }
*/

  if ( tmp == 0 ) {
    tell_object( this_object(), "You missed.\n" );
    if ( attacker_ob ) {
      say( query_name() +" missed "+ name_of_attacker +".\n", attacker_ob );
      tell_object( attacker_ob, query_name() +" missed you.\n" );

      // This check is in case we have a monster continually missing.
      // So that we turn to attack it.
      if ( !attacker_ob->query_attack() )
        attacker_ob->attacked_by( this_object() );
    } else {
      say( query_name() +" missed "+ name_of_attacker +".\n" );
    }

    return 1;
  }

#if DEBUG_COMBAT
  EV_COST->cost11( cost - get_eval_cost() );
#endif

  if ( !query_npc() && attacker_ob ) {
    (mapping)SKILL_D->do_learn( skill_pts, weapon, attacker_ob, weap_sk,
      Int, Wis, query_real_name(), query_class(), query_level() );

    if ( weapon && weapon == second_weapon ) {

      (mapping)SKILL_D->do_learn( skill_pts,
       "Two Weapon", attacker_ob,
        this_object()->query_skill( "Two Weapon" ),
        Int, Wis, query_real_name(), query_class(), query_level() );
    }

  }

#if DEBUG_COMBAT
  EV_COST->cost9( cost - get_eval_cost() );
#endif

  this_object()->add_exp( (100 * tmp) / XP_FACTOR );

  if ( attacker_ob &&
       (string)attacker_ob->query_name() != (string)NAME_OF_GHOST ) {
    string how, what, where, *how_what;

    how_what = (string *)MESSAGE_D->query_messages( tmp, dam_type,
      (weapon ? (string *)weapon->query_hit_messages() : hit_messages) );
    how = how_what[1];
    what = how_what[0];

    switch ( hit_at ) {
      case HEAD: where = (string)attacker_ob->query_head_str(); break;
      case BODY: where = (string)attacker_ob->query_body_str(); break;
      case ARM:  where = (string)attacker_ob->query_arm_str();  break;
      case LEG:  where = (string)attacker_ob->query_leg_str();  break;
      default:   where = "somewhere"; break;
    }

    message( "# "+ what +" ($ "+ where + how +".", this_object(),
      attacker_ob );

#if DEBUG_COMBAT
    EV_COST->cost12( cost - get_eval_cost() );
#endif

    return 1;
  }

  attacker_ob = alt_attacker_ob;
  alt_attacker_ob = 0;

  if ( attacker_ob )
    return 1;
}

int fail_wield( object w, int where ) {
  return hook( "block_wield", ({ w, where }) );
}

int fail_wear( object w ) {
  return hook( "block_wear", ({ w }) );
}

void wield( object w ) {
  hook( "wield", ({ w }) );

  if ( wielded_weapon )
    stop_wielding( wielded_weapon );
  wielded_weapon = w;

  if ( query_is_player() )
    weapon_class = 0;

  parry_ac = (int)w->query_parry_ac();
  dex_wc = (int)w->query_dex_wc();

//  calculate_power();
}

void second_wield( object w ) {
  hook( "second_wield", ({ w }) );

  if ( second_weapon )
    stop_wielding( second_weapon );

  second_weapon = w;
}

void heal_self( int h ) {
  if ( h > 0 ) {
    int hp_healed, sp_healed;

#ifdef LOG_HEAL
    if ( HEAL_LOG_CHECK ) {
      catch( log_file( "HEAL", "["+ ctime() +"] "+ capitalize( name ) +" ["+
        query_level() +"] ("+ h +") by "+
        capitalize( this_interactive()->query_real_name() ) +".\n" ) );
    }
#endif

    if ( (hit_point + h) > query_max_hp() ) {
      hp_healed = query_max_hp() - hit_point;
      hit_point = query_max_hp();
    } else {
      hp_healed = h;
      hit_point += h;
    }

    if ( (spell_points + h) > query_max_sp() ) {
      sp_healed = query_max_sp() - spell_points;
      spell_points = query_max_sp();
    } else {
      spell_points += h;
      sp_healed = h;
    }

    hook( "heal_self", ({ hp_healed, sp_healed }) );
  }
}

void restore_spell_points( int h ) {
  if ( h > 0 ) {
#ifdef LOG_HEAL
    if ( HEAL_LOG_CHECK ) {
      catch( log_file( "HEAL", "["+ ctime() +"] "+ capitalize( name ) +" ["+
        query_level() +"] ("+ h +" SP) by "+
        capitalize( this_interactive()->query_real_name() ) +".\n" ) );
    }
#endif

    if ( (spell_points += h) > query_max_sp() )
      spell_points = query_max_sp();
  }
}

void reduce_spell_points( int i ) {
  if ( i > 0 )
    spell_points -= i;

  hook( "reduce_spell_points", ({ i }) );
}

void restore_hit_points( int h ) {
  if ( h > 0 ) {
#ifdef LOG_HEAL
    if ( HEAL_LOG_CHECK ) {
      catch( log_file( "HEAL", "["+ ctime() +"] "+ capitalize( name ) +" ["+
        query_level() +"] ("+ h +" HP) by "+
        capitalize( this_interactive()->query_real_name() ) +".\n" ) );
    }
#endif

    if ( (hit_point += h) > query_max_hp() )
      hit_point = query_max_hp();
  }
}

void attack_object(object ob) {
// object tp;

  /* Fir 27 Apr 2013: ldhit_fix: No attacking if you're a statue. */
  if ( !ob || ob->query_ghost() || this_object()->query_is_statue() )
    return;

//  if ( this_player() != this_object() ) {  // bugfix (Greyson)
//    tp = this_player();
//    set_this_player( this_object() );
//  }

  set_heart_beat( 1 );

  if ( !ob->query_attack() )
    ob->start_fight();

  if ( attacker_ob == ob ) {
    attack();
  } else if ( alt_attacker_ob == ob ) {
    alt_attacker_ob = attacker_ob;
    attacker_ob = ob;
    attack();
  } else {
    alt_attacker_ob = attacker_ob;
    attacker_ob = ob;
    attacker_ob->attacked_by( this_object() );
    attack();
  }

//  if ( tp ) set_this_player( tp );
}

void set_attacker( object ob ) {
  if ( attacker_ob == ob )
    ;
  else if ( alt_attacker_ob == ob ) {
    alt_attacker_ob = attacker_ob;
    attacker_ob = ob;
  } else {
    alt_attacker_ob = attacker_ob;
    attacker_ob = ob;
  }
}

/*
 * This is used to set who this living is currently hunting.
 * Added by Toshina, 17-Aug-96, for J19.
 */
void set_hunted( object ob ) {
  hunted = ob;
  if ( hunted ) {
    hunting_time = 1000;
  } else {
    hunting_time = 0;
  }
}

void zap_object( object ob ) {
  if ( ob ) {
    ob->attacked_by( this_object() );

    say( query_name() +" summons a flash from the sky.\n" );
    write( "You summon a flash from the sky.\n" );

    ob->hit_player( 100000 );

    write( "There is a big clap of thunder.\n\n" );
  }
}

void stop_hunter() {
  // added 6Jul94 by Dawg to remove spurious msgs
  if ( !hunter && !alt_hunter )
    return;

  hunter = alt_hunter = 0;
  tell_object( this_object(), "You are no longer hunted.\n" );
}

void run_away() {
  object here;
  mixed dirs;
  int i, j;

  if ( wimpy_disabled )
    return;

  if ( !(here = environment()) )
    return;

#ifdef VERSION_WIMPY_DIR
  if ( whimpy_direction ) {
    // If the player has gone considerably lower on hit points than
    // their wimpy limit, they are more likely to panic in a lose-control
    // way. (What percent of their hit points they still have minus a random
    // number between 0 and their wimpy percentage.)
    if ( (query_hit_point() * 100 / query_max_hp()) -
         random(query_wimpy() * 10) > 0 )
      command( whimpy_direction );
    else
      tell_object( this_object(), "You forget which way to run!\n" );
  }
#endif
  dirs = (mixed)environment()->query_dest_dir();
  if ( dirs && here == environment() )
    command( dirs[1 + (random( sizeof( dirs ) ) / 2 * 2 )] );

  j = random( 6 );
  while ( i < 6 && here == environment() ) {
    if ( query_body_position() && !random( 10 ) )
      command( "stand" );

    i += 1;

    j += 1;
    if ( j > 6 )
      j = 1;

    switch ( j ) {
      case 1: command( "east" );  break;
      case 2: command( "west" );  break;
      case 3: command( "north" ); break;
      case 4: command( "south" ); break;
      case 5: command( "up" );    break;
      case 6: command( "down" );  break;
    }
  }

  if ( here == environment() ) {
    say( query_name() +" is panicking and trying to flee.\n", this_object() );
    if ( this_object() )
      tell_object( this_object(), "You panic and try to flee!\n" );
  } else if ( this_object() ) {
    tell_object( this_object(), "You run away in a panic!\n" );
  }
}

void stop_fight() {
  attacker_ob = alt_attacker_ob;
  alt_attacker_ob = 0;
}

void set_attack_disabled( int active ) {
  if ( member( ({ "lib", "greyson", "dawg", }),
               geteuid( previous_object() ) ) != -1 ) {
    attack_disabled = active;
  }
}

void set_wimpy_disabled( int active ) {
  wimpy_disabled = active;
}

int reduce_hit_point( int dam ) {
#ifdef LOG_HEAL
  if ( this_interactive() && this_interactive() != this_object() ) {
    log_file( "REDUCE_HP", (name ? capitalize( name ) : query_name()) +" ("+
      dam +") by "+ capitalize(this_interactive()->query_real_name()) +"\n" );
  }
#endif

  if ( this_player() && this_object()->query_is_statue() ) {
    write( "Time has petrified "+ query_objective() +".\n" );
    stop_fight();
    this_player()->stop_fight();

    return 0;
  }

  if ( dam > hit_point - 1 )
    dam = hit_point - 1;

  if ( this_player() && this_player() != this_object() ) {
    mixed dam_key;

    if ( this_player()->query_is_player() ) {
      dam_key = (string)this_player()->query_real_name();
    } else {
      dam_key = this_player();
    }

    add_m_damagers(dam_key, dam);
    if ( query_m_damagers(dam_key) <= 0 )
      del_m_damagers(dam_key);
  }

  // Should not be used to modify amount as usually used for costs
  // or specific amount of reduction or healing
  hook("reduce_hit_point", ({ dam }) );

  ks_handle_attack(this_player(), attacker_ob, dam);

  if ( (hit_point -= dam) <= 0 )
    hit_point = 1;
  this_object()->report_hp_sp();
  return hit_point;
}

int pay_spell_points( int arg ) {
  if ( spell_points < arg ) {
    write( "You are too low on power.\n" );
    return 0;
  }

  reduce_spell_points( arg );

  return 1;
}

void stop_any_hunt() {
  if ( hunter )
    hunter->stop_hunter();

  if ( alt_hunter )
    alt_hunter->stop_hunter();

  hunted = hunter = alt_hunter = 0;
}

int calculate_power() {
#if 1
  return 0;
#else
  int i, temp_power;

  if ( !weapon_class && !dex_wc ) {
    temp_power = 100 * (tmp_Str + tmp_Dex) / 2;
  } else {
    i = weapon_class + dex_wc;
    temp_power = weapon_class * 100 + (tmp_Str * weapon_class * 100) / i +
      (tmp_Dex * dex_wc * 100) / i;
  }

  if ( (temp_power % 100) >= 50)
    power = (temp_power / 100) + 1;
  else
    power = temp_power / 100;

  return power;
#endif
}

void set_detect_invisible( status arg ) {
  can_detect_invis = arg;
}

status see_invisible() {
  return can_detect_invis;
}

int query_hit_point() {
  return hit_point;
}

int query_wc() {
  return weapon_class;
}

int query_ac() {
  return armour_class;
}

int query_head_ac() {
  return head_ac;
}

int query_body_ac() {
  return body_ac;
}

int query_arm_ac() {
  return arm_ac;
}

int query_leg_ac() {
  return leg_ac;
}

int query_block_ac() {
  return block_ac;
}

object query_alt_hunter() {
  return alt_hunter;
}

object query_alt_attack() {
  return alt_attacker_ob;
}

static void set_wimpy( int w ) {
  whimpy = w;
}

int query_wimpy() {
  return whimpy;
}

#ifdef VERSION_WIMPY_DIR
static void set_wimpy_direction( string direction ) {
  whimpy_direction = direction;
}

string query_wimpy_direction() {
  return whimpy_direction;
}
#endif

// int query_power() { return power; }

object query_hunter() {
  return hunter;
}

object query_wielded_weapon() {
  return wielded_weapon;
}

int query_attack_disabled() {
  return attack_disabled;
}

static void set_aim_at( int a ) {
  aiming_at = a;
}

int query_aim_at() {
  return aiming_at;
}

object query_worn_shield() {
  return worn_shield;
}

int query_dead() {
  return dead;
}

object query_hunted() {
  return hunted;
}

object query_attack() {
  return attacker_ob;
}

int query_parry_ac() {
  return parry_ac;
}

int query_weapon_class() {
#if BRAWL_IN_COMBAT
 object brawl;
 int brawl_bonus;
#endif

 int ret;

 ret = weapon_class;
#if BRAWL_IN_COMBAT
 brawl = present("\nbrawl_skill",this_object());

 if (brawl && !wielded_weapon && !second_weapon && !worn_shield) {
   if (hit_messages == (string *)brawl->query_hit_messages()) {
   brawl_bonus = (int)brawl->do_combat();
   } else {
   brawl_bonus = random((int)brawl->virtual_class());
   }
   ret += (int)WEAPON_CLASS_OF_HANDS + brawl_bonus;
   if (this_object()->query_is_player() && !hit_messages)
   hit_messages = (string *)brawl->query_hit_messages();
 } else {
  if ( (wielded_weapon && !wielded_weapon->query_hit_messages() ) ||
       (second_weapon && !second_weapon->query_hit_messages() ) )
  hit_messages = 0;
 }
#endif

 if ( !ret )
   ret = (int)WEAPON_CLASS_OF_HANDS;
#if BRAWL_IN_COMBAT
 ret += hook( "weapon_class", ({ ret, brawl_bonus }) );
#else
 ret += hook( "weapon_class", ({ ret }) );
#endif

 return ret;
}

string query_killed_string() {
  return "killed";
}

string query_died_string() {
  return "died";
}

string *query_hit_messages() {
  return hit_messages;
}

// ----------------------------------------------------------------------------