1 /* NetHack 3.7	do_name.c	$NHDT-Date: 1614818323 2021/03/04 00:38:43 $  $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.198 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Pasi Kallinen, 2018. */
4 /* NetHack may be freely redistributed.  See license for details. */
5 
6 #include "hack.h"
7 
8 static char *nextmbuf(void);
9 static void getpos_help_keyxhelp(winid, const char *, const char *, int);
10 static void getpos_help(boolean, const char *);
11 static int QSORTCALLBACK cmp_coord_distu(const void *, const void *);
12 static int gloc_filter_classify_glyph(int);
13 static int gloc_filter_floodfill_matcharea(int, int);
14 static void gloc_filter_floodfill(int, int);
15 static void gloc_filter_init(void);
16 static void gloc_filter_done(void);
17 static boolean gather_locs_interesting(int, int, int);
18 static void gather_locs(coord **, int *, int);
19 static void auto_describe(int, int);
20 static void do_mgivenname(void);
21 static boolean alreadynamed(struct monst *, char *, char *);
22 static void do_oname(struct obj *);
23 static int name_ok(struct obj *);
24 static int call_ok(struct obj *);
25 static char *docall_xname(struct obj *);
26 static void namefloorobj(void);
27 
28 extern const char what_is_an_unknown_object[]; /* from pager.c */
29 
30 #define NUMMBUF 5
31 
32 /* manage a pool of BUFSZ buffers, so callers don't have to */
33 static char *
nextmbuf(void)34 nextmbuf(void)
35 {
36     static char NEARDATA bufs[NUMMBUF][BUFSZ];
37     static int bufidx = 0;
38 
39     bufidx = (bufidx + 1) % NUMMBUF;
40     return bufs[bufidx];
41 }
42 
43 /* function for getpos() to highlight desired map locations.
44  * parameter value 0 = initialize, 1 = highlight, 2 = done
45  */
46 static void (*getpos_hilitefunc)(int) = (void (*)(int)) 0;
47 static boolean (*getpos_getvalid)(int, int) = (boolean (*)(int, int)) 0;
48 
49 void
getpos_sethilite(void (* gp_hilitef)(int),boolean (* gp_getvalidf)(int,int))50 getpos_sethilite(void (*gp_hilitef)(int), boolean (*gp_getvalidf)(int, int))
51 {
52     getpos_hilitefunc = gp_hilitef;
53     getpos_getvalid = gp_getvalidf;
54 }
55 
56 static const char *const gloc_descr[NUM_GLOCS][4] = {
57     { "any monsters", "monster", "next/previous monster", "monsters" },
58     { "any items", "item", "next/previous object", "objects" },
59     { "any doors", "door", "next/previous door or doorway",
60       "doors or doorways" },
61     { "any unexplored areas", "unexplored area", "unexplored location",
62       "locations next to unexplored locations" },
63     { "anything interesting", "interesting thing", "anything interesting",
64       "anything interesting" },
65     { "any valid locations", "valid location", "valid location",
66       "valid locations" }
67 };
68 
69 static const char *const gloc_filtertxt[NUM_GFILTER] = {
70     "",
71     " in view",
72     " in this area"
73 };
74 
75 static void
getpos_help_keyxhelp(winid tmpwin,const char * k1,const char * k2,int gloc)76 getpos_help_keyxhelp(winid tmpwin, const char *k1, const char *k2, int gloc)
77 {
78     char sbuf[BUFSZ], fbuf[QBUFSZ];
79     const char *move_cursor_to = "move the cursor to ",
80                *filtertxt = gloc_filtertxt[iflags.getloc_filter];
81 
82     if (gloc == GLOC_EXPLORE) {
83         /* default of "move to unexplored location" is inaccurate
84            because the position will be one spot short of that */
85         move_cursor_to = "move the cursor next to an ";
86         if (iflags.getloc_usemenu)
87             /* default is too wide for basic 80-column tty so shorten it
88                to avoid wrapping */
89             filtertxt = strsubst(strcpy(fbuf, filtertxt),
90                                  "this area", "area");
91     }
92     Sprintf(sbuf, "Use '%s'/'%s' to %s%s%s.",
93             k1, k2,
94             iflags.getloc_usemenu ? "get a menu of " : move_cursor_to,
95             gloc_descr[gloc][2 + iflags.getloc_usemenu], filtertxt);
96     putstr(tmpwin, 0, sbuf);
97 }
98 
99 DISABLE_WARNING_FORMAT_NONLITERAL
100 
101 /* the response for '?' help request in getpos() */
102 static void
getpos_help(boolean force,const char * goal)103 getpos_help(boolean force, const char *goal)
104 {
105     static const char *const fastmovemode[2] = { "8 units at a time",
106                                                  "skipping same glyphs" };
107     char sbuf[BUFSZ];
108     boolean doing_what_is;
109     winid tmpwin = create_nhwindow(NHW_MENU);
110 
111     Sprintf(sbuf,
112             "Use '%c', '%c', '%c', '%c' to move the cursor to %s.", /* hjkl */
113             g.Cmd.move_W, g.Cmd.move_S, g.Cmd.move_N, g.Cmd.move_E, goal);
114     putstr(tmpwin, 0, sbuf);
115     Sprintf(sbuf,
116             "Use 'H', 'J', 'K', 'L' to fast-move the cursor, %s.",
117             fastmovemode[iflags.getloc_moveskip]);
118     putstr(tmpwin, 0, sbuf);
119     putstr(tmpwin, 0, "Or enter a background symbol (ex. '<').");
120     Sprintf(sbuf, "Use '%s' to move the cursor on yourself.",
121            visctrl(g.Cmd.spkeys[NHKF_GETPOS_SELF]));
122     putstr(tmpwin, 0, sbuf);
123     if (!iflags.terrainmode || (iflags.terrainmode & TER_MON) != 0) {
124         getpos_help_keyxhelp(tmpwin,
125                              visctrl(g.Cmd.spkeys[NHKF_GETPOS_MON_NEXT]),
126                              visctrl(g.Cmd.spkeys[NHKF_GETPOS_MON_PREV]),
127                              GLOC_MONS);
128     }
129     if (!iflags.terrainmode || (iflags.terrainmode & TER_OBJ) != 0) {
130         getpos_help_keyxhelp(tmpwin,
131                              visctrl(g.Cmd.spkeys[NHKF_GETPOS_OBJ_NEXT]),
132                              visctrl(g.Cmd.spkeys[NHKF_GETPOS_OBJ_PREV]),
133                              GLOC_OBJS);
134     }
135     if (!iflags.terrainmode || (iflags.terrainmode & TER_MAP) != 0) {
136         /* these are primarily useful when choosing a travel
137            destination for the '_' command */
138         getpos_help_keyxhelp(tmpwin,
139                              visctrl(g.Cmd.spkeys[NHKF_GETPOS_DOOR_NEXT]),
140                              visctrl(g.Cmd.spkeys[NHKF_GETPOS_DOOR_PREV]),
141                              GLOC_DOOR);
142         getpos_help_keyxhelp(tmpwin,
143                              visctrl(g.Cmd.spkeys[NHKF_GETPOS_UNEX_NEXT]),
144                              visctrl(g.Cmd.spkeys[NHKF_GETPOS_UNEX_PREV]),
145                              GLOC_EXPLORE);
146         getpos_help_keyxhelp(tmpwin,
147                           visctrl(g.Cmd.spkeys[NHKF_GETPOS_INTERESTING_NEXT]),
148                           visctrl(g.Cmd.spkeys[NHKF_GETPOS_INTERESTING_PREV]),
149                              GLOC_INTERESTING);
150     }
151     Sprintf(sbuf, "Use '%s' to change fast-move mode to %s.",
152             visctrl(g.Cmd.spkeys[NHKF_GETPOS_MOVESKIP]),
153             fastmovemode[!iflags.getloc_moveskip]);
154     putstr(tmpwin, 0, sbuf);
155     if (!iflags.terrainmode || (iflags.terrainmode & TER_DETECT) == 0) {
156         Sprintf(sbuf, "Use '%s' to toggle menu listing for possible targets.",
157                 visctrl(g.Cmd.spkeys[NHKF_GETPOS_MENU]));
158         putstr(tmpwin, 0, sbuf);
159         Sprintf(sbuf,
160                 "Use '%s' to change the mode of limiting possible targets.",
161                 visctrl(g.Cmd.spkeys[NHKF_GETPOS_LIMITVIEW]));
162         putstr(tmpwin, 0, sbuf);
163     }
164     if (!iflags.terrainmode) {
165         char kbuf[BUFSZ];
166 
167         if (getpos_getvalid) {
168             Sprintf(sbuf, "Use '%s' or '%s' to move to valid locations.",
169                     visctrl(g.Cmd.spkeys[NHKF_GETPOS_VALID_NEXT]),
170                     visctrl(g.Cmd.spkeys[NHKF_GETPOS_VALID_PREV]));
171             putstr(tmpwin, 0, sbuf);
172         }
173         if (getpos_hilitefunc) {
174             Sprintf(sbuf, "Use '%s' to display valid locations.",
175                     visctrl(g.Cmd.spkeys[NHKF_GETPOS_SHOWVALID]));
176             putstr(tmpwin, 0, sbuf);
177         }
178         Sprintf(sbuf, "Use '%s' to toggle automatic description.",
179                 visctrl(g.Cmd.spkeys[NHKF_GETPOS_AUTODESC]));
180         putstr(tmpwin, 0, sbuf);
181         if (iflags.cmdassist) { /* assisting the '/' command, I suppose... */
182             Sprintf(sbuf,
183                     (iflags.getpos_coords == GPCOORDS_NONE)
184          ? "(Set 'whatis_coord' option to include coordinates with '%s' text.)"
185          : "(Reset 'whatis_coord' option to omit coordinates from '%s' text.)",
186                     visctrl(g.Cmd.spkeys[NHKF_GETPOS_AUTODESC]));
187         }
188         /* disgusting hack; the alternate selection characters work for any
189            getpos call, but only matter for dowhatis (and doquickwhatis) */
190         doing_what_is = (goal == what_is_an_unknown_object);
191         if (doing_what_is) {
192             Sprintf(kbuf, "'%s' or '%s' or '%s' or '%s'",
193                     visctrl(g.Cmd.spkeys[NHKF_GETPOS_PICK]),
194                     visctrl(g.Cmd.spkeys[NHKF_GETPOS_PICK_Q]),
195                     visctrl(g.Cmd.spkeys[NHKF_GETPOS_PICK_O]),
196                     visctrl(g.Cmd.spkeys[NHKF_GETPOS_PICK_V]));
197         } else {
198             Sprintf(kbuf, "'%s'", visctrl(g.Cmd.spkeys[NHKF_GETPOS_PICK]));
199         }
200         Snprintf(sbuf, sizeof(sbuf),
201                  "Type a %s when you are at the right place.", kbuf);
202         putstr(tmpwin, 0, sbuf);
203         if (doing_what_is) {
204             Sprintf(sbuf,
205        "  '%s' describe current spot, show 'more info', move to another spot.",
206                     visctrl(g.Cmd.spkeys[NHKF_GETPOS_PICK_V]));
207             putstr(tmpwin, 0, sbuf);
208             Sprintf(sbuf,
209                     "  '%s' describe current spot,%s move to another spot;",
210                     visctrl(g.Cmd.spkeys[NHKF_GETPOS_PICK]),
211                     flags.help ? " prompt if 'more info'," : "");
212             putstr(tmpwin, 0, sbuf);
213             Sprintf(sbuf,
214                     "  '%s' describe current spot, move to another spot;",
215                     visctrl(g.Cmd.spkeys[NHKF_GETPOS_PICK_Q]));
216             putstr(tmpwin, 0, sbuf);
217             Sprintf(sbuf,
218                     "  '%s' describe current spot, stop looking at things;",
219                     visctrl(g.Cmd.spkeys[NHKF_GETPOS_PICK_O]));
220             putstr(tmpwin, 0, sbuf);
221         }
222     }
223     if (!force)
224         putstr(tmpwin, 0, "Type Space or Escape when you're done.");
225     putstr(tmpwin, 0, "");
226     display_nhwindow(tmpwin, TRUE);
227     destroy_nhwindow(tmpwin);
228 }
229 
230 RESTORE_WARNING_FORMAT_NONLITERAL
231 
232 static int QSORTCALLBACK
cmp_coord_distu(const void * a,const void * b)233 cmp_coord_distu(const void *a, const void *b)
234 {
235     const coord *c1 = a;
236     const coord *c2 = b;
237     int dx, dy, dist_1, dist_2;
238 
239     dx = u.ux - c1->x;
240     dy = u.uy - c1->y;
241     dist_1 = max(abs(dx), abs(dy));
242     dx = u.ux - c2->x;
243     dy = u.uy - c2->y;
244     dist_2 = max(abs(dx), abs(dy));
245 
246     if (dist_1 == dist_2)
247         return (c1->y != c2->y) ? (c1->y - c2->y) : (c1->x - c2->x);
248 
249     return dist_1 - dist_2;
250 }
251 
252 #define IS_UNEXPLORED_LOC(x,y) \
253     (isok((x), (y))                                     \
254      && glyph_is_unexplored(levl[(x)][(y)].glyph)   \
255      && !levl[(x)][(y)].seenv)
256 
257 #define GLOC_SAME_AREA(x,y)                                     \
258     (isok((x), (y))                                             \
259      && (selection_getpoint((x),(y), g.gloc_filter_map)))
260 
261 static int
gloc_filter_classify_glyph(int glyph)262 gloc_filter_classify_glyph(int glyph)
263 {
264     int c;
265 
266     if (!glyph_is_cmap(glyph))
267         return 0;
268 
269     c = glyph_to_cmap(glyph);
270 
271     if (is_cmap_room(c) || is_cmap_furniture(c))
272         return 1;
273     else if (is_cmap_wall(c) || c == S_tree)
274         return 2;
275     else if (is_cmap_corr(c))
276         return 3;
277     else if (is_cmap_water(c))
278         return 4;
279     else if (is_cmap_lava(c))
280         return 5;
281     return 0;
282 }
283 
284 static int
gloc_filter_floodfill_matcharea(int x,int y)285 gloc_filter_floodfill_matcharea(int x, int y)
286 {
287     int glyph = back_to_glyph(x, y);
288 
289     if (!levl[x][y].seenv)
290         return FALSE;
291 
292     if (glyph == g.gloc_filter_floodfill_match_glyph)
293         return TRUE;
294 
295     if (gloc_filter_classify_glyph(glyph)
296         == gloc_filter_classify_glyph(g.gloc_filter_floodfill_match_glyph))
297         return TRUE;
298 
299     return FALSE;
300 }
301 
302 static void
gloc_filter_floodfill(int x,int y)303 gloc_filter_floodfill(int x, int y)
304 {
305     g.gloc_filter_floodfill_match_glyph = back_to_glyph(x, y);
306 
307     set_selection_floodfillchk(gloc_filter_floodfill_matcharea);
308     selection_floodfill(g.gloc_filter_map, x, y, FALSE);
309 }
310 
311 static void
gloc_filter_init(void)312 gloc_filter_init(void)
313 {
314     if (iflags.getloc_filter == GFILTER_AREA) {
315         if (!g.gloc_filter_map) {
316             g.gloc_filter_map = selection_new();
317         }
318         /* special case: if we're in a doorway, try to figure out which
319            direction we're moving, and use that side of the doorway */
320         if (IS_DOOR(levl[u.ux][u.uy].typ)) {
321             if (u.dx || u.dy) {
322                 gloc_filter_floodfill(u.ux + u.dx, u.uy + u.dy);
323             } else {
324                 /* TODO: maybe add both sides of the doorway? */
325             }
326         } else {
327             gloc_filter_floodfill(u.ux, u.uy);
328         }
329     }
330 }
331 
332 static void
gloc_filter_done(void)333 gloc_filter_done(void)
334 {
335     if (g.gloc_filter_map) {
336         selection_free(g.gloc_filter_map, TRUE);
337         g.gloc_filter_map = (struct selectionvar *) 0;
338 
339     }
340 }
341 
342 DISABLE_WARNING_UNREACHABLE_CODE
343 
344 static boolean
gather_locs_interesting(int x,int y,int gloc)345 gather_locs_interesting(int x, int y, int gloc)
346 {
347     int glyph, sym;
348 
349     if (iflags.getloc_filter == GFILTER_VIEW && !cansee(x, y))
350         return FALSE;
351     if (iflags.getloc_filter == GFILTER_AREA && !GLOC_SAME_AREA(x, y)
352         && !GLOC_SAME_AREA(x - 1, y) && !GLOC_SAME_AREA(x, y - 1)
353         && !GLOC_SAME_AREA(x + 1, y) && !GLOC_SAME_AREA(x, y + 1))
354         return FALSE;
355 
356     glyph = glyph_at(x, y);
357     sym = glyph_is_cmap(glyph) ? glyph_to_cmap(glyph) : -1;
358     switch (gloc) {
359     default:
360     case GLOC_MONS:
361         /* unlike '/M', this skips monsters revealed by
362            warning glyphs and remembered unseen ones */
363         return (glyph_is_monster(glyph)
364                 && glyph != monnum_to_glyph(PM_LONG_WORM_TAIL));
365     case GLOC_OBJS:
366         return (glyph_is_object(glyph)
367                 && glyph != objnum_to_glyph(BOULDER)
368                 && glyph != objnum_to_glyph(ROCK));
369     case GLOC_DOOR:
370         return (glyph_is_cmap(glyph)
371                 && (is_cmap_door(sym)
372                     || is_cmap_drawbridge(sym)
373                     || sym == S_ndoor));
374     case GLOC_EXPLORE:
375         return (glyph_is_cmap(glyph)
376                 && !glyph_is_nothing(glyph_to_cmap(glyph))
377                 && (is_cmap_door(sym)
378                     || is_cmap_drawbridge(sym)
379                     || sym == S_ndoor
380                     || is_cmap_room(sym)
381                     || is_cmap_corr(sym))
382                 && (IS_UNEXPLORED_LOC(x + 1, y)
383                     || IS_UNEXPLORED_LOC(x - 1, y)
384                     || IS_UNEXPLORED_LOC(x, y + 1)
385                     || IS_UNEXPLORED_LOC(x, y - 1)));
386     case GLOC_VALID:
387         if (getpos_getvalid)
388             return (*getpos_getvalid)(x, y);
389         /*FALLTHRU*/
390     case GLOC_INTERESTING:
391         return (gather_locs_interesting(x, y, GLOC_DOOR)
392                 || !((glyph_is_cmap(glyph)
393                       && (is_cmap_wall(sym)
394                           || sym == S_tree
395                           || sym == S_bars
396                           || sym == S_ice
397                           || sym == S_air
398                           || sym == S_cloud
399                           || is_cmap_lava(sym)
400                           || is_cmap_water(sym)
401                           || sym == S_ndoor
402                           || is_cmap_room(sym)
403                           || is_cmap_corr(sym)))
404                      || glyph_is_nothing(glyph)
405                      || glyph_is_unexplored(glyph)));
406     }
407     /*NOTREACHED*/
408     return FALSE;
409 }
410 
411 RESTORE_WARNINGS
412 
413 /* gather locations for monsters or objects shown on the map */
414 static void
gather_locs(coord ** arr_p,int * cnt_p,int gloc)415 gather_locs(coord **arr_p, int *cnt_p, int gloc)
416 {
417     int x, y, pass, idx;
418 
419     /*
420      * We always include the hero's location even if there is no monster
421      * (invisible hero without see invisible) or object (usual case)
422      * displayed there.  That way, the count will always be at least 1,
423      * and player has a visual indicator (cursor returns to hero's spot)
424      * highlighting when successive 'm's or 'o's have cycled all the way
425      * through all monsters or objects.
426      *
427      * Hero's spot will always sort to array[0] because it will always
428      * be the shortest distance (namely, 0 units) away from <u.ux,u.uy>.
429      */
430 
431     gloc_filter_init();
432 
433     *cnt_p = idx = 0;
434     for (pass = 0; pass < 2; pass++) {
435         for (x = 1; x < COLNO; x++)
436             for (y = 0; y < ROWNO; y++) {
437                 if ((x == u.ux && y == u.uy)
438                     || gather_locs_interesting(x, y, gloc)) {
439                     if (!pass) {
440                         ++*cnt_p;
441                     } else {
442                         (*arr_p)[idx].x = x;
443                         (*arr_p)[idx].y = y;
444                         ++idx;
445                     }
446                 }
447             }
448 
449         if (!pass) /* end of first pass */
450             *arr_p = (coord *) alloc(*cnt_p * sizeof (coord));
451         else /* end of second pass */
452             qsort(*arr_p, *cnt_p, sizeof (coord), cmp_coord_distu);
453     } /* pass */
454 
455     gloc_filter_done();
456 }
457 
458 char *
dxdy_to_dist_descr(int dx,int dy,boolean fulldir)459 dxdy_to_dist_descr(int dx, int dy, boolean fulldir)
460 {
461     static char buf[30];
462     int dst;
463 
464     if (!dx && !dy) {
465         Sprintf(buf, "here");
466     } else if ((dst = xytod(dx, dy)) != -1) {
467         /* explicit direction; 'one step' is implicit */
468         Sprintf(buf, "%s", directionname(dst));
469     } else {
470         static const char *dirnames[4][2] = {
471             { "n", "north" },
472             { "s", "south" },
473             { "w", "west" },
474             { "e", "east" } };
475         buf[0] = '\0';
476         /* 9999: protect buf[] against overflow caused by invalid values */
477         if (dy) {
478             if (abs(dy) > 9999)
479                 dy = sgn(dy) * 9999;
480             Sprintf(eos(buf), "%d%s%s", abs(dy), dirnames[(dy > 0)][fulldir],
481                     dx ? "," : "");
482         }
483         if (dx) {
484             if (abs(dx) > 9999)
485                 dx = sgn(dx) * 9999;
486             Sprintf(eos(buf), "%d%s", abs(dx),
487                     dirnames[2 + (dx > 0)][fulldir]);
488         }
489     }
490     return buf;
491 }
492 
493 DISABLE_WARNING_FORMAT_NONLITERAL
494 
495 /* coordinate formatting for 'whatis_coord' option */
496 char *
coord_desc(int x,int y,char * outbuf,char cmode)497 coord_desc(int x, int y, char *outbuf, char cmode)
498 {
499     static char screen_fmt[16]; /* [12] suffices: "[%02d,%02d]" */
500     int dx, dy;
501 
502     outbuf[0] = '\0';
503     switch (cmode) {
504     default:
505         break;
506     case GPCOORDS_COMFULL:
507     case GPCOORDS_COMPASS:
508         /* "east", "3s", "2n,4w" */
509         dx = x - u.ux;
510         dy = y - u.uy;
511         Sprintf(outbuf, "(%s)",
512                 dxdy_to_dist_descr(dx, dy, cmode == GPCOORDS_COMFULL));
513         break;
514     case GPCOORDS_MAP: /* x,y */
515         /* upper left corner of map is <1,0>;
516            with default COLNO,ROWNO lower right corner is <79,20> */
517         Sprintf(outbuf, "<%d,%d>", x, y);
518         break;
519     case GPCOORDS_SCREEN: /* y+2,x */
520         /* for normal map sizes, force a fixed-width formatting so that
521            /m, /M, /o, and /O output lines up cleanly; map sizes bigger
522            than Nx999 or 999xM will still work, but not line up like normal
523            when displayed in a column setting */
524         if (!*screen_fmt)
525             Sprintf(screen_fmt, "[%%%sd,%%%sd]",
526                     (ROWNO - 1 + 2 < 100) ? "02" :  "03",
527                     (COLNO - 1 < 100) ? "02" : "03");
528         /* map line 0 is screen row 2;
529            map column 0 isn't used, map column 1 is screen column 1 */
530         Sprintf(outbuf, screen_fmt, y + 2, x);
531         break;
532     }
533     return outbuf;
534 }
535 
536 RESTORE_WARNING_FORMAT_NONLITERAL
537 
538 static void
auto_describe(int cx,int cy)539 auto_describe(int cx, int cy)
540 {
541     coord cc;
542     int sym = 0;
543     char tmpbuf[BUFSZ];
544     const char *firstmatch = "unknown";
545 
546     cc.x = cx;
547     cc.y = cy;
548     if (do_screen_description(cc, TRUE, sym, tmpbuf, &firstmatch,
549                               (struct permonst **) 0)) {
550         (void) coord_desc(cx, cy, tmpbuf, iflags.getpos_coords);
551         custompline((SUPPRESS_HISTORY | OVERRIDE_MSGTYPE),
552                     "%s%s%s%s%s", firstmatch, *tmpbuf ? " " : "", tmpbuf,
553                     (iflags.autodescribe
554                      && getpos_getvalid && !(*getpos_getvalid)(cx, cy))
555                       ? " (illegal)" : "",
556                     (iflags.getloc_travelmode && !is_valid_travelpt(cx, cy))
557                       ? " (no travel path)" : "");
558         curs(WIN_MAP, cx, cy);
559         flush_screen(0);
560     }
561 }
562 
563 boolean
getpos_menu(coord * ccp,int gloc)564 getpos_menu(coord *ccp, int gloc)
565 {
566     coord *garr = DUMMY;
567     int gcount = 0;
568     winid tmpwin;
569     anything any;
570     int i, pick_cnt;
571     menu_item *picks = (menu_item *) 0;
572     char tmpbuf[BUFSZ];
573 
574     gather_locs(&garr, &gcount, gloc);
575 
576     if (gcount < 2) { /* gcount always includes the hero */
577         free((genericptr_t) garr);
578         You("cannot %s %s.",
579             (iflags.getloc_filter == GFILTER_VIEW) ? "see" : "detect",
580             gloc_descr[gloc][0]);
581         return FALSE;
582     }
583 
584     tmpwin = create_nhwindow(NHW_MENU);
585     start_menu(tmpwin, MENU_BEHAVE_STANDARD);
586     any = cg.zeroany;
587 
588     /* gather_locs returns array[0] == you. skip it. */
589     for (i = 1; i < gcount; i++) {
590         char fullbuf[BUFSZ];
591         coord tmpcc;
592         const char *firstmatch = "unknown";
593         int sym = 0;
594 
595         any.a_int = i + 1;
596         tmpcc.x = garr[i].x;
597         tmpcc.y = garr[i].y;
598         if (do_screen_description(tmpcc, TRUE, sym, tmpbuf,
599                               &firstmatch, (struct permonst **)0)) {
600             (void) coord_desc(garr[i].x, garr[i].y, tmpbuf,
601                               iflags.getpos_coords);
602             Snprintf(fullbuf, sizeof fullbuf, "%s%s%s", firstmatch,
603                     (*tmpbuf ? " " : ""), tmpbuf);
604             add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0,
605                      ATR_NONE, fullbuf, MENU_ITEMFLAGS_NONE);
606         }
607     }
608 
609     Sprintf(tmpbuf, "Pick %s%s%s",
610             an(gloc_descr[gloc][1]),
611             gloc_filtertxt[iflags.getloc_filter],
612             iflags.getloc_travelmode ? " for travel destination" : "");
613     end_menu(tmpwin, tmpbuf);
614     pick_cnt = select_menu(tmpwin, PICK_ONE, &picks);
615     destroy_nhwindow(tmpwin);
616     if (pick_cnt > 0) {
617         ccp->x = garr[picks->item.a_int - 1].x;
618         ccp->y = garr[picks->item.a_int - 1].y;
619         free((genericptr_t) picks);
620     }
621     free((genericptr_t) garr);
622     return (pick_cnt > 0);
623 }
624 
625 int
getpos(coord * ccp,boolean force,const char * goal)626 getpos(coord *ccp, boolean force, const char *goal)
627 {
628     const char *cp;
629     static struct {
630         int nhkf, ret;
631     } const pick_chars_def[] = {
632         { NHKF_GETPOS_PICK, LOOK_TRADITIONAL },
633         { NHKF_GETPOS_PICK_Q, LOOK_QUICK },
634         { NHKF_GETPOS_PICK_O, LOOK_ONCE },
635         { NHKF_GETPOS_PICK_V, LOOK_VERBOSE }
636     };
637     static const int mMoOdDxX_def[] = {
638         NHKF_GETPOS_MON_NEXT,
639         NHKF_GETPOS_MON_PREV,
640         NHKF_GETPOS_OBJ_NEXT,
641         NHKF_GETPOS_OBJ_PREV,
642         NHKF_GETPOS_DOOR_NEXT,
643         NHKF_GETPOS_DOOR_PREV,
644         NHKF_GETPOS_UNEX_NEXT,
645         NHKF_GETPOS_UNEX_PREV,
646         NHKF_GETPOS_INTERESTING_NEXT,
647         NHKF_GETPOS_INTERESTING_PREV,
648         NHKF_GETPOS_VALID_NEXT,
649         NHKF_GETPOS_VALID_PREV
650     };
651     char pick_chars[6];
652     char mMoOdDxX[13];
653     int result = 0;
654     int cx, cy, i, c;
655     int sidx, tx, ty;
656     boolean msg_given = TRUE; /* clear message window by default */
657     boolean show_goal_msg = FALSE;
658     boolean hilite_state = FALSE;
659     coord *garr[NUM_GLOCS] = DUMMY;
660     int gcount[NUM_GLOCS] = DUMMY;
661     int gidx[NUM_GLOCS] = DUMMY;
662 
663     for (i = 0; i < SIZE(pick_chars_def); i++)
664         pick_chars[i] = g.Cmd.spkeys[pick_chars_def[i].nhkf];
665     pick_chars[SIZE(pick_chars_def)] = '\0';
666 
667     for (i = 0; i < SIZE(mMoOdDxX_def); i++)
668         mMoOdDxX[i] = g.Cmd.spkeys[mMoOdDxX_def[i]];
669     mMoOdDxX[SIZE(mMoOdDxX_def)] = '\0';
670 
671     if (!goal)
672         goal = "desired location";
673     if (flags.verbose) {
674         pline("(For instructions type a '%s')",
675               visctrl(g.Cmd.spkeys[NHKF_GETPOS_HELP]));
676         msg_given = TRUE;
677     }
678     cx = ccp->x;
679     cy = ccp->y;
680 #ifdef CLIPPING
681     cliparound(cx, cy);
682 #endif
683     curs(WIN_MAP, cx, cy);
684     flush_screen(0);
685 #ifdef MAC
686     lock_mouse_cursor(TRUE);
687 #endif
688     for (;;) {
689         if (show_goal_msg) {
690             pline("Move cursor to %s:", goal);
691             curs(WIN_MAP, cx, cy);
692             flush_screen(0);
693             show_goal_msg = FALSE;
694         } else if (iflags.autodescribe && !msg_given && !hilite_state) {
695             auto_describe(cx, cy);
696         }
697 
698         c = nh_poskey(&tx, &ty, &sidx);
699 
700         if (hilite_state) {
701             (*getpos_hilitefunc)(2);
702             hilite_state = FALSE;
703             curs(WIN_MAP, cx, cy);
704             flush_screen(0);
705         }
706 
707         if (iflags.autodescribe)
708             msg_given = FALSE;
709 
710         if (c == g.Cmd.spkeys[NHKF_ESC]) {
711             cx = cy = -10;
712             msg_given = TRUE; /* force clear */
713             result = -1;
714             break;
715         }
716         if (c == 0) {
717             if (!isok(tx, ty))
718                 continue;
719             /* a mouse click event, just assign and return */
720             cx = tx;
721             cy = ty;
722             break;
723         }
724         if ((cp = index(pick_chars, c)) != 0) {
725             /* '.' => 0, ',' => 1, ';' => 2, ':' => 3 */
726             result = pick_chars_def[(int) (cp - pick_chars)].ret;
727             break;
728         }
729         for (i = 0; i < 8; i++) {
730             int dx, dy;
731 
732             if (g.Cmd.dirchars[i] == c) {
733                 /* a normal movement letter or digit */
734                 dx = xdir[i];
735                 dy = ydir[i];
736             } else if (g.Cmd.alphadirchars[i] == lowc((char) c)
737                        || (g.Cmd.num_pad && g.Cmd.dirchars[i] == (c & 0177))) {
738                 /* a shifted movement letter or Meta-digit */
739                 if (iflags.getloc_moveskip) {
740                     /* skip same glyphs */
741                     int glyph = glyph_at(cx, cy);
742 
743                     dx = xdir[i];
744                     dy = ydir[i];
745                     while (isok(cx + dx, cy + dy)
746                            && glyph == glyph_at(cx + dx, cy + dy)
747                            && isok(cx + dx + xdir[i], cy + dy + ydir[i])
748                            && glyph == glyph_at(cx + dx + xdir[i],
749                                                 cy + dy + ydir[i])) {
750                         dx += xdir[i];
751                         dy += ydir[i];
752                     }
753                 } else {
754                     dx = 8 * xdir[i];
755                     dy = 8 * ydir[i];
756                 }
757             } else
758                 continue;
759 
760             /* truncate at map edge; diagonal moves complicate this... */
761             if (cx + dx < 1) {
762                 dy -= sgn(dy) * (1 - (cx + dx));
763                 dx = 1 - cx; /* so that (cx+dx == 1) */
764             } else if (cx + dx > COLNO - 1) {
765                 dy += sgn(dy) * ((COLNO - 1) - (cx + dx));
766                 dx = (COLNO - 1) - cx;
767             }
768             if (cy + dy < 0) {
769                 dx -= sgn(dx) * (0 - (cy + dy));
770                 dy = 0 - cy; /* so that (cy+dy == 0) */
771             } else if (cy + dy > ROWNO - 1) {
772                 dx += sgn(dx) * ((ROWNO - 1) - (cy + dy));
773                 dy = (ROWNO - 1) - cy;
774             }
775             cx += dx;
776             cy += dy;
777             goto nxtc;
778         }
779 
780         if (c == g.Cmd.spkeys[NHKF_GETPOS_HELP] || redraw_cmd(c)) {
781             if (c == g.Cmd.spkeys[NHKF_GETPOS_HELP])
782                 getpos_help(force, goal);
783             else /* ^R */
784                 docrt(); /* redraw */
785             /* update message window to reflect that we're still targetting */
786             show_goal_msg = TRUE;
787             msg_given = TRUE;
788         } else if (c == g.Cmd.spkeys[NHKF_GETPOS_SHOWVALID]
789                    && getpos_hilitefunc) {
790             if (!hilite_state) {
791                 (*getpos_hilitefunc)(0);
792                 (*getpos_hilitefunc)(1);
793                 hilite_state = TRUE;
794             }
795             goto nxtc;
796         } else if (c == g.Cmd.spkeys[NHKF_GETPOS_AUTODESC]) {
797             iflags.autodescribe = !iflags.autodescribe;
798             pline("Automatic description %sis %s.",
799                   flags.verbose ? "of features under cursor " : "",
800                   iflags.autodescribe ? "on" : "off");
801             if (!iflags.autodescribe)
802                 show_goal_msg = TRUE;
803             msg_given = TRUE;
804             goto nxtc;
805         } else if (c == g.Cmd.spkeys[NHKF_GETPOS_LIMITVIEW]) {
806             static const char *const view_filters[NUM_GFILTER] = {
807                 "Not limiting targets",
808                 "Limiting targets to those in sight",
809                 "Limiting targets to those in same area"
810             };
811 
812             iflags.getloc_filter = (iflags.getloc_filter + 1) % NUM_GFILTER;
813             for (i = 0; i < NUM_GLOCS; i++) {
814                 if (garr[i]) {
815                     free((genericptr_t) garr[i]);
816                     garr[i] = NULL;
817                 }
818                 gidx[i] = gcount[i] = 0;
819             }
820             pline("%s.", view_filters[iflags.getloc_filter]);
821             msg_given = TRUE;
822             goto nxtc;
823         } else if (c == g.Cmd.spkeys[NHKF_GETPOS_MENU]) {
824             iflags.getloc_usemenu = !iflags.getloc_usemenu;
825             pline("%s a menu to show possible targets%s.",
826                   iflags.getloc_usemenu ? "Using" : "Not using",
827                   iflags.getloc_usemenu
828                       ? " for 'm|M', 'o|O', 'd|D', and 'x|X'" : "");
829             msg_given = TRUE;
830             goto nxtc;
831         } else if (c == g.Cmd.spkeys[NHKF_GETPOS_SELF]) {
832             /* reset 'm&M', 'o&O', &c; otherwise, there's no way for player
833                to achieve that except by manually cycling through all spots */
834             for (i = 0; i < NUM_GLOCS; i++)
835                 gidx[i] = 0;
836             cx = u.ux;
837             cy = u.uy;
838             goto nxtc;
839         } else if (c == g.Cmd.spkeys[NHKF_GETPOS_MOVESKIP]) {
840             iflags.getloc_moveskip = !iflags.getloc_moveskip;
841             pline("%skipping over similar terrain when fastmoving the cursor.",
842                   iflags.getloc_moveskip ? "S" : "Not s");
843             msg_given = TRUE;
844             goto nxtc;
845         } else if ((cp = index(mMoOdDxX, c)) != 0) { /* 'm|M', 'o|O', &c */
846             /* nearest or farthest monster or object or door or unexplored */
847             int gtmp = (int) (cp - mMoOdDxX), /* 0..7 */
848                 gloc = gtmp >> 1;             /* 0..3 */
849 
850             if (iflags.getloc_usemenu) {
851                 coord tmpcrd;
852 
853                 if (getpos_menu(&tmpcrd, gloc)) {
854                     cx = tmpcrd.x;
855                     cy = tmpcrd.y;
856                 }
857                 goto nxtc;
858             }
859 
860             if (!garr[gloc]) {
861                 gather_locs(&garr[gloc], &gcount[gloc], gloc);
862                 gidx[gloc] = 0; /* garr[][0] is hero's spot */
863             }
864             if (!(gtmp & 1)) {  /* c=='m' || c=='o' || c=='d' || c=='x') */
865                 gidx[gloc] = (gidx[gloc] + 1) % gcount[gloc];
866             } else {            /* c=='M' || c=='O' || c=='D' || c=='X') */
867                 if (--gidx[gloc] < 0)
868                     gidx[gloc] = gcount[gloc] - 1;
869             }
870             cx = garr[gloc][gidx[gloc]].x;
871             cy = garr[gloc][gidx[gloc]].y;
872             goto nxtc;
873         } else {
874             if (!index(quitchars, c)) {
875                 char matching[MAXPCHARS];
876                 int pass, lo_x, lo_y, hi_x, hi_y, k = 0;
877 
878                 (void) memset((genericptr_t) matching, 0, sizeof matching);
879                 for (sidx = 1; sidx < MAXPCHARS; sidx++) { /* [0] left as 0 */
880                     if (IS_DOOR(sidx) || IS_WALL(sidx)
881                         || sidx == SDOOR || sidx == SCORR
882                         || glyph_to_cmap(k) == S_room
883                         || glyph_to_cmap(k) == S_darkroom
884                         || glyph_to_cmap(k) == S_corr
885                         || glyph_to_cmap(k) == S_litcorr)
886                         continue;
887                     if (c == defsyms[sidx].sym || c == (int) g.showsyms[sidx])
888                         matching[sidx] = (char) ++k;
889                 }
890                 if (k) {
891                     for (pass = 0; pass <= 1; pass++) {
892                         /* pass 0: just past current pos to lower right;
893                            pass 1: upper left corner to current pos */
894                         lo_y = (pass == 0) ? cy : 0;
895                         hi_y = (pass == 0) ? ROWNO - 1 : cy;
896                         for (ty = lo_y; ty <= hi_y; ty++) {
897                             lo_x = (pass == 0 && ty == lo_y) ? cx + 1 : 1;
898                             hi_x = (pass == 1 && ty == hi_y) ? cx : COLNO - 1;
899                             for (tx = lo_x; tx <= hi_x; tx++) {
900                                 /* first, look at what is currently visible
901                                    (might be monster) */
902                                 k = glyph_at(tx, ty);
903                                 if (glyph_is_cmap(k)
904                                     && matching[glyph_to_cmap(k)])
905                                     goto foundc;
906                                 /* next, try glyph that's remembered here
907                                    (might be trap or object) */
908                                 if (g.level.flags.hero_memory
909                                     /* !terrainmode: don't move to remembered
910                                        trap or object if not currently shown */
911                                     && !iflags.terrainmode) {
912                                     k = levl[tx][ty].glyph;
913                                     if (glyph_is_cmap(k)
914                                         && matching[glyph_to_cmap(k)])
915                                         goto foundc;
916                                 }
917                                 /* last, try actual terrain here (shouldn't
918                                    we be using g.lastseentyp[][] instead?) */
919                                 if (levl[tx][ty].seenv) {
920                                     k = back_to_glyph(tx, ty);
921                                     if (glyph_is_cmap(k)
922                                         && matching[glyph_to_cmap(k)])
923                                         goto foundc;
924                                 }
925                                 continue;
926                             foundc:
927                                 cx = tx, cy = ty;
928                                 if (msg_given) {
929                                     clear_nhwindow(WIN_MESSAGE);
930                                     msg_given = FALSE;
931                                 }
932                                 goto nxtc;
933                             } /* column */
934                         }     /* row */
935                     }         /* pass */
936                     pline("Can't find dungeon feature '%c'.", c);
937                     msg_given = TRUE;
938                     goto nxtc;
939                 } else {
940                     char note[QBUFSZ];
941 
942                     if (!force)
943                         Strcpy(note, "aborted");
944                     else /* hjkl */
945                         Sprintf(note, "use '%c', '%c', '%c', '%c' or '%s'",
946                                 g.Cmd.move_W, g.Cmd.move_S, g.Cmd.move_N, g.Cmd.move_E,
947                                 visctrl(g.Cmd.spkeys[NHKF_GETPOS_PICK]));
948                     pline("Unknown direction: '%s' (%s).", visctrl((char) c),
949                           note);
950                     msg_given = TRUE;
951                 } /* k => matching */
952             }     /* !quitchars */
953             if (force)
954                 goto nxtc;
955             pline("Done.");
956             msg_given = FALSE; /* suppress clear */
957             cx = -1;
958             cy = 0;
959             result = 0; /* not -1 */
960             break;
961         }
962     nxtc:
963         ;
964 #ifdef CLIPPING
965         cliparound(cx, cy);
966 #endif
967         curs(WIN_MAP, cx, cy);
968         flush_screen(0);
969     }
970 #ifdef MAC
971     lock_mouse_cursor(FALSE);
972 #endif
973     if (msg_given)
974         clear_nhwindow(WIN_MESSAGE);
975     ccp->x = cx;
976     ccp->y = cy;
977     for (i = 0; i < NUM_GLOCS; i++)
978         if (garr[i])
979             free((genericptr_t) garr[i]);
980     getpos_hilitefunc = (void (*)(int)) 0;
981     getpos_getvalid = (boolean (*)(int, int)) 0;
982     return result;
983 }
984 
985 /* allocate space for a monster's name; removes old name if there is one */
986 void
new_mgivenname(struct monst * mon,int lth)987 new_mgivenname(struct monst *mon,
988                int lth) /* desired length (caller handles adding 1
989                            for terminator) */
990 {
991     if (lth) {
992         /* allocate mextra if necessary; otherwise get rid of old name */
993         if (!mon->mextra)
994             mon->mextra = newmextra();
995         else
996             free_mgivenname(mon); /* already has mextra, might also have name */
997         MGIVENNAME(mon) = (char *) alloc((unsigned) lth);
998     } else {
999         /* zero length: the new name is empty; get rid of the old name */
1000         if (has_mgivenname(mon))
1001             free_mgivenname(mon);
1002     }
1003 }
1004 
1005 /* release a monster's name; retains mextra even if all fields are now null */
1006 void
free_mgivenname(struct monst * mon)1007 free_mgivenname(struct monst *mon)
1008 {
1009     if (has_mgivenname(mon)) {
1010         free((genericptr_t) MGIVENNAME(mon));
1011         MGIVENNAME(mon) = (char *) 0;
1012     }
1013 }
1014 
1015 /* allocate space for an object's name; removes old name if there is one */
1016 void
new_oname(struct obj * obj,int lth)1017 new_oname(struct obj *obj,
1018           int lth) /* desired length (caller handles adding 1
1019                       for terminator) */
1020 {
1021     if (lth) {
1022         /* allocate oextra if necessary; otherwise get rid of old name */
1023         if (!obj->oextra)
1024             obj->oextra = newoextra();
1025         else
1026             free_oname(obj); /* already has oextra, might also have name */
1027         ONAME(obj) = (char *) alloc((unsigned) lth);
1028     } else {
1029         /* zero length: the new name is empty; get rid of the old name */
1030         if (has_oname(obj))
1031             free_oname(obj);
1032     }
1033 }
1034 
1035 /* release an object's name; retains oextra even if all fields are now null */
1036 void
free_oname(struct obj * obj)1037 free_oname(struct obj *obj)
1038 {
1039     if (has_oname(obj)) {
1040         free((genericptr_t) ONAME(obj));
1041         ONAME(obj) = (char *) 0;
1042     }
1043 }
1044 
1045 /*  safe_oname() always returns a valid pointer to
1046  *  a string, either the pointer to an object's name
1047  *  if it has one, or a pointer to an empty string
1048  *  if it doesn't.
1049  */
1050 const char *
safe_oname(struct obj * obj)1051 safe_oname(struct obj *obj)
1052 {
1053     if (has_oname(obj))
1054         return ONAME(obj);
1055     return "";
1056 }
1057 
1058 /* historical note: this returns a monster pointer because it used to
1059    allocate a new bigger block of memory to hold the monster and its name */
1060 struct monst *
christen_monst(struct monst * mtmp,const char * name)1061 christen_monst(struct monst *mtmp, const char *name)
1062 {
1063     int lth;
1064     char buf[PL_PSIZ];
1065 
1066     /* g.dogname & g.catname are PL_PSIZ arrays; object names have same limit */
1067     lth = (name && *name) ? ((int) strlen(name) + 1) : 0;
1068     if (lth > PL_PSIZ) {
1069         lth = PL_PSIZ;
1070         name = strncpy(buf, name, PL_PSIZ - 1);
1071         buf[PL_PSIZ - 1] = '\0';
1072     }
1073     new_mgivenname(mtmp, lth); /* removes old name if one is present */
1074     if (lth)
1075         Strcpy(MGIVENNAME(mtmp), name);
1076     return mtmp;
1077 }
1078 
1079 /* check whether user-supplied name matches or nearly matches an unnameable
1080    monster's name; if so, give an alternate reject message for do_mgivenname() */
1081 static boolean
alreadynamed(struct monst * mtmp,char * monnambuf,char * usrbuf)1082 alreadynamed(struct monst *mtmp, char *monnambuf, char *usrbuf)
1083 {
1084     char pronounbuf[10], *p;
1085 
1086     if (fuzzymatch(usrbuf, monnambuf, " -_", TRUE)
1087         /* catch trying to name "the Oracle" as "Oracle" */
1088         || (!strncmpi(monnambuf, "the ", 4)
1089             && fuzzymatch(usrbuf, monnambuf + 4, " -_", TRUE))
1090         /* catch trying to name "invisible Orcus" as "Orcus" */
1091         || ((p = strstri(monnambuf, "invisible ")) != 0
1092             && fuzzymatch(usrbuf, p + 10, " -_", TRUE))
1093         /* catch trying to name "the {priest,Angel} of Crom" as "Crom" */
1094         || ((p = strstri(monnambuf, " of ")) != 0
1095             && fuzzymatch(usrbuf, p + 4, " -_", TRUE))) {
1096         pline("%s is already called %s.",
1097               upstart(strcpy(pronounbuf, mhe(mtmp))), monnambuf);
1098         return TRUE;
1099     } else if (mtmp->data == &mons[PM_JUIBLEX]
1100                && strstri(monnambuf, "Juiblex")
1101                && !strcmpi(usrbuf, "Jubilex")) {
1102         pline("%s doesn't like being called %s.", upstart(monnambuf), usrbuf);
1103         return TRUE;
1104     }
1105     return FALSE;
1106 }
1107 
1108 /* allow player to assign a name to some chosen monster */
1109 static void
do_mgivenname(void)1110 do_mgivenname(void)
1111 {
1112     char buf[BUFSZ], monnambuf[BUFSZ], qbuf[QBUFSZ];
1113     coord cc;
1114     int cx, cy;
1115     struct monst *mtmp = 0;
1116 
1117     if (Hallucination) {
1118         You("would never recognize it anyway.");
1119         return;
1120     }
1121     cc.x = u.ux;
1122     cc.y = u.uy;
1123     if (getpos(&cc, FALSE, "the monster you want to name") < 0
1124         || !isok(cc.x, cc.y))
1125         return;
1126     cx = cc.x, cy = cc.y;
1127 
1128     if (cx == u.ux && cy == u.uy) {
1129         if (u.usteed && canspotmon(u.usteed)) {
1130             mtmp = u.usteed;
1131         } else {
1132             pline("This %s creature is called %s and cannot be renamed.",
1133                   beautiful(), g.plname);
1134             return;
1135         }
1136     } else
1137         mtmp = m_at(cx, cy);
1138 
1139     if (!mtmp
1140         || (!sensemon(mtmp)
1141             && (!(cansee(cx, cy) || see_with_infrared(mtmp))
1142                 || mtmp->mundetected || M_AP_TYPE(mtmp) == M_AP_FURNITURE
1143                 || M_AP_TYPE(mtmp) == M_AP_OBJECT
1144                 || (mtmp->minvis && !See_invisible)))) {
1145         pline("I see no monster there.");
1146         return;
1147     }
1148     /* special case similar to the one in lookat() */
1149     Sprintf(qbuf, "What do you want to call %s?",
1150             distant_monnam(mtmp, ARTICLE_THE, monnambuf));
1151     buf[0] = '\0';
1152 #ifdef EDIT_GETLIN
1153     /* if there's an existing name, make it be the default answer */
1154     if (has_mgivenname(mtmp))
1155         Strcpy(buf, MGIVENNAME(mtmp));
1156 #endif
1157     getlin(qbuf, buf);
1158     if (!*buf || *buf == '\033')
1159         return;
1160     /* strip leading and trailing spaces; unnames monster if all spaces */
1161     (void) mungspaces(buf);
1162 
1163     /* Unique monsters have their own specific names or titles.
1164      * Shopkeepers, temple priests and other minions use alternate
1165      * name formatting routines which ignore any user-supplied name.
1166      *
1167      * Don't say the name is being rejected if it happens to match
1168      * the existing name.
1169      *
1170      * TODO: should have an alternate message when the attempt is to
1171      * remove existing name without assigning a new one.
1172      */
1173     if ((mtmp->data->geno & G_UNIQ) && !mtmp->ispriest) {
1174         if (!alreadynamed(mtmp, monnambuf, buf))
1175             pline("%s doesn't like being called names!", upstart(monnambuf));
1176     } else if (mtmp->isshk
1177                && !(Deaf || mtmp->msleeping || !mtmp->mcanmove
1178                     || mtmp->data->msound <= MS_ANIMAL)) {
1179         if (!alreadynamed(mtmp, monnambuf, buf))
1180             verbalize("I'm %s, not %s.", shkname(mtmp), buf);
1181     } else if (mtmp->ispriest || mtmp->isminion || mtmp->isshk
1182                || mtmp->data == &mons[PM_GHOST] || has_ebones(mtmp)) {
1183         if (!alreadynamed(mtmp, monnambuf, buf))
1184             pline("%s will not accept the name %s.", upstart(monnambuf), buf);
1185     } else
1186         (void) christen_monst(mtmp, buf);
1187 }
1188 
1189 /*
1190  * This routine used to change the address of 'obj' so be unsafe if not
1191  * used with extreme care.  Applying a name to an object no longer
1192  * allocates a replacement object, so that old risk is gone.
1193  */
1194 static void
do_oname(register struct obj * obj)1195 do_oname(register struct obj *obj)
1196 {
1197     char buf[BUFSZ], qbuf[QBUFSZ];
1198     const char *aname;
1199     short objtyp;
1200 
1201     /* Do this now because there's no point in even asking for a name */
1202     if (obj->otyp == SPE_NOVEL) {
1203         pline("%s already has a published name.", Ysimple_name2(obj));
1204         return;
1205     }
1206 
1207     Sprintf(qbuf, "What do you want to name %s ",
1208             is_plural(obj) ? "these" : "this");
1209     (void) safe_qbuf(qbuf, qbuf, "?", obj, xname, simpleonames, "item");
1210     buf[0] = '\0';
1211 #ifdef EDIT_GETLIN
1212     /* if there's an existing name, make it be the default answer */
1213     if (has_oname(obj))
1214         Strcpy(buf, ONAME(obj));
1215 #endif
1216     getlin(qbuf, buf);
1217     if (!*buf || *buf == '\033')
1218         return;
1219     /* strip leading and trailing spaces; unnames item if all spaces */
1220     (void) mungspaces(buf);
1221 
1222     /*
1223      * We don't violate illiteracy conduct here, although it is
1224      * arguable that we should for anything other than "X".  Doing so
1225      * would make attaching player's notes to hero's inventory have an
1226      * in-game effect, which may or may not be the correct thing to do.
1227      */
1228 
1229     /* relax restrictions over proper capitalization for artifacts */
1230     if ((aname = artifact_name(buf, &objtyp)) != 0 && objtyp == obj->otyp)
1231         Strcpy(buf, aname);
1232 
1233     if (obj->oartifact) {
1234         pline_The("artifact seems to resist the attempt.");
1235         return;
1236     } else if (restrict_name(obj, buf) || exist_artifact(obj->otyp, buf)) {
1237         /* this used to be flavored as actually writing on the weapon and
1238          * breaking illiterate, but that was silly for a number of reasons. */
1239         pline("For some reason, you can't mentally assign that name to %s.",
1240               is_plural(obj) ? "them" : "it");
1241         display_nhwindow(WIN_MESSAGE, FALSE);
1242     }
1243     ++g.via_naming; /* This ought to be an argument rather than a static... */
1244     obj = oname(obj, buf);
1245     --g.via_naming; /* ...but oname() is used in a lot of places, so defer. */
1246 }
1247 
1248 DISABLE_WARNING_FORMAT_NONLITERAL /* sprintf on an arbitrary format string from
1249                                    * an array of format strings */
1250 
1251 /* Confer a grand-sounding name on a weapon. The weapon is assumed to be a
1252  * non-artifact and have a good enchantment or other good property. */
1253 struct obj *
weapon_oname(struct obj * wpn)1254 weapon_oname(struct obj *wpn)
1255 {
1256     /* Don't randomly name stacks. */
1257     if (wpn->quan > 1)
1258         return wpn;
1259 
1260     char basename[BUFSZ];
1261     int skill = weapon_type(wpn);
1262     if (skill == P_SABER)
1263         Strcpy(basename, "Sabre");
1264     else if (wpn->otyp == TWO_HANDED_SWORD)
1265         Strcpy(basename, "Claymore");
1266     else if (is_sword(wpn))
1267         Strcpy(basename, "Sword");
1268     else if (wpn->otyp == MORNING_STAR)
1269         Strcpy(basename, "Morning Star");
1270     else if (wpn->otyp == RUBBER_HOSE)
1271         Strcpy(basename, "Hose");
1272     else if ((skill == P_POLEARMS) || (skill == P_KNIFE)
1273              || (wpn->otyp == ATHAME))
1274         Strcpy(basename, OBJ_NAME(objects[wpn->otyp]));
1275     else
1276         Strcpy(basename, weapon_descr(wpn));
1277 
1278     /* Possible names. Any names with a %s in them must be sprintf'd with
1279      * basename. */
1280     const char* wpn_names[] = {
1281         "%s of Justice",  "%s of Honor",   "%s of Glory",    "%s of Revenge",
1282         "%s of Sorrow",   "%s of Yendor",  "%s of Victory",  "%s of Carnage",
1283         "%s of Serenity", "%s of Fable",   "%s of Legend",   "%s of Integrity",
1284         "%s of Redress",  "%s of Fate",    "%s of Punition", "%s of Reckoning",
1285         "%s of Omen",     "%s of Truth",   "%s of Virtue",   "%s of Bloodlust",
1286         "%s of Disaster", "%s of Torment", "%s of Doom",     "%s of the Gods",
1287         "%s of Quarrels", "%s of Grace",   "%s of Respect",  "%s of Paranoia",
1288         "%s of Fury",     "%s of Hubris",  "%s of Insanity",
1289         "%s of the Horde","%s of Gilgamesh","%s of the Planes",
1290         "%s of Excellence", "%s of Vengeance", "%s of Swift Defeat",
1291         "%s of Protection",
1292         "Righteous %s",   "Mighty %s",     "Unstoppable %s", "Sacred %s",
1293         "Holy %s",        "Lucky %s",      "Dudley's %s",    "Chaos %s",
1294         "Hungry %s",      "Inexorable %s", "Virtuous %s",    "Death %s",
1295         "Unlimited %s",   "Infinite %s",   "Chaos %s",       "Peerless %s",
1296         "Elegant %s",     "Artful %s",     "Wicked %s",      "Clever %s",
1297         "Due Process",    "Puddingbane",   "Vladsbane",      "Newtsbane",
1298         "Monster Slayer", "Orphan Maker",  "Aggressive Negotiation",
1299         "Old Reliable",   "Punisher",      "Barman's Friend"
1300     };
1301 
1302     char buf[BUFSZ];
1303     if (!rn2(25 - (2 * wpn->spe))) {
1304         char nbuf[PL_NSIZ+1];
1305         const char* ttname = tt_name();
1306         if (ttname) {
1307             Strcpy(nbuf, ttname);
1308             Sprintf(buf, "%s of %s", upstart(basename), upstart(nbuf));
1309             return oname(wpn, buf);
1310         }
1311         /* if a name couldn't be found, fall through to default */
1312     }
1313     const char* name = wpn_names[rn2(SIZE(wpn_names))];
1314     if (strstri(name, "%s")) {
1315         Sprintf(buf, name, upstart(basename));
1316         return oname(wpn, buf);
1317     }
1318     else {
1319         return oname(wpn, name);
1320     }
1321 }
1322 
1323 RESTORE_WARNING_FORMAT_NONLITERAL
1324 
1325 struct obj *
oname(struct obj * obj,const char * name)1326 oname(struct obj *obj, const char *name)
1327 {
1328     int lth;
1329     char buf[PL_PSIZ];
1330 
1331     lth = *name ? (int) (strlen(name) + 1) : 0;
1332     if (lth > PL_PSIZ) {
1333         lth = PL_PSIZ;
1334         name = strncpy(buf, name, PL_PSIZ - 1);
1335         buf[PL_PSIZ - 1] = '\0';
1336     }
1337     /* If named artifact exists in the game, do not create another.
1338      * Also trying to create an artifact shouldn't de-artifact
1339      * it (e.g. Excalibur from prayer). In this case the object
1340      * will retain its current name. */
1341     if (obj->oartifact || (lth && exist_artifact(obj->otyp, name)))
1342         return obj;
1343 
1344     new_oname(obj, lth); /* removes old name if one is present */
1345     if (lth)
1346         Strcpy(ONAME(obj), name);
1347 
1348     if (lth)
1349         artifact_exists(obj, name, TRUE);
1350     if (obj->oartifact) {
1351         /* can't dual-wield with artifact as secondary weapon */
1352         if (obj == uswapwep)
1353             untwoweapon();
1354         /* activate warning if you've just named your weapon "Sting" */
1355         if (obj == uwep)
1356             set_artifact_intrinsic(obj, TRUE, W_WEP);
1357         /* if obj is owned by a shop, increase your bill */
1358         if (obj->unpaid)
1359             alter_cost(obj, 0L);
1360         if (g.via_naming) {
1361             livelog_printf(LL_ARTIFACT, "chose %s to be named \"%s\"", ansimpleoname(obj), bare_artifactname(obj));
1362         }
1363         /* set up specific materials for the artifact */
1364         switch(obj->oartifact) {
1365         case ART_SUNSWORD:
1366         case ART_ITLACHIAYAQUE:
1367             set_material(obj, GOLD);
1368             break;
1369         case ART_WEREBANE:
1370         case ART_DEMONBANE:
1371         case ART_GRAYSWANDIR:
1372         /* case ART_MITRE_OF_HOLINESS: */
1373             set_material(obj, SILVER);
1374             break;
1375         case ART_SCEPTRE_OF_MIGHT:
1376             /* don't make it hurt elves */
1377             obj->material = METAL;
1378             break;
1379         case ART_YENDORIAN_EXPRESS_CARD:
1380             set_material(obj, PLATINUM);
1381             break;
1382         default:
1383             /* prevent any wishes for materials on an artifact */
1384             set_material(obj, objects[obj->otyp].oc_material);
1385         }
1386     }
1387     if (carried(obj))
1388         update_inventory();
1389     return obj;
1390 }
1391 
1392 boolean
objtyp_is_callable(int i)1393 objtyp_is_callable(int i)
1394 {
1395     if (objects[i].oc_uname)
1396         return TRUE;
1397 
1398     switch(objects[i].oc_class) {
1399     case SCROLL_CLASS:
1400     case POTION_CLASS:
1401     case WAND_CLASS:
1402     case RING_CLASS:
1403     case AMULET_CLASS:
1404     case GEM_CLASS:
1405     case SPBOOK_CLASS:
1406     case ARMOR_CLASS:
1407     case TOOL_CLASS:
1408     case VENOM_CLASS:
1409         if (OBJ_DESCR(objects[i]))
1410             return TRUE;
1411         break;
1412     default:
1413         break;
1414     }
1415     return FALSE;
1416 }
1417 
1418 /* getobj callback for object to name (specific item) - anything but gold */
1419 static int
name_ok(struct obj * obj)1420 name_ok(struct obj *obj)
1421 {
1422     if (!obj || obj->oclass == COIN_CLASS)
1423         return GETOBJ_EXCLUDE;
1424 
1425     return GETOBJ_SUGGEST;
1426 }
1427 
1428 /* getobj callback for object to call (name its type) */
1429 static int
call_ok(struct obj * obj)1430 call_ok(struct obj *obj)
1431 {
1432     if (!obj || !objtyp_is_callable(obj->otyp))
1433         return GETOBJ_EXCLUDE;
1434 
1435     return GETOBJ_SUGGEST;
1436 }
1437 
1438 /* C and #name commands - player can name monster or object or type of obj */
1439 int
docallcmd(void)1440 docallcmd(void)
1441 {
1442     struct obj *obj;
1443     winid win;
1444     anything any;
1445     menu_item *pick_list = 0;
1446     char ch;
1447     /* if player wants a,b,c instead of i,o when looting, do that here too */
1448     boolean abc = flags.lootabc;
1449 
1450     win = create_nhwindow(NHW_MENU);
1451     start_menu(win, MENU_BEHAVE_STANDARD);
1452     any = cg.zeroany;
1453     any.a_char = 'm'; /* group accelerator 'C' */
1454     add_menu(win, &nul_glyphinfo, &any, abc ? 0 : any.a_char, 'C',
1455              ATR_NONE, "a monster", MENU_ITEMFLAGS_NONE);
1456     if (g.invent) {
1457         /* we use y and n as accelerators so that we can accept user's
1458            response keyed to old "name an individual object?" prompt */
1459         any.a_char = 'i'; /* group accelerator 'y' */
1460         add_menu(win, &nul_glyphinfo, &any, abc ? 0 : any.a_char, 'y',
1461                  ATR_NONE, "a particular object in inventory",
1462                  MENU_ITEMFLAGS_NONE);
1463         any.a_char = 'o'; /* group accelerator 'n' */
1464         add_menu(win, &nul_glyphinfo, &any, abc ? 0 : any.a_char, 'n',
1465                  ATR_NONE, "the type of an object in inventory",
1466                  MENU_ITEMFLAGS_NONE);
1467     }
1468     any.a_char = 'f'; /* group accelerator ',' (or ':' instead?) */
1469     add_menu(win, &nul_glyphinfo, &any, abc ? 0 : any.a_char, ',',
1470              ATR_NONE, "the type of an object upon the floor",
1471              MENU_ITEMFLAGS_NONE);
1472     any.a_char = 'd'; /* group accelerator '\' */
1473     add_menu(win, &nul_glyphinfo, &any, abc ? 0 : any.a_char, '\\',
1474              ATR_NONE, "the type of an object on discoveries list",
1475              MENU_ITEMFLAGS_NONE);
1476     any.a_char = 'a'; /* group accelerator 'l' */
1477     add_menu(win, &nul_glyphinfo, &any, abc ? 0 : any.a_char, 'l',
1478              ATR_NONE, "record an annotation for the current level",
1479              MENU_ITEMFLAGS_NONE);
1480     end_menu(win, "What do you want to name?");
1481     if (select_menu(win, PICK_ONE, &pick_list) > 0) {
1482         ch = pick_list[0].item.a_char;
1483         free((genericptr_t) pick_list);
1484     } else
1485         ch = 'q';
1486     destroy_nhwindow(win);
1487 
1488     switch (ch) {
1489     default:
1490     case 'q':
1491         break;
1492     case 'm': /* name a visible monster */
1493         do_mgivenname();
1494         break;
1495     case 'i': /* name an individual object in inventory */
1496         obj = getobj("name", name_ok, GETOBJ_PROMPT);
1497         if (obj)
1498             do_oname(obj);
1499         break;
1500     case 'o': /* name a type of object in inventory */
1501         obj = getobj("call", call_ok, GETOBJ_NOFLAGS);
1502         if (obj) {
1503             /* behave as if examining it in inventory;
1504                this might set dknown if it was picked up
1505                while blind and the hero can now see */
1506             (void) xname(obj);
1507 
1508             if (!obj->dknown) {
1509                 You("would never recognize another one.");
1510 #if 0
1511             } else if (!call_ok(obj)) {
1512                 You("know those as well as you ever will.");
1513 #endif
1514             } else {
1515                 docall(obj);
1516             }
1517         }
1518         break;
1519     case 'f': /* name a type of object visible on the floor */
1520         namefloorobj();
1521         break;
1522     case 'd': /* name a type of object on the discoveries list */
1523         rename_disco();
1524         break;
1525     case 'a': /* annotate level */
1526         donamelevel();
1527         break;
1528     }
1529     return 0;
1530 }
1531 
1532 /* for use by safe_qbuf() */
1533 static char *
docall_xname(struct obj * obj)1534 docall_xname(struct obj *obj)
1535 {
1536     struct obj otemp;
1537 
1538     otemp = *obj;
1539     otemp.oextra = (struct oextra *) 0;
1540     otemp.quan = 1L;
1541     /* in case water is already known, convert "[un]holy water" to "water" */
1542     otemp.blessed = otemp.cursed = 0;
1543     /* remove attributes that are doname() caliber but get formatted
1544        by xname(); most of these fixups aren't really needed because the
1545        relevant type of object isn't callable so won't reach this far */
1546     if (otemp.oclass == WEAPON_CLASS)
1547         otemp.opoisoned = 0; /* not poisoned */
1548     else if (otemp.oclass == POTION_CLASS)
1549         otemp.odiluted = 0; /* not diluted */
1550     else if (otemp.otyp == TOWEL || otemp.otyp == STATUE)
1551         otemp.spe = 0; /* not wet or historic */
1552     else if (otemp.otyp == TIN)
1553         otemp.known = 0; /* suppress tin type (homemade, &c) and mon type */
1554     else if (otemp.otyp == FIGURINE)
1555         otemp.corpsenm = NON_PM; /* suppress mon type */
1556     else if (otemp.otyp == HEAVY_IRON_BALL)
1557         otemp.owt = objects[HEAVY_IRON_BALL].oc_weight; /* not "very heavy" */
1558     else if (otemp.oclass == FOOD_CLASS && otemp.globby)
1559         otemp.owt = 120; /* 6*20, neither a small glob nor a large one */
1560 
1561     return an(xname(&otemp));
1562 }
1563 
1564 void
docall(struct obj * obj)1565 docall(struct obj *obj)
1566 {
1567     char buf[BUFSZ], qbuf[QBUFSZ];
1568     char **str1;
1569 
1570     if (!obj->dknown)
1571         return; /* probably blind */
1572     flush_screen(1); /* buffered updates might matter to player's response */
1573 
1574     if (obj->oclass == POTION_CLASS && obj->fromsink)
1575         /* kludge, meaning it's sink water */
1576         Sprintf(qbuf, "Call a stream of %s fluid:",
1577                 OBJ_DESCR(objects[obj->otyp]));
1578     else
1579         (void) safe_qbuf(qbuf, "Call ", ":", obj,
1580                          docall_xname, simpleonames, "thing");
1581     /* pointer to old name */
1582     str1 = &(objects[obj->otyp].oc_uname);
1583     buf[0] = '\0';
1584 #ifdef EDIT_GETLIN
1585     /* if there's an existing name, make it be the default answer */
1586     if (*str1)
1587         Strcpy(buf, *str1);
1588 #endif
1589     getlin(qbuf, buf);
1590     if (!*buf || *buf == '\033')
1591         return;
1592 
1593     /* clear old name */
1594     if (*str1)
1595         free((genericptr_t) *str1);
1596 
1597     /* strip leading and trailing spaces; uncalls item if all spaces */
1598     (void) mungspaces(buf);
1599     if (!*buf) {
1600         if (*str1) { /* had name, so possibly remove from disco[] */
1601             /* strip name first, for the update_inventory() call
1602                from undiscover_object() */
1603             *str1 = (char *) 0;
1604             undiscover_object(obj->otyp);
1605         }
1606     } else {
1607         *str1 = dupstr(buf);
1608         discover_object(obj->otyp, FALSE, TRUE); /* possibly add to disco[] */
1609     }
1610 }
1611 
1612 static void
namefloorobj(void)1613 namefloorobj(void)
1614 {
1615     coord cc;
1616     int glyph;
1617     char buf[BUFSZ];
1618     struct obj *obj = 0;
1619     boolean fakeobj = FALSE, use_plural;
1620 
1621     cc.x = u.ux, cc.y = u.uy;
1622     /* "dot for under/over you" only makes sense when the cursor hasn't
1623        been moved off the hero's '@' yet, but there's no way to adjust
1624        the help text once getpos() has started */
1625     Sprintf(buf, "object on map (or '.' for one %s you)",
1626             (u.uundetected && hides_under(g.youmonst.data)) ? "over" : "under");
1627     if (getpos(&cc, FALSE, buf) < 0 || cc.x <= 0)
1628         return;
1629     if (cc.x == u.ux && cc.y == u.uy) {
1630         obj = vobj_at(u.ux, u.uy);
1631     } else {
1632         glyph = glyph_at(cc.x, cc.y);
1633         if (glyph_is_object(glyph))
1634             fakeobj = object_from_map(glyph, cc.x, cc.y, &obj);
1635         /* else 'obj' stays null */
1636     }
1637     if (!obj) {
1638         /* "under you" is safe here since there's no object to hide under */
1639         pline("There doesn't seem to be any object %s.",
1640               (cc.x == u.ux && cc.y == u.uy) ? "under you" : "there");
1641         return;
1642     }
1643     /* note well: 'obj' might be an instance of STRANGE_OBJECT if target
1644        is a mimic; passing that to xname (directly or via simpleonames)
1645        would yield "glorkum" so we need to handle it explicitly; it will
1646        always fail the Hallucination test and pass the !callable test,
1647        resulting in the "can't be assigned a type name" message */
1648     Strcpy(buf, (obj->otyp != STRANGE_OBJECT)
1649                  ? simpleonames(obj)
1650                  : obj_descr[STRANGE_OBJECT].oc_name);
1651     use_plural = (obj->quan > 1L);
1652     if (Hallucination) {
1653         const char *unames[6];
1654         char tmpbuf[BUFSZ];
1655 
1656         /* straight role name */
1657         unames[0] = ((Upolyd ? u.mfemale : flags.female) && g.urole.name.f)
1658                      ? g.urole.name.f
1659                      : g.urole.name.m;
1660         /* random rank title for hero's role
1661 
1662            note: the 30 is hardcoded in xlev_to_rank, so should be
1663            hardcoded here too */
1664         unames[1] = rank_of(rn2_on_display_rng(30) + 1,
1665                             Role_switch, flags.female);
1666         /* random fake monster */
1667         unames[2] = bogusmon(tmpbuf, (char *) 0, -1);
1668         /* increased chance for fake monster */
1669         unames[3] = unames[2];
1670         /* traditional */
1671         unames[4] = roguename();
1672         /* silly */
1673         unames[5] = "Wibbly Wobbly";
1674         pline("%s %s to call you \"%s.\"",
1675               The(buf), use_plural ? "decide" : "decides",
1676               unames[rn2_on_display_rng(SIZE(unames))]);
1677     } else if (!call_ok(obj)) {
1678         pline("%s %s can't be assigned a type name.",
1679               use_plural ? "Those" : "That", buf);
1680     } else if (!obj->dknown) {
1681         You("don't know %s %s well enough to name %s.",
1682             use_plural ? "those" : "that", buf, use_plural ? "them" : "it");
1683     } else {
1684         docall(obj);
1685     }
1686     if (fakeobj) {
1687         obj->where = OBJ_FREE; /* object_from_map() sets it to OBJ_FLOOR */
1688         dealloc_obj(obj);
1689     }
1690 }
1691 
1692 static const char *const ghostnames[] = {
1693     /* these names should have length < PL_NSIZ */
1694     /* Capitalize the names for aesthetics -dgk */
1695     "Adri",    "Andries",       "Andreas",     "Bert",    "David",  "Dirk",
1696     "Emile",   "Frans",         "Fred",        "Greg",    "Hether", "Jay",
1697     "John",    "Jon",           "Karnov",      "Kay",     "Kenny",  "Kevin",
1698     "Maud",    "Michiel",       "Mike",        "Peter",   "Robert", "Ron",
1699     "Tom",     "Wilmar",        "Nick Danger", "Phoenix", "Jiro",   "Mizue",
1700     "Stephan", "Lance Braccus", "Shadowhawk",  "Dudley"
1701 };
1702 
1703 /* ghost names formerly set by x_monnam(), now by makemon() instead */
1704 const char *
rndghostname(void)1705 rndghostname(void)
1706 {
1707     return rn2(7) ? ghostnames[rn2(SIZE(ghostnames))] : (const char *) g.plname;
1708 }
1709 
1710 /*
1711  * Monster naming functions:
1712  * x_monnam is the generic monster-naming function.
1713  *                seen        unseen       detected               named
1714  * mon_nam:     the newt        it      the invisible orc       Fido
1715  * noit_mon_nam:the newt (as if detected) the invisible orc     Fido
1716  * l_monnam:    newt            it      invisible orc           dog called Fido
1717  * Monnam:      The newt        It      The invisible orc       Fido
1718  * noit_Monnam: The newt (as if detected) The invisible orc     Fido
1719  * adj_monnam:  the poor newt   It      the poor invisible orc  the poor Fido
1720  * Adjmonnam:   The poor newt   It      The poor invisible orc  The poor Fido
1721  * Amonnam:     A newt          It      An invisible orc        Fido
1722  * a_monnam:    a newt          it      an invisible orc        Fido
1723  * m_monnam:    newt            xan     orc                     Fido
1724  * y_monnam:    your newt     your xan  your invisible orc      Fido
1725  * noname_monnam(mon,article):
1726  *              article newt    art xan art invisible orc       art dog
1727  */
1728 
1729 /*
1730  * article
1731  *
1732  * ARTICLE_NONE, ARTICLE_THE, ARTICLE_A: obvious
1733  * ARTICLE_YOUR: "your" on pets, "the" on everything else
1734  *
1735  * If the monster would be referred to as "it" or if the monster has a name
1736  * _and_ there is no adjective, "invisible", "saddled", etc., override this
1737  * and always use no article.
1738  *
1739  * suppress
1740  *
1741  * SUPPRESS_IT, SUPPRESS_INVISIBLE, SUPPRESS_HALLUCINATION, SUPPRESS_SADDLE.
1742  * EXACT_NAME: combination of all the above
1743  * SUPPRESS_NAME: omit monster's assigned name (unless uniq w/ pname).
1744  *
1745  * Bug: if the monster is a priest or shopkeeper, not every one of these
1746  * options works, since those are special cases.
1747  */
1748 char *
x_monnam(register struct monst * mtmp,int article,const char * adjective,int suppress,boolean called)1749 x_monnam(register struct monst *mtmp, int article,
1750          const char *adjective, int suppress, boolean called)
1751 {
1752     char *buf = nextmbuf();
1753     struct permonst *mdat = mtmp->data;
1754     const char *pm_name = pmname(mdat, Mgender(mtmp));
1755     boolean do_hallu, do_invis, do_it, do_saddle, do_name;
1756     boolean name_at_start, has_adjectives;
1757     char *bp;
1758 
1759     if (g.program_state.gameover)
1760         suppress |= SUPPRESS_HALLUCINATION;
1761     if (article == ARTICLE_YOUR && !mtmp->mtame)
1762         article = ARTICLE_THE;
1763 
1764     do_hallu = Hallucination && !(suppress & SUPPRESS_HALLUCINATION);
1765     do_invis = mtmp->minvis && !(suppress & SUPPRESS_INVISIBLE);
1766     do_it = !canspotmon(mtmp) && article != ARTICLE_YOUR
1767             && !g.program_state.gameover && mtmp != u.usteed
1768             && !(u.uswallow && mtmp == u.ustuck) && !(suppress & SUPPRESS_IT);
1769     do_saddle = !(suppress & SUPPRESS_SADDLE);
1770     do_name = !(suppress & SUPPRESS_NAME) || type_is_pname(mdat);
1771 
1772     buf[0] = '\0';
1773 
1774     /* unseen monsters, etc.  Use "it" */
1775     if (do_it) {
1776         Strcpy(buf, "it");
1777         return buf;
1778     }
1779 
1780     /* priests and minions: don't even use this function */
1781     if (mtmp->ispriest || mtmp->isminion) {
1782         char priestnambuf[BUFSZ];
1783         char *name;
1784         long save_prop = EHalluc_resistance;
1785         unsigned save_invis = mtmp->minvis;
1786 
1787         /* when true name is wanted, explicitly block Hallucination */
1788         if (!do_hallu)
1789             EHalluc_resistance = 1L;
1790         if (!do_invis)
1791             mtmp->minvis = 0;
1792         name = priestname(mtmp, priestnambuf);
1793         EHalluc_resistance = save_prop;
1794         mtmp->minvis = save_invis;
1795         if (article == ARTICLE_NONE && !strncmp(name, "the ", 4))
1796             name += 4;
1797         return strcpy(buf, name);
1798     }
1799 #if 0
1800     /* an "aligned priest" not flagged as a priest or minion should be
1801        "priest" or "priestess" (normally handled by priestname()) */
1802     if (mdat == &mons[PM_ALIGNED_CLERIC])
1803         pm_name = mtmp->female ? "priestess" : "priest";
1804     else if (mdat == &mons[PM_HIGH_CLERIC] && mtmp->female)
1805         pm_name = "high priestess";
1806 #endif
1807 
1808     /* Shopkeepers: use shopkeeper name.  For normal shopkeepers, just
1809      * "Asidonhopo"; for unusual ones, "Asidonhopo the invisible
1810      * shopkeeper" or "Asidonhopo the blue dragon".  If hallucinating,
1811      * none of this applies.
1812      */
1813     if (mtmp->isshk && !do_hallu) {
1814         if (adjective && article == ARTICLE_THE) {
1815             /* pathological case: "the angry Asidonhopo the blue dragon"
1816                sounds silly */
1817             Strcpy(buf, "the ");
1818             Strcat(strcat(buf, adjective), " ");
1819             Strcat(buf, shkname(mtmp));
1820             return buf;
1821         }
1822         Strcat(buf, shkname(mtmp));
1823         if (mdat == &mons[PM_SHOPKEEPER] && !do_invis)
1824             return buf;
1825         Strcat(buf, " the ");
1826         if (do_invis)
1827             Strcat(buf, "invisible ");
1828         Strcat(buf, pm_name);
1829         return buf;
1830     }
1831 
1832     /* Put the adjectives in the buffer */
1833     if (adjective)
1834         Strcat(strcat(buf, adjective), " ");
1835     if (do_invis)
1836         Strcat(buf, "invisible ");
1837     if (do_saddle && (mtmp->misc_worn_check & W_SADDLE) && !Blind
1838         && !Hallucination)
1839         Strcat(buf, "saddled ");
1840     has_adjectives = (buf[0] != '\0');
1841 
1842     /* Put the actual monster name or type into the buffer now.
1843        Remember whether the buffer starts with a personal name. */
1844     if (do_hallu) {
1845         char rnamecode;
1846         char *rname = rndmonnam(&rnamecode);
1847 
1848         Strcat(buf, rname);
1849         name_at_start = bogon_is_pname(rnamecode);
1850     } else if (do_name && has_mgivenname(mtmp)) {
1851         char *name = MGIVENNAME(mtmp);
1852 
1853         if (mdat == &mons[PM_GHOST]) {
1854             Sprintf(eos(buf), "%s ghost", s_suffix(name));
1855             name_at_start = TRUE;
1856         } else if (has_ebones(mtmp)) {
1857             Sprintf(eos(buf), "%s of %s", pm_name, name);
1858             name_at_start = FALSE;
1859         } else if (called) {
1860             Sprintf(eos(buf), "%s called %s", pm_name, name);
1861             name_at_start = (boolean) type_is_pname(mdat);
1862         } else if (is_mplayer(mdat) && (bp = strstri(name, " the ")) != 0) {
1863             /* <name> the <adjective> <invisible> <saddled> <rank> */
1864             char pbuf[BUFSZ];
1865 
1866             Strcpy(pbuf, name);
1867             pbuf[bp - name + 5] = '\0'; /* adjectives right after " the " */
1868             if (has_adjectives)
1869                 Strcat(pbuf, buf);
1870             Strcat(pbuf, bp + 5); /* append the rest of the name */
1871             Strcpy(buf, pbuf);
1872             article = ARTICLE_NONE;
1873             name_at_start = TRUE;
1874         } else {
1875             Strcat(buf, name);
1876             name_at_start = TRUE;
1877         }
1878     } else if (is_mplayer(mdat) && !In_endgame(&u.uz)) {
1879         char pbuf[BUFSZ];
1880 
1881         Strcpy(pbuf, rank_of((int) mtmp->m_lev, monsndx(mdat),
1882                              (boolean) mtmp->female));
1883         Strcat(buf, lcase(pbuf));
1884         name_at_start = FALSE;
1885     } else {
1886         Strcat(buf, pm_name);
1887         name_at_start = (boolean) type_is_pname(mdat);
1888     }
1889 
1890     if (name_at_start && (article == ARTICLE_YOUR || !has_adjectives)) {
1891         if (mdat == &mons[PM_WIZARD_OF_YENDOR])
1892             article = ARTICLE_THE;
1893         else
1894             article = ARTICLE_NONE;
1895     } else if ((mdat->geno & G_UNIQ) && article == ARTICLE_A) {
1896         article = ARTICLE_THE;
1897     }
1898 
1899     {
1900         char buf2[BUFSZ];
1901 
1902         switch (article) {
1903         case ARTICLE_YOUR:
1904             Strcpy(buf2, "your ");
1905             Strcat(buf2, buf);
1906             Strcpy(buf, buf2);
1907             return buf;
1908         case ARTICLE_THE:
1909             Strcpy(buf2, "the ");
1910             Strcat(buf2, buf);
1911             Strcpy(buf, buf2);
1912             return buf;
1913         case ARTICLE_A:
1914             return an(buf);
1915         case ARTICLE_NONE:
1916         default:
1917             return buf;
1918         }
1919     }
1920 }
1921 
1922 char *
l_monnam(struct monst * mtmp)1923 l_monnam(struct monst *mtmp)
1924 {
1925     return x_monnam(mtmp, ARTICLE_NONE, (char *) 0,
1926                     (has_mgivenname(mtmp)) ? SUPPRESS_SADDLE : 0, TRUE);
1927 }
1928 
1929 char *
mon_nam(struct monst * mtmp)1930 mon_nam(struct monst *mtmp)
1931 {
1932     return x_monnam(mtmp, ARTICLE_THE, (char *) 0,
1933                     (has_mgivenname(mtmp)) ? SUPPRESS_SADDLE : 0, FALSE);
1934 }
1935 
1936 /* print the name as if mon_nam() was called, but assume that the player
1937  * can always see the monster--used for probing and for monsters aggravating
1938  * the player with a cursed potion of invisibility
1939  */
1940 char *
noit_mon_nam(struct monst * mtmp)1941 noit_mon_nam(struct monst *mtmp)
1942 {
1943     return x_monnam(mtmp, ARTICLE_THE, (char *) 0,
1944                     (has_mgivenname(mtmp)) ? (SUPPRESS_SADDLE | SUPPRESS_IT)
1945                                       : SUPPRESS_IT,
1946                     FALSE);
1947 }
1948 
1949 char *
Monnam(struct monst * mtmp)1950 Monnam(struct monst *mtmp)
1951 {
1952     register char *bp = mon_nam(mtmp);
1953 
1954     *bp = highc(*bp);
1955     return  bp;
1956 }
1957 
1958 char *
noit_Monnam(struct monst * mtmp)1959 noit_Monnam(struct monst *mtmp)
1960 {
1961     register char *bp = noit_mon_nam(mtmp);
1962 
1963     *bp = highc(*bp);
1964     return  bp;
1965 }
1966 
1967 /* return "a dog" rather than "Fido", honoring hallucination and visibility */
1968 char *
noname_monnam(struct monst * mtmp,int article)1969 noname_monnam(struct monst *mtmp, int article)
1970 {
1971     return x_monnam(mtmp, article, (char *) 0, SUPPRESS_NAME, FALSE);
1972 }
1973 
1974 /* monster's own name -- overrides hallucination and [in]visibility
1975    so shouldn't be used in ordinary messages (mainly for disclosure) */
1976 char *
m_monnam(struct monst * mtmp)1977 m_monnam(struct monst *mtmp)
1978 {
1979     return x_monnam(mtmp, ARTICLE_NONE, (char *) 0, EXACT_NAME, FALSE);
1980 }
1981 
1982 /* pet name: "your little dog" */
1983 char *
y_monnam(struct monst * mtmp)1984 y_monnam(struct monst *mtmp)
1985 {
1986     int prefix, suppression_flag;
1987 
1988     prefix = mtmp->mtame ? ARTICLE_YOUR : ARTICLE_THE;
1989     suppression_flag = (has_mgivenname(mtmp)
1990                         /* "saddled" is redundant when mounted */
1991                         || mtmp == u.usteed)
1992                            ? SUPPRESS_SADDLE
1993                            : 0;
1994 
1995     return x_monnam(mtmp, prefix, (char *) 0, suppression_flag, FALSE);
1996 }
1997 
1998 char *
adj_monnam(struct monst * mtmp,const char * adj)1999 adj_monnam(struct monst *mtmp, const char *adj)
2000 {
2001     return x_monnam(mtmp, ARTICLE_THE, adj,
2002                     has_mgivenname(mtmp) ? SUPPRESS_SADDLE : 0, FALSE);
2003 }
2004 
2005 /* Equivalent to adj_monnam(), but uppercases the result. */
2006 char *
Adjmonnam(struct monst * mtmp,const char * adj)2007 Adjmonnam(struct monst *mtmp, const char *adj)
2008 {
2009     char *bp = adj_monnam(mtmp, adj);
2010     *bp = highc(*bp);
2011     return  bp;
2012 }
2013 
2014 char *
a_monnam(struct monst * mtmp)2015 a_monnam(struct monst *mtmp)
2016 {
2017     return x_monnam(mtmp, ARTICLE_A, (char *) 0,
2018                     has_mgivenname(mtmp) ? SUPPRESS_SADDLE : 0, FALSE);
2019 }
2020 
2021 char *
Amonnam(struct monst * mtmp)2022 Amonnam(struct monst *mtmp)
2023 {
2024     char *bp = a_monnam(mtmp);
2025 
2026     *bp = highc(*bp);
2027     return  bp;
2028 }
2029 
2030 /* used for monster ID by the '/', ';', and 'C' commands to block remote
2031    identification of the endgame altars via their attending priests */
2032 char *
distant_monnam(struct monst * mon,int article,char * outbuf)2033 distant_monnam(struct monst *mon,
2034                int article, /* only ARTICLE_NONE and ARTICLE_THE
2035                                are handled here */
2036                char *outbuf)
2037 {
2038     /* high priest(ess)'s identity is concealed on the Astral Plane,
2039        unless you're adjacent (overridden for hallucination which does
2040        its own obfuscation) */
2041     if (mon->data == &mons[PM_HIGH_CLERIC] && !Hallucination
2042         && Is_astralevel(&u.uz) && distu(mon->mx, mon->my) > 2) {
2043         Strcpy(outbuf, article == ARTICLE_THE ? "the " : "");
2044         Strcat(outbuf, mon->female ? "high priestess" : "high priest");
2045     } else {
2046         Strcpy(outbuf, x_monnam(mon, article, (char *) 0, 0, TRUE));
2047     }
2048     return outbuf;
2049 }
2050 
2051 /* returns mon_nam(mon) relative to other_mon; normal name unless they're
2052    the same, in which case the reference is to {him|her|it} self */
2053 char *
mon_nam_too(struct monst * mon,struct monst * other_mon)2054 mon_nam_too(struct monst *mon, struct monst *other_mon)
2055 {
2056     char *outbuf;
2057 
2058     if (mon != other_mon) {
2059         outbuf = mon_nam(mon);
2060     } else {
2061         outbuf = nextmbuf();
2062         switch (pronoun_gender(mon, PRONOUN_HALLU)) {
2063         case 0:
2064             Strcpy(outbuf, "himself");
2065             break;
2066         case 1:
2067             Strcpy(outbuf, "herself");
2068             break;
2069         default:
2070         case 2:
2071             Strcpy(outbuf, "itself");
2072             break;
2073         case 3: /* could happen when hallucinating */
2074             Strcpy(outbuf, "themselves");
2075             break;
2076         }
2077     }
2078     return outbuf;
2079 }
2080 
2081 /* construct "<monnamtext> <verb> <othertext> {him|her|it}self" which might
2082    be distorted by Hallu; if that's plural, adjust monnamtext and verb */
2083 char *
monverbself(struct monst * mon,char * monnamtext,const char * verb,const char * othertext)2084 monverbself(struct monst *mon,
2085             char *monnamtext, /* modifiable 'mbuf' with adequare room at end */
2086             const char *verb, const char *othertext)
2087 {
2088     char *verbs, selfbuf[40]; /* sizeof "themselves" suffices */
2089 
2090     /* "himself"/"herself"/"itself", maybe "themselves" if hallucinating */
2091     Strcpy(selfbuf, mon_nam_too(mon, mon));
2092     /* verb starts plural; this will yield singular except for "themselves" */
2093     verbs = vtense(selfbuf, verb);
2094     if (!strcmp(verb, verbs)) { /* a match indicates that it stayed plural */
2095         monnamtext = makeplural(monnamtext);
2096         /* for "it", makeplural() produces "them" but we want "they" */
2097         if (!strcmpi(monnamtext, genders[3].he)) {
2098             boolean capitaliz = (monnamtext[0] == highc(monnamtext[0]));
2099 
2100             Strcpy(monnamtext, genders[3].him);
2101             if (capitaliz)
2102                 monnamtext[0] = highc(monnamtext[0]);
2103         }
2104     }
2105     Strcat(strcat(monnamtext, " "), verbs);
2106     if (othertext && *othertext)
2107         Strcat(strcat(monnamtext, " "), othertext);
2108     Strcat(strcat(monnamtext, " "), selfbuf);
2109     return monnamtext;
2110 }
2111 
2112 /* for debugging messages, where data might be suspect and we aren't
2113    taking what the hero does or doesn't know into consideration */
2114 char *
minimal_monnam(struct monst * mon,boolean ckloc)2115 minimal_monnam(struct monst *mon, boolean ckloc)
2116 {
2117     struct permonst *ptr;
2118     char *outbuf = nextmbuf();
2119 
2120     if (!mon) {
2121         Strcpy(outbuf, "[Null monster]");
2122     } else if ((ptr = mon->data) == 0) {
2123         Strcpy(outbuf, "[Null mon->data]");
2124     } else if (ptr < &mons[0]) {
2125         Sprintf(outbuf, "[Invalid mon->data %s < %s]",
2126                 fmt_ptr((genericptr_t) mon->data),
2127                 fmt_ptr((genericptr_t) &mons[0]));
2128     } else if (ptr >= &mons[NUMMONS]) {
2129         Sprintf(outbuf, "[Invalid mon->data %s >= %s]",
2130                 fmt_ptr((genericptr_t) mon->data),
2131                 fmt_ptr((genericptr_t) &mons[NUMMONS]));
2132     } else if (ckloc && ptr == &mons[PM_LONG_WORM]
2133                && g.level.monsters[mon->mx][mon->my] != mon) {
2134         Sprintf(outbuf, "%s <%d,%d>",
2135                 pmname(&mons[PM_LONG_WORM_TAIL], Mgender(mon)),
2136                 mon->mx, mon->my);
2137     } else {
2138         Sprintf(outbuf, "%s%s <%d,%d>",
2139                 mon->mtame ? "tame " : mon->mpeaceful ? "peaceful " : "",
2140                 pmname(mon->data, Mgender(mon)), mon->mx, mon->my);
2141         if (mon->cham != NON_PM)
2142             Sprintf(eos(outbuf), "{%s}",
2143                     pmname(&mons[mon->cham], Mgender(mon)));
2144     }
2145     return outbuf;
2146 }
2147 
2148 #ifndef PMNAME_MACROS
2149 int
Mgender(struct monst * mtmp)2150 Mgender(struct monst *mtmp)
2151 {
2152     int mgender = MALE;
2153 
2154     if (mtmp == &g.youmonst) {
2155         if (Upolyd ? u.mfemale : flags.female)
2156             mgender = FEMALE;
2157     } else if (mtmp->female) {
2158         mgender = FEMALE;
2159     }
2160     return mgender;
2161 }
2162 
2163 const char *
pmname(struct permonst * pm,int mgender)2164 pmname(struct permonst *pm, int mgender)
2165 {
2166     if ((mgender >= MALE && mgender < NUM_MGENDERS) && pm->pmnames[mgender])
2167         return pm->pmnames[mgender];
2168     else
2169         return pm->pmnames[NEUTRAL];
2170 }
2171 #endif /* PMNAME_MACROS */
2172 
2173 /* fake monsters used to be in a hard-coded array, now in a data file
2174  * The which parameter is passed along to get_rnd_text and represents the line
2175  * of BOGUSMONFILE to return. Specify -1 for a random one. */
2176 char *
bogusmon(char * buf,char * code,int which)2177 bogusmon(char *buf, char *code, int which)
2178 {
2179     static const char bogon_codes[] = "-_+|="; /* see dat/bonusmon.txt */
2180     char *mnam = buf;
2181 
2182     if (code)
2183         *code = '\0';
2184     /* might fail (return empty buf[]) if the file isn't available */
2185     get_rnd_text(BOGUSMONFILE, buf, which, rn2_on_display_rng);
2186     if (!*mnam) {
2187         Strcpy(buf, "bogon");
2188     } else if (index(bogon_codes, *mnam)) { /* strip prefix if present */
2189         if (code)
2190             *code = *mnam;
2191         ++mnam;
2192     }
2193     return mnam;
2194 }
2195 
2196 /* return a random monster name, for hallucination */
2197 char *
rndmonnam(char * code)2198 rndmonnam(char *code)
2199 {
2200     static char buf[BUFSZ];
2201     char *mnam;
2202     int name;
2203 #define BOGUSMONSIZE 100 /* arbitrary */
2204 
2205     if (code)
2206         *code = '\0';
2207 
2208     do {
2209         name = rn2_on_display_rng(SPECIAL_PM + BOGUSMONSIZE - LOW_PM) + LOW_PM;
2210     } while (name < SPECIAL_PM
2211              && (type_is_pname(&mons[name]) || (mons[name].geno & G_NOGEN)));
2212 
2213     if (name >= SPECIAL_PM) {
2214         mnam = bogusmon(buf, code, -1);
2215     } else {
2216         mnam = strcpy(buf, pmname(&mons[name], rn2_on_display_rng(2)));
2217     }
2218     return mnam;
2219 #undef BOGUSMONSIZE
2220 }
2221 
2222 /* check bogusmon prefix to decide whether it's a personal name */
2223 boolean
bogon_is_pname(char code)2224 bogon_is_pname(char code)
2225 {
2226     if (!code)
2227         return FALSE;
2228     return index("-+=", code) ? TRUE : FALSE;
2229 }
2230 
2231 /* name of a Rogue player */
2232 const char *
roguename(void)2233 roguename(void)
2234 {
2235     char *i, *opts;
2236 
2237     if ((opts = nh_getenv("ROGUEOPTS")) != 0) {
2238         for (i = opts; *i; i++)
2239             if (!strncmp("name=", i, 5)) {
2240                 char *j;
2241                 if ((j = index(i + 5, ',')) != 0)
2242                     *j = (char) 0;
2243                 return i + 5;
2244             }
2245     }
2246     return rn2(3) ? (rn2(2) ? "Michael Toy" : "Kenneth Arnold")
2247                   : "Glenn Wichman";
2248 }
2249 
2250 static NEARDATA const char *const hcolors[] = {
2251     "ultraviolet", "infrared", "bluish-orange", "reddish-green", "dark white",
2252     "light black", "sky blue-pink", "pinkish-cyan", "indigo-chartreuse",
2253     "excitingly dull", "salty", "sweet", "sour", "bitter", "savory", "umami",
2254     "neon", "fluorescent", "phosphorescent", "translucent", "opaque",
2255     "psychedelic", "iridescent", "rainbow-colored", "polychromatic",
2256     "colorless", "colorless green",
2257     "striped", "spiral", "swirly", "plaid", "checkered", "argyle", "paisley",
2258     "blotchy", "guernsey-spotted", "polka-dotted", "square", "round",
2259     "triangular", "cabernet", "sangria", "fuchsia", "wisteria", "lemon-lime",
2260     "strawberry-banana", "peppermint", "romantic", "incandescent",
2261     "octarine", /* Discworld: the Colour of Magic */
2262 };
2263 
2264 const char *
hcolor(const char * colorpref)2265 hcolor(const char *colorpref)
2266 {
2267     return (Hallucination || !colorpref)
2268         ? hcolors[rn2_on_display_rng(SIZE(hcolors))]
2269         : colorpref;
2270 }
2271 
2272 /* return a random real color unless hallucinating */
2273 const char *
rndcolor(void)2274 rndcolor(void)
2275 {
2276     int k = rn2(CLR_MAX);
2277 
2278     return Hallucination ? hcolor((char *) 0)
2279                          : (k == NO_COLOR) ? "colorless"
2280                                            : c_obj_colors[k];
2281 }
2282 
2283 static NEARDATA const char *const hliquids[] = {
2284     "yoghurt", "oobleck", "clotted blood", "diluted water", "purified water",
2285     "instant coffee", "tea", "herbal infusion", "liquid rainbow",
2286     "creamy foam", "mulled wine", "bouillon", "nectar", "grog", "flubber",
2287     "ketchup", "slow light", "oil", "vinaigrette", "liquid crystal", "honey",
2288     "caramel sauce", "ink", "aqueous humour", "milk substitute",
2289     "fruit juice", "glowing lava", "gastric acid", "mineral water",
2290     "cough syrup", "quicksilver", "sweet vitriol", "grey goo", "pink slime",
2291     /* "new coke (tm)", --better not */
2292 };
2293 
2294 /* if hallucinating, return a random liquid instead of 'liquidpref' */
2295 const char *
hliquid(const char * liquidpref)2296 hliquid(const char *liquidpref) /* use as-is when not hallucinating (unless empty) */
2297 {
2298     if (Hallucination || !liquidpref || !*liquidpref) {
2299         int indx, count = SIZE(hliquids);
2300 
2301         /* if we have a non-hallucinatory default value, include it
2302            among the choices */
2303         if (liquidpref && *liquidpref)
2304             ++count;
2305         indx = rn2_on_display_rng(count);
2306         if (indx < SIZE(hliquids))
2307             return hliquids[indx];
2308     }
2309     return liquidpref;
2310 }
2311 
2312 /* Aliases for road-runner nemesis
2313  */
2314 static const char *const coynames[] = {
2315     "Carnivorous Vulgaris", "Road-Runnerus Digestus", "Eatibus Anythingus",
2316     "Famishus-Famishus", "Eatibus Almost Anythingus", "Eatius Birdius",
2317     "Famishius Fantasticus", "Eternalii Famishiis", "Famishus Vulgarus",
2318     "Famishius Vulgaris Ingeniusi", "Eatius-Slobbius", "Hardheadipus Oedipus",
2319     "Carnivorous Slobbius", "Hard-Headipus Ravenus", "Evereadii Eatibus",
2320     "Apetitius Giganticus", "Hungrii Flea-Bagius", "Overconfidentii Vulgaris",
2321     "Caninus Nervous Rex", "Grotesques Appetitus", "Nemesis Ridiculii",
2322     "Canis latrans"
2323 };
2324 
2325 char *
coyotename(struct monst * mtmp,char * buf)2326 coyotename(struct monst *mtmp, char *buf)
2327 {
2328     if (mtmp && buf) {
2329         Sprintf(buf, "%s - %s",
2330                 x_monnam(mtmp, ARTICLE_NONE, (char *) 0, 0, TRUE),
2331                 mtmp->mcan ? coynames[SIZE(coynames) - 1]
2332                            : coynames[mtmp->m_id % (SIZE(coynames) - 1)]);
2333     }
2334     return buf;
2335 }
2336 
2337 char *
rndorcname(char * s)2338 rndorcname(char *s)
2339 {
2340     static const char *v[] = { "a", "ai", "og", "u" };
2341     static const char *snd[] = { "gor", "gris", "un", "bane", "ruk",
2342                                  "oth","ul", "z", "thos","akh","hai" };
2343     int i, iend = rn1(2, 3), vstart = rn2(2);
2344 
2345     if (s) {
2346         *s = '\0';
2347         for (i = 0; i < iend; ++i) {
2348             vstart = 1 - vstart;                /* 0 -> 1, 1 -> 0 */
2349             Sprintf(eos(s), "%s%s", (i > 0 && !rn2(30)) ? "-" : "",
2350                     vstart ? v[rn2(SIZE(v))] : snd[rn2(SIZE(snd))]);
2351         }
2352     }
2353     return s;
2354 }
2355 
2356 struct monst *
christen_orc(struct monst * mtmp,const char * gang,const char * other)2357 christen_orc(struct monst *mtmp, const char *gang, const char *other)
2358 {
2359     int sz = 0;
2360     char buf[BUFSZ], buf2[BUFSZ], *orcname;
2361 
2362     orcname = rndorcname(buf2);
2363     sz = (int) strlen(orcname);
2364     if (gang)
2365         sz += (int) (strlen(gang) + sizeof " of " - sizeof "");
2366     else if (other)
2367         sz += (int) strlen(other);
2368 
2369     if (sz < BUFSZ) {
2370         char gbuf[BUFSZ];
2371         boolean nameit = FALSE;
2372 
2373         if (gang && orcname) {
2374             Sprintf(buf, "%s of %s", upstart(orcname),
2375                     upstart(strcpy(gbuf, gang)));
2376             nameit = TRUE;
2377         } else if (other && orcname) {
2378             Sprintf(buf, "%s%s", upstart(orcname), other);
2379             nameit = TRUE;
2380         }
2381         if (nameit)
2382             mtmp = christen_monst(mtmp, buf);
2383     }
2384     return mtmp;
2385 }
2386 
2387 /* make sure "The Colour of Magic" remains the first entry in here */
2388 static const char *const sir_Terry_novels[] = {
2389     "The Colour of Magic", "The Light Fantastic", "Equal Rites", "Mort",
2390     "Sourcery", "Wyrd Sisters", "Pyramids", "Guards! Guards!", "Eric",
2391     "Moving Pictures", "Reaper Man", "Witches Abroad", "Small Gods",
2392     "Lords and Ladies", "Men at Arms", "Soul Music", "Interesting Times",
2393     "Maskerade", "Feet of Clay", "Hogfather", "Jingo", "The Last Continent",
2394     "Carpe Jugulum", "The Fifth Elephant", "The Truth", "Thief of Time",
2395     "The Last Hero", "The Amazing Maurice and His Educated Rodents",
2396     "Night Watch", "The Wee Free Men", "Monstrous Regiment",
2397     "A Hat Full of Sky", "Going Postal", "Thud!", "Wintersmith",
2398     "Making Money", "Unseen Academicals", "I Shall Wear Midnight", "Snuff",
2399     "Raising Steam", "The Shepherd's Crown"
2400 };
2401 
2402 const char *
noveltitle(int * novidx)2403 noveltitle(int *novidx)
2404 {
2405     int j, k = SIZE(sir_Terry_novels);
2406 
2407     j = rn2(k);
2408     if (novidx) {
2409         if (*novidx == -1)
2410             *novidx = j;
2411         else if (*novidx >= 0 && *novidx < k)
2412             j = *novidx;
2413     }
2414     return sir_Terry_novels[j];
2415 }
2416 
2417 /* figure out canonical novel title from player-specified one */
2418 const char *
lookup_novel(const char * lookname,int * idx)2419 lookup_novel(const char *lookname, int *idx)
2420 {
2421     int k;
2422 
2423     /*
2424      * Accept variant spellings:
2425      * _The_Colour_of_Magic_ uses British spelling, and American
2426      * editions keep that, but we also recognize American spelling;
2427      * _Sourcery_ is a joke rather than British spelling of "sorcery".
2428      */
2429     if (!strcmpi(The(lookname), "The Color of Magic"))
2430         lookname = sir_Terry_novels[0];
2431     else if (!strcmpi(lookname, "Sorcery"))
2432         lookname = "Sourcery"; /* [4] */
2433 
2434     for (k = 0; k < SIZE(sir_Terry_novels); ++k) {
2435         if (!strcmpi(lookname, sir_Terry_novels[k])
2436             || !strcmpi(The(lookname), sir_Terry_novels[k])) {
2437             if (idx)
2438                 *idx = k;
2439             return sir_Terry_novels[k];
2440         }
2441     }
2442     /* name not found; if novelidx is already set, override the name */
2443     if (idx && *idx >= 0 && *idx < SIZE(sir_Terry_novels))
2444         return sir_Terry_novels[*idx];
2445 
2446     return (const char *) 0;
2447 }
2448 
2449 /* Most of these are actual names of nymphs from mythology. */
2450 const char* nymphnames[] = {
2451     "Erythea", "Hesperia", "Arethusa", "Pasithea", "Thaleia", "Halimede",
2452     "Actaea", "Electra", "Maia", "Nesaea", "Alcyone", "Asterope",
2453     "Callianeira", "Nausithoe", "Dione", "Thetis", "Ephyra", "Eulimene",
2454     "Nerea", "Laomedeia", "Echo", "Maera", "Eurydice", "Lysianassa", "Phoebe",
2455     "Daphnis", "Daphnae", "Melinoe", "Othreis", "Polychrome"
2456 };
2457 
2458 const char* maldemonnames[] = {
2459     "Agiel", "Kali", "Amon", "Foras", "Armaros", "Orias", "Malthus", "Asag",
2460     "Raum", "Iblis", "Vanth", "Bael", "Leonard", "Barbas", "Charun", "Ishmael",
2461     "Balthamel", "Rahvin"
2462 };
2463 
2464 const char* femdemonnames[] = {
2465     "Mara", "Lamia", "Meraxes", "Daeva", "Amy", "Lilith", "Aliss", "Berith",
2466     "Euryale", "Zorya", "Rhaenyra", "Bellatrix", "Rusalka", "Messaana",
2467     "Jadis", "Anzu", "Eve", "Bilquis", "Cyndane", "Vanessa", "Graendal"
2468 };
2469 #define rnd_name(list) list[rn2(SIZE(list))]
2470 
2471 /* Monster introduces themselves. If they're not currently named, give them a
2472  * random name from the specified list. */
2473 void
mintroduce(struct monst * mtmp)2474 mintroduce(struct monst *mtmp)
2475 {
2476     if (!has_mgivenname(mtmp)) {
2477         const char* name;
2478         if (mtmp->data->mlet == S_NYMPH) {
2479             name = rnd_name(nymphnames);
2480         }
2481         else if (is_demon(mtmp->data)) {
2482             if (mtmp->female)
2483                 name = rnd_name(femdemonnames);
2484             else
2485                 name = rnd_name(maldemonnames);
2486         }
2487         else {
2488             impossible("mintroduce: monster type %s has no defined names!",
2489                        mtmp->data->pmnames[NEUTRAL]);
2490             return;
2491         }
2492         const char* pronoun =
2493             genders[is_neuter(mtmp->data) ? 2 : mtmp->female].him;
2494         if (!Deaf) {
2495             pline("%s introduces %sself to you as %s.", Monnam(mtmp), pronoun,
2496                   name);
2497             christen_monst(mtmp, name);
2498         } else {
2499             pline("%s seems to be introducing %sself, but you can't hear %s.",
2500                   Monnam(mtmp), pronoun, pronoun);
2501         }
2502     }
2503     return;
2504 }
2505 
2506 /* Return a string describing how wounded a monster is.
2507  * This is in do_name.c only because it gives us access to nextmbuf().
2508  */
2509 char *
mon_wounds(struct monst * mon,boolean addspace,boolean clarity)2510 mon_wounds(struct monst *mon,
2511            boolean addspace, /* add ' ' to end of string */
2512            boolean clarity) /* end of game: don't decline for non-healer or
2513                              * obfuscate for hallucination */
2514 {
2515     char *outbuf = nextmbuf();
2516     const struct permonst *mdat = mon->data;
2517     const char *adverb, *adjective;
2518 
2519     /* Cases where you can't see wounds. */
2520     if (!clarity
2521         && (!Role_if(PM_HEALER) || !canseemon(mon)
2522             || (u.uswallow && mon == u.ustuck && Blind))) {
2523         return NULL;
2524     }
2525     if (DEADMONSTER(mon)) {
2526         return NULL;
2527     }
2528     if (mon->mhp == mon->mhpmax) {
2529         Sprintf(outbuf, "uninjured%s", addspace ? " " : "");
2530         return outbuf;
2531     }
2532 
2533     adjective = is_fleshy(mdat) ? "wounded" : "damaged";
2534 
2535     if (Hallucination && !clarity) {
2536         const char *wound_adverbs[] = {
2537             "mildly", "mostly", "somewhat", "lightly", "massively", "extremely",
2538             "flagrantly", "flamboyantly", "supremely", "excessively", "truly",
2539             "terribly", "incredibly", "unbelievably", "obscenely", "insanely",
2540             "amazingly", "absolutely"
2541         };
2542         const char *wound_adjectives[] = {
2543             "bloodied", "bruised", "crushed", "marred", "blemished",
2544             "dinged up", "totaled", "wrecked", "beaten up", "defaced",
2545             "pulverized", "battered", "tarnished", "ruined", "mangled",
2546             "trashed", "smashed up", "crumpled", "scratched", "scraped"
2547         };
2548         adverb = wound_adverbs[rn2(SIZE(wound_adverbs))];
2549         adjective = wound_adjectives[rn2(SIZE(wound_adjectives))];
2550     }
2551     else if (mon->mhp * 6 <= mon->mhpmax) {     /* <= 16.6% */
2552         adverb = "critically";
2553     }
2554     else if (mon->mhp * 4 <= mon->mhpmax) {     /* <= 25%   */
2555         adverb = "badly";
2556     }
2557     else if (mon->mhp * 3 <= mon->mhpmax) {     /* <= 33.3% */
2558         adverb = "seriously";
2559     }
2560     else if (mon->mhp * 4 <= mon->mhpmax * 3) { /* <= 75%   */
2561         adverb = "moderately";
2562     }
2563     else {                                      /*  < 100%  */
2564         adverb = "slightly";
2565     }
2566 
2567     Sprintf(outbuf, "%s %s%s", adverb, adjective, addspace ? " " : "");
2568     return outbuf;
2569 }
2570 
2571 /* Print a message indicating that mon is wounded.
2572  * It will only print something if the monster's wound adjective changes (e.g.
2573  * if it goes from slightly wounded to moderately wounded).
2574  * pre_wound_hp is its HP amount before being damaged in the caller of this
2575  * function; the caller must save its initial HP to pass through to this.
2576  */
2577 void
print_mon_wounded(struct monst * mon,int pre_wound_hp)2578 print_mon_wounded(struct monst *mon, int pre_wound_hp)
2579 {
2580     int mcurrhp;
2581     char *m_old_wounds, *m_curr_wounds;
2582 
2583     if (!mon) {
2584         impossible("print_mon_wounded: null mon!");
2585         return;
2586     }
2587     if (!Role_if(PM_HEALER)) {
2588         return;  /* optimization; redundant with healer check in mon_wounds */
2589     }
2590     if (mon->mhp == pre_wound_hp && !Hallucination) {
2591         /* impossible for adjective to change */
2592         return;
2593     }
2594 
2595     mcurrhp = mon->mhp;
2596     mon->mhp = pre_wound_hp;
2597     m_old_wounds = mon_wounds(mon, FALSE, FALSE);
2598     mon->mhp = mcurrhp;
2599 
2600     m_curr_wounds = mon_wounds(mon, FALSE, FALSE);
2601 
2602     if (m_curr_wounds != NULL
2603         && (Hallucination || m_old_wounds == NULL
2604             || strcmp(m_old_wounds, m_curr_wounds))) {
2605         pline("%s is %s.", Monnam(mon), m_curr_wounds);
2606     }
2607 }
2608 
2609 /*do_name.c*/
2610