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. */
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 dwa_abil
[] = { { 1, &HInfravision
, "", "" },
89 elf_abil
[] = { { 1, &HInfravision
, "", "" },
90 { 4, &HSleep_resistance
, "awake", "tired" },
93 gno_abil
[] = { { 1, &HInfravision
, "", "" },
96 orc_abil
[] = { { 1, &HInfravision
, "", "" },
97 { 1, &HPoison_resistance
, "", "" },
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 */
109 adjattrib(ndx
, incr
, msgflg
)
111 int msgflg
; /* positive => no message, zero => message, and */
112 { /* negative => conditional (msg if change made) */
117 if (Fixed_abil
|| !incr
)
120 if ((ndx
== A_INT
|| ndx
== A_WIS
) && uarmh
&& uarmh
->otyp
== DUNCE_CAP
) {
122 Your("cap constricts briefly, then relaxes again.");
126 old_acurr
= ACURR(ndx
);
129 if (ABASE(ndx
) > AMAX(ndx
)) {
130 incr
= ABASE(ndx
) - AMAX(ndx
);
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);
140 if (ABASE(ndx
) < ATTRMIN(ndx
)) {
141 incr
= ABASE(ndx
) - ATTRMIN(ndx
);
142 ABASE(ndx
) = ATTRMIN(ndx
);
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
);
158 You_feel("%s%s!", (incr
> 1 || incr
< -1) ? "very " : "", attrstr
);
160 if (moves
> 1 && (ndx
== A_STR
|| ndx
== A_CON
))
161 (void) encumber_msg();
166 gainstr(otmp
, incr
, givemsg
)
174 if (ABASE(A_STR
) < 18)
175 num
= (rn2(4) ? 1 : rnd(6));
176 else if (ABASE(A_STR
) < STR18(85))
181 (void) adjattrib(A_STR
, (otmp
&& otmp
->cursed
) ? -num
: num
,
185 /* may kill you; cause may be poison or monster like 'a' */
190 int ustr
= ABASE(A_STR
) - num
;
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
;
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 */
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)
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.");
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
)) {
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? ]*/
269 i
= !fatal
? 1 : rn2(fatal
+ (thrown_weapon
? 20 : 0));
270 if (i
== 0 && typ
!= A_CHA
) {
273 pline_The("poison was deadly...");
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 */
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
);
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();
301 if (u
.uluck
< 0 && u
.uluck
< LUCKMIN
)
303 if (u
.uluck
> 0 && 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
)) {
317 bonchance
-= otmp
->quan
;
318 else if (otmp
->blessed
)
319 bonchance
+= otmp
->quan
;
321 bonchance
+= otmp
->quan
;
324 return sgn((int) bonchance
);
327 /* there has just been an inventory change affecting a luck-granting item */
331 int luckbon
= stone_luck(TRUE
);
333 if (!luckbon
&& !carrying(LUCKSTONE
))
335 else if (luckbon
>= 0)
336 u
.moreluck
= LUCKADD
;
338 u
.moreluck
= -LUCKADD
;
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 */
363 exercise(i
, 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
)
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
)
390 (inc_or_dec
) ? "inc" : "dec", AEXE(i
));
392 if (moves
> 0 && (i
== A_STR
|| i
== A_CON
))
393 (void) encumber_msg();
402 int hs
= (u
.uhunger
> 1000) ? SATIATED
: (u
.uhunger
> 150)
410 debugpline0("exerper: Hunger checks");
413 exercise(A_DEX
, FALSE
);
414 if (Role_if(PM_MONK
))
415 exercise(A_WIS
, FALSE
);
418 exercise(A_CON
, TRUE
);
421 exercise(A_STR
, FALSE
);
422 if (Role_if(PM_MONK
)) /* fasting */
423 exercise(A_WIS
, TRUE
);
427 exercise(A_CON
, FALSE
);
431 /* Encumbrance Checks */
432 debugpline0("exerper: Encumber checks");
433 switch (near_capacity()) {
435 exercise(A_STR
, TRUE
);
438 exercise(A_STR
, TRUE
);
439 exercise(A_DEX
, FALSE
);
442 exercise(A_DEX
, FALSE
);
443 exercise(A_CON
, FALSE
);
450 debugpline0("exerper: Status checks");
451 if ((HClairvoyant
& (INTRINSIC
| TIMEOUT
)) && !BClairvoyant
)
452 exercise(A_WIS
, TRUE
);
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 */
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 */
479 int i
, ax
, mod_val
, lolim
, hilim
;
481 /* Check out the periodic accumulations */
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
) {
500 /* nothing to do here if no exercise or abuse has occurred
501 (Int and Cha always fall into this category) */
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 */
512 if ((ax
< 0) ? (ABASE(i
) <= lolim
) : (ABASE(i
) >= hilim
))
514 /* can't exercise non-Wisdom while polymorphed; previous
515 exercise/abuse gradually wears off without impact then */
516 if (Upolyd
&& i
!= A_WIS
)
519 debugpline2("exerchk: testing %s (%d).",
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
)))
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 */
548 /* then print an explanation */
550 (mod_val
> 0) ? "must have been" : "haven't been",
551 exertext
[i
][(mod_val
> 0) ? 0 : 1]);
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
);
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
];
576 while (np
> 0 && tryct
< 100) {
578 for (i
= 0; (i
< A_MAX
) && ((x
-= urole
.attrdist
[i
]) > 0); i
++)
581 continue; /* impossible */
583 if (ABASE(i
) >= ATTRMAX(i
)) {
594 while (np
< 0 && tryct
< 100) { /* for redistribution */
597 for (i
= 0; (i
< A_MAX
) && ((x
-= urole
.attrdist
[i
]) > 0); i
++)
600 continue; /* impossible */
602 if (ABASE(i
) <= ATTRMIN(i
)) {
618 for (i
= 0; i
< A_MAX
; i
++) {
619 if (i
== A_INT
|| i
== A_WIS
)
621 /* Polymorphing doesn't change your mind */
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();
643 if (ability
== &(HWarning
) || ability
== &(HSee_invisible
))
647 STATIC_OVL
const struct innate
*
648 check_innate_abil(ability
, frommask
)
652 const struct innate
*abil
= 0;
654 if (frommask
== FROMEXPER
)
655 switch (Role_switch
) {
656 case PM_ARCHEOLOGIST
:
698 else if (frommask
== FROMRACE
)
699 switch (Race_switch
) {
719 while (abil
&& abil
->ability
) {
720 if ((abil
->ability
== ability
) && (u
.ulevel
>= abil
->ulevel
))
724 return (struct innate
*) 0;
727 /* reasons for innate ability */
729 #define FROM_ROLE 1 /* from experience at level 1 */
731 #define FROM_INTR 3 /* intrinsically (eating some corpse or prayer reward) */
732 #define FROM_EXP 4 /* from experience for some level > 1 */
736 /* check whether particular ability has been obtained via innate attribute */
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)
747 if ((*ability
& FROMOUTSIDE
) != 0L)
749 if ((*ability
& FROMFORM
) != 0L)
760 /* innately() would report FROM_FORM for this; caller wants specificity */
761 if (propidx
== DRAIN_RES
&& u
.ulycn
>= LOW_PM
)
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
)
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
)
772 if (propidx
== BLINDED
&& !haseyes(youmonst
.data
))
779 int propidx
; /* special cases can have negative values */
781 static char buf
[BUFSZ
];
785 * Restrict the source of the attributes just to debug mode for now
788 static NEARDATA
const char because_of
[] = " because of %s";
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"
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)
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() */
850 && ublindf
->oartifact
== ART_EYES_OF_THE_OVERWORLD
)
851 Sprintf(buf
, because_of
, bare_artifactname(ublindf
));
854 if (u
.uprops
[INVIS
].blocked
& W_ARMC
)
855 Sprintf(buf
, because_of
,
856 ysimple_name(uarmc
)); /* mummy wrapping */
859 if (wizard
&& (u
.uprops
[CLAIRVOYANT
].blocked
& W_ARMH
))
860 Sprintf(buf
, because_of
,
861 ysimple_name(uarmh
)); /* cornuthaum */
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
:
922 switch (Race_switch
) {
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
)
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
);
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
);
978 if (newlevel
> oldlevel
)
979 add_weapon_skill(newlevel
- oldlevel
);
981 lose_weapon_skill(oldlevel
- newlevel
);
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 */
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
);
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)
1019 else if (ACURR(A_CON
) <= 6)
1021 else if (ACURR(A_CON
) <= 14)
1023 else if (ACURR(A_CON
) <= 16)
1025 else if (ACURR(A_CON
) == 17)
1027 else if (ACURR(A_CON
) == 18)
1035 if (u
.ulevel
< MAXULEV
)
1036 u
.uhpinc
[u
.ulevel
] = (xchar
) hp
;
1044 register int tmp
= (u
.abon
.a
[x
] + u
.atemp
.a
[x
] + u
.acurr
.a
[x
]);
1047 if (tmp
>= 125 || (uarmg
&& uarmg
->otyp
== GAUNTLETS_OF_POWER
))
1051 return (x
= ((tmp
<= 3) ? 3 : tmp
));
1053 return (schar
) ((tmp
<= 3) ? 3 : tmp
);
1055 } else if (x
== A_CHA
) {
1057 && (youmonst
.data
->mlet
== S_NYMPH
|| u
.umonnum
== PM_SUCCUBUS
1058 || u
.umonnum
== PM_INCUBUS
))
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
)
1068 return (x
= ((tmp
>= 25) ? 25 : (tmp
<= 3) ? 3 : tmp
));
1070 return (schar
) ((tmp
>= 25) ? 25 : (tmp
<= 3) ? 3 : tmp
);
1074 /* condense clumsy ACURR(A_STR) value into value that fits into game formulas
1079 register int str
= ACURR(A_STR
);
1084 return (schar
) (19 + str
/ 50); /* map to 19..21 */
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 */
1093 extremeattr(attrindx
) /* does attrindx's value match its max or min? */
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
)
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 */
1123 int newalign
= u
.ualign
.record
+ n
;
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 */
1137 uchangealign(newalign
, reason
)
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 */
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 " : "");
1154 /* putting on or taking off a helm of opposite alignment */
1155 u
.ualign
.type
= (aligntyp
) newalign
;
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);