Add key rebinding
[aNetHack.git] / src / do_name.c
blob5c470265ab2e754e23f8f161df356a91c371f0f9
1 /* NetHack 3.6 do_name.c $NHDT-Date: 1452669022 2016/01/13 07:10:22 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.90 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 #include "hack.h"
7 STATIC_DCL char *NDECL(nextmbuf);
8 STATIC_DCL void FDECL(getpos_help, (BOOLEAN_P, const char *));
9 STATIC_DCL int FDECL(CFDECLSPEC cmp_coord_distu, (const void *,
10 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 void FDECL(auto_describe, (int, int));
14 STATIC_DCL void NDECL(do_mname);
15 STATIC_DCL boolean FDECL(alreadynamed, (struct monst *, char *, char *));
16 STATIC_DCL void FDECL(do_oname, (struct obj *));
17 STATIC_DCL void NDECL(namefloorobj);
18 STATIC_DCL char *FDECL(bogusmon, (char *,char *));
20 extern const char what_is_an_unknown_object[]; /* from pager.c */
22 #define NUMMBUF 5
24 /* manage a pool of BUFSZ buffers, so callers don't have to */
25 STATIC_OVL char *
26 nextmbuf()
28 static char NEARDATA bufs[NUMMBUF][BUFSZ];
29 static int bufidx = 0;
31 bufidx = (bufidx + 1) % NUMMBUF;
32 return bufs[bufidx];
35 /* function for getpos() to highlight desired map locations.
36 * parameter value 0 = initialize, 1 = highlight, 2 = done
38 static void FDECL((*getpos_hilitefunc), (int)) = (void FDECL((*), (int))) 0;
40 void
41 getpos_sethilite(f)
42 void FDECL((*f), (int));
44 getpos_hilitefunc = f;
47 /* the response for '?' help request in getpos() */
48 STATIC_OVL void
49 getpos_help(force, goal)
50 boolean force;
51 const char *goal;
53 char sbuf[BUFSZ];
54 boolean doing_what_is;
55 winid tmpwin = create_nhwindow(NHW_MENU);
57 Sprintf(sbuf, "Use '%c', '%c', '%c', '%c' to move the cursor to %s.", /* hjkl */
58 Cmd.move_W, Cmd.move_S, Cmd.move_N, Cmd.move_E, goal);
59 putstr(tmpwin, 0, sbuf);
60 putstr(tmpwin, 0, "Use 'H', 'J', 'K', 'L' to move the cursor 8 units at a time.");
61 putstr(tmpwin, 0, "Or enter a background symbol (ex. '<').");
62 Sprintf(sbuf, "Use '%s' to move the cursor on yourself.",
63 visctrl(Cmd.spkeys[NHKF_GETPOS_SELF]));
64 putstr(tmpwin, 0, sbuf);
65 if (!iflags.terrainmode || (iflags.terrainmode & TER_MON) != 0) {
66 Sprintf(sbuf, "Use '%s' or '%s' to move the cursor to next monster.",
67 visctrl(Cmd.spkeys[NHKF_GETPOS_MON_NEXT]),
68 visctrl(Cmd.spkeys[NHKF_GETPOS_MON_PREV]));
69 putstr(tmpwin, 0, sbuf);
71 if (!iflags.terrainmode || (iflags.terrainmode & TER_OBJ) != 0) {
72 Sprintf(sbuf, "Use '%s' or '%s' to move the cursor to next object.",
73 visctrl(Cmd.spkeys[NHKF_GETPOS_OBJ_NEXT]),
74 visctrl(Cmd.spkeys[NHKF_GETPOS_OBJ_PREV]));
75 putstr(tmpwin, 0, sbuf);
77 if (!iflags.terrainmode || (iflags.terrainmode & TER_MAP) != 0) {
78 /* both of these are primarily useful when choosing a travel
79 destination for the '_' command */
80 Sprintf(sbuf,
81 "Use '%s' or '%s' to move the cursor to next door or doorway.",
82 visctrl(Cmd.spkeys[NHKF_GETPOS_DOOR_NEXT]),
83 visctrl(Cmd.spkeys[NHKF_GETPOS_DOOR_PREV]));
84 putstr(tmpwin, 0, sbuf);
85 Sprintf(sbuf,
86 "Use '%s' or '%s' to move the cursor to unexplored location.",
87 visctrl(Cmd.spkeys[NHKF_GETPOS_UNEX_NEXT]),
88 visctrl(Cmd.spkeys[NHKF_GETPOS_UNEX_PREV]));
89 putstr(tmpwin, 0, sbuf);
91 if (!iflags.terrainmode) {
92 char kbuf[BUFSZ];
93 if (getpos_hilitefunc) {
94 Sprintf(sbuf, "Use '%s' to display valid locations.",
95 visctrl(Cmd.spkeys[NHKF_GETPOS_SHOWVALID]));
96 putstr(tmpwin, 0, sbuf);
98 Sprintf(sbuf, "Use '%s' to toggle automatic description.",
99 visctrl(Cmd.spkeys[NHKF_GETPOS_AUTODESC]));
100 putstr(tmpwin, 0, sbuf);
101 if (iflags.cmdassist) { /* assisting the '/' command, I suppose... */
102 Sprintf(sbuf,
103 (iflags.getpos_coords == GPCOORDS_NONE)
104 ? "(Set 'whatis_coord' option to include coordinates with '%s' text.)"
105 : "(Reset 'whatis_coord' option to omit coordinates from '%s' text.)",
106 visctrl(Cmd.spkeys[NHKF_GETPOS_AUTODESC]));
108 /* disgusting hack; the alternate selection characters work for any
109 getpos call, but only matter for dowhatis (and doquickwhatis) */
110 doing_what_is = (goal == what_is_an_unknown_object);
111 if (doing_what_is) {
112 Sprintf(kbuf, "'%s' or '%s' or '%s' or '%s'",
113 visctrl(Cmd.spkeys[NHKF_GETPOS_PICK]),
114 visctrl(Cmd.spkeys[NHKF_GETPOS_PICK_Q]),
115 visctrl(Cmd.spkeys[NHKF_GETPOS_PICK_O]),
116 visctrl(Cmd.spkeys[NHKF_GETPOS_PICK_V]));
117 } else {
118 Sprintf(kbuf, "'%s'", visctrl(Cmd.spkeys[NHKF_GETPOS_PICK]));
120 Sprintf(sbuf, "Type a %s when you are at the right place.", kbuf);
121 putstr(tmpwin, 0, sbuf);
122 if (doing_what_is) {
123 Sprintf(sbuf,
124 " '%s' describe current spot, show 'more info', move to another spot.",
125 visctrl(Cmd.spkeys[NHKF_GETPOS_PICK_V]));
126 putstr(tmpwin, 0, sbuf);
127 Sprintf(sbuf,
128 " '%s' describe current spot,%s move to another spot;",
129 visctrl(Cmd.spkeys[NHKF_GETPOS_PICK]),
130 flags.help ? " prompt if 'more info'," : "");
131 putstr(tmpwin, 0, sbuf);
132 Sprintf(sbuf,
133 " '%s' describe current spot, move to another spot;",
134 visctrl(Cmd.spkeys[NHKF_GETPOS_PICK_Q]));
135 putstr(tmpwin, 0, sbuf);
136 Sprintf(sbuf,
137 " '%s' describe current spot, stop looking at things;",
138 visctrl(Cmd.spkeys[NHKF_GETPOS_PICK_O]));
139 putstr(tmpwin, 0, sbuf);
142 if (!force)
143 putstr(tmpwin, 0, "Type Space or Escape when you're done.");
144 putstr(tmpwin, 0, "");
145 display_nhwindow(tmpwin, TRUE);
146 destroy_nhwindow(tmpwin);
149 STATIC_OVL int
150 cmp_coord_distu(a, b)
151 const void *a;
152 const void *b;
154 const coord *c1 = a;
155 const coord *c2 = b;
156 int dx, dy, dist_1, dist_2;
158 dx = u.ux - c1->x;
159 dy = u.uy - c1->y;
160 dist_1 = max(abs(dx), abs(dy));
161 dx = u.ux - c2->x;
162 dy = u.uy - c2->y;
163 dist_2 = max(abs(dx), abs(dy));
165 if (dist_1 == dist_2)
166 return (c1->y != c2->y) ? (c1->y - c2->y) : (c1->x - c2->x);
168 return dist_1 - dist_2;
171 enum gloctypes {
172 GLOC_MONS = 0,
173 GLOC_OBJS,
174 GLOC_DOOR,
175 GLOC_EXPLORE,
177 NUM_GLOCS
181 #define IS_UNEXPLORED_LOC(x,y) \
182 (isok((x), (y)) \
183 && glyph_is_cmap(levl[(x)][(y)].glyph) \
184 && glyph_to_cmap(levl[(x)][(y)].glyph) == S_stone \
185 && !levl[(x)][(y)].seenv)
187 STATIC_OVL boolean
188 gather_locs_interesting(x,y, gloc)
189 int x,y, gloc;
191 /* TODO: if glyph is a pile glyph, convert to ordinary one
192 * in order to keep tail/boulder/rock check simple.
194 int glyph = glyph_at(x, y);
196 switch (gloc) {
197 default:
198 case GLOC_MONS:
199 /* unlike '/M', this skips monsters revealed by
200 warning glyphs and remembered unseen ones */
201 return (glyph_is_monster(glyph)
202 && glyph != monnum_to_glyph(PM_LONG_WORM_TAIL));
203 case GLOC_OBJS:
204 return (glyph_is_object(glyph)
205 && glyph != objnum_to_glyph(BOULDER)
206 && glyph != objnum_to_glyph(ROCK));
207 case GLOC_DOOR:
208 return (glyph_is_cmap(glyph)
209 && (is_cmap_door(glyph_to_cmap(glyph))
210 || is_cmap_drawbridge(glyph_to_cmap(glyph))
211 || glyph_to_cmap(glyph) == S_ndoor));
212 case GLOC_EXPLORE:
213 return (glyph_is_cmap(glyph)
214 && (is_cmap_door(glyph_to_cmap(glyph))
215 || is_cmap_drawbridge(glyph_to_cmap(glyph))
216 || glyph_to_cmap(glyph) == S_ndoor
217 || glyph_to_cmap(glyph) == S_room
218 || glyph_to_cmap(glyph) == S_darkroom
219 || glyph_to_cmap(glyph) == S_corr
220 || glyph_to_cmap(glyph) == S_litcorr)
221 && (IS_UNEXPLORED_LOC(x + 1, y)
222 || IS_UNEXPLORED_LOC(x - 1, y)
223 || IS_UNEXPLORED_LOC(x, y + 1)
224 || IS_UNEXPLORED_LOC(x, y - 1)));
226 /*NOTREACHED*/
227 return FALSE;
230 /* gather locations for monsters or objects shown on the map */
231 STATIC_OVL void
232 gather_locs(arr_p, cnt_p, gloc)
233 coord **arr_p;
234 int *cnt_p;
235 int gloc;
237 int x, y, pass, idx;
240 * We always include the hero's location even if there is no monster
241 * (invisible hero without see invisible) or object (usual case)
242 * displayed there. That way, the count will always be at least 1,
243 * and player has a visual indicator (cursor returns to hero's spot)
244 * highlighting when successive 'm's or 'o's have cycled all the way
245 * through all monsters or objects.
247 * Hero's spot will always sort to array[0] because it will always
248 * be the shortest distance (namely, 0 units) away from <u.ux,u.uy>.
250 *cnt_p = idx = 0;
251 for (pass = 0; pass < 2; pass++) {
252 for (x = 1; x < COLNO; x++)
253 for (y = 0; y < ROWNO; y++) {
254 if ((x == u.ux && y == u.uy)
255 || gather_locs_interesting(x, y, gloc)) {
256 if (!pass) {
257 ++*cnt_p;
258 } else {
259 (*arr_p)[idx].x = x;
260 (*arr_p)[idx].y = y;
261 ++idx;
266 if (!pass) /* end of first pass */
267 *arr_p = (coord *) alloc(*cnt_p * sizeof (coord));
268 else /* end of second pass */
269 qsort(*arr_p, *cnt_p, sizeof (coord), cmp_coord_distu);
270 } /* pass */
273 char *
274 dxdy_to_dist_descr(dx, dy, fulldir)
275 int dx, dy;
276 boolean fulldir;
278 static char buf[30];
279 int dst;
281 if (!dx && !dy) {
282 Sprintf(buf, "here");
283 } else if ((dst = xytod(dx, dy)) != -1) {
284 /* explicit direction; 'one step' is implicit */
285 Sprintf(buf, "%s", directionname(dst));
286 } else {
287 const char *dirnames[4][2] = {
288 { "n", "north" },
289 { "s", "south" },
290 { "w", "west" },
291 { "e", "east" } };
292 buf[0] = '\0';
293 /* 9999: protect buf[] against overflow caused by invalid values */
294 if (dy) {
295 if (abs(dy) > 9999)
296 dy = sgn(dy) * 9999;
297 Sprintf(eos(buf), "%d%s%s", abs(dy), dirnames[(dy > 0)][fulldir],
298 dx ? "," : "");
300 if (dx) {
301 if (abs(dx) > 9999)
302 dx = sgn(dx) * 9999;
303 Sprintf(eos(buf), "%d%s", abs(dx), dirnames[2 + (dx > 0)][fulldir]);
306 return buf;
309 /* coordinate formatting for 'whatis_coord' option */
310 char *
311 coord_desc(x, y, outbuf, cmode)
312 int x, y;
313 char *outbuf, cmode;
315 static char screen_fmt[16]; /* [12] suffices: "[%02d,%02d]" */
316 int dx, dy;
318 outbuf[0] = '\0';
319 switch (cmode) {
320 default:
321 break;
322 case GPCOORDS_COMFULL:
323 case GPCOORDS_COMPASS:
324 /* "east", "3s", "2n,4w" */
325 dx = x - u.ux;
326 dy = y - u.uy;
327 Sprintf(outbuf, "(%s)",
328 dxdy_to_dist_descr(dx, dy, cmode == GPCOORDS_COMFULL));
329 break;
330 case GPCOORDS_MAP: /* x,y */
331 /* upper left corner of map is <1,0>;
332 with default COLNO,ROWNO lower right corner is <79,20> */
333 Sprintf(outbuf, "<%d,%d>", x, y);
334 break;
335 case GPCOORDS_SCREEN: /* y+2,x */
336 /* for normal map sizes, force a fixed-width formatting so that
337 /m, /M, /o, and /O output lines up cleanly; map sizes bigger
338 than Nx999 or 999xM will still work, but not line up like normal
339 when displayed in a column setting */
340 if (!*screen_fmt)
341 Sprintf(screen_fmt, "[%%%sd,%%%sd]",
342 (ROWNO - 1 + 2 < 100) ? "02" : "03",
343 (COLNO - 1 < 100) ? "02" : "03");
344 /* map line 0 is screen row 2;
345 map column 0 isn't used, map column 1 is screen column 1 */
346 Sprintf(outbuf, screen_fmt, y + 2, x);
347 break;
349 return outbuf;
352 STATIC_OVL void
353 auto_describe(cx, cy)
354 int cx, cy;
356 coord cc;
357 int sym = 0;
358 char tmpbuf[BUFSZ];
359 const char *firstmatch = "unknown";
361 cc.x = cx;
362 cc.y = cy;
363 if (do_screen_description(cc, TRUE, sym, tmpbuf, &firstmatch)) {
364 (void) coord_desc(cx, cy, tmpbuf, iflags.getpos_coords);
365 pline("%s%s%s%s", firstmatch, *tmpbuf ? " " : "", tmpbuf,
366 (iflags.getloc_travelmode && !is_valid_travelpt(cx,cy))
367 ? " (no travel path)" : "");
368 curs(WIN_MAP, cx, cy);
369 flush_screen(0);
374 getpos(ccp, force, goal)
375 coord *ccp;
376 boolean force;
377 const char *goal;
379 const char *cp;
380 struct {
381 int nhkf, ret;
382 } const pick_chars_def[] = {
383 { NHKF_GETPOS_PICK, LOOK_TRADITIONAL },
384 { NHKF_GETPOS_PICK_Q, LOOK_QUICK },
385 { NHKF_GETPOS_PICK_O, LOOK_ONCE },
386 { NHKF_GETPOS_PICK_V, LOOK_VERBOSE }
388 const int mMoOdDxX_def[] = {
389 NHKF_GETPOS_MON_NEXT,
390 NHKF_GETPOS_MON_PREV,
391 NHKF_GETPOS_OBJ_NEXT,
392 NHKF_GETPOS_OBJ_PREV,
393 NHKF_GETPOS_DOOR_NEXT,
394 NHKF_GETPOS_DOOR_PREV,
395 NHKF_GETPOS_UNEX_NEXT,
396 NHKF_GETPOS_UNEX_PREV
398 char pick_chars[6];
399 char mMoOdDxX[9];
400 int result = 0;
401 int cx, cy, i, c;
402 int sidx, tx, ty;
403 boolean msg_given = TRUE; /* clear message window by default */
404 boolean show_goal_msg = FALSE;
405 boolean hilite_state = FALSE;
406 coord *garr[NUM_GLOCS] = DUMMY;
407 int gcount[NUM_GLOCS] = DUMMY;
408 int gidx[NUM_GLOCS] = DUMMY;
410 for (i = 0; i < SIZE(pick_chars_def); i++)
411 pick_chars[i] = Cmd.spkeys[pick_chars_def[i].nhkf];
412 pick_chars[SIZE(pick_chars_def)] = '\0';
414 for (i = 0; i < SIZE(mMoOdDxX_def); i++)
415 mMoOdDxX[i] = Cmd.spkeys[mMoOdDxX_def[i]];
416 mMoOdDxX[SIZE(mMoOdDxX_def)] = '\0';
418 if (!goal)
419 goal = "desired location";
420 if (flags.verbose) {
421 pline("(For instructions type a '%s')",
422 visctrl(Cmd.spkeys[NHKF_GETPOS_HELP]));
423 msg_given = TRUE;
425 cx = ccp->x;
426 cy = ccp->y;
427 #ifdef CLIPPING
428 cliparound(cx, cy);
429 #endif
430 curs(WIN_MAP, cx, cy);
431 flush_screen(0);
432 #ifdef MAC
433 lock_mouse_cursor(TRUE);
434 #endif
435 for (;;) {
436 if (show_goal_msg) {
437 pline("Move cursor to %s:", goal);
438 curs(WIN_MAP, cx, cy);
439 flush_screen(0);
440 show_goal_msg = FALSE;
441 } else if (iflags.autodescribe && !msg_given && !hilite_state) {
442 auto_describe(cx, cy);
445 c = nh_poskey(&tx, &ty, &sidx);
447 if (hilite_state) {
448 (*getpos_hilitefunc)(2);
449 hilite_state = FALSE;
450 curs(WIN_MAP, cx, cy);
451 flush_screen(0);
454 if (iflags.autodescribe)
455 msg_given = FALSE;
457 if (c == Cmd.spkeys[NHKF_ESC]) {
458 cx = cy = -10;
459 msg_given = TRUE; /* force clear */
460 result = -1;
461 break;
463 if (c == 0) {
464 if (!isok(tx, ty))
465 continue;
466 /* a mouse click event, just assign and return */
467 cx = tx;
468 cy = ty;
469 break;
471 if ((cp = index(pick_chars, c)) != 0) {
472 /* '.' => 0, ',' => 1, ';' => 2, ':' => 3 */
473 result = pick_chars_def[(int) (cp - pick_chars)].ret;
474 break;
476 for (i = 0; i < 8; i++) {
477 int dx, dy;
479 if (Cmd.dirchars[i] == c) {
480 /* a normal movement letter or digit */
481 dx = xdir[i];
482 dy = ydir[i];
483 } else if (Cmd.alphadirchars[i] == lowc((char) c)
484 || (Cmd.num_pad && Cmd.dirchars[i] == (c & 0177))) {
485 /* a shifted movement letter or Meta-digit */
486 dx = 8 * xdir[i];
487 dy = 8 * ydir[i];
488 } else
489 continue;
491 /* truncate at map edge; diagonal moves complicate this... */
492 if (cx + dx < 1) {
493 dy -= sgn(dy) * (1 - (cx + dx));
494 dx = 1 - cx; /* so that (cx+dx == 1) */
495 } else if (cx + dx > COLNO - 1) {
496 dy += sgn(dy) * ((COLNO - 1) - (cx + dx));
497 dx = (COLNO - 1) - cx;
499 if (cy + dy < 0) {
500 dx -= sgn(dx) * (0 - (cy + dy));
501 dy = 0 - cy; /* so that (cy+dy == 0) */
502 } else if (cy + dy > ROWNO - 1) {
503 dx += sgn(dx) * ((ROWNO - 1) - (cy + dy));
504 dy = (ROWNO - 1) - cy;
506 cx += dx;
507 cy += dy;
508 goto nxtc;
511 if (c == Cmd.spkeys[NHKF_GETPOS_HELP] || redraw_cmd(c)) {
512 if (c == Cmd.spkeys[NHKF_GETPOS_HELP])
513 getpos_help(force, goal);
514 else /* ^R */
515 docrt(); /* redraw */
516 /* update message window to reflect that we're still targetting */
517 show_goal_msg = TRUE;
518 msg_given = TRUE;
519 } else if (c == Cmd.spkeys[NHKF_GETPOS_SHOWVALID]
520 && getpos_hilitefunc) {
521 if (!hilite_state) {
522 (*getpos_hilitefunc)(0);
523 (*getpos_hilitefunc)(1);
524 hilite_state = TRUE;
526 goto nxtc;
527 } else if (c == Cmd.spkeys[NHKF_GETPOS_AUTODESC]) {
528 iflags.autodescribe = !iflags.autodescribe;
529 pline("Automatic description %sis %s.",
530 flags.verbose ? "of features under cursor " : "",
531 iflags.autodescribe ? "on" : "off");
532 if (!iflags.autodescribe)
533 show_goal_msg = TRUE;
534 msg_given = TRUE;
535 goto nxtc;
536 } else if (c == Cmd.spkeys[NHKF_GETPOS_SELF]) {
537 /* reset 'm&M', 'o&O', &c; otherwise, there's no way for player
538 to achieve that except by manually cycling through all spots */
539 for (i = 0; i < NUM_GLOCS; i++)
540 gidx[i] = 0;
541 cx = u.ux;
542 cy = u.uy;
543 goto nxtc;
544 } else if ((cp = index(mMoOdDxX, c)) != 0) { /* 'm|M', 'o|O', &c */
545 /* nearest or farthest monster or object or door or unexplored */
546 int gtmp = (int) (cp - mMoOdDxX), /* 0..7 */
547 gloc = gtmp >> 1; /* 0..3 */
549 if (!garr[gloc]) {
550 gather_locs(&garr[gloc], &gcount[gloc], gloc);
551 gidx[gloc] = 0; /* garr[][0] is hero's spot */
553 if (!(gtmp & 1)) { /* c=='m' || c=='o' || c=='d' || c=='x') */
554 gidx[gloc] = (gidx[gloc] + 1) % gcount[gloc];
555 } else { /* c=='M' || c=='O' || c=='D' || c=='X') */
556 if (--gidx[gloc] < 0)
557 gidx[gloc] = gcount[gloc] - 1;
559 cx = garr[gloc][gidx[gloc]].x;
560 cy = garr[gloc][gidx[gloc]].y;
561 goto nxtc;
562 } else {
563 if (!index(quitchars, c)) {
564 char matching[MAXPCHARS];
565 int pass, lo_x, lo_y, hi_x, hi_y, k = 0;
567 (void) memset((genericptr_t) matching, 0, sizeof matching);
568 for (sidx = 1; sidx < MAXPCHARS; sidx++) { /* [0] left as 0 */
569 if (IS_DOOR(sidx) || IS_WALL(sidx)
570 || sidx == SDOOR || sidx == SCORR
571 || glyph_to_cmap(k) == S_room
572 || glyph_to_cmap(k) == S_darkroom
573 || glyph_to_cmap(k) == S_corr
574 || glyph_to_cmap(k) == S_litcorr)
575 continue;
576 if (c == defsyms[sidx].sym || c == (int) showsyms[sidx])
577 matching[sidx] = (char) ++k;
579 if (k) {
580 for (pass = 0; pass <= 1; pass++) {
581 /* pass 0: just past current pos to lower right;
582 pass 1: upper left corner to current pos */
583 lo_y = (pass == 0) ? cy : 0;
584 hi_y = (pass == 0) ? ROWNO - 1 : cy;
585 for (ty = lo_y; ty <= hi_y; ty++) {
586 lo_x = (pass == 0 && ty == lo_y) ? cx + 1 : 1;
587 hi_x = (pass == 1 && ty == hi_y) ? cx : COLNO - 1;
588 for (tx = lo_x; tx <= hi_x; tx++) {
589 /* first, look at what is currently visible
590 (might be monster) */
591 k = glyph_at(tx, ty);
592 if (glyph_is_cmap(k)
593 && matching[glyph_to_cmap(k)])
594 goto foundc;
595 /* next, try glyph that's remembered here
596 (might be trap or object) */
597 if (level.flags.hero_memory
598 /* !terrainmode: don't move to remembered
599 trap or object if not currently shown */
600 && !iflags.terrainmode) {
601 k = levl[tx][ty].glyph;
602 if (glyph_is_cmap(k)
603 && matching[glyph_to_cmap(k)])
604 goto foundc;
606 /* last, try actual terrain here (shouldn't
607 we be using lastseentyp[][] instead?) */
608 if (levl[tx][ty].seenv) {
609 k = back_to_glyph(tx, ty);
610 if (glyph_is_cmap(k)
611 && matching[glyph_to_cmap(k)])
612 goto foundc;
614 continue;
615 foundc:
616 cx = tx, cy = ty;
617 if (msg_given) {
618 clear_nhwindow(WIN_MESSAGE);
619 msg_given = FALSE;
621 goto nxtc;
622 } /* column */
623 } /* row */
624 } /* pass */
625 pline("Can't find dungeon feature '%c'.", c);
626 msg_given = TRUE;
627 goto nxtc;
628 } else {
629 char note[QBUFSZ];
631 if (!force)
632 Strcpy(note, "aborted");
633 else
634 Sprintf(note, "use '%c', '%c', '%c', '%c' or '%s'", /* hjkl */
635 Cmd.move_W, Cmd.move_S, Cmd.move_N,
636 Cmd.move_E,
637 visctrl(Cmd.spkeys[NHKF_GETPOS_PICK]));
638 pline("Unknown direction: '%s' (%s).", visctrl((char) c),
639 note);
640 msg_given = TRUE;
641 } /* k => matching */
642 } /* !quitchars */
643 if (force)
644 goto nxtc;
645 pline("Done.");
646 msg_given = FALSE; /* suppress clear */
647 cx = -1;
648 cy = 0;
649 result = 0; /* not -1 */
650 break;
652 nxtc:
654 #ifdef CLIPPING
655 cliparound(cx, cy);
656 #endif
657 curs(WIN_MAP, cx, cy);
658 flush_screen(0);
660 #ifdef MAC
661 lock_mouse_cursor(FALSE);
662 #endif
663 if (msg_given)
664 clear_nhwindow(WIN_MESSAGE);
665 ccp->x = cx;
666 ccp->y = cy;
667 for (i = 0; i < NUM_GLOCS; i++)
668 if (garr[i])
669 free((genericptr_t) garr[i]);
670 getpos_hilitefunc = (void FDECL((*), (int))) 0;
671 return result;
674 /* allocate space for a monster's name; removes old name if there is one */
675 void
676 new_mname(mon, lth)
677 struct monst *mon;
678 int lth; /* desired length (caller handles adding 1 for terminator) */
680 if (lth) {
681 /* allocate mextra if necessary; otherwise get rid of old name */
682 if (!mon->mextra)
683 mon->mextra = newmextra();
684 else
685 free_mname(mon); /* already has mextra, might also have name */
686 MNAME(mon) = (char *) alloc((unsigned) lth);
687 } else {
688 /* zero length: the new name is empty; get rid of the old name */
689 if (has_mname(mon))
690 free_mname(mon);
694 /* release a monster's name; retains mextra even if all fields are now null */
695 void
696 free_mname(mon)
697 struct monst *mon;
699 if (has_mname(mon)) {
700 free((genericptr_t) MNAME(mon));
701 MNAME(mon) = (char *) 0;
705 /* allocate space for an object's name; removes old name if there is one */
706 void
707 new_oname(obj, lth)
708 struct obj *obj;
709 int lth; /* desired length (caller handles adding 1 for terminator) */
711 if (lth) {
712 /* allocate oextra if necessary; otherwise get rid of old name */
713 if (!obj->oextra)
714 obj->oextra = newoextra();
715 else
716 free_oname(obj); /* already has oextra, might also have name */
717 ONAME(obj) = (char *) alloc((unsigned) lth);
718 } else {
719 /* zero length: the new name is empty; get rid of the old name */
720 if (has_oname(obj))
721 free_oname(obj);
725 /* release an object's name; retains oextra even if all fields are now null */
726 void
727 free_oname(obj)
728 struct obj *obj;
730 if (has_oname(obj)) {
731 free((genericptr_t) ONAME(obj));
732 ONAME(obj) = (char *) 0;
736 /* safe_oname() always returns a valid pointer to
737 * a string, either the pointer to an object's name
738 * if it has one, or a pointer to an empty string
739 * if it doesn't.
741 const char *
742 safe_oname(obj)
743 struct obj *obj;
745 if (has_oname(obj))
746 return ONAME(obj);
747 return "";
750 /* historical note: this returns a monster pointer because it used to
751 allocate a new bigger block of memory to hold the monster and its name */
752 struct monst *
753 christen_monst(mtmp, name)
754 struct monst *mtmp;
755 const char *name;
757 int lth;
758 char buf[PL_PSIZ];
760 /* dogname & catname are PL_PSIZ arrays; object names have same limit */
761 lth = (name && *name) ? ((int) strlen(name) + 1) : 0;
762 if (lth > PL_PSIZ) {
763 lth = PL_PSIZ;
764 name = strncpy(buf, name, PL_PSIZ - 1);
765 buf[PL_PSIZ - 1] = '\0';
767 new_mname(mtmp, lth); /* removes old name if one is present */
768 if (lth)
769 Strcpy(MNAME(mtmp), name);
770 return mtmp;
773 /* check whether user-supplied name matches or nearly matches an unnameable
774 monster's name; if so, give an alternate reject message for do_mname() */
775 STATIC_OVL boolean
776 alreadynamed(mtmp, monnambuf, usrbuf)
777 struct monst *mtmp;
778 char *monnambuf, *usrbuf;
780 char pronounbuf[10], *p;
782 if (fuzzymatch(usrbuf, monnambuf, " -_", TRUE)
783 /* catch trying to name "the Oracle" as "Oracle" */
784 || (!strncmpi(monnambuf, "the ", 4)
785 && fuzzymatch(usrbuf, monnambuf + 4, " -_", TRUE))
786 /* catch trying to name "invisible Orcus" as "Orcus" */
787 || ((p = strstri(monnambuf, "invisible ")) != 0
788 && fuzzymatch(usrbuf, p + 10, " -_", TRUE))
789 /* catch trying to name "the {priest,Angel} of Crom" as "Crom" */
790 || ((p = strstri(monnambuf, " of ")) != 0
791 && fuzzymatch(usrbuf, p + 4, " -_", TRUE))) {
792 pline("%s is already called %s.",
793 upstart(strcpy(pronounbuf, mhe(mtmp))), monnambuf);
794 return TRUE;
795 } else if (mtmp->data == &mons[PM_JUIBLEX]
796 && strstri(monnambuf, "Juiblex")
797 && !strcmpi(usrbuf, "Jubilex")) {
798 pline("%s doesn't like being called %s.", upstart(monnambuf), usrbuf);
799 return TRUE;
801 return FALSE;
804 /* allow player to assign a name to some chosen monster */
805 STATIC_OVL void
806 do_mname()
808 char buf[BUFSZ], monnambuf[BUFSZ], qbuf[QBUFSZ];
809 coord cc;
810 int cx, cy;
811 struct monst *mtmp = 0;
813 if (Hallucination) {
814 You("would never recognize it anyway.");
815 return;
817 cc.x = u.ux;
818 cc.y = u.uy;
819 if (getpos(&cc, FALSE, "the monster you want to name") < 0
820 || (cx = cc.x) < 0)
821 return;
822 cy = cc.y;
824 if (cx == u.ux && cy == u.uy) {
825 if (u.usteed && canspotmon(u.usteed)) {
826 mtmp = u.usteed;
827 } else {
828 pline("This %s creature is called %s and cannot be renamed.",
829 beautiful(), plname);
830 return;
832 } else
833 mtmp = m_at(cx, cy);
835 if (!mtmp
836 || (!sensemon(mtmp)
837 && (!(cansee(cx, cy) || see_with_infrared(mtmp))
838 || mtmp->mundetected || mtmp->m_ap_type == M_AP_FURNITURE
839 || mtmp->m_ap_type == M_AP_OBJECT
840 || (mtmp->minvis && !See_invisible)))) {
841 pline("I see no monster there.");
842 return;
844 /* special case similar to the one in lookat() */
845 Sprintf(qbuf, "What do you want to call %s?",
846 distant_monnam(mtmp, ARTICLE_THE, monnambuf));
847 getlin(qbuf, buf);
848 if (!*buf || *buf == '\033')
849 return;
850 /* strip leading and trailing spaces; unnames monster if all spaces */
851 (void) mungspaces(buf);
853 /* Unique monsters have their own specific names or titles.
854 * Shopkeepers, temple priests and other minions use alternate
855 * name formatting routines which ignore any user-supplied name.
857 * Don't say the name is being rejected if it happens to match
858 * the existing name.
860 if ((mtmp->data->geno & G_UNIQ) && !mtmp->ispriest) {
861 if (!alreadynamed(mtmp, monnambuf, buf))
862 pline("%s doesn't like being called names!", upstart(monnambuf));
863 } else if (mtmp->isshk
864 && !(Deaf || mtmp->msleeping || !mtmp->mcanmove
865 || mtmp->data->msound <= MS_ANIMAL)) {
866 if (!alreadynamed(mtmp, monnambuf, buf))
867 verbalize("I'm %s, not %s.", shkname(mtmp), buf);
868 } else if (mtmp->ispriest || mtmp->isminion || mtmp->isshk) {
869 if (!alreadynamed(mtmp, monnambuf, buf))
870 pline("%s will not accept the name %s.", upstart(monnambuf), buf);
871 } else
872 (void) christen_monst(mtmp, buf);
876 * This routine changes the address of obj. Be careful not to call it
877 * when there might be pointers around in unknown places. For now: only
878 * when obj is in the inventory.
880 STATIC_OVL
881 void
882 do_oname(obj)
883 register struct obj *obj;
885 char *bufp, buf[BUFSZ], bufcpy[BUFSZ], qbuf[QBUFSZ];
886 const char *aname;
887 short objtyp;
889 /* Do this now because there's no point in even asking for a name */
890 if (obj->otyp == SPE_NOVEL) {
891 pline("%s already has a published name.", Ysimple_name2(obj));
892 return;
895 Sprintf(qbuf, "What do you want to name %s ",
896 is_plural(obj) ? "these" : "this");
897 (void) safe_qbuf(qbuf, qbuf, "?", obj, xname, simpleonames, "item");
898 getlin(qbuf, buf);
899 if (!*buf || *buf == '\033')
900 return;
901 /* strip leading and trailing spaces; unnames item if all spaces */
902 (void) mungspaces(buf);
905 * We don't violate illiteracy conduct here, although it is
906 * arguable that we should for anything other than "X". Doing so
907 * would make attaching player's notes to hero's inventory have an
908 * in-game effect, which may or may not be the correct thing to do.
910 * We do violate illiteracy in oname() if player creates Sting or
911 * Orcrist, clearly being literate (no pun intended...).
914 /* relax restrictions over proper capitalization for artifacts */
915 if ((aname = artifact_name(buf, &objtyp)) != 0 && objtyp == obj->otyp)
916 Strcpy(buf, aname);
918 if (obj->oartifact) {
919 pline_The("artifact seems to resist the attempt.");
920 return;
921 } else if (restrict_name(obj, buf) || exist_artifact(obj->otyp, buf)) {
922 /* this used to change one letter, substituting a value
923 of 'a' through 'y' (due to an off by one error, 'z'
924 would never be selected) and then force that to
925 upper case if such was the case of the input;
926 now, the hand slip scuffs one or two letters as if
927 the text had been trodden upon, sometimes picking
928 punctuation instead of an arbitrary letter;
929 unfortunately, we have to cover the possibility of
930 it targetting spaces so failing to make any change
931 (we know that it must eventually target a nonspace
932 because buf[] matches a valid artifact name) */
933 Strcpy(bufcpy, buf);
934 /* for "the Foo of Bar", only scuff "Foo of Bar" part */
935 bufp = !strncmpi(bufcpy, "the ", 4) ? (buf + 4) : buf;
936 do {
937 wipeout_text(bufp, rnd(2), (unsigned) 0);
938 } while (!strcmp(buf, bufcpy));
939 pline("While engraving, your %s slips.", body_part(HAND));
940 display_nhwindow(WIN_MESSAGE, FALSE);
941 You("engrave: \"%s\".", buf);
943 obj = oname(obj, buf);
946 struct obj *
947 oname(obj, name)
948 struct obj *obj;
949 const char *name;
951 int lth;
952 char buf[PL_PSIZ];
954 lth = *name ? (int) (strlen(name) + 1) : 0;
955 if (lth > PL_PSIZ) {
956 lth = PL_PSIZ;
957 name = strncpy(buf, name, PL_PSIZ - 1);
958 buf[PL_PSIZ - 1] = '\0';
960 /* If named artifact exists in the game, do not create another.
961 * Also trying to create an artifact shouldn't de-artifact
962 * it (e.g. Excalibur from prayer). In this case the object
963 * will retain its current name. */
964 if (obj->oartifact || (lth && exist_artifact(obj->otyp, name)))
965 return obj;
967 new_oname(obj, lth); /* removes old name if one is present */
968 if (lth)
969 Strcpy(ONAME(obj), name);
971 if (lth)
972 artifact_exists(obj, name, TRUE);
973 if (obj->oartifact) {
974 /* can't dual-wield with artifact as secondary weapon */
975 if (obj == uswapwep)
976 untwoweapon();
977 /* activate warning if you've just named your weapon "Sting" */
978 if (obj == uwep)
979 set_artifact_intrinsic(obj, TRUE, W_WEP);
980 /* if obj is owned by a shop, increase your bill */
981 if (obj->unpaid)
982 alter_cost(obj, 0L);
983 /* violate illiteracy conduct since successfully wrote arti-name */
984 u.uconduct.literate++;
986 if (carried(obj))
987 update_inventory();
988 return obj;
991 static NEARDATA const char callable[] = {
992 SCROLL_CLASS, POTION_CLASS, WAND_CLASS, RING_CLASS, AMULET_CLASS,
993 GEM_CLASS, SPBOOK_CLASS, ARMOR_CLASS, TOOL_CLASS, 0
996 boolean
997 objtyp_is_callable(i)
998 int i;
1000 return (boolean) (objects[i].oc_uname
1001 || (OBJ_DESCR(objects[i])
1002 && index(callable, objects[i].oc_class)));
1005 /* C and #name commands - player can name monster or object or type of obj */
1007 docallcmd()
1009 struct obj *obj;
1010 winid win;
1011 anything any;
1012 menu_item *pick_list = 0;
1013 char ch, allowall[2];
1014 /* if player wants a,b,c instead of i,o when looting, do that here too */
1015 boolean abc = flags.lootabc;
1017 win = create_nhwindow(NHW_MENU);
1018 start_menu(win);
1019 any = zeroany;
1020 any.a_char = 'm'; /* group accelerator 'C' */
1021 add_menu(win, NO_GLYPH, &any, abc ? 0 : any.a_char, 'C', ATR_NONE,
1022 "a monster", MENU_UNSELECTED);
1023 if (invent) {
1024 /* we use y and n as accelerators so that we can accept user's
1025 response keyed to old "name an individual object?" prompt */
1026 any.a_char = 'i'; /* group accelerator 'y' */
1027 add_menu(win, NO_GLYPH, &any, abc ? 0 : any.a_char, 'y', ATR_NONE,
1028 "a particular object in inventory", MENU_UNSELECTED);
1029 any.a_char = 'o'; /* group accelerator 'n' */
1030 add_menu(win, NO_GLYPH, &any, abc ? 0 : any.a_char, 'n', ATR_NONE,
1031 "the type of an object in inventory", MENU_UNSELECTED);
1033 any.a_char = 'f'; /* group accelerator ',' (or ':' instead?) */
1034 add_menu(win, NO_GLYPH, &any, abc ? 0 : any.a_char, ',', ATR_NONE,
1035 "the type of an object upon the floor", MENU_UNSELECTED);
1036 any.a_char = 'd'; /* group accelerator '\' */
1037 add_menu(win, NO_GLYPH, &any, abc ? 0 : any.a_char, '\\', ATR_NONE,
1038 "the type of an object on discoveries list", MENU_UNSELECTED);
1039 any.a_char = 'a'; /* group accelerator 'l' */
1040 add_menu(win, NO_GLYPH, &any, abc ? 0 : any.a_char, 'l', ATR_NONE,
1041 "record an annotation for the current level", MENU_UNSELECTED);
1042 end_menu(win, "What do you want to name?");
1043 if (select_menu(win, PICK_ONE, &pick_list) > 0) {
1044 ch = pick_list[0].item.a_char;
1045 free((genericptr_t) pick_list);
1046 } else
1047 ch = 'q';
1048 destroy_nhwindow(win);
1050 switch (ch) {
1051 default:
1052 case 'q':
1053 break;
1054 case 'm': /* name a visible monster */
1055 do_mname();
1056 break;
1057 case 'i': /* name an individual object in inventory */
1058 allowall[0] = ALL_CLASSES;
1059 allowall[1] = '\0';
1060 obj = getobj(allowall, "name");
1061 if (obj)
1062 do_oname(obj);
1063 break;
1064 case 'o': /* name a type of object in inventory */
1065 obj = getobj(callable, "call");
1066 if (obj) {
1067 /* behave as if examining it in inventory;
1068 this might set dknown if it was picked up
1069 while blind and the hero can now see */
1070 (void) xname(obj);
1072 if (!obj->dknown) {
1073 You("would never recognize another one.");
1074 #if 0
1075 } else if (!objtyp_is_callable(obj->otyp)) {
1076 You("know those as well as you ever will.");
1077 #endif
1078 } else {
1079 docall(obj);
1082 break;
1083 case 'f': /* name a type of object visible on the floor */
1084 namefloorobj();
1085 break;
1086 case 'd': /* name a type of object on the discoveries list */
1087 rename_disco();
1088 break;
1089 case 'a': /* annotate level */
1090 donamelevel();
1091 break;
1093 return 0;
1096 void
1097 docall(obj)
1098 register struct obj *obj;
1100 char buf[BUFSZ], qbuf[QBUFSZ];
1101 struct obj otemp;
1102 register char **str1;
1104 if (!obj->dknown)
1105 return; /* probably blind */
1106 otemp = *obj;
1107 otemp.quan = 1L;
1108 otemp.oextra = (struct oextra *) 0;
1110 if (objects[otemp.otyp].oc_class == POTION_CLASS && otemp.fromsink)
1111 /* kludge, meaning it's sink water */
1112 Sprintf(qbuf, "Call a stream of %s fluid:",
1113 OBJ_DESCR(objects[otemp.otyp]));
1114 else
1115 Sprintf(qbuf, "Call %s:", an(xname(&otemp)));
1116 getlin(qbuf, buf);
1117 if (!*buf || *buf == '\033')
1118 return;
1120 /* clear old name */
1121 str1 = &(objects[obj->otyp].oc_uname);
1122 if (*str1)
1123 free((genericptr_t) *str1);
1125 /* strip leading and trailing spaces; uncalls item if all spaces */
1126 (void) mungspaces(buf);
1127 if (!*buf) {
1128 if (*str1) { /* had name, so possibly remove from disco[] */
1129 /* strip name first, for the update_inventory() call
1130 from undiscover_object() */
1131 *str1 = (char *) 0;
1132 undiscover_object(obj->otyp);
1134 } else {
1135 *str1 = dupstr(buf);
1136 discover_object(obj->otyp, FALSE, TRUE); /* possibly add to disco[] */
1140 STATIC_OVL void
1141 namefloorobj()
1143 coord cc;
1144 int glyph;
1145 char buf[BUFSZ];
1146 struct obj *obj = 0;
1147 boolean fakeobj = FALSE, use_plural;
1149 cc.x = u.ux, cc.y = u.uy;
1150 /* "dot for under/over you" only makes sense when the cursor hasn't
1151 been moved off the hero's '@' yet, but there's no way to adjust
1152 the help text once getpos() has started */
1153 Sprintf(buf, "object on map (or '.' for one %s you)",
1154 (u.uundetected && hides_under(youmonst.data)) ? "over" : "under");
1155 if (getpos(&cc, FALSE, buf) < 0 || cc.x <= 0)
1156 return;
1157 if (cc.x == u.ux && cc.y == u.uy) {
1158 obj = vobj_at(u.ux, u.uy);
1159 } else {
1160 glyph = glyph_at(cc.x, cc.y);
1161 if (glyph_is_object(glyph))
1162 fakeobj = object_from_map(glyph, cc.x, cc.y, &obj);
1163 /* else 'obj' stays null */
1165 if (!obj) {
1166 /* "under you" is safe here since there's no object to hide under */
1167 pline("There doesn't seem to be any object %s.",
1168 (cc.x == u.ux && cc.y == u.uy) ? "under you" : "there");
1169 return;
1171 /* note well: 'obj' might be as instance of STRANGE_OBJECT if target
1172 is a mimic; passing that to xname (directly or via simpleonames)
1173 would yield "glorkum" so we need to handle it explicitly; it will
1174 always fail the Hallucination test and pass the !callable test,
1175 resulting in the "can't be assigned a type name" message */
1176 Strcpy(buf, (obj->otyp != STRANGE_OBJECT)
1177 ? simpleonames(obj)
1178 : obj_descr[STRANGE_OBJECT].oc_name);
1179 use_plural = (obj->quan > 1L);
1180 if (Hallucination) {
1181 const char *unames[6];
1182 char tmpbuf[BUFSZ];
1184 /* straight role name */
1185 unames[0] = ((Upolyd ? u.mfemale : flags.female) && urole.name.f)
1186 ? urole.name.f
1187 : urole.name.m;
1188 /* random rank title for hero's role */
1189 unames[1] = rank_of(rnd(30), Role_switch, flags.female);
1190 /* random fake monster */
1191 unames[2] = bogusmon(tmpbuf, (char *) 0);
1192 /* increased chance for fake monster */
1193 unames[3] = unames[2];
1194 /* traditional */
1195 unames[4] = roguename();
1196 /* silly */
1197 unames[5] = "Wibbly Wobbly";
1198 pline("%s %s to call you \"%s.\"",
1199 The(buf), use_plural ? "decide" : "decides",
1200 unames[rn2(SIZE(unames))]);
1201 } else if (!objtyp_is_callable(obj->otyp)) {
1202 pline("%s %s can't be assigned a type name.",
1203 use_plural ? "Those" : "That", buf);
1204 } else if (!obj->dknown) {
1205 You("don't know %s %s well enough to name %s.",
1206 use_plural ? "those" : "that", buf, use_plural ? "them" : "it");
1207 } else {
1208 docall(obj);
1210 if (fakeobj)
1211 dealloc_obj(obj);
1214 static const char *const ghostnames[] = {
1215 /* these names should have length < PL_NSIZ */
1216 /* Capitalize the names for aesthetics -dgk */
1217 "Adri", "Andries", "Andreas", "Bert", "David", "Dirk",
1218 "Emile", "Frans", "Fred", "Greg", "Hether", "Jay",
1219 "John", "Jon", "Karnov", "Kay", "Kenny", "Kevin",
1220 "Maud", "Michiel", "Mike", "Peter", "Robert", "Ron",
1221 "Tom", "Wilmar", "Nick Danger", "Phoenix", "Jiro", "Mizue",
1222 "Stephan", "Lance Braccus", "Shadowhawk"
1225 /* ghost names formerly set by x_monnam(), now by makemon() instead */
1226 const char *
1227 rndghostname()
1229 return rn2(7) ? ghostnames[rn2(SIZE(ghostnames))] : (const char *) plname;
1233 * Monster naming functions:
1234 * x_monnam is the generic monster-naming function.
1235 * seen unseen detected named
1236 * mon_nam: the newt it the invisible orc Fido
1237 * noit_mon_nam:the newt (as if detected) the invisible orc Fido
1238 * l_monnam: newt it invisible orc dog called Fido
1239 * Monnam: The newt It The invisible orc Fido
1240 * noit_Monnam: The newt (as if detected) The invisible orc Fido
1241 * Adjmonnam: The poor newt It The poor invisible orc The poor Fido
1242 * Amonnam: A newt It An invisible orc Fido
1243 * a_monnam: a newt it an invisible orc Fido
1244 * m_monnam: newt xan orc Fido
1245 * y_monnam: your newt your xan your invisible orc Fido
1248 /* Bug: if the monster is a priest or shopkeeper, not every one of these
1249 * options works, since those are special cases.
1251 char *
1252 x_monnam(mtmp, article, adjective, suppress, called)
1253 register struct monst *mtmp;
1254 int article;
1255 /* ARTICLE_NONE, ARTICLE_THE, ARTICLE_A: obvious
1256 * ARTICLE_YOUR: "your" on pets, "the" on everything else
1258 * If the monster would be referred to as "it" or if the monster has a name
1259 * _and_ there is no adjective, "invisible", "saddled", etc., override this
1260 * and always use no article.
1262 const char *adjective;
1263 int suppress;
1264 /* SUPPRESS_IT, SUPPRESS_INVISIBLE, SUPPRESS_HALLUCINATION, SUPPRESS_SADDLE.
1265 * EXACT_NAME: combination of all the above
1267 boolean called;
1269 char *buf = nextmbuf();
1270 struct permonst *mdat = mtmp->data;
1271 const char *pm_name = mdat->mname;
1272 boolean do_hallu, do_invis, do_it, do_saddle;
1273 boolean name_at_start, has_adjectives;
1274 char *bp;
1276 if (program_state.gameover)
1277 suppress |= SUPPRESS_HALLUCINATION;
1278 if (article == ARTICLE_YOUR && !mtmp->mtame)
1279 article = ARTICLE_THE;
1281 do_hallu = Hallucination && !(suppress & SUPPRESS_HALLUCINATION);
1282 do_invis = mtmp->minvis && !(suppress & SUPPRESS_INVISIBLE);
1283 do_it = !canspotmon(mtmp) && article != ARTICLE_YOUR
1284 && !program_state.gameover && mtmp != u.usteed
1285 && !(u.uswallow && mtmp == u.ustuck) && !(suppress & SUPPRESS_IT);
1286 do_saddle = !(suppress & SUPPRESS_SADDLE);
1288 buf[0] = '\0';
1290 /* unseen monsters, etc. Use "it" */
1291 if (do_it) {
1292 Strcpy(buf, "it");
1293 return buf;
1296 /* priests and minions: don't even use this function */
1297 if (mtmp->ispriest || mtmp->isminion) {
1298 char priestnambuf[BUFSZ];
1299 char *name;
1300 long save_prop = EHalluc_resistance;
1301 unsigned save_invis = mtmp->minvis;
1303 /* when true name is wanted, explicitly block Hallucination */
1304 if (!do_hallu)
1305 EHalluc_resistance = 1L;
1306 if (!do_invis)
1307 mtmp->minvis = 0;
1308 name = priestname(mtmp, priestnambuf);
1309 EHalluc_resistance = save_prop;
1310 mtmp->minvis = save_invis;
1311 if (article == ARTICLE_NONE && !strncmp(name, "the ", 4))
1312 name += 4;
1313 return strcpy(buf, name);
1315 /* an "aligned priest" not flagged as a priest or minion should be
1316 "priest" or "priestess" (normally handled by priestname()) */
1317 if (mdat == &mons[PM_ALIGNED_PRIEST])
1318 pm_name = mtmp->female ? "priestess" : "priest";
1319 else if (mdat == &mons[PM_HIGH_PRIEST] && mtmp->female)
1320 pm_name = "high priestess";
1322 /* Shopkeepers: use shopkeeper name. For normal shopkeepers, just
1323 * "Asidonhopo"; for unusual ones, "Asidonhopo the invisible
1324 * shopkeeper" or "Asidonhopo the blue dragon". If hallucinating,
1325 * none of this applies.
1327 if (mtmp->isshk && !do_hallu) {
1328 if (adjective && article == ARTICLE_THE) {
1329 /* pathological case: "the angry Asidonhopo the blue dragon"
1330 sounds silly */
1331 Strcpy(buf, "the ");
1332 Strcat(strcat(buf, adjective), " ");
1333 Strcat(buf, shkname(mtmp));
1334 return buf;
1336 Strcat(buf, shkname(mtmp));
1337 if (mdat == &mons[PM_SHOPKEEPER] && !do_invis)
1338 return buf;
1339 Strcat(buf, " the ");
1340 if (do_invis)
1341 Strcat(buf, "invisible ");
1342 Strcat(buf, pm_name);
1343 return buf;
1346 /* Put the adjectives in the buffer */
1347 if (adjective)
1348 Strcat(strcat(buf, adjective), " ");
1349 if (do_invis)
1350 Strcat(buf, "invisible ");
1351 if (do_saddle && (mtmp->misc_worn_check & W_SADDLE) && !Blind
1352 && !Hallucination)
1353 Strcat(buf, "saddled ");
1354 if (buf[0] != 0)
1355 has_adjectives = TRUE;
1356 else
1357 has_adjectives = FALSE;
1359 /* Put the actual monster name or type into the buffer now */
1360 /* Be sure to remember whether the buffer starts with a name */
1361 if (do_hallu) {
1362 char rnamecode;
1363 char *rname = rndmonnam(&rnamecode);
1365 Strcat(buf, rname);
1366 name_at_start = bogon_is_pname(rnamecode);
1367 } else if (has_mname(mtmp)) {
1368 char *name = MNAME(mtmp);
1370 if (mdat == &mons[PM_GHOST]) {
1371 Sprintf(eos(buf), "%s ghost", s_suffix(name));
1372 name_at_start = TRUE;
1373 } else if (called) {
1374 Sprintf(eos(buf), "%s called %s", pm_name, name);
1375 name_at_start = (boolean) type_is_pname(mdat);
1376 } else if (is_mplayer(mdat) && (bp = strstri(name, " the ")) != 0) {
1377 /* <name> the <adjective> <invisible> <saddled> <rank> */
1378 char pbuf[BUFSZ];
1380 Strcpy(pbuf, name);
1381 pbuf[bp - name + 5] = '\0'; /* adjectives right after " the " */
1382 if (has_adjectives)
1383 Strcat(pbuf, buf);
1384 Strcat(pbuf, bp + 5); /* append the rest of the name */
1385 Strcpy(buf, pbuf);
1386 article = ARTICLE_NONE;
1387 name_at_start = TRUE;
1388 } else {
1389 Strcat(buf, name);
1390 name_at_start = TRUE;
1392 } else if (is_mplayer(mdat) && !In_endgame(&u.uz)) {
1393 char pbuf[BUFSZ];
1395 Strcpy(pbuf, rank_of((int) mtmp->m_lev, monsndx(mdat),
1396 (boolean) mtmp->female));
1397 Strcat(buf, lcase(pbuf));
1398 name_at_start = FALSE;
1399 } else {
1400 Strcat(buf, pm_name);
1401 name_at_start = (boolean) type_is_pname(mdat);
1404 if (name_at_start && (article == ARTICLE_YOUR || !has_adjectives)) {
1405 if (mdat == &mons[PM_WIZARD_OF_YENDOR])
1406 article = ARTICLE_THE;
1407 else
1408 article = ARTICLE_NONE;
1409 } else if ((mdat->geno & G_UNIQ) && article == ARTICLE_A) {
1410 article = ARTICLE_THE;
1414 char buf2[BUFSZ];
1416 switch (article) {
1417 case ARTICLE_YOUR:
1418 Strcpy(buf2, "your ");
1419 Strcat(buf2, buf);
1420 Strcpy(buf, buf2);
1421 return buf;
1422 case ARTICLE_THE:
1423 Strcpy(buf2, "the ");
1424 Strcat(buf2, buf);
1425 Strcpy(buf, buf2);
1426 return buf;
1427 case ARTICLE_A:
1428 return an(buf);
1429 case ARTICLE_NONE:
1430 default:
1431 return buf;
1436 char *
1437 l_monnam(mtmp)
1438 struct monst *mtmp;
1440 return x_monnam(mtmp, ARTICLE_NONE, (char *) 0,
1441 (has_mname(mtmp)) ? SUPPRESS_SADDLE : 0, TRUE);
1444 char *
1445 mon_nam(mtmp)
1446 struct monst *mtmp;
1448 return x_monnam(mtmp, ARTICLE_THE, (char *) 0,
1449 (has_mname(mtmp)) ? SUPPRESS_SADDLE : 0, FALSE);
1452 /* print the name as if mon_nam() was called, but assume that the player
1453 * can always see the monster--used for probing and for monsters aggravating
1454 * the player with a cursed potion of invisibility
1456 char *
1457 noit_mon_nam(mtmp)
1458 struct monst *mtmp;
1460 return x_monnam(mtmp, ARTICLE_THE, (char *) 0,
1461 (has_mname(mtmp)) ? (SUPPRESS_SADDLE | SUPPRESS_IT)
1462 : SUPPRESS_IT,
1463 FALSE);
1466 char *
1467 Monnam(mtmp)
1468 struct monst *mtmp;
1470 register char *bp = mon_nam(mtmp);
1472 *bp = highc(*bp);
1473 return bp;
1476 char *
1477 noit_Monnam(mtmp)
1478 struct monst *mtmp;
1480 register char *bp = noit_mon_nam(mtmp);
1482 *bp = highc(*bp);
1483 return bp;
1486 /* monster's own name */
1487 char *
1488 m_monnam(mtmp)
1489 struct monst *mtmp;
1491 return x_monnam(mtmp, ARTICLE_NONE, (char *) 0, EXACT_NAME, FALSE);
1494 /* pet name: "your little dog" */
1495 char *
1496 y_monnam(mtmp)
1497 struct monst *mtmp;
1499 int prefix, suppression_flag;
1501 prefix = mtmp->mtame ? ARTICLE_YOUR : ARTICLE_THE;
1502 suppression_flag = (has_mname(mtmp)
1503 /* "saddled" is redundant when mounted */
1504 || mtmp == u.usteed)
1505 ? SUPPRESS_SADDLE
1506 : 0;
1508 return x_monnam(mtmp, prefix, (char *) 0, suppression_flag, FALSE);
1511 char *
1512 Adjmonnam(mtmp, adj)
1513 struct monst *mtmp;
1514 const char *adj;
1516 char *bp = x_monnam(mtmp, ARTICLE_THE, adj,
1517 has_mname(mtmp) ? SUPPRESS_SADDLE : 0, FALSE);
1519 *bp = highc(*bp);
1520 return bp;
1523 char *
1524 a_monnam(mtmp)
1525 struct monst *mtmp;
1527 return x_monnam(mtmp, ARTICLE_A, (char *) 0,
1528 has_mname(mtmp) ? SUPPRESS_SADDLE : 0, FALSE);
1531 char *
1532 Amonnam(mtmp)
1533 struct monst *mtmp;
1535 char *bp = a_monnam(mtmp);
1537 *bp = highc(*bp);
1538 return bp;
1541 /* used for monster ID by the '/', ';', and 'C' commands to block remote
1542 identification of the endgame altars via their attending priests */
1543 char *
1544 distant_monnam(mon, article, outbuf)
1545 struct monst *mon;
1546 int article; /* only ARTICLE_NONE and ARTICLE_THE are handled here */
1547 char *outbuf;
1549 /* high priest(ess)'s identity is concealed on the Astral Plane,
1550 unless you're adjacent (overridden for hallucination which does
1551 its own obfuscation) */
1552 if (mon->data == &mons[PM_HIGH_PRIEST] && !Hallucination
1553 && Is_astralevel(&u.uz) && distu(mon->mx, mon->my) > 2) {
1554 Strcpy(outbuf, article == ARTICLE_THE ? "the " : "");
1555 Strcat(outbuf, mon->female ? "high priestess" : "high priest");
1556 } else {
1557 Strcpy(outbuf, x_monnam(mon, article, (char *) 0, 0, TRUE));
1559 return outbuf;
1562 /* fake monsters used to be in a hard-coded array, now in a data file */
1563 STATIC_OVL char *
1564 bogusmon(buf, code)
1565 char *buf, *code;
1567 char *mname = buf;
1569 get_rnd_text(BOGUSMONFILE, buf);
1570 /* strip prefix if present */
1571 if (!letter(*mname)) {
1572 if (code)
1573 *code = *mname;
1574 ++mname;
1575 } else {
1576 if (code)
1577 *code = '\0';
1579 return mname;
1582 /* return a random monster name, for hallucination */
1583 char *
1584 rndmonnam(code)
1585 char *code;
1587 static char buf[BUFSZ];
1588 char *mname;
1589 int name;
1590 #define BOGUSMONSIZE 100 /* arbitrary */
1592 if (code)
1593 *code = '\0';
1595 do {
1596 name = rn1(SPECIAL_PM + BOGUSMONSIZE - LOW_PM, LOW_PM);
1597 } while (name < SPECIAL_PM
1598 && (type_is_pname(&mons[name]) || (mons[name].geno & G_NOGEN)));
1600 if (name >= SPECIAL_PM) {
1601 mname = bogusmon(buf, code);
1602 } else {
1603 mname = strcpy(buf, mons[name].mname);
1605 return mname;
1606 #undef BOGUSMONSIZE
1609 /* check bogusmon prefix to decide whether it's a personal name */
1610 boolean
1611 bogon_is_pname(code)
1612 char code;
1614 if (!code)
1615 return FALSE;
1616 return index("-+=", code) ? TRUE : FALSE;
1619 /* name of a Rogue player */
1620 const char *
1621 roguename()
1623 char *i, *opts;
1625 if ((opts = nh_getenv("ROGUEOPTS")) != 0) {
1626 for (i = opts; *i; i++)
1627 if (!strncmp("name=", i, 5)) {
1628 char *j;
1629 if ((j = index(i + 5, ',')) != 0)
1630 *j = (char) 0;
1631 return i + 5;
1634 return rn2(3) ? (rn2(2) ? "Michael Toy" : "Kenneth Arnold")
1635 : "Glenn Wichman";
1638 static NEARDATA const char *const hcolors[] = {
1639 "ultraviolet", "infrared", "bluish-orange", "reddish-green", "dark white",
1640 "light black", "sky blue-pink", "salty", "sweet", "sour", "bitter",
1641 "striped", "spiral", "swirly", "plaid", "checkered", "argyle", "paisley",
1642 "blotchy", "guernsey-spotted", "polka-dotted", "square", "round",
1643 "triangular", "cabernet", "sangria", "fuchsia", "wisteria", "lemon-lime",
1644 "strawberry-banana", "peppermint", "romantic", "incandescent",
1645 "octarine", /* Discworld: the Colour of Magic */
1648 const char *
1649 hcolor(colorpref)
1650 const char *colorpref;
1652 return (Hallucination || !colorpref) ? hcolors[rn2(SIZE(hcolors))]
1653 : colorpref;
1656 /* return a random real color unless hallucinating */
1657 const char *
1658 rndcolor()
1660 int k = rn2(CLR_MAX);
1662 return Hallucination ? hcolor((char *) 0)
1663 : (k == NO_COLOR) ? "colorless"
1664 : c_obj_colors[k];
1667 static NEARDATA const char *const hliquids[] = {
1668 "yoghurt", "oobleck", "clotted blood", "diluted water", "purified water",
1669 "instant coffee", "tea", "herbal infusion", "liquid rainbow",
1670 "creamy foam", "mulled wine", "bouillon", "nectar", "grog", "flubber",
1671 "ketchup", "slow light", "oil", "vinaigrette", "liquid crystal", "honey",
1672 "caramel sauce", "ink", "aqueous humour", "milk substitute", "fruit juice",
1673 "glowing lava", "gastric acid", "mineral water", "cough syrup", "quicksilver",
1674 "sweet vitriol", "grey goo", "pink slime",
1677 const char *
1678 hliquid(liquidpref)
1679 const char *liquidpref;
1681 return (Hallucination || !liquidpref) ? hliquids[rn2(SIZE(hliquids))]
1682 : liquidpref;
1685 /* Aliases for road-runner nemesis
1687 static const char *const coynames[] = {
1688 "Carnivorous Vulgaris", "Road-Runnerus Digestus", "Eatibus Anythingus",
1689 "Famishus-Famishus", "Eatibus Almost Anythingus", "Eatius Birdius",
1690 "Famishius Fantasticus", "Eternalii Famishiis", "Famishus Vulgarus",
1691 "Famishius Vulgaris Ingeniusi", "Eatius-Slobbius", "Hardheadipus Oedipus",
1692 "Carnivorous Slobbius", "Hard-Headipus Ravenus", "Evereadii Eatibus",
1693 "Apetitius Giganticus", "Hungrii Flea-Bagius", "Overconfidentii Vulgaris",
1694 "Caninus Nervous Rex", "Grotesques Appetitus", "Nemesis Ridiculii",
1695 "Canis latrans"
1698 char *
1699 coyotename(mtmp, buf)
1700 struct monst *mtmp;
1701 char *buf;
1703 if (mtmp && buf) {
1704 Sprintf(buf, "%s - %s",
1705 x_monnam(mtmp, ARTICLE_NONE, (char *) 0, 0, TRUE),
1706 mtmp->mcan ? coynames[SIZE(coynames) - 1]
1707 : coynames[mtmp->m_id % (SIZE(coynames) - 1)]);
1709 return buf;
1712 /* make sure "The Colour of Magic" remains the first entry in here */
1713 static const char *const sir_Terry_novels[] = {
1714 "The Colour of Magic", "The Light Fantastic", "Equal Rites", "Mort",
1715 "Sourcery", "Wyrd Sisters", "Pyramids", "Guards! Guards!", "Eric",
1716 "Moving Pictures", "Reaper Man", "Witches Abroad", "Small Gods",
1717 "Lords and Ladies", "Men at Arms", "Soul Music", "Interesting Times",
1718 "Maskerade", "Feet of Clay", "Hogfather", "Jingo", "The Last Continent",
1719 "Carpe Jugulum", "The Fifth Elephant", "The Truth", "Thief of Time",
1720 "The Last Hero", "The Amazing Maurice and his Educated Rodents",
1721 "Night Watch", "The Wee Free Men", "Monstrous Regiment",
1722 "A Hat Full of Sky", "Going Postal", "Thud!", "Wintersmith",
1723 "Making Money", "Unseen Academicals", "I Shall Wear Midnight", "Snuff",
1724 "Raising Steam", "The Shepherd's Crown"
1727 const char *
1728 noveltitle(novidx)
1729 int *novidx;
1731 int j, k = SIZE(sir_Terry_novels);
1733 j = rn2(k);
1734 if (novidx) {
1735 if (*novidx == -1)
1736 *novidx = j;
1737 else if (*novidx >= 0 && *novidx < k)
1738 j = *novidx;
1740 return sir_Terry_novels[j];
1743 const char *
1744 lookup_novel(lookname, idx)
1745 const char *lookname;
1746 int *idx;
1748 int k;
1750 /* Take American or U.K. spelling of this one */
1751 if (!strcmpi(The(lookname), "The Color of Magic"))
1752 lookname = sir_Terry_novels[0];
1754 for (k = 0; k < SIZE(sir_Terry_novels); ++k) {
1755 if (!strcmpi(lookname, sir_Terry_novels[k])
1756 || !strcmpi(The(lookname), sir_Terry_novels[k])) {
1757 if (idx)
1758 *idx = k;
1759 return sir_Terry_novels[k];
1762 /* name not found; if novelidx is already set, override the name */
1763 if (idx && *idx >= 0 && *idx < SIZE(sir_Terry_novels))
1764 return sir_Terry_novels[*idx];
1766 return (const char *) 0;
1769 /*do_name.c*/