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