1 /* NetHack 3.7 do_name.c $NHDT-Date: 1614818323 2021/03/04 00:38:43 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.198 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Pasi Kallinen, 2018. */
4 /* NetHack may be freely redistributed. See license for details. */
5
6 #include "hack.h"
7
8 static char *nextmbuf(void);
9 static void getpos_help_keyxhelp(winid, const char *, const char *, int);
10 static void getpos_help(boolean, const char *);
11 static int QSORTCALLBACK cmp_coord_distu(const void *, const void *);
12 static int gloc_filter_classify_glyph(int);
13 static int gloc_filter_floodfill_matcharea(int, int);
14 static void gloc_filter_floodfill(int, int);
15 static void gloc_filter_init(void);
16 static void gloc_filter_done(void);
17 static boolean gather_locs_interesting(int, int, int);
18 static void gather_locs(coord **, int *, int);
19 static void auto_describe(int, int);
20 static void do_mgivenname(void);
21 static boolean alreadynamed(struct monst *, char *, char *);
22 static void do_oname(struct obj *);
23 static int name_ok(struct obj *);
24 static int call_ok(struct obj *);
25 static char *docall_xname(struct obj *);
26 static void namefloorobj(void);
27
28 extern const char what_is_an_unknown_object[]; /* from pager.c */
29
30 #define NUMMBUF 5
31
32 /* manage a pool of BUFSZ buffers, so callers don't have to */
33 static char *
nextmbuf(void)34 nextmbuf(void)
35 {
36 static char NEARDATA bufs[NUMMBUF][BUFSZ];
37 static int bufidx = 0;
38
39 bufidx = (bufidx + 1) % NUMMBUF;
40 return bufs[bufidx];
41 }
42
43 /* function for getpos() to highlight desired map locations.
44 * parameter value 0 = initialize, 1 = highlight, 2 = done
45 */
46 static void (*getpos_hilitefunc)(int) = (void (*)(int)) 0;
47 static boolean (*getpos_getvalid)(int, int) = (boolean (*)(int, int)) 0;
48
49 void
getpos_sethilite(void (* gp_hilitef)(int),boolean (* gp_getvalidf)(int,int))50 getpos_sethilite(void (*gp_hilitef)(int), boolean (*gp_getvalidf)(int, int))
51 {
52 getpos_hilitefunc = gp_hilitef;
53 getpos_getvalid = gp_getvalidf;
54 }
55
56 static const char *const gloc_descr[NUM_GLOCS][4] = {
57 { "any monsters", "monster", "next/previous monster", "monsters" },
58 { "any items", "item", "next/previous object", "objects" },
59 { "any doors", "door", "next/previous door or doorway",
60 "doors or doorways" },
61 { "any unexplored areas", "unexplored area", "unexplored location",
62 "locations next to unexplored locations" },
63 { "anything interesting", "interesting thing", "anything interesting",
64 "anything interesting" },
65 { "any valid locations", "valid location", "valid location",
66 "valid locations" }
67 };
68
69 static const char *const gloc_filtertxt[NUM_GFILTER] = {
70 "",
71 " in view",
72 " in this area"
73 };
74
75 static void
getpos_help_keyxhelp(winid tmpwin,const char * k1,const char * k2,int gloc)76 getpos_help_keyxhelp(winid tmpwin, const char *k1, const char *k2, int gloc)
77 {
78 char sbuf[BUFSZ], fbuf[QBUFSZ];
79 const char *move_cursor_to = "move the cursor to ",
80 *filtertxt = gloc_filtertxt[iflags.getloc_filter];
81
82 if (gloc == GLOC_EXPLORE) {
83 /* default of "move to unexplored location" is inaccurate
84 because the position will be one spot short of that */
85 move_cursor_to = "move the cursor next to an ";
86 if (iflags.getloc_usemenu)
87 /* default is too wide for basic 80-column tty so shorten it
88 to avoid wrapping */
89 filtertxt = strsubst(strcpy(fbuf, filtertxt),
90 "this area", "area");
91 }
92 Sprintf(sbuf, "Use '%s'/'%s' to %s%s%s.",
93 k1, k2,
94 iflags.getloc_usemenu ? "get a menu of " : move_cursor_to,
95 gloc_descr[gloc][2 + iflags.getloc_usemenu], filtertxt);
96 putstr(tmpwin, 0, sbuf);
97 }
98
99 DISABLE_WARNING_FORMAT_NONLITERAL
100
101 /* the response for '?' help request in getpos() */
102 static void
getpos_help(boolean force,const char * goal)103 getpos_help(boolean force, const char *goal)
104 {
105 static const char *const fastmovemode[2] = { "8 units at a time",
106 "skipping same glyphs" };
107 char sbuf[BUFSZ];
108 boolean doing_what_is;
109 winid tmpwin = create_nhwindow(NHW_MENU);
110
111 Sprintf(sbuf,
112 "Use '%c', '%c', '%c', '%c' to move the cursor to %s.", /* hjkl */
113 g.Cmd.move_W, g.Cmd.move_S, g.Cmd.move_N, g.Cmd.move_E, goal);
114 putstr(tmpwin, 0, sbuf);
115 Sprintf(sbuf,
116 "Use 'H', 'J', 'K', 'L' to fast-move the cursor, %s.",
117 fastmovemode[iflags.getloc_moveskip]);
118 putstr(tmpwin, 0, sbuf);
119 putstr(tmpwin, 0, "Or enter a background symbol (ex. '<').");
120 Sprintf(sbuf, "Use '%s' to move the cursor on yourself.",
121 visctrl(g.Cmd.spkeys[NHKF_GETPOS_SELF]));
122 putstr(tmpwin, 0, sbuf);
123 if (!iflags.terrainmode || (iflags.terrainmode & TER_MON) != 0) {
124 getpos_help_keyxhelp(tmpwin,
125 visctrl(g.Cmd.spkeys[NHKF_GETPOS_MON_NEXT]),
126 visctrl(g.Cmd.spkeys[NHKF_GETPOS_MON_PREV]),
127 GLOC_MONS);
128 }
129 if (!iflags.terrainmode || (iflags.terrainmode & TER_OBJ) != 0) {
130 getpos_help_keyxhelp(tmpwin,
131 visctrl(g.Cmd.spkeys[NHKF_GETPOS_OBJ_NEXT]),
132 visctrl(g.Cmd.spkeys[NHKF_GETPOS_OBJ_PREV]),
133 GLOC_OBJS);
134 }
135 if (!iflags.terrainmode || (iflags.terrainmode & TER_MAP) != 0) {
136 /* these are primarily useful when choosing a travel
137 destination for the '_' command */
138 getpos_help_keyxhelp(tmpwin,
139 visctrl(g.Cmd.spkeys[NHKF_GETPOS_DOOR_NEXT]),
140 visctrl(g.Cmd.spkeys[NHKF_GETPOS_DOOR_PREV]),
141 GLOC_DOOR);
142 getpos_help_keyxhelp(tmpwin,
143 visctrl(g.Cmd.spkeys[NHKF_GETPOS_UNEX_NEXT]),
144 visctrl(g.Cmd.spkeys[NHKF_GETPOS_UNEX_PREV]),
145 GLOC_EXPLORE);
146 getpos_help_keyxhelp(tmpwin,
147 visctrl(g.Cmd.spkeys[NHKF_GETPOS_INTERESTING_NEXT]),
148 visctrl(g.Cmd.spkeys[NHKF_GETPOS_INTERESTING_PREV]),
149 GLOC_INTERESTING);
150 }
151 Sprintf(sbuf, "Use '%s' to change fast-move mode to %s.",
152 visctrl(g.Cmd.spkeys[NHKF_GETPOS_MOVESKIP]),
153 fastmovemode[!iflags.getloc_moveskip]);
154 putstr(tmpwin, 0, sbuf);
155 if (!iflags.terrainmode || (iflags.terrainmode & TER_DETECT) == 0) {
156 Sprintf(sbuf, "Use '%s' to toggle menu listing for possible targets.",
157 visctrl(g.Cmd.spkeys[NHKF_GETPOS_MENU]));
158 putstr(tmpwin, 0, sbuf);
159 Sprintf(sbuf,
160 "Use '%s' to change the mode of limiting possible targets.",
161 visctrl(g.Cmd.spkeys[NHKF_GETPOS_LIMITVIEW]));
162 putstr(tmpwin, 0, sbuf);
163 }
164 if (!iflags.terrainmode) {
165 char kbuf[BUFSZ];
166
167 if (getpos_getvalid) {
168 Sprintf(sbuf, "Use '%s' or '%s' to move to valid locations.",
169 visctrl(g.Cmd.spkeys[NHKF_GETPOS_VALID_NEXT]),
170 visctrl(g.Cmd.spkeys[NHKF_GETPOS_VALID_PREV]));
171 putstr(tmpwin, 0, sbuf);
172 }
173 if (getpos_hilitefunc) {
174 Sprintf(sbuf, "Use '%s' to display valid locations.",
175 visctrl(g.Cmd.spkeys[NHKF_GETPOS_SHOWVALID]));
176 putstr(tmpwin, 0, sbuf);
177 }
178 Sprintf(sbuf, "Use '%s' to toggle automatic description.",
179 visctrl(g.Cmd.spkeys[NHKF_GETPOS_AUTODESC]));
180 putstr(tmpwin, 0, sbuf);
181 if (iflags.cmdassist) { /* assisting the '/' command, I suppose... */
182 Sprintf(sbuf,
183 (iflags.getpos_coords == GPCOORDS_NONE)
184 ? "(Set 'whatis_coord' option to include coordinates with '%s' text.)"
185 : "(Reset 'whatis_coord' option to omit coordinates from '%s' text.)",
186 visctrl(g.Cmd.spkeys[NHKF_GETPOS_AUTODESC]));
187 }
188 /* disgusting hack; the alternate selection characters work for any
189 getpos call, but only matter for dowhatis (and doquickwhatis) */
190 doing_what_is = (goal == what_is_an_unknown_object);
191 if (doing_what_is) {
192 Sprintf(kbuf, "'%s' or '%s' or '%s' or '%s'",
193 visctrl(g.Cmd.spkeys[NHKF_GETPOS_PICK]),
194 visctrl(g.Cmd.spkeys[NHKF_GETPOS_PICK_Q]),
195 visctrl(g.Cmd.spkeys[NHKF_GETPOS_PICK_O]),
196 visctrl(g.Cmd.spkeys[NHKF_GETPOS_PICK_V]));
197 } else {
198 Sprintf(kbuf, "'%s'", visctrl(g.Cmd.spkeys[NHKF_GETPOS_PICK]));
199 }
200 Snprintf(sbuf, sizeof(sbuf),
201 "Type a %s when you are at the right place.", kbuf);
202 putstr(tmpwin, 0, sbuf);
203 if (doing_what_is) {
204 Sprintf(sbuf,
205 " '%s' describe current spot, show 'more info', move to another spot.",
206 visctrl(g.Cmd.spkeys[NHKF_GETPOS_PICK_V]));
207 putstr(tmpwin, 0, sbuf);
208 Sprintf(sbuf,
209 " '%s' describe current spot,%s move to another spot;",
210 visctrl(g.Cmd.spkeys[NHKF_GETPOS_PICK]),
211 flags.help ? " prompt if 'more info'," : "");
212 putstr(tmpwin, 0, sbuf);
213 Sprintf(sbuf,
214 " '%s' describe current spot, move to another spot;",
215 visctrl(g.Cmd.spkeys[NHKF_GETPOS_PICK_Q]));
216 putstr(tmpwin, 0, sbuf);
217 Sprintf(sbuf,
218 " '%s' describe current spot, stop looking at things;",
219 visctrl(g.Cmd.spkeys[NHKF_GETPOS_PICK_O]));
220 putstr(tmpwin, 0, sbuf);
221 }
222 }
223 if (!force)
224 putstr(tmpwin, 0, "Type Space or Escape when you're done.");
225 putstr(tmpwin, 0, "");
226 display_nhwindow(tmpwin, TRUE);
227 destroy_nhwindow(tmpwin);
228 }
229
230 RESTORE_WARNING_FORMAT_NONLITERAL
231
232 static int QSORTCALLBACK
cmp_coord_distu(const void * a,const void * b)233 cmp_coord_distu(const void *a, const void *b)
234 {
235 const coord *c1 = a;
236 const coord *c2 = b;
237 int dx, dy, dist_1, dist_2;
238
239 dx = u.ux - c1->x;
240 dy = u.uy - c1->y;
241 dist_1 = max(abs(dx), abs(dy));
242 dx = u.ux - c2->x;
243 dy = u.uy - c2->y;
244 dist_2 = max(abs(dx), abs(dy));
245
246 if (dist_1 == dist_2)
247 return (c1->y != c2->y) ? (c1->y - c2->y) : (c1->x - c2->x);
248
249 return dist_1 - dist_2;
250 }
251
252 #define IS_UNEXPLORED_LOC(x,y) \
253 (isok((x), (y)) \
254 && glyph_is_unexplored(levl[(x)][(y)].glyph) \
255 && !levl[(x)][(y)].seenv)
256
257 #define GLOC_SAME_AREA(x,y) \
258 (isok((x), (y)) \
259 && (selection_getpoint((x),(y), g.gloc_filter_map)))
260
261 static int
gloc_filter_classify_glyph(int glyph)262 gloc_filter_classify_glyph(int glyph)
263 {
264 int c;
265
266 if (!glyph_is_cmap(glyph))
267 return 0;
268
269 c = glyph_to_cmap(glyph);
270
271 if (is_cmap_room(c) || is_cmap_furniture(c))
272 return 1;
273 else if (is_cmap_wall(c) || c == S_tree)
274 return 2;
275 else if (is_cmap_corr(c))
276 return 3;
277 else if (is_cmap_water(c))
278 return 4;
279 else if (is_cmap_lava(c))
280 return 5;
281 return 0;
282 }
283
284 static int
gloc_filter_floodfill_matcharea(int x,int y)285 gloc_filter_floodfill_matcharea(int x, int y)
286 {
287 int glyph = back_to_glyph(x, y);
288
289 if (!levl[x][y].seenv)
290 return FALSE;
291
292 if (glyph == g.gloc_filter_floodfill_match_glyph)
293 return TRUE;
294
295 if (gloc_filter_classify_glyph(glyph)
296 == gloc_filter_classify_glyph(g.gloc_filter_floodfill_match_glyph))
297 return TRUE;
298
299 return FALSE;
300 }
301
302 static void
gloc_filter_floodfill(int x,int y)303 gloc_filter_floodfill(int x, int y)
304 {
305 g.gloc_filter_floodfill_match_glyph = back_to_glyph(x, y);
306
307 set_selection_floodfillchk(gloc_filter_floodfill_matcharea);
308 selection_floodfill(g.gloc_filter_map, x, y, FALSE);
309 }
310
311 static void
gloc_filter_init(void)312 gloc_filter_init(void)
313 {
314 if (iflags.getloc_filter == GFILTER_AREA) {
315 if (!g.gloc_filter_map) {
316 g.gloc_filter_map = selection_new();
317 }
318 /* special case: if we're in a doorway, try to figure out which
319 direction we're moving, and use that side of the doorway */
320 if (IS_DOOR(levl[u.ux][u.uy].typ)) {
321 if (u.dx || u.dy) {
322 gloc_filter_floodfill(u.ux + u.dx, u.uy + u.dy);
323 } else {
324 /* TODO: maybe add both sides of the doorway? */
325 }
326 } else {
327 gloc_filter_floodfill(u.ux, u.uy);
328 }
329 }
330 }
331
332 static void
gloc_filter_done(void)333 gloc_filter_done(void)
334 {
335 if (g.gloc_filter_map) {
336 selection_free(g.gloc_filter_map, TRUE);
337 g.gloc_filter_map = (struct selectionvar *) 0;
338
339 }
340 }
341
342 DISABLE_WARNING_UNREACHABLE_CODE
343
344 static boolean
gather_locs_interesting(int x,int y,int gloc)345 gather_locs_interesting(int x, int y, int gloc)
346 {
347 int glyph, sym;
348
349 if (iflags.getloc_filter == GFILTER_VIEW && !cansee(x, y))
350 return FALSE;
351 if (iflags.getloc_filter == GFILTER_AREA && !GLOC_SAME_AREA(x, y)
352 && !GLOC_SAME_AREA(x - 1, y) && !GLOC_SAME_AREA(x, y - 1)
353 && !GLOC_SAME_AREA(x + 1, y) && !GLOC_SAME_AREA(x, y + 1))
354 return FALSE;
355
356 glyph = glyph_at(x, y);
357 sym = glyph_is_cmap(glyph) ? glyph_to_cmap(glyph) : -1;
358 switch (gloc) {
359 default:
360 case GLOC_MONS:
361 /* unlike '/M', this skips monsters revealed by
362 warning glyphs and remembered unseen ones */
363 return (glyph_is_monster(glyph)
364 && glyph != monnum_to_glyph(PM_LONG_WORM_TAIL));
365 case GLOC_OBJS:
366 return (glyph_is_object(glyph)
367 && glyph != objnum_to_glyph(BOULDER)
368 && glyph != objnum_to_glyph(ROCK));
369 case GLOC_DOOR:
370 return (glyph_is_cmap(glyph)
371 && (is_cmap_door(sym)
372 || is_cmap_drawbridge(sym)
373 || sym == S_ndoor));
374 case GLOC_EXPLORE:
375 return (glyph_is_cmap(glyph)
376 && !glyph_is_nothing(glyph_to_cmap(glyph))
377 && (is_cmap_door(sym)
378 || is_cmap_drawbridge(sym)
379 || sym == S_ndoor
380 || is_cmap_room(sym)
381 || is_cmap_corr(sym))
382 && (IS_UNEXPLORED_LOC(x + 1, y)
383 || IS_UNEXPLORED_LOC(x - 1, y)
384 || IS_UNEXPLORED_LOC(x, y + 1)
385 || IS_UNEXPLORED_LOC(x, y - 1)));
386 case GLOC_VALID:
387 if (getpos_getvalid)
388 return (*getpos_getvalid)(x, y);
389 /*FALLTHRU*/
390 case GLOC_INTERESTING:
391 return (gather_locs_interesting(x, y, GLOC_DOOR)
392 || !((glyph_is_cmap(glyph)
393 && (is_cmap_wall(sym)
394 || sym == S_tree
395 || sym == S_bars
396 || sym == S_ice
397 || sym == S_air
398 || sym == S_cloud
399 || is_cmap_lava(sym)
400 || is_cmap_water(sym)
401 || sym == S_ndoor
402 || is_cmap_room(sym)
403 || is_cmap_corr(sym)))
404 || glyph_is_nothing(glyph)
405 || glyph_is_unexplored(glyph)));
406 }
407 /*NOTREACHED*/
408 return FALSE;
409 }
410
411 RESTORE_WARNINGS
412
413 /* gather locations for monsters or objects shown on the map */
414 static void
gather_locs(coord ** arr_p,int * cnt_p,int gloc)415 gather_locs(coord **arr_p, int *cnt_p, int gloc)
416 {
417 int x, y, pass, idx;
418
419 /*
420 * We always include the hero's location even if there is no monster
421 * (invisible hero without see invisible) or object (usual case)
422 * displayed there. That way, the count will always be at least 1,
423 * and player has a visual indicator (cursor returns to hero's spot)
424 * highlighting when successive 'm's or 'o's have cycled all the way
425 * through all monsters or objects.
426 *
427 * Hero's spot will always sort to array[0] because it will always
428 * be the shortest distance (namely, 0 units) away from <u.ux,u.uy>.
429 */
430
431 gloc_filter_init();
432
433 *cnt_p = idx = 0;
434 for (pass = 0; pass < 2; pass++) {
435 for (x = 1; x < COLNO; x++)
436 for (y = 0; y < ROWNO; y++) {
437 if ((x == u.ux && y == u.uy)
438 || gather_locs_interesting(x, y, gloc)) {
439 if (!pass) {
440 ++*cnt_p;
441 } else {
442 (*arr_p)[idx].x = x;
443 (*arr_p)[idx].y = y;
444 ++idx;
445 }
446 }
447 }
448
449 if (!pass) /* end of first pass */
450 *arr_p = (coord *) alloc(*cnt_p * sizeof (coord));
451 else /* end of second pass */
452 qsort(*arr_p, *cnt_p, sizeof (coord), cmp_coord_distu);
453 } /* pass */
454
455 gloc_filter_done();
456 }
457
458 char *
dxdy_to_dist_descr(int dx,int dy,boolean fulldir)459 dxdy_to_dist_descr(int dx, int dy, boolean fulldir)
460 {
461 static char buf[30];
462 int dst;
463
464 if (!dx && !dy) {
465 Sprintf(buf, "here");
466 } else if ((dst = xytod(dx, dy)) != -1) {
467 /* explicit direction; 'one step' is implicit */
468 Sprintf(buf, "%s", directionname(dst));
469 } else {
470 static const char *dirnames[4][2] = {
471 { "n", "north" },
472 { "s", "south" },
473 { "w", "west" },
474 { "e", "east" } };
475 buf[0] = '\0';
476 /* 9999: protect buf[] against overflow caused by invalid values */
477 if (dy) {
478 if (abs(dy) > 9999)
479 dy = sgn(dy) * 9999;
480 Sprintf(eos(buf), "%d%s%s", abs(dy), dirnames[(dy > 0)][fulldir],
481 dx ? "," : "");
482 }
483 if (dx) {
484 if (abs(dx) > 9999)
485 dx = sgn(dx) * 9999;
486 Sprintf(eos(buf), "%d%s", abs(dx),
487 dirnames[2 + (dx > 0)][fulldir]);
488 }
489 }
490 return buf;
491 }
492
493 DISABLE_WARNING_FORMAT_NONLITERAL
494
495 /* coordinate formatting for 'whatis_coord' option */
496 char *
coord_desc(int x,int y,char * outbuf,char cmode)497 coord_desc(int x, int y, char *outbuf, char cmode)
498 {
499 static char screen_fmt[16]; /* [12] suffices: "[%02d,%02d]" */
500 int dx, dy;
501
502 outbuf[0] = '\0';
503 switch (cmode) {
504 default:
505 break;
506 case GPCOORDS_COMFULL:
507 case GPCOORDS_COMPASS:
508 /* "east", "3s", "2n,4w" */
509 dx = x - u.ux;
510 dy = y - u.uy;
511 Sprintf(outbuf, "(%s)",
512 dxdy_to_dist_descr(dx, dy, cmode == GPCOORDS_COMFULL));
513 break;
514 case GPCOORDS_MAP: /* x,y */
515 /* upper left corner of map is <1,0>;
516 with default COLNO,ROWNO lower right corner is <79,20> */
517 Sprintf(outbuf, "<%d,%d>", x, y);
518 break;
519 case GPCOORDS_SCREEN: /* y+2,x */
520 /* for normal map sizes, force a fixed-width formatting so that
521 /m, /M, /o, and /O output lines up cleanly; map sizes bigger
522 than Nx999 or 999xM will still work, but not line up like normal
523 when displayed in a column setting */
524 if (!*screen_fmt)
525 Sprintf(screen_fmt, "[%%%sd,%%%sd]",
526 (ROWNO - 1 + 2 < 100) ? "02" : "03",
527 (COLNO - 1 < 100) ? "02" : "03");
528 /* map line 0 is screen row 2;
529 map column 0 isn't used, map column 1 is screen column 1 */
530 Sprintf(outbuf, screen_fmt, y + 2, x);
531 break;
532 }
533 return outbuf;
534 }
535
536 RESTORE_WARNING_FORMAT_NONLITERAL
537
538 static void
auto_describe(int cx,int cy)539 auto_describe(int cx, int cy)
540 {
541 coord cc;
542 int sym = 0;
543 char tmpbuf[BUFSZ];
544 const char *firstmatch = "unknown";
545
546 cc.x = cx;
547 cc.y = cy;
548 if (do_screen_description(cc, TRUE, sym, tmpbuf, &firstmatch,
549 (struct permonst **) 0)) {
550 (void) coord_desc(cx, cy, tmpbuf, iflags.getpos_coords);
551 custompline((SUPPRESS_HISTORY | OVERRIDE_MSGTYPE),
552 "%s%s%s%s%s", firstmatch, *tmpbuf ? " " : "", tmpbuf,
553 (iflags.autodescribe
554 && getpos_getvalid && !(*getpos_getvalid)(cx, cy))
555 ? " (illegal)" : "",
556 (iflags.getloc_travelmode && !is_valid_travelpt(cx, cy))
557 ? " (no travel path)" : "");
558 curs(WIN_MAP, cx, cy);
559 flush_screen(0);
560 }
561 }
562
563 boolean
getpos_menu(coord * ccp,int gloc)564 getpos_menu(coord *ccp, int gloc)
565 {
566 coord *garr = DUMMY;
567 int gcount = 0;
568 winid tmpwin;
569 anything any;
570 int i, pick_cnt;
571 menu_item *picks = (menu_item *) 0;
572 char tmpbuf[BUFSZ];
573
574 gather_locs(&garr, &gcount, gloc);
575
576 if (gcount < 2) { /* gcount always includes the hero */
577 free((genericptr_t) garr);
578 You("cannot %s %s.",
579 (iflags.getloc_filter == GFILTER_VIEW) ? "see" : "detect",
580 gloc_descr[gloc][0]);
581 return FALSE;
582 }
583
584 tmpwin = create_nhwindow(NHW_MENU);
585 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
586 any = cg.zeroany;
587
588 /* gather_locs returns array[0] == you. skip it. */
589 for (i = 1; i < gcount; i++) {
590 char fullbuf[BUFSZ];
591 coord tmpcc;
592 const char *firstmatch = "unknown";
593 int sym = 0;
594
595 any.a_int = i + 1;
596 tmpcc.x = garr[i].x;
597 tmpcc.y = garr[i].y;
598 if (do_screen_description(tmpcc, TRUE, sym, tmpbuf,
599 &firstmatch, (struct permonst **)0)) {
600 (void) coord_desc(garr[i].x, garr[i].y, tmpbuf,
601 iflags.getpos_coords);
602 Snprintf(fullbuf, sizeof fullbuf, "%s%s%s", firstmatch,
603 (*tmpbuf ? " " : ""), tmpbuf);
604 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0,
605 ATR_NONE, fullbuf, MENU_ITEMFLAGS_NONE);
606 }
607 }
608
609 Sprintf(tmpbuf, "Pick %s%s%s",
610 an(gloc_descr[gloc][1]),
611 gloc_filtertxt[iflags.getloc_filter],
612 iflags.getloc_travelmode ? " for travel destination" : "");
613 end_menu(tmpwin, tmpbuf);
614 pick_cnt = select_menu(tmpwin, PICK_ONE, &picks);
615 destroy_nhwindow(tmpwin);
616 if (pick_cnt > 0) {
617 ccp->x = garr[picks->item.a_int - 1].x;
618 ccp->y = garr[picks->item.a_int - 1].y;
619 free((genericptr_t) picks);
620 }
621 free((genericptr_t) garr);
622 return (pick_cnt > 0);
623 }
624
625 int
getpos(coord * ccp,boolean force,const char * goal)626 getpos(coord *ccp, boolean force, const char *goal)
627 {
628 const char *cp;
629 static struct {
630 int nhkf, ret;
631 } const pick_chars_def[] = {
632 { NHKF_GETPOS_PICK, LOOK_TRADITIONAL },
633 { NHKF_GETPOS_PICK_Q, LOOK_QUICK },
634 { NHKF_GETPOS_PICK_O, LOOK_ONCE },
635 { NHKF_GETPOS_PICK_V, LOOK_VERBOSE }
636 };
637 static const int mMoOdDxX_def[] = {
638 NHKF_GETPOS_MON_NEXT,
639 NHKF_GETPOS_MON_PREV,
640 NHKF_GETPOS_OBJ_NEXT,
641 NHKF_GETPOS_OBJ_PREV,
642 NHKF_GETPOS_DOOR_NEXT,
643 NHKF_GETPOS_DOOR_PREV,
644 NHKF_GETPOS_UNEX_NEXT,
645 NHKF_GETPOS_UNEX_PREV,
646 NHKF_GETPOS_INTERESTING_NEXT,
647 NHKF_GETPOS_INTERESTING_PREV,
648 NHKF_GETPOS_VALID_NEXT,
649 NHKF_GETPOS_VALID_PREV
650 };
651 char pick_chars[6];
652 char mMoOdDxX[13];
653 int result = 0;
654 int cx, cy, i, c;
655 int sidx, tx, ty;
656 boolean msg_given = TRUE; /* clear message window by default */
657 boolean show_goal_msg = FALSE;
658 boolean hilite_state = FALSE;
659 coord *garr[NUM_GLOCS] = DUMMY;
660 int gcount[NUM_GLOCS] = DUMMY;
661 int gidx[NUM_GLOCS] = DUMMY;
662
663 for (i = 0; i < SIZE(pick_chars_def); i++)
664 pick_chars[i] = g.Cmd.spkeys[pick_chars_def[i].nhkf];
665 pick_chars[SIZE(pick_chars_def)] = '\0';
666
667 for (i = 0; i < SIZE(mMoOdDxX_def); i++)
668 mMoOdDxX[i] = g.Cmd.spkeys[mMoOdDxX_def[i]];
669 mMoOdDxX[SIZE(mMoOdDxX_def)] = '\0';
670
671 if (!goal)
672 goal = "desired location";
673 if (flags.verbose) {
674 pline("(For instructions type a '%s')",
675 visctrl(g.Cmd.spkeys[NHKF_GETPOS_HELP]));
676 msg_given = TRUE;
677 }
678 cx = ccp->x;
679 cy = ccp->y;
680 #ifdef CLIPPING
681 cliparound(cx, cy);
682 #endif
683 curs(WIN_MAP, cx, cy);
684 flush_screen(0);
685 #ifdef MAC
686 lock_mouse_cursor(TRUE);
687 #endif
688 for (;;) {
689 if (show_goal_msg) {
690 pline("Move cursor to %s:", goal);
691 curs(WIN_MAP, cx, cy);
692 flush_screen(0);
693 show_goal_msg = FALSE;
694 } else if (iflags.autodescribe && !msg_given && !hilite_state) {
695 auto_describe(cx, cy);
696 }
697
698 c = nh_poskey(&tx, &ty, &sidx);
699
700 if (hilite_state) {
701 (*getpos_hilitefunc)(2);
702 hilite_state = FALSE;
703 curs(WIN_MAP, cx, cy);
704 flush_screen(0);
705 }
706
707 if (iflags.autodescribe)
708 msg_given = FALSE;
709
710 if (c == g.Cmd.spkeys[NHKF_ESC]) {
711 cx = cy = -10;
712 msg_given = TRUE; /* force clear */
713 result = -1;
714 break;
715 }
716 if (c == 0) {
717 if (!isok(tx, ty))
718 continue;
719 /* a mouse click event, just assign and return */
720 cx = tx;
721 cy = ty;
722 break;
723 }
724 if ((cp = index(pick_chars, c)) != 0) {
725 /* '.' => 0, ',' => 1, ';' => 2, ':' => 3 */
726 result = pick_chars_def[(int) (cp - pick_chars)].ret;
727 break;
728 }
729 for (i = 0; i < 8; i++) {
730 int dx, dy;
731
732 if (g.Cmd.dirchars[i] == c) {
733 /* a normal movement letter or digit */
734 dx = xdir[i];
735 dy = ydir[i];
736 } else if (g.Cmd.alphadirchars[i] == lowc((char) c)
737 || (g.Cmd.num_pad && g.Cmd.dirchars[i] == (c & 0177))) {
738 /* a shifted movement letter or Meta-digit */
739 if (iflags.getloc_moveskip) {
740 /* skip same glyphs */
741 int glyph = glyph_at(cx, cy);
742
743 dx = xdir[i];
744 dy = ydir[i];
745 while (isok(cx + dx, cy + dy)
746 && glyph == glyph_at(cx + dx, cy + dy)
747 && isok(cx + dx + xdir[i], cy + dy + ydir[i])
748 && glyph == glyph_at(cx + dx + xdir[i],
749 cy + dy + ydir[i])) {
750 dx += xdir[i];
751 dy += ydir[i];
752 }
753 } else {
754 dx = 8 * xdir[i];
755 dy = 8 * ydir[i];
756 }
757 } else
758 continue;
759
760 /* truncate at map edge; diagonal moves complicate this... */
761 if (cx + dx < 1) {
762 dy -= sgn(dy) * (1 - (cx + dx));
763 dx = 1 - cx; /* so that (cx+dx == 1) */
764 } else if (cx + dx > COLNO - 1) {
765 dy += sgn(dy) * ((COLNO - 1) - (cx + dx));
766 dx = (COLNO - 1) - cx;
767 }
768 if (cy + dy < 0) {
769 dx -= sgn(dx) * (0 - (cy + dy));
770 dy = 0 - cy; /* so that (cy+dy == 0) */
771 } else if (cy + dy > ROWNO - 1) {
772 dx += sgn(dx) * ((ROWNO - 1) - (cy + dy));
773 dy = (ROWNO - 1) - cy;
774 }
775 cx += dx;
776 cy += dy;
777 goto nxtc;
778 }
779
780 if (c == g.Cmd.spkeys[NHKF_GETPOS_HELP] || redraw_cmd(c)) {
781 if (c == g.Cmd.spkeys[NHKF_GETPOS_HELP])
782 getpos_help(force, goal);
783 else /* ^R */
784 docrt(); /* redraw */
785 /* update message window to reflect that we're still targetting */
786 show_goal_msg = TRUE;
787 msg_given = TRUE;
788 } else if (c == g.Cmd.spkeys[NHKF_GETPOS_SHOWVALID]
789 && getpos_hilitefunc) {
790 if (!hilite_state) {
791 (*getpos_hilitefunc)(0);
792 (*getpos_hilitefunc)(1);
793 hilite_state = TRUE;
794 }
795 goto nxtc;
796 } else if (c == g.Cmd.spkeys[NHKF_GETPOS_AUTODESC]) {
797 iflags.autodescribe = !iflags.autodescribe;
798 pline("Automatic description %sis %s.",
799 flags.verbose ? "of features under cursor " : "",
800 iflags.autodescribe ? "on" : "off");
801 if (!iflags.autodescribe)
802 show_goal_msg = TRUE;
803 msg_given = TRUE;
804 goto nxtc;
805 } else if (c == g.Cmd.spkeys[NHKF_GETPOS_LIMITVIEW]) {
806 static const char *const view_filters[NUM_GFILTER] = {
807 "Not limiting targets",
808 "Limiting targets to those in sight",
809 "Limiting targets to those in same area"
810 };
811
812 iflags.getloc_filter = (iflags.getloc_filter + 1) % NUM_GFILTER;
813 for (i = 0; i < NUM_GLOCS; i++) {
814 if (garr[i]) {
815 free((genericptr_t) garr[i]);
816 garr[i] = NULL;
817 }
818 gidx[i] = gcount[i] = 0;
819 }
820 pline("%s.", view_filters[iflags.getloc_filter]);
821 msg_given = TRUE;
822 goto nxtc;
823 } else if (c == g.Cmd.spkeys[NHKF_GETPOS_MENU]) {
824 iflags.getloc_usemenu = !iflags.getloc_usemenu;
825 pline("%s a menu to show possible targets%s.",
826 iflags.getloc_usemenu ? "Using" : "Not using",
827 iflags.getloc_usemenu
828 ? " for 'm|M', 'o|O', 'd|D', and 'x|X'" : "");
829 msg_given = TRUE;
830 goto nxtc;
831 } else if (c == g.Cmd.spkeys[NHKF_GETPOS_SELF]) {
832 /* reset 'm&M', 'o&O', &c; otherwise, there's no way for player
833 to achieve that except by manually cycling through all spots */
834 for (i = 0; i < NUM_GLOCS; i++)
835 gidx[i] = 0;
836 cx = u.ux;
837 cy = u.uy;
838 goto nxtc;
839 } else if (c == g.Cmd.spkeys[NHKF_GETPOS_MOVESKIP]) {
840 iflags.getloc_moveskip = !iflags.getloc_moveskip;
841 pline("%skipping over similar terrain when fastmoving the cursor.",
842 iflags.getloc_moveskip ? "S" : "Not s");
843 msg_given = TRUE;
844 goto nxtc;
845 } else if ((cp = index(mMoOdDxX, c)) != 0) { /* 'm|M', 'o|O', &c */
846 /* nearest or farthest monster or object or door or unexplored */
847 int gtmp = (int) (cp - mMoOdDxX), /* 0..7 */
848 gloc = gtmp >> 1; /* 0..3 */
849
850 if (iflags.getloc_usemenu) {
851 coord tmpcrd;
852
853 if (getpos_menu(&tmpcrd, gloc)) {
854 cx = tmpcrd.x;
855 cy = tmpcrd.y;
856 }
857 goto nxtc;
858 }
859
860 if (!garr[gloc]) {
861 gather_locs(&garr[gloc], &gcount[gloc], gloc);
862 gidx[gloc] = 0; /* garr[][0] is hero's spot */
863 }
864 if (!(gtmp & 1)) { /* c=='m' || c=='o' || c=='d' || c=='x') */
865 gidx[gloc] = (gidx[gloc] + 1) % gcount[gloc];
866 } else { /* c=='M' || c=='O' || c=='D' || c=='X') */
867 if (--gidx[gloc] < 0)
868 gidx[gloc] = gcount[gloc] - 1;
869 }
870 cx = garr[gloc][gidx[gloc]].x;
871 cy = garr[gloc][gidx[gloc]].y;
872 goto nxtc;
873 } else {
874 if (!index(quitchars, c)) {
875 char matching[MAXPCHARS];
876 int pass, lo_x, lo_y, hi_x, hi_y, k = 0;
877
878 (void) memset((genericptr_t) matching, 0, sizeof matching);
879 for (sidx = 1; sidx < MAXPCHARS; sidx++) { /* [0] left as 0 */
880 if (IS_DOOR(sidx) || IS_WALL(sidx)
881 || sidx == SDOOR || sidx == SCORR
882 || glyph_to_cmap(k) == S_room
883 || glyph_to_cmap(k) == S_darkroom
884 || glyph_to_cmap(k) == S_corr
885 || glyph_to_cmap(k) == S_litcorr)
886 continue;
887 if (c == defsyms[sidx].sym || c == (int) g.showsyms[sidx])
888 matching[sidx] = (char) ++k;
889 }
890 if (k) {
891 for (pass = 0; pass <= 1; pass++) {
892 /* pass 0: just past current pos to lower right;
893 pass 1: upper left corner to current pos */
894 lo_y = (pass == 0) ? cy : 0;
895 hi_y = (pass == 0) ? ROWNO - 1 : cy;
896 for (ty = lo_y; ty <= hi_y; ty++) {
897 lo_x = (pass == 0 && ty == lo_y) ? cx + 1 : 1;
898 hi_x = (pass == 1 && ty == hi_y) ? cx : COLNO - 1;
899 for (tx = lo_x; tx <= hi_x; tx++) {
900 /* first, look at what is currently visible
901 (might be monster) */
902 k = glyph_at(tx, ty);
903 if (glyph_is_cmap(k)
904 && matching[glyph_to_cmap(k)])
905 goto foundc;
906 /* next, try glyph that's remembered here
907 (might be trap or object) */
908 if (g.level.flags.hero_memory
909 /* !terrainmode: don't move to remembered
910 trap or object if not currently shown */
911 && !iflags.terrainmode) {
912 k = levl[tx][ty].glyph;
913 if (glyph_is_cmap(k)
914 && matching[glyph_to_cmap(k)])
915 goto foundc;
916 }
917 /* last, try actual terrain here (shouldn't
918 we be using g.lastseentyp[][] instead?) */
919 if (levl[tx][ty].seenv) {
920 k = back_to_glyph(tx, ty);
921 if (glyph_is_cmap(k)
922 && matching[glyph_to_cmap(k)])
923 goto foundc;
924 }
925 continue;
926 foundc:
927 cx = tx, cy = ty;
928 if (msg_given) {
929 clear_nhwindow(WIN_MESSAGE);
930 msg_given = FALSE;
931 }
932 goto nxtc;
933 } /* column */
934 } /* row */
935 } /* pass */
936 pline("Can't find dungeon feature '%c'.", c);
937 msg_given = TRUE;
938 goto nxtc;
939 } else {
940 char note[QBUFSZ];
941
942 if (!force)
943 Strcpy(note, "aborted");
944 else /* hjkl */
945 Sprintf(note, "use '%c', '%c', '%c', '%c' or '%s'",
946 g.Cmd.move_W, g.Cmd.move_S, g.Cmd.move_N, g.Cmd.move_E,
947 visctrl(g.Cmd.spkeys[NHKF_GETPOS_PICK]));
948 pline("Unknown direction: '%s' (%s).", visctrl((char) c),
949 note);
950 msg_given = TRUE;
951 } /* k => matching */
952 } /* !quitchars */
953 if (force)
954 goto nxtc;
955 pline("Done.");
956 msg_given = FALSE; /* suppress clear */
957 cx = -1;
958 cy = 0;
959 result = 0; /* not -1 */
960 break;
961 }
962 nxtc:
963 ;
964 #ifdef CLIPPING
965 cliparound(cx, cy);
966 #endif
967 curs(WIN_MAP, cx, cy);
968 flush_screen(0);
969 }
970 #ifdef MAC
971 lock_mouse_cursor(FALSE);
972 #endif
973 if (msg_given)
974 clear_nhwindow(WIN_MESSAGE);
975 ccp->x = cx;
976 ccp->y = cy;
977 for (i = 0; i < NUM_GLOCS; i++)
978 if (garr[i])
979 free((genericptr_t) garr[i]);
980 getpos_hilitefunc = (void (*)(int)) 0;
981 getpos_getvalid = (boolean (*)(int, int)) 0;
982 return result;
983 }
984
985 /* allocate space for a monster's name; removes old name if there is one */
986 void
new_mgivenname(struct monst * mon,int lth)987 new_mgivenname(struct monst *mon,
988 int lth) /* desired length (caller handles adding 1
989 for terminator) */
990 {
991 if (lth) {
992 /* allocate mextra if necessary; otherwise get rid of old name */
993 if (!mon->mextra)
994 mon->mextra = newmextra();
995 else
996 free_mgivenname(mon); /* already has mextra, might also have name */
997 MGIVENNAME(mon) = (char *) alloc((unsigned) lth);
998 } else {
999 /* zero length: the new name is empty; get rid of the old name */
1000 if (has_mgivenname(mon))
1001 free_mgivenname(mon);
1002 }
1003 }
1004
1005 /* release a monster's name; retains mextra even if all fields are now null */
1006 void
free_mgivenname(struct monst * mon)1007 free_mgivenname(struct monst *mon)
1008 {
1009 if (has_mgivenname(mon)) {
1010 free((genericptr_t) MGIVENNAME(mon));
1011 MGIVENNAME(mon) = (char *) 0;
1012 }
1013 }
1014
1015 /* allocate space for an object's name; removes old name if there is one */
1016 void
new_oname(struct obj * obj,int lth)1017 new_oname(struct obj *obj,
1018 int lth) /* desired length (caller handles adding 1
1019 for terminator) */
1020 {
1021 if (lth) {
1022 /* allocate oextra if necessary; otherwise get rid of old name */
1023 if (!obj->oextra)
1024 obj->oextra = newoextra();
1025 else
1026 free_oname(obj); /* already has oextra, might also have name */
1027 ONAME(obj) = (char *) alloc((unsigned) lth);
1028 } else {
1029 /* zero length: the new name is empty; get rid of the old name */
1030 if (has_oname(obj))
1031 free_oname(obj);
1032 }
1033 }
1034
1035 /* release an object's name; retains oextra even if all fields are now null */
1036 void
free_oname(struct obj * obj)1037 free_oname(struct obj *obj)
1038 {
1039 if (has_oname(obj)) {
1040 free((genericptr_t) ONAME(obj));
1041 ONAME(obj) = (char *) 0;
1042 }
1043 }
1044
1045 /* safe_oname() always returns a valid pointer to
1046 * a string, either the pointer to an object's name
1047 * if it has one, or a pointer to an empty string
1048 * if it doesn't.
1049 */
1050 const char *
safe_oname(struct obj * obj)1051 safe_oname(struct obj *obj)
1052 {
1053 if (has_oname(obj))
1054 return ONAME(obj);
1055 return "";
1056 }
1057
1058 /* historical note: this returns a monster pointer because it used to
1059 allocate a new bigger block of memory to hold the monster and its name */
1060 struct monst *
christen_monst(struct monst * mtmp,const char * name)1061 christen_monst(struct monst *mtmp, const char *name)
1062 {
1063 int lth;
1064 char buf[PL_PSIZ];
1065
1066 /* g.dogname & g.catname are PL_PSIZ arrays; object names have same limit */
1067 lth = (name && *name) ? ((int) strlen(name) + 1) : 0;
1068 if (lth > PL_PSIZ) {
1069 lth = PL_PSIZ;
1070 name = strncpy(buf, name, PL_PSIZ - 1);
1071 buf[PL_PSIZ - 1] = '\0';
1072 }
1073 new_mgivenname(mtmp, lth); /* removes old name if one is present */
1074 if (lth)
1075 Strcpy(MGIVENNAME(mtmp), name);
1076 return mtmp;
1077 }
1078
1079 /* check whether user-supplied name matches or nearly matches an unnameable
1080 monster's name; if so, give an alternate reject message for do_mgivenname() */
1081 static boolean
alreadynamed(struct monst * mtmp,char * monnambuf,char * usrbuf)1082 alreadynamed(struct monst *mtmp, char *monnambuf, char *usrbuf)
1083 {
1084 char pronounbuf[10], *p;
1085
1086 if (fuzzymatch(usrbuf, monnambuf, " -_", TRUE)
1087 /* catch trying to name "the Oracle" as "Oracle" */
1088 || (!strncmpi(monnambuf, "the ", 4)
1089 && fuzzymatch(usrbuf, monnambuf + 4, " -_", TRUE))
1090 /* catch trying to name "invisible Orcus" as "Orcus" */
1091 || ((p = strstri(monnambuf, "invisible ")) != 0
1092 && fuzzymatch(usrbuf, p + 10, " -_", TRUE))
1093 /* catch trying to name "the {priest,Angel} of Crom" as "Crom" */
1094 || ((p = strstri(monnambuf, " of ")) != 0
1095 && fuzzymatch(usrbuf, p + 4, " -_", TRUE))) {
1096 pline("%s is already called %s.",
1097 upstart(strcpy(pronounbuf, mhe(mtmp))), monnambuf);
1098 return TRUE;
1099 } else if (mtmp->data == &mons[PM_JUIBLEX]
1100 && strstri(monnambuf, "Juiblex")
1101 && !strcmpi(usrbuf, "Jubilex")) {
1102 pline("%s doesn't like being called %s.", upstart(monnambuf), usrbuf);
1103 return TRUE;
1104 }
1105 return FALSE;
1106 }
1107
1108 /* allow player to assign a name to some chosen monster */
1109 static void
do_mgivenname(void)1110 do_mgivenname(void)
1111 {
1112 char buf[BUFSZ], monnambuf[BUFSZ], qbuf[QBUFSZ];
1113 coord cc;
1114 int cx, cy;
1115 struct monst *mtmp = 0;
1116
1117 if (Hallucination) {
1118 You("would never recognize it anyway.");
1119 return;
1120 }
1121 cc.x = u.ux;
1122 cc.y = u.uy;
1123 if (getpos(&cc, FALSE, "the monster you want to name") < 0
1124 || !isok(cc.x, cc.y))
1125 return;
1126 cx = cc.x, cy = cc.y;
1127
1128 if (cx == u.ux && cy == u.uy) {
1129 if (u.usteed && canspotmon(u.usteed)) {
1130 mtmp = u.usteed;
1131 } else {
1132 pline("This %s creature is called %s and cannot be renamed.",
1133 beautiful(), g.plname);
1134 return;
1135 }
1136 } else
1137 mtmp = m_at(cx, cy);
1138
1139 if (!mtmp
1140 || (!sensemon(mtmp)
1141 && (!(cansee(cx, cy) || see_with_infrared(mtmp))
1142 || mtmp->mundetected || M_AP_TYPE(mtmp) == M_AP_FURNITURE
1143 || M_AP_TYPE(mtmp) == M_AP_OBJECT
1144 || (mtmp->minvis && !See_invisible)))) {
1145 pline("I see no monster there.");
1146 return;
1147 }
1148 /* special case similar to the one in lookat() */
1149 Sprintf(qbuf, "What do you want to call %s?",
1150 distant_monnam(mtmp, ARTICLE_THE, monnambuf));
1151 buf[0] = '\0';
1152 #ifdef EDIT_GETLIN
1153 /* if there's an existing name, make it be the default answer */
1154 if (has_mgivenname(mtmp))
1155 Strcpy(buf, MGIVENNAME(mtmp));
1156 #endif
1157 getlin(qbuf, buf);
1158 if (!*buf || *buf == '\033')
1159 return;
1160 /* strip leading and trailing spaces; unnames monster if all spaces */
1161 (void) mungspaces(buf);
1162
1163 /* Unique monsters have their own specific names or titles.
1164 * Shopkeepers, temple priests and other minions use alternate
1165 * name formatting routines which ignore any user-supplied name.
1166 *
1167 * Don't say the name is being rejected if it happens to match
1168 * the existing name.
1169 *
1170 * TODO: should have an alternate message when the attempt is to
1171 * remove existing name without assigning a new one.
1172 */
1173 if ((mtmp->data->geno & G_UNIQ) && !mtmp->ispriest) {
1174 if (!alreadynamed(mtmp, monnambuf, buf))
1175 pline("%s doesn't like being called names!", upstart(monnambuf));
1176 } else if (mtmp->isshk
1177 && !(Deaf || mtmp->msleeping || !mtmp->mcanmove
1178 || mtmp->data->msound <= MS_ANIMAL)) {
1179 if (!alreadynamed(mtmp, monnambuf, buf))
1180 verbalize("I'm %s, not %s.", shkname(mtmp), buf);
1181 } else if (mtmp->ispriest || mtmp->isminion || mtmp->isshk
1182 || mtmp->data == &mons[PM_GHOST] || has_ebones(mtmp)) {
1183 if (!alreadynamed(mtmp, monnambuf, buf))
1184 pline("%s will not accept the name %s.", upstart(monnambuf), buf);
1185 } else
1186 (void) christen_monst(mtmp, buf);
1187 }
1188
1189 /*
1190 * This routine used to change the address of 'obj' so be unsafe if not
1191 * used with extreme care. Applying a name to an object no longer
1192 * allocates a replacement object, so that old risk is gone.
1193 */
1194 static void
do_oname(register struct obj * obj)1195 do_oname(register struct obj *obj)
1196 {
1197 char buf[BUFSZ], qbuf[QBUFSZ];
1198 const char *aname;
1199 short objtyp;
1200
1201 /* Do this now because there's no point in even asking for a name */
1202 if (obj->otyp == SPE_NOVEL) {
1203 pline("%s already has a published name.", Ysimple_name2(obj));
1204 return;
1205 }
1206
1207 Sprintf(qbuf, "What do you want to name %s ",
1208 is_plural(obj) ? "these" : "this");
1209 (void) safe_qbuf(qbuf, qbuf, "?", obj, xname, simpleonames, "item");
1210 buf[0] = '\0';
1211 #ifdef EDIT_GETLIN
1212 /* if there's an existing name, make it be the default answer */
1213 if (has_oname(obj))
1214 Strcpy(buf, ONAME(obj));
1215 #endif
1216 getlin(qbuf, buf);
1217 if (!*buf || *buf == '\033')
1218 return;
1219 /* strip leading and trailing spaces; unnames item if all spaces */
1220 (void) mungspaces(buf);
1221
1222 /*
1223 * We don't violate illiteracy conduct here, although it is
1224 * arguable that we should for anything other than "X". Doing so
1225 * would make attaching player's notes to hero's inventory have an
1226 * in-game effect, which may or may not be the correct thing to do.
1227 */
1228
1229 /* relax restrictions over proper capitalization for artifacts */
1230 if ((aname = artifact_name(buf, &objtyp)) != 0 && objtyp == obj->otyp)
1231 Strcpy(buf, aname);
1232
1233 if (obj->oartifact) {
1234 pline_The("artifact seems to resist the attempt.");
1235 return;
1236 } else if (restrict_name(obj, buf) || exist_artifact(obj->otyp, buf)) {
1237 /* this used to be flavored as actually writing on the weapon and
1238 * breaking illiterate, but that was silly for a number of reasons. */
1239 pline("For some reason, you can't mentally assign that name to %s.",
1240 is_plural(obj) ? "them" : "it");
1241 display_nhwindow(WIN_MESSAGE, FALSE);
1242 }
1243 ++g.via_naming; /* This ought to be an argument rather than a static... */
1244 obj = oname(obj, buf);
1245 --g.via_naming; /* ...but oname() is used in a lot of places, so defer. */
1246 }
1247
1248 DISABLE_WARNING_FORMAT_NONLITERAL /* sprintf on an arbitrary format string from
1249 * an array of format strings */
1250
1251 /* Confer a grand-sounding name on a weapon. The weapon is assumed to be a
1252 * non-artifact and have a good enchantment or other good property. */
1253 struct obj *
weapon_oname(struct obj * wpn)1254 weapon_oname(struct obj *wpn)
1255 {
1256 /* Don't randomly name stacks. */
1257 if (wpn->quan > 1)
1258 return wpn;
1259
1260 char basename[BUFSZ];
1261 int skill = weapon_type(wpn);
1262 if (skill == P_SABER)
1263 Strcpy(basename, "Sabre");
1264 else if (wpn->otyp == TWO_HANDED_SWORD)
1265 Strcpy(basename, "Claymore");
1266 else if (is_sword(wpn))
1267 Strcpy(basename, "Sword");
1268 else if (wpn->otyp == MORNING_STAR)
1269 Strcpy(basename, "Morning Star");
1270 else if (wpn->otyp == RUBBER_HOSE)
1271 Strcpy(basename, "Hose");
1272 else if ((skill == P_POLEARMS) || (skill == P_KNIFE)
1273 || (wpn->otyp == ATHAME))
1274 Strcpy(basename, OBJ_NAME(objects[wpn->otyp]));
1275 else
1276 Strcpy(basename, weapon_descr(wpn));
1277
1278 /* Possible names. Any names with a %s in them must be sprintf'd with
1279 * basename. */
1280 const char* wpn_names[] = {
1281 "%s of Justice", "%s of Honor", "%s of Glory", "%s of Revenge",
1282 "%s of Sorrow", "%s of Yendor", "%s of Victory", "%s of Carnage",
1283 "%s of Serenity", "%s of Fable", "%s of Legend", "%s of Integrity",
1284 "%s of Redress", "%s of Fate", "%s of Punition", "%s of Reckoning",
1285 "%s of Omen", "%s of Truth", "%s of Virtue", "%s of Bloodlust",
1286 "%s of Disaster", "%s of Torment", "%s of Doom", "%s of the Gods",
1287 "%s of Quarrels", "%s of Grace", "%s of Respect", "%s of Paranoia",
1288 "%s of Fury", "%s of Hubris", "%s of Insanity",
1289 "%s of the Horde","%s of Gilgamesh","%s of the Planes",
1290 "%s of Excellence", "%s of Vengeance", "%s of Swift Defeat",
1291 "%s of Protection",
1292 "Righteous %s", "Mighty %s", "Unstoppable %s", "Sacred %s",
1293 "Holy %s", "Lucky %s", "Dudley's %s", "Chaos %s",
1294 "Hungry %s", "Inexorable %s", "Virtuous %s", "Death %s",
1295 "Unlimited %s", "Infinite %s", "Chaos %s", "Peerless %s",
1296 "Elegant %s", "Artful %s", "Wicked %s", "Clever %s",
1297 "Due Process", "Puddingbane", "Vladsbane", "Newtsbane",
1298 "Monster Slayer", "Orphan Maker", "Aggressive Negotiation",
1299 "Old Reliable", "Punisher", "Barman's Friend"
1300 };
1301
1302 char buf[BUFSZ];
1303 if (!rn2(25 - (2 * wpn->spe))) {
1304 char nbuf[PL_NSIZ+1];
1305 const char* ttname = tt_name();
1306 if (ttname) {
1307 Strcpy(nbuf, ttname);
1308 Sprintf(buf, "%s of %s", upstart(basename), upstart(nbuf));
1309 return oname(wpn, buf);
1310 }
1311 /* if a name couldn't be found, fall through to default */
1312 }
1313 const char* name = wpn_names[rn2(SIZE(wpn_names))];
1314 if (strstri(name, "%s")) {
1315 Sprintf(buf, name, upstart(basename));
1316 return oname(wpn, buf);
1317 }
1318 else {
1319 return oname(wpn, name);
1320 }
1321 }
1322
1323 RESTORE_WARNING_FORMAT_NONLITERAL
1324
1325 struct obj *
oname(struct obj * obj,const char * name)1326 oname(struct obj *obj, const char *name)
1327 {
1328 int lth;
1329 char buf[PL_PSIZ];
1330
1331 lth = *name ? (int) (strlen(name) + 1) : 0;
1332 if (lth > PL_PSIZ) {
1333 lth = PL_PSIZ;
1334 name = strncpy(buf, name, PL_PSIZ - 1);
1335 buf[PL_PSIZ - 1] = '\0';
1336 }
1337 /* If named artifact exists in the game, do not create another.
1338 * Also trying to create an artifact shouldn't de-artifact
1339 * it (e.g. Excalibur from prayer). In this case the object
1340 * will retain its current name. */
1341 if (obj->oartifact || (lth && exist_artifact(obj->otyp, name)))
1342 return obj;
1343
1344 new_oname(obj, lth); /* removes old name if one is present */
1345 if (lth)
1346 Strcpy(ONAME(obj), name);
1347
1348 if (lth)
1349 artifact_exists(obj, name, TRUE);
1350 if (obj->oartifact) {
1351 /* can't dual-wield with artifact as secondary weapon */
1352 if (obj == uswapwep)
1353 untwoweapon();
1354 /* activate warning if you've just named your weapon "Sting" */
1355 if (obj == uwep)
1356 set_artifact_intrinsic(obj, TRUE, W_WEP);
1357 /* if obj is owned by a shop, increase your bill */
1358 if (obj->unpaid)
1359 alter_cost(obj, 0L);
1360 if (g.via_naming) {
1361 livelog_printf(LL_ARTIFACT, "chose %s to be named \"%s\"", ansimpleoname(obj), bare_artifactname(obj));
1362 }
1363 /* set up specific materials for the artifact */
1364 switch(obj->oartifact) {
1365 case ART_SUNSWORD:
1366 case ART_ITLACHIAYAQUE:
1367 set_material(obj, GOLD);
1368 break;
1369 case ART_WEREBANE:
1370 case ART_DEMONBANE:
1371 case ART_GRAYSWANDIR:
1372 /* case ART_MITRE_OF_HOLINESS: */
1373 set_material(obj, SILVER);
1374 break;
1375 case ART_SCEPTRE_OF_MIGHT:
1376 /* don't make it hurt elves */
1377 obj->material = METAL;
1378 break;
1379 case ART_YENDORIAN_EXPRESS_CARD:
1380 set_material(obj, PLATINUM);
1381 break;
1382 default:
1383 /* prevent any wishes for materials on an artifact */
1384 set_material(obj, objects[obj->otyp].oc_material);
1385 }
1386 }
1387 if (carried(obj))
1388 update_inventory();
1389 return obj;
1390 }
1391
1392 boolean
objtyp_is_callable(int i)1393 objtyp_is_callable(int i)
1394 {
1395 if (objects[i].oc_uname)
1396 return TRUE;
1397
1398 switch(objects[i].oc_class) {
1399 case SCROLL_CLASS:
1400 case POTION_CLASS:
1401 case WAND_CLASS:
1402 case RING_CLASS:
1403 case AMULET_CLASS:
1404 case GEM_CLASS:
1405 case SPBOOK_CLASS:
1406 case ARMOR_CLASS:
1407 case TOOL_CLASS:
1408 case VENOM_CLASS:
1409 if (OBJ_DESCR(objects[i]))
1410 return TRUE;
1411 break;
1412 default:
1413 break;
1414 }
1415 return FALSE;
1416 }
1417
1418 /* getobj callback for object to name (specific item) - anything but gold */
1419 static int
name_ok(struct obj * obj)1420 name_ok(struct obj *obj)
1421 {
1422 if (!obj || obj->oclass == COIN_CLASS)
1423 return GETOBJ_EXCLUDE;
1424
1425 return GETOBJ_SUGGEST;
1426 }
1427
1428 /* getobj callback for object to call (name its type) */
1429 static int
call_ok(struct obj * obj)1430 call_ok(struct obj *obj)
1431 {
1432 if (!obj || !objtyp_is_callable(obj->otyp))
1433 return GETOBJ_EXCLUDE;
1434
1435 return GETOBJ_SUGGEST;
1436 }
1437
1438 /* C and #name commands - player can name monster or object or type of obj */
1439 int
docallcmd(void)1440 docallcmd(void)
1441 {
1442 struct obj *obj;
1443 winid win;
1444 anything any;
1445 menu_item *pick_list = 0;
1446 char ch;
1447 /* if player wants a,b,c instead of i,o when looting, do that here too */
1448 boolean abc = flags.lootabc;
1449
1450 win = create_nhwindow(NHW_MENU);
1451 start_menu(win, MENU_BEHAVE_STANDARD);
1452 any = cg.zeroany;
1453 any.a_char = 'm'; /* group accelerator 'C' */
1454 add_menu(win, &nul_glyphinfo, &any, abc ? 0 : any.a_char, 'C',
1455 ATR_NONE, "a monster", MENU_ITEMFLAGS_NONE);
1456 if (g.invent) {
1457 /* we use y and n as accelerators so that we can accept user's
1458 response keyed to old "name an individual object?" prompt */
1459 any.a_char = 'i'; /* group accelerator 'y' */
1460 add_menu(win, &nul_glyphinfo, &any, abc ? 0 : any.a_char, 'y',
1461 ATR_NONE, "a particular object in inventory",
1462 MENU_ITEMFLAGS_NONE);
1463 any.a_char = 'o'; /* group accelerator 'n' */
1464 add_menu(win, &nul_glyphinfo, &any, abc ? 0 : any.a_char, 'n',
1465 ATR_NONE, "the type of an object in inventory",
1466 MENU_ITEMFLAGS_NONE);
1467 }
1468 any.a_char = 'f'; /* group accelerator ',' (or ':' instead?) */
1469 add_menu(win, &nul_glyphinfo, &any, abc ? 0 : any.a_char, ',',
1470 ATR_NONE, "the type of an object upon the floor",
1471 MENU_ITEMFLAGS_NONE);
1472 any.a_char = 'd'; /* group accelerator '\' */
1473 add_menu(win, &nul_glyphinfo, &any, abc ? 0 : any.a_char, '\\',
1474 ATR_NONE, "the type of an object on discoveries list",
1475 MENU_ITEMFLAGS_NONE);
1476 any.a_char = 'a'; /* group accelerator 'l' */
1477 add_menu(win, &nul_glyphinfo, &any, abc ? 0 : any.a_char, 'l',
1478 ATR_NONE, "record an annotation for the current level",
1479 MENU_ITEMFLAGS_NONE);
1480 end_menu(win, "What do you want to name?");
1481 if (select_menu(win, PICK_ONE, &pick_list) > 0) {
1482 ch = pick_list[0].item.a_char;
1483 free((genericptr_t) pick_list);
1484 } else
1485 ch = 'q';
1486 destroy_nhwindow(win);
1487
1488 switch (ch) {
1489 default:
1490 case 'q':
1491 break;
1492 case 'm': /* name a visible monster */
1493 do_mgivenname();
1494 break;
1495 case 'i': /* name an individual object in inventory */
1496 obj = getobj("name", name_ok, GETOBJ_PROMPT);
1497 if (obj)
1498 do_oname(obj);
1499 break;
1500 case 'o': /* name a type of object in inventory */
1501 obj = getobj("call", call_ok, GETOBJ_NOFLAGS);
1502 if (obj) {
1503 /* behave as if examining it in inventory;
1504 this might set dknown if it was picked up
1505 while blind and the hero can now see */
1506 (void) xname(obj);
1507
1508 if (!obj->dknown) {
1509 You("would never recognize another one.");
1510 #if 0
1511 } else if (!call_ok(obj)) {
1512 You("know those as well as you ever will.");
1513 #endif
1514 } else {
1515 docall(obj);
1516 }
1517 }
1518 break;
1519 case 'f': /* name a type of object visible on the floor */
1520 namefloorobj();
1521 break;
1522 case 'd': /* name a type of object on the discoveries list */
1523 rename_disco();
1524 break;
1525 case 'a': /* annotate level */
1526 donamelevel();
1527 break;
1528 }
1529 return 0;
1530 }
1531
1532 /* for use by safe_qbuf() */
1533 static char *
docall_xname(struct obj * obj)1534 docall_xname(struct obj *obj)
1535 {
1536 struct obj otemp;
1537
1538 otemp = *obj;
1539 otemp.oextra = (struct oextra *) 0;
1540 otemp.quan = 1L;
1541 /* in case water is already known, convert "[un]holy water" to "water" */
1542 otemp.blessed = otemp.cursed = 0;
1543 /* remove attributes that are doname() caliber but get formatted
1544 by xname(); most of these fixups aren't really needed because the
1545 relevant type of object isn't callable so won't reach this far */
1546 if (otemp.oclass == WEAPON_CLASS)
1547 otemp.opoisoned = 0; /* not poisoned */
1548 else if (otemp.oclass == POTION_CLASS)
1549 otemp.odiluted = 0; /* not diluted */
1550 else if (otemp.otyp == TOWEL || otemp.otyp == STATUE)
1551 otemp.spe = 0; /* not wet or historic */
1552 else if (otemp.otyp == TIN)
1553 otemp.known = 0; /* suppress tin type (homemade, &c) and mon type */
1554 else if (otemp.otyp == FIGURINE)
1555 otemp.corpsenm = NON_PM; /* suppress mon type */
1556 else if (otemp.otyp == HEAVY_IRON_BALL)
1557 otemp.owt = objects[HEAVY_IRON_BALL].oc_weight; /* not "very heavy" */
1558 else if (otemp.oclass == FOOD_CLASS && otemp.globby)
1559 otemp.owt = 120; /* 6*20, neither a small glob nor a large one */
1560
1561 return an(xname(&otemp));
1562 }
1563
1564 void
docall(struct obj * obj)1565 docall(struct obj *obj)
1566 {
1567 char buf[BUFSZ], qbuf[QBUFSZ];
1568 char **str1;
1569
1570 if (!obj->dknown)
1571 return; /* probably blind */
1572 flush_screen(1); /* buffered updates might matter to player's response */
1573
1574 if (obj->oclass == POTION_CLASS && obj->fromsink)
1575 /* kludge, meaning it's sink water */
1576 Sprintf(qbuf, "Call a stream of %s fluid:",
1577 OBJ_DESCR(objects[obj->otyp]));
1578 else
1579 (void) safe_qbuf(qbuf, "Call ", ":", obj,
1580 docall_xname, simpleonames, "thing");
1581 /* pointer to old name */
1582 str1 = &(objects[obj->otyp].oc_uname);
1583 buf[0] = '\0';
1584 #ifdef EDIT_GETLIN
1585 /* if there's an existing name, make it be the default answer */
1586 if (*str1)
1587 Strcpy(buf, *str1);
1588 #endif
1589 getlin(qbuf, buf);
1590 if (!*buf || *buf == '\033')
1591 return;
1592
1593 /* clear old name */
1594 if (*str1)
1595 free((genericptr_t) *str1);
1596
1597 /* strip leading and trailing spaces; uncalls item if all spaces */
1598 (void) mungspaces(buf);
1599 if (!*buf) {
1600 if (*str1) { /* had name, so possibly remove from disco[] */
1601 /* strip name first, for the update_inventory() call
1602 from undiscover_object() */
1603 *str1 = (char *) 0;
1604 undiscover_object(obj->otyp);
1605 }
1606 } else {
1607 *str1 = dupstr(buf);
1608 discover_object(obj->otyp, FALSE, TRUE); /* possibly add to disco[] */
1609 }
1610 }
1611
1612 static void
namefloorobj(void)1613 namefloorobj(void)
1614 {
1615 coord cc;
1616 int glyph;
1617 char buf[BUFSZ];
1618 struct obj *obj = 0;
1619 boolean fakeobj = FALSE, use_plural;
1620
1621 cc.x = u.ux, cc.y = u.uy;
1622 /* "dot for under/over you" only makes sense when the cursor hasn't
1623 been moved off the hero's '@' yet, but there's no way to adjust
1624 the help text once getpos() has started */
1625 Sprintf(buf, "object on map (or '.' for one %s you)",
1626 (u.uundetected && hides_under(g.youmonst.data)) ? "over" : "under");
1627 if (getpos(&cc, FALSE, buf) < 0 || cc.x <= 0)
1628 return;
1629 if (cc.x == u.ux && cc.y == u.uy) {
1630 obj = vobj_at(u.ux, u.uy);
1631 } else {
1632 glyph = glyph_at(cc.x, cc.y);
1633 if (glyph_is_object(glyph))
1634 fakeobj = object_from_map(glyph, cc.x, cc.y, &obj);
1635 /* else 'obj' stays null */
1636 }
1637 if (!obj) {
1638 /* "under you" is safe here since there's no object to hide under */
1639 pline("There doesn't seem to be any object %s.",
1640 (cc.x == u.ux && cc.y == u.uy) ? "under you" : "there");
1641 return;
1642 }
1643 /* note well: 'obj' might be an instance of STRANGE_OBJECT if target
1644 is a mimic; passing that to xname (directly or via simpleonames)
1645 would yield "glorkum" so we need to handle it explicitly; it will
1646 always fail the Hallucination test and pass the !callable test,
1647 resulting in the "can't be assigned a type name" message */
1648 Strcpy(buf, (obj->otyp != STRANGE_OBJECT)
1649 ? simpleonames(obj)
1650 : obj_descr[STRANGE_OBJECT].oc_name);
1651 use_plural = (obj->quan > 1L);
1652 if (Hallucination) {
1653 const char *unames[6];
1654 char tmpbuf[BUFSZ];
1655
1656 /* straight role name */
1657 unames[0] = ((Upolyd ? u.mfemale : flags.female) && g.urole.name.f)
1658 ? g.urole.name.f
1659 : g.urole.name.m;
1660 /* random rank title for hero's role
1661
1662 note: the 30 is hardcoded in xlev_to_rank, so should be
1663 hardcoded here too */
1664 unames[1] = rank_of(rn2_on_display_rng(30) + 1,
1665 Role_switch, flags.female);
1666 /* random fake monster */
1667 unames[2] = bogusmon(tmpbuf, (char *) 0, -1);
1668 /* increased chance for fake monster */
1669 unames[3] = unames[2];
1670 /* traditional */
1671 unames[4] = roguename();
1672 /* silly */
1673 unames[5] = "Wibbly Wobbly";
1674 pline("%s %s to call you \"%s.\"",
1675 The(buf), use_plural ? "decide" : "decides",
1676 unames[rn2_on_display_rng(SIZE(unames))]);
1677 } else if (!call_ok(obj)) {
1678 pline("%s %s can't be assigned a type name.",
1679 use_plural ? "Those" : "That", buf);
1680 } else if (!obj->dknown) {
1681 You("don't know %s %s well enough to name %s.",
1682 use_plural ? "those" : "that", buf, use_plural ? "them" : "it");
1683 } else {
1684 docall(obj);
1685 }
1686 if (fakeobj) {
1687 obj->where = OBJ_FREE; /* object_from_map() sets it to OBJ_FLOOR */
1688 dealloc_obj(obj);
1689 }
1690 }
1691
1692 static const char *const ghostnames[] = {
1693 /* these names should have length < PL_NSIZ */
1694 /* Capitalize the names for aesthetics -dgk */
1695 "Adri", "Andries", "Andreas", "Bert", "David", "Dirk",
1696 "Emile", "Frans", "Fred", "Greg", "Hether", "Jay",
1697 "John", "Jon", "Karnov", "Kay", "Kenny", "Kevin",
1698 "Maud", "Michiel", "Mike", "Peter", "Robert", "Ron",
1699 "Tom", "Wilmar", "Nick Danger", "Phoenix", "Jiro", "Mizue",
1700 "Stephan", "Lance Braccus", "Shadowhawk", "Dudley"
1701 };
1702
1703 /* ghost names formerly set by x_monnam(), now by makemon() instead */
1704 const char *
rndghostname(void)1705 rndghostname(void)
1706 {
1707 return rn2(7) ? ghostnames[rn2(SIZE(ghostnames))] : (const char *) g.plname;
1708 }
1709
1710 /*
1711 * Monster naming functions:
1712 * x_monnam is the generic monster-naming function.
1713 * seen unseen detected named
1714 * mon_nam: the newt it the invisible orc Fido
1715 * noit_mon_nam:the newt (as if detected) the invisible orc Fido
1716 * l_monnam: newt it invisible orc dog called Fido
1717 * Monnam: The newt It The invisible orc Fido
1718 * noit_Monnam: The newt (as if detected) The invisible orc Fido
1719 * adj_monnam: the poor newt It the poor invisible orc the poor Fido
1720 * Adjmonnam: The poor newt It The poor invisible orc The poor Fido
1721 * Amonnam: A newt It An invisible orc Fido
1722 * a_monnam: a newt it an invisible orc Fido
1723 * m_monnam: newt xan orc Fido
1724 * y_monnam: your newt your xan your invisible orc Fido
1725 * noname_monnam(mon,article):
1726 * article newt art xan art invisible orc art dog
1727 */
1728
1729 /*
1730 * article
1731 *
1732 * ARTICLE_NONE, ARTICLE_THE, ARTICLE_A: obvious
1733 * ARTICLE_YOUR: "your" on pets, "the" on everything else
1734 *
1735 * If the monster would be referred to as "it" or if the monster has a name
1736 * _and_ there is no adjective, "invisible", "saddled", etc., override this
1737 * and always use no article.
1738 *
1739 * suppress
1740 *
1741 * SUPPRESS_IT, SUPPRESS_INVISIBLE, SUPPRESS_HALLUCINATION, SUPPRESS_SADDLE.
1742 * EXACT_NAME: combination of all the above
1743 * SUPPRESS_NAME: omit monster's assigned name (unless uniq w/ pname).
1744 *
1745 * Bug: if the monster is a priest or shopkeeper, not every one of these
1746 * options works, since those are special cases.
1747 */
1748 char *
x_monnam(register struct monst * mtmp,int article,const char * adjective,int suppress,boolean called)1749 x_monnam(register struct monst *mtmp, int article,
1750 const char *adjective, int suppress, boolean called)
1751 {
1752 char *buf = nextmbuf();
1753 struct permonst *mdat = mtmp->data;
1754 const char *pm_name = pmname(mdat, Mgender(mtmp));
1755 boolean do_hallu, do_invis, do_it, do_saddle, do_name;
1756 boolean name_at_start, has_adjectives;
1757 char *bp;
1758
1759 if (g.program_state.gameover)
1760 suppress |= SUPPRESS_HALLUCINATION;
1761 if (article == ARTICLE_YOUR && !mtmp->mtame)
1762 article = ARTICLE_THE;
1763
1764 do_hallu = Hallucination && !(suppress & SUPPRESS_HALLUCINATION);
1765 do_invis = mtmp->minvis && !(suppress & SUPPRESS_INVISIBLE);
1766 do_it = !canspotmon(mtmp) && article != ARTICLE_YOUR
1767 && !g.program_state.gameover && mtmp != u.usteed
1768 && !(u.uswallow && mtmp == u.ustuck) && !(suppress & SUPPRESS_IT);
1769 do_saddle = !(suppress & SUPPRESS_SADDLE);
1770 do_name = !(suppress & SUPPRESS_NAME) || type_is_pname(mdat);
1771
1772 buf[0] = '\0';
1773
1774 /* unseen monsters, etc. Use "it" */
1775 if (do_it) {
1776 Strcpy(buf, "it");
1777 return buf;
1778 }
1779
1780 /* priests and minions: don't even use this function */
1781 if (mtmp->ispriest || mtmp->isminion) {
1782 char priestnambuf[BUFSZ];
1783 char *name;
1784 long save_prop = EHalluc_resistance;
1785 unsigned save_invis = mtmp->minvis;
1786
1787 /* when true name is wanted, explicitly block Hallucination */
1788 if (!do_hallu)
1789 EHalluc_resistance = 1L;
1790 if (!do_invis)
1791 mtmp->minvis = 0;
1792 name = priestname(mtmp, priestnambuf);
1793 EHalluc_resistance = save_prop;
1794 mtmp->minvis = save_invis;
1795 if (article == ARTICLE_NONE && !strncmp(name, "the ", 4))
1796 name += 4;
1797 return strcpy(buf, name);
1798 }
1799 #if 0
1800 /* an "aligned priest" not flagged as a priest or minion should be
1801 "priest" or "priestess" (normally handled by priestname()) */
1802 if (mdat == &mons[PM_ALIGNED_CLERIC])
1803 pm_name = mtmp->female ? "priestess" : "priest";
1804 else if (mdat == &mons[PM_HIGH_CLERIC] && mtmp->female)
1805 pm_name = "high priestess";
1806 #endif
1807
1808 /* Shopkeepers: use shopkeeper name. For normal shopkeepers, just
1809 * "Asidonhopo"; for unusual ones, "Asidonhopo the invisible
1810 * shopkeeper" or "Asidonhopo the blue dragon". If hallucinating,
1811 * none of this applies.
1812 */
1813 if (mtmp->isshk && !do_hallu) {
1814 if (adjective && article == ARTICLE_THE) {
1815 /* pathological case: "the angry Asidonhopo the blue dragon"
1816 sounds silly */
1817 Strcpy(buf, "the ");
1818 Strcat(strcat(buf, adjective), " ");
1819 Strcat(buf, shkname(mtmp));
1820 return buf;
1821 }
1822 Strcat(buf, shkname(mtmp));
1823 if (mdat == &mons[PM_SHOPKEEPER] && !do_invis)
1824 return buf;
1825 Strcat(buf, " the ");
1826 if (do_invis)
1827 Strcat(buf, "invisible ");
1828 Strcat(buf, pm_name);
1829 return buf;
1830 }
1831
1832 /* Put the adjectives in the buffer */
1833 if (adjective)
1834 Strcat(strcat(buf, adjective), " ");
1835 if (do_invis)
1836 Strcat(buf, "invisible ");
1837 if (do_saddle && (mtmp->misc_worn_check & W_SADDLE) && !Blind
1838 && !Hallucination)
1839 Strcat(buf, "saddled ");
1840 has_adjectives = (buf[0] != '\0');
1841
1842 /* Put the actual monster name or type into the buffer now.
1843 Remember whether the buffer starts with a personal name. */
1844 if (do_hallu) {
1845 char rnamecode;
1846 char *rname = rndmonnam(&rnamecode);
1847
1848 Strcat(buf, rname);
1849 name_at_start = bogon_is_pname(rnamecode);
1850 } else if (do_name && has_mgivenname(mtmp)) {
1851 char *name = MGIVENNAME(mtmp);
1852
1853 if (mdat == &mons[PM_GHOST]) {
1854 Sprintf(eos(buf), "%s ghost", s_suffix(name));
1855 name_at_start = TRUE;
1856 } else if (has_ebones(mtmp)) {
1857 Sprintf(eos(buf), "%s of %s", pm_name, name);
1858 name_at_start = FALSE;
1859 } else if (called) {
1860 Sprintf(eos(buf), "%s called %s", pm_name, name);
1861 name_at_start = (boolean) type_is_pname(mdat);
1862 } else if (is_mplayer(mdat) && (bp = strstri(name, " the ")) != 0) {
1863 /* <name> the <adjective> <invisible> <saddled> <rank> */
1864 char pbuf[BUFSZ];
1865
1866 Strcpy(pbuf, name);
1867 pbuf[bp - name + 5] = '\0'; /* adjectives right after " the " */
1868 if (has_adjectives)
1869 Strcat(pbuf, buf);
1870 Strcat(pbuf, bp + 5); /* append the rest of the name */
1871 Strcpy(buf, pbuf);
1872 article = ARTICLE_NONE;
1873 name_at_start = TRUE;
1874 } else {
1875 Strcat(buf, name);
1876 name_at_start = TRUE;
1877 }
1878 } else if (is_mplayer(mdat) && !In_endgame(&u.uz)) {
1879 char pbuf[BUFSZ];
1880
1881 Strcpy(pbuf, rank_of((int) mtmp->m_lev, monsndx(mdat),
1882 (boolean) mtmp->female));
1883 Strcat(buf, lcase(pbuf));
1884 name_at_start = FALSE;
1885 } else {
1886 Strcat(buf, pm_name);
1887 name_at_start = (boolean) type_is_pname(mdat);
1888 }
1889
1890 if (name_at_start && (article == ARTICLE_YOUR || !has_adjectives)) {
1891 if (mdat == &mons[PM_WIZARD_OF_YENDOR])
1892 article = ARTICLE_THE;
1893 else
1894 article = ARTICLE_NONE;
1895 } else if ((mdat->geno & G_UNIQ) && article == ARTICLE_A) {
1896 article = ARTICLE_THE;
1897 }
1898
1899 {
1900 char buf2[BUFSZ];
1901
1902 switch (article) {
1903 case ARTICLE_YOUR:
1904 Strcpy(buf2, "your ");
1905 Strcat(buf2, buf);
1906 Strcpy(buf, buf2);
1907 return buf;
1908 case ARTICLE_THE:
1909 Strcpy(buf2, "the ");
1910 Strcat(buf2, buf);
1911 Strcpy(buf, buf2);
1912 return buf;
1913 case ARTICLE_A:
1914 return an(buf);
1915 case ARTICLE_NONE:
1916 default:
1917 return buf;
1918 }
1919 }
1920 }
1921
1922 char *
l_monnam(struct monst * mtmp)1923 l_monnam(struct monst *mtmp)
1924 {
1925 return x_monnam(mtmp, ARTICLE_NONE, (char *) 0,
1926 (has_mgivenname(mtmp)) ? SUPPRESS_SADDLE : 0, TRUE);
1927 }
1928
1929 char *
mon_nam(struct monst * mtmp)1930 mon_nam(struct monst *mtmp)
1931 {
1932 return x_monnam(mtmp, ARTICLE_THE, (char *) 0,
1933 (has_mgivenname(mtmp)) ? SUPPRESS_SADDLE : 0, FALSE);
1934 }
1935
1936 /* print the name as if mon_nam() was called, but assume that the player
1937 * can always see the monster--used for probing and for monsters aggravating
1938 * the player with a cursed potion of invisibility
1939 */
1940 char *
noit_mon_nam(struct monst * mtmp)1941 noit_mon_nam(struct monst *mtmp)
1942 {
1943 return x_monnam(mtmp, ARTICLE_THE, (char *) 0,
1944 (has_mgivenname(mtmp)) ? (SUPPRESS_SADDLE | SUPPRESS_IT)
1945 : SUPPRESS_IT,
1946 FALSE);
1947 }
1948
1949 char *
Monnam(struct monst * mtmp)1950 Monnam(struct monst *mtmp)
1951 {
1952 register char *bp = mon_nam(mtmp);
1953
1954 *bp = highc(*bp);
1955 return bp;
1956 }
1957
1958 char *
noit_Monnam(struct monst * mtmp)1959 noit_Monnam(struct monst *mtmp)
1960 {
1961 register char *bp = noit_mon_nam(mtmp);
1962
1963 *bp = highc(*bp);
1964 return bp;
1965 }
1966
1967 /* return "a dog" rather than "Fido", honoring hallucination and visibility */
1968 char *
noname_monnam(struct monst * mtmp,int article)1969 noname_monnam(struct monst *mtmp, int article)
1970 {
1971 return x_monnam(mtmp, article, (char *) 0, SUPPRESS_NAME, FALSE);
1972 }
1973
1974 /* monster's own name -- overrides hallucination and [in]visibility
1975 so shouldn't be used in ordinary messages (mainly for disclosure) */
1976 char *
m_monnam(struct monst * mtmp)1977 m_monnam(struct monst *mtmp)
1978 {
1979 return x_monnam(mtmp, ARTICLE_NONE, (char *) 0, EXACT_NAME, FALSE);
1980 }
1981
1982 /* pet name: "your little dog" */
1983 char *
y_monnam(struct monst * mtmp)1984 y_monnam(struct monst *mtmp)
1985 {
1986 int prefix, suppression_flag;
1987
1988 prefix = mtmp->mtame ? ARTICLE_YOUR : ARTICLE_THE;
1989 suppression_flag = (has_mgivenname(mtmp)
1990 /* "saddled" is redundant when mounted */
1991 || mtmp == u.usteed)
1992 ? SUPPRESS_SADDLE
1993 : 0;
1994
1995 return x_monnam(mtmp, prefix, (char *) 0, suppression_flag, FALSE);
1996 }
1997
1998 char *
adj_monnam(struct monst * mtmp,const char * adj)1999 adj_monnam(struct monst *mtmp, const char *adj)
2000 {
2001 return x_monnam(mtmp, ARTICLE_THE, adj,
2002 has_mgivenname(mtmp) ? SUPPRESS_SADDLE : 0, FALSE);
2003 }
2004
2005 /* Equivalent to adj_monnam(), but uppercases the result. */
2006 char *
Adjmonnam(struct monst * mtmp,const char * adj)2007 Adjmonnam(struct monst *mtmp, const char *adj)
2008 {
2009 char *bp = adj_monnam(mtmp, adj);
2010 *bp = highc(*bp);
2011 return bp;
2012 }
2013
2014 char *
a_monnam(struct monst * mtmp)2015 a_monnam(struct monst *mtmp)
2016 {
2017 return x_monnam(mtmp, ARTICLE_A, (char *) 0,
2018 has_mgivenname(mtmp) ? SUPPRESS_SADDLE : 0, FALSE);
2019 }
2020
2021 char *
Amonnam(struct monst * mtmp)2022 Amonnam(struct monst *mtmp)
2023 {
2024 char *bp = a_monnam(mtmp);
2025
2026 *bp = highc(*bp);
2027 return bp;
2028 }
2029
2030 /* used for monster ID by the '/', ';', and 'C' commands to block remote
2031 identification of the endgame altars via their attending priests */
2032 char *
distant_monnam(struct monst * mon,int article,char * outbuf)2033 distant_monnam(struct monst *mon,
2034 int article, /* only ARTICLE_NONE and ARTICLE_THE
2035 are handled here */
2036 char *outbuf)
2037 {
2038 /* high priest(ess)'s identity is concealed on the Astral Plane,
2039 unless you're adjacent (overridden for hallucination which does
2040 its own obfuscation) */
2041 if (mon->data == &mons[PM_HIGH_CLERIC] && !Hallucination
2042 && Is_astralevel(&u.uz) && distu(mon->mx, mon->my) > 2) {
2043 Strcpy(outbuf, article == ARTICLE_THE ? "the " : "");
2044 Strcat(outbuf, mon->female ? "high priestess" : "high priest");
2045 } else {
2046 Strcpy(outbuf, x_monnam(mon, article, (char *) 0, 0, TRUE));
2047 }
2048 return outbuf;
2049 }
2050
2051 /* returns mon_nam(mon) relative to other_mon; normal name unless they're
2052 the same, in which case the reference is to {him|her|it} self */
2053 char *
mon_nam_too(struct monst * mon,struct monst * other_mon)2054 mon_nam_too(struct monst *mon, struct monst *other_mon)
2055 {
2056 char *outbuf;
2057
2058 if (mon != other_mon) {
2059 outbuf = mon_nam(mon);
2060 } else {
2061 outbuf = nextmbuf();
2062 switch (pronoun_gender(mon, PRONOUN_HALLU)) {
2063 case 0:
2064 Strcpy(outbuf, "himself");
2065 break;
2066 case 1:
2067 Strcpy(outbuf, "herself");
2068 break;
2069 default:
2070 case 2:
2071 Strcpy(outbuf, "itself");
2072 break;
2073 case 3: /* could happen when hallucinating */
2074 Strcpy(outbuf, "themselves");
2075 break;
2076 }
2077 }
2078 return outbuf;
2079 }
2080
2081 /* construct "<monnamtext> <verb> <othertext> {him|her|it}self" which might
2082 be distorted by Hallu; if that's plural, adjust monnamtext and verb */
2083 char *
monverbself(struct monst * mon,char * monnamtext,const char * verb,const char * othertext)2084 monverbself(struct monst *mon,
2085 char *monnamtext, /* modifiable 'mbuf' with adequare room at end */
2086 const char *verb, const char *othertext)
2087 {
2088 char *verbs, selfbuf[40]; /* sizeof "themselves" suffices */
2089
2090 /* "himself"/"herself"/"itself", maybe "themselves" if hallucinating */
2091 Strcpy(selfbuf, mon_nam_too(mon, mon));
2092 /* verb starts plural; this will yield singular except for "themselves" */
2093 verbs = vtense(selfbuf, verb);
2094 if (!strcmp(verb, verbs)) { /* a match indicates that it stayed plural */
2095 monnamtext = makeplural(monnamtext);
2096 /* for "it", makeplural() produces "them" but we want "they" */
2097 if (!strcmpi(monnamtext, genders[3].he)) {
2098 boolean capitaliz = (monnamtext[0] == highc(monnamtext[0]));
2099
2100 Strcpy(monnamtext, genders[3].him);
2101 if (capitaliz)
2102 monnamtext[0] = highc(monnamtext[0]);
2103 }
2104 }
2105 Strcat(strcat(monnamtext, " "), verbs);
2106 if (othertext && *othertext)
2107 Strcat(strcat(monnamtext, " "), othertext);
2108 Strcat(strcat(monnamtext, " "), selfbuf);
2109 return monnamtext;
2110 }
2111
2112 /* for debugging messages, where data might be suspect and we aren't
2113 taking what the hero does or doesn't know into consideration */
2114 char *
minimal_monnam(struct monst * mon,boolean ckloc)2115 minimal_monnam(struct monst *mon, boolean ckloc)
2116 {
2117 struct permonst *ptr;
2118 char *outbuf = nextmbuf();
2119
2120 if (!mon) {
2121 Strcpy(outbuf, "[Null monster]");
2122 } else if ((ptr = mon->data) == 0) {
2123 Strcpy(outbuf, "[Null mon->data]");
2124 } else if (ptr < &mons[0]) {
2125 Sprintf(outbuf, "[Invalid mon->data %s < %s]",
2126 fmt_ptr((genericptr_t) mon->data),
2127 fmt_ptr((genericptr_t) &mons[0]));
2128 } else if (ptr >= &mons[NUMMONS]) {
2129 Sprintf(outbuf, "[Invalid mon->data %s >= %s]",
2130 fmt_ptr((genericptr_t) mon->data),
2131 fmt_ptr((genericptr_t) &mons[NUMMONS]));
2132 } else if (ckloc && ptr == &mons[PM_LONG_WORM]
2133 && g.level.monsters[mon->mx][mon->my] != mon) {
2134 Sprintf(outbuf, "%s <%d,%d>",
2135 pmname(&mons[PM_LONG_WORM_TAIL], Mgender(mon)),
2136 mon->mx, mon->my);
2137 } else {
2138 Sprintf(outbuf, "%s%s <%d,%d>",
2139 mon->mtame ? "tame " : mon->mpeaceful ? "peaceful " : "",
2140 pmname(mon->data, Mgender(mon)), mon->mx, mon->my);
2141 if (mon->cham != NON_PM)
2142 Sprintf(eos(outbuf), "{%s}",
2143 pmname(&mons[mon->cham], Mgender(mon)));
2144 }
2145 return outbuf;
2146 }
2147
2148 #ifndef PMNAME_MACROS
2149 int
Mgender(struct monst * mtmp)2150 Mgender(struct monst *mtmp)
2151 {
2152 int mgender = MALE;
2153
2154 if (mtmp == &g.youmonst) {
2155 if (Upolyd ? u.mfemale : flags.female)
2156 mgender = FEMALE;
2157 } else if (mtmp->female) {
2158 mgender = FEMALE;
2159 }
2160 return mgender;
2161 }
2162
2163 const char *
pmname(struct permonst * pm,int mgender)2164 pmname(struct permonst *pm, int mgender)
2165 {
2166 if ((mgender >= MALE && mgender < NUM_MGENDERS) && pm->pmnames[mgender])
2167 return pm->pmnames[mgender];
2168 else
2169 return pm->pmnames[NEUTRAL];
2170 }
2171 #endif /* PMNAME_MACROS */
2172
2173 /* fake monsters used to be in a hard-coded array, now in a data file
2174 * The which parameter is passed along to get_rnd_text and represents the line
2175 * of BOGUSMONFILE to return. Specify -1 for a random one. */
2176 char *
bogusmon(char * buf,char * code,int which)2177 bogusmon(char *buf, char *code, int which)
2178 {
2179 static const char bogon_codes[] = "-_+|="; /* see dat/bonusmon.txt */
2180 char *mnam = buf;
2181
2182 if (code)
2183 *code = '\0';
2184 /* might fail (return empty buf[]) if the file isn't available */
2185 get_rnd_text(BOGUSMONFILE, buf, which, rn2_on_display_rng);
2186 if (!*mnam) {
2187 Strcpy(buf, "bogon");
2188 } else if (index(bogon_codes, *mnam)) { /* strip prefix if present */
2189 if (code)
2190 *code = *mnam;
2191 ++mnam;
2192 }
2193 return mnam;
2194 }
2195
2196 /* return a random monster name, for hallucination */
2197 char *
rndmonnam(char * code)2198 rndmonnam(char *code)
2199 {
2200 static char buf[BUFSZ];
2201 char *mnam;
2202 int name;
2203 #define BOGUSMONSIZE 100 /* arbitrary */
2204
2205 if (code)
2206 *code = '\0';
2207
2208 do {
2209 name = rn2_on_display_rng(SPECIAL_PM + BOGUSMONSIZE - LOW_PM) + LOW_PM;
2210 } while (name < SPECIAL_PM
2211 && (type_is_pname(&mons[name]) || (mons[name].geno & G_NOGEN)));
2212
2213 if (name >= SPECIAL_PM) {
2214 mnam = bogusmon(buf, code, -1);
2215 } else {
2216 mnam = strcpy(buf, pmname(&mons[name], rn2_on_display_rng(2)));
2217 }
2218 return mnam;
2219 #undef BOGUSMONSIZE
2220 }
2221
2222 /* check bogusmon prefix to decide whether it's a personal name */
2223 boolean
bogon_is_pname(char code)2224 bogon_is_pname(char code)
2225 {
2226 if (!code)
2227 return FALSE;
2228 return index("-+=", code) ? TRUE : FALSE;
2229 }
2230
2231 /* name of a Rogue player */
2232 const char *
roguename(void)2233 roguename(void)
2234 {
2235 char *i, *opts;
2236
2237 if ((opts = nh_getenv("ROGUEOPTS")) != 0) {
2238 for (i = opts; *i; i++)
2239 if (!strncmp("name=", i, 5)) {
2240 char *j;
2241 if ((j = index(i + 5, ',')) != 0)
2242 *j = (char) 0;
2243 return i + 5;
2244 }
2245 }
2246 return rn2(3) ? (rn2(2) ? "Michael Toy" : "Kenneth Arnold")
2247 : "Glenn Wichman";
2248 }
2249
2250 static NEARDATA const char *const hcolors[] = {
2251 "ultraviolet", "infrared", "bluish-orange", "reddish-green", "dark white",
2252 "light black", "sky blue-pink", "pinkish-cyan", "indigo-chartreuse",
2253 "excitingly dull", "salty", "sweet", "sour", "bitter", "savory", "umami",
2254 "neon", "fluorescent", "phosphorescent", "translucent", "opaque",
2255 "psychedelic", "iridescent", "rainbow-colored", "polychromatic",
2256 "colorless", "colorless green",
2257 "striped", "spiral", "swirly", "plaid", "checkered", "argyle", "paisley",
2258 "blotchy", "guernsey-spotted", "polka-dotted", "square", "round",
2259 "triangular", "cabernet", "sangria", "fuchsia", "wisteria", "lemon-lime",
2260 "strawberry-banana", "peppermint", "romantic", "incandescent",
2261 "octarine", /* Discworld: the Colour of Magic */
2262 };
2263
2264 const char *
hcolor(const char * colorpref)2265 hcolor(const char *colorpref)
2266 {
2267 return (Hallucination || !colorpref)
2268 ? hcolors[rn2_on_display_rng(SIZE(hcolors))]
2269 : colorpref;
2270 }
2271
2272 /* return a random real color unless hallucinating */
2273 const char *
rndcolor(void)2274 rndcolor(void)
2275 {
2276 int k = rn2(CLR_MAX);
2277
2278 return Hallucination ? hcolor((char *) 0)
2279 : (k == NO_COLOR) ? "colorless"
2280 : c_obj_colors[k];
2281 }
2282
2283 static NEARDATA const char *const hliquids[] = {
2284 "yoghurt", "oobleck", "clotted blood", "diluted water", "purified water",
2285 "instant coffee", "tea", "herbal infusion", "liquid rainbow",
2286 "creamy foam", "mulled wine", "bouillon", "nectar", "grog", "flubber",
2287 "ketchup", "slow light", "oil", "vinaigrette", "liquid crystal", "honey",
2288 "caramel sauce", "ink", "aqueous humour", "milk substitute",
2289 "fruit juice", "glowing lava", "gastric acid", "mineral water",
2290 "cough syrup", "quicksilver", "sweet vitriol", "grey goo", "pink slime",
2291 /* "new coke (tm)", --better not */
2292 };
2293
2294 /* if hallucinating, return a random liquid instead of 'liquidpref' */
2295 const char *
hliquid(const char * liquidpref)2296 hliquid(const char *liquidpref) /* use as-is when not hallucinating (unless empty) */
2297 {
2298 if (Hallucination || !liquidpref || !*liquidpref) {
2299 int indx, count = SIZE(hliquids);
2300
2301 /* if we have a non-hallucinatory default value, include it
2302 among the choices */
2303 if (liquidpref && *liquidpref)
2304 ++count;
2305 indx = rn2_on_display_rng(count);
2306 if (indx < SIZE(hliquids))
2307 return hliquids[indx];
2308 }
2309 return liquidpref;
2310 }
2311
2312 /* Aliases for road-runner nemesis
2313 */
2314 static const char *const coynames[] = {
2315 "Carnivorous Vulgaris", "Road-Runnerus Digestus", "Eatibus Anythingus",
2316 "Famishus-Famishus", "Eatibus Almost Anythingus", "Eatius Birdius",
2317 "Famishius Fantasticus", "Eternalii Famishiis", "Famishus Vulgarus",
2318 "Famishius Vulgaris Ingeniusi", "Eatius-Slobbius", "Hardheadipus Oedipus",
2319 "Carnivorous Slobbius", "Hard-Headipus Ravenus", "Evereadii Eatibus",
2320 "Apetitius Giganticus", "Hungrii Flea-Bagius", "Overconfidentii Vulgaris",
2321 "Caninus Nervous Rex", "Grotesques Appetitus", "Nemesis Ridiculii",
2322 "Canis latrans"
2323 };
2324
2325 char *
coyotename(struct monst * mtmp,char * buf)2326 coyotename(struct monst *mtmp, char *buf)
2327 {
2328 if (mtmp && buf) {
2329 Sprintf(buf, "%s - %s",
2330 x_monnam(mtmp, ARTICLE_NONE, (char *) 0, 0, TRUE),
2331 mtmp->mcan ? coynames[SIZE(coynames) - 1]
2332 : coynames[mtmp->m_id % (SIZE(coynames) - 1)]);
2333 }
2334 return buf;
2335 }
2336
2337 char *
rndorcname(char * s)2338 rndorcname(char *s)
2339 {
2340 static const char *v[] = { "a", "ai", "og", "u" };
2341 static const char *snd[] = { "gor", "gris", "un", "bane", "ruk",
2342 "oth","ul", "z", "thos","akh","hai" };
2343 int i, iend = rn1(2, 3), vstart = rn2(2);
2344
2345 if (s) {
2346 *s = '\0';
2347 for (i = 0; i < iend; ++i) {
2348 vstart = 1 - vstart; /* 0 -> 1, 1 -> 0 */
2349 Sprintf(eos(s), "%s%s", (i > 0 && !rn2(30)) ? "-" : "",
2350 vstart ? v[rn2(SIZE(v))] : snd[rn2(SIZE(snd))]);
2351 }
2352 }
2353 return s;
2354 }
2355
2356 struct monst *
christen_orc(struct monst * mtmp,const char * gang,const char * other)2357 christen_orc(struct monst *mtmp, const char *gang, const char *other)
2358 {
2359 int sz = 0;
2360 char buf[BUFSZ], buf2[BUFSZ], *orcname;
2361
2362 orcname = rndorcname(buf2);
2363 sz = (int) strlen(orcname);
2364 if (gang)
2365 sz += (int) (strlen(gang) + sizeof " of " - sizeof "");
2366 else if (other)
2367 sz += (int) strlen(other);
2368
2369 if (sz < BUFSZ) {
2370 char gbuf[BUFSZ];
2371 boolean nameit = FALSE;
2372
2373 if (gang && orcname) {
2374 Sprintf(buf, "%s of %s", upstart(orcname),
2375 upstart(strcpy(gbuf, gang)));
2376 nameit = TRUE;
2377 } else if (other && orcname) {
2378 Sprintf(buf, "%s%s", upstart(orcname), other);
2379 nameit = TRUE;
2380 }
2381 if (nameit)
2382 mtmp = christen_monst(mtmp, buf);
2383 }
2384 return mtmp;
2385 }
2386
2387 /* make sure "The Colour of Magic" remains the first entry in here */
2388 static const char *const sir_Terry_novels[] = {
2389 "The Colour of Magic", "The Light Fantastic", "Equal Rites", "Mort",
2390 "Sourcery", "Wyrd Sisters", "Pyramids", "Guards! Guards!", "Eric",
2391 "Moving Pictures", "Reaper Man", "Witches Abroad", "Small Gods",
2392 "Lords and Ladies", "Men at Arms", "Soul Music", "Interesting Times",
2393 "Maskerade", "Feet of Clay", "Hogfather", "Jingo", "The Last Continent",
2394 "Carpe Jugulum", "The Fifth Elephant", "The Truth", "Thief of Time",
2395 "The Last Hero", "The Amazing Maurice and His Educated Rodents",
2396 "Night Watch", "The Wee Free Men", "Monstrous Regiment",
2397 "A Hat Full of Sky", "Going Postal", "Thud!", "Wintersmith",
2398 "Making Money", "Unseen Academicals", "I Shall Wear Midnight", "Snuff",
2399 "Raising Steam", "The Shepherd's Crown"
2400 };
2401
2402 const char *
noveltitle(int * novidx)2403 noveltitle(int *novidx)
2404 {
2405 int j, k = SIZE(sir_Terry_novels);
2406
2407 j = rn2(k);
2408 if (novidx) {
2409 if (*novidx == -1)
2410 *novidx = j;
2411 else if (*novidx >= 0 && *novidx < k)
2412 j = *novidx;
2413 }
2414 return sir_Terry_novels[j];
2415 }
2416
2417 /* figure out canonical novel title from player-specified one */
2418 const char *
lookup_novel(const char * lookname,int * idx)2419 lookup_novel(const char *lookname, int *idx)
2420 {
2421 int k;
2422
2423 /*
2424 * Accept variant spellings:
2425 * _The_Colour_of_Magic_ uses British spelling, and American
2426 * editions keep that, but we also recognize American spelling;
2427 * _Sourcery_ is a joke rather than British spelling of "sorcery".
2428 */
2429 if (!strcmpi(The(lookname), "The Color of Magic"))
2430 lookname = sir_Terry_novels[0];
2431 else if (!strcmpi(lookname, "Sorcery"))
2432 lookname = "Sourcery"; /* [4] */
2433
2434 for (k = 0; k < SIZE(sir_Terry_novels); ++k) {
2435 if (!strcmpi(lookname, sir_Terry_novels[k])
2436 || !strcmpi(The(lookname), sir_Terry_novels[k])) {
2437 if (idx)
2438 *idx = k;
2439 return sir_Terry_novels[k];
2440 }
2441 }
2442 /* name not found; if novelidx is already set, override the name */
2443 if (idx && *idx >= 0 && *idx < SIZE(sir_Terry_novels))
2444 return sir_Terry_novels[*idx];
2445
2446 return (const char *) 0;
2447 }
2448
2449 /* Most of these are actual names of nymphs from mythology. */
2450 const char* nymphnames[] = {
2451 "Erythea", "Hesperia", "Arethusa", "Pasithea", "Thaleia", "Halimede",
2452 "Actaea", "Electra", "Maia", "Nesaea", "Alcyone", "Asterope",
2453 "Callianeira", "Nausithoe", "Dione", "Thetis", "Ephyra", "Eulimene",
2454 "Nerea", "Laomedeia", "Echo", "Maera", "Eurydice", "Lysianassa", "Phoebe",
2455 "Daphnis", "Daphnae", "Melinoe", "Othreis", "Polychrome"
2456 };
2457
2458 const char* maldemonnames[] = {
2459 "Agiel", "Kali", "Amon", "Foras", "Armaros", "Orias", "Malthus", "Asag",
2460 "Raum", "Iblis", "Vanth", "Bael", "Leonard", "Barbas", "Charun", "Ishmael",
2461 "Balthamel", "Rahvin"
2462 };
2463
2464 const char* femdemonnames[] = {
2465 "Mara", "Lamia", "Meraxes", "Daeva", "Amy", "Lilith", "Aliss", "Berith",
2466 "Euryale", "Zorya", "Rhaenyra", "Bellatrix", "Rusalka", "Messaana",
2467 "Jadis", "Anzu", "Eve", "Bilquis", "Cyndane", "Vanessa", "Graendal"
2468 };
2469 #define rnd_name(list) list[rn2(SIZE(list))]
2470
2471 /* Monster introduces themselves. If they're not currently named, give them a
2472 * random name from the specified list. */
2473 void
mintroduce(struct monst * mtmp)2474 mintroduce(struct monst *mtmp)
2475 {
2476 if (!has_mgivenname(mtmp)) {
2477 const char* name;
2478 if (mtmp->data->mlet == S_NYMPH) {
2479 name = rnd_name(nymphnames);
2480 }
2481 else if (is_demon(mtmp->data)) {
2482 if (mtmp->female)
2483 name = rnd_name(femdemonnames);
2484 else
2485 name = rnd_name(maldemonnames);
2486 }
2487 else {
2488 impossible("mintroduce: monster type %s has no defined names!",
2489 mtmp->data->pmnames[NEUTRAL]);
2490 return;
2491 }
2492 const char* pronoun =
2493 genders[is_neuter(mtmp->data) ? 2 : mtmp->female].him;
2494 if (!Deaf) {
2495 pline("%s introduces %sself to you as %s.", Monnam(mtmp), pronoun,
2496 name);
2497 christen_monst(mtmp, name);
2498 } else {
2499 pline("%s seems to be introducing %sself, but you can't hear %s.",
2500 Monnam(mtmp), pronoun, pronoun);
2501 }
2502 }
2503 return;
2504 }
2505
2506 /* Return a string describing how wounded a monster is.
2507 * This is in do_name.c only because it gives us access to nextmbuf().
2508 */
2509 char *
mon_wounds(struct monst * mon,boolean addspace,boolean clarity)2510 mon_wounds(struct monst *mon,
2511 boolean addspace, /* add ' ' to end of string */
2512 boolean clarity) /* end of game: don't decline for non-healer or
2513 * obfuscate for hallucination */
2514 {
2515 char *outbuf = nextmbuf();
2516 const struct permonst *mdat = mon->data;
2517 const char *adverb, *adjective;
2518
2519 /* Cases where you can't see wounds. */
2520 if (!clarity
2521 && (!Role_if(PM_HEALER) || !canseemon(mon)
2522 || (u.uswallow && mon == u.ustuck && Blind))) {
2523 return NULL;
2524 }
2525 if (DEADMONSTER(mon)) {
2526 return NULL;
2527 }
2528 if (mon->mhp == mon->mhpmax) {
2529 Sprintf(outbuf, "uninjured%s", addspace ? " " : "");
2530 return outbuf;
2531 }
2532
2533 adjective = is_fleshy(mdat) ? "wounded" : "damaged";
2534
2535 if (Hallucination && !clarity) {
2536 const char *wound_adverbs[] = {
2537 "mildly", "mostly", "somewhat", "lightly", "massively", "extremely",
2538 "flagrantly", "flamboyantly", "supremely", "excessively", "truly",
2539 "terribly", "incredibly", "unbelievably", "obscenely", "insanely",
2540 "amazingly", "absolutely"
2541 };
2542 const char *wound_adjectives[] = {
2543 "bloodied", "bruised", "crushed", "marred", "blemished",
2544 "dinged up", "totaled", "wrecked", "beaten up", "defaced",
2545 "pulverized", "battered", "tarnished", "ruined", "mangled",
2546 "trashed", "smashed up", "crumpled", "scratched", "scraped"
2547 };
2548 adverb = wound_adverbs[rn2(SIZE(wound_adverbs))];
2549 adjective = wound_adjectives[rn2(SIZE(wound_adjectives))];
2550 }
2551 else if (mon->mhp * 6 <= mon->mhpmax) { /* <= 16.6% */
2552 adverb = "critically";
2553 }
2554 else if (mon->mhp * 4 <= mon->mhpmax) { /* <= 25% */
2555 adverb = "badly";
2556 }
2557 else if (mon->mhp * 3 <= mon->mhpmax) { /* <= 33.3% */
2558 adverb = "seriously";
2559 }
2560 else if (mon->mhp * 4 <= mon->mhpmax * 3) { /* <= 75% */
2561 adverb = "moderately";
2562 }
2563 else { /* < 100% */
2564 adverb = "slightly";
2565 }
2566
2567 Sprintf(outbuf, "%s %s%s", adverb, adjective, addspace ? " " : "");
2568 return outbuf;
2569 }
2570
2571 /* Print a message indicating that mon is wounded.
2572 * It will only print something if the monster's wound adjective changes (e.g.
2573 * if it goes from slightly wounded to moderately wounded).
2574 * pre_wound_hp is its HP amount before being damaged in the caller of this
2575 * function; the caller must save its initial HP to pass through to this.
2576 */
2577 void
print_mon_wounded(struct monst * mon,int pre_wound_hp)2578 print_mon_wounded(struct monst *mon, int pre_wound_hp)
2579 {
2580 int mcurrhp;
2581 char *m_old_wounds, *m_curr_wounds;
2582
2583 if (!mon) {
2584 impossible("print_mon_wounded: null mon!");
2585 return;
2586 }
2587 if (!Role_if(PM_HEALER)) {
2588 return; /* optimization; redundant with healer check in mon_wounds */
2589 }
2590 if (mon->mhp == pre_wound_hp && !Hallucination) {
2591 /* impossible for adjective to change */
2592 return;
2593 }
2594
2595 mcurrhp = mon->mhp;
2596 mon->mhp = pre_wound_hp;
2597 m_old_wounds = mon_wounds(mon, FALSE, FALSE);
2598 mon->mhp = mcurrhp;
2599
2600 m_curr_wounds = mon_wounds(mon, FALSE, FALSE);
2601
2602 if (m_curr_wounds != NULL
2603 && (Hallucination || m_old_wounds == NULL
2604 || strcmp(m_old_wounds, m_curr_wounds))) {
2605 pline("%s is %s.", Monnam(mon), m_curr_wounds);
2606 }
2607 }
2608
2609 /*do_name.c*/
2610