Give feedback just before timed levitation runs out
[aNetHack.git] / src / do_name.c
blobc5ee58081aada6503f4fad74acb31270a13b4142
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 putstr(tmpwin, 0, "Use '@' to move the cursor on yourself.");
63 if (!iflags.terrainmode || (iflags.terrainmode & TER_MON) != 0)
64 putstr(tmpwin, 0, "Use 'm' or 'M' to move the cursor to next monster.");
65 if (!iflags.terrainmode || (iflags.terrainmode & TER_OBJ) != 0)
66 putstr(tmpwin, 0, "Use 'o' or 'O' to move the cursor to next object.");
67 if (!iflags.terrainmode || (iflags.terrainmode & TER_MAP) != 0) {
68 /* both of these are primarily useful when choosing a travel
69 destination for the '_' command */
70 putstr(tmpwin, 0,
71 "Use 'd' or 'D' to move the cursor to next door or doorway.");
72 putstr(tmpwin, 0,
73 "Use 'x' or 'X' to move the cursor to unexplored location.");
75 if (!iflags.terrainmode) {
76 if (getpos_hilitefunc)
77 putstr(tmpwin, 0, "Use '$' to display valid locations.");
78 putstr(tmpwin, 0, "Use '#' to toggle automatic description.");
79 if (iflags.cmdassist) /* assisting the '/' command, I suppose... */
80 putstr(tmpwin, 0,
81 (iflags.getpos_coords == GPCOORDS_NONE)
82 ? "(Set 'whatis_coord' option to include coordinates with '#' text.)"
83 : "(Reset 'whatis_coord' option to omit coordinates from '#' text.)");
84 /* disgusting hack; the alternate selection characters work for any
85 getpos call, but only matter for dowhatis (and doquickwhatis) */
86 doing_what_is = (goal == what_is_an_unknown_object);
87 Sprintf(sbuf, "Type a '.'%s when you are at the right place.",
88 doing_what_is ? " or ',' or ';' or ':'" : "");
89 putstr(tmpwin, 0, sbuf);
90 if (doing_what_is) {
91 putstr(tmpwin, 0,
92 " ':' describe current spot, show 'more info', move to another spot.");
93 Sprintf(sbuf,
94 " '.' describe current spot,%s move to another spot;",
95 flags.help ? " prompt if 'more info'," : "");
96 putstr(tmpwin, 0, sbuf);
97 putstr(tmpwin, 0,
98 " ',' describe current spot, move to another spot;");
99 putstr(tmpwin, 0,
100 " ';' describe current spot, stop looking at things;");
103 if (!force)
104 putstr(tmpwin, 0, "Type Space or Escape when you're done.");
105 putstr(tmpwin, 0, "");
106 display_nhwindow(tmpwin, TRUE);
107 destroy_nhwindow(tmpwin);
110 STATIC_OVL int
111 cmp_coord_distu(a, b)
112 const void *a;
113 const void *b;
115 const coord *c1 = a;
116 const coord *c2 = b;
117 int dx, dy, dist_1, dist_2;
119 dx = u.ux - c1->x;
120 dy = u.uy - c1->y;
121 dist_1 = max(abs(dx), abs(dy));
122 dx = u.ux - c2->x;
123 dy = u.uy - c2->y;
124 dist_2 = max(abs(dx), abs(dy));
126 if (dist_1 == dist_2)
127 return (c1->y != c2->y) ? (c1->y - c2->y) : (c1->x - c2->x);
129 return dist_1 - dist_2;
132 enum gloctypes {
133 GLOC_MONS = 0,
134 GLOC_OBJS,
135 GLOC_DOOR,
136 GLOC_EXPLORE,
138 NUM_GLOCS
142 #define IS_UNEXPLORED_LOC(x,y) \
143 (isok((x), (y)) \
144 && glyph_is_cmap(levl[(x)][(y)].glyph) \
145 && glyph_to_cmap(levl[(x)][(y)].glyph) == S_stone \
146 && !levl[(x)][(y)].seenv)
148 STATIC_OVL boolean
149 gather_locs_interesting(x,y, gloc)
150 int x,y, gloc;
152 /* TODO: if glyph is a pile glyph, convert to ordinary one
153 * in order to keep tail/boulder/rock check simple.
155 int glyph = glyph_at(x, y);
157 switch (gloc) {
158 default:
159 case GLOC_MONS:
160 /* unlike '/M', this skips monsters revealed by
161 warning glyphs and remembered unseen ones */
162 return (glyph_is_monster(glyph)
163 && glyph != monnum_to_glyph(PM_LONG_WORM_TAIL));
164 case GLOC_OBJS:
165 return (glyph_is_object(glyph)
166 && glyph != objnum_to_glyph(BOULDER)
167 && glyph != objnum_to_glyph(ROCK));
168 case GLOC_DOOR:
169 return (glyph_is_cmap(glyph)
170 && (is_cmap_door(glyph_to_cmap(glyph))
171 || is_cmap_drawbridge(glyph_to_cmap(glyph))
172 || glyph_to_cmap(glyph) == S_ndoor));
173 case GLOC_EXPLORE:
174 return (glyph_is_cmap(glyph)
175 && (is_cmap_door(glyph_to_cmap(glyph))
176 || is_cmap_drawbridge(glyph_to_cmap(glyph))
177 || glyph_to_cmap(glyph) == S_ndoor
178 || glyph_to_cmap(glyph) == S_room
179 || glyph_to_cmap(glyph) == S_darkroom
180 || glyph_to_cmap(glyph) == S_corr
181 || glyph_to_cmap(glyph) == S_litcorr)
182 && (IS_UNEXPLORED_LOC(x + 1, y)
183 || IS_UNEXPLORED_LOC(x - 1, y)
184 || IS_UNEXPLORED_LOC(x, y + 1)
185 || IS_UNEXPLORED_LOC(x, y - 1)));
187 /*NOTREACHED*/
188 return FALSE;
191 /* gather locations for monsters or objects shown on the map */
192 STATIC_OVL void
193 gather_locs(arr_p, cnt_p, gloc)
194 coord **arr_p;
195 int *cnt_p;
196 int gloc;
198 int x, y, pass, idx;
201 * We always include the hero's location even if there is no monster
202 * (invisible hero without see invisible) or object (usual case)
203 * displayed there. That way, the count will always be at least 1,
204 * and player has a visual indicator (cursor returns to hero's spot)
205 * highlighting when successive 'm's or 'o's have cycled all the way
206 * through all monsters or objects.
208 * Hero's spot will always sort to array[0] because it will always
209 * be the shortest distance (namely, 0 units) away from <u.ux,u.uy>.
211 *cnt_p = idx = 0;
212 for (pass = 0; pass < 2; pass++) {
213 for (x = 1; x < COLNO; x++)
214 for (y = 0; y < ROWNO; y++) {
215 if ((x == u.ux && y == u.uy)
216 || gather_locs_interesting(x, y, gloc)) {
217 if (!pass) {
218 ++*cnt_p;
219 } else {
220 (*arr_p)[idx].x = x;
221 (*arr_p)[idx].y = y;
222 ++idx;
227 if (!pass) /* end of first pass */
228 *arr_p = (coord *) alloc(*cnt_p * sizeof (coord));
229 else /* end of second pass */
230 qsort(*arr_p, *cnt_p, sizeof (coord), cmp_coord_distu);
231 } /* pass */
234 char *
235 dxdy_to_dist_descr(dx, dy, fulldir)
236 int dx, dy;
237 boolean fulldir;
239 static char buf[30];
240 int dst;
242 if (!dx && !dy) {
243 Sprintf(buf, "here");
244 } else if ((dst = xytod(dx, dy)) != -1) {
245 /* explicit direction; 'one step' is implicit */
246 Sprintf(buf, "%s", directionname(dst));
247 } else {
248 const char *dirnames[4][2] = {
249 { "n", "north" },
250 { "s", "south" },
251 { "w", "west" },
252 { "e", "east" } };
253 buf[0] = '\0';
254 /* 9999: protect buf[] against overflow caused by invalid values */
255 if (dy) {
256 if (abs(dy) > 9999)
257 dy = sgn(dy) * 9999;
258 Sprintf(eos(buf), "%d%s%s", abs(dy), dirnames[(dy > 0)][fulldir],
259 dx ? "," : "");
261 if (dx) {
262 if (abs(dx) > 9999)
263 dx = sgn(dx) * 9999;
264 Sprintf(eos(buf), "%d%s", abs(dx), dirnames[2 + (dx > 0)][fulldir]);
267 return buf;
270 /* coordinate formatting for 'whatis_coord' option */
271 char *
272 coord_desc(x, y, outbuf, cmode)
273 int x, y;
274 char *outbuf, cmode;
276 static char screen_fmt[16]; /* [12] suffices: "[%02d,%02d]" */
277 int dx, dy;
279 outbuf[0] = '\0';
280 switch (cmode) {
281 default:
282 break;
283 case GPCOORDS_COMFULL:
284 case GPCOORDS_COMPASS:
285 /* "east", "3s", "2n,4w" */
286 dx = x - u.ux;
287 dy = y - u.uy;
288 Sprintf(outbuf, "(%s)",
289 dxdy_to_dist_descr(dx, dy, cmode == GPCOORDS_COMFULL));
290 break;
291 case GPCOORDS_MAP: /* x,y */
292 /* upper left corner of map is <1,0>;
293 with default COLNO,ROWNO lower right corner is <79,20> */
294 Sprintf(outbuf, "<%d,%d>", x, y);
295 break;
296 case GPCOORDS_SCREEN: /* y+2,x */
297 /* for normal map sizes, force a fixed-width formatting so that
298 /m, /M, /o, and /O output lines up cleanly; map sizes bigger
299 than Nx999 or 999xM will still work, but not line up like normal
300 when displayed in a column setting */
301 if (!*screen_fmt)
302 Sprintf(screen_fmt, "[%%%sd,%%%sd]",
303 (ROWNO - 1 + 2 < 100) ? "02" : "03",
304 (COLNO - 1 < 100) ? "02" : "03");
305 /* map line 0 is screen row 2;
306 map column 0 isn't used, map column 1 is screen column 1 */
307 Sprintf(outbuf, screen_fmt, y + 2, x);
308 break;
310 return outbuf;
313 STATIC_OVL void
314 auto_describe(cx, cy)
315 int cx, cy;
317 coord cc;
318 int sym = 0;
319 char tmpbuf[BUFSZ];
320 const char *firstmatch = "unknown";
322 cc.x = cx;
323 cc.y = cy;
324 if (do_screen_description(cc, TRUE, sym, tmpbuf, &firstmatch)) {
325 (void) coord_desc(cx, cy, tmpbuf, iflags.getpos_coords);
326 pline("%s%s%s%s", firstmatch, *tmpbuf ? " " : "", tmpbuf,
327 (iflags.getloc_travelmode && !is_valid_travelpt(cx,cy))
328 ? " (no travel path)" : "");
329 curs(WIN_MAP, cx, cy);
330 flush_screen(0);
335 getpos(ccp, force, goal)
336 coord *ccp;
337 boolean force;
338 const char *goal;
340 static const char pick_chars[] = ".,;:",
341 mMoOdDxX[] = "mMoOdDxX";
342 const char *cp;
343 int result = 0;
344 int cx, cy, i, c;
345 int sidx, tx, ty;
346 boolean msg_given = TRUE; /* clear message window by default */
347 boolean show_goal_msg = FALSE;
348 boolean hilite_state = FALSE;
349 coord *garr[NUM_GLOCS] = DUMMY;
350 int gcount[NUM_GLOCS] = DUMMY;
351 int gidx[NUM_GLOCS] = DUMMY;
353 if (!goal)
354 goal = "desired location";
355 if (flags.verbose) {
356 pline("(For instructions type a '?')");
357 msg_given = TRUE;
359 cx = ccp->x;
360 cy = ccp->y;
361 #ifdef CLIPPING
362 cliparound(cx, cy);
363 #endif
364 curs(WIN_MAP, cx, cy);
365 flush_screen(0);
366 #ifdef MAC
367 lock_mouse_cursor(TRUE);
368 #endif
369 for (;;) {
370 if (show_goal_msg) {
371 pline("Move cursor to %s:", goal);
372 curs(WIN_MAP, cx, cy);
373 flush_screen(0);
374 show_goal_msg = FALSE;
375 } else if (iflags.autodescribe && !msg_given && !hilite_state) {
376 auto_describe(cx, cy);
379 c = nh_poskey(&tx, &ty, &sidx);
381 if (hilite_state) {
382 (*getpos_hilitefunc)(2);
383 hilite_state = FALSE;
384 curs(WIN_MAP, cx, cy);
385 flush_screen(0);
388 if (iflags.autodescribe)
389 msg_given = FALSE;
391 if (c == '\033') {
392 cx = cy = -10;
393 msg_given = TRUE; /* force clear */
394 result = -1;
395 break;
397 if (c == 0) {
398 if (!isok(tx, ty))
399 continue;
400 /* a mouse click event, just assign and return */
401 cx = tx;
402 cy = ty;
403 break;
405 if ((cp = index(pick_chars, c)) != 0) {
406 /* '.' => 0, ',' => 1, ';' => 2, ':' => 3 */
407 result = (int) (cp - pick_chars);
408 break;
410 for (i = 0; i < 8; i++) {
411 int dx, dy;
413 if (Cmd.dirchars[i] == c) {
414 /* a normal movement letter or digit */
415 dx = xdir[i];
416 dy = ydir[i];
417 } else if (Cmd.alphadirchars[i] == lowc((char) c)
418 || (Cmd.num_pad && Cmd.dirchars[i] == (c & 0177))) {
419 /* a shifted movement letter or Meta-digit */
420 dx = 8 * xdir[i];
421 dy = 8 * ydir[i];
422 } else
423 continue;
425 /* truncate at map edge; diagonal moves complicate this... */
426 if (cx + dx < 1) {
427 dy -= sgn(dy) * (1 - (cx + dx));
428 dx = 1 - cx; /* so that (cx+dx == 1) */
429 } else if (cx + dx > COLNO - 1) {
430 dy += sgn(dy) * ((COLNO - 1) - (cx + dx));
431 dx = (COLNO - 1) - cx;
433 if (cy + dy < 0) {
434 dx -= sgn(dx) * (0 - (cy + dy));
435 dy = 0 - cy; /* so that (cy+dy == 0) */
436 } else if (cy + dy > ROWNO - 1) {
437 dx += sgn(dx) * ((ROWNO - 1) - (cy + dy));
438 dy = (ROWNO - 1) - cy;
440 cx += dx;
441 cy += dy;
442 goto nxtc;
445 if (c == '?' || redraw_cmd(c)) {
446 if (c == '?')
447 getpos_help(force, goal);
448 else /* ^R */
449 docrt(); /* redraw */
450 /* update message window to reflect that we're still targetting */
451 show_goal_msg = TRUE;
452 msg_given = TRUE;
453 } else if (c == '$' && getpos_hilitefunc) {
454 if (!hilite_state) {
455 (*getpos_hilitefunc)(0);
456 (*getpos_hilitefunc)(1);
457 hilite_state = TRUE;
459 goto nxtc;
460 } else if (c == '#') {
461 /* unfortunately, using '#' as a command means we can't move
462 cursor to sinks, iron bars, and poison clouds; perhaps
463 when autodescribe is already on, next '#' should try to
464 move to '#' rather than to toggle off? (or ask; ick...) */
465 iflags.autodescribe = !iflags.autodescribe;
466 pline("Automatic description %sis %s.",
467 flags.verbose ? "of features under cursor " : "",
468 iflags.autodescribe ? "on" : "off");
469 if (!iflags.autodescribe)
470 show_goal_msg = TRUE;
471 msg_given = TRUE;
472 goto nxtc;
473 } else if (c == '@') { /* return to hero's spot */
474 /* reset 'm&M', 'o&O', &c; otherwise, there's no way for player
475 to achieve that except by manually cycling through all spots */
476 for (i = 0; i < NUM_GLOCS; i++)
477 gidx[i] = 0;
478 cx = u.ux;
479 cy = u.uy;
480 goto nxtc;
481 } else if ((cp = index(mMoOdDxX, c)) != 0) { /* 'm|M', 'o|O', &c */
482 /* nearest or farthest monster or object or door or unexplored */
483 int gtmp = (int) (cp - mMoOdDxX), /* 0..7 */
484 gloc = gtmp >> 1; /* 0..3 */
486 if (!garr[gloc]) {
487 gather_locs(&garr[gloc], &gcount[gloc], gloc);
488 gidx[gloc] = 0; /* garr[][0] is hero's spot */
490 if (!(gtmp & 1)) { /* c=='m' || c=='o' || c=='d' || c=='x') */
491 gidx[gloc] = (gidx[gloc] + 1) % gcount[gloc];
492 } else { /* c=='M' || c=='O' || c=='D' || c=='X') */
493 if (--gidx[gloc] < 0)
494 gidx[gloc] = gcount[gloc] - 1;
496 cx = garr[gloc][gidx[gloc]].x;
497 cy = garr[gloc][gidx[gloc]].y;
498 goto nxtc;
499 } else {
500 if (!index(quitchars, c)) {
501 char matching[MAXPCHARS];
502 int pass, lo_x, lo_y, hi_x, hi_y, k = 0;
504 (void) memset((genericptr_t) matching, 0, sizeof matching);
505 for (sidx = 1; sidx < MAXPCHARS; sidx++) { /* [0] left as 0 */
506 if (IS_DOOR(sidx) || IS_WALL(sidx)
507 || sidx == SDOOR || sidx == SCORR
508 || glyph_to_cmap(k) == S_room
509 || glyph_to_cmap(k) == S_darkroom
510 || glyph_to_cmap(k) == S_corr
511 || glyph_to_cmap(k) == S_litcorr)
512 continue;
513 if (c == defsyms[sidx].sym || c == (int) showsyms[sidx])
514 matching[sidx] = (char) ++k;
516 if (k) {
517 for (pass = 0; pass <= 1; pass++) {
518 /* pass 0: just past current pos to lower right;
519 pass 1: upper left corner to current pos */
520 lo_y = (pass == 0) ? cy : 0;
521 hi_y = (pass == 0) ? ROWNO - 1 : cy;
522 for (ty = lo_y; ty <= hi_y; ty++) {
523 lo_x = (pass == 0 && ty == lo_y) ? cx + 1 : 1;
524 hi_x = (pass == 1 && ty == hi_y) ? cx : COLNO - 1;
525 for (tx = lo_x; tx <= hi_x; tx++) {
526 /* first, look at what is currently visible
527 (might be monster) */
528 k = glyph_at(tx, ty);
529 if (glyph_is_cmap(k)
530 && matching[glyph_to_cmap(k)])
531 goto foundc;
532 /* next, try glyph that's remembered here
533 (might be trap or object) */
534 if (level.flags.hero_memory
535 /* !terrainmode: don't move to remembered
536 trap or object if not currently shown */
537 && !iflags.terrainmode) {
538 k = levl[tx][ty].glyph;
539 if (glyph_is_cmap(k)
540 && matching[glyph_to_cmap(k)])
541 goto foundc;
543 /* last, try actual terrain here (shouldn't
544 we be using lastseentyp[][] instead?) */
545 if (levl[tx][ty].seenv) {
546 k = back_to_glyph(tx, ty);
547 if (glyph_is_cmap(k)
548 && matching[glyph_to_cmap(k)])
549 goto foundc;
551 continue;
552 foundc:
553 cx = tx, cy = ty;
554 if (msg_given) {
555 clear_nhwindow(WIN_MESSAGE);
556 msg_given = FALSE;
558 goto nxtc;
559 } /* column */
560 } /* row */
561 } /* pass */
562 pline("Can't find dungeon feature '%c'.", c);
563 msg_given = TRUE;
564 goto nxtc;
565 } else {
566 char note[QBUFSZ];
568 if (!force)
569 Strcpy(note, "aborted");
570 else
571 Sprintf(note, "use '%c', '%c', '%c', '%c' or '.'", /* hjkl */
572 Cmd.move_W, Cmd.move_S, Cmd.move_N,
573 Cmd.move_E);
574 pline("Unknown direction: '%s' (%s).", visctrl((char) c),
575 note);
576 msg_given = TRUE;
577 } /* k => matching */
578 } /* !quitchars */
579 if (force)
580 goto nxtc;
581 pline("Done.");
582 msg_given = FALSE; /* suppress clear */
583 cx = -1;
584 cy = 0;
585 result = 0; /* not -1 */
586 break;
588 nxtc:
590 #ifdef CLIPPING
591 cliparound(cx, cy);
592 #endif
593 curs(WIN_MAP, cx, cy);
594 flush_screen(0);
596 #ifdef MAC
597 lock_mouse_cursor(FALSE);
598 #endif
599 if (msg_given)
600 clear_nhwindow(WIN_MESSAGE);
601 ccp->x = cx;
602 ccp->y = cy;
603 for (i = 0; i < NUM_GLOCS; i++)
604 if (garr[i])
605 free((genericptr_t) garr[i]);
606 getpos_hilitefunc = (void FDECL((*), (int))) 0;
607 return result;
610 /* allocate space for a monster's name; removes old name if there is one */
611 void
612 new_mname(mon, lth)
613 struct monst *mon;
614 int lth; /* desired length (caller handles adding 1 for terminator) */
616 if (lth) {
617 /* allocate mextra if necessary; otherwise get rid of old name */
618 if (!mon->mextra)
619 mon->mextra = newmextra();
620 else
621 free_mname(mon); /* already has mextra, might also have name */
622 MNAME(mon) = (char *) alloc((unsigned) lth);
623 } else {
624 /* zero length: the new name is empty; get rid of the old name */
625 if (has_mname(mon))
626 free_mname(mon);
630 /* release a monster's name; retains mextra even if all fields are now null */
631 void
632 free_mname(mon)
633 struct monst *mon;
635 if (has_mname(mon)) {
636 free((genericptr_t) MNAME(mon));
637 MNAME(mon) = (char *) 0;
641 /* allocate space for an object's name; removes old name if there is one */
642 void
643 new_oname(obj, lth)
644 struct obj *obj;
645 int lth; /* desired length (caller handles adding 1 for terminator) */
647 if (lth) {
648 /* allocate oextra if necessary; otherwise get rid of old name */
649 if (!obj->oextra)
650 obj->oextra = newoextra();
651 else
652 free_oname(obj); /* already has oextra, might also have name */
653 ONAME(obj) = (char *) alloc((unsigned) lth);
654 } else {
655 /* zero length: the new name is empty; get rid of the old name */
656 if (has_oname(obj))
657 free_oname(obj);
661 /* release an object's name; retains oextra even if all fields are now null */
662 void
663 free_oname(obj)
664 struct obj *obj;
666 if (has_oname(obj)) {
667 free((genericptr_t) ONAME(obj));
668 ONAME(obj) = (char *) 0;
672 /* safe_oname() always returns a valid pointer to
673 * a string, either the pointer to an object's name
674 * if it has one, or a pointer to an empty string
675 * if it doesn't.
677 const char *
678 safe_oname(obj)
679 struct obj *obj;
681 if (has_oname(obj))
682 return ONAME(obj);
683 return "";
686 /* historical note: this returns a monster pointer because it used to
687 allocate a new bigger block of memory to hold the monster and its name */
688 struct monst *
689 christen_monst(mtmp, name)
690 struct monst *mtmp;
691 const char *name;
693 int lth;
694 char buf[PL_PSIZ];
696 /* dogname & catname are PL_PSIZ arrays; object names have same limit */
697 lth = (name && *name) ? ((int) strlen(name) + 1) : 0;
698 if (lth > PL_PSIZ) {
699 lth = PL_PSIZ;
700 name = strncpy(buf, name, PL_PSIZ - 1);
701 buf[PL_PSIZ - 1] = '\0';
703 new_mname(mtmp, lth); /* removes old name if one is present */
704 if (lth)
705 Strcpy(MNAME(mtmp), name);
706 return mtmp;
709 /* check whether user-supplied name matches or nearly matches an unnameable
710 monster's name; if so, give an alternate reject message for do_mname() */
711 STATIC_OVL boolean
712 alreadynamed(mtmp, monnambuf, usrbuf)
713 struct monst *mtmp;
714 char *monnambuf, *usrbuf;
716 char pronounbuf[10], *p;
718 if (fuzzymatch(usrbuf, monnambuf, " -_", TRUE)
719 /* catch trying to name "the Oracle" as "Oracle" */
720 || (!strncmpi(monnambuf, "the ", 4)
721 && fuzzymatch(usrbuf, monnambuf + 4, " -_", TRUE))
722 /* catch trying to name "invisible Orcus" as "Orcus" */
723 || ((p = strstri(monnambuf, "invisible ")) != 0
724 && fuzzymatch(usrbuf, p + 10, " -_", TRUE))
725 /* catch trying to name "the {priest,Angel} of Crom" as "Crom" */
726 || ((p = strstri(monnambuf, " of ")) != 0
727 && fuzzymatch(usrbuf, p + 4, " -_", TRUE))) {
728 pline("%s is already called %s.",
729 upstart(strcpy(pronounbuf, mhe(mtmp))), monnambuf);
730 return TRUE;
731 } else if (mtmp->data == &mons[PM_JUIBLEX]
732 && strstri(monnambuf, "Juiblex")
733 && !strcmpi(usrbuf, "Jubilex")) {
734 pline("%s doesn't like being called %s.", upstart(monnambuf), usrbuf);
735 return TRUE;
737 return FALSE;
740 /* allow player to assign a name to some chosen monster */
741 STATIC_OVL void
742 do_mname()
744 char buf[BUFSZ], monnambuf[BUFSZ], qbuf[QBUFSZ];
745 coord cc;
746 int cx, cy;
747 struct monst *mtmp = 0;
749 if (Hallucination) {
750 You("would never recognize it anyway.");
751 return;
753 cc.x = u.ux;
754 cc.y = u.uy;
755 if (getpos(&cc, FALSE, "the monster you want to name") < 0
756 || (cx = cc.x) < 0)
757 return;
758 cy = cc.y;
760 if (cx == u.ux && cy == u.uy) {
761 if (u.usteed && canspotmon(u.usteed)) {
762 mtmp = u.usteed;
763 } else {
764 pline("This %s creature is called %s and cannot be renamed.",
765 beautiful(), plname);
766 return;
768 } else
769 mtmp = m_at(cx, cy);
771 if (!mtmp
772 || (!sensemon(mtmp)
773 && (!(cansee(cx, cy) || see_with_infrared(mtmp))
774 || mtmp->mundetected || mtmp->m_ap_type == M_AP_FURNITURE
775 || mtmp->m_ap_type == M_AP_OBJECT
776 || (mtmp->minvis && !See_invisible)))) {
777 pline("I see no monster there.");
778 return;
780 /* special case similar to the one in lookat() */
781 Sprintf(qbuf, "What do you want to call %s?",
782 distant_monnam(mtmp, ARTICLE_THE, monnambuf));
783 getlin(qbuf, buf);
784 if (!*buf || *buf == '\033')
785 return;
786 /* strip leading and trailing spaces; unnames monster if all spaces */
787 (void) mungspaces(buf);
789 /* Unique monsters have their own specific names or titles.
790 * Shopkeepers, temple priests and other minions use alternate
791 * name formatting routines which ignore any user-supplied name.
793 * Don't say the name is being rejected if it happens to match
794 * the existing name.
796 if ((mtmp->data->geno & G_UNIQ) && !mtmp->ispriest) {
797 if (!alreadynamed(mtmp, monnambuf, buf))
798 pline("%s doesn't like being called names!", upstart(monnambuf));
799 } else if (mtmp->isshk
800 && !(Deaf || mtmp->msleeping || !mtmp->mcanmove
801 || mtmp->data->msound <= MS_ANIMAL)) {
802 if (!alreadynamed(mtmp, monnambuf, buf))
803 verbalize("I'm %s, not %s.", shkname(mtmp), buf);
804 } else if (mtmp->ispriest || mtmp->isminion || mtmp->isshk) {
805 if (!alreadynamed(mtmp, monnambuf, buf))
806 pline("%s will not accept the name %s.", upstart(monnambuf), buf);
807 } else
808 (void) christen_monst(mtmp, buf);
812 * This routine changes the address of obj. Be careful not to call it
813 * when there might be pointers around in unknown places. For now: only
814 * when obj is in the inventory.
816 STATIC_OVL
817 void
818 do_oname(obj)
819 register struct obj *obj;
821 char *bufp, buf[BUFSZ], bufcpy[BUFSZ], qbuf[QBUFSZ];
822 const char *aname;
823 short objtyp;
825 /* Do this now because there's no point in even asking for a name */
826 if (obj->otyp == SPE_NOVEL) {
827 pline("%s already has a published name.", Ysimple_name2(obj));
828 return;
831 Sprintf(qbuf, "What do you want to name %s ",
832 is_plural(obj) ? "these" : "this");
833 (void) safe_qbuf(qbuf, qbuf, "?", obj, xname, simpleonames, "item");
834 getlin(qbuf, buf);
835 if (!*buf || *buf == '\033')
836 return;
837 /* strip leading and trailing spaces; unnames item if all spaces */
838 (void) mungspaces(buf);
841 * We don't violate illiteracy conduct here, although it is
842 * arguable that we should for anything other than "X". Doing so
843 * would make attaching player's notes to hero's inventory have an
844 * in-game effect, which may or may not be the correct thing to do.
846 * We do violate illiteracy in oname() if player creates Sting or
847 * Orcrist, clearly being literate (no pun intended...).
850 /* relax restrictions over proper capitalization for artifacts */
851 if ((aname = artifact_name(buf, &objtyp)) != 0 && objtyp == obj->otyp)
852 Strcpy(buf, aname);
854 if (obj->oartifact) {
855 pline_The("artifact seems to resist the attempt.");
856 return;
857 } else if (restrict_name(obj, buf) || exist_artifact(obj->otyp, buf)) {
858 /* this used to change one letter, substituting a value
859 of 'a' through 'y' (due to an off by one error, 'z'
860 would never be selected) and then force that to
861 upper case if such was the case of the input;
862 now, the hand slip scuffs one or two letters as if
863 the text had been trodden upon, sometimes picking
864 punctuation instead of an arbitrary letter;
865 unfortunately, we have to cover the possibility of
866 it targetting spaces so failing to make any change
867 (we know that it must eventually target a nonspace
868 because buf[] matches a valid artifact name) */
869 Strcpy(bufcpy, buf);
870 /* for "the Foo of Bar", only scuff "Foo of Bar" part */
871 bufp = !strncmpi(bufcpy, "the ", 4) ? (buf + 4) : buf;
872 do {
873 wipeout_text(bufp, rnd(2), (unsigned) 0);
874 } while (!strcmp(buf, bufcpy));
875 pline("While engraving, your %s slips.", body_part(HAND));
876 display_nhwindow(WIN_MESSAGE, FALSE);
877 You("engrave: \"%s\".", buf);
879 obj = oname(obj, buf);
882 struct obj *
883 oname(obj, name)
884 struct obj *obj;
885 const char *name;
887 int lth;
888 char buf[PL_PSIZ];
890 lth = *name ? (int) (strlen(name) + 1) : 0;
891 if (lth > PL_PSIZ) {
892 lth = PL_PSIZ;
893 name = strncpy(buf, name, PL_PSIZ - 1);
894 buf[PL_PSIZ - 1] = '\0';
896 /* If named artifact exists in the game, do not create another.
897 * Also trying to create an artifact shouldn't de-artifact
898 * it (e.g. Excalibur from prayer). In this case the object
899 * will retain its current name. */
900 if (obj->oartifact || (lth && exist_artifact(obj->otyp, name)))
901 return obj;
903 new_oname(obj, lth); /* removes old name if one is present */
904 if (lth)
905 Strcpy(ONAME(obj), name);
907 if (lth)
908 artifact_exists(obj, name, TRUE);
909 if (obj->oartifact) {
910 /* can't dual-wield with artifact as secondary weapon */
911 if (obj == uswapwep)
912 untwoweapon();
913 /* activate warning if you've just named your weapon "Sting" */
914 if (obj == uwep)
915 set_artifact_intrinsic(obj, TRUE, W_WEP);
916 /* if obj is owned by a shop, increase your bill */
917 if (obj->unpaid)
918 alter_cost(obj, 0L);
919 /* violate illiteracy conduct since successfully wrote arti-name */
920 u.uconduct.literate++;
922 if (carried(obj))
923 update_inventory();
924 return obj;
927 static NEARDATA const char callable[] = {
928 SCROLL_CLASS, POTION_CLASS, WAND_CLASS, RING_CLASS, AMULET_CLASS,
929 GEM_CLASS, SPBOOK_CLASS, ARMOR_CLASS, TOOL_CLASS, 0
932 boolean
933 objtyp_is_callable(i)
934 int i;
936 return (boolean) (objects[i].oc_uname
937 || (OBJ_DESCR(objects[i])
938 && index(callable, objects[i].oc_class)));
941 /* C and #name commands - player can name monster or object or type of obj */
943 docallcmd()
945 struct obj *obj;
946 winid win;
947 anything any;
948 menu_item *pick_list = 0;
949 char ch, allowall[2];
950 /* if player wants a,b,c instead of i,o when looting, do that here too */
951 boolean abc = flags.lootabc;
953 win = create_nhwindow(NHW_MENU);
954 start_menu(win);
955 any = zeroany;
956 any.a_char = 'm'; /* group accelerator 'C' */
957 add_menu(win, NO_GLYPH, &any, abc ? 0 : any.a_char, 'C', ATR_NONE,
958 "a monster", MENU_UNSELECTED);
959 if (invent) {
960 /* we use y and n as accelerators so that we can accept user's
961 response keyed to old "name an individual object?" prompt */
962 any.a_char = 'i'; /* group accelerator 'y' */
963 add_menu(win, NO_GLYPH, &any, abc ? 0 : any.a_char, 'y', ATR_NONE,
964 "a particular object in inventory", MENU_UNSELECTED);
965 any.a_char = 'o'; /* group accelerator 'n' */
966 add_menu(win, NO_GLYPH, &any, abc ? 0 : any.a_char, 'n', ATR_NONE,
967 "the type of an object in inventory", MENU_UNSELECTED);
969 any.a_char = 'f'; /* group accelerator ',' (or ':' instead?) */
970 add_menu(win, NO_GLYPH, &any, abc ? 0 : any.a_char, ',', ATR_NONE,
971 "the type of an object upon the floor", MENU_UNSELECTED);
972 any.a_char = 'd'; /* group accelerator '\' */
973 add_menu(win, NO_GLYPH, &any, abc ? 0 : any.a_char, '\\', ATR_NONE,
974 "the type of an object on discoveries list", MENU_UNSELECTED);
975 any.a_char = 'a'; /* group accelerator 'l' */
976 add_menu(win, NO_GLYPH, &any, abc ? 0 : any.a_char, 'l', ATR_NONE,
977 "record an annotation for the current level", MENU_UNSELECTED);
978 end_menu(win, "What do you want to name?");
979 if (select_menu(win, PICK_ONE, &pick_list) > 0) {
980 ch = pick_list[0].item.a_char;
981 free((genericptr_t) pick_list);
982 } else
983 ch = 'q';
984 destroy_nhwindow(win);
986 switch (ch) {
987 default:
988 case 'q':
989 break;
990 case 'm': /* name a visible monster */
991 do_mname();
992 break;
993 case 'i': /* name an individual object in inventory */
994 allowall[0] = ALL_CLASSES;
995 allowall[1] = '\0';
996 obj = getobj(allowall, "name");
997 if (obj)
998 do_oname(obj);
999 break;
1000 case 'o': /* name a type of object in inventory */
1001 obj = getobj(callable, "call");
1002 if (obj) {
1003 /* behave as if examining it in inventory;
1004 this might set dknown if it was picked up
1005 while blind and the hero can now see */
1006 (void) xname(obj);
1008 if (!obj->dknown) {
1009 You("would never recognize another one.");
1010 #if 0
1011 } else if (!objtyp_is_callable(obj->otyp)) {
1012 You("know those as well as you ever will.");
1013 #endif
1014 } else {
1015 docall(obj);
1018 break;
1019 case 'f': /* name a type of object visible on the floor */
1020 namefloorobj();
1021 break;
1022 case 'd': /* name a type of object on the discoveries list */
1023 rename_disco();
1024 break;
1025 case 'a': /* annotate level */
1026 donamelevel();
1027 break;
1029 return 0;
1032 void
1033 docall(obj)
1034 register struct obj *obj;
1036 char buf[BUFSZ], qbuf[QBUFSZ];
1037 struct obj otemp;
1038 register char **str1;
1040 if (!obj->dknown)
1041 return; /* probably blind */
1042 otemp = *obj;
1043 otemp.quan = 1L;
1044 otemp.oextra = (struct oextra *) 0;
1046 if (objects[otemp.otyp].oc_class == POTION_CLASS && otemp.fromsink)
1047 /* kludge, meaning it's sink water */
1048 Sprintf(qbuf, "Call a stream of %s fluid:",
1049 OBJ_DESCR(objects[otemp.otyp]));
1050 else
1051 Sprintf(qbuf, "Call %s:", an(xname(&otemp)));
1052 getlin(qbuf, buf);
1053 if (!*buf || *buf == '\033')
1054 return;
1056 /* clear old name */
1057 str1 = &(objects[obj->otyp].oc_uname);
1058 if (*str1)
1059 free((genericptr_t) *str1);
1061 /* strip leading and trailing spaces; uncalls item if all spaces */
1062 (void) mungspaces(buf);
1063 if (!*buf) {
1064 if (*str1) { /* had name, so possibly remove from disco[] */
1065 /* strip name first, for the update_inventory() call
1066 from undiscover_object() */
1067 *str1 = (char *) 0;
1068 undiscover_object(obj->otyp);
1070 } else {
1071 *str1 = dupstr(buf);
1072 discover_object(obj->otyp, FALSE, TRUE); /* possibly add to disco[] */
1076 STATIC_OVL void
1077 namefloorobj()
1079 coord cc;
1080 int glyph;
1081 char buf[BUFSZ];
1082 struct obj *obj = 0;
1083 boolean fakeobj = FALSE, use_plural;
1085 cc.x = u.ux, cc.y = u.uy;
1086 /* "dot for under/over you" only makes sense when the cursor hasn't
1087 been moved off the hero's '@' yet, but there's no way to adjust
1088 the help text once getpos() has started */
1089 Sprintf(buf, "object on map (or '.' for one %s you)",
1090 (u.uundetected && hides_under(youmonst.data)) ? "over" : "under");
1091 if (getpos(&cc, FALSE, buf) < 0 || cc.x <= 0)
1092 return;
1093 if (cc.x == u.ux && cc.y == u.uy) {
1094 obj = vobj_at(u.ux, u.uy);
1095 } else {
1096 glyph = glyph_at(cc.x, cc.y);
1097 if (glyph_is_object(glyph))
1098 fakeobj = object_from_map(glyph, cc.x, cc.y, &obj);
1099 /* else 'obj' stays null */
1101 if (!obj) {
1102 /* "under you" is safe here since there's no object to hide under */
1103 pline("There doesn't seem to be any object %s.",
1104 (cc.x == u.ux && cc.y == u.uy) ? "under you" : "there");
1105 return;
1107 /* note well: 'obj' might be as instance of STRANGE_OBJECT if target
1108 is a mimic; passing that to xname (directly or via simpleonames)
1109 would yield "glorkum" so we need to handle it explicitly; it will
1110 always fail the Hallucination test and pass the !callable test,
1111 resulting in the "can't be assigned a type name" message */
1112 Strcpy(buf, (obj->otyp != STRANGE_OBJECT)
1113 ? simpleonames(obj)
1114 : obj_descr[STRANGE_OBJECT].oc_name);
1115 use_plural = (obj->quan > 1L);
1116 if (Hallucination) {
1117 const char *unames[6];
1118 char tmpbuf[BUFSZ];
1120 /* straight role name */
1121 unames[0] = ((Upolyd ? u.mfemale : flags.female) && urole.name.f)
1122 ? urole.name.f
1123 : urole.name.m;
1124 /* random rank title for hero's role */
1125 unames[1] = rank_of(rnd(30), Role_switch, flags.female);
1126 /* random fake monster */
1127 unames[2] = bogusmon(tmpbuf, (char *) 0);
1128 /* increased chance for fake monster */
1129 unames[3] = unames[2];
1130 /* traditional */
1131 unames[4] = roguename();
1132 /* silly */
1133 unames[5] = "Wibbly Wobbly";
1134 pline("%s %s to call you \"%s.\"",
1135 The(buf), use_plural ? "decide" : "decides",
1136 unames[rn2(SIZE(unames))]);
1137 } else if (!objtyp_is_callable(obj->otyp)) {
1138 pline("%s %s can't be assigned a type name.",
1139 use_plural ? "Those" : "That", buf);
1140 } else if (!obj->dknown) {
1141 You("don't know %s %s well enough to name %s.",
1142 use_plural ? "those" : "that", buf, use_plural ? "them" : "it");
1143 } else {
1144 docall(obj);
1146 if (fakeobj)
1147 dealloc_obj(obj);
1150 static const char *const ghostnames[] = {
1151 /* these names should have length < PL_NSIZ */
1152 /* Capitalize the names for aesthetics -dgk */
1153 "Adri", "Andries", "Andreas", "Bert", "David", "Dirk",
1154 "Emile", "Frans", "Fred", "Greg", "Hether", "Jay",
1155 "John", "Jon", "Karnov", "Kay", "Kenny", "Kevin",
1156 "Maud", "Michiel", "Mike", "Peter", "Robert", "Ron",
1157 "Tom", "Wilmar", "Nick Danger", "Phoenix", "Jiro", "Mizue",
1158 "Stephan", "Lance Braccus", "Shadowhawk"
1161 /* ghost names formerly set by x_monnam(), now by makemon() instead */
1162 const char *
1163 rndghostname()
1165 return rn2(7) ? ghostnames[rn2(SIZE(ghostnames))] : (const char *) plname;
1169 * Monster naming functions:
1170 * x_monnam is the generic monster-naming function.
1171 * seen unseen detected named
1172 * mon_nam: the newt it the invisible orc Fido
1173 * noit_mon_nam:the newt (as if detected) the invisible orc Fido
1174 * l_monnam: newt it invisible orc dog called Fido
1175 * Monnam: The newt It The invisible orc Fido
1176 * noit_Monnam: The newt (as if detected) The invisible orc Fido
1177 * Adjmonnam: The poor newt It The poor invisible orc The poor Fido
1178 * Amonnam: A newt It An invisible orc Fido
1179 * a_monnam: a newt it an invisible orc Fido
1180 * m_monnam: newt xan orc Fido
1181 * y_monnam: your newt your xan your invisible orc Fido
1184 /* Bug: if the monster is a priest or shopkeeper, not every one of these
1185 * options works, since those are special cases.
1187 char *
1188 x_monnam(mtmp, article, adjective, suppress, called)
1189 register struct monst *mtmp;
1190 int article;
1191 /* ARTICLE_NONE, ARTICLE_THE, ARTICLE_A: obvious
1192 * ARTICLE_YOUR: "your" on pets, "the" on everything else
1194 * If the monster would be referred to as "it" or if the monster has a name
1195 * _and_ there is no adjective, "invisible", "saddled", etc., override this
1196 * and always use no article.
1198 const char *adjective;
1199 int suppress;
1200 /* SUPPRESS_IT, SUPPRESS_INVISIBLE, SUPPRESS_HALLUCINATION, SUPPRESS_SADDLE.
1201 * EXACT_NAME: combination of all the above
1203 boolean called;
1205 char *buf = nextmbuf();
1206 struct permonst *mdat = mtmp->data;
1207 const char *pm_name = mdat->mname;
1208 boolean do_hallu, do_invis, do_it, do_saddle;
1209 boolean name_at_start, has_adjectives;
1210 char *bp;
1212 if (program_state.gameover)
1213 suppress |= SUPPRESS_HALLUCINATION;
1214 if (article == ARTICLE_YOUR && !mtmp->mtame)
1215 article = ARTICLE_THE;
1217 do_hallu = Hallucination && !(suppress & SUPPRESS_HALLUCINATION);
1218 do_invis = mtmp->minvis && !(suppress & SUPPRESS_INVISIBLE);
1219 do_it = !canspotmon(mtmp) && article != ARTICLE_YOUR
1220 && !program_state.gameover && mtmp != u.usteed
1221 && !(u.uswallow && mtmp == u.ustuck) && !(suppress & SUPPRESS_IT);
1222 do_saddle = !(suppress & SUPPRESS_SADDLE);
1224 buf[0] = '\0';
1226 /* unseen monsters, etc. Use "it" */
1227 if (do_it) {
1228 Strcpy(buf, "it");
1229 return buf;
1232 /* priests and minions: don't even use this function */
1233 if (mtmp->ispriest || mtmp->isminion) {
1234 char priestnambuf[BUFSZ];
1235 char *name;
1236 long save_prop = EHalluc_resistance;
1237 unsigned save_invis = mtmp->minvis;
1239 /* when true name is wanted, explicitly block Hallucination */
1240 if (!do_hallu)
1241 EHalluc_resistance = 1L;
1242 if (!do_invis)
1243 mtmp->minvis = 0;
1244 name = priestname(mtmp, priestnambuf);
1245 EHalluc_resistance = save_prop;
1246 mtmp->minvis = save_invis;
1247 if (article == ARTICLE_NONE && !strncmp(name, "the ", 4))
1248 name += 4;
1249 return strcpy(buf, name);
1251 /* an "aligned priest" not flagged as a priest or minion should be
1252 "priest" or "priestess" (normally handled by priestname()) */
1253 if (mdat == &mons[PM_ALIGNED_PRIEST])
1254 pm_name = mtmp->female ? "priestess" : "priest";
1255 else if (mdat == &mons[PM_HIGH_PRIEST] && mtmp->female)
1256 pm_name = "high priestess";
1258 /* Shopkeepers: use shopkeeper name. For normal shopkeepers, just
1259 * "Asidonhopo"; for unusual ones, "Asidonhopo the invisible
1260 * shopkeeper" or "Asidonhopo the blue dragon". If hallucinating,
1261 * none of this applies.
1263 if (mtmp->isshk && !do_hallu) {
1264 if (adjective && article == ARTICLE_THE) {
1265 /* pathological case: "the angry Asidonhopo the blue dragon"
1266 sounds silly */
1267 Strcpy(buf, "the ");
1268 Strcat(strcat(buf, adjective), " ");
1269 Strcat(buf, shkname(mtmp));
1270 return buf;
1272 Strcat(buf, shkname(mtmp));
1273 if (mdat == &mons[PM_SHOPKEEPER] && !do_invis)
1274 return buf;
1275 Strcat(buf, " the ");
1276 if (do_invis)
1277 Strcat(buf, "invisible ");
1278 Strcat(buf, pm_name);
1279 return buf;
1282 /* Put the adjectives in the buffer */
1283 if (adjective)
1284 Strcat(strcat(buf, adjective), " ");
1285 if (do_invis)
1286 Strcat(buf, "invisible ");
1287 if (do_saddle && (mtmp->misc_worn_check & W_SADDLE) && !Blind
1288 && !Hallucination)
1289 Strcat(buf, "saddled ");
1290 if (buf[0] != 0)
1291 has_adjectives = TRUE;
1292 else
1293 has_adjectives = FALSE;
1295 /* Put the actual monster name or type into the buffer now */
1296 /* Be sure to remember whether the buffer starts with a name */
1297 if (do_hallu) {
1298 char rnamecode;
1299 char *rname = rndmonnam(&rnamecode);
1301 Strcat(buf, rname);
1302 name_at_start = bogon_is_pname(rnamecode);
1303 } else if (has_mname(mtmp)) {
1304 char *name = MNAME(mtmp);
1306 if (mdat == &mons[PM_GHOST]) {
1307 Sprintf(eos(buf), "%s ghost", s_suffix(name));
1308 name_at_start = TRUE;
1309 } else if (called) {
1310 Sprintf(eos(buf), "%s called %s", pm_name, name);
1311 name_at_start = (boolean) type_is_pname(mdat);
1312 } else if (is_mplayer(mdat) && (bp = strstri(name, " the ")) != 0) {
1313 /* <name> the <adjective> <invisible> <saddled> <rank> */
1314 char pbuf[BUFSZ];
1316 Strcpy(pbuf, name);
1317 pbuf[bp - name + 5] = '\0'; /* adjectives right after " the " */
1318 if (has_adjectives)
1319 Strcat(pbuf, buf);
1320 Strcat(pbuf, bp + 5); /* append the rest of the name */
1321 Strcpy(buf, pbuf);
1322 article = ARTICLE_NONE;
1323 name_at_start = TRUE;
1324 } else {
1325 Strcat(buf, name);
1326 name_at_start = TRUE;
1328 } else if (is_mplayer(mdat) && !In_endgame(&u.uz)) {
1329 char pbuf[BUFSZ];
1331 Strcpy(pbuf, rank_of((int) mtmp->m_lev, monsndx(mdat),
1332 (boolean) mtmp->female));
1333 Strcat(buf, lcase(pbuf));
1334 name_at_start = FALSE;
1335 } else {
1336 Strcat(buf, pm_name);
1337 name_at_start = (boolean) type_is_pname(mdat);
1340 if (name_at_start && (article == ARTICLE_YOUR || !has_adjectives)) {
1341 if (mdat == &mons[PM_WIZARD_OF_YENDOR])
1342 article = ARTICLE_THE;
1343 else
1344 article = ARTICLE_NONE;
1345 } else if ((mdat->geno & G_UNIQ) && article == ARTICLE_A) {
1346 article = ARTICLE_THE;
1350 char buf2[BUFSZ];
1352 switch (article) {
1353 case ARTICLE_YOUR:
1354 Strcpy(buf2, "your ");
1355 Strcat(buf2, buf);
1356 Strcpy(buf, buf2);
1357 return buf;
1358 case ARTICLE_THE:
1359 Strcpy(buf2, "the ");
1360 Strcat(buf2, buf);
1361 Strcpy(buf, buf2);
1362 return buf;
1363 case ARTICLE_A:
1364 return an(buf);
1365 case ARTICLE_NONE:
1366 default:
1367 return buf;
1372 char *
1373 l_monnam(mtmp)
1374 struct monst *mtmp;
1376 return x_monnam(mtmp, ARTICLE_NONE, (char *) 0,
1377 (has_mname(mtmp)) ? SUPPRESS_SADDLE : 0, TRUE);
1380 char *
1381 mon_nam(mtmp)
1382 struct monst *mtmp;
1384 return x_monnam(mtmp, ARTICLE_THE, (char *) 0,
1385 (has_mname(mtmp)) ? SUPPRESS_SADDLE : 0, FALSE);
1388 /* print the name as if mon_nam() was called, but assume that the player
1389 * can always see the monster--used for probing and for monsters aggravating
1390 * the player with a cursed potion of invisibility
1392 char *
1393 noit_mon_nam(mtmp)
1394 struct monst *mtmp;
1396 return x_monnam(mtmp, ARTICLE_THE, (char *) 0,
1397 (has_mname(mtmp)) ? (SUPPRESS_SADDLE | SUPPRESS_IT)
1398 : SUPPRESS_IT,
1399 FALSE);
1402 char *
1403 Monnam(mtmp)
1404 struct monst *mtmp;
1406 register char *bp = mon_nam(mtmp);
1408 *bp = highc(*bp);
1409 return bp;
1412 char *
1413 noit_Monnam(mtmp)
1414 struct monst *mtmp;
1416 register char *bp = noit_mon_nam(mtmp);
1418 *bp = highc(*bp);
1419 return bp;
1422 /* monster's own name */
1423 char *
1424 m_monnam(mtmp)
1425 struct monst *mtmp;
1427 return x_monnam(mtmp, ARTICLE_NONE, (char *) 0, EXACT_NAME, FALSE);
1430 /* pet name: "your little dog" */
1431 char *
1432 y_monnam(mtmp)
1433 struct monst *mtmp;
1435 int prefix, suppression_flag;
1437 prefix = mtmp->mtame ? ARTICLE_YOUR : ARTICLE_THE;
1438 suppression_flag = (has_mname(mtmp)
1439 /* "saddled" is redundant when mounted */
1440 || mtmp == u.usteed)
1441 ? SUPPRESS_SADDLE
1442 : 0;
1444 return x_monnam(mtmp, prefix, (char *) 0, suppression_flag, FALSE);
1447 char *
1448 Adjmonnam(mtmp, adj)
1449 struct monst *mtmp;
1450 const char *adj;
1452 char *bp = x_monnam(mtmp, ARTICLE_THE, adj,
1453 has_mname(mtmp) ? SUPPRESS_SADDLE : 0, FALSE);
1455 *bp = highc(*bp);
1456 return bp;
1459 char *
1460 a_monnam(mtmp)
1461 struct monst *mtmp;
1463 return x_monnam(mtmp, ARTICLE_A, (char *) 0,
1464 has_mname(mtmp) ? SUPPRESS_SADDLE : 0, FALSE);
1467 char *
1468 Amonnam(mtmp)
1469 struct monst *mtmp;
1471 char *bp = a_monnam(mtmp);
1473 *bp = highc(*bp);
1474 return bp;
1477 /* used for monster ID by the '/', ';', and 'C' commands to block remote
1478 identification of the endgame altars via their attending priests */
1479 char *
1480 distant_monnam(mon, article, outbuf)
1481 struct monst *mon;
1482 int article; /* only ARTICLE_NONE and ARTICLE_THE are handled here */
1483 char *outbuf;
1485 /* high priest(ess)'s identity is concealed on the Astral Plane,
1486 unless you're adjacent (overridden for hallucination which does
1487 its own obfuscation) */
1488 if (mon->data == &mons[PM_HIGH_PRIEST] && !Hallucination
1489 && Is_astralevel(&u.uz) && distu(mon->mx, mon->my) > 2) {
1490 Strcpy(outbuf, article == ARTICLE_THE ? "the " : "");
1491 Strcat(outbuf, mon->female ? "high priestess" : "high priest");
1492 } else {
1493 Strcpy(outbuf, x_monnam(mon, article, (char *) 0, 0, TRUE));
1495 return outbuf;
1498 /* fake monsters used to be in a hard-coded array, now in a data file */
1499 STATIC_OVL char *
1500 bogusmon(buf, code)
1501 char *buf, *code;
1503 char *mname = buf;
1505 get_rnd_text(BOGUSMONFILE, buf);
1506 /* strip prefix if present */
1507 if (!letter(*mname)) {
1508 if (code)
1509 *code = *mname;
1510 ++mname;
1511 } else {
1512 if (code)
1513 *code = '\0';
1515 return mname;
1518 /* return a random monster name, for hallucination */
1519 char *
1520 rndmonnam(code)
1521 char *code;
1523 static char buf[BUFSZ];
1524 char *mname;
1525 int name;
1526 #define BOGUSMONSIZE 100 /* arbitrary */
1528 if (code)
1529 *code = '\0';
1531 do {
1532 name = rn1(SPECIAL_PM + BOGUSMONSIZE - LOW_PM, LOW_PM);
1533 } while (name < SPECIAL_PM
1534 && (type_is_pname(&mons[name]) || (mons[name].geno & G_NOGEN)));
1536 if (name >= SPECIAL_PM) {
1537 mname = bogusmon(buf, code);
1538 } else {
1539 mname = strcpy(buf, mons[name].mname);
1541 return mname;
1542 #undef BOGUSMONSIZE
1545 /* check bogusmon prefix to decide whether it's a personal name */
1546 boolean
1547 bogon_is_pname(code)
1548 char code;
1550 if (!code)
1551 return FALSE;
1552 return index("-+=", code) ? TRUE : FALSE;
1555 /* name of a Rogue player */
1556 const char *
1557 roguename()
1559 char *i, *opts;
1561 if ((opts = nh_getenv("ROGUEOPTS")) != 0) {
1562 for (i = opts; *i; i++)
1563 if (!strncmp("name=", i, 5)) {
1564 char *j;
1565 if ((j = index(i + 5, ',')) != 0)
1566 *j = (char) 0;
1567 return i + 5;
1570 return rn2(3) ? (rn2(2) ? "Michael Toy" : "Kenneth Arnold")
1571 : "Glenn Wichman";
1574 static NEARDATA const char *const hcolors[] = {
1575 "ultraviolet", "infrared", "bluish-orange", "reddish-green", "dark white",
1576 "light black", "sky blue-pink", "salty", "sweet", "sour", "bitter",
1577 "striped", "spiral", "swirly", "plaid", "checkered", "argyle", "paisley",
1578 "blotchy", "guernsey-spotted", "polka-dotted", "square", "round",
1579 "triangular", "cabernet", "sangria", "fuchsia", "wisteria", "lemon-lime",
1580 "strawberry-banana", "peppermint", "romantic", "incandescent",
1581 "octarine", /* Discworld: the Colour of Magic */
1584 const char *
1585 hcolor(colorpref)
1586 const char *colorpref;
1588 return (Hallucination || !colorpref) ? hcolors[rn2(SIZE(hcolors))]
1589 : colorpref;
1592 /* return a random real color unless hallucinating */
1593 const char *
1594 rndcolor()
1596 int k = rn2(CLR_MAX);
1598 return Hallucination ? hcolor((char *) 0)
1599 : (k == NO_COLOR) ? "colorless"
1600 : c_obj_colors[k];
1603 static NEARDATA const char *const hliquids[] = {
1604 "yoghurt", "oobleck", "clotted blood", "diluted water", "purified water",
1605 "instant coffee", "tea", "herbal infusion", "liquid rainbow",
1606 "creamy foam", "mulled wine", "bouillon", "nectar", "grog", "flubber",
1607 "ketchup", "slow light", "oil", "vinaigrette", "liquid crystal", "honey",
1608 "caramel sauce", "ink", "aqueous humour", "milk substitute", "fruit juice",
1609 "glowing lava", "gastric acid", "mineral water", "cough syrup", "quicksilver",
1610 "sweet vitriol", "grey goo", "pink slime",
1613 const char *
1614 hliquid(liquidpref)
1615 const char *liquidpref;
1617 return (Hallucination || !liquidpref) ? hliquids[rn2(SIZE(hliquids))]
1618 : liquidpref;
1621 /* Aliases for road-runner nemesis
1623 static const char *const coynames[] = {
1624 "Carnivorous Vulgaris", "Road-Runnerus Digestus", "Eatibus Anythingus",
1625 "Famishus-Famishus", "Eatibus Almost Anythingus", "Eatius Birdius",
1626 "Famishius Fantasticus", "Eternalii Famishiis", "Famishus Vulgarus",
1627 "Famishius Vulgaris Ingeniusi", "Eatius-Slobbius", "Hardheadipus Oedipus",
1628 "Carnivorous Slobbius", "Hard-Headipus Ravenus", "Evereadii Eatibus",
1629 "Apetitius Giganticus", "Hungrii Flea-Bagius", "Overconfidentii Vulgaris",
1630 "Caninus Nervous Rex", "Grotesques Appetitus", "Nemesis Ridiculii",
1631 "Canis latrans"
1634 char *
1635 coyotename(mtmp, buf)
1636 struct monst *mtmp;
1637 char *buf;
1639 if (mtmp && buf) {
1640 Sprintf(buf, "%s - %s",
1641 x_monnam(mtmp, ARTICLE_NONE, (char *) 0, 0, TRUE),
1642 mtmp->mcan ? coynames[SIZE(coynames) - 1]
1643 : coynames[mtmp->m_id % (SIZE(coynames) - 1)]);
1645 return buf;
1648 /* make sure "The Colour of Magic" remains the first entry in here */
1649 static const char *const sir_Terry_novels[] = {
1650 "The Colour of Magic", "The Light Fantastic", "Equal Rites", "Mort",
1651 "Sourcery", "Wyrd Sisters", "Pyramids", "Guards! Guards!", "Eric",
1652 "Moving Pictures", "Reaper Man", "Witches Abroad", "Small Gods",
1653 "Lords and Ladies", "Men at Arms", "Soul Music", "Interesting Times",
1654 "Maskerade", "Feet of Clay", "Hogfather", "Jingo", "The Last Continent",
1655 "Carpe Jugulum", "The Fifth Elephant", "The Truth", "Thief of Time",
1656 "The Last Hero", "The Amazing Maurice and his Educated Rodents",
1657 "Night Watch", "The Wee Free Men", "Monstrous Regiment",
1658 "A Hat Full of Sky", "Going Postal", "Thud!", "Wintersmith",
1659 "Making Money", "Unseen Academicals", "I Shall Wear Midnight", "Snuff",
1660 "Raising Steam", "The Shepherd's Crown"
1663 const char *
1664 noveltitle(novidx)
1665 int *novidx;
1667 int j, k = SIZE(sir_Terry_novels);
1669 j = rn2(k);
1670 if (novidx) {
1671 if (*novidx == -1)
1672 *novidx = j;
1673 else if (*novidx >= 0 && *novidx < k)
1674 j = *novidx;
1676 return sir_Terry_novels[j];
1679 const char *
1680 lookup_novel(lookname, idx)
1681 const char *lookname;
1682 int *idx;
1684 int k;
1686 /* Take American or U.K. spelling of this one */
1687 if (!strcmpi(The(lookname), "The Color of Magic"))
1688 lookname = sir_Terry_novels[0];
1690 for (k = 0; k < SIZE(sir_Terry_novels); ++k) {
1691 if (!strcmpi(lookname, sir_Terry_novels[k])
1692 || !strcmpi(The(lookname), sir_Terry_novels[k])) {
1693 if (idx)
1694 *idx = k;
1695 return sir_Terry_novels[k];
1698 /* name not found; if novelidx is already set, override the name */
1699 if (idx && *idx >= 0 && *idx < SIZE(sir_Terry_novels))
1700 return sir_Terry_novels[*idx];
1702 return (const char *) 0;
1705 /*do_name.c*/