fix #H5188 - getting an artifact break illiterate
[aNetHack.git] / src / do_name.c
blob98293152e469047238c794850a5ab42f364fd56d
1 /* NetHack 3.6 do_name.c $NHDT-Date: 1489494376 2017/03/14 12:26:16 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.116 $ */
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 const char *const gloc_descr[NUM_GLOCS][4] = {
48 { "any monsters", "monster", "next monster", "monsters" },
49 { "any items", "item", "next object", "objects" },
50 { "any doors", "door", "next door or doorway", "doors or doorways" },
51 { "any unexplored areas", "unexplored area", "unexplored location",
52 "unexplored locations" },
53 { "anything interesting", "interesting thing", "anything interesting",
54 "anything interesting" }
58 void
59 getpos_help_keyxhelp(tmpwin, k1, k2, gloc)
60 winid tmpwin;
61 const char *k1;
62 const char *k2;
63 int gloc;
65 char sbuf[BUFSZ];
67 Sprintf(sbuf, "Use '%s' or '%s' to %s%s%s.",
68 k1, k2,
69 iflags.getloc_usemenu ? "get a menu of "
70 : "move the cursor to ",
71 gloc_descr[gloc][2 + iflags.getloc_usemenu],
72 iflags.getloc_limitview ? " in view" : "");
73 putstr(tmpwin, 0, sbuf);
76 /* the response for '?' help request in getpos() */
77 STATIC_OVL void
78 getpos_help(force, goal)
79 boolean force;
80 const char *goal;
82 char sbuf[BUFSZ];
83 boolean doing_what_is;
84 winid tmpwin = create_nhwindow(NHW_MENU);
86 Sprintf(sbuf,
87 "Use '%c', '%c', '%c', '%c' to move the cursor to %s.", /* hjkl */
88 Cmd.move_W, Cmd.move_S, Cmd.move_N, Cmd.move_E, goal);
89 putstr(tmpwin, 0, sbuf);
90 putstr(tmpwin, 0,
91 "Use 'H', 'J', 'K', 'L' to move the cursor 8 units at a time.");
92 putstr(tmpwin, 0, "Or enter a background symbol (ex. '<').");
93 Sprintf(sbuf, "Use '%s' to move the cursor on yourself.",
94 visctrl(Cmd.spkeys[NHKF_GETPOS_SELF]));
95 putstr(tmpwin, 0, sbuf);
96 if (!iflags.terrainmode || (iflags.terrainmode & TER_MON) != 0) {
97 getpos_help_keyxhelp(tmpwin,
98 visctrl(Cmd.spkeys[NHKF_GETPOS_MON_NEXT]),
99 visctrl(Cmd.spkeys[NHKF_GETPOS_MON_PREV]),
100 GLOC_MONS);
102 if (!iflags.terrainmode || (iflags.terrainmode & TER_OBJ) != 0) {
103 getpos_help_keyxhelp(tmpwin,
104 visctrl(Cmd.spkeys[NHKF_GETPOS_OBJ_NEXT]),
105 visctrl(Cmd.spkeys[NHKF_GETPOS_OBJ_PREV]),
106 GLOC_OBJS);
108 if (!iflags.terrainmode || (iflags.terrainmode & TER_MAP) != 0) {
109 /* these are primarily useful when choosing a travel
110 destination for the '_' command */
111 getpos_help_keyxhelp(tmpwin,
112 visctrl(Cmd.spkeys[NHKF_GETPOS_DOOR_NEXT]),
113 visctrl(Cmd.spkeys[NHKF_GETPOS_DOOR_PREV]),
114 GLOC_DOOR);
115 getpos_help_keyxhelp(tmpwin,
116 visctrl(Cmd.spkeys[NHKF_GETPOS_UNEX_NEXT]),
117 visctrl(Cmd.spkeys[NHKF_GETPOS_UNEX_PREV]),
118 GLOC_EXPLORE);
119 getpos_help_keyxhelp(tmpwin,
120 visctrl(Cmd.spkeys[NHKF_GETPOS_INTERESTING_NEXT]),
121 visctrl(Cmd.spkeys[NHKF_GETPOS_INTERESTING_PREV]),
122 GLOC_INTERESTING);
124 Sprintf(sbuf, "Use '%s' to toggle menu listing for possible targets.",
125 visctrl(Cmd.spkeys[NHKF_GETPOS_MENU]));
126 putstr(tmpwin, 0, sbuf);
127 Sprintf(sbuf,
128 "Use '%s' to toggle limiting possible targets to in view only.",
129 visctrl(Cmd.spkeys[NHKF_GETPOS_LIMITVIEW]));
130 putstr(tmpwin, 0, sbuf);
131 if (!iflags.terrainmode) {
132 char kbuf[BUFSZ];
133 if (getpos_hilitefunc) {
134 Sprintf(sbuf, "Use '%s' to display valid locations.",
135 visctrl(Cmd.spkeys[NHKF_GETPOS_SHOWVALID]));
136 putstr(tmpwin, 0, sbuf);
138 Sprintf(sbuf, "Use '%s' to toggle automatic description.",
139 visctrl(Cmd.spkeys[NHKF_GETPOS_AUTODESC]));
140 putstr(tmpwin, 0, sbuf);
141 if (iflags.cmdassist) { /* assisting the '/' command, I suppose... */
142 Sprintf(sbuf,
143 (iflags.getpos_coords == GPCOORDS_NONE)
144 ? "(Set 'whatis_coord' option to include coordinates with '%s' text.)"
145 : "(Reset 'whatis_coord' option to omit coordinates from '%s' text.)",
146 visctrl(Cmd.spkeys[NHKF_GETPOS_AUTODESC]));
148 /* disgusting hack; the alternate selection characters work for any
149 getpos call, but only matter for dowhatis (and doquickwhatis) */
150 doing_what_is = (goal == what_is_an_unknown_object);
151 if (doing_what_is) {
152 Sprintf(kbuf, "'%s' or '%s' or '%s' or '%s'",
153 visctrl(Cmd.spkeys[NHKF_GETPOS_PICK]),
154 visctrl(Cmd.spkeys[NHKF_GETPOS_PICK_Q]),
155 visctrl(Cmd.spkeys[NHKF_GETPOS_PICK_O]),
156 visctrl(Cmd.spkeys[NHKF_GETPOS_PICK_V]));
157 } else {
158 Sprintf(kbuf, "'%s'", visctrl(Cmd.spkeys[NHKF_GETPOS_PICK]));
160 Sprintf(sbuf, "Type a %s when you are at the right place.", kbuf);
161 putstr(tmpwin, 0, sbuf);
162 if (doing_what_is) {
163 Sprintf(sbuf,
164 " '%s' describe current spot, show 'more info', move to another spot.",
165 visctrl(Cmd.spkeys[NHKF_GETPOS_PICK_V]));
166 putstr(tmpwin, 0, sbuf);
167 Sprintf(sbuf,
168 " '%s' describe current spot,%s move to another spot;",
169 visctrl(Cmd.spkeys[NHKF_GETPOS_PICK]),
170 flags.help ? " prompt if 'more info'," : "");
171 putstr(tmpwin, 0, sbuf);
172 Sprintf(sbuf,
173 " '%s' describe current spot, move to another spot;",
174 visctrl(Cmd.spkeys[NHKF_GETPOS_PICK_Q]));
175 putstr(tmpwin, 0, sbuf);
176 Sprintf(sbuf,
177 " '%s' describe current spot, stop looking at things;",
178 visctrl(Cmd.spkeys[NHKF_GETPOS_PICK_O]));
179 putstr(tmpwin, 0, sbuf);
182 if (!force)
183 putstr(tmpwin, 0, "Type Space or Escape when you're done.");
184 putstr(tmpwin, 0, "");
185 display_nhwindow(tmpwin, TRUE);
186 destroy_nhwindow(tmpwin);
189 STATIC_OVL int
190 cmp_coord_distu(a, b)
191 const void *a;
192 const void *b;
194 const coord *c1 = a;
195 const coord *c2 = b;
196 int dx, dy, dist_1, dist_2;
198 dx = u.ux - c1->x;
199 dy = u.uy - c1->y;
200 dist_1 = max(abs(dx), abs(dy));
201 dx = u.ux - c2->x;
202 dy = u.uy - c2->y;
203 dist_2 = max(abs(dx), abs(dy));
205 if (dist_1 == dist_2)
206 return (c1->y != c2->y) ? (c1->y - c2->y) : (c1->x - c2->x);
208 return dist_1 - dist_2;
211 #define IS_UNEXPLORED_LOC(x,y) \
212 (isok((x), (y)) \
213 && glyph_is_cmap(levl[(x)][(y)].glyph) \
214 && glyph_to_cmap(levl[(x)][(y)].glyph) == S_stone \
215 && !levl[(x)][(y)].seenv)
217 STATIC_OVL boolean
218 gather_locs_interesting(x,y, gloc)
219 int x,y, gloc;
221 /* TODO: if glyph is a pile glyph, convert to ordinary one
222 * in order to keep tail/boulder/rock check simple.
224 int glyph = glyph_at(x, y);
226 if (iflags.getloc_limitview && !cansee(x,y))
227 return FALSE;
229 switch (gloc) {
230 default:
231 case GLOC_MONS:
232 /* unlike '/M', this skips monsters revealed by
233 warning glyphs and remembered unseen ones */
234 return (glyph_is_monster(glyph)
235 && glyph != monnum_to_glyph(PM_LONG_WORM_TAIL));
236 case GLOC_OBJS:
237 return (glyph_is_object(glyph)
238 && glyph != objnum_to_glyph(BOULDER)
239 && glyph != objnum_to_glyph(ROCK));
240 case GLOC_DOOR:
241 return (glyph_is_cmap(glyph)
242 && (is_cmap_door(glyph_to_cmap(glyph))
243 || is_cmap_drawbridge(glyph_to_cmap(glyph))
244 || glyph_to_cmap(glyph) == S_ndoor));
245 case GLOC_EXPLORE:
246 return (glyph_is_cmap(glyph)
247 && (is_cmap_door(glyph_to_cmap(glyph))
248 || is_cmap_drawbridge(glyph_to_cmap(glyph))
249 || glyph_to_cmap(glyph) == S_ndoor
250 || glyph_to_cmap(glyph) == S_room
251 || glyph_to_cmap(glyph) == S_darkroom
252 || glyph_to_cmap(glyph) == S_corr
253 || glyph_to_cmap(glyph) == S_litcorr)
254 && (IS_UNEXPLORED_LOC(x + 1, y)
255 || IS_UNEXPLORED_LOC(x - 1, y)
256 || IS_UNEXPLORED_LOC(x, y + 1)
257 || IS_UNEXPLORED_LOC(x, y - 1)));
258 case GLOC_INTERESTING:
259 return gather_locs_interesting(x,y, GLOC_DOOR)
260 || !(glyph_is_cmap(glyph)
261 && (is_cmap_wall(glyph_to_cmap(glyph))
262 || glyph_to_cmap(glyph) == S_tree
263 || glyph_to_cmap(glyph) == S_bars
264 || glyph_to_cmap(glyph) == S_ice
265 || glyph_to_cmap(glyph) == S_air
266 || glyph_to_cmap(glyph) == S_cloud
267 || glyph_to_cmap(glyph) == S_lava
268 || glyph_to_cmap(glyph) == S_water
269 || glyph_to_cmap(glyph) == S_pool
270 || glyph_to_cmap(glyph) == S_ndoor
271 || glyph_to_cmap(glyph) == S_room
272 || glyph_to_cmap(glyph) == S_darkroom
273 || glyph_to_cmap(glyph) == S_corr
274 || glyph_to_cmap(glyph) == S_litcorr));
276 /*NOTREACHED*/
277 return FALSE;
280 /* gather locations for monsters or objects shown on the map */
281 STATIC_OVL void
282 gather_locs(arr_p, cnt_p, gloc)
283 coord **arr_p;
284 int *cnt_p;
285 int gloc;
287 int x, y, pass, idx;
290 * We always include the hero's location even if there is no monster
291 * (invisible hero without see invisible) or object (usual case)
292 * displayed there. That way, the count will always be at least 1,
293 * and player has a visual indicator (cursor returns to hero's spot)
294 * highlighting when successive 'm's or 'o's have cycled all the way
295 * through all monsters or objects.
297 * Hero's spot will always sort to array[0] because it will always
298 * be the shortest distance (namely, 0 units) away from <u.ux,u.uy>.
300 *cnt_p = idx = 0;
301 for (pass = 0; pass < 2; pass++) {
302 for (x = 1; x < COLNO; x++)
303 for (y = 0; y < ROWNO; y++) {
304 if ((x == u.ux && y == u.uy)
305 || gather_locs_interesting(x, y, gloc)) {
306 if (!pass) {
307 ++*cnt_p;
308 } else {
309 (*arr_p)[idx].x = x;
310 (*arr_p)[idx].y = y;
311 ++idx;
316 if (!pass) /* end of first pass */
317 *arr_p = (coord *) alloc(*cnt_p * sizeof (coord));
318 else /* end of second pass */
319 qsort(*arr_p, *cnt_p, sizeof (coord), cmp_coord_distu);
320 } /* pass */
323 char *
324 dxdy_to_dist_descr(dx, dy, fulldir)
325 int dx, dy;
326 boolean fulldir;
328 static char buf[30];
329 int dst;
331 if (!dx && !dy) {
332 Sprintf(buf, "here");
333 } else if ((dst = xytod(dx, dy)) != -1) {
334 /* explicit direction; 'one step' is implicit */
335 Sprintf(buf, "%s", directionname(dst));
336 } else {
337 const char *dirnames[4][2] = {
338 { "n", "north" },
339 { "s", "south" },
340 { "w", "west" },
341 { "e", "east" } };
342 buf[0] = '\0';
343 /* 9999: protect buf[] against overflow caused by invalid values */
344 if (dy) {
345 if (abs(dy) > 9999)
346 dy = sgn(dy) * 9999;
347 Sprintf(eos(buf), "%d%s%s", abs(dy), dirnames[(dy > 0)][fulldir],
348 dx ? "," : "");
350 if (dx) {
351 if (abs(dx) > 9999)
352 dx = sgn(dx) * 9999;
353 Sprintf(eos(buf), "%d%s", abs(dx), dirnames[2 + (dx > 0)][fulldir]);
356 return buf;
359 /* coordinate formatting for 'whatis_coord' option */
360 char *
361 coord_desc(x, y, outbuf, cmode)
362 int x, y;
363 char *outbuf, cmode;
365 static char screen_fmt[16]; /* [12] suffices: "[%02d,%02d]" */
366 int dx, dy;
368 outbuf[0] = '\0';
369 switch (cmode) {
370 default:
371 break;
372 case GPCOORDS_COMFULL:
373 case GPCOORDS_COMPASS:
374 /* "east", "3s", "2n,4w" */
375 dx = x - u.ux;
376 dy = y - u.uy;
377 Sprintf(outbuf, "(%s)",
378 dxdy_to_dist_descr(dx, dy, cmode == GPCOORDS_COMFULL));
379 break;
380 case GPCOORDS_MAP: /* x,y */
381 /* upper left corner of map is <1,0>;
382 with default COLNO,ROWNO lower right corner is <79,20> */
383 Sprintf(outbuf, "<%d,%d>", x, y);
384 break;
385 case GPCOORDS_SCREEN: /* y+2,x */
386 /* for normal map sizes, force a fixed-width formatting so that
387 /m, /M, /o, and /O output lines up cleanly; map sizes bigger
388 than Nx999 or 999xM will still work, but not line up like normal
389 when displayed in a column setting */
390 if (!*screen_fmt)
391 Sprintf(screen_fmt, "[%%%sd,%%%sd]",
392 (ROWNO - 1 + 2 < 100) ? "02" : "03",
393 (COLNO - 1 < 100) ? "02" : "03");
394 /* map line 0 is screen row 2;
395 map column 0 isn't used, map column 1 is screen column 1 */
396 Sprintf(outbuf, screen_fmt, y + 2, x);
397 break;
399 return outbuf;
402 STATIC_OVL void
403 auto_describe(cx, cy)
404 int cx, cy;
406 coord cc;
407 int sym = 0;
408 char tmpbuf[BUFSZ];
409 const char *firstmatch = "unknown";
411 cc.x = cx;
412 cc.y = cy;
413 if (do_screen_description(cc, TRUE, sym, tmpbuf, &firstmatch)) {
414 (void) coord_desc(cx, cy, tmpbuf, iflags.getpos_coords);
415 pline("%s%s%s%s", firstmatch, *tmpbuf ? " " : "", tmpbuf,
416 (iflags.getloc_travelmode && !is_valid_travelpt(cx,cy))
417 ? " (no travel path)" : "");
418 curs(WIN_MAP, cx, cy);
419 flush_screen(0);
423 boolean
424 getpos_menu(ccp, fovonly, gloc)
425 coord *ccp;
426 boolean fovonly;
427 int gloc;
429 coord *garr = DUMMY;
430 int gcount = 0;
431 winid tmpwin;
432 anything any;
433 int i, pick_cnt;
434 menu_item *picks = (menu_item *) 0;
435 char tmpbuf[BUFSZ];
437 gather_locs(&garr, &gcount, gloc);
439 if (gcount < 2) { /* gcount always includes the hero */
440 free((genericptr_t) garr);
441 You("cannot %s %s.",
442 fovonly ? "see" : "detect", gloc_descr[gloc][0]);
443 return FALSE;
446 tmpwin = create_nhwindow(NHW_MENU);
447 start_menu(tmpwin);
448 any = zeroany;
450 /* gather_locs returns array[0] == you. skip it. */
451 for (i = 1; i < gcount; i++) {
452 char fullbuf[BUFSZ];
453 coord tmpcc;
454 const char *firstmatch = "unknown";
455 int sym = 0;
456 any.a_int = i + 1;
457 tmpcc.x = garr[i].x;
458 tmpcc.y = garr[i].y;
459 if (do_screen_description(tmpcc, TRUE, sym, tmpbuf, &firstmatch)) {
460 (void) coord_desc(garr[i].x, garr[i].y, tmpbuf, iflags.getpos_coords);
461 Sprintf(fullbuf, "%s%s%s", firstmatch, (*tmpbuf ? " " : ""), tmpbuf);
462 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, fullbuf,
463 MENU_UNSELECTED);
467 Sprintf(tmpbuf, "Pick a target %s%s%s",
468 gloc_descr[gloc][1], fovonly ? " in view" : "",
469 iflags.getloc_travelmode ? " for travel" : "");
470 end_menu(tmpwin, tmpbuf);
471 pick_cnt = select_menu(tmpwin, PICK_ONE, &picks);
472 destroy_nhwindow(tmpwin);
473 if (pick_cnt > 0) {
474 ccp->x = garr[picks->item.a_int - 1].x;
475 ccp->y = garr[picks->item.a_int - 1].y;
476 free((genericptr_t) picks);
478 free((genericptr_t) garr);
479 return (pick_cnt > 0);
483 getpos(ccp, force, goal)
484 coord *ccp;
485 boolean force;
486 const char *goal;
488 const char *cp;
489 struct {
490 int nhkf, ret;
491 } const pick_chars_def[] = {
492 { NHKF_GETPOS_PICK, LOOK_TRADITIONAL },
493 { NHKF_GETPOS_PICK_Q, LOOK_QUICK },
494 { NHKF_GETPOS_PICK_O, LOOK_ONCE },
495 { NHKF_GETPOS_PICK_V, LOOK_VERBOSE }
497 const int mMoOdDxX_def[] = {
498 NHKF_GETPOS_MON_NEXT,
499 NHKF_GETPOS_MON_PREV,
500 NHKF_GETPOS_OBJ_NEXT,
501 NHKF_GETPOS_OBJ_PREV,
502 NHKF_GETPOS_DOOR_NEXT,
503 NHKF_GETPOS_DOOR_PREV,
504 NHKF_GETPOS_UNEX_NEXT,
505 NHKF_GETPOS_UNEX_PREV,
506 NHKF_GETPOS_INTERESTING_NEXT,
507 NHKF_GETPOS_INTERESTING_PREV
509 char pick_chars[6];
510 char mMoOdDxX[11];
511 int result = 0;
512 int cx, cy, i, c;
513 int sidx, tx, ty;
514 boolean msg_given = TRUE; /* clear message window by default */
515 boolean show_goal_msg = FALSE;
516 boolean hilite_state = FALSE;
517 coord *garr[NUM_GLOCS] = DUMMY;
518 int gcount[NUM_GLOCS] = DUMMY;
519 int gidx[NUM_GLOCS] = DUMMY;
521 for (i = 0; i < SIZE(pick_chars_def); i++)
522 pick_chars[i] = Cmd.spkeys[pick_chars_def[i].nhkf];
523 pick_chars[SIZE(pick_chars_def)] = '\0';
525 for (i = 0; i < SIZE(mMoOdDxX_def); i++)
526 mMoOdDxX[i] = Cmd.spkeys[mMoOdDxX_def[i]];
527 mMoOdDxX[SIZE(mMoOdDxX_def)] = '\0';
529 if (!goal)
530 goal = "desired location";
531 if (flags.verbose) {
532 pline("(For instructions type a '%s')",
533 visctrl(Cmd.spkeys[NHKF_GETPOS_HELP]));
534 msg_given = TRUE;
536 cx = ccp->x;
537 cy = ccp->y;
538 #ifdef CLIPPING
539 cliparound(cx, cy);
540 #endif
541 curs(WIN_MAP, cx, cy);
542 flush_screen(0);
543 #ifdef MAC
544 lock_mouse_cursor(TRUE);
545 #endif
546 for (;;) {
547 if (show_goal_msg) {
548 pline("Move cursor to %s:", goal);
549 curs(WIN_MAP, cx, cy);
550 flush_screen(0);
551 show_goal_msg = FALSE;
552 } else if (iflags.autodescribe && !msg_given && !hilite_state) {
553 auto_describe(cx, cy);
556 c = nh_poskey(&tx, &ty, &sidx);
558 if (hilite_state) {
559 (*getpos_hilitefunc)(2);
560 hilite_state = FALSE;
561 curs(WIN_MAP, cx, cy);
562 flush_screen(0);
565 if (iflags.autodescribe)
566 msg_given = FALSE;
568 if (c == Cmd.spkeys[NHKF_ESC]) {
569 cx = cy = -10;
570 msg_given = TRUE; /* force clear */
571 result = -1;
572 break;
574 if (c == 0) {
575 if (!isok(tx, ty))
576 continue;
577 /* a mouse click event, just assign and return */
578 cx = tx;
579 cy = ty;
580 break;
582 if ((cp = index(pick_chars, c)) != 0) {
583 /* '.' => 0, ',' => 1, ';' => 2, ':' => 3 */
584 result = pick_chars_def[(int) (cp - pick_chars)].ret;
585 break;
587 for (i = 0; i < 8; i++) {
588 int dx, dy;
590 if (Cmd.dirchars[i] == c) {
591 /* a normal movement letter or digit */
592 dx = xdir[i];
593 dy = ydir[i];
594 } else if (Cmd.alphadirchars[i] == lowc((char) c)
595 || (Cmd.num_pad && Cmd.dirchars[i] == (c & 0177))) {
596 /* a shifted movement letter or Meta-digit */
597 dx = 8 * xdir[i];
598 dy = 8 * ydir[i];
599 } else
600 continue;
602 /* truncate at map edge; diagonal moves complicate this... */
603 if (cx + dx < 1) {
604 dy -= sgn(dy) * (1 - (cx + dx));
605 dx = 1 - cx; /* so that (cx+dx == 1) */
606 } else if (cx + dx > COLNO - 1) {
607 dy += sgn(dy) * ((COLNO - 1) - (cx + dx));
608 dx = (COLNO - 1) - cx;
610 if (cy + dy < 0) {
611 dx -= sgn(dx) * (0 - (cy + dy));
612 dy = 0 - cy; /* so that (cy+dy == 0) */
613 } else if (cy + dy > ROWNO - 1) {
614 dx += sgn(dx) * ((ROWNO - 1) - (cy + dy));
615 dy = (ROWNO - 1) - cy;
617 cx += dx;
618 cy += dy;
619 goto nxtc;
622 if (c == Cmd.spkeys[NHKF_GETPOS_HELP] || redraw_cmd(c)) {
623 if (c == Cmd.spkeys[NHKF_GETPOS_HELP])
624 getpos_help(force, goal);
625 else /* ^R */
626 docrt(); /* redraw */
627 /* update message window to reflect that we're still targetting */
628 show_goal_msg = TRUE;
629 msg_given = TRUE;
630 } else if (c == Cmd.spkeys[NHKF_GETPOS_SHOWVALID]
631 && getpos_hilitefunc) {
632 if (!hilite_state) {
633 (*getpos_hilitefunc)(0);
634 (*getpos_hilitefunc)(1);
635 hilite_state = TRUE;
637 goto nxtc;
638 } else if (c == Cmd.spkeys[NHKF_GETPOS_AUTODESC]) {
639 iflags.autodescribe = !iflags.autodescribe;
640 pline("Automatic description %sis %s.",
641 flags.verbose ? "of features under cursor " : "",
642 iflags.autodescribe ? "on" : "off");
643 if (!iflags.autodescribe)
644 show_goal_msg = TRUE;
645 msg_given = TRUE;
646 goto nxtc;
647 } else if (c == Cmd.spkeys[NHKF_GETPOS_LIMITVIEW]) {
648 iflags.getloc_limitview = !iflags.getloc_limitview;
649 for (i = 0; i < NUM_GLOCS; i++) {
650 if (garr[i]) {
651 free((genericptr_t) garr[i]);
652 garr[i] = NULL;
654 gidx[i] = gcount[i] = 0;
656 pline("%s possible targets to those in sight only.",
657 iflags.getloc_limitview ? "Limiting" : "Not limiting");
658 msg_given = TRUE;
659 goto nxtc;
660 } else if (c == Cmd.spkeys[NHKF_GETPOS_MENU]) {
661 iflags.getloc_usemenu = !iflags.getloc_usemenu;
662 pline("%s a menu to show possible targets.",
663 iflags.getloc_usemenu ? "Using" : "Not using");
664 msg_given = TRUE;
665 goto nxtc;
666 } else if (c == Cmd.spkeys[NHKF_GETPOS_SELF]) {
667 /* reset 'm&M', 'o&O', &c; otherwise, there's no way for player
668 to achieve that except by manually cycling through all spots */
669 for (i = 0; i < NUM_GLOCS; i++)
670 gidx[i] = 0;
671 cx = u.ux;
672 cy = u.uy;
673 goto nxtc;
674 } else if ((cp = index(mMoOdDxX, c)) != 0) { /* 'm|M', 'o|O', &c */
675 /* nearest or farthest monster or object or door or unexplored */
676 int gtmp = (int) (cp - mMoOdDxX), /* 0..7 */
677 gloc = gtmp >> 1; /* 0..3 */
679 if (iflags.getloc_usemenu) {
680 coord tmpcrd;
681 if (getpos_menu(&tmpcrd, iflags.getloc_limitview, gloc)) {
682 cx = tmpcrd.x;
683 cy = tmpcrd.y;
685 goto nxtc;
688 if (!garr[gloc]) {
689 gather_locs(&garr[gloc], &gcount[gloc], gloc);
690 gidx[gloc] = 0; /* garr[][0] is hero's spot */
692 if (!(gtmp & 1)) { /* c=='m' || c=='o' || c=='d' || c=='x') */
693 gidx[gloc] = (gidx[gloc] + 1) % gcount[gloc];
694 } else { /* c=='M' || c=='O' || c=='D' || c=='X') */
695 if (--gidx[gloc] < 0)
696 gidx[gloc] = gcount[gloc] - 1;
698 cx = garr[gloc][gidx[gloc]].x;
699 cy = garr[gloc][gidx[gloc]].y;
700 goto nxtc;
701 } else {
702 if (!index(quitchars, c)) {
703 char matching[MAXPCHARS];
704 int pass, lo_x, lo_y, hi_x, hi_y, k = 0;
706 (void) memset((genericptr_t) matching, 0, sizeof matching);
707 for (sidx = 1; sidx < MAXPCHARS; sidx++) { /* [0] left as 0 */
708 if (IS_DOOR(sidx) || IS_WALL(sidx)
709 || sidx == SDOOR || sidx == SCORR
710 || glyph_to_cmap(k) == S_room
711 || glyph_to_cmap(k) == S_darkroom
712 || glyph_to_cmap(k) == S_corr
713 || glyph_to_cmap(k) == S_litcorr)
714 continue;
715 if (c == defsyms[sidx].sym || c == (int) showsyms[sidx])
716 matching[sidx] = (char) ++k;
718 if (k) {
719 for (pass = 0; pass <= 1; pass++) {
720 /* pass 0: just past current pos to lower right;
721 pass 1: upper left corner to current pos */
722 lo_y = (pass == 0) ? cy : 0;
723 hi_y = (pass == 0) ? ROWNO - 1 : cy;
724 for (ty = lo_y; ty <= hi_y; ty++) {
725 lo_x = (pass == 0 && ty == lo_y) ? cx + 1 : 1;
726 hi_x = (pass == 1 && ty == hi_y) ? cx : COLNO - 1;
727 for (tx = lo_x; tx <= hi_x; tx++) {
728 /* first, look at what is currently visible
729 (might be monster) */
730 k = glyph_at(tx, ty);
731 if (glyph_is_cmap(k)
732 && matching[glyph_to_cmap(k)])
733 goto foundc;
734 /* next, try glyph that's remembered here
735 (might be trap or object) */
736 if (level.flags.hero_memory
737 /* !terrainmode: don't move to remembered
738 trap or object if not currently shown */
739 && !iflags.terrainmode) {
740 k = levl[tx][ty].glyph;
741 if (glyph_is_cmap(k)
742 && matching[glyph_to_cmap(k)])
743 goto foundc;
745 /* last, try actual terrain here (shouldn't
746 we be using lastseentyp[][] instead?) */
747 if (levl[tx][ty].seenv) {
748 k = back_to_glyph(tx, ty);
749 if (glyph_is_cmap(k)
750 && matching[glyph_to_cmap(k)])
751 goto foundc;
753 continue;
754 foundc:
755 cx = tx, cy = ty;
756 if (msg_given) {
757 clear_nhwindow(WIN_MESSAGE);
758 msg_given = FALSE;
760 goto nxtc;
761 } /* column */
762 } /* row */
763 } /* pass */
764 pline("Can't find dungeon feature '%c'.", c);
765 msg_given = TRUE;
766 goto nxtc;
767 } else {
768 char note[QBUFSZ];
770 if (!force)
771 Strcpy(note, "aborted");
772 else
773 Sprintf(note, "use '%c', '%c', '%c', '%c' or '%s'", /* hjkl */
774 Cmd.move_W, Cmd.move_S, Cmd.move_N,
775 Cmd.move_E,
776 visctrl(Cmd.spkeys[NHKF_GETPOS_PICK]));
777 pline("Unknown direction: '%s' (%s).", visctrl((char) c),
778 note);
779 msg_given = TRUE;
780 } /* k => matching */
781 } /* !quitchars */
782 if (force)
783 goto nxtc;
784 pline("Done.");
785 msg_given = FALSE; /* suppress clear */
786 cx = -1;
787 cy = 0;
788 result = 0; /* not -1 */
789 break;
791 nxtc:
793 #ifdef CLIPPING
794 cliparound(cx, cy);
795 #endif
796 curs(WIN_MAP, cx, cy);
797 flush_screen(0);
799 #ifdef MAC
800 lock_mouse_cursor(FALSE);
801 #endif
802 if (msg_given)
803 clear_nhwindow(WIN_MESSAGE);
804 ccp->x = cx;
805 ccp->y = cy;
806 for (i = 0; i < NUM_GLOCS; i++)
807 if (garr[i])
808 free((genericptr_t) garr[i]);
809 getpos_hilitefunc = (void FDECL((*), (int))) 0;
810 return result;
813 /* allocate space for a monster's name; removes old name if there is one */
814 void
815 new_mname(mon, lth)
816 struct monst *mon;
817 int lth; /* desired length (caller handles adding 1 for terminator) */
819 if (lth) {
820 /* allocate mextra if necessary; otherwise get rid of old name */
821 if (!mon->mextra)
822 mon->mextra = newmextra();
823 else
824 free_mname(mon); /* already has mextra, might also have name */
825 MNAME(mon) = (char *) alloc((unsigned) lth);
826 } else {
827 /* zero length: the new name is empty; get rid of the old name */
828 if (has_mname(mon))
829 free_mname(mon);
833 /* release a monster's name; retains mextra even if all fields are now null */
834 void
835 free_mname(mon)
836 struct monst *mon;
838 if (has_mname(mon)) {
839 free((genericptr_t) MNAME(mon));
840 MNAME(mon) = (char *) 0;
844 /* allocate space for an object's name; removes old name if there is one */
845 void
846 new_oname(obj, lth)
847 struct obj *obj;
848 int lth; /* desired length (caller handles adding 1 for terminator) */
850 if (lth) {
851 /* allocate oextra if necessary; otherwise get rid of old name */
852 if (!obj->oextra)
853 obj->oextra = newoextra();
854 else
855 free_oname(obj); /* already has oextra, might also have name */
856 ONAME(obj) = (char *) alloc((unsigned) lth);
857 } else {
858 /* zero length: the new name is empty; get rid of the old name */
859 if (has_oname(obj))
860 free_oname(obj);
864 /* release an object's name; retains oextra even if all fields are now null */
865 void
866 free_oname(obj)
867 struct obj *obj;
869 if (has_oname(obj)) {
870 free((genericptr_t) ONAME(obj));
871 ONAME(obj) = (char *) 0;
875 /* safe_oname() always returns a valid pointer to
876 * a string, either the pointer to an object's name
877 * if it has one, or a pointer to an empty string
878 * if it doesn't.
880 const char *
881 safe_oname(obj)
882 struct obj *obj;
884 if (has_oname(obj))
885 return ONAME(obj);
886 return "";
889 /* historical note: this returns a monster pointer because it used to
890 allocate a new bigger block of memory to hold the monster and its name */
891 struct monst *
892 christen_monst(mtmp, name)
893 struct monst *mtmp;
894 const char *name;
896 int lth;
897 char buf[PL_PSIZ];
899 /* dogname & catname are PL_PSIZ arrays; object names have same limit */
900 lth = (name && *name) ? ((int) strlen(name) + 1) : 0;
901 if (lth > PL_PSIZ) {
902 lth = PL_PSIZ;
903 name = strncpy(buf, name, PL_PSIZ - 1);
904 buf[PL_PSIZ - 1] = '\0';
906 new_mname(mtmp, lth); /* removes old name if one is present */
907 if (lth)
908 Strcpy(MNAME(mtmp), name);
909 return mtmp;
912 /* check whether user-supplied name matches or nearly matches an unnameable
913 monster's name; if so, give an alternate reject message for do_mname() */
914 STATIC_OVL boolean
915 alreadynamed(mtmp, monnambuf, usrbuf)
916 struct monst *mtmp;
917 char *monnambuf, *usrbuf;
919 char pronounbuf[10], *p;
921 if (fuzzymatch(usrbuf, monnambuf, " -_", TRUE)
922 /* catch trying to name "the Oracle" as "Oracle" */
923 || (!strncmpi(monnambuf, "the ", 4)
924 && fuzzymatch(usrbuf, monnambuf + 4, " -_", TRUE))
925 /* catch trying to name "invisible Orcus" as "Orcus" */
926 || ((p = strstri(monnambuf, "invisible ")) != 0
927 && fuzzymatch(usrbuf, p + 10, " -_", TRUE))
928 /* catch trying to name "the {priest,Angel} of Crom" as "Crom" */
929 || ((p = strstri(monnambuf, " of ")) != 0
930 && fuzzymatch(usrbuf, p + 4, " -_", TRUE))) {
931 pline("%s is already called %s.",
932 upstart(strcpy(pronounbuf, mhe(mtmp))), monnambuf);
933 return TRUE;
934 } else if (mtmp->data == &mons[PM_JUIBLEX]
935 && strstri(monnambuf, "Juiblex")
936 && !strcmpi(usrbuf, "Jubilex")) {
937 pline("%s doesn't like being called %s.", upstart(monnambuf), usrbuf);
938 return TRUE;
940 return FALSE;
943 /* allow player to assign a name to some chosen monster */
944 STATIC_OVL void
945 do_mname()
947 char buf[BUFSZ], monnambuf[BUFSZ], qbuf[QBUFSZ];
948 coord cc;
949 int cx, cy;
950 struct monst *mtmp = 0;
952 if (Hallucination) {
953 You("would never recognize it anyway.");
954 return;
956 cc.x = u.ux;
957 cc.y = u.uy;
958 if (getpos(&cc, FALSE, "the monster you want to name") < 0
959 || (cx = cc.x) < 0)
960 return;
961 cy = cc.y;
963 if (cx == u.ux && cy == u.uy) {
964 if (u.usteed && canspotmon(u.usteed)) {
965 mtmp = u.usteed;
966 } else {
967 pline("This %s creature is called %s and cannot be renamed.",
968 beautiful(), plname);
969 return;
971 } else
972 mtmp = m_at(cx, cy);
974 if (!mtmp
975 || (!sensemon(mtmp)
976 && (!(cansee(cx, cy) || see_with_infrared(mtmp))
977 || mtmp->mundetected || mtmp->m_ap_type == M_AP_FURNITURE
978 || mtmp->m_ap_type == M_AP_OBJECT
979 || (mtmp->minvis && !See_invisible)))) {
980 pline("I see no monster there.");
981 return;
983 /* special case similar to the one in lookat() */
984 Sprintf(qbuf, "What do you want to call %s?",
985 distant_monnam(mtmp, ARTICLE_THE, monnambuf));
986 getlin(qbuf, buf);
987 if (!*buf || *buf == '\033')
988 return;
989 /* strip leading and trailing spaces; unnames monster if all spaces */
990 (void) mungspaces(buf);
992 /* Unique monsters have their own specific names or titles.
993 * Shopkeepers, temple priests and other minions use alternate
994 * name formatting routines which ignore any user-supplied name.
996 * Don't say the name is being rejected if it happens to match
997 * the existing name.
999 if ((mtmp->data->geno & G_UNIQ) && !mtmp->ispriest) {
1000 if (!alreadynamed(mtmp, monnambuf, buf))
1001 pline("%s doesn't like being called names!", upstart(monnambuf));
1002 } else if (mtmp->isshk
1003 && !(Deaf || mtmp->msleeping || !mtmp->mcanmove
1004 || mtmp->data->msound <= MS_ANIMAL)) {
1005 if (!alreadynamed(mtmp, monnambuf, buf))
1006 verbalize("I'm %s, not %s.", shkname(mtmp), buf);
1007 } else if (mtmp->ispriest || mtmp->isminion || mtmp->isshk) {
1008 if (!alreadynamed(mtmp, monnambuf, buf))
1009 pline("%s will not accept the name %s.", upstart(monnambuf), buf);
1010 } else
1011 (void) christen_monst(mtmp, buf);
1014 STATIC_VAR int via_naming = 0;
1017 * This routine used to change the address of 'obj' so be unsafe if not
1018 * used with extreme care. Applying a name to an object no longer
1019 * allocates a replacement object, so that old risk is gone.
1021 STATIC_OVL
1022 void
1023 do_oname(obj)
1024 register struct obj *obj;
1026 char *bufp, buf[BUFSZ], bufcpy[BUFSZ], qbuf[QBUFSZ];
1027 const char *aname;
1028 short objtyp;
1030 /* Do this now because there's no point in even asking for a name */
1031 if (obj->otyp == SPE_NOVEL) {
1032 pline("%s already has a published name.", Ysimple_name2(obj));
1033 return;
1036 Sprintf(qbuf, "What do you want to name %s ",
1037 is_plural(obj) ? "these" : "this");
1038 (void) safe_qbuf(qbuf, qbuf, "?", obj, xname, simpleonames, "item");
1039 getlin(qbuf, buf);
1040 if (!*buf || *buf == '\033')
1041 return;
1042 /* strip leading and trailing spaces; unnames item if all spaces */
1043 (void) mungspaces(buf);
1046 * We don't violate illiteracy conduct here, although it is
1047 * arguable that we should for anything other than "X". Doing so
1048 * would make attaching player's notes to hero's inventory have an
1049 * in-game effect, which may or may not be the correct thing to do.
1051 * We do violate illiteracy in oname() if player creates Sting or
1052 * Orcrist, clearly being literate (no pun intended...).
1055 /* relax restrictions over proper capitalization for artifacts */
1056 if ((aname = artifact_name(buf, &objtyp)) != 0 && objtyp == obj->otyp)
1057 Strcpy(buf, aname);
1059 if (obj->oartifact) {
1060 pline_The("artifact seems to resist the attempt.");
1061 return;
1062 } else if (restrict_name(obj, buf) || exist_artifact(obj->otyp, buf)) {
1063 /* this used to change one letter, substituting a value
1064 of 'a' through 'y' (due to an off by one error, 'z'
1065 would never be selected) and then force that to
1066 upper case if such was the case of the input;
1067 now, the hand slip scuffs one or two letters as if
1068 the text had been trodden upon, sometimes picking
1069 punctuation instead of an arbitrary letter;
1070 unfortunately, we have to cover the possibility of
1071 it targetting spaces so failing to make any change
1072 (we know that it must eventually target a nonspace
1073 because buf[] matches a valid artifact name) */
1074 Strcpy(bufcpy, buf);
1075 /* for "the Foo of Bar", only scuff "Foo of Bar" part */
1076 bufp = !strncmpi(bufcpy, "the ", 4) ? (buf + 4) : buf;
1077 do {
1078 wipeout_text(bufp, rnd(2), (unsigned) 0);
1079 } while (!strcmp(buf, bufcpy));
1080 pline("While engraving, your %s slips.", body_part(HAND));
1081 display_nhwindow(WIN_MESSAGE, FALSE);
1082 You("engrave: \"%s\".", buf);
1084 ++via_naming; /* This ought to be an argument rather than a static... */
1085 obj = oname(obj, buf);
1086 --via_naming; /* ...but oname() is used in a lot of places, so defer. */
1089 struct obj *
1090 oname(obj, name)
1091 struct obj *obj;
1092 const char *name;
1094 int lth;
1095 char buf[PL_PSIZ];
1097 lth = *name ? (int) (strlen(name) + 1) : 0;
1098 if (lth > PL_PSIZ) {
1099 lth = PL_PSIZ;
1100 name = strncpy(buf, name, PL_PSIZ - 1);
1101 buf[PL_PSIZ - 1] = '\0';
1103 /* If named artifact exists in the game, do not create another.
1104 * Also trying to create an artifact shouldn't de-artifact
1105 * it (e.g. Excalibur from prayer). In this case the object
1106 * will retain its current name. */
1107 if (obj->oartifact || (lth && exist_artifact(obj->otyp, name)))
1108 return obj;
1110 new_oname(obj, lth); /* removes old name if one is present */
1111 if (lth)
1112 Strcpy(ONAME(obj), name);
1114 if (lth)
1115 artifact_exists(obj, name, TRUE);
1116 if (obj->oartifact) {
1117 /* can't dual-wield with artifact as secondary weapon */
1118 if (obj == uswapwep)
1119 untwoweapon();
1120 /* activate warning if you've just named your weapon "Sting" */
1121 if (obj == uwep)
1122 set_artifact_intrinsic(obj, TRUE, W_WEP);
1123 /* if obj is owned by a shop, increase your bill */
1124 if (obj->unpaid)
1125 alter_cost(obj, 0L);
1126 if (via_naming) {
1127 /* violate illiteracy conduct since successfully wrote arti-name */
1128 u.uconduct.literate++;
1131 if (carried(obj))
1132 update_inventory();
1133 return obj;
1136 static NEARDATA const char callable[] = {
1137 SCROLL_CLASS, POTION_CLASS, WAND_CLASS, RING_CLASS, AMULET_CLASS,
1138 GEM_CLASS, SPBOOK_CLASS, ARMOR_CLASS, TOOL_CLASS, 0
1141 boolean
1142 objtyp_is_callable(i)
1143 int i;
1145 return (boolean) (objects[i].oc_uname
1146 || (OBJ_DESCR(objects[i])
1147 && index(callable, objects[i].oc_class)));
1150 /* C and #name commands - player can name monster or object or type of obj */
1152 docallcmd()
1154 struct obj *obj;
1155 winid win;
1156 anything any;
1157 menu_item *pick_list = 0;
1158 char ch, allowall[2];
1159 /* if player wants a,b,c instead of i,o when looting, do that here too */
1160 boolean abc = flags.lootabc;
1162 win = create_nhwindow(NHW_MENU);
1163 start_menu(win);
1164 any = zeroany;
1165 any.a_char = 'm'; /* group accelerator 'C' */
1166 add_menu(win, NO_GLYPH, &any, abc ? 0 : any.a_char, 'C', ATR_NONE,
1167 "a monster", MENU_UNSELECTED);
1168 if (invent) {
1169 /* we use y and n as accelerators so that we can accept user's
1170 response keyed to old "name an individual object?" prompt */
1171 any.a_char = 'i'; /* group accelerator 'y' */
1172 add_menu(win, NO_GLYPH, &any, abc ? 0 : any.a_char, 'y', ATR_NONE,
1173 "a particular object in inventory", MENU_UNSELECTED);
1174 any.a_char = 'o'; /* group accelerator 'n' */
1175 add_menu(win, NO_GLYPH, &any, abc ? 0 : any.a_char, 'n', ATR_NONE,
1176 "the type of an object in inventory", MENU_UNSELECTED);
1178 any.a_char = 'f'; /* group accelerator ',' (or ':' instead?) */
1179 add_menu(win, NO_GLYPH, &any, abc ? 0 : any.a_char, ',', ATR_NONE,
1180 "the type of an object upon the floor", MENU_UNSELECTED);
1181 any.a_char = 'd'; /* group accelerator '\' */
1182 add_menu(win, NO_GLYPH, &any, abc ? 0 : any.a_char, '\\', ATR_NONE,
1183 "the type of an object on discoveries list", MENU_UNSELECTED);
1184 any.a_char = 'a'; /* group accelerator 'l' */
1185 add_menu(win, NO_GLYPH, &any, abc ? 0 : any.a_char, 'l', ATR_NONE,
1186 "record an annotation for the current level", MENU_UNSELECTED);
1187 end_menu(win, "What do you want to name?");
1188 if (select_menu(win, PICK_ONE, &pick_list) > 0) {
1189 ch = pick_list[0].item.a_char;
1190 free((genericptr_t) pick_list);
1191 } else
1192 ch = 'q';
1193 destroy_nhwindow(win);
1195 switch (ch) {
1196 default:
1197 case 'q':
1198 break;
1199 case 'm': /* name a visible monster */
1200 do_mname();
1201 break;
1202 case 'i': /* name an individual object in inventory */
1203 allowall[0] = ALL_CLASSES;
1204 allowall[1] = '\0';
1205 obj = getobj(allowall, "name");
1206 if (obj)
1207 do_oname(obj);
1208 break;
1209 case 'o': /* name a type of object in inventory */
1210 obj = getobj(callable, "call");
1211 if (obj) {
1212 /* behave as if examining it in inventory;
1213 this might set dknown if it was picked up
1214 while blind and the hero can now see */
1215 (void) xname(obj);
1217 if (!obj->dknown) {
1218 You("would never recognize another one.");
1219 #if 0
1220 } else if (!objtyp_is_callable(obj->otyp)) {
1221 You("know those as well as you ever will.");
1222 #endif
1223 } else {
1224 docall(obj);
1227 break;
1228 case 'f': /* name a type of object visible on the floor */
1229 namefloorobj();
1230 break;
1231 case 'd': /* name a type of object on the discoveries list */
1232 rename_disco();
1233 break;
1234 case 'a': /* annotate level */
1235 donamelevel();
1236 break;
1238 return 0;
1241 void
1242 docall(obj)
1243 register struct obj *obj;
1245 char buf[BUFSZ], qbuf[QBUFSZ];
1246 struct obj otemp;
1247 register char **str1;
1249 if (!obj->dknown)
1250 return; /* probably blind */
1251 otemp = *obj;
1252 otemp.quan = 1L;
1253 otemp.oextra = (struct oextra *) 0;
1255 if (objects[otemp.otyp].oc_class == POTION_CLASS && otemp.fromsink)
1256 /* kludge, meaning it's sink water */
1257 Sprintf(qbuf, "Call a stream of %s fluid:",
1258 OBJ_DESCR(objects[otemp.otyp]));
1259 else
1260 Sprintf(qbuf, "Call %s:", an(xname(&otemp)));
1261 getlin(qbuf, buf);
1262 if (!*buf || *buf == '\033')
1263 return;
1265 /* clear old name */
1266 str1 = &(objects[obj->otyp].oc_uname);
1267 if (*str1)
1268 free((genericptr_t) *str1);
1270 /* strip leading and trailing spaces; uncalls item if all spaces */
1271 (void) mungspaces(buf);
1272 if (!*buf) {
1273 if (*str1) { /* had name, so possibly remove from disco[] */
1274 /* strip name first, for the update_inventory() call
1275 from undiscover_object() */
1276 *str1 = (char *) 0;
1277 undiscover_object(obj->otyp);
1279 } else {
1280 *str1 = dupstr(buf);
1281 discover_object(obj->otyp, FALSE, TRUE); /* possibly add to disco[] */
1285 STATIC_OVL void
1286 namefloorobj()
1288 coord cc;
1289 int glyph;
1290 char buf[BUFSZ];
1291 struct obj *obj = 0;
1292 boolean fakeobj = FALSE, use_plural;
1294 cc.x = u.ux, cc.y = u.uy;
1295 /* "dot for under/over you" only makes sense when the cursor hasn't
1296 been moved off the hero's '@' yet, but there's no way to adjust
1297 the help text once getpos() has started */
1298 Sprintf(buf, "object on map (or '.' for one %s you)",
1299 (u.uundetected && hides_under(youmonst.data)) ? "over" : "under");
1300 if (getpos(&cc, FALSE, buf) < 0 || cc.x <= 0)
1301 return;
1302 if (cc.x == u.ux && cc.y == u.uy) {
1303 obj = vobj_at(u.ux, u.uy);
1304 } else {
1305 glyph = glyph_at(cc.x, cc.y);
1306 if (glyph_is_object(glyph))
1307 fakeobj = object_from_map(glyph, cc.x, cc.y, &obj);
1308 /* else 'obj' stays null */
1310 if (!obj) {
1311 /* "under you" is safe here since there's no object to hide under */
1312 pline("There doesn't seem to be any object %s.",
1313 (cc.x == u.ux && cc.y == u.uy) ? "under you" : "there");
1314 return;
1316 /* note well: 'obj' might be as instance of STRANGE_OBJECT if target
1317 is a mimic; passing that to xname (directly or via simpleonames)
1318 would yield "glorkum" so we need to handle it explicitly; it will
1319 always fail the Hallucination test and pass the !callable test,
1320 resulting in the "can't be assigned a type name" message */
1321 Strcpy(buf, (obj->otyp != STRANGE_OBJECT)
1322 ? simpleonames(obj)
1323 : obj_descr[STRANGE_OBJECT].oc_name);
1324 use_plural = (obj->quan > 1L);
1325 if (Hallucination) {
1326 const char *unames[6];
1327 char tmpbuf[BUFSZ];
1329 /* straight role name */
1330 unames[0] = ((Upolyd ? u.mfemale : flags.female) && urole.name.f)
1331 ? urole.name.f
1332 : urole.name.m;
1333 /* random rank title for hero's role */
1334 unames[1] = rank_of(rnd(30), Role_switch, flags.female);
1335 /* random fake monster */
1336 unames[2] = bogusmon(tmpbuf, (char *) 0);
1337 /* increased chance for fake monster */
1338 unames[3] = unames[2];
1339 /* traditional */
1340 unames[4] = roguename();
1341 /* silly */
1342 unames[5] = "Wibbly Wobbly";
1343 pline("%s %s to call you \"%s.\"",
1344 The(buf), use_plural ? "decide" : "decides",
1345 unames[rn2(SIZE(unames))]);
1346 } else if (!objtyp_is_callable(obj->otyp)) {
1347 pline("%s %s can't be assigned a type name.",
1348 use_plural ? "Those" : "That", buf);
1349 } else if (!obj->dknown) {
1350 You("don't know %s %s well enough to name %s.",
1351 use_plural ? "those" : "that", buf, use_plural ? "them" : "it");
1352 } else {
1353 docall(obj);
1355 if (fakeobj)
1356 dealloc_obj(obj);
1359 static const char *const ghostnames[] = {
1360 /* these names should have length < PL_NSIZ */
1361 /* Capitalize the names for aesthetics -dgk */
1362 "Adri", "Andries", "Andreas", "Bert", "David", "Dirk",
1363 "Emile", "Frans", "Fred", "Greg", "Hether", "Jay",
1364 "John", "Jon", "Karnov", "Kay", "Kenny", "Kevin",
1365 "Maud", "Michiel", "Mike", "Peter", "Robert", "Ron",
1366 "Tom", "Wilmar", "Nick Danger", "Phoenix", "Jiro", "Mizue",
1367 "Stephan", "Lance Braccus", "Shadowhawk"
1370 /* ghost names formerly set by x_monnam(), now by makemon() instead */
1371 const char *
1372 rndghostname()
1374 return rn2(7) ? ghostnames[rn2(SIZE(ghostnames))] : (const char *) plname;
1378 * Monster naming functions:
1379 * x_monnam is the generic monster-naming function.
1380 * seen unseen detected named
1381 * mon_nam: the newt it the invisible orc Fido
1382 * noit_mon_nam:the newt (as if detected) the invisible orc Fido
1383 * l_monnam: newt it invisible orc dog called Fido
1384 * Monnam: The newt It The invisible orc Fido
1385 * noit_Monnam: The newt (as if detected) The invisible orc Fido
1386 * Adjmonnam: The poor newt It The poor invisible orc The poor Fido
1387 * Amonnam: A newt It An invisible orc Fido
1388 * a_monnam: a newt it an invisible orc Fido
1389 * m_monnam: newt xan orc Fido
1390 * y_monnam: your newt your xan your invisible orc Fido
1393 /* Bug: if the monster is a priest or shopkeeper, not every one of these
1394 * options works, since those are special cases.
1396 char *
1397 x_monnam(mtmp, article, adjective, suppress, called)
1398 register struct monst *mtmp;
1399 int article;
1400 /* ARTICLE_NONE, ARTICLE_THE, ARTICLE_A: obvious
1401 * ARTICLE_YOUR: "your" on pets, "the" on everything else
1403 * If the monster would be referred to as "it" or if the monster has a name
1404 * _and_ there is no adjective, "invisible", "saddled", etc., override this
1405 * and always use no article.
1407 const char *adjective;
1408 int suppress;
1409 /* SUPPRESS_IT, SUPPRESS_INVISIBLE, SUPPRESS_HALLUCINATION, SUPPRESS_SADDLE.
1410 * EXACT_NAME: combination of all the above
1412 boolean called;
1414 char *buf = nextmbuf();
1415 struct permonst *mdat = mtmp->data;
1416 const char *pm_name = mdat->mname;
1417 boolean do_hallu, do_invis, do_it, do_saddle;
1418 boolean name_at_start, has_adjectives;
1419 char *bp;
1421 if (program_state.gameover)
1422 suppress |= SUPPRESS_HALLUCINATION;
1423 if (article == ARTICLE_YOUR && !mtmp->mtame)
1424 article = ARTICLE_THE;
1426 do_hallu = Hallucination && !(suppress & SUPPRESS_HALLUCINATION);
1427 do_invis = mtmp->minvis && !(suppress & SUPPRESS_INVISIBLE);
1428 do_it = !canspotmon(mtmp) && article != ARTICLE_YOUR
1429 && !program_state.gameover && mtmp != u.usteed
1430 && !(u.uswallow && mtmp == u.ustuck) && !(suppress & SUPPRESS_IT);
1431 do_saddle = !(suppress & SUPPRESS_SADDLE);
1433 buf[0] = '\0';
1435 /* unseen monsters, etc. Use "it" */
1436 if (do_it) {
1437 Strcpy(buf, "it");
1438 return buf;
1441 /* priests and minions: don't even use this function */
1442 if (mtmp->ispriest || mtmp->isminion) {
1443 char priestnambuf[BUFSZ];
1444 char *name;
1445 long save_prop = EHalluc_resistance;
1446 unsigned save_invis = mtmp->minvis;
1448 /* when true name is wanted, explicitly block Hallucination */
1449 if (!do_hallu)
1450 EHalluc_resistance = 1L;
1451 if (!do_invis)
1452 mtmp->minvis = 0;
1453 name = priestname(mtmp, priestnambuf);
1454 EHalluc_resistance = save_prop;
1455 mtmp->minvis = save_invis;
1456 if (article == ARTICLE_NONE && !strncmp(name, "the ", 4))
1457 name += 4;
1458 return strcpy(buf, name);
1460 /* an "aligned priest" not flagged as a priest or minion should be
1461 "priest" or "priestess" (normally handled by priestname()) */
1462 if (mdat == &mons[PM_ALIGNED_PRIEST])
1463 pm_name = mtmp->female ? "priestess" : "priest";
1464 else if (mdat == &mons[PM_HIGH_PRIEST] && mtmp->female)
1465 pm_name = "high priestess";
1467 /* Shopkeepers: use shopkeeper name. For normal shopkeepers, just
1468 * "Asidonhopo"; for unusual ones, "Asidonhopo the invisible
1469 * shopkeeper" or "Asidonhopo the blue dragon". If hallucinating,
1470 * none of this applies.
1472 if (mtmp->isshk && !do_hallu) {
1473 if (adjective && article == ARTICLE_THE) {
1474 /* pathological case: "the angry Asidonhopo the blue dragon"
1475 sounds silly */
1476 Strcpy(buf, "the ");
1477 Strcat(strcat(buf, adjective), " ");
1478 Strcat(buf, shkname(mtmp));
1479 return buf;
1481 Strcat(buf, shkname(mtmp));
1482 if (mdat == &mons[PM_SHOPKEEPER] && !do_invis)
1483 return buf;
1484 Strcat(buf, " the ");
1485 if (do_invis)
1486 Strcat(buf, "invisible ");
1487 Strcat(buf, pm_name);
1488 return buf;
1491 /* Put the adjectives in the buffer */
1492 if (adjective)
1493 Strcat(strcat(buf, adjective), " ");
1494 if (do_invis)
1495 Strcat(buf, "invisible ");
1496 if (do_saddle && (mtmp->misc_worn_check & W_SADDLE) && !Blind
1497 && !Hallucination)
1498 Strcat(buf, "saddled ");
1499 if (buf[0] != 0)
1500 has_adjectives = TRUE;
1501 else
1502 has_adjectives = FALSE;
1504 /* Put the actual monster name or type into the buffer now */
1505 /* Be sure to remember whether the buffer starts with a name */
1506 if (do_hallu) {
1507 char rnamecode;
1508 char *rname = rndmonnam(&rnamecode);
1510 Strcat(buf, rname);
1511 name_at_start = bogon_is_pname(rnamecode);
1512 } else if (has_mname(mtmp)) {
1513 char *name = MNAME(mtmp);
1515 if (mdat == &mons[PM_GHOST]) {
1516 Sprintf(eos(buf), "%s ghost", s_suffix(name));
1517 name_at_start = TRUE;
1518 } else if (called) {
1519 Sprintf(eos(buf), "%s called %s", pm_name, name);
1520 name_at_start = (boolean) type_is_pname(mdat);
1521 } else if (is_mplayer(mdat) && (bp = strstri(name, " the ")) != 0) {
1522 /* <name> the <adjective> <invisible> <saddled> <rank> */
1523 char pbuf[BUFSZ];
1525 Strcpy(pbuf, name);
1526 pbuf[bp - name + 5] = '\0'; /* adjectives right after " the " */
1527 if (has_adjectives)
1528 Strcat(pbuf, buf);
1529 Strcat(pbuf, bp + 5); /* append the rest of the name */
1530 Strcpy(buf, pbuf);
1531 article = ARTICLE_NONE;
1532 name_at_start = TRUE;
1533 } else {
1534 Strcat(buf, name);
1535 name_at_start = TRUE;
1537 } else if (is_mplayer(mdat) && !In_endgame(&u.uz)) {
1538 char pbuf[BUFSZ];
1540 Strcpy(pbuf, rank_of((int) mtmp->m_lev, monsndx(mdat),
1541 (boolean) mtmp->female));
1542 Strcat(buf, lcase(pbuf));
1543 name_at_start = FALSE;
1544 } else {
1545 Strcat(buf, pm_name);
1546 name_at_start = (boolean) type_is_pname(mdat);
1549 if (name_at_start && (article == ARTICLE_YOUR || !has_adjectives)) {
1550 if (mdat == &mons[PM_WIZARD_OF_YENDOR])
1551 article = ARTICLE_THE;
1552 else
1553 article = ARTICLE_NONE;
1554 } else if ((mdat->geno & G_UNIQ) && article == ARTICLE_A) {
1555 article = ARTICLE_THE;
1559 char buf2[BUFSZ];
1561 switch (article) {
1562 case ARTICLE_YOUR:
1563 Strcpy(buf2, "your ");
1564 Strcat(buf2, buf);
1565 Strcpy(buf, buf2);
1566 return buf;
1567 case ARTICLE_THE:
1568 Strcpy(buf2, "the ");
1569 Strcat(buf2, buf);
1570 Strcpy(buf, buf2);
1571 return buf;
1572 case ARTICLE_A:
1573 return an(buf);
1574 case ARTICLE_NONE:
1575 default:
1576 return buf;
1581 char *
1582 l_monnam(mtmp)
1583 struct monst *mtmp;
1585 return x_monnam(mtmp, ARTICLE_NONE, (char *) 0,
1586 (has_mname(mtmp)) ? SUPPRESS_SADDLE : 0, TRUE);
1589 char *
1590 mon_nam(mtmp)
1591 struct monst *mtmp;
1593 return x_monnam(mtmp, ARTICLE_THE, (char *) 0,
1594 (has_mname(mtmp)) ? SUPPRESS_SADDLE : 0, FALSE);
1597 /* print the name as if mon_nam() was called, but assume that the player
1598 * can always see the monster--used for probing and for monsters aggravating
1599 * the player with a cursed potion of invisibility
1601 char *
1602 noit_mon_nam(mtmp)
1603 struct monst *mtmp;
1605 return x_monnam(mtmp, ARTICLE_THE, (char *) 0,
1606 (has_mname(mtmp)) ? (SUPPRESS_SADDLE | SUPPRESS_IT)
1607 : SUPPRESS_IT,
1608 FALSE);
1611 char *
1612 Monnam(mtmp)
1613 struct monst *mtmp;
1615 register char *bp = mon_nam(mtmp);
1617 *bp = highc(*bp);
1618 return bp;
1621 char *
1622 noit_Monnam(mtmp)
1623 struct monst *mtmp;
1625 register char *bp = noit_mon_nam(mtmp);
1627 *bp = highc(*bp);
1628 return bp;
1631 /* monster's own name */
1632 char *
1633 m_monnam(mtmp)
1634 struct monst *mtmp;
1636 return x_monnam(mtmp, ARTICLE_NONE, (char *) 0, EXACT_NAME, FALSE);
1639 /* pet name: "your little dog" */
1640 char *
1641 y_monnam(mtmp)
1642 struct monst *mtmp;
1644 int prefix, suppression_flag;
1646 prefix = mtmp->mtame ? ARTICLE_YOUR : ARTICLE_THE;
1647 suppression_flag = (has_mname(mtmp)
1648 /* "saddled" is redundant when mounted */
1649 || mtmp == u.usteed)
1650 ? SUPPRESS_SADDLE
1651 : 0;
1653 return x_monnam(mtmp, prefix, (char *) 0, suppression_flag, FALSE);
1656 char *
1657 Adjmonnam(mtmp, adj)
1658 struct monst *mtmp;
1659 const char *adj;
1661 char *bp = x_monnam(mtmp, ARTICLE_THE, adj,
1662 has_mname(mtmp) ? SUPPRESS_SADDLE : 0, FALSE);
1664 *bp = highc(*bp);
1665 return bp;
1668 char *
1669 a_monnam(mtmp)
1670 struct monst *mtmp;
1672 return x_monnam(mtmp, ARTICLE_A, (char *) 0,
1673 has_mname(mtmp) ? SUPPRESS_SADDLE : 0, FALSE);
1676 char *
1677 Amonnam(mtmp)
1678 struct monst *mtmp;
1680 char *bp = a_monnam(mtmp);
1682 *bp = highc(*bp);
1683 return bp;
1686 /* used for monster ID by the '/', ';', and 'C' commands to block remote
1687 identification of the endgame altars via their attending priests */
1688 char *
1689 distant_monnam(mon, article, outbuf)
1690 struct monst *mon;
1691 int article; /* only ARTICLE_NONE and ARTICLE_THE are handled here */
1692 char *outbuf;
1694 /* high priest(ess)'s identity is concealed on the Astral Plane,
1695 unless you're adjacent (overridden for hallucination which does
1696 its own obfuscation) */
1697 if (mon->data == &mons[PM_HIGH_PRIEST] && !Hallucination
1698 && Is_astralevel(&u.uz) && distu(mon->mx, mon->my) > 2) {
1699 Strcpy(outbuf, article == ARTICLE_THE ? "the " : "");
1700 Strcat(outbuf, mon->female ? "high priestess" : "high priest");
1701 } else {
1702 Strcpy(outbuf, x_monnam(mon, article, (char *) 0, 0, TRUE));
1704 return outbuf;
1707 /* fake monsters used to be in a hard-coded array, now in a data file */
1708 STATIC_OVL char *
1709 bogusmon(buf, code)
1710 char *buf, *code;
1712 char *mname = buf;
1714 get_rnd_text(BOGUSMONFILE, buf);
1715 /* strip prefix if present */
1716 if (!letter(*mname)) {
1717 if (code)
1718 *code = *mname;
1719 ++mname;
1720 } else {
1721 if (code)
1722 *code = '\0';
1724 return mname;
1727 /* return a random monster name, for hallucination */
1728 char *
1729 rndmonnam(code)
1730 char *code;
1732 static char buf[BUFSZ];
1733 char *mname;
1734 int name;
1735 #define BOGUSMONSIZE 100 /* arbitrary */
1737 if (code)
1738 *code = '\0';
1740 do {
1741 name = rn1(SPECIAL_PM + BOGUSMONSIZE - LOW_PM, LOW_PM);
1742 } while (name < SPECIAL_PM
1743 && (type_is_pname(&mons[name]) || (mons[name].geno & G_NOGEN)));
1745 if (name >= SPECIAL_PM) {
1746 mname = bogusmon(buf, code);
1747 } else {
1748 mname = strcpy(buf, mons[name].mname);
1750 return mname;
1751 #undef BOGUSMONSIZE
1754 /* check bogusmon prefix to decide whether it's a personal name */
1755 boolean
1756 bogon_is_pname(code)
1757 char code;
1759 if (!code)
1760 return FALSE;
1761 return index("-+=", code) ? TRUE : FALSE;
1764 /* name of a Rogue player */
1765 const char *
1766 roguename()
1768 char *i, *opts;
1770 if ((opts = nh_getenv("ROGUEOPTS")) != 0) {
1771 for (i = opts; *i; i++)
1772 if (!strncmp("name=", i, 5)) {
1773 char *j;
1774 if ((j = index(i + 5, ',')) != 0)
1775 *j = (char) 0;
1776 return i + 5;
1779 return rn2(3) ? (rn2(2) ? "Michael Toy" : "Kenneth Arnold")
1780 : "Glenn Wichman";
1783 static NEARDATA const char *const hcolors[] = {
1784 "ultraviolet", "infrared", "bluish-orange", "reddish-green", "dark white",
1785 "light black", "sky blue-pink", "salty", "sweet", "sour", "bitter",
1786 "striped", "spiral", "swirly", "plaid", "checkered", "argyle", "paisley",
1787 "blotchy", "guernsey-spotted", "polka-dotted", "square", "round",
1788 "triangular", "cabernet", "sangria", "fuchsia", "wisteria", "lemon-lime",
1789 "strawberry-banana", "peppermint", "romantic", "incandescent",
1790 "octarine", /* Discworld: the Colour of Magic */
1793 const char *
1794 hcolor(colorpref)
1795 const char *colorpref;
1797 return (Hallucination || !colorpref) ? hcolors[rn2(SIZE(hcolors))]
1798 : colorpref;
1801 /* return a random real color unless hallucinating */
1802 const char *
1803 rndcolor()
1805 int k = rn2(CLR_MAX);
1807 return Hallucination ? hcolor((char *) 0)
1808 : (k == NO_COLOR) ? "colorless"
1809 : c_obj_colors[k];
1812 static NEARDATA const char *const hliquids[] = {
1813 "yoghurt", "oobleck", "clotted blood", "diluted water", "purified water",
1814 "instant coffee", "tea", "herbal infusion", "liquid rainbow",
1815 "creamy foam", "mulled wine", "bouillon", "nectar", "grog", "flubber",
1816 "ketchup", "slow light", "oil", "vinaigrette", "liquid crystal", "honey",
1817 "caramel sauce", "ink", "aqueous humour", "milk substitute", "fruit juice",
1818 "glowing lava", "gastric acid", "mineral water", "cough syrup", "quicksilver",
1819 "sweet vitriol", "grey goo", "pink slime",
1822 const char *
1823 hliquid(liquidpref)
1824 const char *liquidpref;
1826 return (Hallucination || !liquidpref) ? hliquids[rn2(SIZE(hliquids))]
1827 : liquidpref;
1830 /* Aliases for road-runner nemesis
1832 static const char *const coynames[] = {
1833 "Carnivorous Vulgaris", "Road-Runnerus Digestus", "Eatibus Anythingus",
1834 "Famishus-Famishus", "Eatibus Almost Anythingus", "Eatius Birdius",
1835 "Famishius Fantasticus", "Eternalii Famishiis", "Famishus Vulgarus",
1836 "Famishius Vulgaris Ingeniusi", "Eatius-Slobbius", "Hardheadipus Oedipus",
1837 "Carnivorous Slobbius", "Hard-Headipus Ravenus", "Evereadii Eatibus",
1838 "Apetitius Giganticus", "Hungrii Flea-Bagius", "Overconfidentii Vulgaris",
1839 "Caninus Nervous Rex", "Grotesques Appetitus", "Nemesis Ridiculii",
1840 "Canis latrans"
1843 char *
1844 coyotename(mtmp, buf)
1845 struct monst *mtmp;
1846 char *buf;
1848 if (mtmp && buf) {
1849 Sprintf(buf, "%s - %s",
1850 x_monnam(mtmp, ARTICLE_NONE, (char *) 0, 0, TRUE),
1851 mtmp->mcan ? coynames[SIZE(coynames) - 1]
1852 : coynames[mtmp->m_id % (SIZE(coynames) - 1)]);
1854 return buf;
1857 /* make sure "The Colour of Magic" remains the first entry in here */
1858 static const char *const sir_Terry_novels[] = {
1859 "The Colour of Magic", "The Light Fantastic", "Equal Rites", "Mort",
1860 "Sourcery", "Wyrd Sisters", "Pyramids", "Guards! Guards!", "Eric",
1861 "Moving Pictures", "Reaper Man", "Witches Abroad", "Small Gods",
1862 "Lords and Ladies", "Men at Arms", "Soul Music", "Interesting Times",
1863 "Maskerade", "Feet of Clay", "Hogfather", "Jingo", "The Last Continent",
1864 "Carpe Jugulum", "The Fifth Elephant", "The Truth", "Thief of Time",
1865 "The Last Hero", "The Amazing Maurice and his Educated Rodents",
1866 "Night Watch", "The Wee Free Men", "Monstrous Regiment",
1867 "A Hat Full of Sky", "Going Postal", "Thud!", "Wintersmith",
1868 "Making Money", "Unseen Academicals", "I Shall Wear Midnight", "Snuff",
1869 "Raising Steam", "The Shepherd's Crown"
1872 const char *
1873 noveltitle(novidx)
1874 int *novidx;
1876 int j, k = SIZE(sir_Terry_novels);
1878 j = rn2(k);
1879 if (novidx) {
1880 if (*novidx == -1)
1881 *novidx = j;
1882 else if (*novidx >= 0 && *novidx < k)
1883 j = *novidx;
1885 return sir_Terry_novels[j];
1888 const char *
1889 lookup_novel(lookname, idx)
1890 const char *lookname;
1891 int *idx;
1893 int k;
1895 /* Take American or U.K. spelling of this one */
1896 if (!strcmpi(The(lookname), "The Color of Magic"))
1897 lookname = sir_Terry_novels[0];
1899 for (k = 0; k < SIZE(sir_Terry_novels); ++k) {
1900 if (!strcmpi(lookname, sir_Terry_novels[k])
1901 || !strcmpi(The(lookname), sir_Terry_novels[k])) {
1902 if (idx)
1903 *idx = k;
1904 return sir_Terry_novels[k];
1907 /* name not found; if novelidx is already set, override the name */
1908 if (idx && *idx >= 0 && *idx < SIZE(sir_Terry_novels))
1909 return sir_Terry_novels[*idx];
1911 return (const char *) 0;
1914 /*do_name.c*/