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. */
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 *,
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 */
24 /* manage a pool of BUFSZ buffers, so callers don't have to */
28 static char NEARDATA bufs
[NUMMBUF
][BUFSZ
];
29 static int bufidx
= 0;
31 bufidx
= (bufidx
+ 1) % NUMMBUF
;
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;
42 void FDECL((*f
), (int));
44 getpos_hilitefunc
= f
;
47 /* the response for '?' help request in getpos() */
49 getpos_help(force
, goal
)
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 */
71 "Use 'd' or 'D' to move the cursor to next door or doorway.");
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... */
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
);
92 " ':' describe current spot, show 'more info', move to another spot.");
94 " '.' describe current spot,%s move to another spot;",
95 flags
.help
? " prompt if 'more info'," : "");
96 putstr(tmpwin
, 0, sbuf
);
98 " ',' describe current spot, move to another spot;");
100 " ';' describe current spot, stop looking at things;");
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
);
111 cmp_coord_distu(a
, b
)
117 int dx
, dy
, dist_1
, dist_2
;
121 dist_1
= max(abs(dx
), abs(dy
));
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
;
142 #define IS_UNEXPLORED_LOC(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)
149 gather_locs_interesting(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
);
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
));
165 return (glyph_is_object(glyph
)
166 && glyph
!= objnum_to_glyph(BOULDER
)
167 && glyph
!= objnum_to_glyph(ROCK
));
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
));
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)));
191 /* gather locations for monsters or objects shown on the map */
193 gather_locs(arr_p
, cnt_p
, gloc
)
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>.
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
)) {
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
);
235 dxdy_to_dist_descr(dx
, dy
, fulldir
)
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
));
248 const char *dirnames
[4][2] = {
254 /* 9999: protect buf[] against overflow caused by invalid values */
258 Sprintf(eos(buf
), "%d%s%s", abs(dy
), dirnames
[(dy
> 0)][fulldir
],
264 Sprintf(eos(buf
), "%d%s", abs(dx
), dirnames
[2 + (dx
> 0)][fulldir
]);
270 /* coordinate formatting for 'whatis_coord' option */
272 coord_desc(x
, y
, outbuf
, cmode
)
276 static char screen_fmt
[16]; /* [12] suffices: "[%02d,%02d]" */
283 case GPCOORDS_COMFULL
:
284 case GPCOORDS_COMPASS
:
285 /* "east", "3s", "2n,4w" */
288 Sprintf(outbuf
, "(%s)",
289 dxdy_to_dist_descr(dx
, dy
, cmode
== GPCOORDS_COMFULL
));
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
);
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 */
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
);
314 auto_describe(cx
, cy
)
320 const char *firstmatch
= "unknown";
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
);
335 getpos(ccp
, force
, goal
)
340 static const char pick_chars
[] = ".,;:",
341 mMoOdDxX
[] = "mMoOdDxX";
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
;
354 goal
= "desired location";
356 pline("(For instructions type a '?')");
364 curs(WIN_MAP
, cx
, cy
);
367 lock_mouse_cursor(TRUE
);
371 pline("Move cursor to %s:", goal
);
372 curs(WIN_MAP
, cx
, cy
);
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
);
382 (*getpos_hilitefunc
)(2);
383 hilite_state
= FALSE
;
384 curs(WIN_MAP
, cx
, cy
);
388 if (iflags
.autodescribe
)
393 msg_given
= TRUE
; /* force clear */
400 /* a mouse click event, just assign and return */
405 if ((cp
= index(pick_chars
, c
)) != 0) {
406 /* '.' => 0, ',' => 1, ';' => 2, ':' => 3 */
407 result
= (int) (cp
- pick_chars
);
410 for (i
= 0; i
< 8; i
++) {
413 if (Cmd
.dirchars
[i
] == c
) {
414 /* a normal movement letter or digit */
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 */
425 /* truncate at map edge; diagonal moves complicate this... */
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
;
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
;
445 if (c
== '?' || redraw_cmd(c
)) {
447 getpos_help(force
, goal
);
449 docrt(); /* redraw */
450 /* update message window to reflect that we're still targetting */
451 show_goal_msg
= TRUE
;
453 } else if (c
== '$' && getpos_hilitefunc
) {
455 (*getpos_hilitefunc
)(0);
456 (*getpos_hilitefunc
)(1);
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
;
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
++)
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 */
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
;
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
)
513 if (c
== defsyms
[sidx
].sym
|| c
== (int) showsyms
[sidx
])
514 matching
[sidx
] = (char) ++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
);
530 && matching
[glyph_to_cmap(k
)])
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
;
540 && matching
[glyph_to_cmap(k
)])
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
);
548 && matching
[glyph_to_cmap(k
)])
555 clear_nhwindow(WIN_MESSAGE
);
562 pline("Can't find dungeon feature '%c'.", c
);
569 Strcpy(note
, "aborted");
571 Sprintf(note
, "use '%c', '%c', '%c', '%c' or '.'", /* hjkl */
572 Cmd
.move_W
, Cmd
.move_S
, Cmd
.move_N
,
574 pline("Unknown direction: '%s' (%s).", visctrl((char) c
),
577 } /* k => matching */
582 msg_given
= FALSE
; /* suppress clear */
585 result
= 0; /* not -1 */
593 curs(WIN_MAP
, cx
, cy
);
597 lock_mouse_cursor(FALSE
);
600 clear_nhwindow(WIN_MESSAGE
);
603 for (i
= 0; i
< NUM_GLOCS
; i
++)
605 free((genericptr_t
) garr
[i
]);
606 getpos_hilitefunc
= (void FDECL((*), (int))) 0;
610 /* allocate space for a monster's name; removes old name if there is one */
614 int lth
; /* desired length (caller handles adding 1 for terminator) */
617 /* allocate mextra if necessary; otherwise get rid of old name */
619 mon
->mextra
= newmextra();
621 free_mname(mon
); /* already has mextra, might also have name */
622 MNAME(mon
) = (char *) alloc((unsigned) lth
);
624 /* zero length: the new name is empty; get rid of the old name */
630 /* release a monster's name; retains mextra even if all fields are now null */
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 */
645 int lth
; /* desired length (caller handles adding 1 for terminator) */
648 /* allocate oextra if necessary; otherwise get rid of old name */
650 obj
->oextra
= newoextra();
652 free_oname(obj
); /* already has oextra, might also have name */
653 ONAME(obj
) = (char *) alloc((unsigned) lth
);
655 /* zero length: the new name is empty; get rid of the old name */
661 /* release an object's name; retains oextra even if all fields are now null */
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
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 */
689 christen_monst(mtmp
, name
)
696 /* dogname & catname are PL_PSIZ arrays; object names have same limit */
697 lth
= (name
&& *name
) ? ((int) strlen(name
) + 1) : 0;
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 */
705 Strcpy(MNAME(mtmp
), name
);
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() */
712 alreadynamed(mtmp
, monnambuf
, usrbuf
)
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
);
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
);
740 /* allow player to assign a name to some chosen monster */
744 char buf
[BUFSZ
], monnambuf
[BUFSZ
], qbuf
[QBUFSZ
];
747 struct monst
*mtmp
= 0;
750 You("would never recognize it anyway.");
755 if (getpos(&cc
, FALSE
, "the monster you want to name") < 0
760 if (cx
== u
.ux
&& cy
== u
.uy
) {
761 if (u
.usteed
&& canspotmon(u
.usteed
)) {
764 pline("This %s creature is called %s and cannot be renamed.",
765 beautiful(), plname
);
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.");
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
));
784 if (!*buf
|| *buf
== '\033')
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
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
);
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.
819 register struct obj
*obj
;
821 char *bufp
, buf
[BUFSZ
], bufcpy
[BUFSZ
], qbuf
[QBUFSZ
];
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
));
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");
835 if (!*buf
|| *buf
== '\033')
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
)
854 if (obj
->oartifact
) {
855 pline_The("artifact seems to resist the attempt.");
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) */
870 /* for "the Foo of Bar", only scuff "Foo of Bar" part */
871 bufp
= !strncmpi(bufcpy
, "the ", 4) ? (buf
+ 4) : buf
;
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
);
890 lth
= *name
? (int) (strlen(name
) + 1) : 0;
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
)))
903 new_oname(obj
, lth
); /* removes old name if one is present */
905 Strcpy(ONAME(obj
), name
);
908 artifact_exists(obj
, name
, TRUE
);
909 if (obj
->oartifact
) {
910 /* can't dual-wield with artifact as secondary weapon */
913 /* activate warning if you've just named your weapon "Sting" */
915 set_artifact_intrinsic(obj
, TRUE
, W_WEP
);
916 /* if obj is owned by a shop, increase your bill */
919 /* violate illiteracy conduct since successfully wrote arti-name */
920 u
.uconduct
.literate
++;
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
933 objtyp_is_callable(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 */
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
);
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
);
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
);
984 destroy_nhwindow(win
);
990 case 'm': /* name a visible monster */
993 case 'i': /* name an individual object in inventory */
994 allowall
[0] = ALL_CLASSES
;
996 obj
= getobj(allowall
, "name");
1000 case 'o': /* name a type of object in inventory */
1001 obj
= getobj(callable
, "call");
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 */
1009 You("would never recognize another one.");
1011 } else if (!objtyp_is_callable(obj
->otyp
)) {
1012 You("know those as well as you ever will.");
1019 case 'f': /* name a type of object visible on the floor */
1022 case 'd': /* name a type of object on the discoveries list */
1025 case 'a': /* annotate level */
1034 register struct obj
*obj
;
1036 char buf
[BUFSZ
], qbuf
[QBUFSZ
];
1038 register char **str1
;
1041 return; /* probably blind */
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
]));
1051 Sprintf(qbuf
, "Call %s:", an(xname(&otemp
)));
1053 if (!*buf
|| *buf
== '\033')
1056 /* clear old name */
1057 str1
= &(objects
[obj
->otyp
].oc_uname
);
1059 free((genericptr_t
) *str1
);
1061 /* strip leading and trailing spaces; uncalls item if all spaces */
1062 (void) mungspaces(buf
);
1064 if (*str1
) { /* had name, so possibly remove from disco[] */
1065 /* strip name first, for the update_inventory() call
1066 from undiscover_object() */
1068 undiscover_object(obj
->otyp
);
1071 *str1
= dupstr(buf
);
1072 discover_object(obj
->otyp
, FALSE
, TRUE
); /* possibly add to disco[] */
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)
1093 if (cc
.x
== u
.ux
&& cc
.y
== u
.uy
) {
1094 obj
= vobj_at(u
.ux
, u
.uy
);
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 */
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");
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
)
1114 : obj_descr
[STRANGE_OBJECT
].oc_name
);
1115 use_plural
= (obj
->quan
> 1L);
1116 if (Hallucination
) {
1117 const char *unames
[6];
1120 /* straight role name */
1121 unames
[0] = ((Upolyd
? u
.mfemale
: flags
.female
) && urole
.name
.f
)
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];
1131 unames
[4] = roguename();
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");
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 */
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.
1188 x_monnam(mtmp
, article
, adjective
, suppress
, called
)
1189 register struct monst
*mtmp
;
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
;
1200 /* SUPPRESS_IT, SUPPRESS_INVISIBLE, SUPPRESS_HALLUCINATION, SUPPRESS_SADDLE.
1201 * EXACT_NAME: combination of all the above
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
;
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
);
1226 /* unseen monsters, etc. Use "it" */
1232 /* priests and minions: don't even use this function */
1233 if (mtmp
->ispriest
|| mtmp
->isminion
) {
1234 char priestnambuf
[BUFSZ
];
1236 long save_prop
= EHalluc_resistance
;
1237 unsigned save_invis
= mtmp
->minvis
;
1239 /* when true name is wanted, explicitly block Hallucination */
1241 EHalluc_resistance
= 1L;
1244 name
= priestname(mtmp
, priestnambuf
);
1245 EHalluc_resistance
= save_prop
;
1246 mtmp
->minvis
= save_invis
;
1247 if (article
== ARTICLE_NONE
&& !strncmp(name
, "the ", 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"
1267 Strcpy(buf
, "the ");
1268 Strcat(strcat(buf
, adjective
), " ");
1269 Strcat(buf
, shkname(mtmp
));
1272 Strcat(buf
, shkname(mtmp
));
1273 if (mdat
== &mons
[PM_SHOPKEEPER
] && !do_invis
)
1275 Strcat(buf
, " the ");
1277 Strcat(buf
, "invisible ");
1278 Strcat(buf
, pm_name
);
1282 /* Put the adjectives in the buffer */
1284 Strcat(strcat(buf
, adjective
), " ");
1286 Strcat(buf
, "invisible ");
1287 if (do_saddle
&& (mtmp
->misc_worn_check
& W_SADDLE
) && !Blind
1289 Strcat(buf
, "saddled ");
1291 has_adjectives
= TRUE
;
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 */
1299 char *rname
= rndmonnam(&rnamecode
);
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> */
1317 pbuf
[bp
- name
+ 5] = '\0'; /* adjectives right after " the " */
1320 Strcat(pbuf
, bp
+ 5); /* append the rest of the name */
1322 article
= ARTICLE_NONE
;
1323 name_at_start
= TRUE
;
1326 name_at_start
= TRUE
;
1328 } else if (is_mplayer(mdat
) && !In_endgame(&u
.uz
)) {
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
;
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
;
1344 article
= ARTICLE_NONE
;
1345 } else if ((mdat
->geno
& G_UNIQ
) && article
== ARTICLE_A
) {
1346 article
= ARTICLE_THE
;
1354 Strcpy(buf2
, "your ");
1359 Strcpy(buf2
, "the ");
1376 return x_monnam(mtmp
, ARTICLE_NONE
, (char *) 0,
1377 (has_mname(mtmp
)) ? SUPPRESS_SADDLE
: 0, TRUE
);
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
1396 return x_monnam(mtmp
, ARTICLE_THE
, (char *) 0,
1397 (has_mname(mtmp
)) ? (SUPPRESS_SADDLE
| SUPPRESS_IT
)
1406 register char *bp
= mon_nam(mtmp
);
1416 register char *bp
= noit_mon_nam(mtmp
);
1422 /* monster's own name */
1427 return x_monnam(mtmp
, ARTICLE_NONE
, (char *) 0, EXACT_NAME
, FALSE
);
1430 /* pet name: "your little dog" */
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
)
1444 return x_monnam(mtmp
, prefix
, (char *) 0, suppression_flag
, FALSE
);
1448 Adjmonnam(mtmp
, adj
)
1452 char *bp
= x_monnam(mtmp
, ARTICLE_THE
, adj
,
1453 has_mname(mtmp
) ? SUPPRESS_SADDLE
: 0, FALSE
);
1463 return x_monnam(mtmp
, ARTICLE_A
, (char *) 0,
1464 has_mname(mtmp
) ? SUPPRESS_SADDLE
: 0, FALSE
);
1471 char *bp
= a_monnam(mtmp
);
1477 /* used for monster ID by the '/', ';', and 'C' commands to block remote
1478 identification of the endgame altars via their attending priests */
1480 distant_monnam(mon
, article
, outbuf
)
1482 int article
; /* only ARTICLE_NONE and ARTICLE_THE are handled here */
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");
1493 Strcpy(outbuf
, x_monnam(mon
, article
, (char *) 0, 0, TRUE
));
1498 /* fake monsters used to be in a hard-coded array, now in a data file */
1505 get_rnd_text(BOGUSMONFILE
, buf
);
1506 /* strip prefix if present */
1507 if (!letter(*mname
)) {
1518 /* return a random monster name, for hallucination */
1523 static char buf
[BUFSZ
];
1526 #define BOGUSMONSIZE 100 /* arbitrary */
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
);
1539 mname
= strcpy(buf
, mons
[name
].mname
);
1545 /* check bogusmon prefix to decide whether it's a personal name */
1547 bogon_is_pname(code
)
1552 return index("-+=", code
) ? TRUE
: FALSE
;
1555 /* name of a Rogue player */
1561 if ((opts
= nh_getenv("ROGUEOPTS")) != 0) {
1562 for (i
= opts
; *i
; i
++)
1563 if (!strncmp("name=", i
, 5)) {
1565 if ((j
= index(i
+ 5, ',')) != 0)
1570 return rn2(3) ? (rn2(2) ? "Michael Toy" : "Kenneth Arnold")
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 */
1586 const char *colorpref
;
1588 return (Hallucination
|| !colorpref
) ? hcolors
[rn2(SIZE(hcolors
))]
1592 /* return a random real color unless hallucinating */
1596 int k
= rn2(CLR_MAX
);
1598 return Hallucination
? hcolor((char *) 0)
1599 : (k
== NO_COLOR
) ? "colorless"
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",
1615 const char *liquidpref
;
1617 return (Hallucination
|| !liquidpref
) ? hliquids
[rn2(SIZE(hliquids
))]
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",
1635 coyotename(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)]);
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"
1667 int j
, k
= SIZE(sir_Terry_novels
);
1673 else if (*novidx
>= 0 && *novidx
< k
)
1676 return sir_Terry_novels
[j
];
1680 lookup_novel(lookname
, idx
)
1681 const char *lookname
;
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
])) {
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;