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 \
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 *));
18 char* saved_plines
[DUMPLOG_MSG_COUNT
] = {0};
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));
30 VA_DECL(const char *, line
)
33 VA_INIT(line
, char *);
34 vpline(line
, VA_ARGS
);
40 vpline(const char *line
, va_list the_args
)
43 vpline(line
, the_args
)
48 #else /* USE_STDARG | USE_VARARG */
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() */
59 /* Do NOT use VA_START and VA_END in here... see above */
64 if (program_state
.done_hup
)
67 if (program_state
.wizkit_wishing
)
70 if (index(line
, '%')) {
71 Vsprintf(pbuf
, line
, VA_ARGS
);
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';
88 if (!iflags
.window_inited
) {
90 iflags
.last_msg
= PLNMSG_UNKNOWN
;
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
);
103 msgtyp
= msgtype_type(line
, no_repeat
);
104 if (msgtyp
== MSGTYP_NOSHOW
105 || (msgtyp
== MSGTYP_NOREP
&& !strcmp(line
, prevmsg
)))
107 if (vision_full_recalc
)
110 flush_screen(1); /* %% */
112 putstr(WIN_MESSAGE
, 0, line
);
114 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
115 execplinehandler(line
);
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() */
133 VA_DECL(const char *, line
)
136 VA_INIT(line
, const char *);
138 vpline(line
, VA_ARGS
);
144 /* work buffer for You(), &c and verbalize() */
145 static char *you_buf
= 0;
146 static int you_buf_siz
= 0;
152 if (siz
> you_buf_siz
) {
154 free((genericptr_t
) you_buf
);
155 you_buf_siz
= siz
+ 10;
156 you_buf
= (char *) alloc((unsigned) you_buf_siz
);
165 free((genericptr_t
) you_buf
), you_buf
= (char *) 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)
178 VA_DECL(const char *, line
)
182 VA_INIT(line
, const char *);
183 vpline(YouMessage(tmp
, "You ", line
), VA_ARGS
);
189 VA_DECL(const char *, line
)
193 VA_INIT(line
, const char *);
194 vpline(YouMessage(tmp
, "Your ", line
), VA_ARGS
);
200 VA_DECL(const char *, line
)
204 VA_INIT(line
, const char *);
206 YouPrefix(tmp
, "You dream that you feel ", line
);
208 YouPrefix(tmp
, "You feel ", line
);
209 vpline(strcat(tmp
, line
), VA_ARGS
);
215 VA_DECL(const char *, line
)
219 VA_INIT(line
, const char *);
220 vpline(YouMessage(tmp
, "You can't ", line
), VA_ARGS
);
226 VA_DECL(const char *, line
)
230 VA_INIT(line
, const char *);
231 vpline(YouMessage(tmp
, "The ", line
), VA_ARGS
);
237 VA_DECL(const char *, line
)
241 VA_INIT(line
, const char *);
242 vpline(YouMessage(tmp
, "There ", line
), VA_ARGS
);
248 VA_DECL(const char *, line
)
252 if (Deaf
|| !flags
.acoustics
)
255 VA_INIT(line
, const char *);
257 YouPrefix(tmp
, "You barely hear ", line
);
259 YouPrefix(tmp
, "You dream that you hear ", line
);
261 YouPrefix(tmp
, "You hear ", line
);
262 vpline(strcat(tmp
, line
), VA_ARGS
);
268 VA_DECL(const char *, line
)
273 VA_INIT(line
, const char *);
275 YouPrefix(tmp
, "You dream that you see ", line
);
276 else if (Blind
) /* caller should have caught this... */
277 YouPrefix(tmp
, "You sense ", line
);
279 YouPrefix(tmp
, "You see ", line
);
280 vpline(strcat(tmp
, line
), VA_ARGS
);
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.
290 VA_DECL(const char *, line
)
295 VA_INIT(line
, const char *);
296 tmp
= You_buf((int) strlen(line
) + sizeof "\"\"");
300 vpline(tmp
, VA_ARGS
);
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));
313 VA_DECL(const char *, line
)
316 VA_INIT(line
, char *);
317 vraw_printf(line
, VA_ARGS
);
323 vraw_printf(const char *line
, va_list the_args
)
326 vraw_printf(line
, the_args
)
331 #else /* USE_STDARG | USE_VARARG */
334 VA_DECL(const char *, line
)
337 char pbuf
[3 * BUFSZ
];
339 /* Do NOT use VA_START and VA_END in here... see above */
341 if (index(line
, '%')) {
342 Vsprintf(pbuf
, line
, VA_ARGS
);
345 if ((ln
= (int) strlen(line
)) > BUFSZ
- 1) {
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 */
352 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
353 execplinehandler(line
);
355 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
356 VA_END(); /* (see vpline) */
362 VA_DECL(const char *, s
)
364 char pbuf
[2 * BUFSZ
];
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
);
375 pline("Program in disorder - perhaps you'd better #quit.");
376 program_state
.in_impossible
= 0;
384 switch ((int) alignment
) {
399 register struct monst
*mtmp
;
401 aligntyp alignment
= mon_aligntyp(mtmp
);
402 char info
[BUFSZ
], monnambuf
[BUFSZ
];
406 Strcat(info
, ", tame");
408 Sprintf(eos(info
), " (%d", mtmp
->mtame
);
410 Sprintf(eos(info
), "; hungry %ld; apport %d",
411 EDOG(mtmp
)->hungrytime
, EDOG(mtmp
)->apport
);
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 */
424 Strcat(info
, ", single segment");
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 */
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
));
444 Strcat(info
, ", cancelled");
446 Strcat(info
, ", confused");
447 if (mtmp
->mblinded
|| !mtmp
->mcansee
)
448 Strcat(info
, ", blind");
450 Strcat(info
, ", stunned");
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");
458 else if (mtmp
->mfrozen
|| !mtmp
->mcanmove
)
459 Strcat(info
, ", can't move");
461 /* [arbitrary reason why it isn't moving] */
462 else if (mtmp
->mstrategy
& STRAT_WAITMASK
)
463 Strcat(info
, ", meditating");
465 Strcat(info
, ", scared");
467 Strcat(info
, ", trapped");
469 Strcat(info
, mtmp
->mspeed
== MFAST
? ", fast" : mtmp
->mspeed
== MSLOW
473 Strcat(info
, ", invisible");
474 if (mtmp
== u
.ustuck
)
475 Strcat(info
, sticks(youmonst
.data
)
477 : !u
.uswallow
? ", holding you"
478 : attacktype_fordmg(u
.ustuck
->data
,
481 : is_animal(u
.ustuck
->data
)
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
);
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");
514 Strcat(info
, ", solidifying");
516 Strcat(info
, ", becoming slimy");
518 Strcat(info
, ", being strangled");
520 Strcat(info
, ", nauseated"); /* !"nauseous" */
522 Strcat(info
, ", confused");
524 Strcat(info
, ", blind");
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 */
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
);
541 Sprintf(eos(info
), ", slippery %s", makeplural(body_part(HAND
)));
543 Strcat(info
, ", trapped");
545 Strcat(info
, Very_fast
? ", very fast" : ", fast");
547 Strcat(info
, ", concealed");
549 Strcat(info
, ", invisible");
551 if (sticks(youmonst
.data
))
552 Strcat(info
, ", holding ");
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
);
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");
574 piousness(showneg
, suffix
)
578 static char buf
[32]; /* bigger than "insufficiently neutral" */
581 /* note: piousness 20 matches MIN_QUEST_ALIGN (quest.h) */
582 if (u
.ualign
.record
>= 20)
584 else if (u
.ualign
.record
> 13)
586 else if (u
.ualign
.record
> 8)
588 else if (u
.ualign
.record
> 3)
590 else if (u
.ualign
.record
== 3)
592 else if (u
.ualign
.record
> 0)
594 else if (u
.ualign
.record
== 0)
597 pio
= "insufficiently";
598 else if (u
.ualign
.record
>= -3)
600 else if (u
.ualign
.record
>= -8)
603 pio
= "transgressed";
605 Sprintf(buf
, "%s", pio
);
606 if (suffix
&& (!showneg
|| u
.ualign
.record
>= 0)) {
607 if (u
.ualign
.record
!= 3)
615 pudding_merge_message(otmp
, otmp2
)
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
) {
628 You_see("parts of the floor melting!");
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" : "");
640 You_hear("a faint sloshing sound.");
644 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
645 static boolean use_pline_handler
= TRUE
;
647 execplinehandler(line
)
654 if (!use_pline_handler
)
657 if (!(env
= nh_getenv("NETHACK_MSGHANDLER"))) {
658 use_pline_handler
= FALSE
;
663 if (f
== 0) { /* child */
667 (void) setgid(getgid());
668 (void) setuid(getuid());
669 (void) execv(args
[0], (char *const *) args
);
671 (void) fprintf(stderr
, "Exec to message handler %s failed.\n",
673 terminate(EXIT_FAILURE
);
676 waitpid(f
, &status
, 0);
677 } else if (f
== -1) {
679 use_pline_handler
= FALSE
;
680 pline("Fork to message handler failed.");
683 #endif /* defined(POSIX_TYPES) || defined(__GNUC__) */