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