diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index b6b3564f6..830f17094 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -3746,6 +3746,8 @@ applies only to new games. .\" Ones less that 8 should be enclosed within double quotes and padded with .\" trailing spaces. . +.lp accessiblemsg +Add location or direction information to messages (default is off). .lp acoustics Enable messages about what your character hears (default on). Note that this has nothing to do with your computer's audio capabilities. @@ -5934,6 +5936,8 @@ option and use the traditional Rogue-like commands. .lp paranoid_confirmation:swim Prevent walking into water or lava. +.lp accessiblemsg +Adds direction or location information to messages. .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 380ece9c7..21e39833a 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -4082,6 +4082,9 @@ applies only to new games. \blist{} %.lp +\item[\ib{accessiblemsg}] +Add location or direction information to messages (default is off). +%.lp \item[\ib{acoustics}] Enable messages about what your character hears (default on). Note that this has nothing to do with your computer's audio capabilities. @@ -6529,6 +6532,9 @@ traditional Rogue-like commands. \item[\ib{paranoid\verb+_+confirmation:swim}] Prevent walking into water or lava. %.lp +\item[\ib{accessiblemsg}] +Adds direction or location information to messages. +%.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 e8c7e199b..0c40114c0 100644 --- a/include/extern.h +++ b/include/extern.h @@ -2336,6 +2336,10 @@ extern void dumplogmsg(const char *); extern void dumplogfreemessages(void); #endif extern void pline(const char *, ...) PRINTF_F(1, 2); +extern void pline_dir(int, const char *, ...) PRINTF_F(2, 3); +extern void pline_xy(coordxy, coordxy, const char *, ...) PRINTF_F(3, 4); +extern void set_msg_dir(int); +extern void set_msg_xy(coordxy, coordxy); extern void custompline(unsigned, const char *, ...) PRINTF_F(2, 3); extern void urgent_pline(const char *, ...) PRINTF_F(1, 2); extern void Norep(const char *, ...) PRINTF_F(1, 2); diff --git a/include/flag.h b/include/flag.h index aa2a6420b..47cfc7fe0 100644 --- a/include/flag.h +++ b/include/flag.h @@ -188,6 +188,11 @@ struct debug_flags { #endif }; +struct accessibility_data { + boolean accessiblemsg; /* use msg_loc for plined messages */ + coord msg_loc; /* accessiblemsg: location */ +}; + /* * Stuff that really isn't option or platform related and does not * get saved and restored. They are set and cleared during the game @@ -438,6 +443,7 @@ struct instance_flags { extern NEARDATA struct flag flags; extern NEARDATA struct instance_flags iflags; +extern NEARDATA struct accessibility_data a11y; /* last_msg values * Usage: diff --git a/include/optlist.h b/include/optlist.h index e0abe2393..6e161735a 100644 --- a/include/optlist.h +++ b/include/optlist.h @@ -135,6 +135,9 @@ static int optfn_##a(int, int, boolean, char *, char *); "your starting alignment (lawful, neutral, or chaotic)") /* end of special ordering; remainder of entries are in alphabetical order */ + NHOPTB(accessiblemsg, Advanced, 0, opt_out, set_in_game, + Off, Yes, No, No, NoAlias, &a11y.accessiblemsg, Term_False, + "add location information to messages") NHOPTB(acoustics, Advanced, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias, &flags.acoustics, Term_False, "can your character hear anything") diff --git a/src/decl.c b/src/decl.c index ae70739c5..8c9fe0146 100644 --- a/src/decl.c +++ b/src/decl.c @@ -104,6 +104,7 @@ const schar dirs_ord[N_DIRS] = NEARDATA boolean has_strong_rngseed = FALSE; struct engr *head_engr; NEARDATA struct instance_flags iflags; +NEARDATA struct accessibility_data a11y; /* NOTE: the order of these words exactly corresponds to the order of oc_material values #define'd in objclass.h. */ const char *materialnm[] = { "mysterious", "liquid", "wax", "organic", @@ -1057,6 +1058,7 @@ decl_globals_init(void) ZERO(flags); ZERO(iflags); + ZERO(a11y); ZERO(u); ZERO(ubirthday); ZERO(urealtime); diff --git a/src/detect.c b/src/detect.c index 0971a2536..507bca04f 100644 --- a/src/detect.c +++ b/src/detect.c @@ -1824,6 +1824,7 @@ find_trap(struct trap *trap) cleared = TRUE; } + set_msg_xy(trap->tx, trap->ty); You("find %s.", an(trapname(trap->ttyp, FALSE))); if (cleared) { @@ -1852,6 +1853,7 @@ mfind0(struct monst *mtmp, boolean via_warning) || hides_under(mtmp->data) || mtmp->data->mlet == S_EEL)) { if (via_warning && found_something) { + set_msg_xy(x, y); Your("danger sense causes you to take a second %s.", Blind ? "to check nearby" : "look close by"); display_nhwindow(WIN_MESSAGE, FALSE); /* flush messages */ @@ -1871,8 +1873,10 @@ mfind0(struct monst *mtmp, boolean via_warning) exercise(A_WIS, TRUE); if (!canspotmon(mtmp)) { map_invisible(x, y); + set_msg_xy(x, y); You_feel("an unseen monster!"); } else if (!sensemon(mtmp)) { + set_msg_xy(x, y); You("find %s.", mtmp->mtame ? y_monnam(mtmp) : a_monnam(mtmp)); } return 1; @@ -1914,6 +1918,7 @@ dosearch0(int aflag) /* intrinsic autosearch vs explicit searching */ exercise(A_WIS, TRUE); nomul(0); feel_location(x, y); /* make sure it shows up */ + set_msg_xy(x, y); You("find a hidden door."); } else if (levl[x][y].typ == SCORR) { if (rnl(7 - fund)) @@ -1923,6 +1928,7 @@ dosearch0(int aflag) /* intrinsic autosearch vs explicit searching */ exercise(A_WIS, TRUE); nomul(0); feel_newsym(x, y); /* make sure it shows up */ + set_msg_xy(x, y); You("find a hidden passage."); } else { /* Be careful not to find anything in an SCORR or SDOOR */ diff --git a/src/dogmove.c b/src/dogmove.c index a93ed4d32..512680c99 100644 --- a/src/dogmove.c +++ b/src/dogmove.c @@ -437,7 +437,7 @@ dog_invent(struct monst *mtmp, struct edog *edog, int udist) char *otmpname = distant_name(otmp, doname); if (flags.verbose) - pline("%s picks up %s.", + pline_xy(omx, omy, "%s picks up %s.", Monnam(mtmp), otmpname); } obj_extract_self(otmp); diff --git a/src/hack.c b/src/hack.c index 1c0f6c4fe..74a66d756 100644 --- a/src/hack.c +++ b/src/hack.c @@ -976,7 +976,7 @@ test_move( Sprintf(buf, "solid stone"); else Sprintf(buf, "%s", an(firstmatch)); - pline("It's %s.", buf); + pline_dir(xytod(dx, dy), "It's %s.", buf); } } return FALSE; @@ -1116,7 +1116,7 @@ test_move( if (mode != TEST_TRAV && gc.context.run >= 2 && !(Blind || Hallucination) && !could_move_onto_boulder(x, y)) { if (mode == DO_MOVE && flags.mention_walls) - pline("A boulder blocks your path."); + pline_dir(xytod(dx,dy), "A boulder blocks your path."); return FALSE; } if (mode == DO_MOVE) { @@ -2194,9 +2194,11 @@ avoid_moving_on_trap(coordxy x, coordxy y, boolean msg) struct trap *trap; if ((trap = t_at(x, y)) && trap->tseen) { - if (msg && flags.mention_walls) + if (msg && flags.mention_walls) { + set_msg_xy(x, y); You("stop in front of %s.", an(trapname(trap->ttyp, FALSE))); + } return TRUE; } return FALSE; @@ -2222,9 +2224,11 @@ avoid_moving_on_liquid( polyforms are allowed to move over water */ return FALSE; /* liquid is safe to traverse */ } else if (is_pool_or_lava(x, y) && levl[x][y].seenv) { - if (msg && flags.mention_walls) + if (msg && flags.mention_walls) { + set_msg_xy(x, y); You("stop at the edge of the %s.", hliquid(is_pool(x,y) ? "water" : "lava")); + } return TRUE; } return FALSE; @@ -3522,7 +3526,7 @@ lookaround(void) if ((gc.context.run != 1 && !is_safemon(mtmp)) || (infront && !gc.context.travel)) { if (flags.mention_walls) - pline("%s blocks your path.", + pline_xy(x, y, "%s blocks your path.", upstart(a_monnam(mtmp))); goto stop; } @@ -3553,8 +3557,10 @@ lookaround(void) if (x != u.ux && y != u.uy) continue; if (gc.context.run != 1 && !gc.context.travel) { - if (flags.mention_walls) + if (flags.mention_walls) { + set_msg_xy(x, y); You("stop in front of the door."); + } goto stop; } /* we're orthogonal to a closed door, consider it a corridor */ diff --git a/src/lock.c b/src/lock.c index f46dae7ee..6ba291237 100644 --- a/src/lock.c +++ b/src/lock.c @@ -860,6 +860,7 @@ doopen_indir(coordxy x, coordxy y) locked = TRUE; break; } + set_msg_xy(cc.x, cc.y); pline("This door%s.", mesg); if (locked && flags.autounlock) { struct obj *unlocktool; @@ -887,6 +888,7 @@ doopen_indir(coordxy x, coordxy y) /* door is known to be CLOSED */ if (rnl(20) < (ACURRSTR + ACURR(A_DEX) + ACURR(A_CON)) / 3) { + set_msg_xy(cc.x, cc.y); pline_The("door opens."); if (door->doormask & D_TRAPPED) { b_trapped("door", FINGER); @@ -899,6 +901,7 @@ doopen_indir(coordxy x, coordxy y) unblock_point(cc.x, cc.y); /* vision: new see through there */ } else { exercise(A_STR, TRUE); + set_msg_xy(cc.x, cc.y); pline_The("door resists!"); } diff --git a/src/mcastu.c b/src/mcastu.c index d8745af37..8839894c5 100644 --- a/src/mcastu.c +++ b/src/mcastu.c @@ -63,7 +63,8 @@ cursetxt(struct monst *mtmp, boolean undirected) else point_msg = "at you, then curses"; - pline("%s points %s.", Monnam(mtmp), point_msg); + pline_xy(mtmp->mx, mtmp->my, + "%s points %s.", Monnam(mtmp), point_msg); } else if ((!(gm.moves % 4) || !rn2(4))) { if (!Deaf) Norep("You hear a mumbled curse."); /* Deaf-aware */ @@ -238,7 +239,7 @@ castmu( penalizing mspec_used. */ if (!foundyou && thinks_it_foundyou && !is_undirected_spell(mattk->adtyp, spellnum)) { - pline("%s casts a spell at %s!", + pline_xy(mtmp->mx, mtmp->my, "%s casts a spell at %s!", canseemon(mtmp) ? Monnam(mtmp) : "Something", is_waterwall(mtmp->mux,mtmp->muy) ? "empty water" : "thin air"); @@ -248,12 +249,14 @@ castmu( nomul(0); if (rn2(ml * 10) < (mtmp->mconf ? 100 : 20)) { /* fumbled attack */ Soundeffect(se_air_crackles, 60); - if (canseemon(mtmp) && !Deaf) + if (canseemon(mtmp) && !Deaf) { + set_msg_xy(mtmp->mx, mtmp->my); pline_The("air crackles around %s.", mon_nam(mtmp)); + } return M_ATTK_MISS; } if (canspotmon(mtmp) || !is_undirected_spell(mattk->adtyp, spellnum)) { - pline("%s casts a spell%s!", + pline_xy(mtmp->mx, mtmp->my, "%s casts a spell%s!", canspotmon(mtmp) ? Monnam(mtmp) : "Something", is_undirected_spell(mattk->adtyp, spellnum) ? "" diff --git a/src/mhitu.c b/src/mhitu.c index a4494ca81..22d58e7d8 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -35,7 +35,7 @@ hitmsg(struct monst *mtmp, struct attack *mattk) if same gender, "engagingly" for nymph, normal msg for others. */ if ((compat = could_seduce(mtmp, &gy.youmonst, mattk)) != 0 && !mtmp->mcan && !mtmp->mspec_used) { - pline("%s %s you %s.", Monst_name, + pline_xy(mtmp->mx, mtmp->my, "%s %s you %s.", Monst_name, !Blind ? "smiles at" : !Deaf ? "talks to" : "touches", (compat == 2) ? "engagingly" : "seductively"); } else { @@ -73,7 +73,7 @@ hitmsg(struct monst *mtmp, struct attack *mattk) && gh.hitmsg_prev != NULL && mattk == gh.hitmsg_prev + 1 && mattk->aatyp == gh.hitmsg_prev->aatyp) ? " again" : ""; - pline("%s %s%s%s", Monst_name, verb, again, punct); + pline_xy(mtmp->mx, mtmp->my, "%s %s%s%s", Monst_name, verb, again, punct); } gh.hitmsg_mid = mtmp->m_id; gh.hitmsg_prev = mattk; @@ -90,9 +90,9 @@ missmu(struct monst *mtmp, boolean nearmiss, struct attack *mattk) map_invisible(mtmp->mx, mtmp->my); if (could_seduce(mtmp, &gy.youmonst, mattk) && !mtmp->mcan) - pline("%s pretends to be friendly.", Monnam(mtmp)); + pline_xy(mtmp->mx, mtmp->my, "%s pretends to be friendly.", Monnam(mtmp)); else - pline("%s %smisses!", Monnam(mtmp), + pline_xy(mtmp->mx, mtmp->my, "%s %smisses!", Monnam(mtmp), (nearmiss && flags.verbose) ? "just " : ""); stop_occupation(); @@ -132,7 +132,7 @@ mswings( boolean bash) /* True: polearm used at too close range */ { if (flags.verbose && !Blind && mon_visible(mtmp)) { - pline("%s %s %s%s %s.", Monnam(mtmp), mswings_verb(otemp, bash), + pline_xy(mtmp->mx, mtmp->my, "%s %s %s%s %s.", Monnam(mtmp), mswings_verb(otemp, bash), (otemp->quan > 1L) ? "one of " : "", mhis(mtmp), xname(otemp)); } } @@ -198,6 +198,7 @@ wildmiss(struct monst *mtmp, struct attack *mattk) ? could_seduce(mtmp, &gy.youmonst, mattk) : 0); Monst_name = Monnam(mtmp); + set_msg_xy(mtmp->mx, mtmp->my); if (unotseen) { /* !mtmp->cansee || (Invis && !perceives(mtmp->data)) */ const char *swings = (mattk->aatyp == AT_BITE) ? "snaps" : (mattk->aatyp == AT_KICK) ? "kicks" diff --git a/src/mon.c b/src/mon.c index cb9e7b313..04326b396 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1606,8 +1606,9 @@ mpickgold(register struct monst* mtmp) add_to_minv(mtmp, gold); if (cansee(mtmp->mx, mtmp->my)) { if (flags.verbose && !mtmp->isgd) - pline("%s picks up some %s.", Monnam(mtmp), - mat_idx == GOLD ? "gold" : "money"); + pline_xy(mtmp->mx, mtmp->my, + "%s picks up some %s.", Monnam(mtmp), + mat_idx == GOLD ? "gold" : "money"); newsym(mtmp->mx, mtmp->my); } } @@ -1666,7 +1667,8 @@ mpickstuff(struct monst *mtmp) char *otmpname = distant_name(otmp, doname); if (flags.verbose) - pline("%s picks up %s.", Monnam(mtmp), otmpname); + pline_xy(mtmp->mx, mtmp->my, + "%s picks up %s.", Monnam(mtmp), otmpname); } obj_extract_self(otmp3); /* remove from floor */ (void) mpickobj(mtmp, otmp3); /* may merge and free otmp3 */ diff --git a/src/mthrowu.c b/src/mthrowu.c index f498c2685..40f94313f 100644 --- a/src/mthrowu.c +++ b/src/mthrowu.c @@ -1023,7 +1023,7 @@ thrwmu(struct monst* mtmp) if (canseemon(mtmp)) { onm = xname(otmp); - pline("%s %s %s.", Monnam(mtmp), + pline_xy(mtmp->mx, mtmp->my, "%s %s %s.", Monnam(mtmp), /* "thrusts" or "swings", or "bashes with" if adjacent */ mswings_verb(otmp, (rang <= 2) ? TRUE : FALSE), obj_is_pname(otmp) ? the(onm) : an(onm)); diff --git a/src/pline.c b/src/pline.c index cfddc44b0..897beec05 100644 --- a/src/pline.c +++ b/src/pline.c @@ -92,6 +92,47 @@ pline(const char *line, ...) va_end(the_args); } +void +pline_dir(int dir, const char *line, ...) +{ + va_list the_args; + + set_msg_dir(dir); + + va_start(the_args, line); + vpline(line, the_args); + va_end(the_args); +} + +void +pline_xy(coordxy x, coordxy y, const char *line, ...) +{ + va_list the_args; + + set_msg_xy(x, y); + + va_start(the_args, line); + vpline(line, the_args); + va_end(the_args); +} + +/* set the direction where next message happens */ +void +set_msg_dir(int dir) +{ + dtoxy(&a11y.msg_loc, dir); + a11y.msg_loc.x += u.ux; + a11y.msg_loc.y += u.uy; +} + +/* set the coordinate where next message happens */ +void +set_msg_xy(coordxy x, coordxy y) +{ + a11y.msg_loc.x = x; + a11y.msg_loc.y = y; +} + static void vpline(const char *line, va_list the_args) { @@ -110,6 +151,24 @@ vpline(const char *line, va_list the_args) if (gp.program_state.wizkit_wishing) return; + if (a11y.accessiblemsg && isok(a11y.msg_loc.x,a11y.msg_loc.y)) { + char *tmp; + char *dirstr; + static char dirstrbuf[BUFSZ]; + int g = (iflags.getpos_coords == GPCOORDS_NONE) + ? GPCOORDS_COMFULL : iflags.getpos_coords; + + dirstr = coord_desc(a11y.msg_loc.x, a11y.msg_loc.y, dirstrbuf, g); + a11y.msg_loc.x = a11y.msg_loc.y = 0; + tmp = (char *)alloc(strlen(line) + sizeof ": " + strlen(dirstr)); + Strcpy(tmp, dirstr); + Strcat(tmp, ": "); + Strcat(tmp, line); + vpline(tmp, the_args); + free((genericptr_t) tmp); + return; + } + if (!strchr(line, '%')) { /* format does not specify any substitutions; use it as-is */ ln = (int) strlen(line); diff --git a/src/steal.c b/src/steal.c index 06ee14594..f37baf28b 100644 --- a/src/steal.c +++ b/src/steal.c @@ -758,7 +758,7 @@ mdrop_obj( } /* obj_no_longer_held(obj); -- done by place_object */ if (verbosely && cansee(omx, omy)) - pline("%s drops %s.", Monnam(mon), obj_name); + pline_xy(mon->mx, mon->my, "%s drops %s.", Monnam(mon), obj_name); if (!flooreffects(obj, omx, omy, "fall")) { place_object(obj, omx, omy); stackobj(obj); diff --git a/src/trap.c b/src/trap.c index af6d9c9c5..bead72fae 100644 --- a/src/trap.c +++ b/src/trap.c @@ -1808,7 +1808,8 @@ trapeffect_pit( /* openfallingtrap; not inescapable here */ if (in_sight) { seetrap(trap); - pline("%s doesn't fall into the pit.", Monnam(mtmp)); + pline_xy(mtmp->mx, mtmp->my, + "%s doesn't fall into the pit.", Monnam(mtmp)); } return Trap_Effect_Finished; } @@ -1819,8 +1820,9 @@ trapeffect_pit( if (!passes_walls(mptr)) mtmp->mtrapped = 1; if (in_sight) { - pline("%s %s into %s pit!", Monnam(mtmp), fallverb, - a_your[trap->madeby_u]); + pline_xy(mtmp->mx, mtmp->my, + "%s %s into %s pit!", Monnam(mtmp), fallverb, + a_your[trap->madeby_u]); if (mptr == &mons[PM_PIT_VIPER] || mptr == &mons[PM_PIT_FIEND]) pline("How pitiful. Isn't that the pits?"); @@ -1872,18 +1874,20 @@ trapeffect_hole( if (in_sight) { seetrap(trap); if (tt == TRAPDOOR) - pline( + pline_xy(mtmp->mx, mtmp->my, "A trap door opens, but %s doesn't fall through.", mon_nam(mtmp)); else /* (tt == HOLE) */ - pline("%s doesn't fall through the hole.", - Monnam(mtmp)); + pline_xy(mtmp->mx, mtmp->my, + "%s doesn't fall through the hole.", + Monnam(mtmp)); } return Trap_Effect_Finished; /* inescapable = FALSE; */ } if (inescapable) { /* sokoban hole */ if (in_sight) { - pline("%s seems to be yanked down!", Monnam(mtmp)); + pline_xy(mtmp->mx, mtmp->my, + "%s seems to be yanked down!", Monnam(mtmp)); /* suppress message in mlevel_tele_trap() */ in_sight = FALSE; seetrap(trap); diff --git a/src/zap.c b/src/zap.c index c012e2744..f04c4a2e4 100644 --- a/src/zap.c +++ b/src/zap.c @@ -4761,7 +4761,7 @@ dobuzz( goto buzzmonst; } else if (zap_hit((int) u.uac, 0)) { range -= 2; - pline("%s hits you!", The(flash_str(fltyp, FALSE))); + pline_dir(xytod(-dx,-dy), "%s hits you!", The(flash_str(fltyp, FALSE))); if (Reflecting) { if (!Blind) { (void) ureflects("But %s reflects from your %s!",