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 /* 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 };
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));
32 VA_DECL(const char *, line
)
35 VA_INIT(line
, char *);
36 vpline(line
, VA_ARGS
);
42 vpline(const char *line
, va_list the_args
)
45 vpline(line
, the_args
)
50 #else /* USE_STDARG | USE_VARARG */
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() */
61 /* Do NOT use VA_START and VA_END in here... see above */
66 if (program_state
.done_hup
)
69 if (program_state
.wizkit_wishing
)
72 if (index(line
, '%')) {
73 Vsprintf(pbuf
, line
, VA_ARGS
);
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';
91 if (!iflags
.window_inited
) {
93 iflags
.last_msg
= PLNMSG_UNKNOWN
;
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).
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
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
);
121 free((genericptr_t
) oldest
);
122 saved_plines
[indx
] = dupstr(line
);
124 saved_pline_index
= (indx
+ 1) % DUMPLOG_MSG_COUNT
;
128 msgtyp
= msgtype_type(line
, no_repeat
);
129 if (msgtyp
== MSGTYP_NOSHOW
130 || (msgtyp
== MSGTYP_NOREP
&& !strcmp(line
, prevmsg
)))
132 if (vision_full_recalc
)
135 flush_screen(1); /* %% */
137 putstr(WIN_MESSAGE
, 0, line
);
139 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
140 execplinehandler(line
);
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() */
158 VA_DECL(const char *, line
)
161 VA_INIT(line
, const char *);
163 vpline(line
, VA_ARGS
);
169 /* work buffer for You(), &c and verbalize() */
170 static char *you_buf
= 0;
171 static int you_buf_siz
= 0;
177 if (siz
> you_buf_siz
) {
179 free((genericptr_t
) you_buf
);
180 you_buf_siz
= siz
+ 10;
181 you_buf
= (char *) alloc((unsigned) you_buf_siz
);
190 free((genericptr_t
) you_buf
), you_buf
= (char *) 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)
203 VA_DECL(const char *, line
)
207 VA_INIT(line
, const char *);
208 vpline(YouMessage(tmp
, "You ", line
), VA_ARGS
);
214 VA_DECL(const char *, line
)
218 VA_INIT(line
, const char *);
219 vpline(YouMessage(tmp
, "Your ", line
), VA_ARGS
);
225 VA_DECL(const char *, line
)
229 VA_INIT(line
, const char *);
231 YouPrefix(tmp
, "You dream that you feel ", line
);
233 YouPrefix(tmp
, "You feel ", line
);
234 vpline(strcat(tmp
, line
), VA_ARGS
);
240 VA_DECL(const char *, line
)
244 VA_INIT(line
, const char *);
245 vpline(YouMessage(tmp
, "You can't ", line
), VA_ARGS
);
251 VA_DECL(const char *, line
)
255 VA_INIT(line
, const char *);
256 vpline(YouMessage(tmp
, "The ", line
), VA_ARGS
);
262 VA_DECL(const char *, line
)
266 VA_INIT(line
, const char *);
267 vpline(YouMessage(tmp
, "There ", line
), VA_ARGS
);
273 VA_DECL(const char *, line
)
277 if (Deaf
|| !flags
.acoustics
)
280 VA_INIT(line
, const char *);
282 YouPrefix(tmp
, "You barely hear ", line
);
284 YouPrefix(tmp
, "You dream that you hear ", line
);
286 YouPrefix(tmp
, "You hear ", line
);
287 vpline(strcat(tmp
, line
), VA_ARGS
);
293 VA_DECL(const char *, line
)
298 VA_INIT(line
, const char *);
300 YouPrefix(tmp
, "You dream that you see ", line
);
301 else if (Blind
) /* caller should have caught this... */
302 YouPrefix(tmp
, "You sense ", line
);
304 YouPrefix(tmp
, "You see ", line
);
305 vpline(strcat(tmp
, line
), VA_ARGS
);
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.
315 VA_DECL(const char *, line
)
320 VA_INIT(line
, const char *);
321 tmp
= You_buf((int) strlen(line
) + sizeof "\"\"");
325 vpline(tmp
, VA_ARGS
);
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));
338 VA_DECL(const char *, line
)
341 VA_INIT(line
, char *);
342 vraw_printf(line
, VA_ARGS
);
348 vraw_printf(const char *line
, va_list the_args
)
351 vraw_printf(line
, the_args
)
356 #else /* USE_STDARG | USE_VARARG */
359 VA_DECL(const char *, line
)
362 char pbuf
[3 * BUFSZ
];
364 /* Do NOT use VA_START and VA_END in here... see above */
366 if (index(line
, '%')) {
367 Vsprintf(pbuf
, line
, VA_ARGS
);
370 if ((ln
= (int) strlen(line
)) > BUFSZ
- 1) {
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 */
377 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
378 execplinehandler(line
);
380 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
381 VA_END(); /* (see vpline) */
387 VA_DECL(const char *, s
)
389 char pbuf
[2 * BUFSZ
];
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
);
400 pline("Program in disorder - perhaps you'd better #quit.");
401 program_state
.in_impossible
= 0;
409 switch ((int) alignment
) {
424 register struct monst
*mtmp
;
426 aligntyp alignment
= mon_aligntyp(mtmp
);
427 char info
[BUFSZ
], monnambuf
[BUFSZ
];
431 Strcat(info
, ", tame");
433 Sprintf(eos(info
), " (%d", mtmp
->mtame
);
435 Sprintf(eos(info
), "; hungry %ld; apport %d",
436 EDOG(mtmp
)->hungrytime
, EDOG(mtmp
)->apport
);
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 */
449 Strcat(info
, ", single segment");
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 */
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
));
469 Strcat(info
, ", cancelled");
471 Strcat(info
, ", confused");
472 if (mtmp
->mblinded
|| !mtmp
->mcansee
)
473 Strcat(info
, ", blind");
475 Strcat(info
, ", stunned");
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");
483 else if (mtmp
->mfrozen
|| !mtmp
->mcanmove
)
484 Strcat(info
, ", can't move");
486 /* [arbitrary reason why it isn't moving] */
487 else if (mtmp
->mstrategy
& STRAT_WAITMASK
)
488 Strcat(info
, ", meditating");
490 Strcat(info
, ", scared");
492 Strcat(info
, ", trapped");
494 Strcat(info
, mtmp
->mspeed
== MFAST
? ", fast" : mtmp
->mspeed
== MSLOW
498 Strcat(info
, ", invisible");
499 if (mtmp
== u
.ustuck
)
500 Strcat(info
, sticks(youmonst
.data
)
502 : !u
.uswallow
? ", holding you"
503 : attacktype_fordmg(u
.ustuck
->data
,
506 : is_animal(u
.ustuck
->data
)
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
);
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");
539 Strcat(info
, ", solidifying");
541 Strcat(info
, ", becoming slimy");
543 Strcat(info
, ", being strangled");
545 Strcat(info
, ", nauseated"); /* !"nauseous" */
547 Strcat(info
, ", confused");
549 Strcat(info
, ", blind");
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 */
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
);
566 Sprintf(eos(info
), ", slippery %s", makeplural(body_part(HAND
)));
568 Strcat(info
, ", trapped");
570 Strcat(info
, Very_fast
? ", very fast" : ", fast");
572 Strcat(info
, ", concealed");
574 Strcat(info
, ", invisible");
576 if (sticks(youmonst
.data
))
577 Strcat(info
, ", holding ");
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
);
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");
599 piousness(showneg
, suffix
)
603 static char buf
[32]; /* bigger than "insufficiently neutral" */
606 /* note: piousness 20 matches MIN_QUEST_ALIGN (quest.h) */
607 if (u
.ualign
.record
>= 20)
609 else if (u
.ualign
.record
> 13)
611 else if (u
.ualign
.record
> 8)
613 else if (u
.ualign
.record
> 3)
615 else if (u
.ualign
.record
== 3)
617 else if (u
.ualign
.record
> 0)
619 else if (u
.ualign
.record
== 0)
622 pio
= "insufficiently";
623 else if (u
.ualign
.record
>= -3)
625 else if (u
.ualign
.record
>= -8)
628 pio
= "transgressed";
630 Sprintf(buf
, "%s", pio
);
631 if (suffix
&& (!showneg
|| u
.ualign
.record
>= 0)) {
632 if (u
.ualign
.record
!= 3)
640 pudding_merge_message(otmp
, otmp2
)
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
) {
653 You_see("parts of the floor melting!");
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" : "");
665 You_hear("a faint sloshing sound.");
669 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
670 static boolean use_pline_handler
= TRUE
;
672 execplinehandler(line
)
679 if (!use_pline_handler
)
682 if (!(env
= nh_getenv("NETHACK_MSGHANDLER"))) {
683 use_pline_handler
= FALSE
;
688 if (f
== 0) { /* child */
692 (void) setgid(getgid());
693 (void) setuid(getuid());
694 (void) execv(args
[0], (char *const *) args
);
696 (void) fprintf(stderr
, "Exec to message handler %s failed.\n",
698 terminate(EXIT_FAILURE
);
701 waitpid(f
, &status
, 0);
702 } else if (f
== -1) {
704 use_pline_handler
= FALSE
;
705 pline("Fork to message handler failed.");
708 #endif /* defined(POSIX_TYPES) || defined(__GNUC__) */