1 /* NetHack 3.6	attrib.c	$NHDT-Date: 1575245050 2019/12/02 00:04:10 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.66 $ */
2 /*      Copyright 1988, 1989, 1990, 1992, M. Stephenson           */
3 /* NetHack may be freely redistributed.  See license for details. */
4 
5 /*  attribute modification routines. */
6 
7 #include "hack.h"
8 #include <ctype.h>
9 
10 /* part of the output on gain or loss of attribute */
11 static const char
12     *const plusattr[] = { "strong", "smart", "wise",
13                           "agile",  "tough", "charismatic" },
14     *const minusattr[] = { "weak",    "stupid",
15                            "foolish", "clumsy",
16                            "fragile", "repulsive" };
17 /* also used by enlightenment for non-abbreviated status info */
18 const char
19     *const attrname[] = { "strength", "intelligence", "wisdom",
20                           "dexterity", "constitution", "charisma" };
21 
22 static const struct innate {
23     schar ulevel;
24     long *ability;
25     const char *gainstr, *losestr;
26 } arc_abil[] = { { 1, &(HStealth), "", "" },
27                  { 1, &(HFast), "", "" },
28                  { 10, &(HSearching), "perceptive", "" },
29                  { 0, 0, 0, 0 } },
30 
31   bar_abil[] = { { 1, &(HPoison_resistance), "", "" },
32                  { 7, &(HFast), "quick", "slow" },
33                  { 15, &(HStealth), "stealthy", "" },
34                  { 0, 0, 0, 0 } },
35 
36   cav_abil[] = { { 7, &(HFast), "quick", "slow" },
37                  { 15, &(HWarning), "sensitive", "" },
38                  { 0, 0, 0, 0 } },
39 
40   hea_abil[] = { { 1, &(HPoison_resistance), "", "" },
41                  { 15, &(HWarning), "sensitive", "" },
42                  { 0, 0, 0, 0 } },
43 
44   kni_abil[] = { { 7, &(HFast), "quick", "slow" }, { 0, 0, 0, 0 } },
45 
46   mon_abil[] = { { 1, &(HFast), "", "" },
47                  { 1, &(HSleep_resistance), "", "" },
48                  { 1, &(HSee_invisible), "", "" },
49                  { 3, &(HPoison_resistance), "healthy", "" },
50                  { 5, &(HStealth), "stealthy", "" },
51                  { 7, &(HWarning), "sensitive", "" },
52                  { 9, &(HSearching), "perceptive", "unaware" },
53                  { 11, &(HFire_resistance), "cool", "warmer" },
54                  { 13, &(HCold_resistance), "warm", "cooler" },
55                  { 15, &(HShock_resistance), "insulated", "conductive" },
56                  { 17, &(HTeleport_control), "controlled", "uncontrolled" },
57                  { 0, 0, 0, 0 } },
58 
59   pri_abil[] = { { 15, &(HWarning), "sensitive", "" },
60                  { 20, &(HFire_resistance), "cool", "warmer" },
61                  { 0, 0, 0, 0 } },
62 
63   ran_abil[] = { { 1, &(HSearching), "", "" },
64                  { 7, &(HStealth), "stealthy", "" },
65                  { 15, &(HSee_invisible), "", "" },
66                  { 0, 0, 0, 0 } },
67 
68   rog_abil[] = { { 1, &(HStealth), "", "" },
69                  { 10, &(HSearching), "perceptive", "" },
70                  { 0, 0, 0, 0 } },
71 
72   sam_abil[] = { { 1, &(HFast), "", "" },
73                  { 15, &(HStealth), "stealthy", "" },
74                  { 0, 0, 0, 0 } },
75 
76   tou_abil[] = { { 10, &(HSearching), "perceptive", "" },
77                  { 20, &(HPoison_resistance), "hardy", "" },
78                  { 0, 0, 0, 0 } },
79 
80   val_abil[] = { { 1, &(HCold_resistance), "", "" },
81                  { 1, &(HStealth), "", "" },
82                  { 7, &(HFast), "quick", "slow" },
83                  { 0, 0, 0, 0 } },
84 
85   wiz_abil[] = { { 15, &(HWarning), "sensitive", "" },
86                  { 17, &(HTeleport_control), "controlled", "uncontrolled" },
87                  { 0, 0, 0, 0 } },
88 
89   /* Intrinsics conferred by race */
90   dwa_abil[] = { { 1, &HInfravision, "", "" },
91                  { 0, 0, 0, 0 } },
92 
93   elf_abil[] = { { 1, &HInfravision, "", "" },
94                  { 4, &HSleep_resistance, "awake", "tired" },
95                  { 0, 0, 0, 0 } },
96 
97   gno_abil[] = { { 1, &HInfravision, "", "" },
98                  { 0, 0, 0, 0 } },
99 
100   orc_abil[] = { { 1, &HInfravision, "", "" },
101                  { 1, &HPoison_resistance, "", "" },
102                  { 0, 0, 0, 0 } },
103 
104   hum_abil[] = { { 0, 0, 0, 0 } };
105 
106 STATIC_DCL void NDECL(exerper);
107 STATIC_DCL void FDECL(postadjabil, (long *));
108 STATIC_DCL const struct innate *FDECL(role_abil, (int));
109 STATIC_DCL const struct innate *FDECL(check_innate_abil, (long *, long));
110 STATIC_DCL int FDECL(innately, (long *));
111 
112 /* adjust an attribute; return TRUE if change is made, FALSE otherwise */
113 boolean
adjattrib(ndx,incr,msgflg)114 adjattrib(ndx, incr, msgflg)
115 int ndx, incr;
116 int msgflg; /* positive => no message, zero => message, and */
117 {           /* negative => conditional (msg if change made) */
118     int old_acurr, old_abase, old_amax, decr;
119     boolean abonflg;
120     const char *attrstr;
121 
122     if (Fixed_abil || !incr)
123         return FALSE;
124 
125     if ((ndx == A_INT || ndx == A_WIS) && uarmh && uarmh->otyp == DUNCE_CAP) {
126         if (msgflg == 0)
127             Your("cap constricts briefly, then relaxes again.");
128         return FALSE;
129     }
130 
131     old_acurr = ACURR(ndx);
132     old_abase = ABASE(ndx);
133     old_amax = AMAX(ndx);
134     ABASE(ndx) += incr; /* when incr is negative, this reduces ABASE() */
135     if (incr > 0) {
136         if (ABASE(ndx) > AMAX(ndx)) {
137             AMAX(ndx) = ABASE(ndx);
138             if (AMAX(ndx) > ATTRMAX(ndx))
139                 ABASE(ndx) = AMAX(ndx) = ATTRMAX(ndx);
140         }
141         attrstr = plusattr[ndx];
142         abonflg = (ABON(ndx) < 0);
143     } else { /* incr is negative */
144         if (ABASE(ndx) < ATTRMIN(ndx)) {
145             /*
146              * If base value has dropped so low that it is trying to be
147              * taken below the minimum, reduce max value (peak reached)
148              * instead.  That means that restore ability and repeated
149              * applications of unicorn horn will not be able to recover
150              * all the lost value.  As of 3.6.2, we only take away
151              * some (average half, possibly zero) of the excess from max
152              * instead of all of it, but without intervening recovery, it
153              * can still eventually drop to the minimum allowed.  After
154              * that, it can't be recovered, only improved with new gains.
155              *
156              * This used to assign a new negative value to incr and then
157              * add it, but that could affect messages below, possibly
158              * making a large decrease be described as a small one.
159              *
160              * decr = rn2(-(ABASE - ATTRMIN) + 1);
161              */
162             decr = rn2(ATTRMIN(ndx) - ABASE(ndx) + 1);
163             ABASE(ndx) = ATTRMIN(ndx);
164             AMAX(ndx) -= decr;
165             if (AMAX(ndx) < ATTRMIN(ndx))
166                 AMAX(ndx) = ATTRMIN(ndx);
167         }
168         attrstr = minusattr[ndx];
169         abonflg = (ABON(ndx) > 0);
170     }
171     if (ACURR(ndx) == old_acurr) {
172         if (msgflg == 0 && flags.verbose) {
173             if (ABASE(ndx) == old_abase && AMAX(ndx) == old_amax) {
174                 pline("You're %s as %s as you can get.",
175                       abonflg ? "currently" : "already", attrstr);
176             } else {
177                 /* current stayed the same but base value changed, or
178                    base is at minimum and reduction caused max to drop */
179                 Your("innate %s has %s.", attrname[ndx],
180                      (incr > 0) ? "improved" : "declined");
181             }
182         }
183         return FALSE;
184     }
185 
186     if (msgflg <= 0)
187         You_feel("%s%s!", (incr > 1 || incr < -1) ? "very " : "", attrstr);
188     context.botl = TRUE;
189     if (program_state.in_moveloop && (ndx == A_STR || ndx == A_CON))
190         (void) encumber_msg();
191     return TRUE;
192 }
193 
194 void
gainstr(otmp,incr,givemsg)195 gainstr(otmp, incr, givemsg)
196 struct obj *otmp;
197 int incr;
198 boolean givemsg;
199 {
200     int num = incr;
201 
202     if (!num) {
203         if (ABASE(A_STR) < 18)
204             num = (rn2(4) ? 1 : rnd(6));
205         else if (ABASE(A_STR) < STR18(85))
206             num = rnd(10);
207         else
208             num = 1;
209     }
210     (void) adjattrib(A_STR, (otmp && otmp->cursed) ? -num : num,
211                      givemsg ? -1 : 1);
212 }
213 
214 /* may kill you; cause may be poison or monster like 'a' */
215 void
losestr(num)216 losestr(num)
217 register int num;
218 {
219     int ustr = ABASE(A_STR) - num;
220 
221     while (ustr < 3) {
222         ++ustr;
223         --num;
224         if (Upolyd) {
225             u.mh -= 6;
226             u.mhmax -= 6;
227         } else {
228             u.uhp -= 6;
229             u.uhpmax -= 6;
230         }
231     }
232     (void) adjattrib(A_STR, -num, 1);
233 }
234 
235 static const struct poison_effect_message {
236     void VDECL((*delivery_func), (const char *, ...));
237     const char *effect_msg;
238 } poiseff[] = {
239     { You_feel, "weaker" },             /* A_STR */
240     { Your, "brain is on fire" },       /* A_INT */
241     { Your, "judgement is impaired" },  /* A_WIS */
242     { Your, "muscles won't obey you" }, /* A_DEX */
243     { You_feel, "very sick" },          /* A_CON */
244     { You, "break out in hives" }       /* A_CHA */
245 };
246 
247 /* feedback for attribute loss due to poisoning */
248 void
poisontell(typ,exclaim)249 poisontell(typ, exclaim)
250 int typ;         /* which attribute */
251 boolean exclaim; /* emphasis */
252 {
253     void VDECL((*func), (const char *, ...)) = poiseff[typ].delivery_func;
254     const char *msg_txt = poiseff[typ].effect_msg;
255 
256     /*
257      * "You feel weaker" or "you feel very sick" aren't appropriate when
258      * wearing or wielding something (gauntlets of power, Ogresmasher)
259      * which forces the attribute to maintain its maximum value.
260      * Phrasing for other attributes which might have fixed values
261      * (dunce cap) is such that we don't need message fixups for them.
262      */
263     if (typ == A_STR && ACURR(A_STR) == STR19(25))
264         msg_txt = "innately weaker";
265     else if (typ == A_CON && ACURR(A_CON) == 25)
266         msg_txt = "sick inside";
267 
268     (*func)("%s%c", msg_txt, exclaim ? '!' : '.');
269 }
270 
271 /* called when an attack or trap has poisoned hero (used to be in mon.c) */
272 void
poisoned(reason,typ,pkiller,fatal,thrown_weapon)273 poisoned(reason, typ, pkiller, fatal, thrown_weapon)
274 const char *reason,    /* controls what messages we display */
275            *pkiller;   /* for score+log file if fatal */
276 int typ, fatal;        /* if fatal is 0, limit damage to adjattrib */
277 boolean thrown_weapon; /* thrown weapons are less deadly */
278 {
279     int i, loss, kprefix = KILLED_BY_AN;
280 
281     /* inform player about being poisoned unless that's already been done;
282        "blast" has given a "blast of poison gas" message; "poison arrow",
283        "poison dart", etc have implicitly given poison messages too... */
284     if (strcmp(reason, "blast") && !strstri(reason, "poison")) {
285         boolean plural = (reason[strlen(reason) - 1] == 's') ? 1 : 0;
286 
287         /* avoid "The" Orcus's sting was poisoned... */
288         pline("%s%s %s poisoned!",
289               isupper((uchar) *reason) ? "" : "The ", reason,
290               plural ? "were" : "was");
291     }
292     if (Poison_resistance) {
293         if (!strcmp(reason, "blast"))
294             shieldeff(u.ux, u.uy);
295         pline_The("poison doesn't seem to affect you.");
296         return;
297     }
298 
299     /* suppress killer prefix if it already has one */
300     i = name_to_mon(pkiller);
301     if (i >= LOW_PM && (mons[i].geno & G_UNIQ)) {
302         kprefix = KILLED_BY;
303         if (!type_is_pname(&mons[i]))
304             pkiller = the(pkiller);
305     } else if (!strncmpi(pkiller, "the ", 4) || !strncmpi(pkiller, "an ", 3)
306                || !strncmpi(pkiller, "a ", 2)) {
307         /*[ does this need a plural check too? ]*/
308         kprefix = KILLED_BY;
309     }
310 
311     i = !fatal ? 1 : rn2(fatal + (thrown_weapon ? 20 : 0));
312     if (i == 0 && typ != A_CHA) {
313         /* instant kill */
314         u.uhp = -1;
315         context.botl = TRUE;
316         pline_The("poison was deadly...");
317     } else if (i > 5) {
318         /* HP damage; more likely--but less severe--with missiles */
319         loss = thrown_weapon ? rnd(6) : rn1(10, 6);
320         losehp(loss, pkiller, kprefix); /* poison damage */
321     } else {
322         /* attribute loss; if typ is A_STR, reduction in current and
323            maximum HP will occur once strength has dropped down to 3 */
324         loss = (thrown_weapon || !fatal) ? 1 : d(2, 2); /* was rn1(3,3) */
325         /* check that a stat change was made */
326         if (adjattrib(typ, -loss, 1))
327             poisontell(typ, TRUE);
328     }
329 
330     if (u.uhp < 1) {
331         killer.format = kprefix;
332         Strcpy(killer.name, pkiller);
333         /* "Poisoned by a poisoned ___" is redundant */
334         done(strstri(pkiller, "poison") ? DIED : POISONING);
335     }
336     (void) encumber_msg();
337 }
338 
339 void
change_luck(n)340 change_luck(n)
341 register schar n;
342 {
343     u.uluck += n;
344     if (u.uluck < 0 && u.uluck < LUCKMIN)
345         u.uluck = LUCKMIN;
346     if (u.uluck > 0 && u.uluck > LUCKMAX)
347         u.uluck = LUCKMAX;
348 }
349 
350 int
stone_luck(parameter)351 stone_luck(parameter)
352 boolean parameter; /* So I can't think up of a good name.  So sue me. --KAA */
353 {
354     register struct obj *otmp;
355     register long bonchance = 0;
356 
357     for (otmp = invent; otmp; otmp = otmp->nobj)
358         if (confers_luck(otmp)) {
359             if (otmp->cursed)
360                 bonchance -= otmp->quan;
361             else if (otmp->blessed)
362                 bonchance += otmp->quan;
363             else if (parameter)
364                 bonchance += otmp->quan;
365         }
366 
367     return sgn((int) bonchance);
368 }
369 
370 /* there has just been an inventory change affecting a luck-granting item */
371 void
set_moreluck()372 set_moreluck()
373 {
374     int luckbon = stone_luck(TRUE);
375 
376     if (!luckbon && !carrying(LUCKSTONE))
377         u.moreluck = 0;
378     else if (luckbon >= 0)
379         u.moreluck = LUCKADD;
380     else
381         u.moreluck = -LUCKADD;
382 }
383 
384 void
restore_attrib()385 restore_attrib()
386 {
387     int i, equilibrium;;
388 
389     /*
390      * Note:  this gets called on every turn but ATIME() is never set
391      * to non-zero anywhere, and ATEMP() is only used for strength loss
392      * from hunger, so it doesn't actually do anything.
393      */
394 
395     for (i = 0; i < A_MAX; i++) { /* all temporary losses/gains */
396         equilibrium = (i == A_STR && u.uhs >= WEAK) ? -1 : 0;
397         if (ATEMP(i) != equilibrium && ATIME(i) != 0) {
398             if (!(--(ATIME(i)))) { /* countdown for change */
399                 ATEMP(i) += (ATEMP(i) > 0) ? -1 : 1;
400                 context.botl = TRUE;
401                 if (ATEMP(i)) /* reset timer */
402                     ATIME(i) = 100 / ACURR(A_CON);
403             }
404         }
405     }
406     if (context.botl)
407         (void) encumber_msg();
408 }
409 
410 #define AVAL 50 /* tune value for exercise gains */
411 
412 void
exercise(i,inc_or_dec)413 exercise(i, inc_or_dec)
414 int i;
415 boolean inc_or_dec;
416 {
417     debugpline0("Exercise:");
418     if (i == A_INT || i == A_CHA)
419         return; /* can't exercise these */
420 
421     /* no physical exercise while polymorphed; the body's temporary */
422     if (Upolyd && i != A_WIS)
423         return;
424 
425     if (abs(AEXE(i)) < AVAL) {
426         /*
427          *      Law of diminishing returns (Part I):
428          *
429          *      Gain is harder at higher attribute values.
430          *      79% at "3" --> 0% at "18"
431          *      Loss is even at all levels (50%).
432          *
433          *      Note: *YES* ACURR is the right one to use.
434          */
435         AEXE(i) += (inc_or_dec) ? (rn2(19) > ACURR(i)) : -rn2(2);
436         debugpline3("%s, %s AEXE = %d",
437                     (i == A_STR) ? "Str" : (i == A_WIS) ? "Wis" : (i == A_DEX)
438                                                                       ? "Dex"
439                                                                       : "Con",
440                     (inc_or_dec) ? "inc" : "dec", AEXE(i));
441     }
442     if (moves > 0 && (i == A_STR || i == A_CON))
443         (void) encumber_msg();
444 }
445 
446 STATIC_OVL void
exerper()447 exerper()
448 {
449     if (!(moves % 10)) {
450         /* Hunger Checks */
451 
452         int hs = (u.uhunger > 1000) ? SATIATED : (u.uhunger > 150)
453                                                      ? NOT_HUNGRY
454                                                      : (u.uhunger > 50)
455                                                            ? HUNGRY
456                                                            : (u.uhunger > 0)
457                                                                  ? WEAK
458                                                                  : FAINTING;
459 
460         debugpline0("exerper: Hunger checks");
461         switch (hs) {
462         case SATIATED:
463             exercise(A_DEX, FALSE);
464             if (Role_if(PM_MONK))
465                 exercise(A_WIS, FALSE);
466             break;
467         case NOT_HUNGRY:
468             exercise(A_CON, TRUE);
469             break;
470         case WEAK:
471             exercise(A_STR, FALSE);
472             if (Role_if(PM_MONK)) /* fasting */
473                 exercise(A_WIS, TRUE);
474             break;
475         case FAINTING:
476         case FAINTED:
477             exercise(A_CON, FALSE);
478             break;
479         }
480 
481         /* Encumbrance Checks */
482         debugpline0("exerper: Encumber checks");
483         switch (near_capacity()) {
484         case MOD_ENCUMBER:
485             exercise(A_STR, TRUE);
486             break;
487         case HVY_ENCUMBER:
488             exercise(A_STR, TRUE);
489             exercise(A_DEX, FALSE);
490             break;
491         case EXT_ENCUMBER:
492             exercise(A_DEX, FALSE);
493             exercise(A_CON, FALSE);
494             break;
495         }
496     }
497 
498     /* status checks */
499     if (!(moves % 5)) {
500         debugpline0("exerper: Status checks");
501         if ((HClairvoyant & (INTRINSIC | TIMEOUT)) && !BClairvoyant)
502             exercise(A_WIS, TRUE);
503         if (HRegeneration)
504             exercise(A_STR, TRUE);
505 
506         if (Sick || Vomiting)
507             exercise(A_CON, FALSE);
508         if (Confusion || Hallucination)
509             exercise(A_WIS, FALSE);
510         if ((Wounded_legs && !u.usteed) || Fumbling || HStun)
511             exercise(A_DEX, FALSE);
512     }
513 }
514 
515 /* exercise/abuse text (must be in attribute order, not botl order);
516    phrased as "You must have been [][0]." or "You haven't been [][1]." */
517 static NEARDATA const char *const exertext[A_MAX][2] = {
518     { "exercising diligently", "exercising properly" },           /* Str */
519     { 0, 0 },                                                     /* Int */
520     { "very observant", "paying attention" },                     /* Wis */
521     { "working on your reflexes", "working on reflexes lately" }, /* Dex */
522     { "leading a healthy life-style", "watching your health" },   /* Con */
523     { 0, 0 },                                                     /* Cha */
524 };
525 
526 void
exerchk()527 exerchk()
528 {
529     int i, ax, mod_val, lolim, hilim;
530 
531     /*  Check out the periodic accumulations */
532     exerper();
533 
534     if (moves >= context.next_attrib_check) {
535         debugpline1("exerchk: ready to test. multi = %d.", multi);
536     }
537     /*  Are we ready for a test? */
538     if (moves >= context.next_attrib_check && !multi) {
539         debugpline0("exerchk: testing.");
540         /*
541          *      Law of diminishing returns (Part II):
542          *
543          *      The effects of "exercise" and "abuse" wear
544          *      off over time.  Even if you *don't* get an
545          *      increase/decrease, you lose some of the
546          *      accumulated effects.
547          */
548         for (i = 0; i < A_MAX; ++i) {
549             ax = AEXE(i);
550             /* nothing to do here if no exercise or abuse has occurred
551                (Int and Cha always fall into this category) */
552             if (!ax)
553                 continue; /* ok to skip nextattrib */
554 
555             mod_val = sgn(ax); /* +1 or -1; used below */
556             /* no further effect for exercise if at max or abuse if at min;
557                can't exceed 18 via exercise even if actual max is higher */
558             lolim = ATTRMIN(i); /* usually 3; might be higher */
559             hilim = ATTRMAX(i); /* usually 18; maybe lower or higher */
560             if (hilim > 18)
561                 hilim = 18;
562             if ((ax < 0) ? (ABASE(i) <= lolim) : (ABASE(i) >= hilim))
563                 goto nextattrib;
564             /* can't exercise non-Wisdom while polymorphed; previous
565                exercise/abuse gradually wears off without impact then */
566             if (Upolyd && i != A_WIS)
567                 goto nextattrib;
568 
569             debugpline2("exerchk: testing %s (%d).",
570                         (i == A_STR)
571                             ? "Str"
572                             : (i == A_INT)
573                                   ? "Int?"
574                                   : (i == A_WIS)
575                                         ? "Wis"
576                                         : (i == A_DEX)
577                                               ? "Dex"
578                                               : (i == A_CON)
579                                                     ? "Con"
580                                                     : (i == A_CHA)
581                                                           ? "Cha?"
582                                                           : "???",
583                         ax);
584             /*
585              *  Law of diminishing returns (Part III):
586              *
587              *  You don't *always* gain by exercising.
588              *  [MRS 92/10/28 - Treat Wisdom specially for balance.]
589              */
590             if (rn2(AVAL) > ((i != A_WIS) ? (abs(ax) * 2 / 3) : abs(ax)))
591                 goto nextattrib;
592 
593             debugpline1("exerchk: changing %d.", i);
594             if (adjattrib(i, mod_val, -1)) {
595                 debugpline1("exerchk: changed %d.", i);
596                 /* if you actually changed an attrib - zero accumulation */
597                 AEXE(i) = ax = 0;
598                 /* then print an explanation */
599                 You("%s %s.",
600                     (mod_val > 0) ? "must have been" : "haven't been",
601                     exertext[i][(mod_val > 0) ? 0 : 1]);
602             }
603  nextattrib:
604             /* this used to be ``AEXE(i) /= 2'' but that would produce
605                platform-dependent rounding/truncation for negative vals */
606             AEXE(i) = (abs(ax) / 2) * mod_val;
607         }
608         context.next_attrib_check += rn1(200, 800);
609         debugpline1("exerchk: next check at %ld.", context.next_attrib_check);
610     }
611 }
612 
613 void
init_attr(np)614 init_attr(np)
615 register int np;
616 {
617     register int i, x, tryct;
618 
619     for (i = 0; i < A_MAX; i++) {
620         ABASE(i) = AMAX(i) = urole.attrbase[i];
621         ATEMP(i) = ATIME(i) = 0;
622         np -= urole.attrbase[i];
623     }
624 
625     tryct = 0;
626     while (np > 0 && tryct < 100) {
627         x = rn2(100);
628         for (i = 0; (i < A_MAX) && ((x -= urole.attrdist[i]) > 0); i++)
629             ;
630         if (i >= A_MAX)
631             continue; /* impossible */
632 
633         if (ABASE(i) >= ATTRMAX(i)) {
634             tryct++;
635             continue;
636         }
637         tryct = 0;
638         ABASE(i)++;
639         AMAX(i)++;
640         np--;
641     }
642 
643     tryct = 0;
644     while (np < 0 && tryct < 100) { /* for redistribution */
645 
646         x = rn2(100);
647         for (i = 0; (i < A_MAX) && ((x -= urole.attrdist[i]) > 0); i++)
648             ;
649         if (i >= A_MAX)
650             continue; /* impossible */
651 
652         if (ABASE(i) <= ATTRMIN(i)) {
653             tryct++;
654             continue;
655         }
656         tryct = 0;
657         ABASE(i)--;
658         AMAX(i)--;
659         np++;
660     }
661 }
662 
663 void
redist_attr()664 redist_attr()
665 {
666     register int i, tmp;
667 
668     for (i = 0; i < A_MAX; i++) {
669         if (i == A_INT || i == A_WIS)
670             continue;
671         /* Polymorphing doesn't change your mind */
672         tmp = AMAX(i);
673         AMAX(i) += (rn2(5) - 2);
674         if (AMAX(i) > ATTRMAX(i))
675             AMAX(i) = ATTRMAX(i);
676         if (AMAX(i) < ATTRMIN(i))
677             AMAX(i) = ATTRMIN(i);
678         ABASE(i) = ABASE(i) * AMAX(i) / tmp;
679         /* ABASE(i) > ATTRMAX(i) is impossible */
680         if (ABASE(i) < ATTRMIN(i))
681             ABASE(i) = ATTRMIN(i);
682     }
683     (void) encumber_msg();
684 }
685 
686 STATIC_OVL
687 void
postadjabil(ability)688 postadjabil(ability)
689 long *ability;
690 {
691     if (!ability)
692         return;
693     if (ability == &(HWarning) || ability == &(HSee_invisible))
694         see_monsters();
695 }
696 
697 STATIC_OVL const struct innate *
role_abil(r)698 role_abil(r)
699 int r;
700 {
701     const struct {
702         short role;
703         const struct innate *abil;
704     } roleabils[] = {
705         { PM_ARCHEOLOGIST, arc_abil },
706         { PM_BARBARIAN, bar_abil },
707         { PM_CAVEMAN, cav_abil },
708         { PM_HEALER, hea_abil },
709         { PM_KNIGHT, kni_abil },
710         { PM_MONK, mon_abil },
711         { PM_PRIEST, pri_abil },
712         { PM_RANGER, ran_abil },
713         { PM_ROGUE, rog_abil },
714         { PM_SAMURAI, sam_abil },
715         { PM_TOURIST, tou_abil },
716         { PM_VALKYRIE, val_abil },
717         { PM_WIZARD, wiz_abil },
718         { 0, 0 }
719     };
720     int i;
721 
722     for (i = 0; roleabils[i].abil && roleabils[i].role != r; i++)
723         continue;
724     return roleabils[i].abil;
725 }
726 
727 STATIC_OVL const struct innate *
check_innate_abil(ability,frommask)728 check_innate_abil(ability, frommask)
729 long *ability;
730 long frommask;
731 {
732     const struct innate *abil = 0;
733 
734     if (frommask == FROMEXPER)
735         abil = role_abil(Role_switch);
736     else if (frommask == FROMRACE)
737         switch (Race_switch) {
738         case PM_DWARF:
739             abil = dwa_abil;
740             break;
741         case PM_ELF:
742             abil = elf_abil;
743             break;
744         case PM_GNOME:
745             abil = gno_abil;
746             break;
747         case PM_ORC:
748             abil = orc_abil;
749             break;
750         case PM_HUMAN:
751             abil = hum_abil;
752             break;
753         default:
754             break;
755         }
756 
757     while (abil && abil->ability) {
758         if ((abil->ability == ability) && (u.ulevel >= abil->ulevel))
759             return abil;
760         abil++;
761     }
762     return (struct innate *) 0;
763 }
764 
765 /* reasons for innate ability */
766 #define FROM_NONE 0
767 #define FROM_ROLE 1 /* from experience at level 1 */
768 #define FROM_RACE 2
769 #define FROM_INTR 3 /* intrinsically (eating some corpse or prayer reward) */
770 #define FROM_EXP  4 /* from experience for some level > 1 */
771 #define FROM_FORM 5
772 #define FROM_LYCN 6
773 
774 /* check whether particular ability has been obtained via innate attribute */
775 STATIC_OVL int
innately(ability)776 innately(ability)
777 long *ability;
778 {
779     const struct innate *iptr;
780 
781     if ((iptr = check_innate_abil(ability, FROMEXPER)) != 0)
782         return (iptr->ulevel == 1) ? FROM_ROLE : FROM_EXP;
783     if ((iptr = check_innate_abil(ability, FROMRACE)) != 0)
784         return FROM_RACE;
785     if ((*ability & FROMOUTSIDE) != 0L)
786         return FROM_INTR;
787     if ((*ability & FROMFORM) != 0L)
788         return FROM_FORM;
789     return FROM_NONE;
790 }
791 
792 int
is_innate(propidx)793 is_innate(propidx)
794 int propidx;
795 {
796     int innateness;
797 
798     /* innately() would report FROM_FORM for this; caller wants specificity */
799     if (propidx == DRAIN_RES && u.ulycn >= LOW_PM)
800         return FROM_LYCN;
801     if (propidx == FAST && Very_fast)
802         return FROM_NONE; /* can't become very fast innately */
803     if ((innateness = innately(&u.uprops[propidx].intrinsic)) != FROM_NONE)
804         return innateness;
805     if (propidx == JUMPING && Role_if(PM_KNIGHT)
806         /* knight has intrinsic jumping, but extrinsic is more versatile so
807            ignore innateness if equipment is going to claim responsibility */
808         && !u.uprops[propidx].extrinsic)
809         return FROM_ROLE;
810     if (propidx == BLINDED && !haseyes(youmonst.data))
811         return FROM_FORM;
812     return FROM_NONE;
813 }
814 
815 char *
from_what(propidx)816 from_what(propidx)
817 int propidx; /* special cases can have negative values */
818 {
819     static char buf[BUFSZ];
820 
821     buf[0] = '\0';
822     /*
823      * Restrict the source of the attributes just to debug mode for now
824      */
825     if (wizard) {
826         static NEARDATA const char because_of[] = " because of %s";
827 
828         if (propidx >= 0) {
829             char *p;
830             struct obj *obj = (struct obj *) 0;
831             int innateness = is_innate(propidx);
832 
833             /*
834              * Properties can be obtained from multiple sources and we
835              * try to pick the most significant one.  Classification
836              * priority is not set in stone; current precedence is:
837              * "from the start" (from role or race at level 1),
838              * "from outside" (eating corpse, divine reward, blessed potion),
839              * "from experience" (from role or race at level 2+),
840              * "from current form" (while polymorphed),
841              * "from timed effect" (potion or spell),
842              * "from worn/wielded equipment" (Firebrand, elven boots, &c),
843              * "from carried equipment" (mainly quest artifacts).
844              * There are exceptions.  Versatile jumping from spell or boots
845              * takes priority over knight's innate but limited jumping.
846              */
847             if (propidx == BLINDED && u.uroleplay.blind)
848                 Sprintf(buf, " from birth");
849             else if (innateness == FROM_ROLE || innateness == FROM_RACE)
850                 Strcpy(buf, " innately");
851             else if (innateness == FROM_INTR) /* [].intrinsic & FROMOUTSIDE */
852                 Strcpy(buf, " intrinsically");
853             else if (innateness == FROM_EXP)
854                 Strcpy(buf, " because of your experience");
855             else if (innateness == FROM_LYCN)
856                 Strcpy(buf, " due to your lycanthropy");
857             else if (innateness == FROM_FORM)
858                 Strcpy(buf, " from current creature form");
859             else if (propidx == FAST && Very_fast)
860                 Sprintf(buf, because_of,
861                         ((HFast & TIMEOUT) != 0L) ? "a potion or spell"
862                           : ((EFast & W_ARMF) != 0L && uarmf->dknown
863                              && objects[uarmf->otyp].oc_name_known)
864                               ? ysimple_name(uarmf) /* speed boots */
865                                 : EFast ? "worn equipment"
866                                   : something);
867             else if (wizard
868                      && (obj = what_gives(&u.uprops[propidx].extrinsic)) != 0)
869                 Sprintf(buf, because_of, obj->oartifact
870                                              ? bare_artifactname(obj)
871                                              : ysimple_name(obj));
872             else if (propidx == BLINDED && Blindfolded_only)
873                 Sprintf(buf, because_of, ysimple_name(ublindf));
874 
875             /* remove some verbosity and/or redundancy */
876             if ((p = strstri(buf, " pair of ")) != 0)
877                 copynchars(p + 1, p + 9, BUFSZ); /* overlapping buffers ok */
878             else if (propidx == STRANGLED
879                      && (p = strstri(buf, " of strangulation")) != 0)
880                 *p = '\0';
881 
882         } else { /* negative property index */
883             /* if more blocking capabilities get implemented we'll need to
884                replace this with what_blocks() comparable to what_gives() */
885             switch (-propidx) {
886             case BLINDED:
887                 if (ublindf
888                     && ublindf->oartifact == ART_EYES_OF_THE_OVERWORLD)
889                     Sprintf(buf, because_of, bare_artifactname(ublindf));
890                 break;
891             case INVIS:
892                 if (u.uprops[INVIS].blocked & W_ARMC)
893                     Sprintf(buf, because_of,
894                             ysimple_name(uarmc)); /* mummy wrapping */
895                 break;
896             case CLAIRVOYANT:
897                 if (wizard && (u.uprops[CLAIRVOYANT].blocked & W_ARMH))
898                     Sprintf(buf, because_of,
899                             ysimple_name(uarmh)); /* cornuthaum */
900                 break;
901             }
902         }
903 
904     } /*wizard*/
905     return buf;
906 }
907 
908 void
adjabil(oldlevel,newlevel)909 adjabil(oldlevel, newlevel)
910 int oldlevel, newlevel;
911 {
912     register const struct innate *abil, *rabil;
913     long prevabil, mask = FROMEXPER;
914 
915     abil = role_abil(Role_switch);
916 
917     switch (Race_switch) {
918     case PM_ELF:
919         rabil = elf_abil;
920         break;
921     case PM_ORC:
922         rabil = orc_abil;
923         break;
924     case PM_HUMAN:
925     case PM_DWARF:
926     case PM_GNOME:
927     default:
928         rabil = 0;
929         break;
930     }
931 
932     while (abil || rabil) {
933         /* Have we finished with the intrinsics list? */
934         if (!abil || !abil->ability) {
935             /* Try the race intrinsics */
936             if (!rabil || !rabil->ability)
937                 break;
938             abil = rabil;
939             rabil = 0;
940             mask = FROMRACE;
941         }
942         prevabil = *(abil->ability);
943         if (oldlevel < abil->ulevel && newlevel >= abil->ulevel) {
944             /* Abilities gained at level 1 can never be lost
945              * via level loss, only via means that remove _any_
946              * sort of ability.  A "gain" of such an ability from
947              * an outside source is devoid of meaning, so we set
948              * FROMOUTSIDE to avoid such gains.
949              */
950             if (abil->ulevel == 1)
951                 *(abil->ability) |= (mask | FROMOUTSIDE);
952             else
953                 *(abil->ability) |= mask;
954             if (!(*(abil->ability) & INTRINSIC & ~mask)) {
955                 if (*(abil->gainstr))
956                     You_feel("%s!", abil->gainstr);
957             }
958         } else if (oldlevel >= abil->ulevel && newlevel < abil->ulevel) {
959             *(abil->ability) &= ~mask;
960             if (!(*(abil->ability) & INTRINSIC)) {
961                 if (*(abil->losestr))
962                     You_feel("%s!", abil->losestr);
963                 else if (*(abil->gainstr))
964                     You_feel("less %s!", abil->gainstr);
965             }
966         }
967         if (prevabil != *(abil->ability)) /* it changed */
968             postadjabil(abil->ability);
969         abil++;
970     }
971 
972     if (oldlevel > 0) {
973         if (newlevel > oldlevel)
974             add_weapon_skill(newlevel - oldlevel);
975         else
976             lose_weapon_skill(oldlevel - newlevel);
977     }
978 }
979 
980 int
newhp()981 newhp()
982 {
983     int hp, conplus;
984 
985     if (u.ulevel == 0) {
986         /* Initialize hit points */
987         hp = urole.hpadv.infix + urace.hpadv.infix;
988         if (urole.hpadv.inrnd > 0)
989             hp += rnd(urole.hpadv.inrnd);
990         if (urace.hpadv.inrnd > 0)
991             hp += rnd(urace.hpadv.inrnd);
992         if (moves <= 1L) { /* initial hero; skip for polyself to new man */
993             /* Initialize alignment stuff */
994             u.ualign.type = aligns[flags.initalign].value;
995             u.ualign.record = urole.initrecord;
996         }
997         /* no Con adjustment for initial hit points */
998     } else {
999         if (u.ulevel < urole.xlev) {
1000             hp = urole.hpadv.lofix + urace.hpadv.lofix;
1001             if (urole.hpadv.lornd > 0)
1002                 hp += rnd(urole.hpadv.lornd);
1003             if (urace.hpadv.lornd > 0)
1004                 hp += rnd(urace.hpadv.lornd);
1005         } else {
1006             hp = urole.hpadv.hifix + urace.hpadv.hifix;
1007             if (urole.hpadv.hirnd > 0)
1008                 hp += rnd(urole.hpadv.hirnd);
1009             if (urace.hpadv.hirnd > 0)
1010                 hp += rnd(urace.hpadv.hirnd);
1011         }
1012         if (ACURR(A_CON) <= 3)
1013             conplus = -2;
1014         else if (ACURR(A_CON) <= 6)
1015             conplus = -1;
1016         else if (ACURR(A_CON) <= 14)
1017             conplus = 0;
1018         else if (ACURR(A_CON) <= 16)
1019             conplus = 1;
1020         else if (ACURR(A_CON) == 17)
1021             conplus = 2;
1022         else if (ACURR(A_CON) == 18)
1023             conplus = 3;
1024         else
1025             conplus = 4;
1026         hp += conplus;
1027     }
1028     if (hp <= 0)
1029         hp = 1;
1030     if (u.ulevel < MAXULEV)
1031         u.uhpinc[u.ulevel] = (xchar) hp;
1032     return hp;
1033 }
1034 
1035 schar
acurr(x)1036 acurr(x)
1037 int x;
1038 {
1039     register int tmp = (u.abon.a[x] + u.atemp.a[x] + u.acurr.a[x]);
1040 
1041     if (x == A_STR) {
1042         if (tmp >= 125 || (uarmg && uarmg->otyp == GAUNTLETS_OF_POWER))
1043             return (schar) 125;
1044         else
1045 #ifdef WIN32_BUG
1046             return (x = ((tmp <= 3) ? 3 : tmp));
1047 #else
1048             return (schar) ((tmp <= 3) ? 3 : tmp);
1049 #endif
1050     } else if (x == A_CHA) {
1051         if (tmp < 18
1052             && (youmonst.data->mlet == S_NYMPH || u.umonnum == PM_SUCCUBUS
1053                 || u.umonnum == PM_INCUBUS))
1054             return (schar) 18;
1055     } else if (x == A_CON) {
1056         if (uwep && uwep->oartifact == ART_OGRESMASHER)
1057             return (schar) 25;
1058     } else if (x == A_INT || x == A_WIS) {
1059         /* yes, this may raise int/wis if player is sufficiently
1060          * stupid.  there are lower levels of cognition than "dunce".
1061          */
1062         if (uarmh && uarmh->otyp == DUNCE_CAP)
1063             return (schar) 6;
1064     }
1065 #ifdef WIN32_BUG
1066     return (x = ((tmp >= 25) ? 25 : (tmp <= 3) ? 3 : tmp));
1067 #else
1068     return (schar) ((tmp >= 25) ? 25 : (tmp <= 3) ? 3 : tmp);
1069 #endif
1070 }
1071 
1072 /* condense clumsy ACURR(A_STR) value into value that fits into game formulas
1073  */
1074 schar
acurrstr()1075 acurrstr()
1076 {
1077     register int str = ACURR(A_STR);
1078 
1079     if (str <= 18)
1080         return (schar) str;
1081     if (str <= 121)
1082         return (schar) (19 + str / 50); /* map to 19..21 */
1083     else
1084         return (schar) (min(str, 125) - 100); /* 22..25 */
1085 }
1086 
1087 /* when wearing (or taking off) an unID'd item, this routine is used
1088    to distinguish between observable +0 result and no-visible-effect
1089    due to an attribute not being able to exceed maximum or minimum */
1090 boolean
extremeattr(attrindx)1091 extremeattr(attrindx) /* does attrindx's value match its max or min? */
1092 int attrindx;
1093 {
1094     /* Fixed_abil and racial MINATTR/MAXATTR aren't relevant here */
1095     int lolimit = 3, hilimit = 25, curval = ACURR(attrindx);
1096 
1097     /* upper limit for Str is 25 but its value is encoded differently */
1098     if (attrindx == A_STR) {
1099         hilimit = STR19(25); /* 125 */
1100         /* lower limit for Str can also be 25 */
1101         if (uarmg && uarmg->otyp == GAUNTLETS_OF_POWER)
1102             lolimit = hilimit;
1103     } else if (attrindx == A_CON) {
1104         if (uwep && uwep->oartifact == ART_OGRESMASHER)
1105             lolimit = hilimit;
1106     }
1107     /* this exception is hypothetical; the only other worn item affecting
1108        Int or Wis is another helmet so can't be in use at the same time */
1109     if (attrindx == A_INT || attrindx == A_WIS) {
1110         if (uarmh && uarmh->otyp == DUNCE_CAP)
1111             hilimit = lolimit = 6;
1112     }
1113 
1114     /* are we currently at either limit? */
1115     return (curval == lolimit || curval == hilimit) ? TRUE : FALSE;
1116 }
1117 
1118 /* avoid possible problems with alignment overflow, and provide a centralized
1119    location for any future alignment limits */
1120 void
adjalign(n)1121 adjalign(n)
1122 int n;
1123 {
1124     int newalign = u.ualign.record + n;
1125 
1126     if (n < 0) {
1127         if (newalign < u.ualign.record)
1128             u.ualign.record = newalign;
1129     } else if (newalign > u.ualign.record) {
1130         u.ualign.record = newalign;
1131         if (u.ualign.record > ALIGNLIM)
1132             u.ualign.record = ALIGNLIM;
1133     }
1134 }
1135 
1136 /* change hero's alignment type, possibly losing use of artifacts */
1137 void
uchangealign(newalign,reason)1138 uchangealign(newalign, reason)
1139 int newalign;
1140 int reason; /* 0==conversion, 1==helm-of-OA on, 2==helm-of-OA off */
1141 {
1142     aligntyp oldalign = u.ualign.type;
1143 
1144     u.ublessed = 0; /* lose divine protection */
1145     /* You/Your/pline message with call flush_screen(), triggering bot(),
1146        so the actual data change needs to come before the message */
1147     context.botl = TRUE; /* status line needs updating */
1148     if (reason == 0) {
1149         /* conversion via altar */
1150         u.ualignbase[A_CURRENT] = (aligntyp) newalign;
1151         /* worn helm of opposite alignment might block change */
1152         if (!uarmh || uarmh->otyp != HELM_OF_OPPOSITE_ALIGNMENT)
1153             u.ualign.type = u.ualignbase[A_CURRENT];
1154         You("have a %ssense of a new direction.",
1155             (u.ualign.type != oldalign) ? "sudden " : "");
1156     } else {
1157         /* putting on or taking off a helm of opposite alignment */
1158         u.ualign.type = (aligntyp) newalign;
1159         if (reason == 1)
1160             Your("mind oscillates %s.", Hallucination ? "wildly" : "briefly");
1161         else if (reason == 2)
1162             Your("mind is %s.", Hallucination
1163                                     ? "much of a muchness"
1164                                     : "back in sync with your body");
1165     }
1166     if (u.ualign.type != oldalign) {
1167         u.ualign.record = 0; /* slate is wiped clean */
1168         retouch_equipment(0);
1169     }
1170 }
1171 
1172 /*attrib.c*/
1173