1    | /*	SCCS Id: @(#)weapon.c	3.3	2000/01/22	*/
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   | #ifndef OVLB
30   | 
31   | STATIC_DCL NEARDATA const short skill_names_indices[];
32   | STATIC_DCL NEARDATA const char *odd_skill_names[];
33   | STATIC_DCL NEARDATA const char *barehands_or_martial[];
34   | 
35   | #else	/* OVLB */
36   | 
37   | STATIC_VAR NEARDATA const short skill_names_indices[P_NUM_SKILLS] = {
38   | 	0,                DAGGER,         KNIFE,        AXE,
39   | 	PICK_AXE,         SHORT_SWORD,    BROADSWORD,   LONG_SWORD,
40   | 	TWO_HANDED_SWORD, SCIMITAR,       PN_SABER,     CLUB,
41   | 	MACE,             MORNING_STAR,   FLAIL,
42   | 	PN_HAMMER,        QUARTERSTAFF,   PN_POLEARMS,  SPEAR,
43   | 	JAVELIN,          TRIDENT,        LANCE,        BOW,
44   | 	SLING,            CROSSBOW,       DART,
45   | 	SHURIKEN,         BOOMERANG,      PN_WHIP,      UNICORN_HORN,
46   | 	PN_ATTACK_SPELL,     PN_HEALING_SPELL,
47   | 	PN_DIVINATION_SPELL, PN_ENCHANTMENT_SPELL,
48   | 	PN_CLERIC_SPELL,     PN_ESCAPE_SPELL,
49   | 	PN_MATTER_SPELL,
50   | 	PN_BARE_HANDED,   PN_TWO_WEAPONS,
51   | #ifdef STEED
52   | 	PN_RIDING
53   | #endif
54   | };
55   | 
56   | /* note: entry [0] isn't used */
57   | STATIC_VAR NEARDATA const char *odd_skill_names[] = {
58   |     "no skill",
59   |     "bare hands",		/* use barehands_or_martial[] instead */
60   |     "two weapon combat",
61   |     "riding",
62   |     "polearms",
63   |     "saber",
64   |     "hammer",
65   |     "whip",
66   |     "attack spells",
67   |     "healing spells",
68   |     "divination spells",
69   |     "enchantment spells",
70   |     "clerical spells",
71   |     "escape spells",
72   |     "matter spells",
73   | };
74   | /* indexed vis `is_martial() */
75   | STATIC_VAR NEARDATA const char *barehands_or_martial[] = {
76   |     "bare handed combat", "martial arts"
77   | };
78   | 
79   | STATIC_OVL
80   | void give_may_advance_msg(skill)
81   | int skill;
82   | {
83   | 	You_feel("more confident in your %sskills.",
84   | 		skill == P_NONE ?
85   | 			"" :
86   | 		skill <= P_LAST_WEAPON ?
87   | 			"weapon " :
88   | 		skill <= P_LAST_SPELL ?
89   | 			"spell casting " :
90   | 		"fighting ");
91   | }
92   | 
93   | #endif	/* OVLB */
94   | 
95   | STATIC_DCL boolean FDECL(can_advance, (int, BOOLEAN_P));
96   | STATIC_DCL int FDECL(slots_required, (int));
97   | 
98   | #ifdef OVL1
99   | 
100  | STATIC_DCL char *FDECL(skill_level_name, (int,char *));
101  | STATIC_DCL void FDECL(skill_advance, (int));
102  | 
103  | #endif	/* OVL1 */
104  | 
105  | #define P_NAME(type) ((skill_names_indices[type] > 0) ? \
106  | 		      OBJ_NAME(objects[skill_names_indices[type]]) : \
107  | 		      (type == P_BARE_HANDED_COMBAT) ? \
108  | 			barehands_or_martial[martial_bonus()] : \
109  | 			odd_skill_names[-skill_names_indices[type]])
110  | 
111  | #ifdef OVLB
112  | static NEARDATA const char kebabable[] = {
113  | 	S_XORN, S_DRAGON, S_JABBERWOCK, S_NAGA, S_GIANT, '\0'
114  | };
115  | 
116  | /*
117  |  *	hitval returns an integer representing the "to hit" bonuses
118  |  *	of "otmp" against the monster.
119  |  */
120  | int
121  | hitval(otmp, mon)
122  | struct obj *otmp;
123  | struct monst *mon;
124  | {
125  | 	int	tmp = 0;
126  | 	struct permonst *ptr = mon->data;
127  | 	boolean Is_weapon = (otmp->oclass == WEAPON_CLASS || is_weptool(otmp));
128  | 
129  | 	if (Is_weapon)
130  | 		tmp += otmp->spe;
131  | 
132  | /*	Put weapon specific "to hit" bonuses in below:		*/
133  | 	tmp += objects[otmp->otyp].oc_hitbon;
134  | 
135  | /*	Put weapon vs. monster type "to hit" bonuses in below:	*/
136  | 
137  | 	/* Blessed weapons used against undead or demons */
138  | 	if (Is_weapon && otmp->blessed &&
139  | 	   (is_demon(ptr) || is_undead(ptr))) tmp += 2;
140  | 
141  | 	if (is_spear(otmp) &&
142  | 	   index(kebabable, ptr->mlet)) tmp += 2;
143  | 
144  | 	/* trident is highly effective against swimmers */
145  | 	if (otmp->otyp == TRIDENT && is_swimmer(ptr)) {
146  | 	   if (is_pool(mon->mx, mon->my)) tmp += 4;
147  | 	   else if (ptr->mlet == S_EEL || ptr->mlet == S_SNAKE) tmp += 2;
148  | 	}
149  | 
150  | 	/* Picks used against xorns and earth elementals */
151  | 	if (is_pick(otmp) &&
152  | 	   (passes_walls(ptr) && thick_skinned(ptr))) tmp += 2;
153  | 
154  | #ifdef INVISIBLE_OBJECTS
155  | 	/* Invisible weapons against monsters who can't see invisible */
156  | 	if (otmp->oinvis && !perceives(ptr)) tmp += 3;
157  | #endif
158  | 
159  | 	/* Check specially named weapon "to hit" bonuses */
160  | 	if (otmp->oartifact) tmp += spec_abon(otmp, mon);
161  | 
162  | 	return tmp;
163  | }
164  | 
165  | /* Historical note: The original versions of Hack used a range of damage
166  |  * which was similar to, but not identical to the damage used in Advanced
167  |  * Dungeons and Dragons.  I figured that since it was so close, I may as well
168  |  * make it exactly the same as AD&D, adding some more weapons in the process.
169  |  * This has the advantage that it is at least possible that the player would
170  |  * already know the damage of at least some of the weapons.  This was circa
171  |  * 1987 and I used the table from Unearthed Arcana until I got tired of typing
172  |  * them in (leading to something of an imbalance towards weapons early in
173  |  * alphabetical order).  The data structure still doesn't include fields that
174  |  * fully allow the appropriate damage to be described (there's no way to say
175  |  * 3d6 or 1d6+1) so we add on the extra damage in dmgval() if the weapon
176  |  * doesn't do an exact die of damage.
177  |  *
178  |  * Of course new weapons were added later in the development of Nethack.  No
179  |  * AD&D consistency was kept, but most of these don't exist in AD&D anyway.
180  |  *
181  |  * Second edition AD&D came out a few years later; luckily it used the same
182  |  * table.  As of this writing (1999), third edition is in progress but not
183  |  * released.  Let's see if the weapon table stays the same.  --KAA
184  |  */
185  | 
186  | /*
187  |  *	dmgval returns an integer representing the damage bonuses
188  |  *	of "otmp" against the monster.
189  |  */
190  | int
191  | dmgval(otmp, mon)
192  | struct obj *otmp;
193  | struct monst *mon;
194  | {
195  | 	int tmp = 0, otyp = otmp->otyp;
196  | 	struct permonst *ptr = mon->data;
197  | 	boolean Is_weapon = (otmp->oclass == WEAPON_CLASS || is_weptool(otmp));
198  | 
199  | 	if (otyp == CREAM_PIE) return 0;
200  | 
201  | 	if (bigmonst(ptr)) {
202  | 	    if (objects[otyp].oc_wldam)
203  | 		tmp = rnd(objects[otyp].oc_wldam);
204  | 	    switch (otyp) {
205  | 		case IRON_CHAIN:
206  | 		case CROSSBOW_BOLT:
207  | 		case MORNING_STAR:
208  | 		case PARTISAN:
209  | 		case RUNESWORD:
210  | 		case ELVEN_BROADSWORD:
211  | 		case BROADSWORD:	tmp++; break;
212  | 
213  | 		case FLAIL:
214  | 		case RANSEUR:
215  | 		case VOULGE:		tmp += rnd(4); break;
216  | 
217  | 		case ACID_VENOM:
218  | 		case HALBERD:
219  | 		case SPETUM:		tmp += rnd(6); break;
220  | 
221  | 		case BATTLE_AXE:
222  | 		case BARDICHE:
223  | 		case TRIDENT:		tmp += d(2,4); break;
224  | 
225  | 		case TSURUGI:
226  | 		case DWARVISH_MATTOCK:
227  | 		case TWO_HANDED_SWORD:	tmp += d(2,6); break;
228  | 	    }
229  | 	} else {
230  | 	    if (objects[otyp].oc_wsdam)
231  | 		tmp = rnd(objects[otyp].oc_wsdam);
232  | 	    switch (otyp) {
233  | 		case IRON_CHAIN:
234  | 		case CROSSBOW_BOLT:
235  | 		case MACE:
236  | 		case WAR_HAMMER:
237  | 		case FLAIL:
238  | 		case SPETUM:
239  | 		case TRIDENT:		tmp++; break;
240  | 
241  | 		case BATTLE_AXE:
242  | 		case BARDICHE:
243  | 		case BILL_GUISARME:
244  | 		case GUISARME:
245  | 		case LUCERN_HAMMER:
246  | 		case MORNING_STAR:
247  | 		case RANSEUR:
248  | 		case BROADSWORD:
249  | 		case ELVEN_BROADSWORD:
250  | 		case RUNESWORD:
251  | 		case VOULGE:		tmp += rnd(4); break;
252  | 
253  | 		case ACID_VENOM:	tmp += rnd(6); break;
254  | 	    }
255  | 	}
256  | 	if (Is_weapon) {
257  | 		tmp += otmp->spe;
258  | 		/* negative enchantment mustn't produce negative damage */
259  | 		if (tmp < 0) tmp = 0;
260  | 	}
261  | 
262  | 	if (objects[otyp].oc_material <= LEATHER && thick_skinned(ptr))
263  | 		/* thick skinned/scaled creatures don't feel it */
264  | 		tmp = 0;
265  | 	if (ptr == &mons[PM_SHADE] && objects[otyp].oc_material != SILVER)
266  | 		tmp = 0;
267  | 
268  | 	/* "very heavy iron ball"; weight increase is in increments of 160 */
269  | 	if (otyp == HEAVY_IRON_BALL && tmp > 0) {
270  | 	    int wt = (int)objects[HEAVY_IRON_BALL].oc_weight;
271  | 
272  | 	    if ((int)otmp->owt > wt) {
273  | 		wt = ((int)otmp->owt - wt) / 160;
274  | 		tmp += rnd(4 * wt);
275  | 		if (tmp > 25) tmp = 25;	/* objects[].oc_wldam */
276  | 	    }
277  | 	}
278  | 
279  | /*	Put weapon vs. monster type damage bonuses in below:	*/
280  | 	if (Is_weapon || otmp->oclass == GEM_CLASS ||
281  | 		otmp->oclass == BALL_CLASS || otmp->oclass == CHAIN_CLASS) {
282  | 	    int bonus = 0;
283  | 
284  | #ifdef STEED
285  | 		/* KMH -- Lances are especially made for riding */
286  | 		if (otmp == uwep && u.usteed &&
287  | 				objects[otmp->otyp].oc_skill == P_LANCE)
288  | 			bonus += d(2,10);
289  | #endif
290  | 
291  | 	    if (otmp->blessed && (is_undead(ptr) || is_demon(ptr)))
292  | 		bonus += rnd(4);
293  | 	    if (is_axe(otmp) && is_wooden(ptr))
294  | 		bonus += rnd(4);
295  | 	    if (objects[otyp].oc_material == SILVER && hates_silver(ptr))
296  | 		bonus += rnd(20);
297  | 
298  | 	    /* if the weapon is going to get a double damage bonus, adjust
299  | 	       this bonus so that effectively it's added after the doubling */
300  | 	    if (bonus > 1 && otmp->oartifact && spec_dbon(otmp, mon, 25) >= 25)
301  | 		bonus = (bonus + 1) / 2;
302  | 
303  | 	    tmp += bonus;
304  | 	}
305  | 
306  | 	if (tmp > 0) {
307  | 		/* It's debateable whether a rusted blunt instrument
308  | 		   should do less damage than a pristine one, since
309  | 		   it will hit with essentially the same impact, but
310  | 		   there ought to some penalty for using damaged gear
311  | 		   so always subtract erosion even for blunt weapons. */
312  | 		tmp -= greatest_erosion(otmp);
313  | 		if (tmp < 1) tmp = 1;
314  | 	}
315  | 
316  | 	return(tmp);
317  | }
318  | 
319  | #endif /* OVLB */
320  | #ifdef OVL0
321  | 
322  | STATIC_DCL struct obj *FDECL(oselect, (struct monst *,int));
323  | #define Oselect(x)	if ((otmp = oselect(mtmp, x)) != 0) return(otmp);
324  | 
325  | STATIC_OVL struct obj *
326  | oselect(mtmp, x)
327  | struct monst *mtmp;
328  | int x;
329  | {
330  | 	struct obj *otmp;
331  | 
332  | 	for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) {
333  | 	    if (otmp->otyp == x &&
334  | 		    /* never select non-cockatrice corpses */
335  | 		    !((x == CORPSE || x == EGG) &&
336  | 			!touch_petrifies(&mons[otmp->corpsenm])) &&
337  | 		    (!otmp->oartifact || touch_artifact(otmp,mtmp)))
338  | 		return otmp;
339  | 	}
340  | 	return (struct obj *)0;
341  | }
342  | 
343  | static NEARDATA const int rwep[] =
344  | {	DWARVISH_SPEAR, SILVER_SPEAR, ELVEN_SPEAR, SPEAR, ORCISH_SPEAR,
345  | 	JAVELIN, SHURIKEN, YA, SILVER_ARROW, ELVEN_ARROW, ARROW,
346  | 	ORCISH_ARROW, CROSSBOW_BOLT, SILVER_DAGGER, ELVEN_DAGGER, DAGGER,
347  | 	ORCISH_DAGGER, KNIFE, FLINT, ROCK, LOADSTONE, LUCKSTONE, DART,
348  | 	/* BOOMERANG, */ CREAM_PIE
349  | 	/* note: CREAM_PIE should NOT be #ifdef KOPS */
350  | };
351  | 
352  | static NEARDATA const int pwep[] =
353  | {	HALBERD, BARDICHE, SPETUM, BILL_GUISARME, VOULGE, RANSEUR, GUISARME,
354  | 	GLAIVE, LUCERN_HAMMER, BEC_DE_CORBIN, FAUCHARD, PARTISAN, LANCE
355  | };
356  | 
357  | static struct obj *propellor;
358  | 
359  | struct obj *
360  | select_rwep(mtmp)	/* select a ranged weapon for the monster */
361  | register struct monst *mtmp;
362  | {
363  | 	register struct obj *otmp;
364  | 	int i;
365  | 
366  | #ifdef KOPS
367  | 	char mlet = mtmp->data->mlet;
368  | #endif
369  | 
370  | 	propellor = &zeroobj;
371  | 	Oselect(EGG); /* cockatrice egg */
372  | #ifdef KOPS
373  | 	if(mlet == S_KOP)	/* pies are first choice for Kops */
374  | 	    Oselect(CREAM_PIE);
375  | #endif
376  | 	if(throws_rocks(mtmp->data))	/* ...boulders for giants */
377  | 	    Oselect(BOULDER);
378  | 
379  | 	/* Select polearms first; they do more damage and aren't expendable */
380  | 	/* The limit of 13 here is based on the monster polearm range limit
381  | 	 * (defined as 5 in mthrowu.c).  5 corresponds to a distance of 2 in
382  | 	 * one direction and 1 in another; one space beyond that would be 3 in
383  | 	 * one direction and 2 in another; 3^2+2^2=13.
384  | 	 */
385  | 	if (dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) <= 13 && couldsee(mtmp->mx, mtmp->my)) {
386  | 	    for (i = 0; i < SIZE(pwep); i++) {
387  | 		/* Only strong monsters can wield big (esp. long) weapons.
388  | 		 * Big weapon is basically the same as bimanual.
389  | 		 * All monsters can wield the remaining weapons.
390  | 		 */
391  | 		if (((strongmonst(mtmp->data) && (mtmp->misc_worn_check & W_ARMS) == 0)
392  | 			|| !objects[pwep[i]].oc_bimanual) &&
393  | 		    (objects[pwep[i]].oc_material != SILVER
394  | 			|| !hates_silver(mtmp->data))) {
395  | 		    if ((otmp = oselect(mtmp, pwep[i])) != 0) {
396  | 			propellor = otmp; /* force the monster to wield it */
397  | 			return otmp;
398  | 		    }
399  | 		}
400  | 	    }
401  | 	}
402  | 
403  | 	/*
404  | 	 * other than these two specific cases, always select the
405  | 	 * most potent ranged weapon to hand.
406  | 	 */
407  | 	for (i = 0; i < SIZE(rwep); i++) {
408  | 	    int prop;
409  | 
410  | 	    /* shooting gems from slings; this goes just before the darts */
411  | 	    /* (shooting rocks is already handled via the rwep[] ordering) */
412  | 	    if (rwep[i] == DART && !likes_gems(mtmp->data) &&
413  | 		    m_carrying(mtmp, SLING)) {		/* propellor */
414  | 		for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
415  | 		    if (otmp->oclass == GEM_CLASS &&
416  | 			    (otmp->otyp != LOADSTONE || !otmp->cursed)) {
417  | 			propellor = m_carrying(mtmp, SLING);
418  | 			return otmp;
419  | 		    }
420  | 	    }
421  | 
422  | 		/* KMH -- This belongs here so darts will work */
423  | 	    propellor = &zeroobj;
424  | 
425  | 	    prop = (objects[rwep[i]]).oc_skill;
426  | 	    if (prop < 0) {
427  | 		switch (-prop) {
428  | 		case P_BOW:
429  | 		  propellor = (oselect(mtmp, YUMI));
430  | 		  if (!propellor) propellor = (oselect(mtmp, ELVEN_BOW));
431  | 		  if (!propellor) propellor = (oselect(mtmp, BOW));
432  | 		  if (!propellor) propellor = (oselect(mtmp, ORCISH_BOW));
433  | 		  break;
434  | 		case P_SLING:
435  | 		  propellor = (oselect(mtmp, SLING));
436  | 		  break;
437  | 		case P_CROSSBOW:
438  | 		  propellor = (oselect(mtmp, CROSSBOW));
439  | 		}
440  | 		if ((otmp = MON_WEP(mtmp)) && otmp->cursed && otmp != propellor
441  | 				&& mtmp->weapon_check == NO_WEAPON_WANTED)
442  | 			propellor = 0;
443  | 	    }
444  | 	    /* propellor = obj, propellor to use
445  | 	     * propellor = &zeroobj, doesn't need a propellor
446  | 	     * propellor = 0, needed one and didn't have one
447  | 	     */
448  | 	    if (propellor != 0) {
449  | 		/* Note: cannot use m_carrying for loadstones, since it will
450  | 		 * always select the first object of a type, and maybe the
451  | 		 * monster is carrying two but only the first is unthrowable.
452  | 		 */
453  | 		if (rwep[i] != LOADSTONE) {
454  | 			/* Don't throw a cursed weapon-in-hand or an artifact */
455  | 			if ((otmp = oselect(mtmp, rwep[i])) && !otmp->oartifact
456  | 			    && (!otmp->cursed || otmp != MON_WEP(mtmp)))
457  | 				return(otmp);
458  | 		} else for(otmp=mtmp->minvent; otmp; otmp=otmp->nobj) {
459  | 		    if (otmp->otyp == LOADSTONE && !otmp->cursed)
460  | 			return otmp;
461  | 		}
462  | 	    }
463  | 	  }
464  | 
465  | 	/* failure */
466  | 	return (struct obj *)0;
467  | }
468  | 
469  | /* Weapons in order of preference */
470  | static NEARDATA short hwep[] = {
471  | 	  CORPSE,  /* cockatrice corpse */
472  | 	  TSURUGI, RUNESWORD, DWARVISH_MATTOCK, TWO_HANDED_SWORD, BATTLE_AXE,
473  | 	  KATANA, UNICORN_HORN, CRYSKNIFE, TRIDENT, LONG_SWORD,
474  | 	  ELVEN_BROADSWORD, BROADSWORD, SCIMITAR, SILVER_SABER,
475  | 	  MORNING_STAR, ELVEN_SHORT_SWORD, DWARVISH_SHORT_SWORD, SHORT_SWORD,
476  | 	  ORCISH_SHORT_SWORD, MACE, AXE, DWARVISH_SPEAR, SILVER_SPEAR,
477  | 	  ELVEN_SPEAR, SPEAR, ORCISH_SPEAR, FLAIL, BULLWHIP, QUARTERSTAFF,
478  | 	  JAVELIN, AKLYS, CLUB, PICK_AXE,
479  | #ifdef KOPS
480  | 	  RUBBER_HOSE,
481  | #endif /* KOPS */
482  | 	  WAR_HAMMER, SILVER_DAGGER, ELVEN_DAGGER, DAGGER, ORCISH_DAGGER,
483  | 	  ATHAME, SCALPEL, KNIFE, WORM_TOOTH
484  | 	};
485  | 
486  | struct obj *
487  | select_hwep(mtmp)	/* select a hand to hand weapon for the monster */
488  | register struct monst *mtmp;
489  | {
490  | 	register struct obj *otmp;
491  | 	register int i;
492  | 	boolean strong = strongmonst(mtmp->data);
493  | 	boolean wearing_shield = (mtmp->misc_worn_check & W_ARMS) != 0;
494  | 
495  | 	/* prefer artifacts to everything else */
496  | 	for(otmp=mtmp->minvent; otmp; otmp = otmp->nobj) {
497  | 		if (otmp->oclass == WEAPON_CLASS
498  | 			&& otmp->oartifact && touch_artifact(otmp,mtmp)
499  | 			&& ((strong && !wearing_shield)
500  | 			    || !objects[otmp->otyp].oc_bimanual))
501  | 		    return otmp;
502  | 	}
503  | 
504  | 	if(is_giant(mtmp->data))	/* giants just love to use clubs */
505  | 	    Oselect(CLUB);
506  | 
507  | 	/* only strong monsters can wield big (esp. long) weapons */
508  | 	/* big weapon is basically the same as bimanual */
509  | 	/* all monsters can wield the remaining weapons */
510  | 	for (i = 0; i < SIZE(hwep); i++)
511  | 	    if (((strong && !wearing_shield)
512  | 			|| !objects[hwep[i]].oc_bimanual) &&
513  | 		    (objects[hwep[i]].oc_material != SILVER
514  | 			|| !hates_silver(mtmp->data)))
515  | 		Oselect(hwep[i]);
516  | 
517  | 	/* failure */
518  | 	return (struct obj *)0;
519  | }
520  | 
521  | /* Called after polymorphing a monster, robbing it, etc....  Monsters
522  |  * otherwise never unwield stuff on their own.  Shouldn't print messages.
523  |  */
524  | void
525  | possibly_unwield(mon)
526  | register struct monst *mon;
527  | {
528  | 	register struct obj *obj;
529  | 	struct obj *mw_tmp;
530  | 
531  | 	if (!(mw_tmp = MON_WEP(mon)))
532  | 		return;
533  | 	for(obj=mon->minvent; obj; obj=obj->nobj)
534  | 		if (obj == mw_tmp) break;
535  | 	if (!obj) { /* The weapon was stolen or destroyed */
536  | 		MON_NOWEP(mon);
537  | 		mon->weapon_check = NEED_WEAPON;
538  | 		return;
539  | 	}
540  | 	if (!attacktype(mon->data, AT_WEAP)) {
541  | 		mw_tmp->owornmask &= ~W_WEP;
542  | 		MON_NOWEP(mon);
543  | 		mon->weapon_check = NO_WEAPON_WANTED;
544  | 		obj_extract_self(obj);
545  | 		/* flooreffects unnecessary, can't wield boulders */
546  | 		place_object(obj, mon->mx, mon->my);
547  | 		stackobj(obj);
548  | 		if (cansee(mon->mx, mon->my)) {
549  | 			pline("%s drops %s.", Monnam(mon),
550  | 				distant_name(obj, doname));
551  | 			newsym(mon->mx, mon->my);
552  | 		}
553  | 		return;
554  | 	}
555  | 	/* The remaining case where there is a change is where a monster
556  | 	 * is polymorphed into a stronger/weaker monster with a different
557  | 	 * choice of weapons.  This has no parallel for players.  It can
558  | 	 * be handled by waiting until mon_wield_item is actually called.
559  | 	 * Though the monster still wields the wrong weapon until then,
560  | 	 * this is OK since the player can't see it.  (FIXME: Not okay since
561  | 	 * probing can reveal it.)
562  | 	 * Note that if there is no change, setting the check to NEED_WEAPON
563  | 	 * is harmless.
564  | 	 * Possible problem: big monster with big cursed weapon gets
565  | 	 * polymorphed into little monster.  But it's not quite clear how to
566  | 	 * handle this anyway....
567  | 	 */
568  | 	mon->weapon_check = NEED_WEAPON;
569  | }
570  | 
571  | /* Let a monster try to wield a weapon, based on mon->weapon_check.
572  |  * Returns 1 if the monster took time to do it, 0 if it did not.
573  |  */
574  | int
575  | mon_wield_item(mon)
576  | register struct monst *mon;
577  | {
578  | 	struct obj *obj;
579  | 
580  | 	/* This case actually should never happen */
581  | 	if (mon->weapon_check == NO_WEAPON_WANTED) return 0;
582  | 	switch(mon->weapon_check) {
583  | 		case NEED_HTH_WEAPON:
584  | 			obj = select_hwep(mon);
585  | 			break;
586  | 		case NEED_RANGED_WEAPON:
587  | 			(void)select_rwep(mon);
588  | 			obj = propellor;
589  | 			break;
590  | 		case NEED_PICK_AXE:
591  | 			obj = m_carrying(mon, PICK_AXE);
592  | 			/* KMH -- allow other picks */
593  | 			if (!obj) obj = m_carrying(mon, DWARVISH_MATTOCK);
594  | 			break;
595  | 		default: impossible("weapon_check %d for %s?",
596  | 				mon->weapon_check, mon_nam(mon));
597  | 			return 0;
598  | 	}
599  | 	if (obj && obj != &zeroobj) {
600  | 		struct obj *mw_tmp = MON_WEP(mon);
601  | 		if (mw_tmp && mw_tmp->otyp == obj->otyp) {
602  | 		/* already wielding it */
603  | 			mon->weapon_check = NEED_WEAPON;
604  | 			return 0;
605  | 		}
606  | 		/* Actually, this isn't necessary--as soon as the monster
607  | 		 * wields the weapon, the weapon welds itself, so the monster
608  | 		 * can know it's cursed and needn't even bother trying.
609  | 		 * Still....
610  | 		 */
611  | 		if (mw_tmp && mw_tmp->cursed && mw_tmp->otyp != CORPSE) {
612  | 		    if (canseemon(mon)) {
613  | 			char welded_buf[BUFSZ];
614  | 			const char *mon_hand = mbodypart(mon, HAND);
615  | 
616  | 			if (bimanual(mw_tmp)) mon_hand = makeplural(mon_hand);
617  | 			Sprintf(welded_buf, "%s welded to %s %s",
618  | 				(mw_tmp->quan == 1L) ? "is" : "are",
619  | 				his[pronoun_gender(mon)], mon_hand);
620  | 
621  | 			if (obj->otyp == PICK_AXE) {
622  | 			    pline("Since %s weapon%s %s,",
623  | 				  s_suffix(mon_nam(mon)),
624  | 				  plur(mw_tmp->quan), welded_buf);
625  | 			    pline("%s cannot wield that %s.",
626  | 				mon_nam(mon), xname(obj));
627  | 			} else {
628  | 			    pline("%s tries to wield %s.", Monnam(mon),
629  | 				doname(obj));
630  | 			    pline("%s %s %s!",
631  | 				  s_suffix(Monnam(mon)),
632  | 				  xname(mw_tmp), welded_buf);
633  | 			}
634  | 			mw_tmp->bknown = 1;
635  | 		    }
636  | 		    mon->weapon_check = NO_WEAPON_WANTED;
637  | 		    return 1;
638  | 		}
639  | 		mon->mw = obj;		/* wield obj */
640  | 		if (mw_tmp) mw_tmp->owornmask &= ~W_WEP;
641  | 		mon->weapon_check = NEED_WEAPON;
642  | 		if (canseemon(mon)) {
643  | 			pline("%s wields %s!", Monnam(mon), doname(obj));
644  | 			if (obj->cursed && obj->otyp != CORPSE) {
645  | 				pline("%s %s to %s %s!",
646  | 					The(xname(obj)),
647  | 					(obj->quan == 1L) ? "welds itself"
648  | 					    : "weld themselves",
649  | 					s_suffix(mon_nam(mon)), mbodypart(mon,HAND));
650  | 				obj->bknown = 1;
651  | 			}
652  | 		}
653  | 		obj->owornmask = W_WEP;
654  | 		return 1;
655  | 	}
656  | 	mon->weapon_check = NEED_WEAPON;
657  | 	return 0;
658  | }
659  | 
660  | int
661  | abon()		/* attack bonus for strength & dexterity */
662  | {
663  | 	int sbon;
664  | 	int str = ACURR(A_STR), dex = ACURR(A_DEX);
665  | 
666  | 	if (Upolyd) return(adj_lev(&mons[u.umonnum]) - 3);
667  | 	if (str < 6) sbon = -2;
668  | 	else if (str < 8) sbon = -1;
669  | 	else if (str < 17) sbon = 0;
670  | 	else if (str <= STR18(50)) sbon = 1;	/* up to 18/50 */
671  | 	else if (str < STR18(100)) sbon = 2;
672  | 	else sbon = 3;
673  | 
674  | /* Game tuning kludge: make it a bit easier for a low level character to hit */
675  | 	sbon += (u.ulevel < 3) ? 1 : 0;
676  | 
677  | 	if (dex < 4) return(sbon-3);
678  | 	else if (dex < 6) return(sbon-2);
679  | 	else if (dex < 8) return(sbon-1);
680  | 	else if (dex < 14) return(sbon);
681  | 	else return(sbon + dex-14);
682  | }
683  | 
684  | #endif /* OVL0 */
685  | #ifdef OVL1
686  | 
687  | int
688  | dbon()		/* damage bonus for strength */
689  | {
690  | 	int str = ACURR(A_STR);
691  | 
692  | 	if (Upolyd) return(0);
693  | 
694  | 	if (str < 6) return(-1);
695  | 	else if (str < 16) return(0);
696  | 	else if (str < 18) return(1);
697  | 	else if (str == 18) return(2);		/* up to 18 */
698  | 	else if (str <= STR18(75)) return(3);		/* up to 18/75 */
699  | 	else if (str <= STR18(90)) return(4);		/* up to 18/90 */
700  | 	else if (str < STR18(100)) return(5);		/* up to 18/99 */
701  | 	else return(6);
702  | }
703  | 
704  | 
705  | /* copy the skill level name into the given buffer */
706  | STATIC_OVL char *
707  | skill_level_name(skill, buf)
708  | int skill;
709  | char *buf;
710  | {
711  |     const char *ptr;
712  | 
713  |     switch (P_SKILL(skill)) {
714  | 	case P_UNSKILLED:    ptr = "Unskilled"; break;
715  | 	case P_BASIC:	     ptr = "Basic";     break;
716  | 	case P_SKILLED:	     ptr = "Skilled";   break;
717  | 	case P_EXPERT:	     ptr = "Expert";    break;
718  | 	/* these are for unarmed combat/martial arts only */
719  | 	case P_MASTER:	     ptr = "Master";    break;
720  | 	case P_GRAND_MASTER: ptr = "Grand Master"; break;
721  | 	default:	     ptr = "Unknown";	break;
722  |     }
723  |     Strcpy(buf, ptr);
724  |     return buf;
725  | }
726  | 
727  | /* return the # of slots required to advance the skill */
728  | STATIC_OVL int
729  | slots_required(skill)
730  | int skill;
731  | {
732  |     int tmp = P_SKILL(skill);
733  | 
734  |     /* The more difficult the training, the more slots it takes.
735  |      *	unskilled -> basic	1
736  |      *	basic -> skilled	2
737  |      *	skilled -> expert	3
738  |      */
739  |     if (skill <= P_LAST_WEAPON || skill == P_TWO_WEAPON_COMBAT)
740  | 	return tmp;
741  | 
742  |     /* Fewer slots used up for unarmed or martial.
743  |      *	unskilled -> basic	1
744  |      *	basic -> skilled	1
745  |      *	skilled -> expert	2
746  |      *	expert -> master	2
747  |      *	master -> grand master	3
748  |      */
749  |     return (tmp + 1) / 2;
750  | }
751  | 
752  | /* return true if this skill can be advanced */
753  | STATIC_OVL boolean
754  | can_advance(skill, speedy)
755  | int skill;
756  | boolean speedy;
757  | {
758  |     return !P_RESTRICTED(skill)
759  | 	    && P_SKILL(skill) < P_MAX_SKILL(skill) && (
760  | #ifdef WIZARD
761  | 	    (wizard && speedy) ||
762  | #endif
763  | 	    (P_ADVANCE(skill) >=
764  | 		(unsigned) practice_needed_to_advance(P_SKILL(skill))
765  | 	    && u.skills_advanced < P_SKILL_LIMIT
766  | 	    && u.weapon_slots >= slots_required(skill)));
767  | }
768  | 
769  | STATIC_OVL void
770  | skill_advance(skill)
771  | int skill;
772  | {
773  |     u.weapon_slots -= slots_required(skill);
774  |     P_SKILL(skill)++;
775  |     u.skill_record[u.skills_advanced++] = skill;
776  |     /* subtly change the advance message to indicate no more advancement */
777  |     You("are now %s skilled in %s.",
778  |     	P_SKILL(skill) >= P_MAX_SKILL(skill) ? "most" : "more",
779  |     	P_NAME(skill));
780  | }
781  | 
782  | static struct skill_range {
783  | 	short first, last;
784  | 	const char *name;
785  | } skill_ranges[] = {
786  |     { P_FIRST_H_TO_H, P_LAST_H_TO_H, "Fighting Skills" },
787  |     { P_FIRST_WEAPON, P_LAST_WEAPON, "Weapon Skills" },
788  |     { P_FIRST_SPELL,  P_LAST_SPELL,  "Spellcasting Skills" },
789  | };
790  | 
791  | /*
792  |  * The `#enhance' extended command.  What we _really_ would like is
793  |  * to keep being able to pick things to advance until we couldn't any
794  |  * more.  This is currently not possible -- the menu code has no way
795  |  * to call us back for instant action.  Even if it did, we would also need
796  |  * to be able to update the menu since selecting one item could make
797  |  * others unselectable.
798  |  */
799  | int
800  | enhance_weapon_skill()
801  | {
802  |     int pass, i, n, len, longest, to_advance;
803  |     char buf[BUFSZ], buf2[BUFSZ];
804  |     menu_item *selected;
805  |     anything any;
806  |     winid win;
807  |     boolean speedy = FALSE;
808  | 
809  | 
810  | #ifdef WIZARD
811  | 	if (wizard && yn("Advance skills without practice?") == 'y')
812  | 	    speedy = TRUE;
813  | #endif
814  | 
815  | 	do {
816  | 	    /* find longest available skill name, count those that can advance */
817  | 	    for (longest = 0, to_advance = 0, i = 0; i < P_NUM_SKILLS; i++) {
818  | 		if (!P_RESTRICTED(i) && (len = strlen(P_NAME(i))) > longest)
819  | 		    longest = len;
820  | 		if (can_advance(i, speedy)) to_advance++;
821  | 	    }
822  | 
823  | 	    win = create_nhwindow(NHW_MENU);
824  | 	    start_menu(win);
825  | 
826  | 	    /* List the skills, making ones that could be advanced
827  | 	       selectable.  List the miscellaneous skills first.
828  | 	       Possible future enhancement:  list spell skills before
829  | 	       weapon skills for spellcaster roles. */
830  | 	  for (pass = 0; pass < SIZE(skill_ranges); pass++)
831  | 	    for (i = skill_ranges[pass].first;
832  | 		 i <= skill_ranges[pass].last; i++) {
833  | 		/* Print headings for skill types */
834  | 		any.a_void = 0;
835  | 		if (i == skill_ranges[pass].first)
836  | 		    add_menu(win, NO_GLYPH, &any, 0, 0, ATR_BOLD,
837  | 			     skill_ranges[pass].name, MENU_UNSELECTED);
838  | 
839  | 		if (P_RESTRICTED(i)) continue;
840  | 		/*
841  | 		 * Sigh, this assumes a monospaced font.
842  | 		 * The 12 is the longest skill level name.
843  | 		 * The "    " is room for a selection letter and dash, "a - ".
844  | 		 */
845  | #ifdef WIZARD
846  | 		if (wizard)
847  | 		    Sprintf(buf2, " %s%-*s %-12s %4d(%4d)",
848  | 			    to_advance == 0 || can_advance(i, speedy) ? "" : "    " ,
849  | 			    longest, P_NAME(i),
850  | 			    skill_level_name(i, buf),
851  | 			    P_ADVANCE(i), practice_needed_to_advance(P_SKILL(i)));
852  | 		else
853  | #endif
854  | 		    Sprintf(buf2, " %s %-*s [%s]",
855  | 			    to_advance == 0 || can_advance(i, speedy) ? "" : "    ",
856  | 			    longest, P_NAME(i),
857  | 			    skill_level_name(i, buf));
858  | 
859  | 		any.a_int = can_advance(i, speedy) ? i+1 : 0;
860  | 		add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf2, MENU_UNSELECTED);
861  | 	    }
862  | 
863  | 	    Strcpy(buf, to_advance ? "Pick a skill to advance:" : "Current skills:");
864  | #ifdef WIZARD
865  | 	    if (wizard && !speedy) Sprintf(eos(buf), "  (%d slot%s available)",
866  | 				u.weapon_slots, plur(u.weapon_slots));
867  | #endif
868  | 	    end_menu(win, buf);
869  | 	    n = select_menu(win, to_advance ? PICK_ONE : PICK_NONE, &selected);
870  | 	    destroy_nhwindow(win);
871  | 	    if (n > 0) {
872  | 		n = selected[0].item.a_int - 1;	/* get item selected */
873  | 		free((genericptr_t)selected);
874  | 		skill_advance(n);
875  | 		/* check for more skills able to advance, if so then .. */
876  | 		for (n = i = 0; i < P_NUM_SKILLS; i++) {
877  | 		    if (can_advance(i, speedy)) {
878  | 			if (!speedy) You_feel("you could be more dangerous!");
879  | 			n++;
880  | 			break;
881  | 		    }
882  | 		}
883  | 	    }
884  | 	} while (speedy && n > 0);
885  | 	return 0;
886  | }
887  | 
888  | /*
889  |  * Change from restricted to unrestricted, allowing P_BASIC as max.  This
890  |  * function may be called with with P_NONE.  Used in pray.c.
891  |  */
892  | void
893  | unrestrict_weapon_skill(skill)
894  | int skill;
895  | {
896  |     if (skill < P_NUM_SKILLS && P_RESTRICTED(skill)) {
897  | 	P_SKILL(skill) = P_UNSKILLED;
898  | 	P_MAX_SKILL(skill) = P_BASIC;
899  | 	P_ADVANCE(skill) = 0;
900  |     }
901  | }
902  | 
903  | #endif /* OVL1 */
904  | #ifdef OVLB
905  | 
906  | void
907  | use_skill(skill,degree)
908  | int skill;
909  | int degree;
910  | {
911  |     boolean advance_before;
912  | 
913  |     if (skill != P_NONE && !P_RESTRICTED(skill)) {
914  | 	advance_before = can_advance(skill, FALSE);
915  | 	P_ADVANCE(skill)+=degree;
916  | 	if (!advance_before && can_advance(skill, FALSE))
917  | 	    give_may_advance_msg(skill);
918  |     }
919  | }
920  | 
921  | void
922  | add_weapon_skill(n)
923  | int n;	/* number of slots to gain; normally one */
924  | {
925  |     int i, before, after;
926  | 
927  |     for (i = 0, before = 0; i < P_NUM_SKILLS; i++)
928  | 	if (can_advance(i, FALSE)) before++;
929  |     u.weapon_slots += n;
930  |     for (i = 0, after = 0; i < P_NUM_SKILLS; i++)
931  | 	if (can_advance(i, FALSE)) after++;
932  |     if (before < after)
933  | 	give_may_advance_msg(P_NONE);
934  | }
935  | 
936  | void
937  | lose_weapon_skill(n)
938  | int n;	/* number of slots to lose; normally one */
939  | {
940  |     int skill;
941  | 
942  |     while (--n >= 0) {
943  | 	/* deduct first from unused slots, then from last placed slot, if any */
944  | 	if (u.weapon_slots) {
945  | 	    u.weapon_slots--;
946  | 	} else if (u.skills_advanced) {
947  | 	    skill = u.skill_record[--u.skills_advanced];
948  | 	    if (P_SKILL(skill) <= P_UNSKILLED)
949  | 		panic("lose_weapon_skill (%d)", skill);
950  | 	    P_SKILL(skill)--;	/* drop skill one level */
951  | 	    /* Lost skill might have taken more than one slot; refund rest. */
952  | 	    u.weapon_slots = slots_required(skill) - 1;
953  | 	    /* It might now be possible to advance some other pending
954  | 	       skill by using the refunded slots, but giving a message
955  | 	       to that effect would seem pretty confusing.... */
956  | 	}
957  |     }
958  | }
959  | 
960  | int
961  | weapon_type(obj)
962  | struct obj *obj;
963  | {
964  | 	/* KMH -- now uses the object table */
965  | 	int type;
966  | 
967  | 	if (!obj)
968  | 		/* Not using a weapon */
969  | 		return (P_BARE_HANDED_COMBAT);
970  | 	if (obj->oclass != WEAPON_CLASS && obj->oclass != TOOL_CLASS &&
971  | 	    obj->oclass != GEM_CLASS)
972  | 		/* Not a weapon, weapon-tool, or ammo */
973  | 		return (P_NONE);
974  | 	type = objects[obj->otyp].oc_skill;
975  | 	return ((type < 0) ? -type : type);
976  | }
977  | 
978  | int
979  | uwep_skill_type()
980  | {
981  | 	if (u.twoweap)
982  | 		return P_TWO_WEAPON_COMBAT;
983  | 	return weapon_type(uwep);
984  | }
985  | 
986  | /*
987  |  * Return hit bonus/penalty based on skill of weapon.
988  |  * Treat restricted weapons as unskilled.
989  |  */
990  | int
991  | weapon_hit_bonus(weapon)
992  | struct obj *weapon;
993  | {
994  |     int type, skill, bonus = 0;
995  |     static const char bad_skill[] = "weapon_hit_bonus: bad skill %d";
996  | 
997  |     type = u.twoweap ? P_TWO_WEAPON_COMBAT : weapon_type(weapon);
998  |     if (type == P_NONE) {
999  | 	bonus = 0;
1000 |     } else if (type <= P_LAST_WEAPON) {
1001 | 	switch (P_SKILL(type)) {
1002 | 	    default: impossible(bad_skill, P_SKILL(type)); /* fall through */
1003 | 	    case P_ISRESTRICTED:
1004 | 	    case P_UNSKILLED:   bonus = -4; break;
1005 | 	    case P_BASIC:       bonus =  0; break;
1006 | 	    case P_SKILLED:     bonus =  2; break;
1007 | 	    case P_EXPERT:      bonus =  3; break;
1008 | 	}
1009 |     } else if (type == P_TWO_WEAPON_COMBAT) {
1010 | 	skill = P_SKILL(P_TWO_WEAPON_COMBAT);
1011 | 	if (P_SKILL(weapon->otyp) < skill) skill = P_SKILL(weapon->otyp);
1012 | 	switch (skill) {
1013 | 	    default: impossible(bad_skill, P_SKILL(type)); /* fall through */
1014 | 	    case P_ISRESTRICTED:
1015 | 	    case P_UNSKILLED:   bonus = -9; break;
1016 | 	    case P_BASIC:	bonus = -7; break;
1017 | 	    case P_SKILLED:	bonus = -5; break;
1018 | 	    case P_EXPERT:	bonus = -3; break;
1019 | 	}
1020 |     } else if (type == P_BARE_HANDED_COMBAT) {
1021 | 	/* restricted == 0 */
1022 | 	bonus = ((P_SKILL(type) + 1) * (martial_bonus() ? 2 : 1)) / 2;
1023 |     }
1024 | 
1025 | #ifdef STEED
1026 | 	/* KMH -- It's harder to hit while you are riding */
1027 | 	if (u.usteed) {
1028 | 		switch (P_SKILL(P_RIDING)) {
1029 | 		    case P_ISRESTRICTED:
1030 | 		    case P_UNSKILLED:   bonus -= 2; break;
1031 | 		    case P_BASIC:       bonus -= 1; break;
1032 | 		    case P_SKILLED:     break;
1033 | 		    case P_EXPERT:      break;
1034 | 		}
1035 | 		if (u.twoweap) bonus -= 2;
1036 | 	}
1037 | #endif
1038 | 
1039 |     return bonus;
1040 | }
1041 | 
1042 | /*
1043 |  * Return damage bonus/penalty based on skill of weapon.
1044 |  * Treat restricted weapons as unskilled.
1045 |  */
1046 | int
1047 | weapon_dam_bonus(weapon)
1048 | struct obj *weapon;
1049 | {
1050 |     int type, skill, bonus = 0;
1051 | 
1052 |     type = u.twoweap ? P_TWO_WEAPON_COMBAT : weapon_type(weapon);
1053 |     if (type == P_NONE) {
1054 | 	bonus = 0;
1055 |     } else if (type <= P_LAST_WEAPON) {
1056 | 	switch (P_SKILL(type)) {
1057 | 	    default: impossible("weapon_dam_bonus: bad skill %d",P_SKILL(type));
1058 | 		     /* fall through */
1059 | 	    case P_ISRESTRICTED:
1060 | 	    case P_UNSKILLED:	bonus = -2; break;
1061 | 	    case P_BASIC:	bonus =  0; break;
1062 | 	    case P_SKILLED:	bonus =  1; break;
1063 | 	    case P_EXPERT:	bonus =  2; break;
1064 | 	}
1065 |     } else if (type == P_TWO_WEAPON_COMBAT) {
1066 | 	skill = P_SKILL(P_TWO_WEAPON_COMBAT);
1067 | 	if (P_SKILL(weapon->otyp) < skill) skill = P_SKILL(weapon->otyp);
1068 | 	switch (skill) {
1069 | 	    default:
1070 | 	    case P_ISRESTRICTED:
1071 | 	    case P_UNSKILLED:	bonus = -3; break;
1072 | 	    case P_BASIC:	bonus = -1; break;
1073 | 	    case P_SKILLED:	bonus = 0; break;
1074 | 	    case P_EXPERT:	bonus = 1; break;
1075 | 	}
1076 |     } else if (type == P_BARE_HANDED_COMBAT) {
1077 | 	bonus = (P_SKILL(type) * (martial_bonus() ? 3 : 1)) / 2;
1078 |     }
1079 | 
1080 | #ifdef STEED
1081 | 	/* KMH -- Riding gives some thrusting damage */
1082 | 	if (u.usteed && type != P_TWO_WEAPON_COMBAT) {
1083 | 		switch (P_SKILL(P_RIDING)) {
1084 | 		    case P_ISRESTRICTED:
1085 | 		    case P_UNSKILLED:   break;
1086 | 		    case P_BASIC:       break;
1087 | 		    case P_SKILLED:     bonus += 1; break;
1088 | 		    case P_EXPERT:      bonus += 2; break;
1089 | 		}
1090 | 	}
1091 | #endif
1092 | 
1093 |     return bonus;
1094 | }
1095 | 
1096 | /*
1097 |  * Initialize weapon skill array for the game.  Start by setting all
1098 |  * skills to restricted, then set the skill for every weapon the
1099 |  * hero is holding, finally reading the given array that sets
1100 |  * maximums.
1101 |  */
1102 | void
1103 | skill_init(class_skill)
1104 | struct def_skill *class_skill;
1105 | {
1106 | 	struct obj *obj;
1107 | 	int skmax, skill;
1108 | 
1109 | 	/* initialize skill array; by default, everything is restricted */
1110 | 	for (skill = 0; skill < P_NUM_SKILLS; skill++) {
1111 | 	    P_SKILL(skill) = P_ISRESTRICTED;
1112 | 	    P_MAX_SKILL(skill) = P_ISRESTRICTED;
1113 | 	    P_ADVANCE(skill) = 0;
1114 | 	}
1115 | 
1116 | 	/* Set skill for all weapons in inventory to be basic */
1117 | 	for (obj = invent; obj; obj = obj->nobj) {
1118 | 	    skill = weapon_type(obj);
1119 | 	    if (skill != P_NONE)
1120 | 		P_SKILL(skill) = P_BASIC;
1121 | 	}
1122 | 
1123 | 	/* set skills for magic */
1124 | 	if (Role_if(PM_HEALER) || Role_if(PM_MONK)) {
1125 | 		P_SKILL(P_HEALING_SPELL) = P_BASIC;
1126 | 	} else if (Role_if(PM_PRIEST)) {
1127 | 		P_SKILL(P_CLERIC_SPELL) = P_BASIC;
1128 | 	} else if (Role_if(PM_WIZARD)) {
1129 | 		P_SKILL(P_ATTACK_SPELL) = P_BASIC;
1130 | 		P_SKILL(P_ENCHANTMENT_SPELL) = P_BASIC;
1131 | 	}
1132 | 
1133 | 	/* walk through array to set skill maximums */
1134 | 	for (; class_skill->skill != P_NONE; class_skill++) {
1135 | 	    skmax = class_skill->skmax;
1136 | 	    skill = class_skill->skill;
1137 | 
1138 | 	    P_MAX_SKILL(skill) = skmax;
1139 | 	    if (P_SKILL(skill) == P_ISRESTRICTED)	/* skill pre-set */
1140 | 		P_SKILL(skill) = P_UNSKILLED;
1141 | 	}
1142 | 
1143 | 	/* High potential fighters already know how to use their hands. */
1144 | 	if (P_MAX_SKILL(P_BARE_HANDED_COMBAT) > P_EXPERT)
1145 | 	    P_SKILL(P_BARE_HANDED_COMBAT) = P_BASIC;
1146 | 
1147 | 	/* Roles that start with a horse know how to ride it */
1148 | #ifdef STEED
1149 | 	if (urole.petnum == PM_PONY)
1150 | 	    P_SKILL(P_RIDING) = P_BASIC;
1151 | #endif
1152 | 
1153 | 	/*
1154 | 	 * Make sure we haven't missed setting the max on a skill
1155 | 	 * & set advance
1156 | 	 */
1157 | 	for (skill = 0; skill < P_NUM_SKILLS; skill++) {
1158 | 	    if (!P_RESTRICTED(skill)) {
1159 | 		if (P_MAX_SKILL(skill) < P_SKILL(skill)) {
1160 | 		    impossible("skill_init: curr > max: %s", P_NAME(skill));
1161 | 		    P_MAX_SKILL(skill) = P_SKILL(skill);
1162 | 		}
1163 | 		P_ADVANCE(skill) = practice_needed_to_advance(P_SKILL(skill)-1);
1164 | 	    }
1165 | 	}
1166 | }
1167 | 
1168 | #endif /* OVLB */
1169 | 
1170 | /*weapon.c*/