Fix the problem pointed out by <email deleted>
where polymorphing into a new man at level 1 could be used to approximately
double or triple your hit points and spell power. With means to drain
level back down to 1 and with amulets of life saving to survive those times
you lose levels instead of gain, you could do this repeatedly and end up
with HP and Pw values in the millions.
This uses the earlier patch that records the HP and Pw increments from
level gains. Now when polymorphing into a new man, level based HP and Pw
are removed from the current values, remainder get multiplied by 80%, 90%,
100%, or 110% (average 95%, so tend to drop slightly), then a brand new set
of level gain increments (reflecting new man's Con and Wis) are added in.
Code for calculating spell energy is moved from pluslvl() and u_init()
into new routine newpw(). It and newhp() take over responsibility for
remembering the level based increments from pluslvl() which didn't deal
with the initial amount (stored in slot [0]; earlier patch didn't need it).
890 lines
23 KiB
C
890 lines
23 KiB
C
/* SCCS Id: @(#)attrib.c 3.5 2005/09/19 */
|
|
/* Copyright 1988, 1989, 1990, 1992, M. Stephenson */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
/* attribute modification routines. */
|
|
|
|
#include "hack.h"
|
|
#include <ctype.h>
|
|
|
|
/* #define DEBUG */ /* uncomment for debugging info */
|
|
|
|
/* part of the output on gain or loss of attribute */
|
|
static
|
|
const char * const plusattr[] = {
|
|
"strong", "smart", "wise", "agile", "tough", "charismatic"
|
|
},
|
|
* const minusattr[] = {
|
|
"weak", "stupid", "foolish", "clumsy", "fragile", "repulsive"
|
|
};
|
|
|
|
|
|
static
|
|
const struct innate {
|
|
schar ulevel;
|
|
long *ability;
|
|
const char *gainstr, *losestr;
|
|
} arc_abil[] = { { 1, &(HStealth), "", "" },
|
|
{ 1, &(HFast), "", "" },
|
|
{ 10, &(HSearching), "perceptive", "" },
|
|
{ 0, 0, 0, 0 } },
|
|
|
|
bar_abil[] = { { 1, &(HPoison_resistance), "", "" },
|
|
{ 7, &(HFast), "quick", "slow" },
|
|
{ 15, &(HStealth), "stealthy", "" },
|
|
{ 0, 0, 0, 0 } },
|
|
|
|
cav_abil[] = { { 7, &(HFast), "quick", "slow" },
|
|
{ 15, &(HWarning), "sensitive", "" },
|
|
{ 0, 0, 0, 0 } },
|
|
|
|
hea_abil[] = { { 1, &(HPoison_resistance), "", "" },
|
|
{ 15, &(HWarning), "sensitive", "" },
|
|
{ 0, 0, 0, 0 } },
|
|
|
|
kni_abil[] = { { 7, &(HFast), "quick", "slow" },
|
|
{ 0, 0, 0, 0 } },
|
|
|
|
mon_abil[] = { { 1, &(HFast), "", "" },
|
|
{ 1, &(HSleep_resistance), "", "" },
|
|
{ 1, &(HSee_invisible), "", "" },
|
|
{ 3, &(HPoison_resistance), "healthy", "" },
|
|
{ 5, &(HStealth), "stealthy", "" },
|
|
{ 7, &(HWarning), "sensitive", "" },
|
|
{ 9, &(HSearching), "perceptive", "unaware" },
|
|
{ 11, &(HFire_resistance), "cool", "warmer" },
|
|
{ 13, &(HCold_resistance), "warm", "cooler" },
|
|
{ 15, &(HShock_resistance), "insulated", "conductive" },
|
|
{ 17, &(HTeleport_control), "controlled","uncontrolled" },
|
|
{ 0, 0, 0, 0 } },
|
|
|
|
pri_abil[] = { { 15, &(HWarning), "sensitive", "" },
|
|
{ 20, &(HFire_resistance), "cool", "warmer" },
|
|
{ 0, 0, 0, 0 } },
|
|
|
|
ran_abil[] = { { 1, &(HSearching), "", "" },
|
|
{ 7, &(HStealth), "stealthy", "" },
|
|
{ 15, &(HSee_invisible), "", "" },
|
|
{ 0, 0, 0, 0 } },
|
|
|
|
rog_abil[] = { { 1, &(HStealth), "", "" },
|
|
{ 10, &(HSearching), "perceptive", "" },
|
|
{ 0, 0, 0, 0 } },
|
|
|
|
sam_abil[] = { { 1, &(HFast), "", "" },
|
|
{ 15, &(HStealth), "stealthy", "" },
|
|
{ 0, 0, 0, 0 } },
|
|
|
|
tou_abil[] = { { 10, &(HSearching), "perceptive", "" },
|
|
{ 20, &(HPoison_resistance), "hardy", "" },
|
|
{ 0, 0, 0, 0 } },
|
|
|
|
val_abil[] = { { 1, &(HCold_resistance), "", "" },
|
|
{ 1, &(HStealth), "", "" },
|
|
{ 7, &(HFast), "quick", "slow" },
|
|
{ 0, 0, 0, 0 } },
|
|
|
|
wiz_abil[] = { { 15, &(HWarning), "sensitive", "" },
|
|
{ 17, &(HTeleport_control), "controlled","uncontrolled" },
|
|
{ 0, 0, 0, 0 } },
|
|
|
|
/* Intrinsics conferred by race */
|
|
elf_abil[] = { { 4, &(HSleep_resistance), "awake", "tired" },
|
|
{ 0, 0, 0, 0 } },
|
|
|
|
orc_abil[] = { { 1, &(HPoison_resistance), "", "" },
|
|
{ 0, 0, 0, 0 } };
|
|
|
|
STATIC_DCL void NDECL(exerper);
|
|
STATIC_DCL void FDECL(postadjabil, (long *));
|
|
STATIC_DCL const struct innate *FDECL(check_innate_abil,(long *, long));
|
|
STATIC_DCL int FDECL(innately, (long *));
|
|
|
|
/* adjust an attribute; return TRUE if change is made, FALSE otherwise */
|
|
boolean
|
|
adjattrib(ndx, incr, msgflg)
|
|
int ndx, incr;
|
|
int msgflg; /* positive => no message, zero => message, and */
|
|
{ /* negative => conditional (msg if change made) */
|
|
if (Fixed_abil || !incr) return FALSE;
|
|
|
|
if ((ndx == A_INT || ndx == A_WIS)
|
|
&& uarmh && uarmh->otyp == DUNCE_CAP) {
|
|
if (msgflg == 0)
|
|
Your("cap constricts briefly, then relaxes again.");
|
|
return FALSE;
|
|
}
|
|
|
|
if (incr > 0) {
|
|
if ((AMAX(ndx) >= ATTRMAX(ndx)) && (ACURR(ndx) >= AMAX(ndx))) {
|
|
if (msgflg == 0 && flags.verbose)
|
|
pline("You're already as %s as you can get.",
|
|
plusattr[ndx]);
|
|
ABASE(ndx) = AMAX(ndx) = ATTRMAX(ndx); /* just in case */
|
|
return FALSE;
|
|
}
|
|
|
|
ABASE(ndx) += incr;
|
|
if(ABASE(ndx) > AMAX(ndx)) {
|
|
incr = ABASE(ndx) - AMAX(ndx);
|
|
AMAX(ndx) += incr;
|
|
if(AMAX(ndx) > ATTRMAX(ndx))
|
|
AMAX(ndx) = ATTRMAX(ndx);
|
|
ABASE(ndx) = AMAX(ndx);
|
|
}
|
|
} else {
|
|
if (ABASE(ndx) <= ATTRMIN(ndx)) {
|
|
if (msgflg == 0 && flags.verbose)
|
|
pline("You're already as %s as you can get.",
|
|
minusattr[ndx]);
|
|
ABASE(ndx) = ATTRMIN(ndx); /* just in case */
|
|
return FALSE;
|
|
}
|
|
|
|
ABASE(ndx) += incr;
|
|
if(ABASE(ndx) < ATTRMIN(ndx)) {
|
|
incr = ABASE(ndx) - ATTRMIN(ndx);
|
|
ABASE(ndx) = ATTRMIN(ndx);
|
|
AMAX(ndx) += incr;
|
|
if(AMAX(ndx) < ATTRMIN(ndx))
|
|
AMAX(ndx) = ATTRMIN(ndx);
|
|
}
|
|
}
|
|
if (msgflg <= 0)
|
|
You_feel("%s%s!",
|
|
(incr > 1 || incr < -1) ? "very ": "",
|
|
(incr > 0) ? plusattr[ndx] : minusattr[ndx]);
|
|
context.botl = 1;
|
|
if (moves > 1 && (ndx == A_STR || ndx == A_CON))
|
|
(void)encumber_msg();
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
gainstr(otmp, incr)
|
|
register struct obj *otmp;
|
|
register int incr;
|
|
{
|
|
int num = 1;
|
|
|
|
if(incr) num = incr;
|
|
else {
|
|
if(ABASE(A_STR) < 18) num = (rn2(4) ? 1 : rnd(6) );
|
|
else if (ABASE(A_STR) < STR18(85)) num = rnd(10);
|
|
}
|
|
(void) adjattrib(A_STR, (otmp && otmp->cursed) ? -num : num, TRUE);
|
|
}
|
|
|
|
void
|
|
losestr(num) /* may kill you; cause may be poison or monster like 'a' */
|
|
register int num;
|
|
{
|
|
int ustr = ABASE(A_STR) - num;
|
|
|
|
while(ustr < 3) {
|
|
++ustr;
|
|
--num;
|
|
if (Upolyd) {
|
|
u.mh -= 6;
|
|
u.mhmax -= 6;
|
|
} else {
|
|
u.uhp -= 6;
|
|
u.uhpmax -= 6;
|
|
}
|
|
}
|
|
(void) adjattrib(A_STR, -num, TRUE);
|
|
}
|
|
|
|
static const struct poison_effect_message {
|
|
void VDECL((*delivery_func), (const char *,...));
|
|
const char *effect_msg;
|
|
} poiseff[] = {
|
|
{ You_feel, "weaker" }, /* A_STR */
|
|
{ Your, "brain is on fire" }, /* A_INT */
|
|
{ Your, "judgement is impaired" }, /* A_WIS */
|
|
{ Your, "muscles won't obey you" }, /* A_DEX */
|
|
{ You_feel, "very sick" }, /* A_CON */
|
|
{ You, "break out in hives" } /* A_CHA */
|
|
};
|
|
|
|
/* feedback for attribute loss due to poisoning */
|
|
void
|
|
poisontell(typ, exclaim)
|
|
int typ; /* which attribute */
|
|
boolean exclaim; /* emphasis */
|
|
{
|
|
void VDECL((*func), (const char *,...)) = poiseff[typ].delivery_func;
|
|
|
|
(*func)("%s%c", poiseff[typ].effect_msg, exclaim ? '!' : '.');
|
|
}
|
|
|
|
/* called when an attack or trap has poisoned the hero (used to be in mon.c) */
|
|
void
|
|
poisoned(reason, typ, pkiller, fatal, thrown_weapon)
|
|
const char *reason, /* controls what messages we display */
|
|
*pkiller; /* for score+log file if fatal */
|
|
int typ, fatal;
|
|
boolean thrown_weapon; /* thrown weapons are less deadly */
|
|
{
|
|
int i, kprefix = KILLED_BY_AN;
|
|
|
|
/* inform player about being poisoned unless that's already been done;
|
|
"blast" has given a "blast of poison gas" message; "poison arrow",
|
|
"poison dart", etc have implicitly given poison messages too... */
|
|
if (strcmp(reason, "blast") && !strstri(reason, "poison")) {
|
|
boolean plural = (reason[strlen(reason) - 1] == 's') ? 1 : 0;
|
|
|
|
/* avoid "The" Orcus's sting was poisoned... */
|
|
pline("%s%s %s poisoned!",
|
|
isupper(*reason) ? "" : "The ", reason,
|
|
plural ? "were" : "was");
|
|
}
|
|
if (Poison_resistance) {
|
|
if (!strcmp(reason, "blast")) shieldeff(u.ux, u.uy);
|
|
pline_The("poison doesn't seem to affect you.");
|
|
return;
|
|
}
|
|
|
|
/* suppress killer prefix if it already has one */
|
|
i = name_to_mon(pkiller);
|
|
if (i >= LOW_PM && (mons[i].geno & G_UNIQ)) {
|
|
kprefix = KILLED_BY;
|
|
if (!type_is_pname(&mons[i])) pkiller = the(pkiller);
|
|
} else if (!strncmpi(pkiller, "the ", 4) ||
|
|
!strncmpi(pkiller, "an ", 3) ||
|
|
!strncmpi(pkiller, "a ", 2)) {
|
|
/*[ does this need a plural check too? ]*/
|
|
kprefix = KILLED_BY;
|
|
}
|
|
|
|
i = rn2(fatal + (thrown_weapon ? 20 : 0));
|
|
if (i == 0 && typ != A_CHA) {
|
|
u.uhp = -1;
|
|
pline_The("poison was deadly...");
|
|
} else if (i <= 5) {
|
|
/* check that a stat change was made */
|
|
if (adjattrib(typ, thrown_weapon ? -1 : -rn1(3,3), 1))
|
|
poisontell(typ, TRUE);
|
|
} else {
|
|
i = thrown_weapon ? rnd(6) : rn1(10,6);
|
|
losehp(i, pkiller, kprefix); /* poison damage */
|
|
}
|
|
|
|
if (u.uhp < 1) {
|
|
killer.format = kprefix;
|
|
Strcpy(killer.name, pkiller);
|
|
/* "Poisoned by a poisoned ___" is redundant */
|
|
done(strstri(pkiller, "poison") ? DIED : POISONING);
|
|
}
|
|
(void) encumber_msg();
|
|
}
|
|
|
|
void
|
|
change_luck(n)
|
|
register schar n;
|
|
{
|
|
u.uluck += n;
|
|
if (u.uluck < 0 && u.uluck < LUCKMIN) u.uluck = LUCKMIN;
|
|
if (u.uluck > 0 && u.uluck > LUCKMAX) u.uluck = LUCKMAX;
|
|
}
|
|
|
|
int
|
|
stone_luck(parameter)
|
|
boolean parameter; /* So I can't think up of a good name. So sue me. --KAA */
|
|
{
|
|
register struct obj *otmp;
|
|
register long bonchance = 0;
|
|
|
|
for (otmp = invent; otmp; otmp = otmp->nobj)
|
|
if (confers_luck(otmp)) {
|
|
if (otmp->cursed) bonchance -= otmp->quan;
|
|
else if (otmp->blessed) bonchance += otmp->quan;
|
|
else if (parameter) bonchance += otmp->quan;
|
|
}
|
|
|
|
return sgn((int)bonchance);
|
|
}
|
|
|
|
/* there has just been an inventory change affecting a luck-granting item */
|
|
void
|
|
set_moreluck()
|
|
{
|
|
int luckbon = stone_luck(TRUE);
|
|
|
|
if (!luckbon && !carrying(LUCKSTONE)) u.moreluck = 0;
|
|
else if (luckbon >= 0) u.moreluck = LUCKADD;
|
|
else u.moreluck = -LUCKADD;
|
|
}
|
|
|
|
void
|
|
restore_attrib()
|
|
{
|
|
int i;
|
|
|
|
for(i = 0; i < A_MAX; i++) { /* all temporary losses/gains */
|
|
|
|
if(ATEMP(i) && ATIME(i)) {
|
|
if(!(--(ATIME(i)))) { /* countdown for change */
|
|
ATEMP(i) += ATEMP(i) > 0 ? -1 : 1;
|
|
|
|
if(ATEMP(i)) /* reset timer */
|
|
ATIME(i) = 100 / ACURR(A_CON);
|
|
}
|
|
}
|
|
}
|
|
(void)encumber_msg();
|
|
}
|
|
|
|
#define AVAL 50 /* tune value for exercise gains */
|
|
|
|
void
|
|
exercise(i, inc_or_dec)
|
|
int i;
|
|
boolean inc_or_dec;
|
|
{
|
|
#ifdef DEBUG
|
|
pline("Exercise:");
|
|
#endif
|
|
if (i == A_INT || i == A_CHA) return; /* can't exercise these */
|
|
|
|
/* no physical exercise while polymorphed; the body's temporary */
|
|
if (Upolyd && i != A_WIS) return;
|
|
|
|
if(abs(AEXE(i)) < AVAL) {
|
|
/*
|
|
* Law of diminishing returns (Part I):
|
|
*
|
|
* Gain is harder at higher attribute values.
|
|
* 79% at "3" --> 0% at "18"
|
|
* Loss is even at all levels (50%).
|
|
*
|
|
* Note: *YES* ACURR is the right one to use.
|
|
*/
|
|
AEXE(i) += (inc_or_dec) ? (rn2(19) > ACURR(i)) : -rn2(2);
|
|
#ifdef DEBUG
|
|
pline("%s, %s AEXE = %d",
|
|
(i == A_STR) ? "Str" : (i == A_WIS) ? "Wis" :
|
|
(i == A_DEX) ? "Dex" : "Con",
|
|
(inc_or_dec) ? "inc" : "dec", AEXE(i));
|
|
#endif
|
|
}
|
|
if (moves > 0 && (i == A_STR || i == A_CON)) (void)encumber_msg();
|
|
}
|
|
|
|
/* hunger values - from eat.c */
|
|
#define SATIATED 0
|
|
#define NOT_HUNGRY 1
|
|
#define HUNGRY 2
|
|
#define WEAK 3
|
|
#define FAINTING 4
|
|
#define FAINTED 5
|
|
#define STARVED 6
|
|
|
|
STATIC_OVL void
|
|
exerper()
|
|
{
|
|
if(!(moves % 10)) {
|
|
/* Hunger Checks */
|
|
|
|
int hs = (u.uhunger > 1000) ? SATIATED :
|
|
(u.uhunger > 150) ? NOT_HUNGRY :
|
|
(u.uhunger > 50) ? HUNGRY :
|
|
(u.uhunger > 0) ? WEAK : FAINTING;
|
|
|
|
#ifdef DEBUG
|
|
pline("exerper: Hunger checks");
|
|
#endif
|
|
switch (hs) {
|
|
case SATIATED: exercise(A_DEX, FALSE);
|
|
if (Role_if(PM_MONK))
|
|
exercise(A_WIS, FALSE);
|
|
break;
|
|
case NOT_HUNGRY: exercise(A_CON, TRUE); break;
|
|
case WEAK: exercise(A_STR, FALSE);
|
|
if (Role_if(PM_MONK)) /* fasting */
|
|
exercise(A_WIS, TRUE);
|
|
break;
|
|
case FAINTING:
|
|
case FAINTED: exercise(A_CON, FALSE); break;
|
|
}
|
|
|
|
/* Encumberance Checks */
|
|
#ifdef DEBUG
|
|
pline("exerper: Encumber checks");
|
|
#endif
|
|
switch (near_capacity()) {
|
|
case MOD_ENCUMBER: exercise(A_STR, TRUE); break;
|
|
case HVY_ENCUMBER: exercise(A_STR, TRUE);
|
|
exercise(A_DEX, FALSE); break;
|
|
case EXT_ENCUMBER: exercise(A_DEX, FALSE);
|
|
exercise(A_CON, FALSE); break;
|
|
}
|
|
|
|
}
|
|
|
|
/* status checks */
|
|
if(!(moves % 5)) {
|
|
#ifdef DEBUG
|
|
pline("exerper: Status checks");
|
|
#endif
|
|
if ((HClairvoyant & (INTRINSIC|TIMEOUT)) &&
|
|
!BClairvoyant) exercise(A_WIS, TRUE);
|
|
if (HRegeneration) exercise(A_STR, TRUE);
|
|
|
|
if(Sick || Vomiting) exercise(A_CON, FALSE);
|
|
if(Confusion || Hallucination) exercise(A_WIS, FALSE);
|
|
if((Wounded_legs
|
|
#ifdef STEED
|
|
&& !u.usteed
|
|
#endif
|
|
) || Fumbling || HStun) exercise(A_DEX, FALSE);
|
|
}
|
|
}
|
|
|
|
void
|
|
exerchk()
|
|
{
|
|
int i, mod_val;
|
|
|
|
/* Check out the periodic accumulations */
|
|
exerper();
|
|
|
|
#ifdef DEBUG
|
|
if(moves >= context.next_attrib_check)
|
|
pline("exerchk: ready to test. multi = %d.", multi);
|
|
#endif
|
|
/* Are we ready for a test? */
|
|
if(moves >= context.next_attrib_check && !multi) {
|
|
#ifdef DEBUG
|
|
pline("exerchk: testing.");
|
|
#endif
|
|
/*
|
|
* Law of diminishing returns (Part II):
|
|
*
|
|
* The effects of "exercise" and "abuse" wear
|
|
* off over time. Even if you *don't* get an
|
|
* increase/decrease, you lose some of the
|
|
* accumulated effects.
|
|
*/
|
|
for(i = 0; i < A_MAX; AEXE(i++) /= 2) {
|
|
|
|
if(ABASE(i) >= 18 || !AEXE(i)) continue;
|
|
if(i == A_INT || i == A_CHA) continue;/* can't exercise these */
|
|
|
|
#ifdef DEBUG
|
|
pline("exerchk: testing %s (%d).",
|
|
(i == A_STR) ? "Str" : (i == A_WIS) ? "Wis" :
|
|
(i == A_DEX) ? "Dex" : "Con", AEXE(i));
|
|
#endif
|
|
/*
|
|
* Law of diminishing returns (Part III):
|
|
*
|
|
* You don't *always* gain by exercising.
|
|
* [MRS 92/10/28 - Treat Wisdom specially for balance.]
|
|
*/
|
|
if(rn2(AVAL) > ((i != A_WIS) ? abs(AEXE(i)*2/3) : abs(AEXE(i))))
|
|
continue;
|
|
mod_val = sgn(AEXE(i));
|
|
|
|
#ifdef DEBUG
|
|
pline("exerchk: changing %d.", i);
|
|
#endif
|
|
if(adjattrib(i, mod_val, -1)) {
|
|
#ifdef DEBUG
|
|
pline("exerchk: changed %d.", i);
|
|
#endif
|
|
/* if you actually changed an attrib - zero accumulation */
|
|
AEXE(i) = 0;
|
|
/* then print an explanation */
|
|
switch(i) {
|
|
case A_STR: You((mod_val >0) ?
|
|
"must have been exercising." :
|
|
"must have been abusing your body.");
|
|
break;
|
|
case A_WIS: You((mod_val >0) ?
|
|
"must have been very observant." :
|
|
"haven't been paying attention.");
|
|
break;
|
|
case A_DEX: You((mod_val >0) ?
|
|
"must have been working on your reflexes." :
|
|
"haven't been working on reflexes lately.");
|
|
break;
|
|
case A_CON: You((mod_val >0) ?
|
|
"must be leading a healthy life-style." :
|
|
"haven't been watching your health.");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
context.next_attrib_check += rn1(200,800);
|
|
#ifdef DEBUG
|
|
pline("exerchk: next check at %ld.", context.next_attrib_check);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void
|
|
init_attr(np)
|
|
register int np;
|
|
{
|
|
register int i, x, tryct;
|
|
|
|
|
|
for(i = 0; i < A_MAX; i++) {
|
|
ABASE(i) = AMAX(i) = urole.attrbase[i];
|
|
ATEMP(i) = ATIME(i) = 0;
|
|
np -= urole.attrbase[i];
|
|
}
|
|
|
|
tryct = 0;
|
|
while(np > 0 && tryct < 100) {
|
|
|
|
x = rn2(100);
|
|
for (i = 0; (i < A_MAX) && ((x -= urole.attrdist[i]) > 0); i++) ;
|
|
if(i >= A_MAX) continue; /* impossible */
|
|
|
|
if(ABASE(i) >= ATTRMAX(i)) {
|
|
|
|
tryct++;
|
|
continue;
|
|
}
|
|
tryct = 0;
|
|
ABASE(i)++;
|
|
AMAX(i)++;
|
|
np--;
|
|
}
|
|
|
|
tryct = 0;
|
|
while(np < 0 && tryct < 100) { /* for redistribution */
|
|
|
|
x = rn2(100);
|
|
for (i = 0; (i < A_MAX) && ((x -= urole.attrdist[i]) > 0); i++) ;
|
|
if(i >= A_MAX) continue; /* impossible */
|
|
|
|
if(ABASE(i) <= ATTRMIN(i)) {
|
|
|
|
tryct++;
|
|
continue;
|
|
}
|
|
tryct = 0;
|
|
ABASE(i)--;
|
|
AMAX(i)--;
|
|
np++;
|
|
}
|
|
}
|
|
|
|
void
|
|
redist_attr()
|
|
{
|
|
register int i, tmp;
|
|
|
|
for(i = 0; i < A_MAX; i++) {
|
|
if (i==A_INT || i==A_WIS) continue;
|
|
/* Polymorphing doesn't change your mind */
|
|
tmp = AMAX(i);
|
|
AMAX(i) += (rn2(5)-2);
|
|
if (AMAX(i) > ATTRMAX(i)) AMAX(i) = ATTRMAX(i);
|
|
if (AMAX(i) < ATTRMIN(i)) AMAX(i) = ATTRMIN(i);
|
|
ABASE(i) = ABASE(i) * AMAX(i) / tmp;
|
|
/* ABASE(i) > ATTRMAX(i) is impossible */
|
|
if (ABASE(i) < ATTRMIN(i)) ABASE(i) = ATTRMIN(i);
|
|
}
|
|
(void)encumber_msg();
|
|
}
|
|
|
|
STATIC_OVL
|
|
void
|
|
postadjabil(ability)
|
|
long *ability;
|
|
{
|
|
if (!ability) return;
|
|
if (ability == &(HWarning) || ability == &(HSee_invisible))
|
|
see_monsters();
|
|
}
|
|
|
|
STATIC_OVL const struct innate *
|
|
check_innate_abil(ability, frommask)
|
|
long *ability;
|
|
long frommask;
|
|
{
|
|
const struct innate *abil;
|
|
|
|
if (frommask == FROMEXPER)
|
|
switch (Role_switch) {
|
|
case PM_ARCHEOLOGIST: abil = arc_abil; break;
|
|
case PM_BARBARIAN: abil = bar_abil; break;
|
|
case PM_CAVEMAN: abil = cav_abil; break;
|
|
case PM_HEALER: abil = hea_abil; break;
|
|
case PM_KNIGHT: abil = kni_abil; break;
|
|
case PM_MONK: abil = mon_abil; break;
|
|
case PM_PRIEST: abil = pri_abil; break;
|
|
case PM_RANGER: abil = ran_abil; break;
|
|
case PM_ROGUE: abil = rog_abil; break;
|
|
case PM_SAMURAI: abil = sam_abil; break;
|
|
#ifdef TOURIST
|
|
case PM_TOURIST: abil = tou_abil; break;
|
|
#endif
|
|
case PM_VALKYRIE: abil = val_abil; break;
|
|
case PM_WIZARD: abil = wiz_abil; break;
|
|
default: abil = 0; break;
|
|
}
|
|
else if (frommask == FROMRACE)
|
|
switch (Race_switch) {
|
|
case PM_ELF: abil = elf_abil; break;
|
|
case PM_ORC: abil = orc_abil; break;
|
|
case PM_HUMAN:
|
|
case PM_DWARF:
|
|
case PM_GNOME:
|
|
default: abil = 0; break;
|
|
}
|
|
|
|
while (abil && abil->ability) {
|
|
if((abil->ability == ability) && (u.ulevel >= abil->ulevel))
|
|
return abil;
|
|
abil++;
|
|
}
|
|
return (struct innate *)0;
|
|
}
|
|
|
|
/*
|
|
* returns 1 if FROMRACE or FROMEXPER and exper level == 1
|
|
* returns 2 if FROMEXPER and exper level > 1
|
|
* otherwise returns 0
|
|
*/
|
|
STATIC_OVL int
|
|
innately(ability)
|
|
long *ability;
|
|
{
|
|
const struct innate *iptr;
|
|
if ((iptr=check_innate_abil(ability, FROMRACE)))
|
|
return 1;
|
|
else if ((iptr=check_innate_abil(ability, FROMEXPER))) {
|
|
if (iptr->ulevel == 1) return 1;
|
|
return 2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
is_innate(propidx)
|
|
int propidx;
|
|
{
|
|
return innately(&u.uprops[propidx].intrinsic);
|
|
}
|
|
|
|
char *
|
|
from_what(propidx)
|
|
int propidx;
|
|
{
|
|
struct obj *obj = (struct obj *)0;
|
|
int innate = is_innate(propidx);
|
|
|
|
static char buf[BUFSZ];
|
|
|
|
buf[0] = '\0';
|
|
/*
|
|
* Restrict the source of the attributes just to debug mode for now
|
|
*/
|
|
#ifdef WIZARD
|
|
if (wizard) {
|
|
if (innate == 2)
|
|
Strcpy(buf, " because of your experience");
|
|
else if (innate == 1)
|
|
Strcpy(buf, " innately");
|
|
else if (wizard && (obj = what_gives(&u.uprops[propidx].extrinsic)))
|
|
Sprintf(buf, " because of %s",
|
|
(obj->oartifact) ? bare_artifactname(obj) : yname(obj));
|
|
}
|
|
#endif
|
|
return buf;
|
|
}
|
|
|
|
void
|
|
adjabil(oldlevel,newlevel)
|
|
int oldlevel, newlevel;
|
|
{
|
|
register const struct innate *abil, *rabil;
|
|
long mask = FROMEXPER;
|
|
|
|
|
|
switch (Role_switch) {
|
|
case PM_ARCHEOLOGIST: abil = arc_abil; break;
|
|
case PM_BARBARIAN: abil = bar_abil; break;
|
|
case PM_CAVEMAN: abil = cav_abil; break;
|
|
case PM_HEALER: abil = hea_abil; break;
|
|
case PM_KNIGHT: abil = kni_abil; break;
|
|
case PM_MONK: abil = mon_abil; break;
|
|
case PM_PRIEST: abil = pri_abil; break;
|
|
case PM_RANGER: abil = ran_abil; break;
|
|
case PM_ROGUE: abil = rog_abil; break;
|
|
case PM_SAMURAI: abil = sam_abil; break;
|
|
#ifdef TOURIST
|
|
case PM_TOURIST: abil = tou_abil; break;
|
|
#endif
|
|
case PM_VALKYRIE: abil = val_abil; break;
|
|
case PM_WIZARD: abil = wiz_abil; break;
|
|
default: abil = 0; break;
|
|
}
|
|
|
|
switch (Race_switch) {
|
|
case PM_ELF: rabil = elf_abil; break;
|
|
case PM_ORC: rabil = orc_abil; break;
|
|
case PM_HUMAN:
|
|
case PM_DWARF:
|
|
case PM_GNOME:
|
|
default: rabil = 0; break;
|
|
}
|
|
|
|
while (abil || rabil) {
|
|
long prevabil;
|
|
/* Have we finished with the intrinsics list? */
|
|
if (!abil || !abil->ability) {
|
|
/* Try the race intrinsics */
|
|
if (!rabil || !rabil->ability) break;
|
|
abil = rabil;
|
|
rabil = 0;
|
|
mask = FROMRACE;
|
|
}
|
|
prevabil = *(abil->ability);
|
|
if(oldlevel < abil->ulevel && newlevel >= abil->ulevel) {
|
|
/* Abilities gained at level 1 can never be lost
|
|
* via level loss, only via means that remove _any_
|
|
* sort of ability. A "gain" of such an ability from
|
|
* an outside source is devoid of meaning, so we set
|
|
* FROMOUTSIDE to avoid such gains.
|
|
*/
|
|
if (abil->ulevel == 1)
|
|
*(abil->ability) |= (mask|FROMOUTSIDE);
|
|
else
|
|
*(abil->ability) |= mask;
|
|
if(!(*(abil->ability) & INTRINSIC & ~mask)) {
|
|
if(*(abil->gainstr))
|
|
You_feel("%s!", abil->gainstr);
|
|
}
|
|
} else if (oldlevel >= abil->ulevel && newlevel < abil->ulevel) {
|
|
*(abil->ability) &= ~mask;
|
|
if(!(*(abil->ability) & INTRINSIC)) {
|
|
if(*(abil->losestr))
|
|
You_feel("%s!", abil->losestr);
|
|
else if(*(abil->gainstr))
|
|
You_feel("less %s!", abil->gainstr);
|
|
}
|
|
}
|
|
if (prevabil != *(abil->ability)) /* it changed */
|
|
postadjabil(abil->ability);
|
|
abil++;
|
|
}
|
|
|
|
if (oldlevel > 0) {
|
|
if (newlevel > oldlevel)
|
|
add_weapon_skill(newlevel - oldlevel);
|
|
else
|
|
lose_weapon_skill(oldlevel - newlevel);
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
newhp()
|
|
{
|
|
int hp, conplus;
|
|
|
|
if (u.ulevel == 0) {
|
|
/* Initialize hit points */
|
|
hp = urole.hpadv.infix + urace.hpadv.infix;
|
|
if (urole.hpadv.inrnd > 0) hp += rnd(urole.hpadv.inrnd);
|
|
if (urace.hpadv.inrnd > 0) hp += rnd(urace.hpadv.inrnd);
|
|
if (moves <= 1L) { /* initial hero; skip for polyself to new man */
|
|
/* Initialize alignment stuff */
|
|
u.ualign.type = aligns[flags.initalign].value;
|
|
u.ualign.record = urole.initrecord;
|
|
}
|
|
/* no Con adjustment for initial hit points */
|
|
} else {
|
|
if (u.ulevel < urole.xlev) {
|
|
hp = urole.hpadv.lofix + urace.hpadv.lofix;
|
|
if (urole.hpadv.lornd > 0) hp += rnd(urole.hpadv.lornd);
|
|
if (urace.hpadv.lornd > 0) hp += rnd(urace.hpadv.lornd);
|
|
} else {
|
|
hp = urole.hpadv.hifix + urace.hpadv.hifix;
|
|
if (urole.hpadv.hirnd > 0) hp += rnd(urole.hpadv.hirnd);
|
|
if (urace.hpadv.hirnd > 0) hp += rnd(urace.hpadv.hirnd);
|
|
}
|
|
if (ACURR(A_CON) <= 3) conplus = -2;
|
|
else if (ACURR(A_CON) <= 6) conplus = -1;
|
|
else if (ACURR(A_CON) <= 14) conplus = 0;
|
|
else if (ACURR(A_CON) <= 16) conplus = 1;
|
|
else if (ACURR(A_CON) == 17) conplus = 2;
|
|
else if (ACURR(A_CON) == 18) conplus = 3;
|
|
else conplus = 4;
|
|
hp += conplus;
|
|
}
|
|
if (hp <= 0) hp = 1;
|
|
if (u.ulevel < MAXULEV) u.uhpinc[u.ulevel] = (xchar)hp;
|
|
return hp;
|
|
}
|
|
|
|
schar
|
|
acurr(x)
|
|
int x;
|
|
{
|
|
register int tmp = (u.abon.a[x] + u.atemp.a[x] + u.acurr.a[x]);
|
|
|
|
if (x == A_STR) {
|
|
if (uarmg && uarmg->otyp == GAUNTLETS_OF_POWER) return(125);
|
|
#ifdef WIN32_BUG
|
|
else return(x=((tmp >= 125) ? 125 : (tmp <= 3) ? 3 : tmp));
|
|
#else
|
|
else return((schar)((tmp >= 125) ? 125 : (tmp <= 3) ? 3 : tmp));
|
|
#endif
|
|
} else if (x == A_CHA) {
|
|
if (tmp < 18 && (youmonst.data->mlet == S_NYMPH ||
|
|
u.umonnum==PM_SUCCUBUS || u.umonnum == PM_INCUBUS))
|
|
return 18;
|
|
} else if (x == A_INT || x == A_WIS) {
|
|
/* yes, this may raise int/wis if player is sufficiently
|
|
* stupid. there are lower levels of cognition than "dunce".
|
|
*/
|
|
if (uarmh && uarmh->otyp == DUNCE_CAP) return(6);
|
|
}
|
|
#ifdef WIN32_BUG
|
|
return(x=((tmp >= 25) ? 25 : (tmp <= 3) ? 3 : tmp));
|
|
#else
|
|
return((schar)((tmp >= 25) ? 25 : (tmp <= 3) ? 3 : tmp));
|
|
#endif
|
|
}
|
|
|
|
/* condense clumsy ACURR(A_STR) value into value that fits into game formulas
|
|
*/
|
|
schar
|
|
acurrstr()
|
|
{
|
|
register int str = ACURR(A_STR);
|
|
|
|
if (str <= 18) return((schar)str);
|
|
if (str <= 121) return((schar)(19 + str / 50)); /* map to 19-21 */
|
|
else return((schar)(str - 100));
|
|
}
|
|
|
|
/* avoid possible problems with alignment overflow, and provide a centralized
|
|
* location for any future alignment limits
|
|
*/
|
|
void
|
|
adjalign(n)
|
|
register int n;
|
|
{
|
|
register int newalign = u.ualign.record + n;
|
|
|
|
if(n < 0) {
|
|
if(newalign < u.ualign.record)
|
|
u.ualign.record = newalign;
|
|
} else
|
|
if(newalign > u.ualign.record) {
|
|
u.ualign.record = newalign;
|
|
if(u.ualign.record > ALIGNLIM)
|
|
u.ualign.record = ALIGNLIM;
|
|
}
|
|
}
|
|
|
|
/*attrib.c*/
|