Source:NetHack 3.6.0/src/weapon.c

From NetHackWiki
(Redirected from Source:Weapon.c)
Jump to: navigation, search

Below is the full text to weapon.c from the source code of NetHack 3.6.0. To link to a particular line, write [[Source:NetHack 3.6.0/src/weapon.c#line123]], for example.

The NetHack General Public License applies to screenshots, source code and other content from NetHack.

This content was modified from the original NetHack source code distribution (by splitting up NetHack content between wiki pages, and possibly further editing). See the page history for a list of who changed it, and on what dates.

Top of file

  1.  /* NetHack 3.6	weapon.c	$NHDT-Date: 1446078767 2015/10/29 00:32:47 $  $NHDT-Branch: master $:$NHDT-Revision: 1.55 $ */
  2.  /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
  3.  /* NetHack may be freely redistributed.  See license for details. */
  4.  
  5.  /*
  6.   *      This module contains code for calculation of "to hit" and damage
  7.   *      bonuses for any given weapon used, as well as weapons selection
  8.   *      code for monsters.
  9.   */
  10.  #include "hack.h"
  11.  
  12.  /* Categories whose names don't come from OBJ_NAME(objects[type])
  13.   */
  14.  #define PN_BARE_HANDED (-1) /* includes martial arts */
  15.  #define PN_TWO_WEAPONS (-2)
  16.  #define PN_RIDING (-3)
  17.  #define PN_POLEARMS (-4)
  18.  #define PN_SABER (-5)
  19.  #define PN_HAMMER (-6)
  20.  #define PN_WHIP (-7)
  21.  #define PN_ATTACK_SPELL (-8)
  22.  #define PN_HEALING_SPELL (-9)
  23.  #define PN_DIVINATION_SPELL (-10)
  24.  #define PN_ENCHANTMENT_SPELL (-11)
  25.  #define PN_CLERIC_SPELL (-12)
  26.  #define PN_ESCAPE_SPELL (-13)
  27.  #define PN_MATTER_SPELL (-14)
  28.  
  29.  STATIC_DCL void FDECL(give_may_advance_msg, (int));
  30.  
  31.  STATIC_VAR NEARDATA const short skill_names_indices[P_NUM_SKILLS] = {
  32.      0, DAGGER, KNIFE, AXE, PICK_AXE, SHORT_SWORD, BROADSWORD, LONG_SWORD,
  33.      TWO_HANDED_SWORD, SCIMITAR, PN_SABER, CLUB, MACE, MORNING_STAR, FLAIL,
  34.      PN_HAMMER, QUARTERSTAFF, PN_POLEARMS, SPEAR, TRIDENT, LANCE, BOW, SLING,
  35.      CROSSBOW, DART, SHURIKEN, BOOMERANG, PN_WHIP, UNICORN_HORN,
  36.      PN_ATTACK_SPELL, PN_HEALING_SPELL, PN_DIVINATION_SPELL,
  37.      PN_ENCHANTMENT_SPELL, PN_CLERIC_SPELL, PN_ESCAPE_SPELL, PN_MATTER_SPELL,
  38.      PN_BARE_HANDED, PN_TWO_WEAPONS, PN_RIDING
  39.  };
  40.  
  41.  /* note: entry [0] isn't used */
  42.  STATIC_VAR NEARDATA const char *const odd_skill_names[] = {
  43.      "no skill", "bare hands", /* use barehands_or_martial[] instead */
  44.      "two weapon combat", "riding", "polearms", "saber", "hammer", "whip",
  45.      "attack spells", "healing spells", "divination spells",
  46.      "enchantment spells", "clerical spells", "escape spells", "matter spells",
  47.  };
  48.  /* indexed vis `is_martial() */
  49.  STATIC_VAR NEARDATA const char *const barehands_or_martial[] = {
  50.      "bare handed combat", "martial arts"
  51.  };
  52.  

give_may_advance_msg

  1.  STATIC_OVL void
  2.  give_may_advance_msg(skill)
  3.  int skill;
  4.  {
  5.      You_feel("more confident in your %sskills.",
  6.               skill == P_NONE ? "" : skill <= P_LAST_WEAPON
  7.                                          ? "weapon "
  8.                                          : skill <= P_LAST_SPELL
  9.                                                ? "spell casting "
  10.                                                : "fighting ");
  11.  }
  12.  


  1.  STATIC_DCL boolean FDECL(can_advance, (int, BOOLEAN_P));
  2.  STATIC_DCL boolean FDECL(could_advance, (int));
  3.  STATIC_DCL boolean FDECL(peaked_skill, (int));
  4.  STATIC_DCL int FDECL(slots_required, (int));
  5.  STATIC_DCL char *FDECL(skill_level_name, (int, char *));
  6.  STATIC_DCL void FDECL(skill_advance, (int));
  7.  
  8.  #define P_NAME(type)                                    \
  9.      ((skill_names_indices[type] > 0)                    \
  10.           ? OBJ_NAME(objects[skill_names_indices[type]]) \
  11.           : (type == P_BARE_HANDED_COMBAT)               \
  12.                 ? barehands_or_martial[martial_bonus()]  \
  13.                 : odd_skill_names[-skill_names_indices[type]])
  14.  
  15.  static NEARDATA const char kebabable[] = { S_XORN, S_DRAGON, S_JABBERWOCK,
  16.                                             S_NAGA, S_GIANT,  '\0' };
  17.  

weapon_descr

  1.  /* weapon's skill category name for use as generalized description of weapon;
  2.     mostly used to shorten "you drop your <weapon>" messages when slippery
  3.     fingers or polymorph causes hero to involuntarily drop wielded weapon(s) */
  4.  const char *
  5.  weapon_descr(obj)
  6.  struct obj *obj;
  7.  {
  8.      int skill = weapon_type(obj);
  9.      const char *descr = P_NAME(skill);
  10.  
  11.      /* assorted special cases */
  12.      switch (skill) {
  13.      case P_NONE:
  14.          /* not a weapon or weptool: use item class name;
  15.             override class name "food" for corpses, tins, and eggs,
  16.             "large rock" for statues and boulders, and "tool" for towels */
  17.          descr = (obj->otyp == CORPSE || obj->otyp == TIN || obj->otyp == EGG
  18.                   || obj->otyp == STATUE || obj->otyp == BOULDER
  19.                   || obj->otyp == TOWEL)
  20.                      ? OBJ_NAME(objects[obj->otyp])
  21.                      : def_oc_syms[(int) obj->oclass].name;
  22.          break;
  23.      case P_SLING:
  24.          if (is_ammo(obj))
  25.              descr = (obj->otyp == ROCK || is_graystone(obj))
  26.                          ? "stone"
  27.                          /* avoid "rock"; what about known glass? */
  28.                          : (obj->oclass == GEM_CLASS)
  29.                              ? "gem"
  30.                              /* in case somebody adds odd sling ammo */
  31.                              : def_oc_syms[(int) obj->oclass].name;
  32.          break;
  33.      case P_BOW:
  34.          if (is_ammo(obj))
  35.              descr = "arrow";
  36.          break;
  37.      case P_CROSSBOW:
  38.          if (is_ammo(obj))
  39.              descr = "bolt";
  40.          break;
  41.      case P_FLAIL:
  42.          if (obj->otyp == GRAPPLING_HOOK)
  43.              descr = "hook";
  44.          break;
  45.      case P_PICK_AXE:
  46.          /* even if "dwarvish mattock" hasn't been discovered yet */
  47.          if (obj->otyp == DWARVISH_MATTOCK)
  48.              descr = "mattock";
  49.          break;
  50.      default:
  51.          break;
  52.      }
  53.      return makesingular(descr);
  54.  }
  55.  

hitval

  1.  /*
  2.   *      hitval returns an integer representing the "to hit" bonuses
  3.   *      of "otmp" against the monster.
  4.   */
  5.  int
  6.  hitval(otmp, mon)
  7.  struct obj *otmp;
  8.  struct monst *mon;
  9.  {
  10.      int tmp = 0;
  11.      struct permonst *ptr = mon->data;
  12.      boolean Is_weapon = (otmp->oclass == WEAPON_CLASS || is_weptool(otmp));
  13.  
  14.      if (Is_weapon)
  15.          tmp += otmp->spe;
  16.  
  17.      /* Put weapon specific "to hit" bonuses in below: */
  18.      tmp += objects[otmp->otyp].oc_hitbon;
  19.  
  20.      /* Put weapon vs. monster type "to hit" bonuses in below: */
  21.  
  22.      /* Blessed weapons used against undead or demons */
  23.      if (Is_weapon && otmp->blessed
  24.          && (is_demon(ptr) || is_undead(ptr) || is_vampshifter(mon)))
  25.          tmp += 2;
  26.  
  27.      if (is_spear(otmp) && index(kebabable, ptr->mlet))
  28.          tmp += 2;
  29.  
  30.      /* trident is highly effective against swimmers */
  31.      if (otmp->otyp == TRIDENT && is_swimmer(ptr)) {
  32.          if (is_pool(mon->mx, mon->my))
  33.              tmp += 4;
  34.          else if (ptr->mlet == S_EEL || ptr->mlet == S_SNAKE)
  35.              tmp += 2;
  36.      }
  37.  
  38.      /* Picks used against xorns and earth elementals */
  39.      if (is_pick(otmp) && (passes_walls(ptr) && thick_skinned(ptr)))
  40.          tmp += 2;
  41.  
  42.      /* Check specially named weapon "to hit" bonuses */
  43.      if (otmp->oartifact)
  44.          tmp += spec_abon(otmp, mon);
  45.  
  46.      return tmp;
  47.  }
  48.  

dmgval

  1.  /* Historical note: The original versions of Hack used a range of damage
  2.   * which was similar to, but not identical to the damage used in Advanced
  3.   * Dungeons and Dragons.  I figured that since it was so close, I may as well
  4.   * make it exactly the same as AD&D, adding some more weapons in the process.
  5.   * This has the advantage that it is at least possible that the player would
  6.   * already know the damage of at least some of the weapons.  This was circa
  7.   * 1987 and I used the table from Unearthed Arcana until I got tired of typing
  8.   * them in (leading to something of an imbalance towards weapons early in
  9.   * alphabetical order).  The data structure still doesn't include fields that
  10.   * fully allow the appropriate damage to be described (there's no way to say
  11.   * 3d6 or 1d6+1) so we add on the extra damage in dmgval() if the weapon
  12.   * doesn't do an exact die of damage.
  13.   *
  14.   * Of course new weapons were added later in the development of Nethack.  No
  15.   * AD&D consistency was kept, but most of these don't exist in AD&D anyway.
  16.   *
  17.   * Second edition AD&D came out a few years later; luckily it used the same
  18.   * table.  As of this writing (1999), third edition is in progress but not
  19.   * released.  Let's see if the weapon table stays the same.  --KAA
  20.   * October 2000: It didn't.  Oh, well.
  21.   */
  22.  
  23.  /*
  24.   *      dmgval returns an integer representing the damage bonuses
  25.   *      of "otmp" against the monster.
  26.   */
  27.  int
  28.  dmgval(otmp, mon)
  29.  struct obj *otmp;
  30.  struct monst *mon;
  31.  {
  32.      int tmp = 0, otyp = otmp->otyp;
  33.      struct permonst *ptr = mon->data;
  34.      boolean Is_weapon = (otmp->oclass == WEAPON_CLASS || is_weptool(otmp));
  35.  
  36.      if (otyp == CREAM_PIE)
  37.          return 0;
  38.  
  39.      if (bigmonst(ptr)) {
  40.          if (objects[otyp].oc_wldam)
  41.              tmp = rnd(objects[otyp].oc_wldam);
  42.          switch (otyp) {
  43.          case IRON_CHAIN:
  44.          case CROSSBOW_BOLT:
  45.          case MORNING_STAR:
  46.          case PARTISAN:
  47.          case RUNESWORD:
  48.          case ELVEN_BROADSWORD:
  49.          case BROADSWORD:
  50.              tmp++;
  51.              break;
  52.  
  53.          case FLAIL:
  54.          case RANSEUR:
  55.          case VOULGE:
  56.              tmp += rnd(4);
  57.              break;
  58.  
  59.          case ACID_VENOM:
  60.          case HALBERD:
  61.          case SPETUM:
  62.              tmp += rnd(6);
  63.              break;
  64.  
  65.          case BATTLE_AXE:
  66.          case BARDICHE:
  67.          case TRIDENT:
  68.              tmp += d(2, 4);
  69.              break;
  70.  
  71.          case TSURUGI:
  72.          case DWARVISH_MATTOCK:
  73.          case TWO_HANDED_SWORD:
  74.              tmp += d(2, 6);
  75.              break;
  76.          }
  77.      } else {
  78.          if (objects[otyp].oc_wsdam)
  79.              tmp = rnd(objects[otyp].oc_wsdam);
  80.          switch (otyp) {
  81.          case IRON_CHAIN:
  82.          case CROSSBOW_BOLT:
  83.          case MACE:
  84.          case WAR_HAMMER:
  85.          case FLAIL:
  86.          case SPETUM:
  87.          case TRIDENT:
  88.              tmp++;
  89.              break;
  90.  
  91.          case BATTLE_AXE:
  92.          case BARDICHE:
  93.          case BILL_GUISARME:
  94.          case GUISARME:
  95.          case LUCERN_HAMMER:
  96.          case MORNING_STAR:
  97.          case RANSEUR:
  98.          case BROADSWORD:
  99.          case ELVEN_BROADSWORD:
  100.          case RUNESWORD:
  101.          case VOULGE:
  102.              tmp += rnd(4);
  103.              break;
  104.  
  105.          case ACID_VENOM:
  106.              tmp += rnd(6);
  107.              break;
  108.          }
  109.      }
  110.      if (Is_weapon) {
  111.          tmp += otmp->spe;
  112.          /* negative enchantment mustn't produce negative damage */
  113.          if (tmp < 0)
  114.              tmp = 0;
  115.      }
  116.  
  117.      if (objects[otyp].oc_material <= LEATHER && thick_skinned(ptr))
  118.          /* thick skinned/scaled creatures don't feel it */
  119.          tmp = 0;
  120.      if (ptr == &mons[PM_SHADE] && !shade_glare(otmp))
  121.          tmp = 0;
  122.  
  123.      /* "very heavy iron ball"; weight increase is in increments of 160 */
  124.      if (otyp == HEAVY_IRON_BALL && tmp > 0) {
  125.          int wt = (int) objects[HEAVY_IRON_BALL].oc_weight;
  126.  
  127.          if ((int) otmp->owt > wt) {
  128.              wt = ((int) otmp->owt - wt) / 160;
  129.              tmp += rnd(4 * wt);
  130.              if (tmp > 25)
  131.                  tmp = 25; /* objects[].oc_wldam */
  132.          }
  133.      }
  134.  
  135.      /* Put weapon vs. monster type damage bonuses in below: */
  136.      if (Is_weapon || otmp->oclass == GEM_CLASS || otmp->oclass == BALL_CLASS
  137.          || otmp->oclass == CHAIN_CLASS) {
  138.          int bonus = 0;
  139.  
  140.          if (otmp->blessed
  141.              && (is_undead(ptr) || is_demon(ptr) || is_vampshifter(mon)))
  142.              bonus += rnd(4);
  143.          if (is_axe(otmp) && is_wooden(ptr))
  144.              bonus += rnd(4);
  145.          if (objects[otyp].oc_material == SILVER && mon_hates_silver(mon))
  146.              bonus += rnd(20);
  147.  
  148.          /* if the weapon is going to get a double damage bonus, adjust
  149.             this bonus so that effectively it's added after the doubling */
  150.          if (bonus > 1 && otmp->oartifact && spec_dbon(otmp, mon, 25) >= 25)
  151.              bonus = (bonus + 1) / 2;
  152.  
  153.          tmp += bonus;
  154.      }
  155.  
  156.      if (tmp > 0) {
  157.          /* It's debatable whether a rusted blunt instrument
  158.             should do less damage than a pristine one, since
  159.             it will hit with essentially the same impact, but
  160.             there ought to some penalty for using damaged gear
  161.             so always subtract erosion even for blunt weapons. */
  162.          tmp -= greatest_erosion(otmp);
  163.          if (tmp < 1)
  164.              tmp = 1;
  165.      }
  166.  
  167.      return  tmp;
  168.  }
  169.  

oselect

  1.  STATIC_DCL struct obj *FDECL(oselect, (struct monst *, int));
  2.  #define Oselect(x)                      \
  3.      if ((otmp = oselect(mtmp, x)) != 0) \
  4.          return otmp;
  5.  
  6.  STATIC_OVL struct obj *
  7.  oselect(mtmp, x)
  8.  struct monst *mtmp;
  9.  int x;
  10.  {
  11.      struct obj *otmp;
  12.  
  13.      for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) {
  14.          if (otmp->otyp == x
  15.              /* never select non-cockatrice corpses */
  16.              && !((x == CORPSE || x == EGG)
  17.                   && !touch_petrifies(&mons[otmp->corpsenm]))
  18.              && (!otmp->oartifact || touch_artifact(otmp, mtmp)))
  19.              return otmp;
  20.      }
  21.      return (struct obj *) 0;
  22.  }
  23.  

select_rwep

  1.  static NEARDATA const int rwep[] = {
  2.      DWARVISH_SPEAR, SILVER_SPEAR, ELVEN_SPEAR, SPEAR, ORCISH_SPEAR, JAVELIN,
  3.      SHURIKEN, YA, SILVER_ARROW, ELVEN_ARROW, ARROW, ORCISH_ARROW,
  4.      CROSSBOW_BOLT, SILVER_DAGGER, ELVEN_DAGGER, DAGGER, ORCISH_DAGGER, KNIFE,
  5.      FLINT, ROCK, LOADSTONE, LUCKSTONE, DART,
  6.      /* BOOMERANG, */ CREAM_PIE
  7.  };
  8.  
  9.  static NEARDATA const int pwep[] = { HALBERD,       BARDICHE, SPETUM,
  10.                                       BILL_GUISARME, VOULGE,   RANSEUR,
  11.                                       GUISARME,      GLAIVE,   LUCERN_HAMMER,
  12.                                       BEC_DE_CORBIN, FAUCHARD, PARTISAN,
  13.                                       LANCE };
  14.  
  15.  static struct obj *propellor;
  16.  
  17.  /* select a ranged weapon for the monster */
  18.  struct obj *
  19.  select_rwep(mtmp)
  20.  register struct monst *mtmp;
  21.  {
  22.      register struct obj *otmp;
  23.      struct obj *mwep;
  24.      boolean mweponly;
  25.      int i;
  26.  
  27.      char mlet = mtmp->data->mlet;
  28.  
  29.      propellor = &zeroobj;
  30.      Oselect(EGG);      /* cockatrice egg */
  31.      if (mlet == S_KOP) /* pies are first choice for Kops */
  32.          Oselect(CREAM_PIE);
  33.      if (throws_rocks(mtmp->data)) /* ...boulders for giants */
  34.          Oselect(BOULDER);
  35.  
  36.      /* Select polearms first; they do more damage and aren't expendable.
  37.         But don't pick one if monster's weapon is welded, because then
  38.         we'd never have a chance to throw non-wielding missiles. */
  39.      /* The limit of 13 here is based on the monster polearm range limit
  40.       * (defined as 5 in mthrowu.c).  5 corresponds to a distance of 2 in
  41.       * one direction and 1 in another; one space beyond that would be 3 in
  42.       * one direction and 2 in another; 3^2+2^2=13.
  43.       */
  44.      mwep = MON_WEP(mtmp);
  45.      /* NO_WEAPON_WANTED means we already tried to wield and failed */
  46.      mweponly = (mwelded(mwep) && mtmp->weapon_check == NO_WEAPON_WANTED);
  47.      if (dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) <= 13
  48.          && couldsee(mtmp->mx, mtmp->my)) {
  49.          for (i = 0; i < SIZE(pwep); i++) {
  50.              /* Only strong monsters can wield big (esp. long) weapons.
  51.               * Big weapon is basically the same as bimanual.
  52.               * All monsters can wield the remaining weapons.
  53.               */
  54.              if (((strongmonst(mtmp->data)
  55.                    && (mtmp->misc_worn_check & W_ARMS) == 0)
  56.                   || !objects[pwep[i]].oc_bimanual)
  57.                  && (objects[pwep[i]].oc_material != SILVER
  58.                      || !mon_hates_silver(mtmp))) {
  59.                  if ((otmp = oselect(mtmp, pwep[i])) != 0
  60.                      && (otmp == mwep || !mweponly)) {
  61.                      propellor = otmp; /* force the monster to wield it */
  62.                      return otmp;
  63.                  }
  64.              }
  65.          }
  66.      }
  67.  
  68.      /*
  69.       * other than these two specific cases, always select the
  70.       * most potent ranged weapon to hand.
  71.       */
  72.      for (i = 0; i < SIZE(rwep); i++) {
  73.          int prop;
  74.  
  75.          /* shooting gems from slings; this goes just before the darts */
  76.          /* (shooting rocks is already handled via the rwep[] ordering) */
  77.          if (rwep[i] == DART && !likes_gems(mtmp->data)
  78.              && m_carrying(mtmp, SLING)) { /* propellor */
  79.              for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
  80.                  if (otmp->oclass == GEM_CLASS
  81.                      && (otmp->otyp != LOADSTONE || !otmp->cursed)) {
  82.                      propellor = m_carrying(mtmp, SLING);
  83.                      return otmp;
  84.                  }
  85.          }
  86.  
  87.          /* KMH -- This belongs here so darts will work */
  88.          propellor = &zeroobj;
  89.  
  90.          prop = (objects[rwep[i]]).oc_skill;
  91.          if (prop < 0) {
  92.              switch (-prop) {
  93.              case P_BOW:
  94.                  propellor = (oselect(mtmp, YUMI));
  95.                  if (!propellor)
  96.                      propellor = (oselect(mtmp, ELVEN_BOW));
  97.                  if (!propellor)
  98.                      propellor = (oselect(mtmp, BOW));
  99.                  if (!propellor)
  100.                      propellor = (oselect(mtmp, ORCISH_BOW));
  101.                  break;
  102.              case P_SLING:
  103.                  propellor = (oselect(mtmp, SLING));
  104.                  break;
  105.              case P_CROSSBOW:
  106.                  propellor = (oselect(mtmp, CROSSBOW));
  107.              }
  108.              if ((otmp = MON_WEP(mtmp)) && mwelded(otmp) && otmp != propellor
  109.                  && mtmp->weapon_check == NO_WEAPON_WANTED)
  110.                  propellor = 0;
  111.          }
  112.          /* propellor = obj, propellor to use
  113.           * propellor = &zeroobj, doesn't need a propellor
  114.           * propellor = 0, needed one and didn't have one
  115.           */
  116.          if (propellor != 0) {
  117.              /* Note: cannot use m_carrying for loadstones, since it will
  118.               * always select the first object of a type, and maybe the
  119.               * monster is carrying two but only the first is unthrowable.
  120.               */
  121.              if (rwep[i] != LOADSTONE) {
  122.                  /* Don't throw a cursed weapon-in-hand or an artifact */
  123.                  if ((otmp = oselect(mtmp, rwep[i])) && !otmp->oartifact
  124.                      && !(otmp == MON_WEP(mtmp) && mwelded(otmp)))
  125.                      return otmp;
  126.              } else
  127.                  for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) {
  128.                      if (otmp->otyp == LOADSTONE && !otmp->cursed)
  129.                          return otmp;
  130.                  }
  131.          }
  132.      }
  133.  
  134.      /* failure */
  135.      return (struct obj *) 0;
  136.  }
  137.  

select_hwep

  1.  /* Weapons in order of preference */
  2.  static const NEARDATA short hwep[] = {
  3.      CORPSE, /* cockatrice corpse */
  4.      TSURUGI, RUNESWORD, DWARVISH_MATTOCK, TWO_HANDED_SWORD, BATTLE_AXE,
  5.      KATANA, UNICORN_HORN, CRYSKNIFE, TRIDENT, LONG_SWORD, ELVEN_BROADSWORD,
  6.      BROADSWORD, SCIMITAR, SILVER_SABER, MORNING_STAR, ELVEN_SHORT_SWORD,
  7.      DWARVISH_SHORT_SWORD, SHORT_SWORD, ORCISH_SHORT_SWORD, MACE, AXE,
  8.      DWARVISH_SPEAR, SILVER_SPEAR, ELVEN_SPEAR, SPEAR, ORCISH_SPEAR, FLAIL,
  9.      BULLWHIP, QUARTERSTAFF, JAVELIN, AKLYS, CLUB, PICK_AXE, RUBBER_HOSE,
  10.      WAR_HAMMER, SILVER_DAGGER, ELVEN_DAGGER, DAGGER, ORCISH_DAGGER, ATHAME,
  11.      SCALPEL, KNIFE, WORM_TOOTH
  12.  };
  13.  
  14.  /* select a hand to hand weapon for the monster */
  15.  struct obj *
  16.  select_hwep(mtmp)
  17.  register struct monst *mtmp;
  18.  {
  19.      register struct obj *otmp;
  20.      register int i;
  21.      boolean strong = strongmonst(mtmp->data);
  22.      boolean wearing_shield = (mtmp->misc_worn_check & W_ARMS) != 0;
  23.  
  24.      /* prefer artifacts to everything else */
  25.      for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) {
  26.          if (otmp->oclass == WEAPON_CLASS && otmp->oartifact
  27.              && touch_artifact(otmp, mtmp)
  28.              && ((strong && !wearing_shield)
  29.                  || !objects[otmp->otyp].oc_bimanual))
  30.              return otmp;
  31.      }
  32.  
  33.      if (is_giant(mtmp->data)) /* giants just love to use clubs */
  34.          Oselect(CLUB);
  35.  
  36.      /* only strong monsters can wield big (esp. long) weapons */
  37.      /* big weapon is basically the same as bimanual */
  38.      /* all monsters can wield the remaining weapons */
  39.      for (i = 0; i < SIZE(hwep); i++) {
  40.          if (hwep[i] == CORPSE && !(mtmp->misc_worn_check & W_ARMG)
  41.              && !resists_ston(mtmp))
  42.              continue;
  43.          if (((strong && !wearing_shield) || !objects[hwep[i]].oc_bimanual)
  44.              && (objects[hwep[i]].oc_material != SILVER
  45.                  || !mon_hates_silver(mtmp)))
  46.              Oselect(hwep[i]);
  47.      }
  48.  
  49.      /* failure */
  50.      return (struct obj *) 0;
  51.  }
  52.  

possibly_unwield

  1.  /* Called after polymorphing a monster, robbing it, etc....  Monsters
  2.   * otherwise never unwield stuff on their own.  Might print message.
  3.   */
  4.  void
  5.  possibly_unwield(mon, polyspot)
  6.  struct monst *mon;
  7.  boolean polyspot;
  8.  {
  9.      struct obj *obj, *mw_tmp;
  10.  
  11.      if (!(mw_tmp = MON_WEP(mon)))
  12.          return;
  13.      for (obj = mon->minvent; obj; obj = obj->nobj)
  14.          if (obj == mw_tmp)
  15.              break;
  16.      if (!obj) { /* The weapon was stolen or destroyed */
  17.          MON_NOWEP(mon);
  18.          mon->weapon_check = NEED_WEAPON;
  19.          return;
  20.      }
  21.      if (!attacktype(mon->data, AT_WEAP)) {
  22.          setmnotwielded(mon, mw_tmp);
  23.          mon->weapon_check = NO_WEAPON_WANTED;
  24.          obj_extract_self(obj);
  25.          if (cansee(mon->mx, mon->my)) {
  26.              pline("%s drops %s.", Monnam(mon), distant_name(obj, doname));
  27.              newsym(mon->mx, mon->my);
  28.          }
  29.          /* might be dropping object into water or lava */
  30.          if (!flooreffects(obj, mon->mx, mon->my, "drop")) {
  31.              if (polyspot)
  32.                  bypass_obj(obj);
  33.              place_object(obj, mon->mx, mon->my);
  34.              stackobj(obj);
  35.          }
  36.          return;
  37.      }
  38.      /* The remaining case where there is a change is where a monster
  39.       * is polymorphed into a stronger/weaker monster with a different
  40.       * choice of weapons.  This has no parallel for players.  It can
  41.       * be handled by waiting until mon_wield_item is actually called.
  42.       * Though the monster still wields the wrong weapon until then,
  43.       * this is OK since the player can't see it.  (FIXME: Not okay since
  44.       * probing can reveal it.)
  45.       * Note that if there is no change, setting the check to NEED_WEAPON
  46.       * is harmless.
  47.       * Possible problem: big monster with big cursed weapon gets
  48.       * polymorphed into little monster.  But it's not quite clear how to
  49.       * handle this anyway....
  50.       */
  51.      if (!(mwelded(mw_tmp) && mon->weapon_check == NO_WEAPON_WANTED))
  52.          mon->weapon_check = NEED_WEAPON;
  53.      return;
  54.  }
  55.  

mon_wield_item

  1.  /* Let a monster try to wield a weapon, based on mon->weapon_check.
  2.   * Returns 1 if the monster took time to do it, 0 if it did not.
  3.   */
  4.  int
  5.  mon_wield_item(mon)
  6.  register struct monst *mon;
  7.  {
  8.      struct obj *obj;
  9.  
  10.      /* This case actually should never happen */
  11.      if (mon->weapon_check == NO_WEAPON_WANTED)
  12.          return 0;
  13.      switch (mon->weapon_check) {
  14.      case NEED_HTH_WEAPON:
  15.          obj = select_hwep(mon);
  16.          break;
  17.      case NEED_RANGED_WEAPON:
  18.          (void) select_rwep(mon);
  19.          obj = propellor;
  20.          break;
  21.      case NEED_PICK_AXE:
  22.          obj = m_carrying(mon, PICK_AXE);
  23.          /* KMH -- allow other picks */
  24.          if (!obj && !which_armor(mon, W_ARMS))
  25.              obj = m_carrying(mon, DWARVISH_MATTOCK);
  26.          break;
  27.      case NEED_AXE:
  28.          /* currently, only 2 types of axe */
  29.          obj = m_carrying(mon, BATTLE_AXE);
  30.          if (!obj || which_armor(mon, W_ARMS))
  31.              obj = m_carrying(mon, AXE);
  32.          break;
  33.      case NEED_PICK_OR_AXE:
  34.          /* prefer pick for fewer switches on most levels */
  35.          obj = m_carrying(mon, DWARVISH_MATTOCK);
  36.          if (!obj)
  37.              obj = m_carrying(mon, BATTLE_AXE);
  38.          if (!obj || which_armor(mon, W_ARMS)) {
  39.              obj = m_carrying(mon, PICK_AXE);
  40.              if (!obj)
  41.                  obj = m_carrying(mon, AXE);
  42.          }
  43.          break;
  44.      default:
  45.          impossible("weapon_check %d for %s?", mon->weapon_check,
  46.                     mon_nam(mon));
  47.          return 0;
  48.      }
  49.      if (obj && obj != &zeroobj) {
  50.          struct obj *mw_tmp = MON_WEP(mon);
  51.          if (mw_tmp && mw_tmp->otyp == obj->otyp) {
  52.              /* already wielding it */
  53.              mon->weapon_check = NEED_WEAPON;
  54.              return 0;
  55.          }
  56.          /* Actually, this isn't necessary--as soon as the monster
  57.           * wields the weapon, the weapon welds itself, so the monster
  58.           * can know it's cursed and needn't even bother trying.
  59.           * Still....
  60.           */
  61.          if (mw_tmp && mwelded(mw_tmp)) {
  62.              if (canseemon(mon)) {
  63.                  char welded_buf[BUFSZ];
  64.                  const char *mon_hand = mbodypart(mon, HAND);
  65.  
  66.                  if (bimanual(mw_tmp))
  67.                      mon_hand = makeplural(mon_hand);
  68.                  Sprintf(welded_buf, "%s welded to %s %s",
  69.                          otense(mw_tmp, "are"), mhis(mon), mon_hand);
  70.  
  71.                  if (obj->otyp == PICK_AXE) {
  72.                      pline("Since %s weapon%s %s,", s_suffix(mon_nam(mon)),
  73.                            plur(mw_tmp->quan), welded_buf);
  74.                      pline("%s cannot wield that %s.", mon_nam(mon),
  75.                            xname(obj));
  76.                  } else {
  77.                      pline("%s tries to wield %s.", Monnam(mon), doname(obj));
  78.                      pline("%s %s!", Yname2(mw_tmp), welded_buf);
  79.                  }
  80.                  mw_tmp->bknown = 1;
  81.              }
  82.              mon->weapon_check = NO_WEAPON_WANTED;
  83.              return 1;
  84.          }
  85.          mon->mw = obj; /* wield obj */
  86.          setmnotwielded(mon, mw_tmp);
  87.          mon->weapon_check = NEED_WEAPON;
  88.          if (canseemon(mon)) {
  89.              pline("%s wields %s!", Monnam(mon), doname(obj));
  90.              if (mwelded(mw_tmp)) {
  91.                  pline("%s %s to %s %s!", Tobjnam(obj, "weld"),
  92.                        is_plural(obj) ? "themselves" : "itself",
  93.                        s_suffix(mon_nam(mon)), mbodypart(mon, HAND));
  94.                  obj->bknown = 1;
  95.              }
  96.          }
  97.          if (artifact_light(obj) && !obj->lamplit) {
  98.              begin_burn(obj, FALSE);
  99.              if (canseemon(mon))
  100.                  pline("%s %s in %s %s!", Tobjnam(obj, "shine"),
  101.                        arti_light_description(obj), s_suffix(mon_nam(mon)),
  102.                        mbodypart(mon, HAND));
  103.          }
  104.          obj->owornmask = W_WEP;
  105.          return 1;
  106.      }
  107.      mon->weapon_check = NEED_WEAPON;
  108.      return 0;
  109.  }
  110.  

mwepgone

  1.  /* force monster to stop wielding current weapon, if any */
  2.  void
  3.  mwepgone(mon)
  4.  struct monst *mon;
  5.  {
  6.      struct obj *mwep = MON_WEP(mon);
  7.  
  8.      if (mwep) {
  9.          setmnotwielded(mon, mwep);
  10.          mon->weapon_check = NEED_WEAPON;
  11.      }
  12.  }
  13.  

abon

  1.  /* attack bonus for strength & dexterity */
  2.  int
  3.  abon()
  4.  {
  5.      int sbon;
  6.      int str = ACURR(A_STR), dex = ACURR(A_DEX);
  7.  
  8.      if (Upolyd)
  9.          return (adj_lev(&mons[u.umonnum]) - 3);
  10.      if (str < 6)
  11.          sbon = -2;
  12.      else if (str < 8)
  13.          sbon = -1;
  14.      else if (str < 17)
  15.          sbon = 0;
  16.      else if (str <= STR18(50))
  17.          sbon = 1; /* up to 18/50 */
  18.      else if (str < STR18(100))
  19.          sbon = 2;
  20.      else
  21.          sbon = 3;
  22.  
  23.      /* Game tuning kludge: make it a bit easier for a low level character to
  24.       * hit */
  25.      sbon += (u.ulevel < 3) ? 1 : 0;
  26.  
  27.      if (dex < 4)
  28.          return (sbon - 3);
  29.      else if (dex < 6)
  30.          return (sbon - 2);
  31.      else if (dex < 8)
  32.          return (sbon - 1);
  33.      else if (dex < 14)
  34.          return sbon;
  35.      else
  36.          return (sbon + dex - 14);
  37.  }
  38.  

dbon

  1.  /* damage bonus for strength */
  2.  int
  3.  dbon()
  4.  {
  5.      int str = ACURR(A_STR);
  6.  
  7.      if (Upolyd)
  8.          return 0;
  9.  
  10.      if (str < 6)
  11.          return -1;
  12.      else if (str < 16)
  13.          return 0;
  14.      else if (str < 18)
  15.          return 1;
  16.      else if (str == 18)
  17.          return 2; /* up to 18 */
  18.      else if (str <= STR18(75))
  19.          return 3; /* up to 18/75 */
  20.      else if (str <= STR18(90))
  21.          return 4; /* up to 18/90 */
  22.      else if (str < STR18(100))
  23.          return 5; /* up to 18/99 */
  24.      else
  25.          return 6;
  26.  }
  27.  

wet_a_towel

  1.  /* increase a towel's wetness */
  2.  void
  3.  wet_a_towel(obj, amt, verbose)
  4.  struct obj *obj;
  5.  int amt; /* positive: new value; negative: increment by -amt; zero: no-op */
  6.  boolean verbose;
  7.  {
  8.      int newspe = (amt <= 0) ? obj->spe - amt : amt;
  9.  
  10.      /* new state is only reported if it's an increase */
  11.      if (newspe > obj->spe) {
  12.          if (verbose) {
  13.              const char *wetness = (newspe < 3)
  14.                                       ? (!obj->spe ? "damp" : "damper")
  15.                                       : (!obj->spe ? "wet" : "wetter");
  16.  
  17.              if (carried(obj))
  18.                  pline("%s gets %s.", Yobjnam2(obj, (const char *) 0),
  19.                        wetness);
  20.              else if (mcarried(obj) && canseemon(obj->ocarry))
  21.                  pline("%s %s gets %s.", s_suffix(Monnam(obj->ocarry)),
  22.                        xname(obj), wetness);
  23.          }
  24.      }
  25.      obj->spe = min(newspe, 7);
  26.  
  27.      /* if hero is wielding this towel, don't give "you begin bashing
  28.         with your wet towel" message on next attack with it */
  29.      if (obj == uwep)
  30.          unweapon = !is_wet_towel(obj);
  31.  }
  32.  

dry_a_towel

  1.  /* decrease a towel's wetness */
  2.  void
  3.  dry_a_towel(obj, amt, verbose)
  4.  struct obj *obj;
  5.  int amt; /* positive: new value; negative: decrement by -amt; zero: no-op */
  6.  boolean verbose;
  7.  {
  8.      int newspe = (amt <= 0) ? obj->spe + amt : amt;
  9.  
  10.      /* new state is only reported if it's a decrease */
  11.      if (newspe < obj->spe) {
  12.          if (verbose) {
  13.              if (carried(obj))
  14.                  pline("%s dries%s.", Yobjnam2(obj, (const char *) 0),
  15.                        !newspe ? " out" : "");
  16.              else if (mcarried(obj) && canseemon(obj->ocarry))
  17.                  pline("%s %s drie%s.", s_suffix(Monnam(obj->ocarry)),
  18.                        xname(obj), !newspe ? " out" : "");
  19.          }
  20.      }
  21.      newspe = min(newspe, 7);
  22.      obj->spe = max(newspe, 0);
  23.  
  24.      /* if hero is wielding this towel and it is now dry, give "you begin
  25.         bashing with your towel" message on next attack with it */
  26.      if (obj == uwep)
  27.          unweapon = !is_wet_towel(obj);
  28.  }
  29.  

skill_level_name

  1.  /* copy the skill level name into the given buffer */
  2.  STATIC_OVL char *
  3.  skill_level_name(skill, buf)
  4.  int skill;
  5.  char *buf;
  6.  {
  7.      const char *ptr;
  8.  
  9.      switch (P_SKILL(skill)) {
  10.      case P_UNSKILLED:
  11.          ptr = "Unskilled";
  12.          break;
  13.      case P_BASIC:
  14.          ptr = "Basic";
  15.          break;
  16.      case P_SKILLED:
  17.          ptr = "Skilled";
  18.          break;
  19.      case P_EXPERT:
  20.          ptr = "Expert";
  21.          break;
  22.      /* these are for unarmed combat/martial arts only */
  23.      case P_MASTER:
  24.          ptr = "Master";
  25.          break;
  26.      case P_GRAND_MASTER:
  27.          ptr = "Grand Master";
  28.          break;
  29.      default:
  30.          ptr = "Unknown";
  31.          break;
  32.      }
  33.      Strcpy(buf, ptr);
  34.      return buf;
  35.  }
  36.  

slots_required

  1.  /* return the # of slots required to advance the skill */
  2.  STATIC_OVL int
  3.  slots_required(skill)
  4.  int skill;
  5.  {
  6.      int tmp = P_SKILL(skill);
  7.  
  8.      /* The more difficult the training, the more slots it takes.
  9.       *  unskilled -> basic      1
  10.       *  basic -> skilled        2
  11.       *  skilled -> expert       3
  12.       */
  13.      if (skill <= P_LAST_WEAPON || skill == P_TWO_WEAPON_COMBAT)
  14.          return tmp;
  15.  
  16.      /* Fewer slots used up for unarmed or martial.
  17.       *  unskilled -> basic      1
  18.       *  basic -> skilled        1
  19.       *  skilled -> expert       2
  20.       *  expert -> master        2
  21.       *  master -> grand master  3
  22.       */
  23.      return (tmp + 1) / 2;
  24.  }
  25.  

can_advance

  1.  /* return true if this skill can be advanced */
  2.  /*ARGSUSED*/
  3.  STATIC_OVL boolean
  4.  can_advance(skill, speedy)
  5.  int skill;
  6.  boolean speedy;
  7.  {
  8.      if (P_RESTRICTED(skill)
  9.          || P_SKILL(skill) >= P_MAX_SKILL(skill)
  10.          || u.skills_advanced >= P_SKILL_LIMIT)
  11.          return FALSE;
  12.  
  13.      if (wizard && speedy)
  14.          return TRUE;
  15.  
  16.      return (boolean) ((int) P_ADVANCE(skill)
  17.                        >= practice_needed_to_advance(P_SKILL(skill))
  18.                        && u.weapon_slots >= slots_required(skill));
  19.  }
  20.  

could_advance

  1.  /* return true if this skill could be advanced if more slots were available */
  2.  STATIC_OVL boolean
  3.  could_advance(skill)
  4.  int skill;
  5.  {
  6.      if (P_RESTRICTED(skill)
  7.          || P_SKILL(skill) >= P_MAX_SKILL(skill)
  8.          || u.skills_advanced >= P_SKILL_LIMIT)
  9.          return FALSE;
  10.  
  11.      return (boolean) ((int) P_ADVANCE(skill)
  12.                        >= practice_needed_to_advance(P_SKILL(skill)));
  13.  }
  14.  

peaked_skill

  1.  /* return true if this skill has reached its maximum and there's been enough
  2.     practice to become eligible for the next step if that had been possible */
  3.  STATIC_OVL boolean
  4.  peaked_skill(skill)
  5.  int skill;
  6.  {
  7.      if (P_RESTRICTED(skill))
  8.          return FALSE;
  9.  
  10.      return (boolean) (P_SKILL(skill) >= P_MAX_SKILL(skill)
  11.                        && ((int) P_ADVANCE(skill)
  12.                            >= practice_needed_to_advance(P_SKILL(skill))));
  13.  }
  14.  

skill_advance

  1.  STATIC_OVL void
  2.  skill_advance(skill)
  3.  int skill;
  4.  {
  5.      u.weapon_slots -= slots_required(skill);
  6.      P_SKILL(skill)++;
  7.      u.skill_record[u.skills_advanced++] = skill;
  8.      /* subtly change the advance message to indicate no more advancement */
  9.      You("are now %s skilled in %s.",
  10.          P_SKILL(skill) >= P_MAX_SKILL(skill) ? "most" : "more",
  11.          P_NAME(skill));
  12.  }
  13.  

enhance_weapon_skill

  1.  static const struct skill_range {
  2.      short first, last;
  3.      const char *name;
  4.  } skill_ranges[] = {
  5.      { P_FIRST_H_TO_H, P_LAST_H_TO_H, "Fighting Skills" },
  6.      { P_FIRST_WEAPON, P_LAST_WEAPON, "Weapon Skills" },
  7.      { P_FIRST_SPELL, P_LAST_SPELL, "Spellcasting Skills" },
  8.  };
  9.  
  10.  /*
  11.   * The `#enhance' extended command.  What we _really_ would like is
  12.   * to keep being able to pick things to advance until we couldn't any
  13.   * more.  This is currently not possible -- the menu code has no way
  14.   * to call us back for instant action.  Even if it did, we would also need
  15.   * to be able to update the menu since selecting one item could make
  16.   * others unselectable.
  17.   */
  18.  int
  19.  enhance_weapon_skill()
  20.  {
  21.      int pass, i, n, len, longest, to_advance, eventually_advance, maxxed_cnt;
  22.      char buf[BUFSZ], sklnambuf[BUFSZ];
  23.      const char *prefix;
  24.      menu_item *selected;
  25.      anything any;
  26.      winid win;
  27.      boolean speedy = FALSE;
  28.  
  29.      if (wizard && yn("Advance skills without practice?") == 'y')
  30.          speedy = TRUE;
  31.  
  32.      do {
  33.          /* find longest available skill name, count those that can advance */
  34.          to_advance = eventually_advance = maxxed_cnt = 0;
  35.          for (longest = 0, i = 0; i < P_NUM_SKILLS; i++) {
  36.              if (P_RESTRICTED(i))
  37.                  continue;
  38.              if ((len = strlen(P_NAME(i))) > longest)
  39.                  longest = len;
  40.              if (can_advance(i, speedy))
  41.                  to_advance++;
  42.              else if (could_advance(i))
  43.                  eventually_advance++;
  44.              else if (peaked_skill(i))
  45.                  maxxed_cnt++;
  46.          }
  47.  
  48.          win = create_nhwindow(NHW_MENU);
  49.          start_menu(win);
  50.  
  51.          /* start with a legend if any entries will be annotated
  52.             with "*" or "#" below */
  53.          if (eventually_advance > 0 || maxxed_cnt > 0) {
  54.              any = zeroany;
  55.              if (eventually_advance > 0) {
  56.                  Sprintf(buf, "(Skill%s flagged by \"*\" may be enhanced %s.)",
  57.                          plur(eventually_advance),
  58.                          (u.ulevel < MAXULEV)
  59.                              ? "when you're more experienced"
  60.                              : "if skill slots become available");
  61.                  add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf,
  62.                           MENU_UNSELECTED);
  63.              }
  64.              if (maxxed_cnt > 0) {
  65.                  Sprintf(buf,
  66.                   "(Skill%s flagged by \"#\" cannot be enhanced any further.)",
  67.                          plur(maxxed_cnt));
  68.                  add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf,
  69.                           MENU_UNSELECTED);
  70.              }
  71.              add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
  72.                       MENU_UNSELECTED);
  73.          }
  74.  
  75.          /* List the skills, making ones that could be advanced
  76.             selectable.  List the miscellaneous skills first.
  77.             Possible future enhancement:  list spell skills before
  78.             weapon skills for spellcaster roles. */
  79.          for (pass = 0; pass < SIZE(skill_ranges); pass++)
  80.              for (i = skill_ranges[pass].first; i <= skill_ranges[pass].last;
  81.                   i++) {
  82.                  /* Print headings for skill types */
  83.                  any = zeroany;
  84.                  if (i == skill_ranges[pass].first)
  85.                      add_menu(win, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
  86.                               skill_ranges[pass].name, MENU_UNSELECTED);
  87.  
  88.                  if (P_RESTRICTED(i))
  89.                      continue;
  90.                  /*
  91.                   * Sigh, this assumes a monospaced font unless
  92.                   * iflags.menu_tab_sep is set in which case it puts
  93.                   * tabs between columns.
  94.                   * The 12 is the longest skill level name.
  95.                   * The "    " is room for a selection letter and dash, "a - ".
  96.                   */
  97.                  if (can_advance(i, speedy))
  98.                      prefix = ""; /* will be preceded by menu choice */
  99.                  else if (could_advance(i))
  100.                      prefix = "  * ";
  101.                  else if (peaked_skill(i))
  102.                      prefix = "  # ";
  103.                  else
  104.                      prefix =
  105.                          (to_advance + eventually_advance + maxxed_cnt > 0)
  106.                              ? "    "
  107.                              : "";
  108.                  (void) skill_level_name(i, sklnambuf);
  109.                  if (wizard) {
  110.                      if (!iflags.menu_tab_sep)
  111.                          Sprintf(buf, " %s%-*s %-12s %5d(%4d)", prefix,
  112.                                  longest, P_NAME(i), sklnambuf, P_ADVANCE(i),
  113.                                  practice_needed_to_advance(P_SKILL(i)));
  114.                      else
  115.                          Sprintf(buf, " %s%s\t%s\t%5d(%4d)", prefix, P_NAME(i),
  116.                                  sklnambuf, P_ADVANCE(i),
  117.                                  practice_needed_to_advance(P_SKILL(i)));
  118.                  } else {
  119.                      if (!iflags.menu_tab_sep)
  120.                          Sprintf(buf, " %s %-*s [%s]", prefix, longest,
  121.                                  P_NAME(i), sklnambuf);
  122.                      else
  123.                          Sprintf(buf, " %s%s\t[%s]", prefix, P_NAME(i),
  124.                                  sklnambuf);
  125.                  }
  126.                  any.a_int = can_advance(i, speedy) ? i + 1 : 0;
  127.                  add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf,
  128.                           MENU_UNSELECTED);
  129.              }
  130.  
  131.          Strcpy(buf, (to_advance > 0) ? "Pick a skill to advance:"
  132.                                       : "Current skills:");
  133.          if (wizard && !speedy)
  134.              Sprintf(eos(buf), "  (%d slot%s available)", u.weapon_slots,
  135.                      plur(u.weapon_slots));
  136.          end_menu(win, buf);
  137.          n = select_menu(win, to_advance ? PICK_ONE : PICK_NONE, &selected);
  138.          destroy_nhwindow(win);
  139.          if (n > 0) {
  140.              n = selected[0].item.a_int - 1; /* get item selected */
  141.              free((genericptr_t) selected);
  142.              skill_advance(n);
  143.              /* check for more skills able to advance, if so then .. */
  144.              for (n = i = 0; i < P_NUM_SKILLS; i++) {
  145.                  if (can_advance(i, speedy)) {
  146.                      if (!speedy)
  147.                          You_feel("you could be more dangerous!");
  148.                      n++;
  149.                      break;
  150.                  }
  151.              }
  152.          }
  153.      } while (speedy && n > 0);
  154.      return 0;
  155.  }
  156.  

unrestrict_weapon_skill

  1.  /*
  2.   * Change from restricted to unrestricted, allowing P_BASIC as max.  This
  3.   * function may be called with with P_NONE.  Used in pray.c.
  4.   */
  5.  void
  6.  unrestrict_weapon_skill(skill)
  7.  int skill;
  8.  {
  9.      if (skill < P_NUM_SKILLS && P_RESTRICTED(skill)) {
  10.          P_SKILL(skill) = P_UNSKILLED;
  11.          P_MAX_SKILL(skill) = P_BASIC;
  12.          P_ADVANCE(skill) = 0;
  13.      }
  14.  }
  15.  

use_skill

  1.  void
  2.  use_skill(skill, degree)
  3.  int skill;
  4.  int degree;
  5.  {
  6.      boolean advance_before;
  7.  
  8.      if (skill != P_NONE && !P_RESTRICTED(skill)) {
  9.          advance_before = can_advance(skill, FALSE);
  10.          P_ADVANCE(skill) += degree;
  11.          if (!advance_before && can_advance(skill, FALSE))
  12.              give_may_advance_msg(skill);
  13.      }
  14.  }
  15.  

add_weapon_skill

  1.  void
  2.  add_weapon_skill(n)
  3.  int n; /* number of slots to gain; normally one */
  4.  {
  5.      int i, before, after;
  6.  
  7.      for (i = 0, before = 0; i < P_NUM_SKILLS; i++)
  8.          if (can_advance(i, FALSE))
  9.              before++;
  10.      u.weapon_slots += n;
  11.      for (i = 0, after = 0; i < P_NUM_SKILLS; i++)
  12.          if (can_advance(i, FALSE))
  13.              after++;
  14.      if (before < after)
  15.          give_may_advance_msg(P_NONE);
  16.  }
  17.  

lose_weapon_skill

  1.  void
  2.  lose_weapon_skill(n)
  3.  int n; /* number of slots to lose; normally one */
  4.  {
  5.      int skill;
  6.  
  7.      while (--n >= 0) {
  8.          /* deduct first from unused slots then from last placed one, if any */
  9.          if (u.weapon_slots) {
  10.              u.weapon_slots--;
  11.          } else if (u.skills_advanced) {
  12.              skill = u.skill_record[--u.skills_advanced];
  13.              if (P_SKILL(skill) <= P_UNSKILLED)
  14.                  panic("lose_weapon_skill (%d)", skill);
  15.              P_SKILL(skill)--; /* drop skill one level */
  16.              /* Lost skill might have taken more than one slot; refund rest. */
  17.              u.weapon_slots = slots_required(skill) - 1;
  18.              /* It might now be possible to advance some other pending
  19.                 skill by using the refunded slots, but giving a message
  20.                 to that effect would seem pretty confusing.... */
  21.          }
  22.      }
  23.  }
  24.  

weapon_type

  1.  int
  2.  weapon_type(obj)
  3.  struct obj *obj;
  4.  {
  5.      /* KMH -- now uses the object table */
  6.      int type;
  7.  
  8.      if (!obj)
  9.          return P_BARE_HANDED_COMBAT; /* Not using a weapon */
  10.      if (obj->oclass != WEAPON_CLASS && obj->oclass != TOOL_CLASS
  11.          && obj->oclass != GEM_CLASS)
  12.          return P_NONE; /* Not a weapon, weapon-tool, or ammo */
  13.      type = objects[obj->otyp].oc_skill;
  14.      return (type < 0) ? -type : type;
  15.  }
  16.  

uwep_skill_type

  1.  int
  2.  uwep_skill_type()
  3.  {
  4.      if (u.twoweap)
  5.          return P_TWO_WEAPON_COMBAT;
  6.      return weapon_type(uwep);
  7.  }
  8.  

weapon_hit_bonus

  1.  /*
  2.   * Return hit bonus/penalty based on skill of weapon.
  3.   * Treat restricted weapons as unskilled.
  4.   */
  5.  int
  6.  weapon_hit_bonus(weapon)
  7.  struct obj *weapon;
  8.  {
  9.      int type, wep_type, skill, bonus = 0;
  10.      static const char bad_skill[] = "weapon_hit_bonus: bad skill %d";
  11.  
  12.      wep_type = weapon_type(weapon);
  13.      /* use two weapon skill only if attacking with one of the wielded weapons
  14.       */
  15.      type = (u.twoweap && (weapon == uwep || weapon == uswapwep))
  16.                 ? P_TWO_WEAPON_COMBAT
  17.                 : wep_type;
  18.      if (type == P_NONE) {
  19.          bonus = 0;
  20.      } else if (type <= P_LAST_WEAPON) {
  21.          switch (P_SKILL(type)) {
  22.          default:
  23.              impossible(bad_skill, P_SKILL(type)); /* fall through */
  24.          case P_ISRESTRICTED:
  25.          case P_UNSKILLED:
  26.              bonus = -4;
  27.              break;
  28.          case P_BASIC:
  29.              bonus = 0;
  30.              break;
  31.          case P_SKILLED:
  32.              bonus = 2;
  33.              break;
  34.          case P_EXPERT:
  35.              bonus = 3;
  36.              break;
  37.          }
  38.      } else if (type == P_TWO_WEAPON_COMBAT) {
  39.          skill = P_SKILL(P_TWO_WEAPON_COMBAT);
  40.          if (P_SKILL(wep_type) < skill)
  41.              skill = P_SKILL(wep_type);
  42.          switch (skill) {
  43.          default:
  44.              impossible(bad_skill, skill); /* fall through */
  45.          case P_ISRESTRICTED:
  46.          case P_UNSKILLED:
  47.              bonus = -9;
  48.              break;
  49.          case P_BASIC:
  50.              bonus = -7;
  51.              break;
  52.          case P_SKILLED:
  53.              bonus = -5;
  54.              break;
  55.          case P_EXPERT:
  56.              bonus = -3;
  57.              break;
  58.          }
  59.      } else if (type == P_BARE_HANDED_COMBAT) {
  60.          /*
  61.           *        b.h. m.a.
  62.           * unskl:  +1  n/a
  63.           * basic:  +1   +3
  64.           * skild:  +2   +4
  65.           * exprt:  +2   +5
  66.           * mastr:  +3   +6
  67.           * grand:  +3   +7
  68.           */
  69.          bonus = P_SKILL(type);
  70.          bonus = max(bonus, P_UNSKILLED) - 1; /* unskilled => 0 */
  71.          bonus = ((bonus + 2) * (martial_bonus() ? 2 : 1)) / 2;
  72.      }
  73.  
  74.      /* KMH -- It's harder to hit while you are riding */
  75.      if (u.usteed) {
  76.          switch (P_SKILL(P_RIDING)) {
  77.          case P_ISRESTRICTED:
  78.          case P_UNSKILLED:
  79.              bonus -= 2;
  80.              break;
  81.          case P_BASIC:
  82.              bonus -= 1;
  83.              break;
  84.          case P_SKILLED:
  85.              break;
  86.          case P_EXPERT:
  87.              break;
  88.          }
  89.          if (u.twoweap)
  90.              bonus -= 2;
  91.      }
  92.  
  93.      return bonus;
  94.  }
  95.  

weapon_dam_bonus

  1.  /*
  2.   * Return damage bonus/penalty based on skill of weapon.
  3.   * Treat restricted weapons as unskilled.
  4.   */
  5.  int
  6.  weapon_dam_bonus(weapon)
  7.  struct obj *weapon;
  8.  {
  9.      int type, wep_type, skill, bonus = 0;
  10.  
  11.      wep_type = weapon_type(weapon);
  12.      /* use two weapon skill only if attacking with one of the wielded weapons
  13.       */
  14.      type = (u.twoweap && (weapon == uwep || weapon == uswapwep))
  15.                 ? P_TWO_WEAPON_COMBAT
  16.                 : wep_type;
  17.      if (type == P_NONE) {
  18.          bonus = 0;
  19.      } else if (type <= P_LAST_WEAPON) {
  20.          switch (P_SKILL(type)) {
  21.          default:
  22.              impossible("weapon_dam_bonus: bad skill %d", P_SKILL(type));
  23.          /* fall through */
  24.          case P_ISRESTRICTED:
  25.          case P_UNSKILLED:
  26.              bonus = -2;
  27.              break;
  28.          case P_BASIC:
  29.              bonus = 0;
  30.              break;
  31.          case P_SKILLED:
  32.              bonus = 1;
  33.              break;
  34.          case P_EXPERT:
  35.              bonus = 2;
  36.              break;
  37.          }
  38.      } else if (type == P_TWO_WEAPON_COMBAT) {
  39.          skill = P_SKILL(P_TWO_WEAPON_COMBAT);
  40.          if (P_SKILL(wep_type) < skill)
  41.              skill = P_SKILL(wep_type);
  42.          switch (skill) {
  43.          default:
  44.          case P_ISRESTRICTED:
  45.          case P_UNSKILLED:
  46.              bonus = -3;
  47.              break;
  48.          case P_BASIC:
  49.              bonus = -1;
  50.              break;
  51.          case P_SKILLED:
  52.              bonus = 0;
  53.              break;
  54.          case P_EXPERT:
  55.              bonus = 1;
  56.              break;
  57.          }
  58.      } else if (type == P_BARE_HANDED_COMBAT) {
  59.          /*
  60.           *        b.h. m.a.
  61.           * unskl:   0  n/a
  62.           * basic:  +1   +3
  63.           * skild:  +1   +4
  64.           * exprt:  +2   +6
  65.           * mastr:  +2   +7
  66.           * grand:  +3   +9
  67.           */
  68.          bonus = P_SKILL(type);
  69.          bonus = max(bonus, P_UNSKILLED) - 1; /* unskilled => 0 */
  70.          bonus = ((bonus + 1) * (martial_bonus() ? 3 : 1)) / 2;
  71.      }
  72.  
  73.      /* KMH -- Riding gives some thrusting damage */
  74.      if (u.usteed && type != P_TWO_WEAPON_COMBAT) {
  75.          switch (P_SKILL(P_RIDING)) {
  76.          case P_ISRESTRICTED:
  77.          case P_UNSKILLED:
  78.              break;
  79.          case P_BASIC:
  80.              break;
  81.          case P_SKILLED:
  82.              bonus += 1;
  83.              break;
  84.          case P_EXPERT:
  85.              bonus += 2;
  86.              break;
  87.          }
  88.      }
  89.  
  90.      return bonus;
  91.  }
  92.  

skill_init

  1.  /*
  2.   * Initialize weapon skill array for the game.  Start by setting all
  3.   * skills to restricted, then set the skill for every weapon the
  4.   * hero is holding, finally reading the given array that sets
  5.   * maximums.
  6.   */
  7.  void
  8.  skill_init(class_skill)
  9.  const struct def_skill *class_skill;
  10.  {
  11.      struct obj *obj;
  12.      int skmax, skill;
  13.  
  14.      /* initialize skill array; by default, everything is restricted */
  15.      for (skill = 0; skill < P_NUM_SKILLS; skill++) {
  16.          P_SKILL(skill) = P_ISRESTRICTED;
  17.          P_MAX_SKILL(skill) = P_ISRESTRICTED;
  18.          P_ADVANCE(skill) = 0;
  19.      }
  20.  
  21.      /* Set skill for all weapons in inventory to be basic */
  22.      for (obj = invent; obj; obj = obj->nobj) {
  23.          /* don't give skill just because of carried ammo, wait until
  24.             we see the relevant launcher (prevents an archeologist's
  25.             touchstone from inadvertently providing skill in sling) */
  26.          if (is_ammo(obj))
  27.              continue;
  28.  
  29.          skill = weapon_type(obj);
  30.          if (skill != P_NONE)
  31.              P_SKILL(skill) = P_BASIC;
  32.      }
  33.  
  34.      /* set skills for magic */
  35.      if (Role_if(PM_HEALER) || Role_if(PM_MONK)) {
  36.          P_SKILL(P_HEALING_SPELL) = P_BASIC;
  37.      } else if (Role_if(PM_PRIEST)) {
  38.          P_SKILL(P_CLERIC_SPELL) = P_BASIC;
  39.      } else if (Role_if(PM_WIZARD)) {
  40.          P_SKILL(P_ATTACK_SPELL) = P_BASIC;
  41.          P_SKILL(P_ENCHANTMENT_SPELL) = P_BASIC;
  42.      }
  43.  
  44.      /* walk through array to set skill maximums */
  45.      for (; class_skill->skill != P_NONE; class_skill++) {
  46.          skmax = class_skill->skmax;
  47.          skill = class_skill->skill;
  48.  
  49.          P_MAX_SKILL(skill) = skmax;
  50.          if (P_SKILL(skill) == P_ISRESTRICTED) /* skill pre-set */
  51.              P_SKILL(skill) = P_UNSKILLED;
  52.      }
  53.  
  54.      /* High potential fighters already know how to use their hands. */
  55.      if (P_MAX_SKILL(P_BARE_HANDED_COMBAT) > P_EXPERT)
  56.          P_SKILL(P_BARE_HANDED_COMBAT) = P_BASIC;
  57.  
  58.      /* Roles that start with a horse know how to ride it */
  59.      if (urole.petnum == PM_PONY)
  60.          P_SKILL(P_RIDING) = P_BASIC;
  61.  
  62.      /*
  63.       * Make sure we haven't missed setting the max on a skill
  64.       * & set advance
  65.       */
  66.      for (skill = 0; skill < P_NUM_SKILLS; skill++) {
  67.          if (!P_RESTRICTED(skill)) {
  68.              if (P_MAX_SKILL(skill) < P_SKILL(skill)) {
  69.                  impossible("skill_init: curr > max: %s", P_NAME(skill));
  70.                  P_MAX_SKILL(skill) = P_SKILL(skill);
  71.              }
  72.              P_ADVANCE(skill) = practice_needed_to_advance(P_SKILL(skill) - 1);
  73.          }
  74.      }
  75.  }
  76.  

setmnotwielded

  1.  void
  2.  setmnotwielded(mon, obj)
  3.  register struct monst *mon;
  4.  register struct obj *obj;
  5.  {
  6.      if (!obj)
  7.          return;
  8.      if (artifact_light(obj) && obj->lamplit) {
  9.          end_burn(obj, FALSE);
  10.          if (canseemon(mon))
  11.              pline("%s in %s %s %s shining.", The(xname(obj)),
  12.                    s_suffix(mon_nam(mon)), mbodypart(mon, HAND),
  13.                    otense(obj, "stop"));
  14.      }
  15.      if (MON_WEP(mon) == obj)
  16.          MON_NOWEP(mon);
  17.      obj->owornmask &= ~W_WEP;
  18.  }
  19.  
  20.  /*weapon.c*/