diff --git a/doc/fixes37.0 b/doc/fixes37.0 index 5eadb3ae3..97c416874 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -1010,6 +1010,7 @@ using 'f' while quiver is empty and 'autoquiver' is Off when wielding a 3.6's tribute: add one new passage to Sourcery, three to Small Gods, one to Lords and Ladies, two to Soul Music, three to Interesting Times monsters can see and remember hero resistances +monsters can gain resistances by eating corpses Platform- and/or Interface-Specific New Features diff --git a/include/extern.h b/include/extern.h index c9d666210..cd293ce98 100644 --- a/include/extern.h +++ b/include/extern.h @@ -655,6 +655,7 @@ extern void morehungry(int); extern void lesshungry(int); extern boolean is_fainted(void); extern void reset_faint(void); +extern int corpse_intrinsic(struct permonst *); extern void violated_vegetarian(void); extern void newuhs(boolean); extern struct obj *floorfood(const char *, int); @@ -665,6 +666,7 @@ extern void food_substitution(struct obj *, struct obj *); extern void eating_conducts(struct permonst *); extern int eat_brains(struct monst *, struct monst *, boolean, int *); extern void fix_petrification(void); +extern boolean should_givit(int, struct permonst *); extern void consume_oeaten(struct obj *, int); extern boolean maybe_finished_meal(boolean); extern void set_tin_variety(struct obj *, int); @@ -1392,6 +1394,7 @@ extern int movemon(void); extern int meatmetal(struct monst *); extern int meatobj(struct monst *); extern int meatcorpse(struct monst *); +extern void mon_givit(struct monst *, struct permonst *); extern void mpickgold(struct monst *); extern boolean mpickstuff(struct monst *, const char *); extern int curr_mon_load(struct monst *); diff --git a/include/mondata.h b/include/mondata.h index 1ab07c6e3..853ce6846 100644 --- a/include/mondata.h +++ b/include/mondata.h @@ -10,22 +10,24 @@ #define pm_resistance(ptr, typ) (((ptr)->mresists & (typ)) != 0) +#define mon_resistancebits(mon) \ + ((mon)->data->mresists | (mon)->mextrinsics | (mon)->mintrinsics) #define resists_fire(mon) \ - ((((mon)->data->mresists | (mon)->mextrinsics) & MR_FIRE) != 0) + ((mon_resistancebits(mon) & MR_FIRE) != 0) #define resists_cold(mon) \ - ((((mon)->data->mresists | (mon)->mextrinsics) & MR_COLD) != 0) + ((mon_resistancebits(mon) & MR_COLD) != 0) #define resists_sleep(mon) \ - ((((mon)->data->mresists | (mon)->mextrinsics) & MR_SLEEP) != 0) + ((mon_resistancebits(mon) & MR_SLEEP) != 0) #define resists_disint(mon) \ - ((((mon)->data->mresists | (mon)->mextrinsics) & MR_DISINT) != 0) + ((mon_resistancebits(mon) & MR_DISINT) != 0) #define resists_elec(mon) \ - ((((mon)->data->mresists | (mon)->mextrinsics) & MR_ELEC) != 0) + ((mon_resistancebits(mon) & MR_ELEC) != 0) #define resists_poison(mon) \ - ((((mon)->data->mresists | (mon)->mextrinsics) & MR_POISON) != 0) + ((mon_resistancebits(mon) & MR_POISON) != 0) #define resists_acid(mon) \ - ((((mon)->data->mresists | (mon)->mextrinsics) & MR_ACID) != 0) + ((mon_resistancebits(mon) & MR_ACID) != 0) #define resists_ston(mon) \ - ((((mon)->data->mresists | (mon)->mextrinsics) & MR_STONE) != 0) + ((mon_resistancebits(mon) & MR_STONE) != 0) #define immune_poisongas(ptr) ((ptr) == &mons[PM_HEZROU]) diff --git a/include/monst.h b/include/monst.h index 30dd4ceac..20643ede0 100644 --- a/include/monst.h +++ b/include/monst.h @@ -106,6 +106,7 @@ struct monst { uchar m_ap_type; /* what mappearance is describing, m_ap_types */ schar mtame; /* level of tameness, implies peaceful */ + unsigned short mintrinsics; /* low 8 correspond to mresists */ unsigned short mextrinsics; /* low 8 correspond to mresists */ unsigned long seen_resistance; /* M_SEEN_x; saw you resist an effect */ int mspec_used; /* monster's special ability attack timeout */ diff --git a/include/patchlevel.h b/include/patchlevel.h index 9b4b90091..3aee99c96 100644 --- a/include/patchlevel.h +++ b/include/patchlevel.h @@ -17,7 +17,7 @@ * Incrementing EDITLEVEL can be used to force invalidation of old bones * and save files. */ -#define EDITLEVEL 34 +#define EDITLEVEL 35 /* * Development status possibilities. diff --git a/src/dogmove.c b/src/dogmove.c index 02327bd5e..504414227 100644 --- a/src/dogmove.c +++ b/src/dogmove.c @@ -204,7 +204,7 @@ dog_eat(struct monst *mtmp, { register struct edog *edog = EDOG(mtmp); boolean poly, grow, heal, eyes, slimer, deadmimic; - int nutrit, res; + int nutrit, res, corpsenm; long oprice; char objnambuf[BUFSZ]; @@ -221,6 +221,7 @@ dog_eat(struct monst *mtmp, grow = mlevelgain(obj); heal = mhealup(obj); eyes = (obj->otyp == CARROT); + corpsenm = (obj->otyp == CORPSE ? obj->corpsenm : NON_PM); if (devour) { if (mtmp->meating > 1) @@ -347,6 +348,8 @@ dog_eat(struct monst *mtmp, mcureblindness(mtmp, canseemon(mtmp)); if (deadmimic) quickmimic(mtmp); + if (corpsenm != NON_PM) + mon_givit(mtmp, &mons[corpsenm]); return 1; } diff --git a/src/eat.c b/src/eat.c index 2125f9a13..27dd5f7e3 100644 --- a/src/eat.c +++ b/src/eat.c @@ -802,15 +802,15 @@ intrinsic_possible(int type, register struct permonst *ptr) return res; } -/* givit() tries to give you an intrinsic based on the monster's level - * and what type of intrinsic it is trying to give you. +/* The "do we or do we not give the intrinsic" logic from givit(), extracted + * into its own function. Depends on the monster's level and the type of + * intrinsic it is trying to give you. */ -static void -givit(int type, register struct permonst *ptr) +boolean +should_givit(int type, struct permonst *ptr) { - register int chance; + int chance; - debugpline1("Attempting to give intrinsic %d", type); /* some intrinsics are easier to get than others */ switch (type) { case POISON_RES: @@ -834,8 +834,19 @@ givit(int type, register struct permonst *ptr) break; } - if (ptr->mlevel <= rn2(chance)) - return; /* failed die roll */ + return (ptr->mlevel > rn2(chance)); +} + +/* givit() tries to give you an intrinsic based on the monster's level + * and what type of intrinsic it is trying to give you. + */ +static void +givit(int type, register struct permonst *ptr) +{ + debugpline1("Attempting to give intrinsic %d", type); + + if (!should_givit(type, ptr)) + return; switch (type) { case FIRE_RES: @@ -1094,8 +1105,6 @@ cpostfx(int pm) /* possibly convey an intrinsic */ if (check_intrinsics) { struct permonst *ptr = &mons[pm]; - boolean conveys_STR = is_giant(ptr); - int i, count; if (dmgtype(ptr, AD_STUN) || dmgtype(ptr, AD_HALU) || pm == PM_VIOLET_FUNGUS) { @@ -1108,36 +1117,8 @@ cpostfx(int pm) if (attacktype(ptr, AT_MAGC) || pm == PM_NEWT) eye_of_newt_buzz(); - /* Check the monster for all of the intrinsics. If this - * monster can give more than one, pick one to try to give - * from among all it can give. - * - * Strength from giants is now treated like an intrinsic - * rather than being given unconditionally. - */ - count = 0; /* number of possible intrinsics */ - tmp = 0; /* which one we will try to give */ - if (conveys_STR) { - count = 1; - tmp = -1; /* use -1 as fake prop index for STR */ - debugpline1("\"Intrinsic\" strength, %d", tmp); - } - for (i = 1; i <= LAST_PROP; i++) { - if (!intrinsic_possible(i, ptr)) - continue; - ++count; - /* a 1 in count chance of replacing the old choice - with this one, and a count-1 in count chance - of keeping the old choice (note that 1 in 1 and - 0 in 1 are what we want for the first candidate) */ - if (!rn2(count)) { - debugpline2("Intrinsic %d replacing %d", i, tmp); - tmp = i; - } - } - /* if strength is the only candidate, give it 50% chance */ - if (conveys_STR && count == 1 && !rn2(2)) - tmp = 0; + tmp = corpse_intrinsic(ptr); + /* if something was chosen, give it now (givit() might fail) */ if (tmp == -1) gainstr((struct obj *) 0, 0, TRUE); @@ -1154,6 +1135,49 @@ cpostfx(int pm) RESTORE_WARNING_FORMAT_NONLITERAL +/* Choose (one of) the intrinsics granted by a corpse, and return it. + * If this corpse gives no intrinsics, return 0. + * For the special not-real-prop cases of strength gain from giants + * return fake prop value of -1. + * Non-deterministic; should only be called once per corpse. + */ +int +corpse_intrinsic(struct permonst *ptr) +{ + /* Check the monster for all of the intrinsics. If this + * monster can give more than one, pick one to try to give + * from among all it can give. + */ + boolean conveys_STR = is_giant(ptr); + int i; + int count = 0; /* number of possible intrinsics */ + int prop = 0; /* which one we will try to give */ + + if (conveys_STR) { + count = 1; + prop = -1; /* use -1 as fake prop index for STR */ + debugpline1("\"Intrinsic\" strength, %d", prop); + } + for (i = 1; i <= LAST_PROP; i++) { + if (!intrinsic_possible(i, ptr)) + continue; + ++count; + /* a 1 in count chance of replacing the old choice + with this one, and a count-1 in count chance + of keeping the old choice (note that 1 in 1 and + 0 in 1 are what we want for the first candidate) */ + if (!rn2(count)) { + debugpline2("Intrinsic %d replacing %d", i, prop); + prop = i; + } + } + /* if strength is the only candidate, give it 50% chance */ + if (conveys_STR && count == 1 && !rn2(2)) + prop = 0; + + return prop; +} + void violated_vegetarian(void) { diff --git a/src/mon.c b/src/mon.c index b63a3ec91..0578921f9 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1247,6 +1247,8 @@ meatobj(struct monst* mtmp) /* for gelatinous cubes */ if (otmp->oclass == SCROLL_CLASS && objdescr_is(otmp, "YUM YUM")) pline("Yum%c", otmp->blessed ? '!' : '.'); + if (otmp->otyp == CORPSE) + mon_givit(mtmp, &mons[otmp->corpsenm]); } else { if (flags.verbose) You_hear("a slurping sound."); @@ -1360,6 +1362,8 @@ meatcorpse(struct monst* mtmp) /* for purple worms and other voracious monsters You_hear("a masticating sound."); } + mon_givit(mtmp, &mons[otmp->corpsenm]); + /* [should include quickmimic but can't handle that unless this gets changed to set mtmp->meating] */ poly = polyfodder(otmp); @@ -1391,6 +1395,67 @@ meatcorpse(struct monst* mtmp) /* for purple worms and other voracious monsters return 0; } +/* Maybe give an intrinsic to a monster from eating a corpse that confers it. */ +void +mon_givit(struct monst* mtmp, struct permonst* ptr) +{ + int prop = corpse_intrinsic(ptr); + boolean vis = canseemon(mtmp); + const char* msg = NULL; + unsigned short intrinsic = 0; /* MR_* constant */ + + if (prop == 0) + return; /* no intrinsic from this corpse */ + + if (!should_givit(prop, ptr)) + return; /* failed die roll */ + + /* Pets don't have all the fields that the hero does, so they can't get all + * the same intrinsics. If it happens to choose strength gain or teleport + * control or whatever, ignore it. */ + switch (prop) { + case FIRE_RES: + intrinsic = MR_FIRE; + msg = "%s shivers slightly."; + break; + case COLD_RES: + intrinsic = MR_COLD; + msg = "%s looks quite warm."; + break; + case SLEEP_RES: + intrinsic = MR_SLEEP; + msg = "%s looks wide awake."; + break; + case DISINT_RES: + intrinsic = MR_DISINT; + msg = "%s looks very firm."; + break; + case SHOCK_RES: + intrinsic = MR_ELEC; + msg = "%s crackles with static electricity."; + break; + case POISON_RES: + intrinsic = MR_POISON; + msg = "%s looks healthy."; + break; + default: + break; + } + + /* Don't give message if it already had this property intrinsically, but + * still do grant the intrinsic if it only had it from mresists. + * Do print the message if it only had this property extrinsically, which is + * why mon_resistancebits isn't used here. */ + if ((mtmp->data->mresists | mtmp->mintrinsics) & intrinsic) + msg = (const char *) 0; + + if (intrinsic) + mtmp->mintrinsics |= intrinsic; + + if (vis && msg) + pline(msg, Monnam(mtmp)); +} + void mpickgold(register struct monst* mtmp) {