NHDT->ANH, nethack->anethack, nhdat->anhdat
[aNetHack.git] / src / pline.c
bloba093176d16c804213934fbf793e76047d1e5153f
1 /* aNetHack 0.0.1 pline.c $ANH-Date: 1489192905 2017/03/11 00:41:45 $ $ANH-Branch: master $:$ANH-Revision: 1.57 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* aNetHack may be freely redistributed. See license for details. */
5 #define NEED_VARARGS /* Uses ... */ /* comment line for pre-compiled headers \
6 */
7 #include "hack.h"
9 static boolean no_repeat = FALSE;
10 static char prevmsg[BUFSZ];
12 static char *FDECL(You_buf, (int));
13 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
14 static void FDECL(execplinehandler, (const char *));
15 #endif
17 #ifdef DUMPLOG
18 /* also used in end.c */
19 unsigned saved_pline_index = 0; /* slot in saved_plines[] to use next */
20 char *saved_plines[DUMPLOG_MSG_COUNT] = { (char *) 0 };
22 /* keep the most recent DUMPLOG_MSG_COUNT messages */
23 void
24 dumplogmsg(line)
25 const char *line;
28 * TODO:
29 * This essentially duplicates message history, which is
30 * currently implemented in an interface-specific manner.
31 * The core should take responsibility for that and have
32 * this share it.
34 unsigned indx = saved_pline_index; /* next slot to use */
35 char *oldest = saved_plines[indx]; /* current content of that slot */
37 if (oldest && strlen(oldest) >= strlen(line)) {
38 /* this buffer will gradually shrink until the 'else' is needed;
39 there's no pressing need to track allocation size instead */
40 Strcpy(oldest, line);
41 } else {
42 if (oldest)
43 free((genericptr_t) oldest);
44 saved_plines[indx] = dupstr(line);
46 saved_pline_index = (indx + 1) % DUMPLOG_MSG_COUNT;
49 /* called during save (unlike the interface-specific message history,
50 this data isn't saved and restored); end-of-game releases saved_pline[]
51 while writing its contents to the final dump log */
52 void
53 dumplogfreemessages()
55 unsigned indx;
57 for (indx = 0; indx < DUMPLOG_MSG_COUNT; ++indx)
58 if (saved_plines[indx])
59 free((genericptr_t) saved_plines[indx]), saved_plines[indx] = 0;
60 saved_pline_index = 0;
62 #endif
64 /*VARARGS1*/
65 /* Note that these declarations rely on knowledge of the internals
66 * of the variable argument handling stuff in "tradstdc.h"
69 #if defined(USE_STDARG) || defined(USE_VARARGS)
70 static void FDECL(vpline, (const char *, va_list));
72 void pline
73 VA_DECL(const char *, line)
75 VA_START(line);
76 VA_INIT(line, char *);
77 vpline(line, VA_ARGS);
78 VA_END();
81 # ifdef USE_STDARG
82 static void
83 vpline(const char *line, va_list the_args)
84 # else
85 static void
86 vpline(line, the_args)
87 const char *line;
88 va_list the_args;
89 # endif
91 #else /* USE_STDARG | USE_VARARG */
93 # define vpline pline
95 void pline
96 VA_DECL(const char *, line)
97 #endif /* USE_STDARG | USE_VARARG */
98 { /* start of vpline() or of nested block in USE_OLDARG's pline() */
99 char pbuf[3 * BUFSZ];
100 int ln;
101 xchar msgtyp;
102 /* Do NOT use VA_START and VA_END in here... see above */
104 if (!line || !*line)
105 return;
106 #ifdef HANGUPHANDLING
107 if (program_state.done_hup)
108 return;
109 #endif
110 if (program_state.wizkit_wishing)
111 return;
113 if (index(line, '%')) {
114 Vsprintf(pbuf, line, VA_ARGS);
115 line = pbuf;
117 if ((ln = (int) strlen(line)) > BUFSZ - 1) {
118 if (line != pbuf) /* no '%' was present */
119 (void) strncpy(pbuf, line, BUFSZ - 1); /* caveat: unterminated */
120 /* truncate, preserving the final 3 characters:
121 "___ extremely long text" -> "___ extremely l...ext"
122 (this may be suboptimal if overflow is less than 3) */
123 (void) strncpy(pbuf + BUFSZ - 1 - 6, "...", 3);
124 /* avoid strncpy; buffers could overlap if excess is small */
125 pbuf[BUFSZ - 1 - 3] = line[ln - 3];
126 pbuf[BUFSZ - 1 - 2] = line[ln - 2];
127 pbuf[BUFSZ - 1 - 1] = line[ln - 1];
128 pbuf[BUFSZ - 1] = '\0';
129 line = pbuf;
131 if (!iflags.window_inited) {
132 raw_print(line);
133 iflags.last_msg = PLNMSG_UNKNOWN;
134 return;
137 #ifdef DUMPLOG
138 /* We hook here early to have options-agnostic output.
139 * Unfortunately, that means Norep() isn't honored (general issue) and
140 * that short lines aren't combined into one longer one (tty behavior).
142 dumplogmsg(line);
143 #endif
145 msgtyp = msgtype_type(line, no_repeat);
146 if (msgtyp == MSGTYP_NOSHOW
147 || (msgtyp == MSGTYP_NOREP && !strcmp(line, prevmsg)))
148 return;
149 if (vision_full_recalc)
150 vision_recalc(0);
151 if (u.ux)
152 flush_screen(1); /* %% */
154 putstr(WIN_MESSAGE, 0, line);
156 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
157 execplinehandler(line);
158 #endif
160 /* this gets cleared after every pline message */
161 iflags.last_msg = PLNMSG_UNKNOWN;
162 (void) strncpy(prevmsg, line, BUFSZ), prevmsg[BUFSZ - 1] = '\0';
163 if (msgtyp == MSGTYP_STOP)
164 display_nhwindow(WIN_MESSAGE, TRUE); /* --more-- */
166 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
167 /* provide closing brace for the nested block
168 which immediately follows USE_OLDARGS's VA_DECL() */
169 VA_END();
170 #endif
173 /*VARARGS1*/
174 void Norep
175 VA_DECL(const char *, line)
177 VA_START(line);
178 VA_INIT(line, const char *);
179 no_repeat = TRUE;
180 vpline(line, VA_ARGS);
181 no_repeat = FALSE;
182 VA_END();
183 return;
186 /* work buffer for You(), &c and verbalize() */
187 static char *you_buf = 0;
188 static int you_buf_siz = 0;
190 static char *
191 You_buf(siz)
192 int siz;
194 if (siz > you_buf_siz) {
195 if (you_buf)
196 free((genericptr_t) you_buf);
197 you_buf_siz = siz + 10;
198 you_buf = (char *) alloc((unsigned) you_buf_siz);
200 return you_buf;
203 void
204 free_youbuf()
206 if (you_buf)
207 free((genericptr_t) you_buf), you_buf = (char *) 0;
208 you_buf_siz = 0;
211 /* `prefix' must be a string literal, not a pointer */
212 #define YouPrefix(pointer, prefix, text) \
213 Strcpy((pointer = You_buf((int) (strlen(text) + sizeof prefix))), prefix)
215 #define YouMessage(pointer, prefix, text) \
216 strcat((YouPrefix(pointer, prefix, text), pointer), text)
218 /*VARARGS1*/
219 void You
220 VA_DECL(const char *, line)
222 char *tmp;
223 VA_START(line);
224 VA_INIT(line, const char *);
225 vpline(YouMessage(tmp, "You ", line), VA_ARGS);
226 VA_END();
229 /*VARARGS1*/
230 void Your
231 VA_DECL(const char *, line)
233 char *tmp;
234 VA_START(line);
235 VA_INIT(line, const char *);
236 vpline(YouMessage(tmp, "Your ", line), VA_ARGS);
237 VA_END();
240 /*VARARGS1*/
241 void You_feel
242 VA_DECL(const char *, line)
244 char *tmp;
245 VA_START(line);
246 VA_INIT(line, const char *);
247 if (Unaware)
248 YouPrefix(tmp, "You dream that you feel ", line);
249 else
250 YouPrefix(tmp, "You feel ", line);
251 vpline(strcat(tmp, line), VA_ARGS);
252 VA_END();
255 /*VARARGS1*/
256 void You_cant
257 VA_DECL(const char *, line)
259 char *tmp;
260 VA_START(line);
261 VA_INIT(line, const char *);
262 vpline(YouMessage(tmp, "You can't ", line), VA_ARGS);
263 VA_END();
266 /*VARARGS1*/
267 void pline_The
268 VA_DECL(const char *, line)
270 char *tmp;
271 VA_START(line);
272 VA_INIT(line, const char *);
273 vpline(YouMessage(tmp, "The ", line), VA_ARGS);
274 VA_END();
277 /*VARARGS1*/
278 void There
279 VA_DECL(const char *, line)
281 char *tmp;
282 VA_START(line);
283 VA_INIT(line, const char *);
284 vpline(YouMessage(tmp, "There ", line), VA_ARGS);
285 VA_END();
288 /*VARARGS1*/
289 void You_hear
290 VA_DECL(const char *, line)
292 char *tmp;
294 if (Deaf || !flags.acoustics)
295 return;
296 VA_START(line);
297 VA_INIT(line, const char *);
298 if (Underwater)
299 YouPrefix(tmp, "You barely hear ", line);
300 else if (Unaware)
301 YouPrefix(tmp, "You dream that you hear ", line);
302 else
303 YouPrefix(tmp, "You hear ", line);
304 vpline(strcat(tmp, line), VA_ARGS);
305 VA_END();
308 /*VARARGS1*/
309 void You_see
310 VA_DECL(const char *, line)
312 char *tmp;
314 VA_START(line);
315 VA_INIT(line, const char *);
316 if (Unaware)
317 YouPrefix(tmp, "You dream that you see ", line);
318 else if (Blind) /* caller should have caught this... */
319 YouPrefix(tmp, "You sense ", line);
320 else
321 YouPrefix(tmp, "You see ", line);
322 vpline(strcat(tmp, line), VA_ARGS);
323 VA_END();
326 /* Print a message inside double-quotes.
327 * The caller is responsible for checking deafness.
328 * Gods can speak directly to you in spite of deafness.
330 /*VARARGS1*/
331 void verbalize
332 VA_DECL(const char *, line)
334 char *tmp;
336 VA_START(line);
337 VA_INIT(line, const char *);
338 tmp = You_buf((int) strlen(line) + sizeof "\"\"");
339 Strcpy(tmp, "\"");
340 Strcat(tmp, line);
341 Strcat(tmp, "\"");
342 vpline(tmp, VA_ARGS);
343 VA_END();
346 /*VARARGS1*/
347 /* Note that these declarations rely on knowledge of the internals
348 * of the variable argument handling stuff in "tradstdc.h"
351 #if defined(USE_STDARG) || defined(USE_VARARGS)
352 static void FDECL(vraw_printf, (const char *, va_list));
354 void raw_printf
355 VA_DECL(const char *, line)
357 VA_START(line);
358 VA_INIT(line, char *);
359 vraw_printf(line, VA_ARGS);
360 VA_END();
363 # ifdef USE_STDARG
364 static void
365 vraw_printf(const char *line, va_list the_args)
366 # else
367 static void
368 vraw_printf(line, the_args)
369 const char *line;
370 va_list the_args;
371 # endif
373 #else /* USE_STDARG | USE_VARARG */
375 void raw_printf
376 VA_DECL(const char *, line)
377 #endif
379 char pbuf[3 * BUFSZ];
380 int ln;
381 /* Do NOT use VA_START and VA_END in here... see above */
383 if (index(line, '%')) {
384 Vsprintf(pbuf, line, VA_ARGS);
385 line = pbuf;
387 if ((ln = (int) strlen(line)) > BUFSZ - 1) {
388 if (line != pbuf)
389 line = strncpy(pbuf, line, BUFSZ - 1);
390 /* unlike pline, we don't futz around to keep last few chars */
391 pbuf[BUFSZ - 1] = '\0'; /* terminate strncpy or truncate vsprintf */
393 raw_print(line);
394 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
395 execplinehandler(line);
396 #endif
397 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
398 VA_END(); /* (see vpline) */
399 #endif
402 /*VARARGS1*/
403 void impossible
404 VA_DECL(const char *, s)
406 char pbuf[2 * BUFSZ];
407 VA_START(s);
408 VA_INIT(s, const char *);
409 if (program_state.in_impossible)
410 panic("impossible called impossible");
412 program_state.in_impossible = 1;
413 Vsprintf(pbuf, s, VA_ARGS);
414 pbuf[BUFSZ - 1] = '\0'; /* sanity */
415 paniclog("impossible", pbuf);
416 pline("%s", pbuf);
417 pline("Program in disorder - perhaps you'd better #quit.");
418 program_state.in_impossible = 0;
419 VA_END();
422 const char *
423 align_str(alignment)
424 aligntyp alignment;
426 switch ((int) alignment) {
427 case A_CHAOTIC:
428 return "chaotic";
429 case A_NEUTRAL:
430 return "neutral";
431 case A_LAWFUL:
432 return "lawful";
433 case A_NONE:
434 return "unaligned";
436 return "unknown";
439 void
440 mstatusline(mtmp)
441 register struct monst *mtmp;
443 aligntyp alignment = mon_aligntyp(mtmp);
444 char info[BUFSZ], monnambuf[BUFSZ];
446 info[0] = 0;
447 if (mtmp->mtame) {
448 Strcat(info, ", tame");
449 if (wizard) {
450 Sprintf(eos(info), " (%d", mtmp->mtame);
451 if (!mtmp->isminion)
452 Sprintf(eos(info), "; hungry %ld; apport %d",
453 EDOG(mtmp)->hungrytime, EDOG(mtmp)->apport);
454 Strcat(info, ")");
456 } else if (mtmp->mpeaceful)
457 Strcat(info, ", peaceful");
459 if (mtmp->data == &mons[PM_LONG_WORM]) {
460 int segndx, nsegs = count_wsegs(mtmp);
462 /* the worm code internals don't consider the head of be one of
463 the worm's segments, but we count it as such when presenting
464 worm feedback to the player */
465 if (!nsegs) {
466 Strcat(info, ", single segment");
467 } else {
468 ++nsegs; /* include head in the segment count */
469 segndx = wseg_at(mtmp, bhitpos.x, bhitpos.y);
470 Sprintf(eos(info), ", %d%s of %d segments",
471 segndx, ordin(segndx), nsegs);
474 if (mtmp->cham >= LOW_PM && mtmp->data != &mons[mtmp->cham])
475 /* don't reveal the innate form (chameleon, vampire, &c),
476 just expose the fact that this current form isn't it */
477 Strcat(info, ", shapechanger");
478 /* pets eating mimic corpses mimic while eating, so this comes first */
479 if (mtmp->meating)
480 Strcat(info, ", eating");
481 /* a stethoscope exposes mimic before getting here so this
482 won't be relevant for it, but wand of probing doesn't */
483 if (mtmp->mundetected || mtmp->m_ap_type)
484 mhidden_description(mtmp, TRUE, eos(info));
485 if (mtmp->mcan)
486 Strcat(info, ", cancelled");
487 if (mtmp->mconf)
488 Strcat(info, ", confused");
489 if (mtmp->mblinded || !mtmp->mcansee)
490 Strcat(info, ", blind");
491 if (mtmp->mstun)
492 Strcat(info, ", stunned");
493 if (mtmp->msleeping)
494 Strcat(info, ", asleep");
495 #if 0 /* unfortunately mfrozen covers temporary sleep and being busy \
496 (donning armor, for instance) as well as paralysis */
497 else if (mtmp->mfrozen)
498 Strcat(info, ", paralyzed");
499 #else
500 else if (mtmp->mfrozen || !mtmp->mcanmove)
501 Strcat(info, ", can't move");
502 #endif
503 /* [arbitrary reason why it isn't moving] */
504 else if (mtmp->mstrategy & STRAT_WAITMASK)
505 Strcat(info, ", meditating");
506 if (mtmp->mflee)
507 Strcat(info, ", scared");
508 if (mtmp->mtrapped)
509 Strcat(info, ", trapped");
510 if (mtmp->mspeed)
511 Strcat(info, mtmp->mspeed == MFAST ? ", fast" : mtmp->mspeed == MSLOW
512 ? ", slow"
513 : ", ???? speed");
514 if (mtmp->minvis)
515 Strcat(info, ", invisible");
516 if (mtmp == u.ustuck)
517 Strcat(info, sticks(youmonst.data)
518 ? ", held by you"
519 : !u.uswallow ? ", holding you"
520 : attacktype_fordmg(u.ustuck->data,
521 AT_ENGL, AD_DGST)
522 ? ", digesting you"
523 : is_animal(u.ustuck->data)
524 ? ", swallowing you"
525 : ", engulfing you");
526 if (mtmp == u.usteed)
527 Strcat(info, ", carrying you");
529 /* avoid "Status of the invisible newt ..., invisible" */
530 /* and unlike a normal mon_nam, use "saddled" even if it has a name */
531 Strcpy(monnambuf, x_monnam(mtmp, ARTICLE_THE, (char *) 0,
532 (SUPPRESS_IT | SUPPRESS_INVISIBLE), FALSE));
534 pline("Status of %s (%s): Level %d HP %d(%d) AC %d%s.", monnambuf,
535 align_str(alignment), mtmp->m_lev, mtmp->mhp, mtmp->mhpmax,
536 find_mac(mtmp), info);
539 void
540 ustatusline()
542 char info[BUFSZ];
544 info[0] = '\0';
545 if (Sick) {
546 Strcat(info, ", dying from");
547 if (u.usick_type & SICK_VOMITABLE)
548 Strcat(info, " food poisoning");
549 if (u.usick_type & SICK_NONVOMITABLE) {
550 if (u.usick_type & SICK_VOMITABLE)
551 Strcat(info, " and");
552 Strcat(info, " illness");
555 if (Stoned)
556 Strcat(info, ", solidifying");
557 if (Slimed)
558 Strcat(info, ", becoming slimy");
559 if (Strangled)
560 Strcat(info, ", being strangled");
561 if (Vomiting)
562 Strcat(info, ", nauseated"); /* !"nauseous" */
563 if (Confusion)
564 Strcat(info, ", confused");
565 if (Blind) {
566 Strcat(info, ", blind");
567 if (u.ucreamed) {
568 if ((long) u.ucreamed < Blinded || Blindfolded
569 || !haseyes(youmonst.data))
570 Strcat(info, ", cover");
571 Strcat(info, "ed by sticky goop");
572 } /* note: "goop" == "glop"; variation is intentional */
574 if (Stunned)
575 Strcat(info, ", stunned");
576 if (!u.usteed && Wounded_legs) {
577 const char *what = body_part(LEG);
578 if ((Wounded_legs & BOTH_SIDES) == BOTH_SIDES)
579 what = makeplural(what);
580 Sprintf(eos(info), ", injured %s", what);
582 if (Glib)
583 Sprintf(eos(info), ", slippery %s", makeplural(body_part(HAND)));
584 if (u.utrap)
585 Strcat(info, ", trapped");
586 if (Fast)
587 Strcat(info, Very_fast ? ", very fast" : ", fast");
588 if (u.uundetected)
589 Strcat(info, ", concealed");
590 if (Invis)
591 Strcat(info, ", invisible");
592 if (u.ustuck) {
593 if (sticks(youmonst.data))
594 Strcat(info, ", holding ");
595 else
596 Strcat(info, ", held by ");
597 Strcat(info, mon_nam(u.ustuck));
600 pline("Status of %s (%s): Level %d HP %d(%d) AC %d%s.", plname,
601 piousness(FALSE, align_str(u.ualign.type)),
602 Upolyd ? mons[u.umonnum].mlevel : u.ulevel, Upolyd ? u.mh : u.uhp,
603 Upolyd ? u.mhmax : u.uhpmax, u.uac, info);
606 void
607 self_invis_message()
609 pline("%s %s.",
610 Hallucination ? "Far out, man! You" : "Gee! All of a sudden, you",
611 See_invisible ? "can see right through yourself"
612 : "can't see yourself");
615 char *
616 piousness(showneg, suffix)
617 boolean showneg;
618 const char *suffix;
620 static char buf[32]; /* bigger than "insufficiently neutral" */
621 const char *pio;
623 /* note: piousness 20 matches MIN_QUEST_ALIGN (quest.h) */
624 if (u.ualign.record >= 20)
625 pio = "piously";
626 else if (u.ualign.record > 13)
627 pio = "devoutly";
628 else if (u.ualign.record > 8)
629 pio = "fervently";
630 else if (u.ualign.record > 3)
631 pio = "stridently";
632 else if (u.ualign.record == 3)
633 pio = "";
634 else if (u.ualign.record > 0)
635 pio = "haltingly";
636 else if (u.ualign.record == 0)
637 pio = "nominally";
638 else if (!showneg)
639 pio = "insufficiently";
640 else if (u.ualign.record >= -3)
641 pio = "strayed";
642 else if (u.ualign.record >= -8)
643 pio = "sinned";
644 else
645 pio = "transgressed";
647 Sprintf(buf, "%s", pio);
648 if (suffix && (!showneg || u.ualign.record >= 0)) {
649 if (u.ualign.record != 3)
650 Strcat(buf, " ");
651 Strcat(buf, suffix);
653 return buf;
656 void
657 pudding_merge_message(otmp, otmp2)
658 struct obj *otmp;
659 struct obj *otmp2;
661 boolean visible =
662 cansee(otmp->ox, otmp->oy) || cansee(otmp2->ox, otmp2->oy);
663 boolean onfloor = otmp->where == OBJ_FLOOR || otmp2->where == OBJ_FLOOR;
664 boolean inpack = carried(otmp) || carried(otmp2);
666 /* the player will know something happened inside his own inventory */
667 if ((!Blind && visible) || inpack) {
668 if (Hallucination) {
669 if (onfloor) {
670 You_see("parts of the floor melting!");
671 } else if (inpack) {
672 Your("pack reaches out and grabs something!");
674 /* even though we can see where they should be,
675 * they'll be out of our view (minvent or container)
676 * so don't actually show anything */
677 } else if (onfloor || inpack) {
678 pline("The %s coalesce%s.", makeplural(obj_typename(otmp->otyp)),
679 inpack ? " inside your pack" : "");
681 } else {
682 You_hear("a faint sloshing sound.");
686 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
687 static boolean use_pline_handler = TRUE;
688 static void
689 execplinehandler(line)
690 const char *line;
692 int f;
693 const char *args[3];
694 char *env;
696 if (!use_pline_handler)
697 return;
699 if (!(env = nh_getenv("ANETHACK_MSGHANDLER"))) {
700 use_pline_handler = FALSE;
701 return;
704 f = fork();
705 if (f == 0) { /* child */
706 args[0] = env;
707 args[1] = line;
708 args[2] = NULL;
709 (void) setgid(getgid());
710 (void) setuid(getuid());
711 (void) execv(args[0], (char *const *) args);
712 perror((char *) 0);
713 (void) fprintf(stderr, "Exec to message handler %s failed.\n",
714 env);
715 terminate(EXIT_FAILURE);
716 } else if (f > 0) {
717 int status;
718 waitpid(f, &status, 0);
719 } else if (f == -1) {
720 perror((char *) 0);
721 use_pline_handler = FALSE;
722 pline("Fork to message handler failed.");
725 #endif /* defined(POSIX_TYPES) || defined(__GNUC__) */
727 /*pline.c*/