Add end-of-game dumplogs
[aNetHack.git] / src / pline.c
blob0f21b444b43ac9d46a73423696adc0f59469fc06
1 /* NetHack 3.6 pline.c $NHDT-Date: 1461437814 2016/04/23 18:56:54 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.51 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack 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 char* saved_plines[DUMPLOG_MSG_COUNT] = {0};
19 #endif
21 /*VARARGS1*/
22 /* Note that these declarations rely on knowledge of the internals
23 * of the variable argument handling stuff in "tradstdc.h"
26 #if defined(USE_STDARG) || defined(USE_VARARGS)
27 static void FDECL(vpline, (const char *, va_list));
29 void pline
30 VA_DECL(const char *, line)
32 VA_START(line);
33 VA_INIT(line, char *);
34 vpline(line, VA_ARGS);
35 VA_END();
38 # ifdef USE_STDARG
39 static void
40 vpline(const char *line, va_list the_args)
41 # else
42 static void
43 vpline(line, the_args)
44 const char *line;
45 va_list the_args;
46 # endif
48 #else /* USE_STDARG | USE_VARARG */
50 # define vpline pline
52 void pline
53 VA_DECL(const char *, line)
54 #endif /* USE_STDARG | USE_VARARG */
55 { /* start of vpline() or of nested block in USE_OLDARG's pline() */
56 char pbuf[3 * BUFSZ];
57 int ln;
58 xchar msgtyp;
59 /* Do NOT use VA_START and VA_END in here... see above */
61 if (!line || !*line)
62 return;
63 #ifdef HANGUPHANDLING
64 if (program_state.done_hup)
65 return;
66 #endif
67 if (program_state.wizkit_wishing)
68 return;
70 if (index(line, '%')) {
71 Vsprintf(pbuf, line, VA_ARGS);
72 line = pbuf;
74 if ((ln = (int) strlen(line)) > BUFSZ - 1) {
75 if (line != pbuf) /* no '%' was present */
76 (void) strncpy(pbuf, line, BUFSZ - 1); /* caveat: unterminated */
77 /* truncate, preserving the final 3 characters:
78 "___ extremely long text" -> "___ extremely l...ext"
79 (this may be suboptimal if overflow is less than 3) */
80 (void) strncpy(pbuf + BUFSZ - 1 - 6, "...", 3);
81 /* avoid strncpy; buffers could overlap if excess is small */
82 pbuf[BUFSZ - 1 - 3] = line[ln - 3];
83 pbuf[BUFSZ - 1 - 2] = line[ln - 2];
84 pbuf[BUFSZ - 1 - 1] = line[ln - 1];
85 pbuf[BUFSZ - 1] = '\0';
86 line = pbuf;
88 if (!iflags.window_inited) {
89 raw_print(line);
90 iflags.last_msg = PLNMSG_UNKNOWN;
91 return;
94 #ifdef DUMPLOG
95 /* We hook here early to have options-agnostic output. */
96 free(saved_plines[DUMPLOG_MSG_COUNT - 1]);
97 for (ln = 0; ln < DUMPLOG_MSG_COUNT - 1; ++ln)
98 saved_plines[DUMPLOG_MSG_COUNT - ln - 1] = saved_plines[DUMPLOG_MSG_COUNT - ln - 2];
99 saved_plines[0] = malloc(strlen(line) + 1);
100 (void) strcpy(saved_plines[0], line);
101 #endif
103 msgtyp = msgtype_type(line, no_repeat);
104 if (msgtyp == MSGTYP_NOSHOW
105 || (msgtyp == MSGTYP_NOREP && !strcmp(line, prevmsg)))
106 return;
107 if (vision_full_recalc)
108 vision_recalc(0);
109 if (u.ux)
110 flush_screen(1); /* %% */
112 putstr(WIN_MESSAGE, 0, line);
114 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
115 execplinehandler(line);
116 #endif
118 /* this gets cleared after every pline message */
119 iflags.last_msg = PLNMSG_UNKNOWN;
120 strncpy(prevmsg, line, BUFSZ), prevmsg[BUFSZ - 1] = '\0';
121 if (msgtyp == MSGTYP_STOP)
122 display_nhwindow(WIN_MESSAGE, TRUE); /* --more-- */
124 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
125 /* provide closing brace for the nested block
126 which immediately follows USE_OLDARGS's VA_DECL() */
127 VA_END();
128 #endif
131 /*VARARGS1*/
132 void Norep
133 VA_DECL(const char *, line)
135 VA_START(line);
136 VA_INIT(line, const char *);
137 no_repeat = TRUE;
138 vpline(line, VA_ARGS);
139 no_repeat = FALSE;
140 VA_END();
141 return;
144 /* work buffer for You(), &c and verbalize() */
145 static char *you_buf = 0;
146 static int you_buf_siz = 0;
148 static char *
149 You_buf(siz)
150 int siz;
152 if (siz > you_buf_siz) {
153 if (you_buf)
154 free((genericptr_t) you_buf);
155 you_buf_siz = siz + 10;
156 you_buf = (char *) alloc((unsigned) you_buf_siz);
158 return you_buf;
161 void
162 free_youbuf()
164 if (you_buf)
165 free((genericptr_t) you_buf), you_buf = (char *) 0;
166 you_buf_siz = 0;
169 /* `prefix' must be a string literal, not a pointer */
170 #define YouPrefix(pointer, prefix, text) \
171 Strcpy((pointer = You_buf((int) (strlen(text) + sizeof prefix))), prefix)
173 #define YouMessage(pointer, prefix, text) \
174 strcat((YouPrefix(pointer, prefix, text), pointer), text)
176 /*VARARGS1*/
177 void You
178 VA_DECL(const char *, line)
180 char *tmp;
181 VA_START(line);
182 VA_INIT(line, const char *);
183 vpline(YouMessage(tmp, "You ", line), VA_ARGS);
184 VA_END();
187 /*VARARGS1*/
188 void Your
189 VA_DECL(const char *, line)
191 char *tmp;
192 VA_START(line);
193 VA_INIT(line, const char *);
194 vpline(YouMessage(tmp, "Your ", line), VA_ARGS);
195 VA_END();
198 /*VARARGS1*/
199 void You_feel
200 VA_DECL(const char *, line)
202 char *tmp;
203 VA_START(line);
204 VA_INIT(line, const char *);
205 if (Unaware)
206 YouPrefix(tmp, "You dream that you feel ", line);
207 else
208 YouPrefix(tmp, "You feel ", line);
209 vpline(strcat(tmp, line), VA_ARGS);
210 VA_END();
213 /*VARARGS1*/
214 void You_cant
215 VA_DECL(const char *, line)
217 char *tmp;
218 VA_START(line);
219 VA_INIT(line, const char *);
220 vpline(YouMessage(tmp, "You can't ", line), VA_ARGS);
221 VA_END();
224 /*VARARGS1*/
225 void pline_The
226 VA_DECL(const char *, line)
228 char *tmp;
229 VA_START(line);
230 VA_INIT(line, const char *);
231 vpline(YouMessage(tmp, "The ", line), VA_ARGS);
232 VA_END();
235 /*VARARGS1*/
236 void There
237 VA_DECL(const char *, line)
239 char *tmp;
240 VA_START(line);
241 VA_INIT(line, const char *);
242 vpline(YouMessage(tmp, "There ", line), VA_ARGS);
243 VA_END();
246 /*VARARGS1*/
247 void You_hear
248 VA_DECL(const char *, line)
250 char *tmp;
252 if (Deaf || !flags.acoustics)
253 return;
254 VA_START(line);
255 VA_INIT(line, const char *);
256 if (Underwater)
257 YouPrefix(tmp, "You barely hear ", line);
258 else if (Unaware)
259 YouPrefix(tmp, "You dream that you hear ", line);
260 else
261 YouPrefix(tmp, "You hear ", line);
262 vpline(strcat(tmp, line), VA_ARGS);
263 VA_END();
266 /*VARARGS1*/
267 void You_see
268 VA_DECL(const char *, line)
270 char *tmp;
272 VA_START(line);
273 VA_INIT(line, const char *);
274 if (Unaware)
275 YouPrefix(tmp, "You dream that you see ", line);
276 else if (Blind) /* caller should have caught this... */
277 YouPrefix(tmp, "You sense ", line);
278 else
279 YouPrefix(tmp, "You see ", line);
280 vpline(strcat(tmp, line), VA_ARGS);
281 VA_END();
284 /* Print a message inside double-quotes.
285 * The caller is responsible for checking deafness.
286 * Gods can speak directly to you in spite of deafness.
288 /*VARARGS1*/
289 void verbalize
290 VA_DECL(const char *, line)
292 char *tmp;
294 VA_START(line);
295 VA_INIT(line, const char *);
296 tmp = You_buf((int) strlen(line) + sizeof "\"\"");
297 Strcpy(tmp, "\"");
298 Strcat(tmp, line);
299 Strcat(tmp, "\"");
300 vpline(tmp, VA_ARGS);
301 VA_END();
304 /*VARARGS1*/
305 /* Note that these declarations rely on knowledge of the internals
306 * of the variable argument handling stuff in "tradstdc.h"
309 #if defined(USE_STDARG) || defined(USE_VARARGS)
310 static void FDECL(vraw_printf, (const char *, va_list));
312 void raw_printf
313 VA_DECL(const char *, line)
315 VA_START(line);
316 VA_INIT(line, char *);
317 vraw_printf(line, VA_ARGS);
318 VA_END();
321 # ifdef USE_STDARG
322 static void
323 vraw_printf(const char *line, va_list the_args)
324 # else
325 static void
326 vraw_printf(line, the_args)
327 const char *line;
328 va_list the_args;
329 # endif
331 #else /* USE_STDARG | USE_VARARG */
333 void raw_printf
334 VA_DECL(const char *, line)
335 #endif
337 char pbuf[3 * BUFSZ];
338 int ln;
339 /* Do NOT use VA_START and VA_END in here... see above */
341 if (index(line, '%')) {
342 Vsprintf(pbuf, line, VA_ARGS);
343 line = pbuf;
345 if ((ln = (int) strlen(line)) > BUFSZ - 1) {
346 if (line != pbuf)
347 line = strncpy(pbuf, line, BUFSZ - 1);
348 /* unlike pline, we don't futz around to keep last few chars */
349 pbuf[BUFSZ - 1] = '\0'; /* terminate strncpy or truncate vsprintf */
351 raw_print(line);
352 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
353 execplinehandler(line);
354 #endif
355 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
356 VA_END(); /* (see vpline) */
357 #endif
360 /*VARARGS1*/
361 void impossible
362 VA_DECL(const char *, s)
364 char pbuf[2 * BUFSZ];
365 VA_START(s);
366 VA_INIT(s, const char *);
367 if (program_state.in_impossible)
368 panic("impossible called impossible");
370 program_state.in_impossible = 1;
371 Vsprintf(pbuf, s, VA_ARGS);
372 pbuf[BUFSZ - 1] = '\0'; /* sanity */
373 paniclog("impossible", pbuf);
374 pline("%s", pbuf);
375 pline("Program in disorder - perhaps you'd better #quit.");
376 program_state.in_impossible = 0;
377 VA_END();
380 const char *
381 align_str(alignment)
382 aligntyp alignment;
384 switch ((int) alignment) {
385 case A_CHAOTIC:
386 return "chaotic";
387 case A_NEUTRAL:
388 return "neutral";
389 case A_LAWFUL:
390 return "lawful";
391 case A_NONE:
392 return "unaligned";
394 return "unknown";
397 void
398 mstatusline(mtmp)
399 register struct monst *mtmp;
401 aligntyp alignment = mon_aligntyp(mtmp);
402 char info[BUFSZ], monnambuf[BUFSZ];
404 info[0] = 0;
405 if (mtmp->mtame) {
406 Strcat(info, ", tame");
407 if (wizard) {
408 Sprintf(eos(info), " (%d", mtmp->mtame);
409 if (!mtmp->isminion)
410 Sprintf(eos(info), "; hungry %ld; apport %d",
411 EDOG(mtmp)->hungrytime, EDOG(mtmp)->apport);
412 Strcat(info, ")");
414 } else if (mtmp->mpeaceful)
415 Strcat(info, ", peaceful");
417 if (mtmp->data == &mons[PM_LONG_WORM]) {
418 int segndx, nsegs = count_wsegs(mtmp);
420 /* the worm code internals don't consider the head of be one of
421 the worm's segments, but we count it as such when presenting
422 worm feedback to the player */
423 if (!nsegs) {
424 Strcat(info, ", single segment");
425 } else {
426 ++nsegs; /* include head in the segment count */
427 segndx = wseg_at(mtmp, bhitpos.x, bhitpos.y);
428 Sprintf(eos(info), ", %d%s of %d segments",
429 segndx, ordin(segndx), nsegs);
432 if (mtmp->cham >= LOW_PM && mtmp->data != &mons[mtmp->cham])
433 /* don't reveal the innate form (chameleon, vampire, &c),
434 just expose the fact that this current form isn't it */
435 Strcat(info, ", shapechanger");
436 /* pets eating mimic corpses mimic while eating, so this comes first */
437 if (mtmp->meating)
438 Strcat(info, ", eating");
439 /* a stethoscope exposes mimic before getting here so this
440 won't be relevant for it, but wand of probing doesn't */
441 if (mtmp->mundetected || mtmp->m_ap_type)
442 mhidden_description(mtmp, TRUE, eos(info));
443 if (mtmp->mcan)
444 Strcat(info, ", cancelled");
445 if (mtmp->mconf)
446 Strcat(info, ", confused");
447 if (mtmp->mblinded || !mtmp->mcansee)
448 Strcat(info, ", blind");
449 if (mtmp->mstun)
450 Strcat(info, ", stunned");
451 if (mtmp->msleeping)
452 Strcat(info, ", asleep");
453 #if 0 /* unfortunately mfrozen covers temporary sleep and being busy \
454 (donning armor, for instance) as well as paralysis */
455 else if (mtmp->mfrozen)
456 Strcat(info, ", paralyzed");
457 #else
458 else if (mtmp->mfrozen || !mtmp->mcanmove)
459 Strcat(info, ", can't move");
460 #endif
461 /* [arbitrary reason why it isn't moving] */
462 else if (mtmp->mstrategy & STRAT_WAITMASK)
463 Strcat(info, ", meditating");
464 if (mtmp->mflee)
465 Strcat(info, ", scared");
466 if (mtmp->mtrapped)
467 Strcat(info, ", trapped");
468 if (mtmp->mspeed)
469 Strcat(info, mtmp->mspeed == MFAST ? ", fast" : mtmp->mspeed == MSLOW
470 ? ", slow"
471 : ", ???? speed");
472 if (mtmp->minvis)
473 Strcat(info, ", invisible");
474 if (mtmp == u.ustuck)
475 Strcat(info, sticks(youmonst.data)
476 ? ", held by you"
477 : !u.uswallow ? ", holding you"
478 : attacktype_fordmg(u.ustuck->data,
479 AT_ENGL, AD_DGST)
480 ? ", digesting you"
481 : is_animal(u.ustuck->data)
482 ? ", swallowing you"
483 : ", engulfing you");
484 if (mtmp == u.usteed)
485 Strcat(info, ", carrying you");
487 /* avoid "Status of the invisible newt ..., invisible" */
488 /* and unlike a normal mon_nam, use "saddled" even if it has a name */
489 Strcpy(monnambuf, x_monnam(mtmp, ARTICLE_THE, (char *) 0,
490 (SUPPRESS_IT | SUPPRESS_INVISIBLE), FALSE));
492 pline("Status of %s (%s): Level %d HP %d(%d) AC %d%s.", monnambuf,
493 align_str(alignment), mtmp->m_lev, mtmp->mhp, mtmp->mhpmax,
494 find_mac(mtmp), info);
497 void
498 ustatusline()
500 char info[BUFSZ];
502 info[0] = '\0';
503 if (Sick) {
504 Strcat(info, ", dying from");
505 if (u.usick_type & SICK_VOMITABLE)
506 Strcat(info, " food poisoning");
507 if (u.usick_type & SICK_NONVOMITABLE) {
508 if (u.usick_type & SICK_VOMITABLE)
509 Strcat(info, " and");
510 Strcat(info, " illness");
513 if (Stoned)
514 Strcat(info, ", solidifying");
515 if (Slimed)
516 Strcat(info, ", becoming slimy");
517 if (Strangled)
518 Strcat(info, ", being strangled");
519 if (Vomiting)
520 Strcat(info, ", nauseated"); /* !"nauseous" */
521 if (Confusion)
522 Strcat(info, ", confused");
523 if (Blind) {
524 Strcat(info, ", blind");
525 if (u.ucreamed) {
526 if ((long) u.ucreamed < Blinded || Blindfolded
527 || !haseyes(youmonst.data))
528 Strcat(info, ", cover");
529 Strcat(info, "ed by sticky goop");
530 } /* note: "goop" == "glop"; variation is intentional */
532 if (Stunned)
533 Strcat(info, ", stunned");
534 if (!u.usteed && Wounded_legs) {
535 const char *what = body_part(LEG);
536 if ((Wounded_legs & BOTH_SIDES) == BOTH_SIDES)
537 what = makeplural(what);
538 Sprintf(eos(info), ", injured %s", what);
540 if (Glib)
541 Sprintf(eos(info), ", slippery %s", makeplural(body_part(HAND)));
542 if (u.utrap)
543 Strcat(info, ", trapped");
544 if (Fast)
545 Strcat(info, Very_fast ? ", very fast" : ", fast");
546 if (u.uundetected)
547 Strcat(info, ", concealed");
548 if (Invis)
549 Strcat(info, ", invisible");
550 if (u.ustuck) {
551 if (sticks(youmonst.data))
552 Strcat(info, ", holding ");
553 else
554 Strcat(info, ", held by ");
555 Strcat(info, mon_nam(u.ustuck));
558 pline("Status of %s (%s): Level %d HP %d(%d) AC %d%s.", plname,
559 piousness(FALSE, align_str(u.ualign.type)),
560 Upolyd ? mons[u.umonnum].mlevel : u.ulevel, Upolyd ? u.mh : u.uhp,
561 Upolyd ? u.mhmax : u.uhpmax, u.uac, info);
564 void
565 self_invis_message()
567 pline("%s %s.",
568 Hallucination ? "Far out, man! You" : "Gee! All of a sudden, you",
569 See_invisible ? "can see right through yourself"
570 : "can't see yourself");
573 char *
574 piousness(showneg, suffix)
575 boolean showneg;
576 const char *suffix;
578 static char buf[32]; /* bigger than "insufficiently neutral" */
579 const char *pio;
581 /* note: piousness 20 matches MIN_QUEST_ALIGN (quest.h) */
582 if (u.ualign.record >= 20)
583 pio = "piously";
584 else if (u.ualign.record > 13)
585 pio = "devoutly";
586 else if (u.ualign.record > 8)
587 pio = "fervently";
588 else if (u.ualign.record > 3)
589 pio = "stridently";
590 else if (u.ualign.record == 3)
591 pio = "";
592 else if (u.ualign.record > 0)
593 pio = "haltingly";
594 else if (u.ualign.record == 0)
595 pio = "nominally";
596 else if (!showneg)
597 pio = "insufficiently";
598 else if (u.ualign.record >= -3)
599 pio = "strayed";
600 else if (u.ualign.record >= -8)
601 pio = "sinned";
602 else
603 pio = "transgressed";
605 Sprintf(buf, "%s", pio);
606 if (suffix && (!showneg || u.ualign.record >= 0)) {
607 if (u.ualign.record != 3)
608 Strcat(buf, " ");
609 Strcat(buf, suffix);
611 return buf;
614 void
615 pudding_merge_message(otmp, otmp2)
616 struct obj *otmp;
617 struct obj *otmp2;
619 boolean visible =
620 cansee(otmp->ox, otmp->oy) || cansee(otmp2->ox, otmp2->oy);
621 boolean onfloor = otmp->where == OBJ_FLOOR || otmp2->where == OBJ_FLOOR;
622 boolean inpack = carried(otmp) || carried(otmp2);
624 /* the player will know something happened inside his own inventory */
625 if ((!Blind && visible) || inpack) {
626 if (Hallucination) {
627 if (onfloor) {
628 You_see("parts of the floor melting!");
629 } else if (inpack) {
630 Your("pack reaches out and grabs something!");
632 /* even though we can see where they should be,
633 * they'll be out of our view (minvent or container)
634 * so don't actually show anything */
635 } else if (onfloor || inpack) {
636 pline("The %s coalesce%s.", makeplural(obj_typename(otmp->otyp)),
637 inpack ? " inside your pack" : "");
639 } else {
640 You_hear("a faint sloshing sound.");
644 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
645 static boolean use_pline_handler = TRUE;
646 static void
647 execplinehandler(line)
648 const char *line;
650 int f;
651 const char *args[3];
652 char *env;
654 if (!use_pline_handler)
655 return;
657 if (!(env = nh_getenv("NETHACK_MSGHANDLER"))) {
658 use_pline_handler = FALSE;
659 return;
662 f = fork();
663 if (f == 0) { /* child */
664 args[0] = env;
665 args[1] = line;
666 args[2] = NULL;
667 (void) setgid(getgid());
668 (void) setuid(getuid());
669 (void) execv(args[0], (char *const *) args);
670 perror((char *) 0);
671 (void) fprintf(stderr, "Exec to message handler %s failed.\n",
672 env);
673 terminate(EXIT_FAILURE);
674 } else if (f > 0) {
675 int status;
676 waitpid(f, &status, 0);
677 } else if (f == -1) {
678 perror((char *) 0);
679 use_pline_handler = FALSE;
680 pline("Fork to message handler failed.");
683 #endif /* defined(POSIX_TYPES) || defined(__GNUC__) */
685 /*pline.c*/