dumplog's saved_plines[]
[aNetHack.git] / src / pline.c
blob6103d29cfd9c10988d3fe12e71a617ce6ce66013
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 /* 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 };
21 #endif
23 /*VARARGS1*/
24 /* Note that these declarations rely on knowledge of the internals
25 * of the variable argument handling stuff in "tradstdc.h"
28 #if defined(USE_STDARG) || defined(USE_VARARGS)
29 static void FDECL(vpline, (const char *, va_list));
31 void pline
32 VA_DECL(const char *, line)
34 VA_START(line);
35 VA_INIT(line, char *);
36 vpline(line, VA_ARGS);
37 VA_END();
40 # ifdef USE_STDARG
41 static void
42 vpline(const char *line, va_list the_args)
43 # else
44 static void
45 vpline(line, the_args)
46 const char *line;
47 va_list the_args;
48 # endif
50 #else /* USE_STDARG | USE_VARARG */
52 # define vpline pline
54 void pline
55 VA_DECL(const char *, line)
56 #endif /* USE_STDARG | USE_VARARG */
57 { /* start of vpline() or of nested block in USE_OLDARG's pline() */
58 char pbuf[3 * BUFSZ];
59 int ln;
60 xchar msgtyp;
61 /* Do NOT use VA_START and VA_END in here... see above */
63 if (!line || !*line)
64 return;
65 #ifdef HANGUPHANDLING
66 if (program_state.done_hup)
67 return;
68 #endif
69 if (program_state.wizkit_wishing)
70 return;
72 if (index(line, '%')) {
73 Vsprintf(pbuf, line, VA_ARGS);
74 line = pbuf;
76 if ((ln = (int) strlen(line)) > BUFSZ - 1) {
77 if (line != pbuf) /* no '%' was present */
78 (void) strncpy(pbuf, line, BUFSZ - 1); /* caveat: unterminated */
79 /* truncate, preserving the final 3 characters:
80 "___ extremely long text" -> "___ extremely l...ext"
81 (this may be suboptimal if overflow is less than 3) */
82 (void) strncpy(pbuf + BUFSZ - 1 - 6, "...", 3);
83 /* avoid strncpy; buffers could overlap if excess is small */
84 pbuf[BUFSZ - 1 - 3] = line[ln - 3];
85 pbuf[BUFSZ - 1 - 2] = line[ln - 2];
86 pbuf[BUFSZ - 1 - 1] = line[ln - 1];
87 pbuf[BUFSZ - 1] = '\0';
88 line = pbuf;
89 ln = BUFSZ - 1;
91 if (!iflags.window_inited) {
92 raw_print(line);
93 iflags.last_msg = PLNMSG_UNKNOWN;
94 return;
97 #ifdef DUMPLOG
98 /* We hook here early to have options-agnostic output.
99 * Unfortunately, that means Norep() isn't honored (general issue) and
100 * that short lines aren't combined into one longer one (tty behavior).
104 * TODO:
105 * This essentially duplicates message history, which is
106 * currently implemented in an interface-specific manner.
107 * The core should take responsibility for that and have
108 * this share it.
109 * Ideally history for prompt lines should be augmented
110 * with the player's response once that has been specified.
112 unsigned indx = saved_pline_index; /* next slot to use */
113 char *oldest = saved_plines[indx]; /* current content of that slot */
115 if (oldest && (int) strlen(oldest) >= ln) { /* ln==strlen(line) */
116 /* this buffer will gradually shrink until the 'else' is needed;
117 there's no pressing need to track allocation size instead */
118 Strcpy(oldest, line);
119 } else {
120 if (oldest)
121 free((genericptr_t) oldest);
122 saved_plines[indx] = dupstr(line);
124 saved_pline_index = (indx + 1) % DUMPLOG_MSG_COUNT;
126 #endif
128 msgtyp = msgtype_type(line, no_repeat);
129 if (msgtyp == MSGTYP_NOSHOW
130 || (msgtyp == MSGTYP_NOREP && !strcmp(line, prevmsg)))
131 return;
132 if (vision_full_recalc)
133 vision_recalc(0);
134 if (u.ux)
135 flush_screen(1); /* %% */
137 putstr(WIN_MESSAGE, 0, line);
139 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
140 execplinehandler(line);
141 #endif
143 /* this gets cleared after every pline message */
144 iflags.last_msg = PLNMSG_UNKNOWN;
145 (void) strncpy(prevmsg, line, BUFSZ), prevmsg[BUFSZ - 1] = '\0';
146 if (msgtyp == MSGTYP_STOP)
147 display_nhwindow(WIN_MESSAGE, TRUE); /* --more-- */
149 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
150 /* provide closing brace for the nested block
151 which immediately follows USE_OLDARGS's VA_DECL() */
152 VA_END();
153 #endif
156 /*VARARGS1*/
157 void Norep
158 VA_DECL(const char *, line)
160 VA_START(line);
161 VA_INIT(line, const char *);
162 no_repeat = TRUE;
163 vpline(line, VA_ARGS);
164 no_repeat = FALSE;
165 VA_END();
166 return;
169 /* work buffer for You(), &c and verbalize() */
170 static char *you_buf = 0;
171 static int you_buf_siz = 0;
173 static char *
174 You_buf(siz)
175 int siz;
177 if (siz > you_buf_siz) {
178 if (you_buf)
179 free((genericptr_t) you_buf);
180 you_buf_siz = siz + 10;
181 you_buf = (char *) alloc((unsigned) you_buf_siz);
183 return you_buf;
186 void
187 free_youbuf()
189 if (you_buf)
190 free((genericptr_t) you_buf), you_buf = (char *) 0;
191 you_buf_siz = 0;
194 /* `prefix' must be a string literal, not a pointer */
195 #define YouPrefix(pointer, prefix, text) \
196 Strcpy((pointer = You_buf((int) (strlen(text) + sizeof prefix))), prefix)
198 #define YouMessage(pointer, prefix, text) \
199 strcat((YouPrefix(pointer, prefix, text), pointer), text)
201 /*VARARGS1*/
202 void You
203 VA_DECL(const char *, line)
205 char *tmp;
206 VA_START(line);
207 VA_INIT(line, const char *);
208 vpline(YouMessage(tmp, "You ", line), VA_ARGS);
209 VA_END();
212 /*VARARGS1*/
213 void Your
214 VA_DECL(const char *, line)
216 char *tmp;
217 VA_START(line);
218 VA_INIT(line, const char *);
219 vpline(YouMessage(tmp, "Your ", line), VA_ARGS);
220 VA_END();
223 /*VARARGS1*/
224 void You_feel
225 VA_DECL(const char *, line)
227 char *tmp;
228 VA_START(line);
229 VA_INIT(line, const char *);
230 if (Unaware)
231 YouPrefix(tmp, "You dream that you feel ", line);
232 else
233 YouPrefix(tmp, "You feel ", line);
234 vpline(strcat(tmp, line), VA_ARGS);
235 VA_END();
238 /*VARARGS1*/
239 void You_cant
240 VA_DECL(const char *, line)
242 char *tmp;
243 VA_START(line);
244 VA_INIT(line, const char *);
245 vpline(YouMessage(tmp, "You can't ", line), VA_ARGS);
246 VA_END();
249 /*VARARGS1*/
250 void pline_The
251 VA_DECL(const char *, line)
253 char *tmp;
254 VA_START(line);
255 VA_INIT(line, const char *);
256 vpline(YouMessage(tmp, "The ", line), VA_ARGS);
257 VA_END();
260 /*VARARGS1*/
261 void There
262 VA_DECL(const char *, line)
264 char *tmp;
265 VA_START(line);
266 VA_INIT(line, const char *);
267 vpline(YouMessage(tmp, "There ", line), VA_ARGS);
268 VA_END();
271 /*VARARGS1*/
272 void You_hear
273 VA_DECL(const char *, line)
275 char *tmp;
277 if (Deaf || !flags.acoustics)
278 return;
279 VA_START(line);
280 VA_INIT(line, const char *);
281 if (Underwater)
282 YouPrefix(tmp, "You barely hear ", line);
283 else if (Unaware)
284 YouPrefix(tmp, "You dream that you hear ", line);
285 else
286 YouPrefix(tmp, "You hear ", line);
287 vpline(strcat(tmp, line), VA_ARGS);
288 VA_END();
291 /*VARARGS1*/
292 void You_see
293 VA_DECL(const char *, line)
295 char *tmp;
297 VA_START(line);
298 VA_INIT(line, const char *);
299 if (Unaware)
300 YouPrefix(tmp, "You dream that you see ", line);
301 else if (Blind) /* caller should have caught this... */
302 YouPrefix(tmp, "You sense ", line);
303 else
304 YouPrefix(tmp, "You see ", line);
305 vpline(strcat(tmp, line), VA_ARGS);
306 VA_END();
309 /* Print a message inside double-quotes.
310 * The caller is responsible for checking deafness.
311 * Gods can speak directly to you in spite of deafness.
313 /*VARARGS1*/
314 void verbalize
315 VA_DECL(const char *, line)
317 char *tmp;
319 VA_START(line);
320 VA_INIT(line, const char *);
321 tmp = You_buf((int) strlen(line) + sizeof "\"\"");
322 Strcpy(tmp, "\"");
323 Strcat(tmp, line);
324 Strcat(tmp, "\"");
325 vpline(tmp, VA_ARGS);
326 VA_END();
329 /*VARARGS1*/
330 /* Note that these declarations rely on knowledge of the internals
331 * of the variable argument handling stuff in "tradstdc.h"
334 #if defined(USE_STDARG) || defined(USE_VARARGS)
335 static void FDECL(vraw_printf, (const char *, va_list));
337 void raw_printf
338 VA_DECL(const char *, line)
340 VA_START(line);
341 VA_INIT(line, char *);
342 vraw_printf(line, VA_ARGS);
343 VA_END();
346 # ifdef USE_STDARG
347 static void
348 vraw_printf(const char *line, va_list the_args)
349 # else
350 static void
351 vraw_printf(line, the_args)
352 const char *line;
353 va_list the_args;
354 # endif
356 #else /* USE_STDARG | USE_VARARG */
358 void raw_printf
359 VA_DECL(const char *, line)
360 #endif
362 char pbuf[3 * BUFSZ];
363 int ln;
364 /* Do NOT use VA_START and VA_END in here... see above */
366 if (index(line, '%')) {
367 Vsprintf(pbuf, line, VA_ARGS);
368 line = pbuf;
370 if ((ln = (int) strlen(line)) > BUFSZ - 1) {
371 if (line != pbuf)
372 line = strncpy(pbuf, line, BUFSZ - 1);
373 /* unlike pline, we don't futz around to keep last few chars */
374 pbuf[BUFSZ - 1] = '\0'; /* terminate strncpy or truncate vsprintf */
376 raw_print(line);
377 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
378 execplinehandler(line);
379 #endif
380 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
381 VA_END(); /* (see vpline) */
382 #endif
385 /*VARARGS1*/
386 void impossible
387 VA_DECL(const char *, s)
389 char pbuf[2 * BUFSZ];
390 VA_START(s);
391 VA_INIT(s, const char *);
392 if (program_state.in_impossible)
393 panic("impossible called impossible");
395 program_state.in_impossible = 1;
396 Vsprintf(pbuf, s, VA_ARGS);
397 pbuf[BUFSZ - 1] = '\0'; /* sanity */
398 paniclog("impossible", pbuf);
399 pline("%s", pbuf);
400 pline("Program in disorder - perhaps you'd better #quit.");
401 program_state.in_impossible = 0;
402 VA_END();
405 const char *
406 align_str(alignment)
407 aligntyp alignment;
409 switch ((int) alignment) {
410 case A_CHAOTIC:
411 return "chaotic";
412 case A_NEUTRAL:
413 return "neutral";
414 case A_LAWFUL:
415 return "lawful";
416 case A_NONE:
417 return "unaligned";
419 return "unknown";
422 void
423 mstatusline(mtmp)
424 register struct monst *mtmp;
426 aligntyp alignment = mon_aligntyp(mtmp);
427 char info[BUFSZ], monnambuf[BUFSZ];
429 info[0] = 0;
430 if (mtmp->mtame) {
431 Strcat(info, ", tame");
432 if (wizard) {
433 Sprintf(eos(info), " (%d", mtmp->mtame);
434 if (!mtmp->isminion)
435 Sprintf(eos(info), "; hungry %ld; apport %d",
436 EDOG(mtmp)->hungrytime, EDOG(mtmp)->apport);
437 Strcat(info, ")");
439 } else if (mtmp->mpeaceful)
440 Strcat(info, ", peaceful");
442 if (mtmp->data == &mons[PM_LONG_WORM]) {
443 int segndx, nsegs = count_wsegs(mtmp);
445 /* the worm code internals don't consider the head of be one of
446 the worm's segments, but we count it as such when presenting
447 worm feedback to the player */
448 if (!nsegs) {
449 Strcat(info, ", single segment");
450 } else {
451 ++nsegs; /* include head in the segment count */
452 segndx = wseg_at(mtmp, bhitpos.x, bhitpos.y);
453 Sprintf(eos(info), ", %d%s of %d segments",
454 segndx, ordin(segndx), nsegs);
457 if (mtmp->cham >= LOW_PM && mtmp->data != &mons[mtmp->cham])
458 /* don't reveal the innate form (chameleon, vampire, &c),
459 just expose the fact that this current form isn't it */
460 Strcat(info, ", shapechanger");
461 /* pets eating mimic corpses mimic while eating, so this comes first */
462 if (mtmp->meating)
463 Strcat(info, ", eating");
464 /* a stethoscope exposes mimic before getting here so this
465 won't be relevant for it, but wand of probing doesn't */
466 if (mtmp->mundetected || mtmp->m_ap_type)
467 mhidden_description(mtmp, TRUE, eos(info));
468 if (mtmp->mcan)
469 Strcat(info, ", cancelled");
470 if (mtmp->mconf)
471 Strcat(info, ", confused");
472 if (mtmp->mblinded || !mtmp->mcansee)
473 Strcat(info, ", blind");
474 if (mtmp->mstun)
475 Strcat(info, ", stunned");
476 if (mtmp->msleeping)
477 Strcat(info, ", asleep");
478 #if 0 /* unfortunately mfrozen covers temporary sleep and being busy \
479 (donning armor, for instance) as well as paralysis */
480 else if (mtmp->mfrozen)
481 Strcat(info, ", paralyzed");
482 #else
483 else if (mtmp->mfrozen || !mtmp->mcanmove)
484 Strcat(info, ", can't move");
485 #endif
486 /* [arbitrary reason why it isn't moving] */
487 else if (mtmp->mstrategy & STRAT_WAITMASK)
488 Strcat(info, ", meditating");
489 if (mtmp->mflee)
490 Strcat(info, ", scared");
491 if (mtmp->mtrapped)
492 Strcat(info, ", trapped");
493 if (mtmp->mspeed)
494 Strcat(info, mtmp->mspeed == MFAST ? ", fast" : mtmp->mspeed == MSLOW
495 ? ", slow"
496 : ", ???? speed");
497 if (mtmp->minvis)
498 Strcat(info, ", invisible");
499 if (mtmp == u.ustuck)
500 Strcat(info, sticks(youmonst.data)
501 ? ", held by you"
502 : !u.uswallow ? ", holding you"
503 : attacktype_fordmg(u.ustuck->data,
504 AT_ENGL, AD_DGST)
505 ? ", digesting you"
506 : is_animal(u.ustuck->data)
507 ? ", swallowing you"
508 : ", engulfing you");
509 if (mtmp == u.usteed)
510 Strcat(info, ", carrying you");
512 /* avoid "Status of the invisible newt ..., invisible" */
513 /* and unlike a normal mon_nam, use "saddled" even if it has a name */
514 Strcpy(monnambuf, x_monnam(mtmp, ARTICLE_THE, (char *) 0,
515 (SUPPRESS_IT | SUPPRESS_INVISIBLE), FALSE));
517 pline("Status of %s (%s): Level %d HP %d(%d) AC %d%s.", monnambuf,
518 align_str(alignment), mtmp->m_lev, mtmp->mhp, mtmp->mhpmax,
519 find_mac(mtmp), info);
522 void
523 ustatusline()
525 char info[BUFSZ];
527 info[0] = '\0';
528 if (Sick) {
529 Strcat(info, ", dying from");
530 if (u.usick_type & SICK_VOMITABLE)
531 Strcat(info, " food poisoning");
532 if (u.usick_type & SICK_NONVOMITABLE) {
533 if (u.usick_type & SICK_VOMITABLE)
534 Strcat(info, " and");
535 Strcat(info, " illness");
538 if (Stoned)
539 Strcat(info, ", solidifying");
540 if (Slimed)
541 Strcat(info, ", becoming slimy");
542 if (Strangled)
543 Strcat(info, ", being strangled");
544 if (Vomiting)
545 Strcat(info, ", nauseated"); /* !"nauseous" */
546 if (Confusion)
547 Strcat(info, ", confused");
548 if (Blind) {
549 Strcat(info, ", blind");
550 if (u.ucreamed) {
551 if ((long) u.ucreamed < Blinded || Blindfolded
552 || !haseyes(youmonst.data))
553 Strcat(info, ", cover");
554 Strcat(info, "ed by sticky goop");
555 } /* note: "goop" == "glop"; variation is intentional */
557 if (Stunned)
558 Strcat(info, ", stunned");
559 if (!u.usteed && Wounded_legs) {
560 const char *what = body_part(LEG);
561 if ((Wounded_legs & BOTH_SIDES) == BOTH_SIDES)
562 what = makeplural(what);
563 Sprintf(eos(info), ", injured %s", what);
565 if (Glib)
566 Sprintf(eos(info), ", slippery %s", makeplural(body_part(HAND)));
567 if (u.utrap)
568 Strcat(info, ", trapped");
569 if (Fast)
570 Strcat(info, Very_fast ? ", very fast" : ", fast");
571 if (u.uundetected)
572 Strcat(info, ", concealed");
573 if (Invis)
574 Strcat(info, ", invisible");
575 if (u.ustuck) {
576 if (sticks(youmonst.data))
577 Strcat(info, ", holding ");
578 else
579 Strcat(info, ", held by ");
580 Strcat(info, mon_nam(u.ustuck));
583 pline("Status of %s (%s): Level %d HP %d(%d) AC %d%s.", plname,
584 piousness(FALSE, align_str(u.ualign.type)),
585 Upolyd ? mons[u.umonnum].mlevel : u.ulevel, Upolyd ? u.mh : u.uhp,
586 Upolyd ? u.mhmax : u.uhpmax, u.uac, info);
589 void
590 self_invis_message()
592 pline("%s %s.",
593 Hallucination ? "Far out, man! You" : "Gee! All of a sudden, you",
594 See_invisible ? "can see right through yourself"
595 : "can't see yourself");
598 char *
599 piousness(showneg, suffix)
600 boolean showneg;
601 const char *suffix;
603 static char buf[32]; /* bigger than "insufficiently neutral" */
604 const char *pio;
606 /* note: piousness 20 matches MIN_QUEST_ALIGN (quest.h) */
607 if (u.ualign.record >= 20)
608 pio = "piously";
609 else if (u.ualign.record > 13)
610 pio = "devoutly";
611 else if (u.ualign.record > 8)
612 pio = "fervently";
613 else if (u.ualign.record > 3)
614 pio = "stridently";
615 else if (u.ualign.record == 3)
616 pio = "";
617 else if (u.ualign.record > 0)
618 pio = "haltingly";
619 else if (u.ualign.record == 0)
620 pio = "nominally";
621 else if (!showneg)
622 pio = "insufficiently";
623 else if (u.ualign.record >= -3)
624 pio = "strayed";
625 else if (u.ualign.record >= -8)
626 pio = "sinned";
627 else
628 pio = "transgressed";
630 Sprintf(buf, "%s", pio);
631 if (suffix && (!showneg || u.ualign.record >= 0)) {
632 if (u.ualign.record != 3)
633 Strcat(buf, " ");
634 Strcat(buf, suffix);
636 return buf;
639 void
640 pudding_merge_message(otmp, otmp2)
641 struct obj *otmp;
642 struct obj *otmp2;
644 boolean visible =
645 cansee(otmp->ox, otmp->oy) || cansee(otmp2->ox, otmp2->oy);
646 boolean onfloor = otmp->where == OBJ_FLOOR || otmp2->where == OBJ_FLOOR;
647 boolean inpack = carried(otmp) || carried(otmp2);
649 /* the player will know something happened inside his own inventory */
650 if ((!Blind && visible) || inpack) {
651 if (Hallucination) {
652 if (onfloor) {
653 You_see("parts of the floor melting!");
654 } else if (inpack) {
655 Your("pack reaches out and grabs something!");
657 /* even though we can see where they should be,
658 * they'll be out of our view (minvent or container)
659 * so don't actually show anything */
660 } else if (onfloor || inpack) {
661 pline("The %s coalesce%s.", makeplural(obj_typename(otmp->otyp)),
662 inpack ? " inside your pack" : "");
664 } else {
665 You_hear("a faint sloshing sound.");
669 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
670 static boolean use_pline_handler = TRUE;
671 static void
672 execplinehandler(line)
673 const char *line;
675 int f;
676 const char *args[3];
677 char *env;
679 if (!use_pline_handler)
680 return;
682 if (!(env = nh_getenv("NETHACK_MSGHANDLER"))) {
683 use_pline_handler = FALSE;
684 return;
687 f = fork();
688 if (f == 0) { /* child */
689 args[0] = env;
690 args[1] = line;
691 args[2] = NULL;
692 (void) setgid(getgid());
693 (void) setuid(getuid());
694 (void) execv(args[0], (char *const *) args);
695 perror((char *) 0);
696 (void) fprintf(stderr, "Exec to message handler %s failed.\n",
697 env);
698 terminate(EXIT_FAILURE);
699 } else if (f > 0) {
700 int status;
701 waitpid(f, &status, 0);
702 } else if (f == -1) {
703 perror((char *) 0);
704 use_pline_handler = FALSE;
705 pline("Fork to message handler failed.");
708 #endif /* defined(POSIX_TYPES) || defined(__GNUC__) */
710 /*pline.c*/