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