Difference for server/skill_util.c from version 1.39 to 1.40


version 1.39 version 1.40
Line 1
 
Line 1
 /*  /*
  * static char *rcsid_skill_util_c =   * static char *rcsid_skill_util_c =
  *   "$Id: skill_util.c,v 1.39 2003/09/04 06:25:32 temitchell Exp $";   *   "$Id: skill_util.c,v 1.40 2003/09/13 05:02:12 mwedel Exp $";
  */   */
 /*  /*
     CrossFire, A Multiplayer game for X-windows      CrossFire, A Multiplayer game for X-windows
Line 42
 
Line 42
 /* define the following for skills utility debuging */  /* define the following for skills utility debuging */
 /* #define SKILL_UTIL_DEBUG */  /* #define SKILL_UTIL_DEBUG */
   
   #define WANT_UNARMED_SKILLS
   
 #include <global.h>  #include <global.h>
 #include <object.h>  #include <object.h>
 #ifndef __CEXTRACT__  #ifndef __CEXTRACT__
 #include <sproto.h>  #include <sproto.h>
 #endif  #endif
 #include <living.h>     /* for defs of STR,CON,DEX,etc. -b.t.*/  #include <living.h>     /* for defs of STR,CON,DEX,etc. -b.t.*/
 #include <skills.h>  
 #include <skillist.h>  
 #include <spells.h>  #include <spells.h>
   
 /* table for stat modification of exp */  char *skill_names[NUM_SKILLS];
   
   /* init_skills basically just sets up the skill_names table
    * above.  The index into the array is set up by the
    * subtypes.
    */
   void init_skills() {
       int i;
       archetype *at;
   
       for (i=0; i<NUM_SKILLS; i++)
    skill_names[i] = NULL;
   
       for(at = first_archetype;at!=NULL;at=at->next) {
    if (at->clone.type == SKILL) {
        if (skill_names[at->clone.subtype] != NULL) {
    LOG(llevError, "init_skills: multiple skill using same subtype %d, %s, %s\n",
        skill_names[at->clone.subtype], at->clone.skill);
        } else {
    skill_names[at->clone.subtype] = add_refcount(at->clone.skill);
        }
    }
       }
   
       /* This isn't really an error if there is no skill subtype set, but
        * checking for this may catch some user errors.
        */
       for (i=1; i<NUM_SKILLS; i++) {
    if (!skill_names[i])
        LOG(llevError, "init_skills: skill subtype %d doesn't have a name?\n",
    i);
       }
   }
   
   
 float stat_exp_mult[MAX_STAT+1]={  /* This function goes through the player inventory and sets
  0.0,   * up the last_skills[] array in the player object.
  0.01, 0.1, 0.3, 0.5, /* 1 - 4 */   * the last_skills[] is used to more quickly lookup skills -
  0.6, 0.7, 0.8, /* 5 - 7 */   * mostly used for sending exp.
  0.85, 0.9, 0.95, 0.96, /* 8 - 11 */   */
  0.97, 0.98, 0.99, /* 12 - 14 */  void link_player_skills(object *op)
  1.0, 1.01, 1.02, 1.03, 1.04, /* 15 - 19 */  {
  1.05, 1.07, 1.09, 1.12, 1.15, /* 20 - 24 */      object *tmp;
  1.2, 1.3, 1.4, 1.5, 1.7, 2.0  
 };  
   
   
 typedef struct _skill_name_table {  
  char *name;  
  int id;  
 }_skill_name_table;  
   
 static _skill_name_table skill_name_table[] = {  
  {"agility", CS_STAT_SKILLEXP_AGILITY},  
  {"personality", CS_STAT_SKILLEXP_PERSONAL},  
  {"mental", CS_STAT_SKILLEXP_MENTAL},  
  {"physique", CS_STAT_SKILLEXP_PHYSIQUE},  
  {"magic", CS_STAT_SKILLEXP_MAGIC},  
  {"wisdom", CS_STAT_SKILLEXP_WISDOM},  
  {"",-1}  
 };  
   
 /* find and assign the skill exp stuff */      for (tmp=op->inv; tmp; tmp=tmp->below) {
    if (tmp->type == SKILL) {
        /* This is really a warning, hence no else below */
        if (op->contr->last_skill_ob[tmp->subtype] && op->contr->last_skill_ob[tmp->subtype] != tmp) {
    LOG(llevError,"Multiple skills with the same subtype? %s, %s\n",
        op->contr->last_skill_ob[tmp->subtype]->skill, tmp->skill);
        }
        if (tmp->subtype >= NUM_SKILLS) {
    LOG(llevError,"Invalid subtype number %d (range 0-%d)\n",
        tmp->subtype, NUM_SKILLS);
        } else {
    op->contr->last_skill_ob[tmp->subtype] = tmp;
    op->contr->last_skill_exp[tmp->subtype] = -1;
        }
    }
       }
   }
   
 void find_skill_exp_name(object *pl, object *exp, int index)  /* This returns the skill pointer of the given name (the
    * one that accumlates exp, has the level, etc).
    *
    * It is presumed that the player will be needing to actually
    * use the skill, so thus if use of the skill requires a skill
    * tool, this code will equip it.
    */
   object *find_skill_by_name(object *who, char *name)
 {  {
       object *skill=NULL, *skill_tool=NULL, *tmp;
   
       if (!name) return NULL;
   
       /* We make sure the length of the string in the object is greater
        * in length than the passed string. Eg, if we have a skill called
        * 'hi', we don't want to match if the user passed 'high'
        */
       for (tmp=who->inv; tmp!=NULL; tmp=tmp->below) {
    if (tmp->type == SKILL && !strncasecmp(name, tmp->skill, strlen(name)) &&
        strlen(tmp->skill) >= strlen(name)) skill = tmp;
   
    /* Try to find appropriate skilltool.  If the player has one already
    * applied, we try to keep using that one.
    */
    else if (tmp->type == SKILL_TOOL && !strncasecmp(name, tmp->skill, strlen(name)) &&
        strlen(tmp->skill) >= strlen(name)) {
        if (QUERY_FLAG(tmp, FLAG_APPLIED)) skill_tool = tmp;
        else if (!skill_tool || !QUERY_FLAG(skill_tool, FLAG_APPLIED))
    skill_tool = tmp;
    }
       }
       /* If this is a skill that can be used without a tool, return it */
       if (skill && QUERY_FLAG(skill, FLAG_CAN_USE_SKILL)) return skill;
   
       /* Player has a tool to use the skill.  IF not applied, apply it -
        * if not successful, return null.  If they do have the skill tool
        * but not the skill itself, give it to them.
        */
       if (skill_tool) {
    if (!QUERY_FLAG(skill_tool, FLAG_APPLIED)) {
        if (apply_special(who, skill_tool, 0)) return NULL;
    }
    if (!skill) {
        skill = give_skill_by_name(who, skill_tool->skill);
        link_player_skills(who);
    }
    return skill;
       }
       return NULL;
   }
   
  register int s;  /* This changes the objects skill to new_skill.
    * note that this function doesn't always need to get used -
    * you can now add skill exp to the player without the chosen_skill being
    * set.  This function is of most interest to players to update
    * the various range information.
    * if new_skill is null, this just unapplies the skill.
    * flag has the current meaning:
    * 0x1: If set, don't update the range pointer.  This is useful when we
    *   need to ready a new skill, but don't want to clobber range.
    * return 1 on success, 0 on error
    */
    
  for(s=0;skill_name_table[s].id != -1;s++)  int change_skill (object *who, object *new_skill, int flag)
  {   {
  if(!strcmp(skill_name_table[s].name, exp->name) )      int old_range = who->contr->shoottype;
   
       if (who->chosen_skill && who->chosen_skill == new_skill)
         {          {
  pl->contr->last_skill_ob[index] = exp;          /* optimization for changing skill to current skill */
  pl->contr->last_skill_id[index] = skill_name_table[s].id;          if (who->type == PLAYER && !(flag & 0x1))
  pl->contr->last_skill_index++;              who->contr->shoottype = range_skill;
  return;          return 1;
         }          }
    
       if (!new_skill || who->chosen_skill)
    if (who->chosen_skill) apply_special(who, who->chosen_skill, AP_UNAPPLY);
   
       /* Only goal in this case was to unapply a skill */
       if (!new_skill) return 0;
   
       if (apply_special (who, new_skill, AP_APPLY)) {
    return 0;
  }   }
       if (flag & 0x1)
    who->contr->shoottype = old_range;
   
       return 1;
 }  }
   
   /* This function just clears the chosen_skill and range_skill values
    * inthe player.
    */
   void clear_skill(object *who)
   {
       who->chosen_skill = NULL;
       CLEAR_FLAG(who, FLAG_READY_SKILL);
       if (who->type == PLAYER) {
    who->contr->ranges[range_skill] = NULL;
    if (who->contr->shoottype == range_skill)
        who->contr->shoottype = range_none;
       }
   }
   
 /* do_skill() - Main skills use function-similar in scope to cast_spell().   /* do_skill() - Main skills use function-similar in scope to cast_spell().
  * We handle all requests for skill use outside of some combat here.    * We handle all requests for skill use outside of some combat here.
Line 113
 
Line 229
  * It returns 0 if no skill was used.   * It returns 0 if no skill was used.
  */   */
   
 int do_skill (object *op, object *part, int dir, char *string) {  int do_skill (object *op, object *part, object *skill, int dir, char *string) {
     int success=0, exp=0;      int success=0, exp=0;
     int skill = op->chosen_skill->stats.sp;  
     int did_alc = 0;      int did_alc = 0;
     object *tmp, *next;      object *tmp, *next;
   
     switch(skill) {      if (!skill) return 0;
   
       /* The code below presumes that the skill points to the object that
        * holds the exp, level, etc of the skill.  So if this is a player
        * go and try to find the actual real skill pointer, and if the
        * the player doesn't have a bucket for that, create one.
        */
       if (skill->type != SKILL && op->type == PLAYER) {
    for (tmp = op->inv; tmp!=NULL; tmp=tmp->below) {
        if (tmp->type == SKILL && tmp->skill == skill->skill) break;
    }
    if (!tmp) tmp=give_skill_by_name(op, skill->skill);
    skill = tmp;
       }
   
       switch(skill->subtype) {
  case SK_LEVITATION:   case SK_LEVITATION:
      if(QUERY_FLAG(op,FLAG_FLYING)) {        if(QUERY_FLAG(op,FLAG_FLYING)) {
  CLEAR_FLAG(op,FLAG_FLYING);   CLEAR_FLAG(op,FLAG_FLYING);
Line 133
 
Line 263
      break;       break;
   
  case SK_STEALING:   case SK_STEALING:
      exp = success = steal(op, dir);       exp = success = steal(op, dir, skill);
      break;       break;
   
  case SK_LOCKPICKING:   case SK_LOCKPICKING:
      exp = success = pick_lock(op, dir);       exp = success = pick_lock(op, dir, skill);
      break;       break;
   
  case SK_HIDING:   case SK_HIDING:
      exp = success = hide(op);       exp = success = hide(op, skill);
      break;       break;
   
  case SK_JUMPING:   case SK_JUMPING:
      success = jump(op, dir);       success = jump(op, dir, skill);
      break;       break;
   
  case SK_INSCRIPTION:   case SK_INSCRIPTION:
      exp = success = write_on_item(op,string);       exp = success = write_on_item(op,string, skill);
      break;       break;
   
  case SK_MEDITATION:   case SK_MEDITATION:
      meditate(op);       meditate(op, skill);
      success=1;       success=1;
      break;       break;
      /* note that the following 'attack' skills gain exp through hit_player() */       /* note that the following 'attack' skills gain exp through hit_player() */
   
  case SK_KARATE:   case SK_KARATE:
      (void) attack_hth(op,dir,"karate-chopped");       (void) attack_hth(op,dir,"karate-chopped", skill);
      break;       break;
   
  case SK_BOXING:   case SK_PUNCHING:
      (void) attack_hth(op,dir,"punched");       (void) attack_hth(op,dir,"punched", skill);
      break;       break;
   
  case SK_FLAME_TOUCH:   case SK_FLAME_TOUCH:
      (void) attack_hth(op,dir,"flamed");       (void) attack_hth(op,dir,"flamed", skill);
      break;       break;
   
  case SK_CLAWING:   case SK_CLAWING:
      (void) attack_hth(op,dir,"clawed");       (void) attack_hth(op,dir,"clawed", skill);
      break;       break;
   
  case SK_MELEE_WEAPON:   case SK_ONE_HANDED_WEAPON:
      (void) attack_melee_weapon(op,dir,NULL);   case SK_TWO_HANDED_WEAPON:
        (void) attack_melee_weapon(op,dir,NULL, skill);
      break;       break;
   
  case SK_FIND_TRAPS:   case SK_FIND_TRAPS:
      exp = success = find_traps(op);       exp = success = find_traps(op, skill);
      break;       break;
   
  case SK_MUSIC:   case SK_SINGING:
      exp = success = singing(op,dir);       exp = success = singing(op,dir, skill);
      break;       break;
   
  case SK_ORATORY:   case SK_ORATORY:
      exp = success = use_oratory(op,dir);       exp = success = use_oratory(op,dir, skill);
      break;       break;
   
  case SK_SMITH:   case SK_SMITHERY:
  case SK_BOWYER:   case SK_BOWYER:
  case SK_JEWELER:   case SK_JEWELER:
  case SK_ALCHEMY:   case SK_ALCHEMY:
Line 208
 
Line 339
  }   }
      }       }
      if (did_alc == 0)       if (did_alc == 0)
  exp = success = skill_ident(op);   exp = success = skill_ident(op,skill);
      break;       break;
   
  case SK_DET_MAGIC:   case SK_DET_MAGIC:
  case SK_DET_CURSE:   case SK_DET_CURSE:
      exp = success = skill_ident(op);       exp = success = skill_ident(op,skill);
      break;       break;
   
  case SK_REMOVE_TRAP:   case SK_DISARM_TRAPS:
      exp = success = remove_trap(op,dir);       exp = success = remove_trap(op,dir, skill);
      break;       break;
   
  case SK_THROWING:   case SK_THROWING:
      success = skill_throw(op,part,dir,string);       success = skill_throw(op,part,dir,string, skill);
      break;       break;
   
  case SK_SET_TRAP:   case SK_SET_TRAP:
Line 234
 
Line 365
      break;       break;
   
  case SK_PRAYING:   case SK_PRAYING:
      success = pray(op);       success = pray(op, skill);
      break;       break;
   
  case SK_SPELL_CASTING:   case SK_SORCERY:
    case SK_EVOCATION:
    case SK_PYROMANCY:
    case SK_SUMMONING:
  case SK_CLIMBING:   case SK_CLIMBING:
  case SK_BARGAINING:   case SK_BARGAINING:
      new_draw_info(NDI_UNIQUE, 0,op,"This skill is already in effect.");       new_draw_info(NDI_UNIQUE, 0,op,"This skill is already in effect.");
Line 251
 
Line 385
   
     /* For players we now update the speed_left from using the skill.       /* For players we now update the speed_left from using the skill.
      * Monsters have no skill use time because of the random nature in        * Monsters have no skill use time because of the random nature in
      * which use_monster_skill is called already simulates this. -b.t.       * which use_monster_skill is called already simulates this.
        * If certain skills should take more/less time, that should be
        * in the code for the skill itself.
      */       */
          
     if(op->type==PLAYER) op->speed_left -= get_skill_time(op,skill);      if(op->type==PLAYER) op->speed_left -= 1.0;
   
     /* this is a good place to add experience for successfull use of skills.      /* this is a good place to add experience for successfull use of skills.
      * Note that add_exp() will figure out player/monster experience       * Note that add_exp() will figure out player/monster experience
      * gain problems.       * gain problems.
      */       */
    
     if(success&&skills[skill].category!=EXP_NONE) add_exp(op,exp);      if(success && exp) change_exp(op,exp, skill->skill, 0);
                          
     return success;      return success;
 }  }
Line 285
 
Line 421
  * This revised method prevents some cases where there are big gaps   * This revised method prevents some cases where there are big gaps
  * in the amount you get just because you are now equal level vs lower   * in the amount you get just because you are now equal level vs lower
  * level   * level
    * who is player/creature that used the skill.
    * op is the object that was 'defeated'.
    * skill is the skill used.  If no skill is used, it should just
    * point back to who.
  *   *
  */   */
   
 int calc_skill_exp(object *who, object *op) {  int calc_skill_exp(object *who, object *op, object *skill) {
     int who_lvl= SK_level(who);      int op_exp=0,op_lvl= 0;
     int sk,op_exp=0,op_lvl= 0;      float base,value,lvl_mult=0.0;
     float base,value,stat_mult=1.0,lvl_mult=0.0;  
       if (!skill) skill = who;
     /* sometimes we dont have a specific opponent to 'overcome'.  
      * 'hiding' is just such a case. For these skills, our  
      * 'opponent is the map we are on. Thus experience  
      * gain is just skills[sk].bexp, and modified by the  
      * map difficulty instead of an opponent SK_level.   
      * In the case that map difficulty =< player then op_lvl = 0  
      */  
   
     /* Oct 95 - where we have an object, I expanded our treatment      /* Oct 95 - where we have an object, I expanded our treatment
      * to 3 cases:       * to 3 cases:
Line 320
 
Line 453
         op_lvl = op->level;          op_lvl = op->level;
     } else { /* all other items/living creatures */       } else { /* all other items/living creatures */
         op_exp = op->stats.exp;          op_exp = op->stats.exp;
         if(!QUERY_FLAG(op,FLAG_ALIVE)) { /* for ident/make items */   
     op_lvl = op->level;      op_lvl = op->level;
      /* This makes no sense, lets simplify it */          if(!QUERY_FLAG(op,FLAG_ALIVE)) { /* for ident/make items */
      /*op_lvl += op->magic ? 0: (5 * abs(op->magic));*/  
      op_lvl += 5 * abs(op->magic);       op_lvl += 5 * abs(op->magic);
  } else /* its a monster, grab its level */   }
     op_lvl= SK_level(op);  
     }      }
   
     if(op_lvl<1) op_lvl = 1;      if(op_lvl<1) op_lvl = 1;
Line 334
 
Line 464
     if(who->type!=PLAYER) {             /* for monsters only */      if(who->type!=PLAYER) {             /* for monsters only */
         return ((int) (op_exp*0.1)+1); /* we add one to insure positive value is returned */          return ((int) (op_exp*0.1)+1); /* we add one to insure positive value is returned */
     } else {                            /* for players */      } else {                            /* for players */
       /* FIRST, what skill are we using? */   base = op_exp;
       if(who->chosen_skill==NULL) {   /* if skill really is a skill, then we can look at the skill archetype for
           LOG(llevError,"Bad call of calc_skill_exp() by player. \n");   * bse reward value (exp) and level multiplier factor.
           return 0;   */
       } else   if (skill->type == SKILL) {
           sk = who->chosen_skill->stats.sp;       base += skill->arch->clone.stats.exp;
        if (settings.simple_exp) {
       base = op_exp + skills[sk].bexp;  /* get base exp */   if (skill->arch->clone.level)
        lvl_mult = (float) skill->arch->clone.level / 100.0;
 #if 0   else
   /* get level multiplier */       lvl_mult = 1.0; /* no adjustment */
       if(who_lvl < op_lvl)               }
           lvl_mult=(float) skills[sk].lexp * (float) ((float) op_lvl - (float) who_lvl);  
       else if(who_lvl == op_lvl)          
           lvl_mult = (float) skills[sk].lexp;  
       else /* player is higher level than the object */  
           lvl_mult = ((float) ((float) op_lvl)/((float) who_lvl));  
 #else  
  /* The above was broken - since skills have different lexp multipliers,  
  * there are cases where the player being higher level gains more exp.  
  * Also, the simple difference times multiplier could result in a lot  
  * of exp in some cases.  
  */  
  if (settings.simple_exp) lvl_mult = (float) skills[sk].lexp;  
  else {   else {
      lvl_mult = ((float) skills[sk].lexp * (float) op_lvl) / (float) who_lvl;   if (skill->level)
        lvl_mult = ((float) skill->arch->clone.level * (float) op_lvl) / ((float) skill->level * 100.0);
    else
        lvl_mult = 1.0;
        }
    } else {
        lvl_mult = (float) op_lvl / (float) skill->level;
  }   }
 #endif   
   
       stat_mult = calc_stat_mult(who,sk);  
     }      }
    
     /* assemble the exp total, and return value */      /* assemble the exp total, and return value */
    
     value =  base * (lvl_mult * stat_mult);      value =  base * lvl_mult;
       if (value < 1) value=1; /* Always give at least 1 exp point */
          
 #ifdef SKILL_UTIL_DEBUG  #ifdef SKILL_UTIL_DEBUG
       LOG(llevDebug,"calc_skill_exp(): who: %s(lvl:%d)  op:%s(lvl:%d)\n",        LOG(llevDebug,"calc_skill_exp(): who: %s(lvl:%d)  op:%s(lvl:%d)\n",
  who->name,who_lvl,op->name,op_lvl);   who->name,skill->level,op->name,op_lvl);
       LOG(llevDebug,"%s(%s): base_exp=%f lvl_mult=%f stat_mult=%f value=%f\n",  
                 who->name,skills[sk].name,base,lvl_mult,stat_mult,value);  
 #endif  #endif
     return ( (int) value);       return ( (int) value);
 }  }
   
   /* Learn skill. This inserts the requested skill in the player's
 /* calc_stat_mult() - figures a factor to modify gained experience by   * inventory. The skill field of the scroll should have the
  * based on the sum of relvant stats of skill 'sk' being used by 'who'.   * exact name of the requested skill.
    * This one actually teaches the player the skill as something
    * they can equip.
    * Return 0 if the player knows the skill, 1 if the
    * player learns the skill, 2 otherwise.
  */   */
    
 float calc_stat_mult(object *who,int sk) {  int
     int sum;  learn_skill (object *pl, object *scroll) {
     float factor = 1.0;      object *tmp;
   
     if (settings.simple_exp) return 1.0;  
            
     sum = get_weighted_skill_stat_sum (who,sk);  
    
     /* look up factor for weighted sum */  
     if(sum <MAX_STAT) factor = stat_exp_mult[sum];  
    
     return factor;      if (!scroll->skill) {
    LOG(llevError,"skill scroll %s does not have skill pointer set.\n", scroll->name);
    return 2;
 }  }
   
 /* find relevant stats or a skill then return their weighted sum.       /* can't use find_skill_by_name because we want skills the player knows
  * I admit the calculation is done in a retarded way.       * but can't use natively.
  * If stat1==NO_STAT_VAL this isnt an associated stat. Returns  
  * zero then. -b.t.   
  */     */ 
   
 int get_weighted_skill_stat_sum ( object *who, int sk) {      for (tmp=pl->inv; tmp!=NULL; tmp=tmp->below)
    float sum;   if (tmp->type == SKILL && !strncasecmp(scroll->skill, tmp->skill, strlen(scroll->skill))) break;
    int number = 1;  
    
     if(skills[sk].stat1==NO_STAT_VAL) {  
           return 0;  
     } else  
          sum = get_attr_value(&(who->stats),skills[sk].stat1);  
   
     if(skills[sk].stat2!=NO_STAT_VAL) {      if (!tmp)
          sum += get_attr_value(&(who->stats),skills[sk].stat2);   tmp = give_skill_by_name(pl, scroll->skill);
          number++;  
     }  
   
     if(skills[sk].stat3!=NO_STAT_VAL) {      if (!tmp) {
          sum += get_attr_value(&(who->stats),skills[sk].stat3);   LOG(llevError,"skill scroll %s does not have valid skill name (%s).\n", scroll->name, scroll->skill);
          number++;   return 2;
     }      }
   
     return ((int) sum/number);      /* player already knows it */
 }      if (QUERY_FLAG(tmp, FLAG_CAN_USE_SKILL)) return 0;
   
 /* init_new_exp_system() - called in init(). This function will reconfigure      /* now a random change to learn, based on player Int.
  * the properties of skills array and experience categories, and links       * give bonus based on level - otherwise stupid characters
  * each skill to the appropriate experience category       * might never be able to learn anything.
  * -b.t. thomas@astro.psu.edu  
  */   */
       if(random_roll(0, 99, pl, PREFER_LOW)>(learn_spell[pl->stats.Int] + (pl->level/5)))
    return 2; /* failure :< */
   
 void init_new_exp_system() {      SET_FLAG(tmp, FLAG_CAN_USE_SKILL);
   static int init_new_exp_done = 0;      link_player_skills(pl);
       return 1;
   if (init_new_exp_done)  
     return;  
   init_new_exp_done = 1;  
    
   init_exp_obj();     /* locate the experience objects and create list of them */  
   link_skills_to_exp(); /* link skills to exp cat, based on shared stats */  
   (void) read_skill_params(); /* overide skills[] w/ values from skill_params */   
   
 }  }
 void dump_skills()  
 {  
     char buf[MAX_BUF];  
     int i;  
   
     fprintf(stderr, "\n");  
     fprintf(stderr, "exper_catgry \t str \t dex \t con \t wis \t cha \t int \t pow \n");  
     for (i = 0; i < nrofexpcat; i++)  
       fprintf(stderr, "%d-%s \t %d \t %d \t %d \t %d \t %d \t %d \t %d \n",  
  i,exp_cat[i]->name,  
                 exp_cat[i]->stats.Str,exp_cat[i]->stats.Dex,exp_cat[i]->stats.Con,  
                 exp_cat[i]->stats.Wis,exp_cat[i]->stats.Cha,exp_cat[i]->stats.Int,   
  exp_cat[i]->stats.Pow);  
    
     fprintf(stderr, "\n");  
     sprintf(buf,"%20s  %12s  %4s %4s %4s  %5s %5s %5s\n",  
  "sk#       Skill name","ExpCat","Time","Base","xlvl","Stat1","Stat2","Stat3");  
     fprintf(stderr, buf);  
     sprintf(buf,"%20s  %12s  %4s %4s %4s  %5s %5s %5s\n",  
  "---       ----------","------","----","----","----","-----","-----","-----");  
     fprintf(stderr, buf);  
     for (i = 0; i < NROFSKILLS; i++) {  
       sprintf(buf,"%2d-%17s  %12s  %4d %4ld %4g  %5s %5s %5s\n", i, skills[i].name,  
         exp_cat[skills[i].category]!=NULL?exp_cat[skills[i].category]->name:"NONE",  
         skills[i].time,  
         skills[i].bexp,  
         skills[i].lexp,  
         skills[i].stat1!= NO_STAT_VAL ? short_stat_name[skills[i].stat1]: "---",  
         skills[i].stat2!= NO_STAT_VAL ? short_stat_name[skills[i].stat2]: "---",  
         skills[i].stat3!= NO_STAT_VAL ? short_stat_name[skills[i].stat3]: "---");  
       fprintf(stderr, buf);   
     }  
     exit(0);  
 }  
   
 /* init_exp_obj() - this routine looks through all the assembled  
  * archetypes for experience objects then copies their values into  
  * the exp_cat[] array. - bt.  
  */  
    
 void init_exp_obj() {  
     archetype *at;  
   
     nrofexpcat = 0;  /* Gives a percentage clipped to 0% -> 100% of a/b. */
     for(at=first_archetype;at!=NULL;at=at->next)  /* Probably belongs in some global utils-type file? */
         if(at->clone.type==EXPERIENCE) {  static int clipped_percent(int a, int b)
              exp_cat[nrofexpcat] = get_object() ;  {
       exp_cat[nrofexpcat]->level = 1;    int rv;
       exp_cat[nrofexpcat]->stats.exp = 0;  
              copy_object(&at->clone, exp_cat[nrofexpcat]);  
              nrofexpcat++;  
              if(nrofexpcat == MAX_EXP_CAT) {  
                  LOG(llevError,"Aborting! Reached limit of available experience\n");  
                  LOG(llevError,"categories. Need to increase value of MAX_EXP_CAT.\n");  
                  exit (0);  
              }  
         }  
    
     if(nrofexpcat == 0) {    if (b <= 0)
         LOG(llevError,"Aborting! No experience objects found in archetypes.\n");      return 0;
         exit (0);  
     }   
 }  
 /* link_skills_to_exp() - this links the skills in skills[] to the appropriate  
  * experience category.  Linking is based only on the stats (1,2,3) of the  
  * skill. If none of the stats assoc. w/ a skill match those in any experience   
  * object then the skill becomes 'miscellaneous' -b.t.  
  */  
    
 void link_skills_to_exp() {    rv = (int)((100.0f * ((float)a) / ((float)b) ) + 0.5f);
    int i=0,j=0;  
    
         for(i=0;i<NROFSKILLS;i++)    if (rv < 0)
             for(j=0;j<nrofexpcat;j++)      return 0;
                 if(check_link(skills[i].stat1,exp_cat[j])) {    else if (rv > 100)
                         skills[i].category = j;      return 100;
                         continue;  
                 } else if(skills[i].category == EXP_NONE   
  && check_link(skills[i].stat2,exp_cat[j])) {  
                         skills[i].category = j;  
                         continue;  
                 } else if(skills[i].category == EXP_NONE   
  && check_link(skills[i].stat3,exp_cat[j])) {  
                         skills[i].category = j;  
                         continue;  
         /* failed to link, set to EXP_NONE */  
                 } else if (j==nrofexpcat || skills[i].stat1 == NO_STAT_VAL) {  
                         skills[i].category = EXP_NONE;  
                         continue;  
                 }  
   
     return rv;
 }  }
   
 /* check_link() - this returns true if the specified stat is set in the  /* show_skills() - Meant to allow players to examine
  * experience object. Wish this was more 'generalized'. Added new stats   * their current skill list.
  * will cause this routine to abort CF. - b.t.   * This shows the amount of exp they have in the skills.
    * we also include some other non skill related info (god,
    * max weapon improvments, item power).
    * Note this function is a bit more complicated becauase we
    * we want ot sort the skills before printing them.  If we
    * just dumped this as we found it, this would be a bit
    * simpler.
  */   */
    
 int check_link (int stat, object *exp) {  void show_skills(object *op) {
        object *tmp=NULL;
         switch (stat) {      char buf[MAX_BUF], *cp;
             case STR:      int i,num_skills_found=0;
                 if(exp->stats.Str) return 1;      static char *periods="........................................";
                 break;      /* Need to have a pointer and use strdup for qsort to work properly */
             case CON:      char skills[NUM_SKILLS][MAX_BUF];
                 if(exp->stats.Con) return 1;  
                 break;  
             case DEX:  
                 if(exp->stats.Dex) return 1;  
                 break;  
             case INT:  
                 if(exp->stats.Int) return 1;  
                 break;  
             case WIS:  
                 if(exp->stats.Wis) return 1;  
                 break;  
             case POW:  
                 if(exp->stats.Pow) return 1;  
  break;  
             case CHA:  
                 if(exp->stats.Cha) return 1;  
                 break;  
             case NO_STAT_VAL:  
                 return 0;  
             default:  
                 LOG(llevError, "Aborting! Tried to link skill with unknown stat!\n");  
                 exit (0);  
         }  
         return 0;  
 }          
   
 /* read_skill_params() - based on init_spell_params(). This  
  * function should read a file 'skill_params' in the /lib   
  * directory.  -b.t.  
  *  
  *  format of the file 'skill_params' is:  
  * name  
  *      EXP_CAT, bexp, lexp, stat1, stat2, stat3  
  *   
  *  see the values in lib/skill_params for more inspiration/direction  
  */  
   
 void read_skill_params () {      for (tmp=op->inv; tmp!=NULL; tmp=tmp->below) {
   FILE *skill_params;   if (tmp->type == SKILL) {
   char fname[MAX_BUF];       /* Basically want to fill this out to 40 spaces with periods */
   char skill_name[256];       sprintf(buf,"%s%s", tmp->name, periods);
   char skill_attrib[256];       buf[40] = 0;
   int cat,bexp,time,stat1,stat2,stat3,skillindex;  
   float lexp;  
    
   sprintf(fname,"%s/%s",settings.confdir,"skill_params");  
   LOG(llevDebug,"Reading skill_params from %s...",fname);  
   if(! (skill_params=fopen(fname,"r")))  
     {  
         perror(fname);  
         return;  
     }  
    
   while(!feof(skill_params))       if (settings.use_permanent_experience) {
     {   sprintf(skills[num_skills_found++],"%slvl:%3d (xp:%lld/%lld/%d%%)",
  /* Large buf, so that long comments don't kill it. */   buf,tmp->level,
  fgets(skill_name,255,skill_params);   tmp->stats.exp,
  if (*skill_name=='#') continue;   level_exp(tmp->level+1, op->expmul),
         skillindex=lookup_skill_by_name(skill_name);   clipped_percent(tmp->last_heal,tmp->stats.exp));
         if(skillindex == -1) {       } else {
             LOG(llevError,"\nskill_params has unrecognized skill: %s",skill_name);   sprintf(skills[num_skills_found++], "%slvl:%3d (xp:%lld/%lld)",
             continue;   buf,tmp->level,
    tmp->stats.exp,
    level_exp(tmp->level+1, op->expmul));
         }          }
         fgets(skill_attrib,255,skill_params);  
         sscanf(skill_attrib,"%d %d %d %f %d %d %d",  
  &cat,&time,&bexp,&lexp,&stat1,&stat2,&stat3);  
         skills[skillindex].category=cat;  
         skills[skillindex].time=time;  
         skills[skillindex].bexp=bexp;  
         skills[skillindex].lexp=lexp;  
         skills[skillindex].stat1=stat1;  
         skills[skillindex].stat2=stat2;  
         skills[skillindex].stat3=stat3;  
     }      }
     fclose(skill_params);  
     LOG(llevDebug,"done.\n");  
 }  }
   
 /* lookup_skill_by_name() - based on look_up_spell_by_name - b.t.      clear_win_info(op);
  * Given name, we return the index of skill 'string' in the skill      new_draw_info(NDI_UNIQUE, 0,op,"Player skills:");
  * array, -1 if no skill is found.      if (num_skills_found > 1) qsort(skills, num_skills_found, MAX_BUF, (int (*)())strcmp);
  */  
   
 int lookup_skill_by_name(char *string) {      for (i=0; i<num_skills_found; i++) {
   int skillnr=0, nmlen;   new_draw_info(NDI_UNIQUE, 0, op, skills[i]);
   char name[MAX_BUF];      }
   
   if(!string) return -1;      new_draw_info_format(NDI_UNIQUE, 0, op,
    "You can handle %d weapon improvements.",op->level/5+5);
   
   strcpy(name, string);      cp = determine_god(op);
   nmlen=strlen(name);      new_draw_info_format(NDI_UNIQUE, 0, op,
    "You worship %s.", cp?cp:"no god at current time");
   
   for (skillnr=0; skillnr<NROFSKILLS; skillnr++) {      new_draw_info_format(NDI_UNIQUE,0,op, "Your equipped item power is %d out of %d\n",
  if (strlen(name)>=strlen(skills[skillnr].name)) /* GROS - This is to prevent strings like "hi" to be matched as "hiding" */   op->contr->item_power, op->level);
  if (!strncmp(name,skills[skillnr].name,MIN(strlen(skills[skillnr].name),  
      nmlen))) return skillnr;  
   }  
   return -1;  
 }  }
   
 /* check_skill_to_fire() -  */  /* use_skill() - similar to invoke command, it executes the skill in the
 int check_skill_to_fire(object *who) {   * direction that the user is facing. Returns false if we are unable to
     int skillnr=0;   * change to the requested skill, or were unable to use the skill properly.
     rangetype shoottype=range_none;   * This is tricky because skills can have spaces.  We basically roll
    * our own find_skill_by_name so we can try to do better string matching.
     if(who->type!=PLAYER) return 1;   */
   
     shoottype = who->contr->shoottype;  
     switch(shoottype) {  
   
  case range_bow:  int use_skill(object *op, char *string) {
      skillnr = SK_MISSILE_WEAPON;      object *skop;
      break;      int len;
   
  case range_none:      if (!string) return 0;
  case range_skill:  
      return 1;  
      break;  
   
  case range_magic:      for (skop = op->inv; skop != NULL; skop=skop->below) {
      if(spells[who->contr->chosen_spell].cleric)   if (skop->type == SKILL && QUERY_FLAG(skop, FLAG_CAN_USE_SKILL) &&
  skillnr = SK_PRAYING;       !strncasecmp(string, skop->skill, MIN(strlen(string), strlen(skop->skill))))
      else  
  skillnr = SK_SPELL_CASTING;  
      break;       break;
    else if (skop->type == SKILL_TOOL &&
  case range_golem:       !strncasecmp(string, skop->skill, MIN(strlen(string), strlen(skop->skill))))
  case range_misc:  
      skillnr = SK_USE_MAGIC_ITEM;  
      break;       break;
   
  default:  
      LOG(llevError,"Warning: bad call of check_skill_to_fire()\n");  
      return 0;  
     }      }
     if (change_skill(who,skillnr)) {       if (!skop) {
 #ifdef SKILL_UTIL_DEBUG   new_draw_info_format(NDI_UNIQUE, 0, op,
       LOG(llevDebug,"check_skill_to_fire(): got skill:%s for %s\n"        "Unable to find skill %s", string);
         ,skills[skillnr].name,who->name);  
 #endif  
  who->contr->shoottype=shoottype;  
  return 1;  
     } else   
  return 0;   return 0;
 }  }
   
 /* check_skill_to_apply() - When a player tries to use an      len=strlen(skop->skill);
  * object which requires a skill this function is called.  
  * (examples are weapons like swords and bows)  
  * It does 2 things: checks for appropriate skill in player inventory  
  * and alters the player status by making the appropriate skill  
  * the new chosen_skill. This routine works for both players  
  * applying and unapplying objects. Returns true if the skill exists  
  * and could be readied or we successfully unapplyied an object.  
  * -bt. thomas@astro.psu.edu  
  */  
   
 /* IMPORTANT NOTE: always call this fctn *after* FLAG_APPLY is set/unset      /* All this logic goes and skips over the skill name to find any
  * and *before* change_abil() is called.       * options given to the skill.  Its pretty simple - if there
        * are extra parameters (as deteremined by string length), we
        * want to skip over any leading spaces.
  */   */
       if(len>=strlen(string)) {
    string=NULL;
       } else {
    string += len;
    while(*string==0x20) string++;
    if(strlen(string)==0) string = NULL;
       }
    
 int check_skill_to_apply(object *who, object *item) {  #ifdef SKILL_UTIL_DEBUG
     int skill=0;      LOG(llevDebug,"use_skill() got skill: %s\n",sknum>-1?skills[sknum].name:"none");
     rangetype shoottype=range_none;  #endif
    
     if(who->type!=PLAYER) return 1; /* this fctn only for players */  
   
     /* first figure out the required skill and ending shoottype            
      * from the item */  
    
     switch(item->type) {  
  case WEAPON:  
      skill = SK_MELEE_WEAPON;  
      shoottype = range_skill;  
      break;  
   
  case BOW:  
      skill = SK_MISSILE_WEAPON;  
      shoottype = range_bow;  
      break;  
   
  case POTION:  
  case SCROLL:  
      skill = SK_USE_MAGIC_ITEM; /* not literacy! */  
      shoottype = range_golem;  
      break;  
   
  case ROD:      /* Change to the new skill, then execute it. */
  case WAND:                      if(do_skill(op,op,skop, op->facing,string)) return 1;
  case HORN:                  
      skill = SK_USE_MAGIC_ITEM;  
      shoottype = range_misc;   
      break;  
   
  default:  
      LOG(llevDebug,"Warning: bad call of check_skill_to_apply()\n");  
      LOG(llevDebug,"No skill exists for item: %s\n",query_name(item));  
      return 0;       return 0;
     }      }
    
     /* now we alter player status appropriately */  
    
     if(!QUERY_FLAG(item,FLAG_APPLIED)) {     /* we are trying to unapply */  
          if(who->chosen_skill && who->chosen_skill->stats.sp == skill)  
            (void) change_skill(who,-1);  
         return 0;  
     } else {                               /* we are trying to apply */  
         if(!who->chosen_skill || (who->chosen_skill && who->chosen_skill->stats.sp!=skill))  
      if(!change_skill(who,skill)) {  
  new_draw_info(NDI_UNIQUE, 0,who,"You can't use that!");  
  CLEAR_FLAG(item,FLAG_APPLIED);  
  return 0;  
      }  
     }  
     who->contr->shoottype=shoottype;  
     return 1;  
 }  
   
 /* init_player_exp() - makes various checks and initialization of player   /* This finds the best unarmed skill the player has, and returns
  * experience objects. If things aren't cool then we change them here.   * it.  Best can vary a little - we consider clawing to always
  *  -b.t.   * be the best for dragons.
    * This could be more intelligent, eg, look at the skill level
    * of the skill and go from there (eg, a level 40 puncher is
    * is probably better than level 1 karate).  OTOH, if you
    * don't bother to set up your skill properly, that is the players
    * problem (although, it might be nice to have a preferred skill
    * field the player can set.
    * Unlike the old code, we don't give out any skills - it is
    * possible you just don't have any ability to get into unarmed
    * combat.  If everyone race/class should have one, this should
    * be handled in the starting treasurelists, not in the code.
  */   */
   static object *find_best_player_hth_skill(object *op)
   {
       object *tmp, *best_skill=NULL;
       int dragon = is_dragon_pl(op), last_skill=sizeof(unarmed_skills), i;
   
 int init_player_exp(object *pl) {      for (tmp=op->inv; tmp; tmp=tmp->below) {
   int i,j,exp_index=0;   if (tmp->type == SKILL) {
   object *tmp,*exp_ob[MAX_EXP_CAT];       if (dragon && tmp->subtype == SK_CLAWING) return tmp;
   
    if(pl->type!=PLAYER) {  
         LOG(llevError, "init_player_exp(): called non-player %s.\n",pl->name);  
  return 0;  
    }  
   
    pl->contr->last_skill_index = 0;  
   
    /* first-pass find all current exp objects */  
    for(tmp=pl->inv;tmp;tmp=tmp->below)  
       if(tmp->type==EXPERIENCE) {  
      exp_ob[exp_index] = tmp;  
      find_skill_exp_name(pl, tmp, pl->contr->last_skill_index);  
      exp_index++;  
       } else if (exp_index == MAX_EXP_CAT)  
        return 0;   
    
    /* second - if pl has wrong nrof experience objects       /* The order in the array is preferred order.  So basically,
     * then give player a complete roster.        * we just cut down the number to search - eg, if we find a skill
         * early on in flame touch, then we only need to look into the unarmed_array
         * to the entry before flame touch - don't care about the entries afterward,
         * because they are infrerior skills.
         * if we end up finding the best skill (i==0) might as well return
         * right away - can't get any better than that.
     */         */   
        for (i=0; i<last_skill; i++) {
   /*  No exp objects - situation for a new player    if (tmp->subtype == unarmed_skills[i]) {
    *  or pre-skills/exp code player file */       best_skill = tmp;
    if(!exp_index) {         last_skill = i;
         for(j=0;j<nrofexpcat;j++) {       if (i==0) return best_skill;
      tmp = get_object();  
      copy_object(exp_cat[j],tmp);  
      insert_ob_in_ob(tmp,pl);  
      tmp->stats.exp = pl->stats.exp/nrofexpcat;  
      exp_ob[j] = tmp;  
      esrv_send_item(pl, tmp);  
      exp_index++;  
      find_skill_exp_name(pl, tmp, pl->contr->last_skill_index);  
         }  
   } else if (exp_index!=nrofexpcat) { /* situation for old save file */  
      if(exp_index<nrofexpcat)   
          LOG(llevError,"init_player_exp(): player has too few experience objects.\n");  
      if(exp_index>nrofexpcat)   
          LOG(llevError,"init_player_exp(): player has too many experience objects.\n");  
      for(j=0;j<nrofexpcat;j++)  
          for(i=0;i<=exp_index;i++)  
            if(!strcmp(exp_ob[i]->name,exp_cat[j]->name)) {  
               break;  
            } else if(i==exp_index) {  
               tmp = get_object();  
               copy_object(exp_cat[j],tmp);  
               insert_ob_in_ob(tmp,pl);  
        exp_ob[i] = tmp;   
               esrv_send_item(pl, tmp);  
               exp_index++;  
            }             }
    }     }
   
    /* now we loop through one more time and set "apply"  
     * flag on valid expericence objects. Fix_player()  
     * requires this, and we get the bonus of being able  
     * to ignore invalid experience objects in the player  
     * inventory (player file from another game set up?).  
     * Also, we reset the score (or "total" experience) of   
     * the player to be the sum of all valid experience   
     * objects.  
     */  
   
    pl->stats.exp = 0;  
    for(i=0;i<exp_index;i++) {   
  if(!QUERY_FLAG(exp_ob[i],FLAG_APPLIED))   
        SET_FLAG(exp_ob[i],FLAG_APPLIED);   
         /* GD: Update perm exp when loading player. */  
         if (settings.use_permanent_experience)  
             calc_perm_exp(exp_ob[i]);  
  pl->stats.exp += exp_ob[i]->stats.exp;  
  player_lvl_adj(NULL, exp_ob[i]);  
    }      }
    return 1;      }
       return best_skill;
 }  }
   
   /* do_skill_attack() - We have got an appropriate opponent from either
    * move_player_attack() or skill_attack(). In this part we get on with
    * attacking, take care of messages from the attack and changes in invisible.
    * Returns true if the attack damaged the opponent.
    * tmp is the targetted monster.
    * op is what is attacking
    * string is passed along to describe what messages to describe
    * the damage.
    */
   
 /* unlink_skill() - removes skill from a player skill list and  static int do_skill_attack(object *tmp, object *op, char *string, object *skill) {
  * unlinks the pointer to the exp object */       int success;
   
 void unlink_skill(object *skillop) {     /* For Players only: if there is no ready weapon, and no "attack" skill
   object *op=skillop?skillop->env:NULL;      * is readied either then try to find a skill for the player to use.
       * it is presumed that if skill is set, it is a valid attack skill (eg,
       * the caller should have set it appropriately).  We still want to pass
       * through that code if skill is set to change to the skill.
       */
       if(op->type==PLAYER) {
    if (!QUERY_FLAG(op,FLAG_READY_WEAPON)) {
        int i;
   
   if(!op||op->type!=PLAYER) {        if (!skill) {
  LOG(llevError,"Error: unlink_skill() called for non-player!\n");   /* See if the players chosen skill is a combat skill, and use
  return;   * it if appropriate.
    */
    if (op->chosen_skill) {
        for (i=0; i<sizeof(unarmed_skills); i++)
    if (op->chosen_skill->subtype == unarmed_skills[i]) {
        skill = op->chosen_skill;
        break;
   }    }
   skillop->exp_obj = NULL;  
 }  }
    /* If we didn't find a skill above, look harder for a good skill */
    if (!skill) {
        skill = find_best_player_hth_skill(op);
   
        if (!skill) {
 /* link_player_skills() - linking skills with experience objects   new_draw_info(NDI_UNIQUE, 0, op, "You have no unarmed combat skills!");
  * and creating a linked list of skills for later fast access.  
  *  
  * Returns true if successfull. To be usefull this routine has to  
  * be called *after* init_player_exp() and give_initial_items()  
  *  
  * fix_player() should be called somewhere after this function because objects  
  * are unapplied here, but fix_player() is not called here.  
  *  
  * Aug 95 added feature whereby old player save files will get a  
  * (needed) basic assortment of skills (see basic_skill array).  
  *   
  * Sept 96. deleted spellcasting and praying as 'basic' skills,  
  * then added in throwing to basic list. -b.t.  
  *  
  * - b.t. thomas@astro.psu.edu  
  */  
   
 int link_player_skills(object *pl) {  
   archetype *at=NULL;  
   int i,j,cat=0,sk_index=0,exp_index=0,old_file=1;  
   object *tmp,*sk_ob[100],*exp_ob[MAX_EXP_CAT];  
   static char *basic_skills[] = { "skill_melee_weapon",  
     "skill_missile_weapon",  
     "skill_use_magic_item",  
     "skill_throwing",  
     "skill_find_traps",  
     "skill_remove_trap"};  
   
    /* We're going to unapply all skills */  
    pl->chosen_skill = NULL;  
    CLEAR_FLAG (pl, FLAG_READY_SKILL);  
    pl->contr->last_skill_index = 0;  
   
    /* first find all exp and skill objects */  
    for(tmp=pl->inv;tmp&&sk_index<100;tmp=tmp->below)  
       if(tmp->type==EXPERIENCE) {  
            exp_ob[exp_index] = tmp;  
     find_skill_exp_name(pl, tmp, pl->contr->last_skill_index);  
            tmp->nrof=1; /* to handle multiple instances */  
            exp_index++;  
       } else if (tmp->type==SKILL) {  
            /* for startup, lets unapply all skills */  
            CLEAR_FLAG (tmp, FLAG_APPLIED);  
            if (tmp->invisible) {  
              tmp->nrof=1; /* to handle multiple instances */  
              sk_ob[sk_index]=tmp;   
              sk_index++;  
            }  
     if(!strcmp(tmp->name,skills[SK_USE_MAGIC_ITEM].name)) old_file = 0;  
       }   
   
    if(exp_index!=nrofexpcat) {  
        LOG(llevError,"link_player_skills() - player has bad number of exp obj\n");  
        if(!init_player_exp(pl)) {   
           LOG(llevError,"link_player_skills() - failed to correct problem.\n");  
    return 0;     return 0;
        }         }
        (void) link_player_skills(pl);  
        return 1;  
    }   
   
    /* This looks like we have an old player file, so give those basic  
     * skills to the player in this case.   
     */  
  /* No, don't do this anymore. */  
    if(0) {   
  object *tmp2;  
  int limit = sizeof(basic_skills)/sizeof(char *);  
   
  /* this is solely for old characters */  
  if(pl->contr->orig_stats.Pow==0) {   
  pl->stats.Pow = pl->stats.Int;  
  pl->contr->orig_stats.Pow = pl->contr->orig_stats.Int;  
  }  
   
  for(i=0;i<limit;i++)   
     if((at=find_archetype(basic_skills[i]))!=NULL) {  
        int add = 1;  
        tmp = arch_to_object(at);  
        /* check if they already have this (basic) skill */  
        for(tmp2=pl->inv;tmp2;tmp2=tmp2->below)  
  if(tmp2->type==SKILL && !strcmp(tmp2->name,tmp->name)) {  
  add = 0;  
  break;  
  }   }
   
  if(add) {   
    insert_ob_in_ob(tmp,pl);  
    sk_ob[sk_index] = tmp;  
    sk_index++;   
  LOG(llevDebug,"Added basic skill: %s to inventory of %s\n",  
  basic_skills[i], pl->name);  
        }         }
     } else {        if (skill != op->chosen_skill) {
    LOG(llevError,    /* now try to ready the new skill */
  "init_player_skills() - can't find basic skill: %s\n",basic_skills[i]);   if(!change_skill(op,skill,0)) {  /* oh oh, trouble! */
        new_draw_info_format(NDI_UNIQUE, 0, tmp, "Couldn't change to skill %s", skill->name);
    return 0;     return 0;
     }      }
    
  }   }
   /* Ok, create linked list and link the associated skills to exp objects */   } else {
    for(i=0;i<sk_index;i++) {       /* Has ready weapon - make sure chosen_skill is set up properly */
         cat = skills[sk_ob[i]->stats.sp].category;       if (!op->chosen_skill || op->current_weapon->skill != op->chosen_skill->skill) {
  if(cat==EXP_NONE) continue;   change_skill(op, find_skill_by_name(op, op->current_weapon->skill), 1);
         }
         for(j=0;exp_ob[j]!=NULL&&j<exp_index;j++)  
            if(!strcmp(exp_cat[cat]->name,exp_ob[j]->name)) break;  
    
         sk_ob[i]->exp_obj = exp_ob[j];  
    }     }
    return 1;  
 }     }   
   
 /* link_player_skill() - links a  skill to exp object when applied or learned by      /* lose invisiblity/hiding status for running attacks */
  * a player. Returns true if can link. Returns false if got misc  
  * skill - bt.  
  */   
   
 int link_player_skill(object *pl, object *skillop) {      if(op->type==PLAYER && op->contr->tmp_invis) {
   object *tmp;   op->contr->tmp_invis=0;
   int cat;   op->invisible=0;
    op->hide=0;
   cat = skills[skillop->stats.sp].category;    update_object(op,UP_OBJ_FACE);
   
   if(cat!=EXP_NONE) {   /* ok the skill has an exp object, now find right one in pl inv */   
       for(tmp=pl->inv;tmp;tmp=tmp->below)  
           if(tmp->type==EXPERIENCE&&!strcmp(exp_cat[cat]->name,tmp->name)) {   
               skillop->exp_obj = tmp;  
        break;  
     }  
       return 1;  
   }  
   skillop->exp_obj = NULL;  
   
   return 0;   
 }  
   
 /* Learn skill. This inserts the requested skill in the player's  
  * inventory. The 'slaying' field of the scroll should have the  
  * exact name of the requested archetype (there should be a better way!?)  
  * -bt. thomas@nomad.astro.psu.edu  
  */  
    
 int  
 learn_skill (object *pl, object *scroll) {  
     object *tmp;  
     archetype *skill;  
     object *tmp2;  
     int has_meditation=0;  
   
     skill=find_archetype(scroll->slaying);  
     if(!skill)  
  return 2;  
     tmp=arch_to_object(skill);  
     if(!tmp) return 2;  
   
     /* check if player already has it */  
     for (tmp2=pl->inv;tmp2;tmp2=tmp2->below) {  
  if(tmp2->type==SKILL&&tmp2->invisible) {  
      if  (tmp2->stats.sp==tmp->stats.sp) return 0;  
      else if (tmp2->stats.sp == SK_MEDITATION) has_meditation=1;  
  }  
     }  
   
     /* Special check - if the player has meditation (monk), they can not  
      * learn melee weapons.  Prevents monk from getting this  
      * skill.  
      */  
     if (tmp->stats.sp == SK_MELEE_WEAPON && has_meditation) {  
  new_draw_info(NDI_UNIQUE, 0,pl,"Your knowledge of inner peace prevents you from learning about melee weapons.");  
  return 2;  
     }  
     /* now a random change to learn, based on player Int */  
     if(random_roll(0, 99, pl, PREFER_LOW)>learn_spell[pl->stats.Int])  
  return 2; /* failure :< */  
   
     /* Everything is cool. Give'em the skill */  
     (void) insert_ob_in_ob(tmp,pl);  
     (void) link_player_skill(pl,tmp);    
     esrv_send_item(pl, tmp);  
     return 1;  
 }  
   
 /* Gives a percentage clipped to 0% -> 100% of a/b. */  
 /* Probably belongs in some global utils-type file? */  
 static int clipped_percent(int a, int b)  
 {  
   int rv;  
   
   if (b <= 0)  
     return 0;  
   
   rv = (int)((100.0f * ((float)a) / ((float)b) ) + 0.5f);  
     
   if (rv < 0)  
     return 0;  
   else if (rv > 100)  
     return 100;  
     
   return rv;  
 }  
   
 /* show_skills() - Meant to allow players to examine  
  * their current skill list. b.t. (thomas@nomad.astro.psu.edu)  
  * I have now added capability to show assoc. experience objects too.  
  * Inportant note: the value of chosen_skill->level is not always  
  * accurate--it is only updated in change_skill(), therefore long  
  * unused skills may have wrong value. This is why we call   
  * skill->exp_obj->level instead (the level of the associated experience  
  * object -- which is always right).  
  * This function is pretty inefficient - it basically goes through the  
  * players inventory 'nrofexpcat' times.  However, this isn't called  
  * that often, so I have no great desire to try to make it more  
  * efficient - MSW  
  */  
    
 void show_skills(object *op) {  
     object *tmp=NULL;  
     char buf[MAX_BUF], *in;  
     int i,length,is_first;  
     static char *periods="........................................";  
   
     length = 31;  
     in = "";  
     clear_win_info(op);  
     new_draw_info(NDI_UNIQUE, 0,op,"Player skills by experience category");  
   
     /* print out skills by category */  
     for (i=0;i<=nrofexpcat;i++) {  
  char Special[100];  
   
  Special[0]='\0';  
  is_first=1;  
  if(i==nrofexpcat) i=EXP_NONE; /* skip to misc exp category */  
   
  for (tmp=op->inv;tmp;tmp=tmp->below) {  
      if(tmp->type!=SKILL) continue;  
   
      /* Don't want to show info for all skills the player may be  
       * able to get by equipping different things.  Doing so would also  
       * result in things like spell casting being shown multiple times (one  
       * for each talisman  
       */  
      if(skills[tmp->stats.sp].category==i   
         &&(tmp->invisible||QUERY_FLAG(tmp,FLAG_APPLIED))) {  
    
  /* header info */  
  if(is_first) {    
      is_first=0;  
      new_draw_info(NDI_UNIQUE, 0,op," ");   
   
      if(tmp->exp_obj) {   
  /* Basically want to fill this out to 40 spaces with periods */  
  sprintf(buf,"%s%s", tmp->exp_obj->name, periods);  
  buf[40] = 0;  
   
  if (settings.use_permanent_experience) {  
      new_draw_info_format(NDI_UNIQUE,0,op,"%slvl:%3d (xp:%lld/%lld/%d%%)",  
  buf,tmp->exp_obj->level,  
  tmp->exp_obj->stats.exp,  
  level_exp(tmp->exp_obj->level+1, op->expmul),  
  clipped_percent(tmp->exp_obj->last_heal,tmp->exp_obj->stats.exp));  
  } else {  
      new_draw_info_format(NDI_UNIQUE,0,op,"%slvl:%3d (xp:%lld/%lld)",  
  buf,tmp->exp_obj->level,  
  tmp->exp_obj->stats.exp,  
  level_exp(tmp->exp_obj->level+1, op->expmul));  
  }  
  if (strcmp(tmp->exp_obj->name,"physique")==0) {  
      sprintf(Special,"You can handle %d weapon improvements.",tmp->exp_obj->level/5+5);  
  }  
  if (strcmp(tmp->exp_obj->name,"wisdom")==0) {  
      char *cp = determine_god(op);  
      sprintf(Special,"You worship %s.", cp?cp:"no god at current time");  
  }  
      } else if(i==EXP_NONE) {   
  new_draw_info(NDI_UNIQUE,0,op,"misc.");  
      }  
  } /* is_first */  
   
  /* print matched skills */  
  if((!op || QUERY_FLAG(op, FLAG_WIZ)))  
      new_draw_info_format(NDI_UNIQUE,0,op,   
  "%s%s %s (%5d)",in,  
  QUERY_FLAG(tmp,FLAG_APPLIED)?"*":"-",  
  skills[tmp->stats.sp].name,tmp->count);  
  else  
      new_draw_info_format(NDI_UNIQUE,0,op,   
  "%s%s %s",in,  
  QUERY_FLAG(tmp,FLAG_APPLIED)?"*":"-",  
  skills[tmp->stats.sp].name);  
      } /* if object is skill object */  
  } /* for this cycle through inventory */  
   
  /* After we print all the skills for a category, print special information */  
  if (Special[0]!='\0') {  
      new_draw_info(NDI_UNIQUE,0,op,Special);  
  }  
     } /* for all the exp categories */  
   
     /* This isn't really skill related.  However, this seems to be as good as any place  
      * to dump this information - after all, we are already showing the number of  
      * of allowed weapon enchantments  
      */  
   
     new_draw_info_format(NDI_UNIQUE,0,op, "Your equipped item power is %d out of %d\n",  
  op->contr->item_power, op->level);  
 }  
   
 /* use_skill() - similar to invoke command, it executes the skill in the   
  * direction that the user is facing. Returns false if we are unable to   
  * change to the requested skill, or were unable to use the skill properly.   
  * -b.t.  
  */  
   
 int use_skill(object *op, char *string) {  
     int sknum=-1;  
   
     /* the skill name appears at the begining of the string,  
      * need to reset the string to next word, if it exists. */  
     /* first eat name of skill and then eat any leading spaces */    
   
     if(string && (sknum=lookup_skill_by_name(string))>=0) {  
  int len;  
   
  if (sknum==-1) {  
      new_draw_info_format(NDI_UNIQUE, 0, op,   
  "Unable to find skill by name %s", string);  
      return 0;  
  }  
   
  len=strlen(skills[sknum].name);  
   
  /* All this logic goes and skips over the skill name to find any  
  * options given to the skill.  
  */  
  if(len>=strlen(string)) {  
      *string=0x0;  
  } else {  
      while(len--){ string++;}  
      while(*string==0x20){string++;}  
  }  
  if(strlen(string)==0) string = NULL;  
   
     }  
      
 #ifdef SKILL_UTIL_DEBUG  
   LOG(llevDebug,"use_skill() got skill: %s\n",sknum>-1?skills[sknum].name:"none");  
 #endif  
   
    /* Change to the new skill, then execute it. */  
    if(change_skill(op,sknum))  
  if(do_skill(op,op,op->facing,string)) return 1;  
    
     return 0;  
 }  
   
   
 /* change_skill() - returns true if we are able to change to the requested  
  * skill. Ignore the 'pl' designation, this code is useful for players and  
  * monsters.  -bt. thomas@astro.psu.edu  
  *  
  * sk_index == -1 means that old skill should be unapplied, and no new skill  
  * applied.  
  */  
   
 /* Sept 95. Got rid of nasty strcmp calls in here -b.t.*/  
   
 /* Dec 95 - cleaned up the code a bit, change_skill now passes an indexed  
  * value for the skill rather than a character string. Added find_skill.   
  * -b.t. */  
    
 int change_skill (object *who, int sk_index)  
 {  
     object *tmp;  
   
     if (who->chosen_skill && who->chosen_skill->stats.sp == sk_index)  
     {  
         /* optimization for changing skill to current skill */  
         if (who->type == PLAYER)  
             who->contr->shoottype = range_skill;  
         return 1;  
     }  
   
     if (sk_index >= 0 && sk_index < NROFSKILLS  
         && (tmp = find_skill (who, sk_index)) != NULL)  
     {  
         if (apply_special (who, tmp, AP_APPLY)) {  
             LOG (llevError, "BUG: change_skill(): can't apply new skill\n");  
             return 0;  
         }  
         return 1;  
     }  
   
     if (who->chosen_skill)  
         if (apply_special (who, who->chosen_skill, AP_UNAPPLY))  
             LOG (llevError, "BUG: change_skill(): can't unapply old skill\n");  
     if (sk_index >= 0)  
         new_draw_info_format (NDI_UNIQUE, 0, who, "You have no knowledge "  
                               "of %s.", skills[sk_index].name);  
     return 0;  
 }  
   
 /* This is like change_skill above, but it is given that  
  * skill is already in who's inventory - this saves us  
  * time if the caller has already done the work for us.  
  * return 0 on success, 1 on failure.  
  */  
   
 int change_skill_to_skill (object *who, object *skl)  
 {  
     if (!skl) return 1;     /* Quick sanity check */  
   
     if (who->chosen_skill == skl)  
     {  
         /* optimization for changing skill to current skill */  
         if (who->type == PLAYER)  
             who->contr->shoottype = range_skill;  
         return 0;  
     }  
   
     if (skl->env != who) {  
  LOG(llevError,"change_skill_to_skill: skill is not in players inventory\n");  
  return 1;  
     }  
   
     if (apply_special (who, skl, AP_APPLY)) {  
  LOG (llevError, "BUG: change_skill(): can't apply new skill\n");  
             return 1;  
     }  
     return 0;  
 }  }
   
 /* attack_melee_weapon() - this handles melee weapon attacks -b.t.      success = attack_ob(tmp,op);
  * For now we are just checking to see if we have a ready weapon here.  
  * But there is a real neato possible feature of this scheme which  
  * bears mentioning:  
  * Since we are only calling this from do_skill() in the future  
  * we may make this routine handle 'special' melee weapons attacks  
  * (like disarming manuever with sai) based on player SK_level and  
  * weapon type.  
  */  
   
 int attack_melee_weapon(object *op, int dir, char *string) {      /* print appropriate  messages to the player */
   
   if(!QUERY_FLAG(op, FLAG_READY_WEAPON)) {      if(success && string!=NULL) {
         if(op->type==PLAYER)          if(op->type==PLAYER)
           new_draw_info(NDI_UNIQUE, 0,op,"You have no ready weapon to attack with!");       new_draw_info_format(NDI_UNIQUE, 0,op,
         return 0;   "You %s %s!",string,query_name(tmp));
     }   else if(tmp->type==PLAYER)
        new_draw_info_format(NDI_UNIQUE, 0,tmp,
   return skill_attack(NULL,op,dir,string);   "%s %s you!",query_name(op),string);
   
 }  
   
 /* attack_hth() - this handles all hand-to-hand attacks -b.t. */  
    
 /* July 5, 1995 - I broke up attack_hth() into 2 parts. In the first  
  * (attack_hth) we check for weapon use, etc in the second (the new  
  * function skill_attack() we actually attack.  
  */  
   
 int attack_hth(object *pl, int dir, char *string) {  
   object *enemy=NULL,*weapon;  
   
   if(QUERY_FLAG(pl, FLAG_READY_WEAPON))  
     for(weapon=pl->inv;weapon;weapon=weapon->below) {  
         if(weapon->type!=WEAPON  
           ||!QUERY_FLAG(weapon, FLAG_APPLIED)) continue;  
         CLEAR_FLAG(weapon,FLAG_APPLIED);  
         CLEAR_FLAG(pl,FLAG_READY_WEAPON);  
         fix_player(pl);  
         if(pl->type==PLAYER) {  
      new_draw_info(NDI_UNIQUE, 0,pl,"You unwield your weapon in order to attack.");  
      esrv_update_item(UPD_FLAGS, pl, weapon);  
         }          }
         break;      return success;
     }         }   
    
   return skill_attack(enemy,pl,dir,string);  
    
 }  
   
 /* skill_attack() - Core routine for use when we attack using a skills  /* skill_attack() - Core routine for use when we attack using a skills
  * system. There are'nt too many changes from before, basically this is   * system. In essence, this code handles
  * a 'wrapper' for the old attack system. In essence, this code handles  
  * all skill-based attacks, ie hth, missile and melee weapons should be   * all skill-based attacks, ie hth, missile and melee weapons should be
  * treated here. If an opponent is already supplied by move_player(),   * treated here. If an opponent is already supplied by move_player(),
  * we move right onto do_skill_attack(), otherwise we find if an   * we move right onto do_skill_attack(), otherwise we find if an
Line 1363
 
Line 819
  * Initial implementation by -bt thomas@astro.psu.edu   * Initial implementation by -bt thomas@astro.psu.edu
  */   */
    
 int skill_attack (object *tmp, object *pl, int dir, char *string) {  int skill_attack (object *tmp, object *pl, int dir, char *string, object *skill) {
   int dx,dy;      sint16 tx,ty;
       mapstruct *m;
       int mflags;
    
   if(!dir) dir=pl->facing;    if(!dir) dir=pl->facing;
   dx=freearr_x[dir],dy=freearr_y[dir];      tx=freearr_x[dir];
       ty=freearr_y[dir];
    
 /* If we don't yet have an opponent, find if one exists, and attack.  /* If we don't yet have an opponent, find if one exists, and attack.
  * Legal opponents are the same as outlined in move_player_attack()   * Legal opponents are the same as outlined in move_player_attack()
  */   */
    
   if(tmp==NULL) {    if(tmp==NULL) {
     if (out_of_map(pl->map, pl->x+dx,pl->y+dy)) return 0;   m = pl->map;
     for(tmp=get_map_ob(pl->map,pl->x+dx,pl->y+dy);tmp;tmp=tmp->above)   tx = pl->x + freearr_x[dir];
    ty = pl->y + freearr_y[dir];
   
    mflags = get_map_flags(m, &m, tx, ty, &tx, &ty);
    if (mflags & P_OUT_OF_MAP) return 0;
    /* space must be blocked for there to be anything interesting to do */
    if (!(mflags & P_BLOCKED)) return 0;
   
    for(tmp=get_map_ob(m, tx, ty); tmp; tmp=tmp->above)
         if((QUERY_FLAG(tmp,FLAG_ALIVE) && tmp->stats.hp>=0)          if((QUERY_FLAG(tmp,FLAG_ALIVE) && tmp->stats.hp>=0)
            || QUERY_FLAG(tmp, FLAG_CAN_ROLL)             || QUERY_FLAG(tmp, FLAG_CAN_ROLL)
            || tmp->type==LOCKED_DOOR ) {             || tmp->type==LOCKED_DOOR ) {
    /* Don't attack party members */
                 if((pl->type==PLAYER && tmp->type==PLAYER) && (pl->contr->party_number!=-1                  if((pl->type==PLAYER && tmp->type==PLAYER) && (pl->contr->party_number!=-1
                        && pl->contr->party_number==tmp->contr->party_number))                         && pl->contr->party_number==tmp->contr->party_number))
                                 return 0;                                  return 0;
                 break;                  break;
         }          }
   }    }
   if(tmp!=NULL) return do_skill_attack(tmp,pl,string);      if (!tmp) {
    
   if(pl->type==PLAYER)    if(pl->type==PLAYER)
     new_draw_info(NDI_UNIQUE, 0,pl,"There is nothing to attack!");      new_draw_info(NDI_UNIQUE, 0,pl,"There is nothing to attack!");
    
   return 0;    return 0;
 }  }
   
 /* do_skill_attack() - We have got an appropriate opponent from either      return do_skill_attack(tmp,pl,string, skill);
  * move_player_attack() or skill_attack(). In this part we get on with  }
  * attacking, take care of messages from the attack and changes in invisible.  
  * Returns true if the attack damaged the opponent.  
  * -b.t. thomas@astro.psu.edu  
  */  
    
 int do_skill_attack(object *tmp, object *op, char *string) {  
     int success;   
     object *pskill = op->chosen_skill;  
     char buf[MAX_BUF], *name = query_name(tmp);  
     static char *attack_hth_skill[] = {"skill_karate", "skill_clawing",  
  "skill_flame_touch", "skill_punching", NULL };  
          
    /* For Players only: if there is no ready weapon, and no "attack" skill  /* attack_hth() - this handles all hand-to-hand attacks -b.t. */
     * is readied either then check if op has a hand-to-hand "attack skill"   /* July 5, 1995 - I broke up attack_hth() into 2 parts. In the first
     * to use instead. We define this an "attack" skill as any skill    * (attack_hth) we check for weapon use, etc in the second (the new
     * that is linked to the strexpobj.   * function skill_attack() we actually attack.
     * If we are using the wrong skill, change it (if possible) to  
     * a more appropriate skill (in order from the attack_hth_skill[]  
     * list). If the miserable slob still does'nt have any of the hth   
     * skills, give them the last attack_hth_skill on the list. There   
     * is probably a better way to do this...how??   
     */      */
   
     if(op->type==PLAYER   int attack_hth(object *pl, int dir, char *string, object *skill) {
  && !QUERY_FLAG(op,FLAG_READY_WEAPON)      object *enemy=NULL,*weapon;
  && ((!pskill||!pskill->exp_obj)||!pskill->exp_obj->stats.Str)) {   
      int i=0,got_one=0;  
      object *tmp2=NULL;  
            
      /* dragons always use "clawing" hth skill per default */      if(QUERY_FLAG(pl, FLAG_READY_WEAPON))
      if (is_dragon_pl(op)) {   for(weapon=pl->inv;weapon;weapon=weapon->below) {
          for(tmp2=op->inv;tmp2;tmp2=tmp2->below)       if (weapon->type==WEAPON && QUERY_FLAG(weapon, FLAG_APPLIED)) {
      if(tmp2->type==SKILL    CLEAR_FLAG(weapon,FLAG_APPLIED);
         && !strcmp("skill_clawing", tmp2->arch->name)) {   CLEAR_FLAG(pl,FLAG_READY_WEAPON);
      got_one=1;   fix_player(pl);
      break;   if(pl->type==PLAYER) {
  }       new_draw_info(NDI_UNIQUE, 0,pl,"You unwield your weapon in order to attack.");
        esrv_update_item(UPD_FLAGS, pl, weapon);
      }       }
        
      /* for other players use first hth skill we find */  
      while(attack_hth_skill[i]!=NULL && !got_one) {   
          for(tmp2=op->inv;tmp2;tmp2=tmp2->below) {  
      if(tmp2->type==SKILL   
         && !strcmp(attack_hth_skill[i],tmp2->arch->name)) {  
          got_one=1;  
          break;           break;
      }       }
          }           }
  if(got_one) break;      return skill_attack(enemy,pl,dir,string, skill);
  i++;  
      }       }
            
      if(!got_one) { /* Arrgh. The player has no hth attack skills in inventory.   
      * Lets give them the last one in attack_hth_skill[] */   
  archetype *skill;  
  skill=find_archetype(attack_hth_skill[i-1]);  
              if(!skill) {   
             LOG(llevError,"do_skill_attack() could'nt find attack skill for %s\n",  
  op->name);  
      return 0;  
  }   
           
                 tmp2=arch_to_object(skill);  /* attack_melee_weapon() - this handles melee weapon attacks -b.t.
  insert_ob_in_ob(tmp2,op);   * For now we are just checking to see if we have a ready weapon here.
  (void) link_player_skill(op,tmp2);   * But there is a real neato possible feature of this scheme which
      }   * bears mentioning:
      /* now try to ready the new skill */   * Since we are only calling this from do_skill() in the future
      if(change_skill_to_skill(op,tmp2)) {  /* oh oh, trouble! */   * we may make this routine handle 'special' melee weapons attacks
         LOG(llevError,"do_skill_attack() could'nt give new hth skill to %s\n",   * (like disarming manuever with sai) based on player SK_level and
  op->name);   * weapon type.
  return 0;  
      }  
     }   
   
 #if 0  
    /* check if op is using a hth attack, if so modify damage */  
           if(QUERY_FLAG(op,FLAG_READY_SKILL) && op->chosen_skill  
              && !QUERY_FLAG(op, FLAG_READY_WEAPON))  
                  if((op->type!=PLAYER) || (op->type==PLAYER  
                         && op->contr->shoottype==range_skill)) {  
     int dam=hth_damage(tmp,op);   
                            if(dam!=op->stats.dam) {   
  op->stats.dam=dam;  
     }  
    }  
 #endif  
   
    /* if we have 'ready weapon' but no 'melee weapons' skill readied  
     * this will flip to that skill. This is only window dressing for  
     * the players--no need to do this for monsters.  
     */  
           if(op->type==PLAYER && QUERY_FLAG(op,FLAG_READY_WEAPON)  
              && (!op->chosen_skill || op->chosen_skill->stats.sp!=SK_MELEE_WEAPON)) {  
  rangetype oldrange=op->contr->shoottype;  
                  (void) change_skill(op,SK_MELEE_WEAPON);  
   
 /* This is just a simple hack - would probably be cleaner to have change_skill  
  * do the right thing, but this isn't that bad.  
  */   */
  if (op->contr->shoottype!=oldrange) {  
      op->contr->shoottype=oldrange;  
  }  
    }  
    
     /* lose invisiblity/hiding status for running attacks */  int attack_melee_weapon(object *op, int dir, char *string, object *skill) {
    
           if(op->type==PLAYER && op->contr->tmp_invis) {      if(!QUERY_FLAG(op, FLAG_READY_WEAPON)) {
                  op->contr->tmp_invis=0;  
                  op->invisible=0;  
                  op->hide=0;  
                  update_object(op,UP_OBJ_FACE);  
           }  
    
           success = attack_ob(tmp,op);  
    
    /* print appropriate  messages to the player */  
    
           if(success && string!=NULL) {  
                  sprintf(buf, string);  
                  if(op->type==PLAYER)                   if(op->type==PLAYER)
                         new_draw_info_format(NDI_UNIQUE, 0,op,       new_draw_info(NDI_UNIQUE, 0,op,"You have no ready weapon to attack with!");
                           "You %s %s!",buf,name);  
                  else if(tmp->type==PLAYER)  
                         new_draw_info_format(NDI_UNIQUE, 0,tmp,  
                            "%s %s you!",query_name(op),buf);  
           }  
    
           return success;  
   
 }                           
   
    
 /* hth_damage() - returns the appropriate amount of hth damage, based on the  
  * skill in use and the opponent's characteristics. This is generalized to  
  * allow monster skill use too. Called only from do_skill_attack().  -b.t.  
  */  
    
 int hth_damage(object *target, object *pl) {  
   int base = pl->arch->clone.stats.dam + dam_bonus[pl->stats.Str];  
   int damage = pl->chosen_skill->stats.dam * (1 + (SK_level(pl)/9));  
    
   if(damage<1)                  /* Safety - not a hth skill per se */  
         return base;  
   else  
         damage += base;  
   
   /* are we using a skill?, if not return normal hth damage */  
   if(pl->chosen_skill==NULL  
         || !QUERY_FLAG(pl,FLAG_READY_SKILL)) return damage;  
   
  /* Against some creatures the hth attack gets no special bonus.  
   * Probably left out some cases here -b.t.  
   */  
   
   if((target->level>SK_level(pl))  
      || QUERY_FLAG(target,FLAG_UNDEAD)  
      || QUERY_FLAG(target,FLAG_SPLITTING)  
      || QUERY_FLAG(target,FLAG_HITBACK)) {  
          pl->attacktype=AT_PHYSICAL;  
          return damage;  
   }  
   
   damage += (((SK_level(pl)/18)+1)*pl->chosen_skill->stats.dam) + 1;  
   
   return damage;  
 }  
   
 /* This is in the same spirit as the similar routine for spells   
  * it should be used anytime a function needs to check the user's  
  * level.  
  */  
   
 int SK_level(object *op)  
 {  
   object *head = op->head ? op->head : op;  
   int level;  
   
   if(head->type==PLAYER && head->chosen_skill && head->chosen_skill->level!=0) {  
  level = head->chosen_skill->level;  
   } else {  
  level = head->level;  
   }  
   
   if(level<=0)  
   {  
     LOG (llevError, "BUG: SK_level(arch %s, name %s): level <= 0\n",  
          op->arch->name, op->name);  
     level = 1; /* safety */  
   }  
   
   return level;  
 }  
   
 /* returns the amount of time it takes to use a skill.   
  * We allow for stats, and level to modify the amount   
  * of time. Monsters have no skill use time because of  
  * the random nature in which use_monster_skill is called  
  * already simulates this. -b.t.   
  */  
   
 float get_skill_time(object *op, int skillnr) {  
   float time = skills[skillnr].time;  
   
   if(!time || op->type!=PLAYER)   
      return 0;       return 0;
   else {  
     int sum = get_weighted_skill_stat_sum (op,skillnr);  
     int level = SK_level(op)/10;   
   
  time *= 1/(1+(sum/15)+level);  
   }  
   return FABS(time);  
 }  
   
 /* get_skill_stat1() - returns the value of the primary skill  
  * stat. Used in various skills code. -b.t.  
  */  
   
 int get_skill_stat1(object *op) {   
   int stat_value = 0, stat=NO_STAT_VAL;  
   
   if((op->chosen_skill) && ((stat = skills[op->chosen_skill->stats.sp].stat1)!=NO_STAT_VAL) )    
     stat_value = get_attr_value(&(op->stats),stat);  
   
   return stat_value;  
 }  
   
 /* get_skill_stat2() - returns the value of the secondary skill  
  * stat. Used in various skills code. -b.t.  
  */  
   
 int get_skill_stat2(object *op) {  
   int stat_value = 0,stat = NO_STAT_VAL;  
   
   if((op->chosen_skill) && ((stat = skills[op->chosen_skill->stats.sp].stat2)!=NO_STAT_VAL) )    
     stat_value = get_attr_value(&(op->stats),stat);  
   
   return stat_value;  
 }  }
       return skill_attack(NULL,op,dir,string, skill);
 /* get_skill_stat3() - returns the value of the tertiary skill   
  * stat. Used in various skills code. -b.t.   
  */   
    
 int get_skill_stat3(object *op) {   
   int stat_value = 0,stat = NO_STAT_VAL;   
    
   if((op->chosen_skill) && ((stat = skills[op->chosen_skill->stats.sp].stat3)!=NO_STAT_VAL) )  
     stat_value = get_attr_value(&(op->stats),stat);    
    
   return stat_value;   
 }   
   
 /*get_weighted_skill_stats() - */  
   
 int get_weighted_skill_stats(object *op) {  
   int value=0;  
   
   value = (get_skill_stat1(op)/2)+(get_skill_stat2(op)/4)+(get_skill_stat3(op)/4);  
   
   return value;  
   
 }  }
   
 /*  
  * Looks for the skill of specified name in op's inventory.  
  *   
  * attributes:  
  *      object *op       the object to be searched (most likely a player)  
  *      char *skname     name of the desired skill  
  *  
  * return:  
  *      object *         the skill object if found, otherwise NULL  
  */  
 object *get_skill_from_inventory(object *op, const char *skname) {  
   object *tmp;   /* search index */  
     
   if (op == NULL) return NULL;  
     
   for (tmp=op->inv; tmp!=NULL; tmp=tmp->below) {  
     if (tmp->type == SKILL && strcmp(tmp->name, skname)==0)  
       return tmp;  
   }  
   return NULL;  
 }  


Legend:
line(s) removed in v.1.39 
line(s) changed
 line(s) added in v.1.40

File made using version 1.98 of cvs2html by leaf at 2011-07-21 17:45