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