//stolen by crat from dw for fr
/**
 * The base object which all races will be inherited from.
 * @author Pinkfish
 * @started 1992 sometime
 */
#define QUEST_AVE 140
#define QUEST_MAX 570
#define BASE 50
#define SCALE 10
#define NORM 15
#define EXPERT 20
#define A_TYPES ([ \
      "claws" : "sharp", \
      "chew" : "sharp", \
      "beak" : "pierce", \
      "bite" : "pierce", \
      "horn" : "pierce", \
      "hands" : "blunt", \
      "feet" : "blunt", \
      "tail" : "blunt", \
      "hoof" : "blunt", \
      "wrestle" : "blunt", \
   ])
inherit "/std/object";
/* Original by who knows.
 * Modifications by Piecemaker, Ember, Presto, Deutha and Dragonkin.
 */
private int height;
private int *sight;
private int *stats;
private string desc;
private string skin;
string *inedible;
string *unrottable;
string *pluckable;
private string *limbs;
private mixed *acs;
private mixed *attacks;
private mapping _fixed_bits;
mixed *bits;
/* The acs array has element blocks:
 *
 *   name, type, amount
 *
 * The attacks array has element blocks:
 *
 *   name, chance, data relative to bonus of 200
 *
 * The bits array has element blocks:
 *
 *   short, name, ({ parent, percentage weight, ({ file name (0 is "/std/bit"),
 *         amount }), child bit 1, child bit 2, ... })
 *
 *   Note that if the size of ({ file name, amount }) part is less than two,
 *   it'll set the filename to default and the amount to 1
 */
void create() {
   do_setup++;
   ::create();
   do_setup--;
   sight = ({ 5, 20, 200, 300 });
   stats = allocate( 5 );
   desc = "a small non-descript thing";
   inedible = ({  });
   unrottable = ({  });
   pluckable = ({  });
   acs = ({ });
   attacks = ({ });
   bits = ({ });
   if ( !do_setup )
      this_object()->setup();
} /* create() */
/**
 * This method returns the base height of the npc type.
 * @return the current base height
 * @see set_height()
 */
int query_height() { return height; }
/**
 * This method sets the base height of the npc type.
 * @param number the new height
 * @see query_height()
 */
void set_height( int number ) { height = number; }
/**
 * Thie method returns the values of the race which determine visibility.
 * It returns an array of the format:<br>
 * <pre>({
 *    dark
 *    pitch dark,
 *    bright light,
 *    bright
 * })</pre>
 * @return the sight array as detailed above
 * @see set_sight()
 * @see query_dark()
 */
int *query_sight() { return copy(sight); }
/**
 * Thie method sets the current sight array for the npc.  This sets the
 * levels at which the race things things are too dark/too bright.<br>
 * <pre>({
 *    pitch dark,
 *    dark,
 *    bright,
 *    bright light
 * })</pre>
 * @param numbers as detailed above
 * @see query_sight()
 * @see query_dark()
 */
void set_sight( int *numbers ) { sight = numbers; }
/**
 * Determines if the race thinks it is dark or light.
 * @return 0 for normal, -1 for dark, -2 for pitch dark, 1 for bright,
 * 2 for toobright
 * @see set_sight()
 * @see query_sight()
 */
int query_dark( int light ) {
   if ( light < sight[ 0 ] ) {
      return -2;
   }
   if ( light < sight[ 1 ] ) {
      return -1;
   }
   if ( light > sight[ 3 ] ) {
      return 2;
   }
   if ( light > sight[ 2 ] ) {
      return 1;
   }
   return 0;
} /* query_dark() */
/**
 * This method queries the default bonus stat values for the race.<br>
 * <pre>({
 *   con,
 *   dex,
 *   int,
 *   str,
 *   wiz
 * })</pre>
 * @return the current stat offsets
 * @see set_stats()
 */
int *query_stats() { return copy(stats); }
/**
 * This method sets the current default bonuses of the stats.  The parameter
 * is an array of numbers, with the numbers corresponding to the stats as
 * shown below.  It is highly inadvisable to set any stat bonus to less than
 * -6, since in conjunction with the guild stat settings this could combine
 * to give a negative total for that stat.  Negative stats can cause
 * permanent unconsciousness.
 * <pre>({
 *   con,
 *   dex,
 *   int,
 *   str,
 *   wis
 * })</pre>
 * @param numbers sets the default bonuses for the stats
 * @see query_stats()
 */
void set_stats( int *numbers ) { stats = numbers; }
/**
 * This method returns the current skin of the race.
 * @return the current skin of the race
 * @see set_skin()
 */
string query_skin() { return skin; }
/**
 * This method sets the skin of the race.  This is what is given back
 * when the npc's corpse is skinned.
 * @param word the skin of the race
 * @see query_skin()
 */
void set_skin( string word ) { skin = word; }
/**
 * This method returns the current description of the npc.
 * @param thing the object which is being described
 * @return the description of the npc
 * @see query_desc()
 */
string query_desc( object thing ) {
   return capitalize( (string)thing->query_pronoun() ) +" is "+ desc +".\n";
} /* query_desc() */
/**
 * This method sets the description for the npc.
 * @param words the description of the npc
 * @see set_desc()
 */
void set_desc( string words ) { desc = words; }
/**
 * This method returns the current limbs of the race.
 * @return the limbs on the race
 * @see find_limbs()
 */
string *query_limbs() {
   if ( !limbs ) {
      this_object()->find_limbs();
   }
   return copy(limbs);
} /* query_limbs() */
/**
 * This method figures out the current set of limbs on the race.  A creature
 * is assigned one "limb" for every "hand" found in the body part array.  The
 * number of limbs is the number of weapons that can be held.
 * @see query_limbs()
 */
void find_limbs() {
   int i;
   string limb;
   limbs = ({ });
   for ( i = 0; i < sizeof( bits ); i += 3 ) {
     if ( sscanf( bits[ i ], "%s hand", limb ) == 1 ) {
       limbs += ({ bits[ i ] });
     }
   }
} /* find_limbs() */
/**
 * The defaults acs for npcs when they are fighting unarmed.
 * @return the default acs
 * @see add_ac()
 */
mixed *query_acs() { return copy(acs); }
/**
 * Adds a default ac to the npc for when they fight unarmed.  Please look
 * add the armour object for more details on this.
 * @param ac_name the name of the ac
 * @param ac_type the type of the ac
 * @param ac_amount the damage/roll stuff to take off
 * @see query_acs()
 */
void add_ac( string ac_name, string ac_type, mixed ac_amount ) {
   acs += ({ ac_name, ac_type, ac_amount });
} /* add_ac() */
/**
 * The default attacks for the npc when they are fighting unarmed.
 * @return the default attacks
 * @see add_attack()
 */
mixed *query_attacks() { return copy(attacks); }
/**
 * This method adds a default attack to the npc.  Please see the
 * help on the weapons for more details on this.
 * @param attack_name the name of the attack
 * @param attack_chance the chance of the attack occuring
 * @param attack_data the damage roll for the attack
 * @see query_attacks()
 */
void add_attack( string attack_name, int attack_chance, int *attack_data ) {
   attacks += ({ attack_name, attack_chance, attack_data });
} /* add_attack() */
/**
 * This method returns all the bits for the npc.
 * @return the complete array of bits
 * @see add_bit()
 * @see query_bit()
 * @see query_possible_bits()
 * @see query_all_inedible()
 * @see query_all_unrottable()
 * @see query_all_pluckable()
 * @see remove_bit()
 */
mixed *query_bits() { return bits; }
/**
 * This method returns the bits which are children of the specified bit.
 * @param word the bit to look for the children of
 * @return the children bits
 * @see add_bit()
 * @see query_bits()
 * @see remove_bit()
 */
mixed *query_bit( string word ) {
   int i;
   i = member_array( word, bits );
   if ( ( i == -1 ) || ( i % 3 ) ) {
      return ({ });
   }
   return bits[ i .. i + 2 ];
} /* query_bit() */
private void fixup_bits() {
   string* str_bits;
   string pl;
   int i;
   if (_fixed_bits) {
      return ;
   }
   _fixed_bits = ([ ]);
   for ( i = sizeof( bits ) - 3; i > -1; i -= 3 ) {
      str_bits = explode(bits[i], " ");
      pl = pluralize(str_bits[<1]);
      if (!_fixed_bits[str_bits[<1]]) {
         _fixed_bits[str_bits[<1]] = ({ });
      }
      _fixed_bits[str_bits[<1]] += ({ ({ str_bits[0..<2], i, 0 }) });
      if (!_fixed_bits[pl]) {
         _fixed_bits[pl] = ({ });
      }
      _fixed_bits[pl] += ({ ({ str_bits[0..<2], i, 1 }) });
   }
} /* fixup_bits() */
/** @ignore yes 
 * For backwards compatibility, as corpses are currently broken in
 * the way they call the query_possible_bits() method. 
 */ 
private string *query_old_possible_bits( string word ) {
   int i;
  int j;
  string *possibles;
  possibles = ({ });
  for ( i = sizeof( bits ) - 3; i > -1; i -= 3 ) {
    if ( ( bits[ i ] == word ) ||
          ( bits[ i + 1 ] == word ) ||
          !word) {
       if (arrayp( bits[i+2][2] )) {
         for ( j = 0; j < bits[i + 2][2][1]; j++) {
           possibles += ({ bits[ i ] });
         }
       } else {
         possibles += ({ bits[ i ] });
       }
     }
   }
   return possibles;
} /* query_old_possible_bits() */
/**
 * This method returns the names of all the possible bits for the
 * npc.
 * @param word the bits to look for the children under
 * @return the array of possible bit names
 * @see add_bit()
 * @see query_bits()
 * @see query_possible_plural_bits()
 * @see remove_bit()
 */
string *query_possible_bits( string word ) {
  string* str_bits;
  string* adj;
  string *possibles;
  mixed* bing;
  
  if(!word) {
    return query_old_possible_bits( word ); 
  }
  fixup_bits();
  str_bits = explode(word, " ");
  possibles = ({ });
  if (_fixed_bits[str_bits[<1]]) {
     adj = str_bits[0..<2];
     foreach (bing in _fixed_bits[str_bits[<1]]) {
        if (!bing[2] &&
            sizeof(adj) == sizeof(adj & bing[0])) {
           possibles += ({ bits[bing[1]] });
        }
     }
  }
  return possibles;
} /* query_possible_bits() */
/**
 * This method returns the plural names for all the bits.
 * @param word the bit to find the children of
 * @return the plural names of all the bits
 * @see add_bit()
 * @see query_bits()
 * @see query_possible_bits()
 * @see remove_bit()
 */
string *query_possible_plural_bits( string word ) {
  string* str_bits;
  string* adj;
  string *possibles;
  mixed* bing;
  fixup_bits();
  str_bits = explode(word, " ");
  possibles = ({ });
  if (_fixed_bits[str_bits[<1]]) {
     adj = str_bits[0..<2];
     foreach (bing in _fixed_bits[str_bits[<1]]) {
        if (bing[2] &&
            sizeof(adj) == sizeof(adj & bing[0])) {
           possibles += ({ bits[bing[1]] });
        }
     }
  }
  return possibles;
} /* query_possible_plural_bits() */
/**
 * This method adds a bit to the current bit setup.
 * @param bit_short the short of the bit
 * @param bit_name the name of the bit
 * @param bit_array the array of children bits
 * @see query_bits()
 * @see remove_bit()
 */
void add_bit( string bit_short, string bit_name, mixed *bit_array ) {
   int i;
   i = member_array( bit_short, bits );
   if ( !( i % 3 ) ) {
      bits[ i ] = bit_short;
      bits[ i + 1 ] = bit_name;
      bits[ i + 2 ] = bit_array;
      return;
   }
   bits += ({ bit_short, bit_name, bit_array });
} /* add_bit() */
/**
 * This method removes a bit from the current bit array
 * @param word the name of the bit to remove
 * @see add_bit()
 * @see query_bits()
 */
void remove_bit( string word ) {
   int i;
   i = member_array( word, bits );
   if ( ( i == -1 ) || ( i % 3 ) ) {
      return;
   }
   bits = delete( bits, i, 3 );
} /* remove_bit() */
/**
 * This method figures out a modifier for the height based on the base
 * weight and height.
 * @param number the height/weight to modify
 * @return the modified heigh/weight
 */
int modifier( int number ) {
   return ( number * ( roll_MdN( 20, 21 ) - 220 ) ) / 2000;
} /* modifier() */
/**
 * This method is called when the npc is first created.  This sets up
 * the weight/height/stats etc.
 * @param thing the npc being created
 */
void start_player( object thing ) {
   if ( !thing->query_base_weight() ) {
      thing->set_base_weight( 1 + weight + modifier( weight ) );
   }
   if ( !thing->query_height() ) {
      thing->set_height( 1 + height + modifier( height ) );
   }
   thing->adjust_bonus_con( stats[ 0 ] );
   thing->adjust_bonus_dex( stats[ 1 ] );
   thing->adjust_bonus_int( stats[ 2 ] );
   thing->adjust_bonus_str( stats[ 3 ] );
   thing->adjust_bonus_wis( stats[ 4 ] );
   thing->reset_all();
   if ( skin ) {
      thing->set_skin( skin );
   }
} /* start_player() */
/**
 * This method figures out how many of each sort of thing 
 * can be worn.
 * @param type the type to check for numbers worn
 * @return the number of type to worn
 */
int query_number_worn( string type ) {
   switch ( type ) {
      case "armband" :
         return 2;
      case "badge" :
         return 15;
      case "bracelet" :
         return 4;
      case "earring" :
        return 2;/* added by ceres since piercing isn't implemented yet */
//         return (int)previous_object()->query_piercings( "ear" );
      case "garter" :
         return 2;
      case "shoulder" :
         return 2;
      case "necklace" :
         return 5;
      case "ring" :
         return 8;
      case "sash" :
         return 2;
      case "shirt" :
         return 2;
      case "belt scabbard" :
         return 2;
      case "small scabbard" :
         return 2;
      case "bandaid" :
         return 5;
      default :
         return 1;
   }
} /* query_number_worn() */
int query_number_wielded( string type ) { return 1; }
int query_skill_bonus( int number, string word ) { return 0; }
int player_start( object thing ) { return 1; }
int player_quit( object thing ) { return 1; }
/**
 * This method sets the level of the npc.
 * @param thing the npc to set the level of
 * @param level the current level
 */
void set_level( object thing, int level ) {
   int i;
   if ( !thing ) {
      return;
   }
   for ( i = sizeof( acs ) - 3; i > -1; i -= 3 ) {
      thing->add_ac( acs[ i ], acs[ i + 1 ], acs[ i + 2 ] );
   }
} /* set_level() */
int *calc_attack( int *data, int number ) {
   int base;
   int num;
   int rand;
   int rating;
   int scale;
   base =  data[ 0 ] * ( BASE + SCALE * number );
   num = data[ 1 ];
   rand = data[ 2 ] * ( BASE + SCALE * number );
   rating = QUEST_AVE * ( base + num * rand ) +
         QUEST_MAX * ( base + ( num * ( 1 + rand ) ) / 2 );
   scale = BASE + SCALE * NORM;
   rating /= scale;
   rating = ( ( BASE + SCALE * EXPERT ) * rating + QUEST_AVE * QUEST_MAX ) /
         ( 2 * QUEST_AVE * QUEST_MAX );
   if ( rating > scale ) {
      scale = ( rating + scale ) / 2;
   }
   return ({ base / scale, num, rand / scale });
} /* calc_attack() */
/**
 * This method sets up the unarmed attacks on the npc.
 * @param thing the npc to setup the attacks on
 */
void set_unarmed_attacks( object thing ) {
   int i, number;
   number = (int)thing->query_skill_bonus( "fighting.combat.melee.unarmed" );
   number = sqrt( number );
   for ( i = sizeof( attacks ) - 3; i > -1; i -= 3 ) {
      thing->remove_attack( attacks[ i ] );
      thing->add_attack( attacks[ i ], attacks[ i + 1 ],
            calc_attack( attacks[ i + 2 ], number ), A_TYPES[ attacks[ i ] ],
            "unarmed", "unarmed_"+ attacks[ i ] );
   }
} /* set_unarmed_attacks() */
/**
 * This method checks to see if the specified bit of the npc is edible or
 * not.
 * @param bit the bit to check
 * @return 1 if the bit is edible, 0 if not
 * @see add_bit()
 * @see query_bits()
 * @see query_all_inedible()
 * @see remove_bit()
 */
int query_eat( string bit ) {
  if (member_array( bit, inedible) == -1) {
    return 1;
  }
  return 0;
} /* query_eat() */
/**
 * This method checks to see if the bit is unrottable, ie: a bit of
 * skeleton.
 * @param bit the bit to check
 * @return 1 if unrottable, 0 if rottable
 * @see add_bit()
 * @see query_bits()
 * @see query_all_unrottable()
 * @see remove_bit()
 */
int query_unrottable( string bit ) {
  if (member_array( bit, unrottable) == -1) {
    return 0;
  }
  return 1;
} /* query_unrottable() */
/**
 * This method checks to see if the bit is pluckable.
 * @param bit the bit to check
 * @return 1 if pluckable, 0 if not
 * @see add_bit()
 * @see query_bits()
 * @see query_all_pluckable()
 * @see remove_bit()
 */
int query_pluckable( string bit ) {
  if (member_array( bit, pluckable) == -1) {
    return 0;
  }
  return 1;
} /* query_pluckable() */
/**
 * This method returns the array of all the inedible bits of the race.
 * @return all the inedible bits
 * @see add_bit()
 * @see query_bits()
 * @see query_eat()
 * @see remove_bit()
 */
string *query_all_inedible() {
   return inedible;
} /* query_all_inedible() */
/**
 * This method returns the array of all the unrottable bits of the race.
 * @return all the unrottable bits
 * @see add_bit()
 * @see query_bits()
 * @see query_unrottable()
 * @see remove_bit()
 */
string *query_all_unrottable() {
   return unrottable;
} /* query_all_unrottable() */
/**
 * This method returns the array of all the pluckable bits of the race.
 * @return all the pluckable bits of the race
 * @see add_bit()
 * @see query_bits()
 * @see query_pluckable()
 * @see remove_bit()
 */
string *query_all_pluckable() {
   return pluckable;
} /* query_all_pluckable() */
// Very generic. These are the zones
string *query_target_zones() {
  return ({ "head", "head", "neck",
              "chest", "back", "back", "back", "lower back", 
              "left front leg", "left front leg", 
              "stomach", "stomach", 
              "right front leg", "right front leg",
              "left rear leg", "left rear leg",
              "right rear leg", "right rear leg",});
}
string map_target_zone(string zone) {
  switch(zone) {
  case "abdomen":
    return "stomach";
  case "hands":
  case "arms":
    return (random(2) ? "left " : "right ") + "front leg";
  case "feet":
  case "legs":
    return (random(2) ? "left " : "right ") + "rear leg";
  default:
    return zone;
  }
}
string map_armour_zone(string zone) {
  switch(zone) {
  case "thorax":
  case "body":
  case "breast":
  case "trunk":
    return "chest";
  case "tail":
    return "back";
    
  case "stomach":
    return "abdomen";
    
  case "left arm":
  case "right arm":
  case "left front leg":
  case "right front leg":
  case "left petral fin":
  case "right petral fin":
  case "left wing":
  case "right wing":
  case "branches":
    return "arms";
  case "left hand":
  case "right hand":
  case "left middle leg":
  case "right middle leg":
  case "left front paw":
  case "right front paw": 
    return "hands";
  case "left leg":
  case "right leg":
  case "left back leg":
  case "right back leg":
  case "left rear leg":
  case "right rear leg":
  case "dorsal fin":
    return "legs";
    
  case "left foot":
  case "right foot":
  case "left rear paw":
  case "left rear paw":
  case "left rear foot":
  case "left rear foot":
  case "left claw":
  case "right claw":
  case "root":
    return "feet";
    
  default:
    return zone;
  }
}