Monsters can gain resistances by eating corpses

This is based on both the EvilHack implementation by
k21971 <keith.simpson1971@gmail.com>, and xNetHack
implementation by copperwater <aosdict@gmail.com>.
This commit is contained in:
Pasi Kallinen
2021-05-23 19:02:38 +03:00
parent 20cbadcf85
commit 8d2407f1f2
8 changed files with 149 additions and 50 deletions

View File

@@ -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

View File

@@ -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 *);

View File

@@ -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])

View File

@@ -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 */

View File

@@ -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.

View File

@@ -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;
}

104
src/eat.c
View File

@@ -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)
{

View File

@@ -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)
{