From 5cc529efc812b59174e5a1cbc0c5df2a0d5e53f8 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 9 Nov 2024 23:49:10 -0500 Subject: [PATCH] follow-up related to #1320 This is additional groundwork related to https://github.com/NetHack/NetHack/issues/1320 This additional groundwork just puts some safeguards in place to make it rather tough to end up with an instant death from handling a cockatrice corpse in your inventory without appropriate protection. At this point, still no actual petrification will occur. --- include/extern.h | 3 ++- src/do.c | 22 +++++++++++++++++++++- src/do_wear.c | 23 +++++++++++++++++++++++ src/invent.c | 15 ++++++++++++++- src/pickup.c | 17 +++++++++++++---- src/zap.c | 2 +- 6 files changed, 74 insertions(+), 8 deletions(-) diff --git a/include/extern.h b/include/extern.h index ceafb5000..d4abee992 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1326,6 +1326,7 @@ extern void silly_thing(const char *, struct obj *) NONNULLARG1; extern void sync_perminvent(void); extern void perm_invent_toggled(boolean negated); extern void prepare_perminvent(winid window); +extern struct obj *carrying_stoning_corpse(void); /* ### ioctl.c ### */ @@ -2363,7 +2364,7 @@ extern int loot_mon(struct monst *, int *, boolean *) NO_NNARGS; extern int dotip(void); extern struct autopickup_exception *check_autopickup_exceptions(struct obj *) NONNULLARG1; extern boolean autopick_testobj(struct obj *, boolean) NONNULLARG1; -extern boolean u_safe_from_fatal_corpse(struct obj *obj) NONNULLARG1; +extern boolean u_safe_from_fatal_corpse(struct obj *obj, int) NONNULLARG1; /* ### pline.c ### */ diff --git a/src/do.c b/src/do.c index 591537f12..204643522 100644 --- a/src/do.c +++ b/src/do.c @@ -20,8 +20,9 @@ staticfn NHFILE *currentlevel_rewrite(void); staticfn void familiar_level_msg(void); staticfn void final_level(void); staticfn void temperature_change_msg(schar); +staticfn boolean better_not_try_to_drop_that(struct obj *); -/* static boolean badspot(coordxy,coordxy); */ + /* static boolean badspot(coordxy,coordxy); */ /* the #drop command: drop one inventory item */ int @@ -709,6 +710,8 @@ drop(struct obj *obj) return ECMD_FAIL; if (!canletgo(obj, "drop")) return ECMD_FAIL; + if (obj->otyp == CORPSE && better_not_try_to_drop_that(obj)) + return ECMD_FAIL; if (obj == uwep) { if (welded(uwep)) { weldmsg(obj); @@ -933,6 +936,23 @@ doddrop(void) return result; } +staticfn boolean +better_not_try_to_drop_that(struct obj *otmp) +{ + char buf[BUFSZ]; + + /* u_safe_from_fatal_corpse() with 0xF checks for gloves and stoning + * resistance before bothering to prompt you. + */ + if (otmp->otyp == CORPSE && !u_safe_from_fatal_corpse(otmp, 0xF)) { + Snprintf( + buf, sizeof buf, + "Drop the %s corpse without any protection while handling it?", + obj_pmname(otmp)); + return (paranoid_ynq(TRUE, buf, FALSE) != 'y'); + } + return FALSE; +} staticfn int /* check callers */ menudrop_split(struct obj *otmp, long cnt) { diff --git a/src/do_wear.c b/src/do_wear.c index 4f20e1b40..d6aac2a60 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -52,6 +52,7 @@ staticfn int takeoff_ok(struct obj *); /* maybe_destroy_armor() may return NULL */ staticfn struct obj *maybe_destroy_armor(struct obj *, struct obj *, boolean *) NONNULLARG3; +staticfn boolean better_not_take_that_off(struct obj *) NONNULLARG1; /* plural "fingers" or optionally "gloves" */ const char * @@ -2641,6 +2642,8 @@ select_off(struct obj *otmp) gloves_simple_name(uarmg)); return 0; } + if (better_not_take_that_off(otmp)) + return 0; } /* special boot checks */ if (otmp == uarmf) { @@ -2886,6 +2889,26 @@ take_off(void) return 1; /* get busy */ } +staticfn boolean +better_not_take_that_off(struct obj *otmp) +{ + struct obj *corpse = carrying_stoning_corpse(); + char buf[BUFSZ]; + + /* u_safe_from_fatal_corpse() with 0x4e instead of 0x6 + would also check for no stoning resistance before + bothering to prompt, but losing stoning resistance + later, without the gloves on could prove dangerous, + so we won't factor that in */ + if (corpse && !u_safe_from_fatal_corpse(corpse, 0x6)) { + Snprintf(buf, sizeof buf, + "Take off your %s despite carrying a dead %s?", + gloves_simple_name(otmp), obj_pmname(corpse)); + return (paranoid_ynq(TRUE, buf, FALSE) != 'y'); + } + return FALSE; +} + /* clear saved context to avoid inappropriate resumption of interrupted 'A' */ void reset_remarm(void) diff --git a/src/invent.c b/src/invent.c index 00ccd045c..fb137aabf 100644 --- a/src/invent.c +++ b/src/invent.c @@ -49,6 +49,7 @@ staticfn void ia_addmenu(winid, int, char, const char *); staticfn void itemactions_pushkeys(struct obj *, int); staticfn int itemactions(struct obj *); staticfn int dispinv_with_action(char *, boolean, const char *); +staticfn struct obj *carrying_cockatrice_corpse(void); /* enum and structs are defined in wintype.h */ static win_request_info wri_info; @@ -1231,7 +1232,7 @@ hold_another_object( obj = addinv_core0(obj, (struct obj *) 0, FALSE); goto drop_it; } else if (obj->otyp == CORPSE - && !u_safe_from_fatal_corpse(obj) + && !u_safe_from_fatal_corpse(obj, 0xF) && obj->wishedfor) { obj->wishedfor = 0; obj = addinv_core0(obj, (struct obj *) 0, FALSE); @@ -1480,6 +1481,18 @@ carrying(int type) return otmp; } +/* return inventory object of type that will petrify on touch */ +struct obj * +carrying_stoning_corpse(void) +{ + struct obj *otmp; + + for (otmp = gi.invent; otmp; otmp = otmp->nobj) + if (otmp->otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm])) + break; + return otmp; +} + /* Fictional and not-so-fictional currencies. * http://concord.wikia.com/wiki/List_of_Fictional_Currencies */ diff --git a/src/pickup.c b/src/pickup.c index b68761a39..48fe61c18 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -261,11 +261,20 @@ query_classes( return TRUE; } +/* + * tests: + * 1 = gloves + * 2 = is_corpse + * 4 = does corpse petrify + * 8 = stone resistance + */ boolean -u_safe_from_fatal_corpse(struct obj *obj) +u_safe_from_fatal_corpse(struct obj *obj, int tests) { - if (uarmg || obj->otyp != CORPSE - || !touch_petrifies(&mons[obj->corpsenm]) || Stone_resistance) + if (((tests & 1) && uarmg) + || ((tests & 2) && obj->otyp != CORPSE) + || ((tests & 4) && !touch_petrifies(&mons[obj->corpsenm])) + || ((tests & 8) && Stone_resistance)) return TRUE; return FALSE; } @@ -274,7 +283,7 @@ u_safe_from_fatal_corpse(struct obj *obj) staticfn boolean fatal_corpse_mistake(struct obj *obj, boolean remotely) { - if (u_safe_from_fatal_corpse(obj) || remotely) + if (u_safe_from_fatal_corpse(obj, 0xF) || remotely) return FALSE; if (poly_when_stoned(gy.youmonst.data) && polymon(PM_STONE_GOLEM)) { diff --git a/src/zap.c b/src/zap.c index 78baab25e..b25b80681 100644 --- a/src/zap.c +++ b/src/zap.c @@ -6189,7 +6189,7 @@ makewish(void) /* TODO? maybe generate a second event describing what was received since these just echo player's request rather than show actual result */ - if (otmp->otyp == CORPSE && !u_safe_from_fatal_corpse(otmp)) + if (otmp->otyp == CORPSE && !u_safe_from_fatal_corpse(otmp, 0xF)) otmp->wishedfor = 1; const char *verb = ((Is_airlevel(&u.uz) || u.uinwater)