diff --git a/src/attrib.c b/src/attrib.c new file mode 100644 index 000000000..0a7421f79 --- /dev/null +++ b/src/attrib.c @@ -0,0 +1,709 @@ +/* SCCS Id: @(#)attrib.c 3.3 2000/05/17 */ +/* Copyright 1988, 1989, 1990, 1992, M. Stephenson */ +/* NetHack may be freely redistributed. See license for details. */ + +/* attribute modification routines. */ + +#include "hack.h" +#include "artifact.h" + +/* #define DEBUG */ /* uncomment for debugging info */ + +#ifdef OVLB + + /* part of the output on gain or loss of attribute */ +static +const char *plusattr[] = { + "strong", "smart", "wise", "agile", "tough", "charismatic" +}, + *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 long next_check = 600L; /* arbitrary first setting */ +STATIC_DCL void NDECL(exerper); + +/* 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]); + flags.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); +} + +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 (otmp->otyp == LUCKSTONE + || (otmp->oartifact && spec_ability(otmp, SPFX_LUCK))) { + 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; +} + +#endif /* OVLB */ +#ifdef OVL1 + +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(); +} + +#endif /* OVL1 */ +#ifdef OVLB + +#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); break; + case NOT_HUNGRY: exercise(A_CON, TRUE); break; + case WEAK: exercise(A_STR, FALSE); 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 || Fumbling || HStun) exercise(A_DEX, FALSE); + } +} + +void +exerchk() +{ + int i, mod_val; + + /* Check out the periodic accumulations */ + exerper(); + +#ifdef DEBUG + if(moves >= next_check) + pline("exerchk: ready to test. multi = %d.", multi); +#endif + /* Are we ready for a test? */ + if(moves >= next_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; + } + } + } + next_check += rn1(200,800); +#ifdef DEBUG + pline("exerchk: next check at %ld.", next_check); +#endif + } +} + +/* next_check will otherwise have its initial 600L after a game restore */ +void +reset_attribute_clock() +{ + if (moves > 600L) next_check = moves + rn1(50,800); +} + + +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(); +} + +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) { + /* 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; + } + + 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); + } + } + 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); + + /* Initialize alignment stuff */ + u.ualign.type = aligns[flags.initalign].value; + u.ualign.record = urole.initrecord; + + return hp; + } 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; + return((hp <= 0) ? 1 : hp); +} + +#endif /* OVLB */ +#ifdef OVL0 + +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)); +} + +#endif /* OVL0 */ +#ifdef OVL2 + +/* 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; + } +} + +#endif /* OVL2 */ + +/*attrib.c*/