1 /* SCCS Id: @(#)attrib.c 3.4 2002/10/07 */
2 /* Copyright 1988, 1989, 1990, 1992, M. Stephenson */
3 /* NetHack may be freely redistributed. See license for details. */
5 /* attribute modification routines. */
9 /* #define DEBUG */ /* uncomment for debugging info */
13 /* part of the output on gain or loss of attribute */
15 const char * const plusattr[] = {
16 "strong", "smart", "wise", "agile", "tough", "charismatic"
18 * const minusattr[] = {
19 "weak", "stupid", "foolish", "clumsy", "fragile", "repulsive"
27 const char *gainstr, *losestr;
28 } arc_abil[] = { { 1, &(HStealth), "", "" },
29 { 1, &(HFast), "", "" },
30 { 10, &(HSearching), "perceptive", "" },
33 bar_abil[] = { { 1, &(HPoison_resistance), "", "" },
34 { 7, &(HFast), "quick", "slow" },
35 { 15, &(HStealth), "stealthy", "" },
38 cav_abil[] = { { 7, &(HFast), "quick", "slow" },
39 { 15, &(HWarning), "sensitive", "" },
42 hea_abil[] = { { 1, &(HPoison_resistance), "", "" },
43 { 15, &(HWarning), "sensitive", "" },
46 kni_abil[] = { { 7, &(HFast), "quick", "slow" },
49 mon_abil[] = { { 1, &(HFast), "", "" },
50 { 1, &(HSleep_resistance), "", "" },
51 { 1, &(HSee_invisible), "", "" },
52 { 3, &(HPoison_resistance), "healthy", "" },
53 { 5, &(HStealth), "stealthy", "" },
54 { 7, &(HWarning), "sensitive", "" },
55 { 9, &(HSearching), "perceptive", "unaware" },
56 { 11, &(HFire_resistance), "cool", "warmer" },
57 { 13, &(HCold_resistance), "warm", "cooler" },
58 { 15, &(HShock_resistance), "insulated", "conductive" },
59 { 17, &(HTeleport_control), "controlled","uncontrolled" },
62 pri_abil[] = { { 15, &(HWarning), "sensitive", "" },
63 { 20, &(HFire_resistance), "cool", "warmer" },
66 ran_abil[] = { { 1, &(HSearching), "", "" },
67 { 7, &(HStealth), "stealthy", "" },
68 { 15, &(HSee_invisible), "", "" },
71 rog_abil[] = { { 1, &(HStealth), "", "" },
72 { 10, &(HSearching), "perceptive", "" },
75 sam_abil[] = { { 1, &(HFast), "", "" },
76 { 15, &(HStealth), "stealthy", "" },
79 tou_abil[] = { { 10, &(HSearching), "perceptive", "" },
80 { 20, &(HPoison_resistance), "hardy", "" },
83 val_abil[] = { { 1, &(HCold_resistance), "", "" },
84 { 1, &(HStealth), "", "" },
85 { 7, &(HFast), "quick", "slow" },
88 wiz_abil[] = { { 15, &(HWarning), "sensitive", "" },
89 { 17, &(HTeleport_control), "controlled","uncontrolled" },
92 /* Intrinsics conferred by race */
93 elf_abil[] = { { 4, &(HSleep_resistance), "awake", "tired" },
96 orc_abil[] = { { 1, &(HPoison_resistance), "", "" },
99 static long next_check = 600L; /* arbitrary first setting */
100 STATIC_DCL void NDECL(exerper);
101 STATIC_DCL void FDECL(postadjabil, (long *));
103 /* adjust an attribute; return TRUE if change is made, FALSE otherwise */
105 adjattrib(ndx, incr, msgflg)
107 int msgflg; /* positive => no message, zero => message, and */
108 { /* negative => conditional (msg if change made) */
109 if (Fixed_abil || !incr) return FALSE;
111 if ((ndx == A_INT || ndx == A_WIS)
112 && uarmh && uarmh->otyp == DUNCE_CAP) {
114 Your("cap constricts briefly, then relaxes again.");
119 if ((AMAX(ndx) >= ATTRMAX(ndx)) && (ACURR(ndx) >= AMAX(ndx))) {
120 if (msgflg == 0 && flags.verbose)
121 pline("You're already as %s as you can get.",
123 ABASE(ndx) = AMAX(ndx) = ATTRMAX(ndx); /* just in case */
128 if(ABASE(ndx) > AMAX(ndx)) {
129 incr = ABASE(ndx) - AMAX(ndx);
131 if(AMAX(ndx) > ATTRMAX(ndx))
132 AMAX(ndx) = ATTRMAX(ndx);
133 ABASE(ndx) = AMAX(ndx);
136 if (ABASE(ndx) <= ATTRMIN(ndx)) {
137 if (msgflg == 0 && flags.verbose)
138 pline("You're already as %s as you can get.",
140 ABASE(ndx) = ATTRMIN(ndx); /* just in case */
145 if(ABASE(ndx) < ATTRMIN(ndx)) {
146 incr = ABASE(ndx) - ATTRMIN(ndx);
147 ABASE(ndx) = ATTRMIN(ndx);
149 if(AMAX(ndx) < ATTRMIN(ndx))
150 AMAX(ndx) = ATTRMIN(ndx);
155 (incr > 1 || incr < -1) ? "very ": "",
156 (incr > 0) ? plusattr[ndx] : minusattr[ndx]);
158 if (moves > 1 && (ndx == A_STR || ndx == A_CON))
159 (void)encumber_msg();
165 register struct obj *otmp;
172 if(ABASE(A_STR) < 18) num = (rn2(4) ? 1 : rnd(6) );
173 else if (ABASE(A_STR) < STR18(85)) num = rnd(10);
175 (void) adjattrib(A_STR, (otmp && otmp->cursed) ? -num : num, TRUE);
179 losestr(num) /* may kill you; cause may be poison or monster like 'a' */
182 int ustr = ABASE(A_STR) - num;
195 (void) adjattrib(A_STR, -num, TRUE);
203 if (u.uluck < 0 && u.uluck < LUCKMIN) u.uluck = LUCKMIN;
204 if (u.uluck > 0 && u.uluck > LUCKMAX) u.uluck = LUCKMAX;
208 stone_luck(parameter)
209 boolean parameter; /* So I can't think up of a good name. So sue me. --KAA */
211 register struct obj *otmp;
212 register long bonchance = 0;
214 for (otmp = invent; otmp; otmp = otmp->nobj)
215 if (confers_luck(otmp)) {
216 if (otmp->cursed) bonchance -= otmp->quan;
217 else if (otmp->blessed) bonchance += otmp->quan;
218 else if (parameter) bonchance += otmp->quan;
221 return sgn((int)bonchance);
224 /* there has just been an inventory change affecting a luck-granting item */
228 int luckbon = stone_luck(TRUE);
230 if (!luckbon && !carrying(LUCKSTONE)) u.moreluck = 0;
231 else if (luckbon >= 0) u.moreluck = LUCKADD;
232 else u.moreluck = -LUCKADD;
243 for(i = 0; i < A_MAX; i++) { /* all temporary losses/gains */
245 if(ATEMP(i) && ATIME(i)) {
246 if(!(--(ATIME(i)))) { /* countdown for change */
247 ATEMP(i) += ATEMP(i) > 0 ? -1 : 1;
249 if(ATEMP(i)) /* reset timer */
250 ATIME(i) = 100 / ACURR(A_CON);
254 (void)encumber_msg();
260 #define AVAL 50 /* tune value for exercise gains */
263 exercise(i, inc_or_dec)
270 if (i == A_INT || i == A_CHA) return; /* can't exercise these */
272 /* no physical exercise while polymorphed; the body's temporary */
273 if (Upolyd && i != A_WIS) return;
275 if(abs(AEXE(i)) < AVAL) {
277 * Law of diminishing returns (Part I):
279 * Gain is harder at higher attribute values.
280 * 79% at "3" --> 0% at "18"
281 * Loss is even at all levels (50%).
283 * Note: *YES* ACURR is the right one to use.
285 AEXE(i) += (inc_or_dec) ? (rn2(19) > ACURR(i)) : -rn2(2);
287 pline("%s, %s AEXE = %d",
288 (i == A_STR) ? "Str" : (i == A_WIS) ? "Wis" :
289 (i == A_DEX) ? "Dex" : "Con",
290 (inc_or_dec) ? "inc" : "dec", AEXE(i));
293 if (moves > 0 && (i == A_STR || i == A_CON)) (void)encumber_msg();
296 /* hunger values - from eat.c */
311 int hs = (u.uhunger > 1000) ? SATIATED :
312 (u.uhunger > 150) ? NOT_HUNGRY :
313 (u.uhunger > 50) ? HUNGRY :
314 (u.uhunger > 0) ? WEAK : FAINTING;
317 pline("exerper: Hunger checks");
320 case SATIATED: exercise(A_DEX, FALSE);
321 if (Role_if(PM_MONK))
322 exercise(A_WIS, FALSE);
324 case NOT_HUNGRY: exercise(A_CON, TRUE); break;
325 case WEAK: exercise(A_STR, FALSE);
326 if (Role_if(PM_MONK)) /* fasting */
327 exercise(A_WIS, TRUE);
330 case FAINTED: exercise(A_CON, FALSE); break;
333 /* Encumberance Checks */
335 pline("exerper: Encumber checks");
337 switch (near_capacity()) {
338 case MOD_ENCUMBER: exercise(A_STR, TRUE); break;
339 case HVY_ENCUMBER: exercise(A_STR, TRUE);
340 exercise(A_DEX, FALSE); break;
341 case EXT_ENCUMBER: exercise(A_DEX, FALSE);
342 exercise(A_CON, FALSE); break;
350 pline("exerper: Status checks");
352 if ((HClairvoyant & (INTRINSIC|TIMEOUT)) &&
353 !BClairvoyant) exercise(A_WIS, TRUE);
354 if (HRegeneration) exercise(A_STR, TRUE);
356 if(Sick || Vomiting) exercise(A_CON, FALSE);
357 if(Confusion || Hallucination) exercise(A_WIS, FALSE);
362 ) || Fumbling || HStun) exercise(A_DEX, FALSE);
371 /* Check out the periodic accumulations */
375 if(moves >= next_check)
376 pline("exerchk: ready to test. multi = %d.", multi);
378 /* Are we ready for a test? */
379 if(moves >= next_check && !multi) {
381 pline("exerchk: testing.");
384 * Law of diminishing returns (Part II):
386 * The effects of "exercise" and "abuse" wear
387 * off over time. Even if you *don't* get an
388 * increase/decrease, you lose some of the
389 * accumulated effects.
391 for(i = 0; i < A_MAX; AEXE(i++) /= 2) {
393 if(ABASE(i) >= 18 || !AEXE(i)) continue;
394 if(i == A_INT || i == A_CHA) continue;/* can't exercise these */
397 pline("exerchk: testing %s (%d).",
398 (i == A_STR) ? "Str" : (i == A_WIS) ? "Wis" :
399 (i == A_DEX) ? "Dex" : "Con", AEXE(i));
402 * Law of diminishing returns (Part III):
404 * You don't *always* gain by exercising.
405 * [MRS 92/10/28 - Treat Wisdom specially for balance.]
407 if(rn2(AVAL) > ((i != A_WIS) ? abs(AEXE(i)*2/3) : abs(AEXE(i))))
409 mod_val = sgn(AEXE(i));
412 pline("exerchk: changing %d.", i);
414 if(adjattrib(i, mod_val, -1)) {
416 pline("exerchk: changed %d.", i);
418 /* if you actually changed an attrib - zero accumulation */
420 /* then print an explanation */
422 case A_STR: You((mod_val >0) ?
423 "must have been exercising." :
424 "must have been abusing your body.");
426 case A_WIS: You((mod_val >0) ?
427 "must have been very observant." :
428 "haven't been paying attention.");
430 case A_DEX: You((mod_val >0) ?
431 "must have been working on your reflexes." :
432 "haven't been working on reflexes lately.");
434 case A_CON: You((mod_val >0) ?
435 "must be leading a healthy life-style." :
436 "haven't been watching your health.");
441 next_check += rn1(200,800);
443 pline("exerchk: next check at %ld.", next_check);
448 /* next_check will otherwise have its initial 600L after a game restore */
450 reset_attribute_clock()
452 if (moves > 600L) next_check = moves + rn1(50,800);
460 register int i, x, tryct;
463 for(i = 0; i < A_MAX; i++) {
464 ABASE(i) = AMAX(i) = urole.attrbase[i];
465 ATEMP(i) = ATIME(i) = 0;
466 np -= urole.attrbase[i];
470 while(np > 0 && tryct < 100) {
473 for (i = 0; (i < A_MAX) && ((x -= urole.attrdist[i]) > 0); i++) ;
474 if(i >= A_MAX) continue; /* impossible */
476 if(ABASE(i) >= ATTRMAX(i)) {
488 while(np < 0 && tryct < 100) { /* for redistribution */
491 for (i = 0; (i < A_MAX) && ((x -= urole.attrdist[i]) > 0); i++) ;
492 if(i >= A_MAX) continue; /* impossible */
494 if(ABASE(i) <= ATTRMIN(i)) {
511 for(i = 0; i < A_MAX; i++) {
512 if (i==A_INT || i==A_WIS) continue;
513 /* Polymorphing doesn't change your mind */
515 AMAX(i) += (rn2(5)-2);
516 if (AMAX(i) > ATTRMAX(i)) AMAX(i) = ATTRMAX(i);
517 if (AMAX(i) < ATTRMIN(i)) AMAX(i) = ATTRMIN(i);
518 ABASE(i) = ABASE(i) * AMAX(i) / tmp;
519 /* ABASE(i) > ATTRMAX(i) is impossible */
520 if (ABASE(i) < ATTRMIN(i)) ABASE(i) = ATTRMIN(i);
522 (void)encumber_msg();
530 if (!ability) return;
531 if (ability == &(HWarning) || ability == &(HSee_invisible))
536 adjabil(oldlevel,newlevel)
537 int oldlevel, newlevel;
539 register const struct innate *abil, *rabil;
540 long mask = FROMEXPER;
543 switch (Role_switch) {
544 case PM_ARCHEOLOGIST: abil = arc_abil; break;
545 case PM_BARBARIAN: abil = bar_abil; break;
546 case PM_CAVEMAN: abil = cav_abil; break;
547 case PM_HEALER: abil = hea_abil; break;
548 case PM_KNIGHT: abil = kni_abil; break;
549 case PM_MONK: abil = mon_abil; break;
550 case PM_PRIEST: abil = pri_abil; break;
551 case PM_RANGER: abil = ran_abil; break;
552 case PM_ROGUE: abil = rog_abil; break;
553 case PM_SAMURAI: abil = sam_abil; break;
555 case PM_TOURIST: abil = tou_abil; break;
557 case PM_VALKYRIE: abil = val_abil; break;
558 case PM_WIZARD: abil = wiz_abil; break;
559 default: abil = 0; break;
562 switch (Race_switch) {
563 case PM_ELF: rabil = elf_abil; break;
564 case PM_ORC: rabil = orc_abil; break;
568 default: rabil = 0; break;
571 while (abil || rabil) {
573 /* Have we finished with the intrinsics list? */
574 if (!abil || !abil->ability) {
575 /* Try the race intrinsics */
576 if (!rabil || !rabil->ability) break;
581 prevabil = *(abil->ability);
582 if(oldlevel < abil->ulevel && newlevel >= abil->ulevel) {
583 /* Abilities gained at level 1 can never be lost
584 * via level loss, only via means that remove _any_
585 * sort of ability. A "gain" of such an ability from
586 * an outside source is devoid of meaning, so we set
587 * FROMOUTSIDE to avoid such gains.
589 if (abil->ulevel == 1)
590 *(abil->ability) |= (mask|FROMOUTSIDE);
592 *(abil->ability) |= mask;
593 if(!(*(abil->ability) & INTRINSIC & ~mask)) {
595 You_feel("%s!", abil->gainstr);
597 } else if (oldlevel >= abil->ulevel && newlevel < abil->ulevel) {
598 *(abil->ability) &= ~mask;
599 if(!(*(abil->ability) & INTRINSIC)) {
601 You_feel("%s!", abil->losestr);
602 else if(*(abil->gainstr))
603 You_feel("less %s!", abil->gainstr);
606 if (prevabil != *(abil->ability)) /* it changed */
607 postadjabil(abil->ability);
612 if (newlevel > oldlevel)
613 add_weapon_skill(newlevel - oldlevel);
615 lose_weapon_skill(oldlevel - newlevel);
627 /* Initialize hit points */
628 hp = urole.hpadv.infix + urace.hpadv.infix;
629 if (urole.hpadv.inrnd > 0) hp += rnd(urole.hpadv.inrnd);
630 if (urace.hpadv.inrnd > 0) hp += rnd(urace.hpadv.inrnd);
632 /* Initialize alignment stuff */
633 u.ualign.type = aligns[flags.initalign].value;
634 u.ualign.record = urole.initrecord;
638 if (u.ulevel < urole.xlev) {
639 hp = urole.hpadv.lofix + urace.hpadv.lofix;
640 if (urole.hpadv.lornd > 0) hp += rnd(urole.hpadv.lornd);
641 if (urace.hpadv.lornd > 0) hp += rnd(urace.hpadv.lornd);
643 hp = urole.hpadv.hifix + urace.hpadv.hifix;
644 if (urole.hpadv.hirnd > 0) hp += rnd(urole.hpadv.hirnd);
645 if (urace.hpadv.hirnd > 0) hp += rnd(urace.hpadv.hirnd);
649 if (ACURR(A_CON) <= 3) conplus = -2;
650 else if (ACURR(A_CON) <= 6) conplus = -1;
651 else if (ACURR(A_CON) <= 14) conplus = 0;
652 else if (ACURR(A_CON) <= 16) conplus = 1;
653 else if (ACURR(A_CON) == 17) conplus = 2;
654 else if (ACURR(A_CON) == 18) conplus = 3;
658 return((hp <= 0) ? 1 : hp);
668 register int tmp = (u.abon.a[x] + u.atemp.a[x] + u.acurr.a[x]);
671 if (uarmg && uarmg->otyp == GAUNTLETS_OF_POWER) return(125);
673 else return(x=((tmp >= 125) ? 125 : (tmp <= 3) ? 3 : tmp));
675 else return((schar)((tmp >= 125) ? 125 : (tmp <= 3) ? 3 : tmp));
677 } else if (x == A_CHA) {
678 if (tmp < 18 && (youmonst.data->mlet == S_NYMPH ||
679 u.umonnum==PM_SUCCUBUS || u.umonnum == PM_INCUBUS))
681 } else if (x == A_INT || x == A_WIS) {
682 /* yes, this may raise int/wis if player is sufficiently
683 * stupid. there are lower levels of cognition than "dunce".
685 if (uarmh && uarmh->otyp == DUNCE_CAP) return(6);
688 return(x=((tmp >= 25) ? 25 : (tmp <= 3) ? 3 : tmp));
690 return((schar)((tmp >= 25) ? 25 : (tmp <= 3) ? 3 : tmp));
694 /* condense clumsy ACURR(A_STR) value into value that fits into game formulas
699 register int str = ACURR(A_STR);
701 if (str <= 18) return((schar)str);
702 if (str <= 121) return((schar)(19 + str / 50)); /* map to 19-21 */
703 else return((schar)(str - 100));
709 /* avoid possible problems with alignment overflow, and provide a centralized
710 * location for any future alignment limits
716 register int newalign = u.ualign.record + n;
719 if(newalign < u.ualign.record)
720 u.ualign.record = newalign;
722 if(newalign > u.ualign.record) {
723 u.ualign.record = newalign;
724 if(u.ualign.record > ALIGNLIM)
725 u.ualign.record = ALIGNLIM;