miscellaneous formatting
[aNetHack.git] / src / do_name.c
blobd494942ce1fe83d4636e029c4fefb7ea43ea8e05
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 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);
1015 * This routine changes the address of obj. Be careful not to call it
1016 * when there might be pointers around in unknown places. For now: only
1017 * when obj is in the inventory.
1019 STATIC_OVL
1020 void
1021 do_oname(obj)
1022 register struct obj *obj;
1024 char *bufp, buf[BUFSZ], bufcpy[BUFSZ], qbuf[QBUFSZ];
1025 const char *aname;
1026 short objtyp;
1028 /* Do this now because there's no point in even asking for a name */
1029 if (obj->otyp == SPE_NOVEL) {
1030 pline("%s already has a published name.", Ysimple_name2(obj));
1031 return;
1034 Sprintf(qbuf, "What do you want to name %s ",
1035 is_plural(obj) ? "these" : "this");
1036 (void) safe_qbuf(qbuf, qbuf, "?", obj, xname, simpleonames, "item");
1037 getlin(qbuf, buf);
1038 if (!*buf || *buf == '\033')
1039 return;
1040 /* strip leading and trailing spaces; unnames item if all spaces */
1041 (void) mungspaces(buf);
1044 * We don't violate illiteracy conduct here, although it is
1045 * arguable that we should for anything other than "X". Doing so
1046 * would make attaching player's notes to hero's inventory have an
1047 * in-game effect, which may or may not be the correct thing to do.
1049 * We do violate illiteracy in oname() if player creates Sting or
1050 * Orcrist, clearly being literate (no pun intended...).
1053 /* relax restrictions over proper capitalization for artifacts */
1054 if ((aname = artifact_name(buf, &objtyp)) != 0 && objtyp == obj->otyp)
1055 Strcpy(buf, aname);
1057 if (obj->oartifact) {
1058 pline_The("artifact seems to resist the attempt.");
1059 return;
1060 } else if (restrict_name(obj, buf) || exist_artifact(obj->otyp, buf)) {
1061 /* this used to change one letter, substituting a value
1062 of 'a' through 'y' (due to an off by one error, 'z'
1063 would never be selected) and then force that to
1064 upper case if such was the case of the input;
1065 now, the hand slip scuffs one or two letters as if
1066 the text had been trodden upon, sometimes picking
1067 punctuation instead of an arbitrary letter;
1068 unfortunately, we have to cover the possibility of
1069 it targetting spaces so failing to make any change
1070 (we know that it must eventually target a nonspace
1071 because buf[] matches a valid artifact name) */
1072 Strcpy(bufcpy, buf);
1073 /* for "the Foo of Bar", only scuff "Foo of Bar" part */
1074 bufp = !strncmpi(bufcpy, "the ", 4) ? (buf + 4) : buf;
1075 do {
1076 wipeout_text(bufp, rnd(2), (unsigned) 0);
1077 } while (!strcmp(buf, bufcpy));
1078 pline("While engraving, your %s slips.", body_part(HAND));
1079 display_nhwindow(WIN_MESSAGE, FALSE);
1080 You("engrave: \"%s\".", buf);
1082 obj = oname(obj, buf);
1085 struct obj *
1086 oname(obj, name)
1087 struct obj *obj;
1088 const char *name;
1090 int lth;
1091 char buf[PL_PSIZ];
1093 lth = *name ? (int) (strlen(name) + 1) : 0;
1094 if (lth > PL_PSIZ) {
1095 lth = PL_PSIZ;
1096 name = strncpy(buf, name, PL_PSIZ - 1);
1097 buf[PL_PSIZ - 1] = '\0';
1099 /* If named artifact exists in the game, do not create another.
1100 * Also trying to create an artifact shouldn't de-artifact
1101 * it (e.g. Excalibur from prayer). In this case the object
1102 * will retain its current name. */
1103 if (obj->oartifact || (lth && exist_artifact(obj->otyp, name)))
1104 return obj;
1106 new_oname(obj, lth); /* removes old name if one is present */
1107 if (lth)
1108 Strcpy(ONAME(obj), name);
1110 if (lth)
1111 artifact_exists(obj, name, TRUE);
1112 if (obj->oartifact) {
1113 /* can't dual-wield with artifact as secondary weapon */
1114 if (obj == uswapwep)
1115 untwoweapon();
1116 /* activate warning if you've just named your weapon "Sting" */
1117 if (obj == uwep)
1118 set_artifact_intrinsic(obj, TRUE, W_WEP);
1119 /* if obj is owned by a shop, increase your bill */
1120 if (obj->unpaid)
1121 alter_cost(obj, 0L);
1122 /* violate illiteracy conduct since successfully wrote arti-name */
1123 u.uconduct.literate++;
1125 if (carried(obj))
1126 update_inventory();
1127 return obj;
1130 static NEARDATA const char callable[] = {
1131 SCROLL_CLASS, POTION_CLASS, WAND_CLASS, RING_CLASS, AMULET_CLASS,
1132 GEM_CLASS, SPBOOK_CLASS, ARMOR_CLASS, TOOL_CLASS, 0
1135 boolean
1136 objtyp_is_callable(i)
1137 int i;
1139 return (boolean) (objects[i].oc_uname
1140 || (OBJ_DESCR(objects[i])
1141 && index(callable, objects[i].oc_class)));
1144 /* C and #name commands - player can name monster or object or type of obj */
1146 docallcmd()
1148 struct obj *obj;
1149 winid win;
1150 anything any;
1151 menu_item *pick_list = 0;
1152 char ch, allowall[2];
1153 /* if player wants a,b,c instead of i,o when looting, do that here too */
1154 boolean abc = flags.lootabc;
1156 win = create_nhwindow(NHW_MENU);
1157 start_menu(win);
1158 any = zeroany;
1159 any.a_char = 'm'; /* group accelerator 'C' */
1160 add_menu(win, NO_GLYPH, &any, abc ? 0 : any.a_char, 'C', ATR_NONE,
1161 "a monster", MENU_UNSELECTED);
1162 if (invent) {
1163 /* we use y and n as accelerators so that we can accept user's
1164 response keyed to old "name an individual object?" prompt */
1165 any.a_char = 'i'; /* group accelerator 'y' */
1166 add_menu(win, NO_GLYPH, &any, abc ? 0 : any.a_char, 'y', ATR_NONE,
1167 "a particular object in inventory", MENU_UNSELECTED);
1168 any.a_char = 'o'; /* group accelerator 'n' */
1169 add_menu(win, NO_GLYPH, &any, abc ? 0 : any.a_char, 'n', ATR_NONE,
1170 "the type of an object in inventory", MENU_UNSELECTED);
1172 any.a_char = 'f'; /* group accelerator ',' (or ':' instead?) */
1173 add_menu(win, NO_GLYPH, &any, abc ? 0 : any.a_char, ',', ATR_NONE,
1174 "the type of an object upon the floor", MENU_UNSELECTED);
1175 any.a_char = 'd'; /* group accelerator '\' */
1176 add_menu(win, NO_GLYPH, &any, abc ? 0 : any.a_char, '\\', ATR_NONE,
1177 "the type of an object on discoveries list", MENU_UNSELECTED);
1178 any.a_char = 'a'; /* group accelerator 'l' */
1179 add_menu(win, NO_GLYPH, &any, abc ? 0 : any.a_char, 'l', ATR_NONE,
1180 "record an annotation for the current level", MENU_UNSELECTED);
1181 end_menu(win, "What do you want to name?");
1182 if (select_menu(win, PICK_ONE, &pick_list) > 0) {
1183 ch = pick_list[0].item.a_char;
1184 free((genericptr_t) pick_list);
1185 } else
1186 ch = 'q';
1187 destroy_nhwindow(win);
1189 switch (ch) {
1190 default:
1191 case 'q':
1192 break;
1193 case 'm': /* name a visible monster */
1194 do_mname();
1195 break;
1196 case 'i': /* name an individual object in inventory */
1197 allowall[0] = ALL_CLASSES;
1198 allowall[1] = '\0';
1199 obj = getobj(allowall, "name");
1200 if (obj)
1201 do_oname(obj);
1202 break;
1203 case 'o': /* name a type of object in inventory */
1204 obj = getobj(callable, "call");
1205 if (obj) {
1206 /* behave as if examining it in inventory;
1207 this might set dknown if it was picked up
1208 while blind and the hero can now see */
1209 (void) xname(obj);
1211 if (!obj->dknown) {
1212 You("would never recognize another one.");
1213 #if 0
1214 } else if (!objtyp_is_callable(obj->otyp)) {
1215 You("know those as well as you ever will.");
1216 #endif
1217 } else {
1218 docall(obj);
1221 break;
1222 case 'f': /* name a type of object visible on the floor */
1223 namefloorobj();
1224 break;
1225 case 'd': /* name a type of object on the discoveries list */
1226 rename_disco();
1227 break;
1228 case 'a': /* annotate level */
1229 donamelevel();
1230 break;
1232 return 0;
1235 void
1236 docall(obj)
1237 register struct obj *obj;
1239 char buf[BUFSZ], qbuf[QBUFSZ];
1240 struct obj otemp;
1241 register char **str1;
1243 if (!obj->dknown)
1244 return; /* probably blind */
1245 otemp = *obj;
1246 otemp.quan = 1L;
1247 otemp.oextra = (struct oextra *) 0;
1249 if (objects[otemp.otyp].oc_class == POTION_CLASS && otemp.fromsink)
1250 /* kludge, meaning it's sink water */
1251 Sprintf(qbuf, "Call a stream of %s fluid:",
1252 OBJ_DESCR(objects[otemp.otyp]));
1253 else
1254 Sprintf(qbuf, "Call %s:", an(xname(&otemp)));
1255 getlin(qbuf, buf);
1256 if (!*buf || *buf == '\033')
1257 return;
1259 /* clear old name */
1260 str1 = &(objects[obj->otyp].oc_uname);
1261 if (*str1)
1262 free((genericptr_t) *str1);
1264 /* strip leading and trailing spaces; uncalls item if all spaces */
1265 (void) mungspaces(buf);
1266 if (!*buf) {
1267 if (*str1) { /* had name, so possibly remove from disco[] */
1268 /* strip name first, for the update_inventory() call
1269 from undiscover_object() */
1270 *str1 = (char *) 0;
1271 undiscover_object(obj->otyp);
1273 } else {
1274 *str1 = dupstr(buf);
1275 discover_object(obj->otyp, FALSE, TRUE); /* possibly add to disco[] */
1279 STATIC_OVL void
1280 namefloorobj()
1282 coord cc;
1283 int glyph;
1284 char buf[BUFSZ];
1285 struct obj *obj = 0;
1286 boolean fakeobj = FALSE, use_plural;
1288 cc.x = u.ux, cc.y = u.uy;
1289 /* "dot for under/over you" only makes sense when the cursor hasn't
1290 been moved off the hero's '@' yet, but there's no way to adjust
1291 the help text once getpos() has started */
1292 Sprintf(buf, "object on map (or '.' for one %s you)",
1293 (u.uundetected && hides_under(youmonst.data)) ? "over" : "under");
1294 if (getpos(&cc, FALSE, buf) < 0 || cc.x <= 0)
1295 return;
1296 if (cc.x == u.ux && cc.y == u.uy) {
1297 obj = vobj_at(u.ux, u.uy);
1298 } else {
1299 glyph = glyph_at(cc.x, cc.y);
1300 if (glyph_is_object(glyph))
1301 fakeobj = object_from_map(glyph, cc.x, cc.y, &obj);
1302 /* else 'obj' stays null */
1304 if (!obj) {
1305 /* "under you" is safe here since there's no object to hide under */
1306 pline("There doesn't seem to be any object %s.",
1307 (cc.x == u.ux && cc.y == u.uy) ? "under you" : "there");
1308 return;
1310 /* note well: 'obj' might be as instance of STRANGE_OBJECT if target
1311 is a mimic; passing that to xname (directly or via simpleonames)
1312 would yield "glorkum" so we need to handle it explicitly; it will
1313 always fail the Hallucination test and pass the !callable test,
1314 resulting in the "can't be assigned a type name" message */
1315 Strcpy(buf, (obj->otyp != STRANGE_OBJECT)
1316 ? simpleonames(obj)
1317 : obj_descr[STRANGE_OBJECT].oc_name);
1318 use_plural = (obj->quan > 1L);
1319 if (Hallucination) {
1320 const char *unames[6];
1321 char tmpbuf[BUFSZ];
1323 /* straight role name */
1324 unames[0] = ((Upolyd ? u.mfemale : flags.female) && urole.name.f)
1325 ? urole.name.f
1326 : urole.name.m;
1327 /* random rank title for hero's role */
1328 unames[1] = rank_of(rnd(30), Role_switch, flags.female);
1329 /* random fake monster */
1330 unames[2] = bogusmon(tmpbuf, (char *) 0);
1331 /* increased chance for fake monster */
1332 unames[3] = unames[2];
1333 /* traditional */
1334 unames[4] = roguename();
1335 /* silly */
1336 unames[5] = "Wibbly Wobbly";
1337 pline("%s %s to call you \"%s.\"",
1338 The(buf), use_plural ? "decide" : "decides",
1339 unames[rn2(SIZE(unames))]);
1340 } else if (!objtyp_is_callable(obj->otyp)) {
1341 pline("%s %s can't be assigned a type name.",
1342 use_plural ? "Those" : "That", buf);
1343 } else if (!obj->dknown) {
1344 You("don't know %s %s well enough to name %s.",
1345 use_plural ? "those" : "that", buf, use_plural ? "them" : "it");
1346 } else {
1347 docall(obj);
1349 if (fakeobj)
1350 dealloc_obj(obj);
1353 static const char *const ghostnames[] = {
1354 /* these names should have length < PL_NSIZ */
1355 /* Capitalize the names for aesthetics -dgk */
1356 "Adri", "Andries", "Andreas", "Bert", "David", "Dirk",
1357 "Emile", "Frans", "Fred", "Greg", "Hether", "Jay",
1358 "John", "Jon", "Karnov", "Kay", "Kenny", "Kevin",
1359 "Maud", "Michiel", "Mike", "Peter", "Robert", "Ron",
1360 "Tom", "Wilmar", "Nick Danger", "Phoenix", "Jiro", "Mizue",
1361 "Stephan", "Lance Braccus", "Shadowhawk"
1364 /* ghost names formerly set by x_monnam(), now by makemon() instead */
1365 const char *
1366 rndghostname()
1368 return rn2(7) ? ghostnames[rn2(SIZE(ghostnames))] : (const char *) plname;
1372 * Monster naming functions:
1373 * x_monnam is the generic monster-naming function.
1374 * seen unseen detected named
1375 * mon_nam: the newt it the invisible orc Fido
1376 * noit_mon_nam:the newt (as if detected) the invisible orc Fido
1377 * l_monnam: newt it invisible orc dog called Fido
1378 * Monnam: The newt It The invisible orc Fido
1379 * noit_Monnam: The newt (as if detected) The invisible orc Fido
1380 * Adjmonnam: The poor newt It The poor invisible orc The poor Fido
1381 * Amonnam: A newt It An invisible orc Fido
1382 * a_monnam: a newt it an invisible orc Fido
1383 * m_monnam: newt xan orc Fido
1384 * y_monnam: your newt your xan your invisible orc Fido
1387 /* Bug: if the monster is a priest or shopkeeper, not every one of these
1388 * options works, since those are special cases.
1390 char *
1391 x_monnam(mtmp, article, adjective, suppress, called)
1392 register struct monst *mtmp;
1393 int article;
1394 /* ARTICLE_NONE, ARTICLE_THE, ARTICLE_A: obvious
1395 * ARTICLE_YOUR: "your" on pets, "the" on everything else
1397 * If the monster would be referred to as "it" or if the monster has a name
1398 * _and_ there is no adjective, "invisible", "saddled", etc., override this
1399 * and always use no article.
1401 const char *adjective;
1402 int suppress;
1403 /* SUPPRESS_IT, SUPPRESS_INVISIBLE, SUPPRESS_HALLUCINATION, SUPPRESS_SADDLE.
1404 * EXACT_NAME: combination of all the above
1406 boolean called;
1408 char *buf = nextmbuf();
1409 struct permonst *mdat = mtmp->data;
1410 const char *pm_name = mdat->mname;
1411 boolean do_hallu, do_invis, do_it, do_saddle;
1412 boolean name_at_start, has_adjectives;
1413 char *bp;
1415 if (program_state.gameover)
1416 suppress |= SUPPRESS_HALLUCINATION;
1417 if (article == ARTICLE_YOUR && !mtmp->mtame)
1418 article = ARTICLE_THE;
1420 do_hallu = Hallucination && !(suppress & SUPPRESS_HALLUCINATION);
1421 do_invis = mtmp->minvis && !(suppress & SUPPRESS_INVISIBLE);
1422 do_it = !canspotmon(mtmp) && article != ARTICLE_YOUR
1423 && !program_state.gameover && mtmp != u.usteed
1424 && !(u.uswallow && mtmp == u.ustuck) && !(suppress & SUPPRESS_IT);
1425 do_saddle = !(suppress & SUPPRESS_SADDLE);
1427 buf[0] = '\0';
1429 /* unseen monsters, etc. Use "it" */
1430 if (do_it) {
1431 Strcpy(buf, "it");
1432 return buf;
1435 /* priests and minions: don't even use this function */
1436 if (mtmp->ispriest || mtmp->isminion) {
1437 char priestnambuf[BUFSZ];
1438 char *name;
1439 long save_prop = EHalluc_resistance;
1440 unsigned save_invis = mtmp->minvis;
1442 /* when true name is wanted, explicitly block Hallucination */
1443 if (!do_hallu)
1444 EHalluc_resistance = 1L;
1445 if (!do_invis)
1446 mtmp->minvis = 0;
1447 name = priestname(mtmp, priestnambuf);
1448 EHalluc_resistance = save_prop;
1449 mtmp->minvis = save_invis;
1450 if (article == ARTICLE_NONE && !strncmp(name, "the ", 4))
1451 name += 4;
1452 return strcpy(buf, name);
1454 /* an "aligned priest" not flagged as a priest or minion should be
1455 "priest" or "priestess" (normally handled by priestname()) */
1456 if (mdat == &mons[PM_ALIGNED_PRIEST])
1457 pm_name = mtmp->female ? "priestess" : "priest";
1458 else if (mdat == &mons[PM_HIGH_PRIEST] && mtmp->female)
1459 pm_name = "high priestess";
1461 /* Shopkeepers: use shopkeeper name. For normal shopkeepers, just
1462 * "Asidonhopo"; for unusual ones, "Asidonhopo the invisible
1463 * shopkeeper" or "Asidonhopo the blue dragon". If hallucinating,
1464 * none of this applies.
1466 if (mtmp->isshk && !do_hallu) {
1467 if (adjective && article == ARTICLE_THE) {
1468 /* pathological case: "the angry Asidonhopo the blue dragon"
1469 sounds silly */
1470 Strcpy(buf, "the ");
1471 Strcat(strcat(buf, adjective), " ");
1472 Strcat(buf, shkname(mtmp));
1473 return buf;
1475 Strcat(buf, shkname(mtmp));
1476 if (mdat == &mons[PM_SHOPKEEPER] && !do_invis)
1477 return buf;
1478 Strcat(buf, " the ");
1479 if (do_invis)
1480 Strcat(buf, "invisible ");
1481 Strcat(buf, pm_name);
1482 return buf;
1485 /* Put the adjectives in the buffer */
1486 if (adjective)
1487 Strcat(strcat(buf, adjective), " ");
1488 if (do_invis)
1489 Strcat(buf, "invisible ");
1490 if (do_saddle && (mtmp->misc_worn_check & W_SADDLE) && !Blind
1491 && !Hallucination)
1492 Strcat(buf, "saddled ");
1493 if (buf[0] != 0)
1494 has_adjectives = TRUE;
1495 else
1496 has_adjectives = FALSE;
1498 /* Put the actual monster name or type into the buffer now */
1499 /* Be sure to remember whether the buffer starts with a name */
1500 if (do_hallu) {
1501 char rnamecode;
1502 char *rname = rndmonnam(&rnamecode);
1504 Strcat(buf, rname);
1505 name_at_start = bogon_is_pname(rnamecode);
1506 } else if (has_mname(mtmp)) {
1507 char *name = MNAME(mtmp);
1509 if (mdat == &mons[PM_GHOST]) {
1510 Sprintf(eos(buf), "%s ghost", s_suffix(name));
1511 name_at_start = TRUE;
1512 } else if (called) {
1513 Sprintf(eos(buf), "%s called %s", pm_name, name);
1514 name_at_start = (boolean) type_is_pname(mdat);
1515 } else if (is_mplayer(mdat) && (bp = strstri(name, " the ")) != 0) {
1516 /* <name> the <adjective> <invisible> <saddled> <rank> */
1517 char pbuf[BUFSZ];
1519 Strcpy(pbuf, name);
1520 pbuf[bp - name + 5] = '\0'; /* adjectives right after " the " */
1521 if (has_adjectives)
1522 Strcat(pbuf, buf);
1523 Strcat(pbuf, bp + 5); /* append the rest of the name */
1524 Strcpy(buf, pbuf);
1525 article = ARTICLE_NONE;
1526 name_at_start = TRUE;
1527 } else {
1528 Strcat(buf, name);
1529 name_at_start = TRUE;
1531 } else if (is_mplayer(mdat) && !In_endgame(&u.uz)) {
1532 char pbuf[BUFSZ];
1534 Strcpy(pbuf, rank_of((int) mtmp->m_lev, monsndx(mdat),
1535 (boolean) mtmp->female));
1536 Strcat(buf, lcase(pbuf));
1537 name_at_start = FALSE;
1538 } else {
1539 Strcat(buf, pm_name);
1540 name_at_start = (boolean) type_is_pname(mdat);
1543 if (name_at_start && (article == ARTICLE_YOUR || !has_adjectives)) {
1544 if (mdat == &mons[PM_WIZARD_OF_YENDOR])
1545 article = ARTICLE_THE;
1546 else
1547 article = ARTICLE_NONE;
1548 } else if ((mdat->geno & G_UNIQ) && article == ARTICLE_A) {
1549 article = ARTICLE_THE;
1553 char buf2[BUFSZ];
1555 switch (article) {
1556 case ARTICLE_YOUR:
1557 Strcpy(buf2, "your ");
1558 Strcat(buf2, buf);
1559 Strcpy(buf, buf2);
1560 return buf;
1561 case ARTICLE_THE:
1562 Strcpy(buf2, "the ");
1563 Strcat(buf2, buf);
1564 Strcpy(buf, buf2);
1565 return buf;
1566 case ARTICLE_A:
1567 return an(buf);
1568 case ARTICLE_NONE:
1569 default:
1570 return buf;
1575 char *
1576 l_monnam(mtmp)
1577 struct monst *mtmp;
1579 return x_monnam(mtmp, ARTICLE_NONE, (char *) 0,
1580 (has_mname(mtmp)) ? SUPPRESS_SADDLE : 0, TRUE);
1583 char *
1584 mon_nam(mtmp)
1585 struct monst *mtmp;
1587 return x_monnam(mtmp, ARTICLE_THE, (char *) 0,
1588 (has_mname(mtmp)) ? SUPPRESS_SADDLE : 0, FALSE);
1591 /* print the name as if mon_nam() was called, but assume that the player
1592 * can always see the monster--used for probing and for monsters aggravating
1593 * the player with a cursed potion of invisibility
1595 char *
1596 noit_mon_nam(mtmp)
1597 struct monst *mtmp;
1599 return x_monnam(mtmp, ARTICLE_THE, (char *) 0,
1600 (has_mname(mtmp)) ? (SUPPRESS_SADDLE | SUPPRESS_IT)
1601 : SUPPRESS_IT,
1602 FALSE);
1605 char *
1606 Monnam(mtmp)
1607 struct monst *mtmp;
1609 register char *bp = mon_nam(mtmp);
1611 *bp = highc(*bp);
1612 return bp;
1615 char *
1616 noit_Monnam(mtmp)
1617 struct monst *mtmp;
1619 register char *bp = noit_mon_nam(mtmp);
1621 *bp = highc(*bp);
1622 return bp;
1625 /* monster's own name */
1626 char *
1627 m_monnam(mtmp)
1628 struct monst *mtmp;
1630 return x_monnam(mtmp, ARTICLE_NONE, (char *) 0, EXACT_NAME, FALSE);
1633 /* pet name: "your little dog" */
1634 char *
1635 y_monnam(mtmp)
1636 struct monst *mtmp;
1638 int prefix, suppression_flag;
1640 prefix = mtmp->mtame ? ARTICLE_YOUR : ARTICLE_THE;
1641 suppression_flag = (has_mname(mtmp)
1642 /* "saddled" is redundant when mounted */
1643 || mtmp == u.usteed)
1644 ? SUPPRESS_SADDLE
1645 : 0;
1647 return x_monnam(mtmp, prefix, (char *) 0, suppression_flag, FALSE);
1650 char *
1651 Adjmonnam(mtmp, adj)
1652 struct monst *mtmp;
1653 const char *adj;
1655 char *bp = x_monnam(mtmp, ARTICLE_THE, adj,
1656 has_mname(mtmp) ? SUPPRESS_SADDLE : 0, FALSE);
1658 *bp = highc(*bp);
1659 return bp;
1662 char *
1663 a_monnam(mtmp)
1664 struct monst *mtmp;
1666 return x_monnam(mtmp, ARTICLE_A, (char *) 0,
1667 has_mname(mtmp) ? SUPPRESS_SADDLE : 0, FALSE);
1670 char *
1671 Amonnam(mtmp)
1672 struct monst *mtmp;
1674 char *bp = a_monnam(mtmp);
1676 *bp = highc(*bp);
1677 return bp;
1680 /* used for monster ID by the '/', ';', and 'C' commands to block remote
1681 identification of the endgame altars via their attending priests */
1682 char *
1683 distant_monnam(mon, article, outbuf)
1684 struct monst *mon;
1685 int article; /* only ARTICLE_NONE and ARTICLE_THE are handled here */
1686 char *outbuf;
1688 /* high priest(ess)'s identity is concealed on the Astral Plane,
1689 unless you're adjacent (overridden for hallucination which does
1690 its own obfuscation) */
1691 if (mon->data == &mons[PM_HIGH_PRIEST] && !Hallucination
1692 && Is_astralevel(&u.uz) && distu(mon->mx, mon->my) > 2) {
1693 Strcpy(outbuf, article == ARTICLE_THE ? "the " : "");
1694 Strcat(outbuf, mon->female ? "high priestess" : "high priest");
1695 } else {
1696 Strcpy(outbuf, x_monnam(mon, article, (char *) 0, 0, TRUE));
1698 return outbuf;
1701 /* fake monsters used to be in a hard-coded array, now in a data file */
1702 STATIC_OVL char *
1703 bogusmon(buf, code)
1704 char *buf, *code;
1706 char *mname = buf;
1708 get_rnd_text(BOGUSMONFILE, buf);
1709 /* strip prefix if present */
1710 if (!letter(*mname)) {
1711 if (code)
1712 *code = *mname;
1713 ++mname;
1714 } else {
1715 if (code)
1716 *code = '\0';
1718 return mname;
1721 /* return a random monster name, for hallucination */
1722 char *
1723 rndmonnam(code)
1724 char *code;
1726 static char buf[BUFSZ];
1727 char *mname;
1728 int name;
1729 #define BOGUSMONSIZE 100 /* arbitrary */
1731 if (code)
1732 *code = '\0';
1734 do {
1735 name = rn1(SPECIAL_PM + BOGUSMONSIZE - LOW_PM, LOW_PM);
1736 } while (name < SPECIAL_PM
1737 && (type_is_pname(&mons[name]) || (mons[name].geno & G_NOGEN)));
1739 if (name >= SPECIAL_PM) {
1740 mname = bogusmon(buf, code);
1741 } else {
1742 mname = strcpy(buf, mons[name].mname);
1744 return mname;
1745 #undef BOGUSMONSIZE
1748 /* check bogusmon prefix to decide whether it's a personal name */
1749 boolean
1750 bogon_is_pname(code)
1751 char code;
1753 if (!code)
1754 return FALSE;
1755 return index("-+=", code) ? TRUE : FALSE;
1758 /* name of a Rogue player */
1759 const char *
1760 roguename()
1762 char *i, *opts;
1764 if ((opts = nh_getenv("ROGUEOPTS")) != 0) {
1765 for (i = opts; *i; i++)
1766 if (!strncmp("name=", i, 5)) {
1767 char *j;
1768 if ((j = index(i + 5, ',')) != 0)
1769 *j = (char) 0;
1770 return i + 5;
1773 return rn2(3) ? (rn2(2) ? "Michael Toy" : "Kenneth Arnold")
1774 : "Glenn Wichman";
1777 static NEARDATA const char *const hcolors[] = {
1778 "ultraviolet", "infrared", "bluish-orange", "reddish-green", "dark white",
1779 "light black", "sky blue-pink", "salty", "sweet", "sour", "bitter",
1780 "striped", "spiral", "swirly", "plaid", "checkered", "argyle", "paisley",
1781 "blotchy", "guernsey-spotted", "polka-dotted", "square", "round",
1782 "triangular", "cabernet", "sangria", "fuchsia", "wisteria", "lemon-lime",
1783 "strawberry-banana", "peppermint", "romantic", "incandescent",
1784 "octarine", /* Discworld: the Colour of Magic */
1787 const char *
1788 hcolor(colorpref)
1789 const char *colorpref;
1791 return (Hallucination || !colorpref) ? hcolors[rn2(SIZE(hcolors))]
1792 : colorpref;
1795 /* return a random real color unless hallucinating */
1796 const char *
1797 rndcolor()
1799 int k = rn2(CLR_MAX);
1801 return Hallucination ? hcolor((char *) 0)
1802 : (k == NO_COLOR) ? "colorless"
1803 : c_obj_colors[k];
1806 static NEARDATA const char *const hliquids[] = {
1807 "yoghurt", "oobleck", "clotted blood", "diluted water", "purified water",
1808 "instant coffee", "tea", "herbal infusion", "liquid rainbow",
1809 "creamy foam", "mulled wine", "bouillon", "nectar", "grog", "flubber",
1810 "ketchup", "slow light", "oil", "vinaigrette", "liquid crystal", "honey",
1811 "caramel sauce", "ink", "aqueous humour", "milk substitute", "fruit juice",
1812 "glowing lava", "gastric acid", "mineral water", "cough syrup", "quicksilver",
1813 "sweet vitriol", "grey goo", "pink slime",
1816 const char *
1817 hliquid(liquidpref)
1818 const char *liquidpref;
1820 return (Hallucination || !liquidpref) ? hliquids[rn2(SIZE(hliquids))]
1821 : liquidpref;
1824 /* Aliases for road-runner nemesis
1826 static const char *const coynames[] = {
1827 "Carnivorous Vulgaris", "Road-Runnerus Digestus", "Eatibus Anythingus",
1828 "Famishus-Famishus", "Eatibus Almost Anythingus", "Eatius Birdius",
1829 "Famishius Fantasticus", "Eternalii Famishiis", "Famishus Vulgarus",
1830 "Famishius Vulgaris Ingeniusi", "Eatius-Slobbius", "Hardheadipus Oedipus",
1831 "Carnivorous Slobbius", "Hard-Headipus Ravenus", "Evereadii Eatibus",
1832 "Apetitius Giganticus", "Hungrii Flea-Bagius", "Overconfidentii Vulgaris",
1833 "Caninus Nervous Rex", "Grotesques Appetitus", "Nemesis Ridiculii",
1834 "Canis latrans"
1837 char *
1838 coyotename(mtmp, buf)
1839 struct monst *mtmp;
1840 char *buf;
1842 if (mtmp && buf) {
1843 Sprintf(buf, "%s - %s",
1844 x_monnam(mtmp, ARTICLE_NONE, (char *) 0, 0, TRUE),
1845 mtmp->mcan ? coynames[SIZE(coynames) - 1]
1846 : coynames[mtmp->m_id % (SIZE(coynames) - 1)]);
1848 return buf;
1851 /* make sure "The Colour of Magic" remains the first entry in here */
1852 static const char *const sir_Terry_novels[] = {
1853 "The Colour of Magic", "The Light Fantastic", "Equal Rites", "Mort",
1854 "Sourcery", "Wyrd Sisters", "Pyramids", "Guards! Guards!", "Eric",
1855 "Moving Pictures", "Reaper Man", "Witches Abroad", "Small Gods",
1856 "Lords and Ladies", "Men at Arms", "Soul Music", "Interesting Times",
1857 "Maskerade", "Feet of Clay", "Hogfather", "Jingo", "The Last Continent",
1858 "Carpe Jugulum", "The Fifth Elephant", "The Truth", "Thief of Time",
1859 "The Last Hero", "The Amazing Maurice and his Educated Rodents",
1860 "Night Watch", "The Wee Free Men", "Monstrous Regiment",
1861 "A Hat Full of Sky", "Going Postal", "Thud!", "Wintersmith",
1862 "Making Money", "Unseen Academicals", "I Shall Wear Midnight", "Snuff",
1863 "Raising Steam", "The Shepherd's Crown"
1866 const char *
1867 noveltitle(novidx)
1868 int *novidx;
1870 int j, k = SIZE(sir_Terry_novels);
1872 j = rn2(k);
1873 if (novidx) {
1874 if (*novidx == -1)
1875 *novidx = j;
1876 else if (*novidx >= 0 && *novidx < k)
1877 j = *novidx;
1879 return sir_Terry_novels[j];
1882 const char *
1883 lookup_novel(lookname, idx)
1884 const char *lookname;
1885 int *idx;
1887 int k;
1889 /* Take American or U.K. spelling of this one */
1890 if (!strcmpi(The(lookname), "The Color of Magic"))
1891 lookname = sir_Terry_novels[0];
1893 for (k = 0; k < SIZE(sir_Terry_novels); ++k) {
1894 if (!strcmpi(lookname, sir_Terry_novels[k])
1895 || !strcmpi(The(lookname), sir_Terry_novels[k])) {
1896 if (idx)
1897 *idx = k;
1898 return sir_Terry_novels[k];
1901 /* name not found; if novelidx is already set, override the name */
1902 if (idx && *idx >= 0 && *idx < SIZE(sir_Terry_novels))
1903 return sir_Terry_novels[*idx];
1905 return (const char *) 0;
1908 /*do_name.c*/