From 3160112ecea60783b57604787949472f83d1ac75 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 14 Jan 2024 13:28:52 +0200 Subject: [PATCH] Accessibility: Show a message when monster is spotted Adds a new boolean option, spot_monsters. If on, every time the hero notices a monster which was out of sight before, a message is given. Combine with accessiblemsg to get the monster location: (3north): You see a newt. Breaks saves and bones. --- doc/Guidebook.mn | 4 +++ doc/Guidebook.tex | 6 ++++ include/extern.h | 2 ++ include/flag.h | 12 +++++++ include/monst.h | 2 ++ include/optlist.h | 3 ++ include/patchlevel.h | 2 +- src/allmain.c | 4 +++ src/do.c | 3 ++ src/hack.c | 75 ++++++++++++++++++++++++++++++++++++++++++++ src/monmove.c | 2 ++ src/teleport.c | 3 ++ src/vision.c | 2 ++ 13 files changed, 119 insertions(+), 1 deletion(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 9e07747b5..373df89d9 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -4650,6 +4650,8 @@ Allow sounds to be emitted from an integrated sound library (default on). Display a sparkly effect when a monster (including yourself) is hit by an attack to which it is resistant (default on). Persistent. +.lp spot_monsters +Show a message when hero notices a monster (default is off). .lp standout Boldface monsters and \(lq\fB\-\-More\-\-\fP\(rq (default off). Persistent. @@ -5938,6 +5940,8 @@ Rogue-like commands. Prevent walking into water or lava. .lp accessiblemsg Adds direction or location information to messages. +.lp spot_monsters +Shows a message when hero notices a monster; combine with accessiblemsg. .lp autodescribe Automatically describe the terrain under the cursor when targeting. .lp mention_walls diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index b0cd2872f..a8f0a9a84 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -5092,6 +5092,9 @@ Allow sounds to be emitted from an integrated sound library (default on). Display a sparkly effect when a monster (including yourself) is hit by an attack to which it is resistant (default on). Persistent. %.lp +\item[\ib{spot\verb+_+monsters}] +Show a message when hero notices a monster (default is off). +%.lp \item[\ib{standout}] Boldface monsters and ``{\tt --More--}'' (default off). Persistent. %.lp @@ -6535,6 +6538,9 @@ Prevent walking into water or lava. \item[\ib{accessiblemsg}] Adds direction or location information to messages. %.lp +\item[\ib{spot\verb+_+monsters}] +Shows a message when hero notices a monster; combine with accessiblemsg. +%.lp \item[\ib{autodescribe}] Automatically describe the terrain under the cursor when targeting. %.lp diff --git a/include/extern.h b/include/extern.h index e09f343ae..c2e3566cd 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1074,6 +1074,8 @@ extern boolean test_move(coordxy, coordxy, coordxy, coordxy, int); extern int wiz_debug_cmd_traveldisplay(void); #endif extern boolean u_rooted(void); +extern void notice_mon(struct monst *) NONNULLARG1; +extern void notice_all_mons(boolean); extern void impact_disturbs_zombies(struct obj *, boolean) NONNULLARG1; extern void disturb_buried_zombies(coordxy, coordxy); extern boolean u_maybe_impaired(void); diff --git a/include/flag.h b/include/flag.h index 9b402109d..3ea4e4063 100644 --- a/include/flag.h +++ b/include/flag.h @@ -191,8 +191,20 @@ struct debug_flags { struct accessibility_data { boolean accessiblemsg; /* use msg_loc for plined messages */ coord msg_loc; /* accessiblemsg: location */ + boolean mon_notices; /* msg when hero notices a monster */ + int mon_notices_blocked; /* temp disable mon_notices */ }; +/* Use notice_mon_off() / notice_mon_on() to temporarily disable + noticing the monsters in the vision code - perhaps the game + needs to output some other messages in between. + Call notice_all_mons() afterwards to catch up. */ +#define notice_mon_off() do { a11y.mon_notices_blocked++; } while(0) +#define notice_mon_on() do { if (--a11y.mon_notices_blocked < 0) { \ + impossible("mon_notices_blocked<0"); \ + a11y.mon_notices_blocked = 0; \ + } } while(0) + /* * Stuff that really isn't option or platform related and does not * get saved and restored. They are set and cleared during the game diff --git a/include/monst.h b/include/monst.h index 4cff71fae..4c0c19531 100644 --- a/include/monst.h +++ b/include/monst.h @@ -160,6 +160,8 @@ struct monst { Bitfield(mtemplit, 1); /* temporarily seen; only valid during bhit() */ Bitfield(meverseen, 1); /* mon has been seen at some point */ + Bitfield(mspotted, 1); /* mon is currently seen by hero */ + #define MAX_NUM_WORMS 32 /* should be 2^(wormno bitfield size) */ unsigned long mstrategy; /* for monsters with mflag3: current strategy */ diff --git a/include/optlist.h b/include/optlist.h index 6e161735a..3ffc2c064 100644 --- a/include/optlist.h +++ b/include/optlist.h @@ -654,6 +654,9 @@ static int optfn_##a(int, int, boolean, char *, char *); NHOPTB(sparkle, Map, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias, &flags.sparkle, Term_False, "display sparkly effect when resisting magic") + NHOPTB(spot_monsters, Advanced, 0, opt_in, set_in_game, + Off, Yes, No, No, NoAlias, &a11y.mon_notices, Term_False, + "message when hero spots a monster") NHOPTB(splash_screen, Advanced, 0, opt_out, set_in_config, On, Yes, No, No, NoAlias, &iflags.wc_splash_screen, Term_False, (char *)0) diff --git a/include/patchlevel.h b/include/patchlevel.h index 5f0524af8..229af04c8 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 96 +#define EDITLEVEL 97 /* * Development status possibilities. diff --git a/src/allmain.c b/src/allmain.c index 25ceaddac..6a58b61e6 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -705,6 +705,8 @@ newgame(void) { int i; + /* make sure welcome messages are given before noticing monsters */ + notice_mon_off(); disp.botlx = TRUE; gc.context.ident = 1; gc.context.warnlevel = 1; @@ -765,6 +767,8 @@ newgame(void) /* Success! */ welcome(TRUE); + notice_mon_on(); /* now we can notice monsters */ + notice_all_mons(TRUE); return; } diff --git a/src/do.c b/src/do.c index 8cac9bfa5..3f0c068a4 100644 --- a/src/do.c +++ b/src/do.c @@ -1807,6 +1807,7 @@ goto_level( /* Reset the screen. */ vision_reset(); /* reset the blockages */ reset_glyphmap(gm_levelchange); + notice_mon_off(); /* not noticing monsters yet! */ docrt(); /* does a full vision recalc */ flush_screen(-1); @@ -1923,6 +1924,8 @@ goto_level( #ifdef INSURANCE save_currentstate(); #endif + notice_mon_on(); + notice_all_mons(TRUE); print_level_annotation(); /* give room entrance message, if any */ diff --git a/src/hack.c b/src/hack.c index 34cfa3047..decb7567d 100644 --- a/src/hack.c +++ b/src/hack.c @@ -12,6 +12,7 @@ static int moverock(void); static void dosinkfall(void); static boolean findtravelpath(int); static boolean trapmove(coordxy, coordxy, struct trap *); +static int QSORTCALLBACK notice_mons_cmp(const genericptr, const genericptr); static schar u_simple_floortyp(coordxy, coordxy); static boolean swim_move_danger(coordxy, coordxy); static boolean domove_bump_mon(struct monst *, int) NONNULLARG1; @@ -1601,6 +1602,80 @@ u_rooted(void) return FALSE; } +void +notice_mon(struct monst *mtmp) +{ + if (a11y.mon_notices && !a11y.mon_notices_blocked) { + boolean spot = canspotmon(mtmp) + && !(is_hider(mtmp->data) + && (mtmp->mundetected + || M_AP_TYPE(mtmp) == M_AP_FURNITURE + || M_AP_TYPE(mtmp) == M_AP_OBJECT)); + + if (spot && !mtmp->mspotted && !DEADMONSTER(mtmp)) { + mtmp->mspotted = TRUE; + set_msg_xy(mtmp->mx, mtmp->my); + You("%s %s.", canseemon(mtmp) ? "see" : "notice", + x_monnam(mtmp, + mtmp->mtame ? ARTICLE_YOUR + : (!has_mgivenname(mtmp) + && !type_is_pname(mtmp->data)) ? ARTICLE_A + : ARTICLE_NONE, + (mtmp->mpeaceful && !mtmp->mtame) ? "peaceful" : 0, + has_mgivenname(mtmp) ? SUPPRESS_SADDLE : 0, FALSE)); + } else if (!spot) { + mtmp->mspotted = FALSE; + } + } +} + +static int QSORTCALLBACK +notice_mons_cmp(const genericptr ptr1, const genericptr ptr2) +{ + const struct monst *m1 = *(const struct monst **) ptr1, + *m2 = *(const struct monst **) ptr2; + + return (distu(m1->mx, m1->my) - distu(m2->mx, m2->my)); +} + +void +notice_all_mons(boolean reset) +{ + if (a11y.mon_notices && !a11y.mon_notices_blocked) { + struct monst *mtmp; + struct monst **arr = NULL; + int i = 0, cnt = 0; + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (canspotmon(mtmp)) + cnt++; + else if (reset) + mtmp->mspotted = FALSE; + + if (!cnt) + return; + + arr = (struct monst **) alloc(cnt * sizeof(struct monst *)); + + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (!canspotmon(mtmp)) + mtmp->mspotted = FALSE; + else if (!DEADMONSTER(mtmp) && i < cnt) + arr[i++] = mtmp; + } + + if (i) { + qsort((genericptr_t) arr, (size_t) i, sizeof *arr, notice_mons_cmp); + + for (i = 0; i < cnt; i++) + notice_mon(arr[i]); + } + + free(arr); + } +} + /* maybe disturb buried zombies when an object is dropped or thrown nearby */ void impact_disturbs_zombies(struct obj *obj, boolean violent) diff --git a/src/monmove.c b/src/monmove.c index 48b346401..e0610fcca 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -1307,6 +1307,8 @@ postmov( boolean canseeit = cansee(mtmp->mx, mtmp->my), didseeit = canseeit; + notice_mon(mtmp); + if (mmoved == MMOVE_MOVED) { nix = mtmp->mx, niy = mtmp->my; /* sequencing issue: when monster movement decides that a diff --git a/src/teleport.c b/src/teleport.c index fad0403da..6a7b08781 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -513,6 +513,7 @@ teleds(coordxy nux, coordxy nuy, int teleds_flags) see_monsters(); gv.vision_full_recalc = 1; nomul(0); + notice_mon_off(); vision_recalc(0); /* vision before effects */ /* this used to take place sooner, but if a --More-- prompt was issued @@ -542,6 +543,8 @@ teleds(coordxy nux, coordxy nuy, int teleds_flags) /* possible shop entry message comes after guard's shrill whistle */ spoteffects(TRUE); invocation_message(); + notice_mon_on(); + notice_all_mons(TRUE); return; } diff --git a/src/vision.c b/src/vision.c index b61d456dd..69d540032 100644 --- a/src/vision.c +++ b/src/vision.c @@ -837,6 +837,8 @@ vision_recalc(int control) /* Set the new min and max pointers. */ gv.viz_rmin = next_rmin; gv.viz_rmax = next_rmax; + + notice_all_mons(TRUE); } /*