1 /* NetHack 3.6 attrib.c $NHDT-Date: 1449269911 2015/12/04 22:58:31 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.51 $ */
2 /* Copyright 1988, 1989, 1990, 1992, M. Stephenson */
3 /* NetHack may be freely redistributed. See license for details. */
5 /* attribute modification routines. */
10 /* part of the output on gain or loss of attribute */
12 *const plusattr[] = { "strong", "smart", "wise",
13 "agile", "tough", "charismatic" },
14 *const minusattr[] = { "weak", "stupid",
16 "fragile", "repulsive" };
18 static const struct innate {
21 const char *gainstr, *losestr;
22 } arc_abil[] = { { 1, &(HStealth), "", "" },
23 { 1, &(HFast), "", "" },
24 { 10, &(HSearching), "perceptive", "" },
27 bar_abil[] = { { 1, &(HPoison_resistance), "", "" },
28 { 7, &(HFast), "quick", "slow" },
29 { 15, &(HStealth), "stealthy", "" },
32 cav_abil[] = { { 7, &(HFast), "quick", "slow" },
33 { 15, &(HWarning), "sensitive", "" },
36 hea_abil[] = { { 1, &(HPoison_resistance), "", "" },
37 { 15, &(HWarning), "sensitive", "" },
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" },
55 pri_abil[] = { { 15, &(HWarning), "sensitive", "" },
56 { 20, &(HFire_resistance), "cool", "warmer" },
59 ran_abil[] = { { 1, &(HSearching), "", "" },
60 { 7, &(HStealth), "stealthy", "" },
61 { 15, &(HSee_invisible), "", "" },
64 rog_abil[] = { { 1, &(HStealth), "", "" },
65 { 10, &(HSearching), "perceptive", "" },
68 sam_abil[] = { { 1, &(HFast), "", "" },
69 { 15, &(HStealth), "stealthy", "" },
72 tou_abil[] = { { 10, &(HSearching), "perceptive", "" },
73 { 20, &(HPoison_resistance), "hardy", "" },
76 val_abil[] = { { 1, &(HCold_resistance), "", "" },
77 { 1, &(HStealth), "", "" },
78 { 7, &(HFast), "quick", "slow" },
81 wiz_abil[] = { { 15, &(HWarning), "sensitive", "" },
82 { 17, &(HTeleport_control), "controlled", "uncontrolled" },
85 /* Intrinsics conferred by race */
86 elf_abil[] = { { 4, &(HSleep_resistance), "awake", "tired" },
89 orc_abil[] = { { 1, &(HPoison_resistance), "", "" }, { 0, 0, 0, 0 } };
91 STATIC_DCL void NDECL(exerper);
92 STATIC_DCL void FDECL(postadjabil, (long *));
93 STATIC_DCL const struct innate *FDECL(check_innate_abil, (long *, long));
94 STATIC_DCL int FDECL(innately, (long *));
96 /* adjust an attribute; return TRUE if change is made, FALSE otherwise */
98 adjattrib(ndx, incr, msgflg)
100 int msgflg; /* positive => no message, zero => message, and */
101 { /* negative => conditional (msg if change made) */
106 if (Fixed_abil || !incr)
109 if ((ndx == A_INT || ndx == A_WIS) && uarmh && uarmh->otyp == DUNCE_CAP) {
111 Your("cap constricts briefly, then relaxes again.");
115 old_acurr = ACURR(ndx);
118 if (ABASE(ndx) > AMAX(ndx)) {
119 incr = ABASE(ndx) - AMAX(ndx);
121 if (AMAX(ndx) > ATTRMAX(ndx))
122 AMAX(ndx) = ATTRMAX(ndx);
123 ABASE(ndx) = AMAX(ndx);
125 attrstr = plusattr[ndx];
126 abonflg = (ABON(ndx) < 0);
129 if (ABASE(ndx) < ATTRMIN(ndx)) {
130 incr = ABASE(ndx) - ATTRMIN(ndx);
131 ABASE(ndx) = ATTRMIN(ndx);
133 if (AMAX(ndx) < ATTRMIN(ndx))
134 AMAX(ndx) = ATTRMIN(ndx);
136 attrstr = minusattr[ndx];
137 abonflg = (ABON(ndx) > 0);
139 if (ACURR(ndx) == old_acurr) {
140 if (msgflg == 0 && flags.verbose)
141 pline("You're %s as %s as you can get.",
142 abonflg ? "currently" : "already", attrstr);
147 You_feel("%s%s!", (incr > 1 || incr < -1) ? "very " : "", attrstr);
149 if (moves > 1 && (ndx == A_STR || ndx == A_CON))
150 (void) encumber_msg();
155 gainstr(otmp, incr, givemsg)
163 if (ABASE(A_STR) < 18)
164 num = (rn2(4) ? 1 : rnd(6));
165 else if (ABASE(A_STR) < STR18(85))
170 (void) adjattrib(A_STR, (otmp && otmp->cursed) ? -num : num,
174 /* may kill you; cause may be poison or monster like 'a' */
179 int ustr = ABASE(A_STR) - num;
192 (void) adjattrib(A_STR, -num, 1);
195 static const struct poison_effect_message {
196 void VDECL((*delivery_func), (const char *, ...));
197 const char *effect_msg;
199 { You_feel, "weaker" }, /* A_STR */
200 { Your, "brain is on fire" }, /* A_INT */
201 { Your, "judgement is impaired" }, /* A_WIS */
202 { Your, "muscles won't obey you" }, /* A_DEX */
203 { You_feel, "very sick" }, /* A_CON */
204 { You, "break out in hives" } /* A_CHA */
207 /* feedback for attribute loss due to poisoning */
209 poisontell(typ, exclaim)
210 int typ; /* which attribute */
211 boolean exclaim; /* emphasis */
213 void VDECL((*func), (const char *, ...)) = poiseff[typ].delivery_func;
215 (*func)("%s%c", poiseff[typ].effect_msg, exclaim ? '!' : '.');
218 /* called when an attack or trap has poisoned the hero (used to be in mon.c)
221 poisoned(reason, typ, pkiller, fatal, thrown_weapon)
222 const char *reason, /* controls what messages we display */
223 *pkiller; /* for score+log file if fatal */
224 int typ, fatal; /* if fatal is 0, limit damage to adjattrib */
225 boolean thrown_weapon; /* thrown weapons are less deadly */
227 int i, loss, kprefix = KILLED_BY_AN;
229 /* inform player about being poisoned unless that's already been done;
230 "blast" has given a "blast of poison gas" message; "poison arrow",
231 "poison dart", etc have implicitly given poison messages too... */
232 if (strcmp(reason, "blast") && !strstri(reason, "poison")) {
233 boolean plural = (reason[strlen(reason) - 1] == 's') ? 1 : 0;
235 /* avoid "The" Orcus's sting was poisoned... */
236 pline("%s%s %s poisoned!", isupper(*reason) ? "" : "The ", reason,
237 plural ? "were" : "was");
239 if (Poison_resistance) {
240 if (!strcmp(reason, "blast"))
241 shieldeff(u.ux, u.uy);
242 pline_The("poison doesn't seem to affect you.");
246 /* suppress killer prefix if it already has one */
247 i = name_to_mon(pkiller);
248 if (i >= LOW_PM && (mons[i].geno & G_UNIQ)) {
250 if (!type_is_pname(&mons[i]))
251 pkiller = the(pkiller);
252 } else if (!strncmpi(pkiller, "the ", 4) || !strncmpi(pkiller, "an ", 3)
253 || !strncmpi(pkiller, "a ", 2)) {
254 /*[ does this need a plural check too? ]*/
258 i = !fatal ? 1 : rn2(fatal + (thrown_weapon ? 20 : 0));
259 if (i == 0 && typ != A_CHA) {
262 pline_The("poison was deadly...");
264 /* HP damage; more likely--but less severe--with missiles */
265 loss = thrown_weapon ? rnd(6) : rn1(10, 6);
266 losehp(loss, pkiller, kprefix); /* poison damage */
268 /* attribute loss; if typ is A_STR, reduction in current and
269 maximum HP will occur once strength has dropped down to 3 */
270 loss = (thrown_weapon || !fatal) ? 1 : d(2, 2); /* was rn1(3,3) */
271 /* check that a stat change was made */
272 if (adjattrib(typ, -loss, 1))
273 poisontell(typ, TRUE);
277 killer.format = kprefix;
278 Strcpy(killer.name, pkiller);
279 /* "Poisoned by a poisoned ___" is redundant */
280 done(strstri(pkiller, "poison") ? DIED : POISONING);
282 (void) encumber_msg();
290 if (u.uluck < 0 && u.uluck < LUCKMIN)
292 if (u.uluck > 0 && u.uluck > LUCKMAX)
297 stone_luck(parameter)
298 boolean parameter; /* So I can't think up of a good name. So sue me. --KAA */
300 register struct obj *otmp;
301 register long bonchance = 0;
303 for (otmp = invent; otmp; otmp = otmp->nobj)
304 if (confers_luck(otmp)) {
306 bonchance -= otmp->quan;
307 else if (otmp->blessed)
308 bonchance += otmp->quan;
310 bonchance += otmp->quan;
313 return sgn((int) bonchance);
316 /* there has just been an inventory change affecting a luck-granting item */
320 int luckbon = stone_luck(TRUE);
322 if (!luckbon && !carrying(LUCKSTONE))
324 else if (luckbon >= 0)
325 u.moreluck = LUCKADD;
327 u.moreluck = -LUCKADD;
335 for (i = 0; i < A_MAX; i++) { /* all temporary losses/gains */
337 if (ATEMP(i) && ATIME(i)) {
338 if (!(--(ATIME(i)))) { /* countdown for change */
339 ATEMP(i) += ATEMP(i) > 0 ? -1 : 1;
341 if (ATEMP(i)) /* reset timer */
342 ATIME(i) = 100 / ACURR(A_CON);
346 (void) encumber_msg();
349 #define AVAL 50 /* tune value for exercise gains */
352 exercise(i, inc_or_dec)
356 debugpline0("Exercise:");
357 if (i == A_INT || i == A_CHA)
358 return; /* can't exercise these */
360 /* no physical exercise while polymorphed; the body's temporary */
361 if (Upolyd && i != A_WIS)
364 if (abs(AEXE(i)) < AVAL) {
366 * Law of diminishing returns (Part I):
368 * Gain is harder at higher attribute values.
369 * 79% at "3" --> 0% at "18"
370 * Loss is even at all levels (50%).
372 * Note: *YES* ACURR is the right one to use.
374 AEXE(i) += (inc_or_dec) ? (rn2(19) > ACURR(i)) : -rn2(2);
375 debugpline3("%s, %s AEXE = %d",
376 (i == A_STR) ? "Str" : (i == A_WIS) ? "Wis" : (i == A_DEX)
379 (inc_or_dec) ? "inc" : "dec", AEXE(i));
381 if (moves > 0 && (i == A_STR || i == A_CON))
382 (void) encumber_msg();
391 int hs = (u.uhunger > 1000) ? SATIATED : (u.uhunger > 150)
399 debugpline0("exerper: Hunger checks");
402 exercise(A_DEX, FALSE);
403 if (Role_if(PM_MONK))
404 exercise(A_WIS, FALSE);
407 exercise(A_CON, TRUE);
410 exercise(A_STR, FALSE);
411 if (Role_if(PM_MONK)) /* fasting */
412 exercise(A_WIS, TRUE);
416 exercise(A_CON, FALSE);
420 /* Encumbrance Checks */
421 debugpline0("exerper: Encumber checks");
422 switch (near_capacity()) {
424 exercise(A_STR, TRUE);
427 exercise(A_STR, TRUE);
428 exercise(A_DEX, FALSE);
431 exercise(A_DEX, FALSE);
432 exercise(A_CON, FALSE);
439 debugpline0("exerper: Status checks");
440 if ((HClairvoyant & (INTRINSIC | TIMEOUT)) && !BClairvoyant)
441 exercise(A_WIS, TRUE);
443 exercise(A_STR, TRUE);
445 if (Sick || Vomiting)
446 exercise(A_CON, FALSE);
447 if (Confusion || Hallucination)
448 exercise(A_WIS, FALSE);
449 if ((Wounded_legs && !u.usteed) || Fumbling || HStun)
450 exercise(A_DEX, FALSE);
454 /* exercise/abuse text (must be in attribute order, not botl order);
455 phrased as "You must have been [][0]." or "You haven't been [][1]." */
456 static NEARDATA const char *const exertext[A_MAX][2] = {
457 { "exercising diligently", "exercising properly" }, /* Str */
459 { "very observant", "paying attention" }, /* Wis */
460 { "working on your reflexes", "working on reflexes lately" }, /* Dex */
461 { "leading a healthy life-style", "watching your health" }, /* Con */
468 int i, ax, mod_val, lolim, hilim;
470 /* Check out the periodic accumulations */
473 if (moves >= context.next_attrib_check) {
474 debugpline1("exerchk: ready to test. multi = %d.", multi);
476 /* Are we ready for a test? */
477 if (moves >= context.next_attrib_check && !multi) {
478 debugpline0("exerchk: testing.");
480 * Law of diminishing returns (Part II):
482 * The effects of "exercise" and "abuse" wear
483 * off over time. Even if you *don't* get an
484 * increase/decrease, you lose some of the
485 * accumulated effects.
487 for (i = 0; i < A_MAX; ++i) {
489 /* nothing to do here if no exercise or abuse has occurred
490 (Int and Cha always fall into this category) */
492 continue; /* ok to skip nextattrib */
494 mod_val = sgn(ax); /* +1 or -1; used below */
495 /* no further effect for exercise if at max or abuse if at min;
496 can't exceed 18 via exercise even if actual max is higher */
497 lolim = ATTRMIN(i); /* usually 3; might be higher */
498 hilim = ATTRMAX(i); /* usually 18; maybe lower or higher */
501 if ((ax < 0) ? (ABASE(i) <= lolim) : (ABASE(i) >= hilim))
503 /* can't exercise non-Wisdom while polymorphed; previous
504 exercise/abuse gradually wears off without impact then */
505 if (Upolyd && i != A_WIS)
508 debugpline2("exerchk: testing %s (%d).",
524 * Law of diminishing returns (Part III):
526 * You don't *always* gain by exercising.
527 * [MRS 92/10/28 - Treat Wisdom specially for balance.]
529 if (rn2(AVAL) > ((i != A_WIS) ? (abs(ax) * 2 / 3) : abs(ax)))
532 debugpline1("exerchk: changing %d.", i);
533 if (adjattrib(i, mod_val, -1)) {
534 debugpline1("exerchk: changed %d.", i);
535 /* if you actually changed an attrib - zero accumulation */
537 /* then print an explanation */
539 (mod_val > 0) ? "must have been" : "haven't been",
540 exertext[i][(mod_val > 0) ? 0 : 1]);
543 /* this used to be ``AEXE(i) /= 2'' but that would produce
544 platform-dependent rounding/truncation for negative vals */
545 AEXE(i) = (abs(ax) / 2) * mod_val;
547 context.next_attrib_check += rn1(200, 800);
548 debugpline1("exerchk: next check at %ld.", context.next_attrib_check);
556 register int i, x, tryct;
558 for (i = 0; i < A_MAX; i++) {
559 ABASE(i) = AMAX(i) = urole.attrbase[i];
560 ATEMP(i) = ATIME(i) = 0;
561 np -= urole.attrbase[i];
565 while (np > 0 && tryct < 100) {
567 for (i = 0; (i < A_MAX) && ((x -= urole.attrdist[i]) > 0); i++)
570 continue; /* impossible */
572 if (ABASE(i) >= ATTRMAX(i)) {
583 while (np < 0 && tryct < 100) { /* for redistribution */
586 for (i = 0; (i < A_MAX) && ((x -= urole.attrdist[i]) > 0); i++)
589 continue; /* impossible */
591 if (ABASE(i) <= ATTRMIN(i)) {
607 for (i = 0; i < A_MAX; i++) {
608 if (i == A_INT || i == A_WIS)
610 /* Polymorphing doesn't change your mind */
612 AMAX(i) += (rn2(5) - 2);
613 if (AMAX(i) > ATTRMAX(i))
614 AMAX(i) = ATTRMAX(i);
615 if (AMAX(i) < ATTRMIN(i))
616 AMAX(i) = ATTRMIN(i);
617 ABASE(i) = ABASE(i) * AMAX(i) / tmp;
618 /* ABASE(i) > ATTRMAX(i) is impossible */
619 if (ABASE(i) < ATTRMIN(i))
620 ABASE(i) = ATTRMIN(i);
622 (void) encumber_msg();
632 if (ability == &(HWarning) || ability == &(HSee_invisible))
636 STATIC_OVL const struct innate *
637 check_innate_abil(ability, frommask)
641 const struct innate *abil = 0;
643 if (frommask == FROMEXPER)
644 switch (Role_switch) {
645 case PM_ARCHEOLOGIST:
687 else if (frommask == FROMRACE)
688 switch (Race_switch) {
702 while (abil && abil->ability) {
703 if ((abil->ability == ability) && (u.ulevel >= abil->ulevel))
707 return (struct innate *) 0;
711 * returns 1 if FROMRACE or FROMEXPER and exper level == 1
712 * returns 2 if FROMEXPER and exper level > 1
713 * otherwise returns 0
719 const struct innate *iptr;
721 if ((iptr = check_innate_abil(ability, FROMRACE)) != 0)
723 else if ((iptr = check_innate_abil(ability, FROMEXPER)) != 0)
724 return (iptr->ulevel == 1) ? 1 : 2;
732 if (propidx == BLINDED && !haseyes(youmonst.data))
734 return innately(&u.uprops[propidx].intrinsic);
739 int propidx; /* special cases can have negative values */
741 static char buf[BUFSZ];
745 * Restrict the source of the attributes just to debug mode for now
748 static NEARDATA const char because_of[] = " because of %s";
752 struct obj *obj = (struct obj *) 0;
753 int innate = is_innate(propidx);
756 Strcpy(buf, " because of your experience");
757 else if (innate == 1)
758 Strcpy(buf, " innately");
760 && (obj = what_gives(&u.uprops[propidx].extrinsic)))
761 Sprintf(buf, because_of, obj->oartifact
762 ? bare_artifactname(obj)
763 : ysimple_name(obj));
764 else if (propidx == BLINDED && u.uroleplay.blind)
765 Sprintf(buf, " from birth");
766 else if (propidx == BLINDED && Blindfolded_only)
767 Sprintf(buf, because_of, ysimple_name(ublindf));
769 /* remove some verbosity and/or redundancy */
770 if ((p = strstri(buf, " pair of ")) != 0)
771 copynchars(p + 1, p + 9, BUFSZ); /* overlapping buffers ok */
772 else if (propidx == STRANGLED
773 && (p = strstri(buf, " of strangulation")) != 0)
776 } else { /* negative property index */
777 /* if more blocking capabilities get implemented we'll need to
778 replace this with what_blocks() comparable to what_gives() */
782 && ublindf->oartifact == ART_EYES_OF_THE_OVERWORLD)
783 Sprintf(buf, because_of, bare_artifactname(ublindf));
786 if (u.uprops[INVIS].blocked & W_ARMC)
787 Sprintf(buf, because_of,
788 ysimple_name(uarmc)); /* mummy wrapping */
791 if (wizard && (u.uprops[CLAIRVOYANT].blocked & W_ARMH))
792 Sprintf(buf, because_of,
793 ysimple_name(uarmh)); /* cornuthaum */
803 adjabil(oldlevel, newlevel)
804 int oldlevel, newlevel;
806 register const struct innate *abil, *rabil;
807 long prevabil, mask = FROMEXPER;
809 switch (Role_switch) {
810 case PM_ARCHEOLOGIST:
854 switch (Race_switch) {
869 while (abil || rabil) {
870 /* Have we finished with the intrinsics list? */
871 if (!abil || !abil->ability) {
872 /* Try the race intrinsics */
873 if (!rabil || !rabil->ability)
879 prevabil = *(abil->ability);
880 if (oldlevel < abil->ulevel && newlevel >= abil->ulevel) {
881 /* Abilities gained at level 1 can never be lost
882 * via level loss, only via means that remove _any_
883 * sort of ability. A "gain" of such an ability from
884 * an outside source is devoid of meaning, so we set
885 * FROMOUTSIDE to avoid such gains.
887 if (abil->ulevel == 1)
888 *(abil->ability) |= (mask | FROMOUTSIDE);
890 *(abil->ability) |= mask;
891 if (!(*(abil->ability) & INTRINSIC & ~mask)) {
892 if (*(abil->gainstr))
893 You_feel("%s!", abil->gainstr);
895 } else if (oldlevel >= abil->ulevel && newlevel < abil->ulevel) {
896 *(abil->ability) &= ~mask;
897 if (!(*(abil->ability) & INTRINSIC)) {
898 if (*(abil->losestr))
899 You_feel("%s!", abil->losestr);
900 else if (*(abil->gainstr))
901 You_feel("less %s!", abil->gainstr);
904 if (prevabil != *(abil->ability)) /* it changed */
905 postadjabil(abil->ability);
910 if (newlevel > oldlevel)
911 add_weapon_skill(newlevel - oldlevel);
913 lose_weapon_skill(oldlevel - newlevel);
923 /* Initialize hit points */
924 hp = urole.hpadv.infix + urace.hpadv.infix;
925 if (urole.hpadv.inrnd > 0)
926 hp += rnd(urole.hpadv.inrnd);
927 if (urace.hpadv.inrnd > 0)
928 hp += rnd(urace.hpadv.inrnd);
929 if (moves <= 1L) { /* initial hero; skip for polyself to new man */
930 /* Initialize alignment stuff */
931 u.ualign.type = aligns[flags.initalign].value;
932 u.ualign.record = urole.initrecord;
934 /* no Con adjustment for initial hit points */
936 if (u.ulevel < urole.xlev) {
937 hp = urole.hpadv.lofix + urace.hpadv.lofix;
938 if (urole.hpadv.lornd > 0)
939 hp += rnd(urole.hpadv.lornd);
940 if (urace.hpadv.lornd > 0)
941 hp += rnd(urace.hpadv.lornd);
943 hp = urole.hpadv.hifix + urace.hpadv.hifix;
944 if (urole.hpadv.hirnd > 0)
945 hp += rnd(urole.hpadv.hirnd);
946 if (urace.hpadv.hirnd > 0)
947 hp += rnd(urace.hpadv.hirnd);
949 if (ACURR(A_CON) <= 3)
951 else if (ACURR(A_CON) <= 6)
953 else if (ACURR(A_CON) <= 14)
955 else if (ACURR(A_CON) <= 16)
957 else if (ACURR(A_CON) == 17)
959 else if (ACURR(A_CON) == 18)
967 if (u.ulevel < MAXULEV)
968 u.uhpinc[u.ulevel] = (xchar) hp;
976 register int tmp = (u.abon.a[x] + u.atemp.a[x] + u.acurr.a[x]);
979 if (tmp >= 125 || (uarmg && uarmg->otyp == GAUNTLETS_OF_POWER))
983 return (x = ((tmp <= 3) ? 3 : tmp));
985 return (schar) ((tmp <= 3) ? 3 : tmp);
987 } else if (x == A_CHA) {
989 && (youmonst.data->mlet == S_NYMPH || u.umonnum == PM_SUCCUBUS
990 || u.umonnum == PM_INCUBUS))
992 } else if (x == A_INT || x == A_WIS) {
993 /* yes, this may raise int/wis if player is sufficiently
994 * stupid. there are lower levels of cognition than "dunce".
996 if (uarmh && uarmh->otyp == DUNCE_CAP)
1000 return (x = ((tmp >= 25) ? 25 : (tmp <= 3) ? 3 : tmp));
1002 return (schar) ((tmp >= 25) ? 25 : (tmp <= 3) ? 3 : tmp);
1006 /* condense clumsy ACURR(A_STR) value into value that fits into game formulas
1011 register int str = ACURR(A_STR);
1016 return (schar) (19 + str / 50); /* map to 19..21 */
1018 return (schar) (min(str, 125) - 100); /* 22..25 */
1021 /* when wearing (or taking off) an unID'd item, this routine is used
1022 to distinguish between observable +0 result and no-visible-effect
1023 due to an attribute not being able to exceed maximum or minimum */
1025 extremeattr(attrindx) /* does attrindx's value match its max or min? */
1028 /* Fixed_abil and racial MINATTR/MAXATTR aren't relevant here */
1029 int lolimit = 3, hilimit = 25, curval = ACURR(attrindx);
1031 /* upper limit for Str is 25 but its value is encoded differently */
1032 if (attrindx == A_STR) {
1033 hilimit = STR19(25); /* 125 */
1034 /* lower limit for Str can also be 25 */
1035 if (uarmg && uarmg->otyp == GAUNTLETS_OF_POWER)
1038 /* this exception is hypothetical; the only other worn item affecting
1039 Int or Wis is another helmet so can't be in use at the same time */
1040 if (attrindx == A_INT || attrindx == A_WIS) {
1041 if (uarmh && uarmh->otyp == DUNCE_CAP)
1042 hilimit = lolimit = 6;
1045 /* are we currently at either limit? */
1046 return (curval == lolimit || curval == hilimit) ? TRUE : FALSE;
1049 /* avoid possible problems with alignment overflow, and provide a centralized
1050 location for any future alignment limits */
1055 int newalign = u.ualign.record + n;
1058 if (newalign < u.ualign.record)
1059 u.ualign.record = newalign;
1060 } else if (newalign > u.ualign.record) {
1061 u.ualign.record = newalign;
1062 if (u.ualign.record > ALIGNLIM)
1063 u.ualign.record = ALIGNLIM;
1067 /* change hero's alignment type, possibly losing use of artifacts */
1069 uchangealign(newalign, reason)
1071 int reason; /* 0==conversion, 1==helm-of-OA on, 2==helm-of-OA off */
1073 aligntyp oldalign = u.ualign.type;
1075 u.ublessed = 0; /* lose divine protection */
1076 context.botl = 1; /* status line needs updating */
1078 /* conversion via altar */
1079 u.ualignbase[A_CURRENT] = (aligntyp) newalign;
1080 /* worn helm of opposite alignment might block change */
1081 if (!uarmh || uarmh->otyp != HELM_OF_OPPOSITE_ALIGNMENT)
1082 u.ualign.type = u.ualignbase[A_CURRENT];
1083 You("have a %ssense of a new direction.",
1084 (u.ualign.type != oldalign) ? "sudden " : "");
1086 /* putting on or taking off a helm of opposite alignment */
1087 u.ualign.type = (aligntyp) newalign;
1089 Your("mind oscillates %s.", Hallucination ? "wildly" : "briefly");
1090 else if (reason == 2)
1091 Your("mind is %s.", Hallucination
1092 ? "much of a muchness"
1093 : "back in sync with your body");
1096 if (u.ualign.type != oldalign) {
1097 u.ualign.record = 0; /* slate is wiped clean */
1098 retouch_equipment(0);