diff --git a/include/extern.h b/include/extern.h index cd0613e01..982ba5ebf 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 extern.h $NHDT-Date: 1706213788 2024/01/25 20:16:28 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1373 $ */ +/* NetHack 3.7 extern.h $NHDT-Date: 1707547708 2024/02/10 06:48:28 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1381 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -3686,6 +3686,7 @@ extern void setnotworn(struct obj *) NO_NNARGS; /* has tests for obj */ extern void allunworn(void); extern struct obj *wearmask_to_obj(long); extern long wearslot(struct obj *) NONNULLARG1; +extern void check_wornmask_slots(void); extern void mon_set_minvis(struct monst *) NONNULLARG1; extern void mon_adjust_speed(struct monst *, int, struct obj *) NONNULLARG1; extern void update_mon_extrinsics(struct monst *, struct obj *, boolean, diff --git a/src/cmd.c b/src/cmd.c index a337922e3..0940eb93b 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 cmd.c $NHDT-Date: 1704225560 2024/01/02 19:59:20 $ $NHDT-Branch: keni-luabits2 $:$NHDT-Revision: 1.699 $ */ +/* NetHack 3.7 cmd.c $NHDT-Date: 1707547723 2024/02/10 06:48:43 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.704 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -4255,6 +4255,7 @@ you_sanity_check(void) impossible("sanity_check: you over monster"); } + check_wornmask_slots(); (void) check_invent_gold("invent"); } diff --git a/src/worn.c b/src/worn.c index aa8f44c41..695a7d705 100644 --- a/src/worn.c +++ b/src/worn.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 worn.c $NHDT-Date: 1652577035 2022/05/15 01:10:35 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.84 $ */ +/* NetHack 3.7 worn.c $NHDT-Date: 1707547726 2024/02/10 06:48:46 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.100 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -13,22 +13,23 @@ static int extra_pref(struct monst *, struct obj *) NONNULLARG1; const struct worn { long w_mask; struct obj **w_obj; -} worn[] = { { W_ARM, &uarm }, - { W_ARMC, &uarmc }, - { W_ARMH, &uarmh }, - { W_ARMS, &uarms }, - { W_ARMG, &uarmg }, - { W_ARMF, &uarmf }, - { W_ARMU, &uarmu }, - { W_RINGL, &uleft }, - { W_RINGR, &uright }, - { W_WEP, &uwep }, - { W_SWAPWEP, &uswapwep }, - { W_QUIVER, &uquiver }, - { W_AMUL, &uamul }, - { W_TOOL, &ublindf }, - { W_BALL, &uball }, - { W_CHAIN, &uchain }, + const char *w_what; /* for failing sanity check's feedback */ +} worn[] = { { W_ARM, &uarm, "suit" }, + { W_ARMC, &uarmc, "cloak" }, + { W_ARMH, &uarmh, "helmet" }, + { W_ARMS, &uarms, "shield" }, + { W_ARMG, &uarmg, "gloves" }, + { W_ARMF, &uarmf, "boots" }, + { W_ARMU, &uarmu, "shirt" }, + { W_RINGL, &uleft, "left ring" }, + { W_RINGR, &uright, "right ring" }, + { W_WEP, &uwep, "weapon" }, + { W_SWAPWEP, &uswapwep, "alternate weapon" }, + { W_QUIVER, &uquiver, "quiver" }, + { W_AMUL, &uamul, "amulet" }, + { W_TOOL, &ublindf, "facewear" }, /* blindfold|towel|lenses */ + { W_BALL, &uball, "chained ball" }, + { W_CHAIN, &uchain, "attached chain" }, { 0, 0 } }; @@ -251,6 +252,95 @@ wearslot(struct obj *obj) return res; } +/* for 'sanity_check' option, called by you_sanity_check() */ +void +check_wornmask_slots(void) +{ + /* we'll skip ball and chain here--they warrant separate sanity check; + at present, we also skip 'uskin' because it isn't included in worn[] */ +#define IGNORE_SLOTS (W_ART | W_ARTI | W_SADDLE | W_BALL| W_CHAIN) + char whybuf[BUFSZ]; + const struct worn *wp; + struct obj *o, *otmp; + long m; + + for (wp = worn; wp->w_mask; wp++) { + o = *wp->w_obj; + if (!o) + continue; + m = wp->w_mask; + if ((m & IGNORE_SLOTS) != 0L && (m & ~IGNORE_SLOTS) == 0L) + continue; + if ((o = *wp->w_obj) != 0) { + whybuf[0] = '\0'; + /* slot pointer (uarm, uwep, &c) is populated; check that object + is in inventory and has the relevant owornmask bit set */ + for (otmp = gi.invent; otmp; otmp = otmp->nobj) + if (otmp == o) + break; + if (!otmp) + Sprintf(whybuf, "%s (%s) not found in invent", + wp->w_what, fmt_ptr(o)); + else if ((o->owornmask & m) == 0L) + Sprintf(whybuf, "%s bit not set in owornmask [0x%08lx]", + wp->w_what, o->owornmask); + else if ((o->owornmask & ~(m | IGNORE_SLOTS)) != 0L) + Sprintf(whybuf, "%s wrong bit set in owornmask [0x%08lx]", + wp->w_what, o->owornmask); + if (whybuf[0]) + impossible("Worn-slot insanity: %s.", whybuf); + } /* o != NULL */ + + /* check whether any item other than the one in the slot pointer + claims to be worn/wielded in this slot; make this test whether + 'o' is Null or not; [sanity_check_worn(mkobj.c) for object by + object checking will most likely have already caught this] */ + for (otmp = gi.invent; otmp; otmp = otmp->nobj) { + if (otmp != o && (otmp->owornmask & wp->w_mask) != 0L) { + Sprintf(whybuf, "%s has owornmask 0x%08lx [0x%08lx bit set]", + wp->w_what, otmp->owornmask, m); + impossible("Worn-slot insanity: %s.", whybuf); + } + } + } + +#ifdef EXTRA_SANITY_CHECKS + /* dual wielding: not a slot but lots of things to verify */ + if (u.twoweap) { + const char *why = NULL; + + if (!uwep || !uswapwep) { + Sprintf(whybuf, "without %s%s%s", + !uwep ? "uwep" : "", + (!uwep && !uswapwep) ? " and without " : "", + !uswapwep ? "uswapwep" : ""); + why = whybuf; + } else if (uarms) + why = "while wearing shield"; + else if (uwep->oclass != WEAPON_CLASS && !is_weptool(uwep)) + why = "uwep is not a weapon"; + else if (is_launcher(uwep) || is_ammo(uwep) || is_missile(uwep)) + why = "uwep is not a melee weapon"; + else if (bimanual(uwep)) + why = "uwep is two-handed"; + else if (uswapwep->oclass != WEAPON_CLASS && !is_weptool(uswapwep)) + why = "uswapwep is not a weapon"; + else if (is_launcher(uswapwep) || is_ammo(uswapwep) + || is_missile(uswapwep)) + why = "uswapwep is not a melee weapon"; + else if (bimanual(uswapwep)) + why = "uswapwep is two-handed"; + else if (!could_twoweap(gy.youmonst.data)) + why = "without two weapon attacks"; + + if (why) + impossible("Two-weapon insanity: %s.", why); + } +#endif /* EXTRA_SANITY_CHECKS */ + return; +#undef IGNORE_SLOTS +} /* check_wornmask_slots() */ + void mon_set_minvis(struct monst *mon) {