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