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 \
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 };
22 /* keep the most recent DUMPLOG_MSG_COUNT messages */
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
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 */
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 */
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;
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));
73 VA_DECL(const char *, line
)
76 VA_INIT(line
, char *);
77 vpline(line
, VA_ARGS
);
83 vpline(const char *line
, va_list the_args
)
86 vpline(line
, the_args
)
91 #else /* USE_STDARG | USE_VARARG */
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() */
102 /* Do NOT use VA_START and VA_END in here... see above */
106 #ifdef HANGUPHANDLING
107 if (program_state
.done_hup
)
110 if (program_state
.wizkit_wishing
)
113 if (index(line
, '%')) {
114 Vsprintf(pbuf
, line
, VA_ARGS
);
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';
131 if (!iflags
.window_inited
) {
133 iflags
.last_msg
= PLNMSG_UNKNOWN
;
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).
145 msgtyp
= msgtype_type(line
, no_repeat
);
146 if (msgtyp
== MSGTYP_NOSHOW
147 || (msgtyp
== MSGTYP_NOREP
&& !strcmp(line
, prevmsg
)))
149 if (vision_full_recalc
)
152 flush_screen(1); /* %% */
154 putstr(WIN_MESSAGE
, 0, line
);
156 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
157 execplinehandler(line
);
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() */
175 VA_DECL(const char *, line
)
178 VA_INIT(line
, const char *);
180 vpline(line
, VA_ARGS
);
186 /* work buffer for You(), &c and verbalize() */
187 static char *you_buf
= 0;
188 static int you_buf_siz
= 0;
194 if (siz
> you_buf_siz
) {
196 free((genericptr_t
) you_buf
);
197 you_buf_siz
= siz
+ 10;
198 you_buf
= (char *) alloc((unsigned) you_buf_siz
);
207 free((genericptr_t
) you_buf
), you_buf
= (char *) 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)
220 VA_DECL(const char *, line
)
224 VA_INIT(line
, const char *);
225 vpline(YouMessage(tmp
, "You ", line
), VA_ARGS
);
231 VA_DECL(const char *, line
)
235 VA_INIT(line
, const char *);
236 vpline(YouMessage(tmp
, "Your ", line
), VA_ARGS
);
242 VA_DECL(const char *, line
)
246 VA_INIT(line
, const char *);
248 YouPrefix(tmp
, "You dream that you feel ", line
);
250 YouPrefix(tmp
, "You feel ", line
);
251 vpline(strcat(tmp
, line
), VA_ARGS
);
257 VA_DECL(const char *, line
)
261 VA_INIT(line
, const char *);
262 vpline(YouMessage(tmp
, "You can't ", line
), VA_ARGS
);
268 VA_DECL(const char *, line
)
272 VA_INIT(line
, const char *);
273 vpline(YouMessage(tmp
, "The ", line
), VA_ARGS
);
279 VA_DECL(const char *, line
)
283 VA_INIT(line
, const char *);
284 vpline(YouMessage(tmp
, "There ", line
), VA_ARGS
);
290 VA_DECL(const char *, line
)
294 if (Deaf
|| !flags
.acoustics
)
297 VA_INIT(line
, const char *);
299 YouPrefix(tmp
, "You barely hear ", line
);
301 YouPrefix(tmp
, "You dream that you hear ", line
);
303 YouPrefix(tmp
, "You hear ", line
);
304 vpline(strcat(tmp
, line
), VA_ARGS
);
310 VA_DECL(const char *, line
)
315 VA_INIT(line
, const char *);
317 YouPrefix(tmp
, "You dream that you see ", line
);
318 else if (Blind
) /* caller should have caught this... */
319 YouPrefix(tmp
, "You sense ", line
);
321 YouPrefix(tmp
, "You see ", line
);
322 vpline(strcat(tmp
, line
), VA_ARGS
);
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.
332 VA_DECL(const char *, line
)
337 VA_INIT(line
, const char *);
338 tmp
= You_buf((int) strlen(line
) + sizeof "\"\"");
342 vpline(tmp
, VA_ARGS
);
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));
355 VA_DECL(const char *, line
)
358 VA_INIT(line
, char *);
359 vraw_printf(line
, VA_ARGS
);
365 vraw_printf(const char *line
, va_list the_args
)
368 vraw_printf(line
, the_args
)
373 #else /* USE_STDARG | USE_VARARG */
376 VA_DECL(const char *, line
)
379 char pbuf
[3 * BUFSZ
];
381 /* Do NOT use VA_START and VA_END in here... see above */
383 if (index(line
, '%')) {
384 Vsprintf(pbuf
, line
, VA_ARGS
);
387 if ((ln
= (int) strlen(line
)) > BUFSZ
- 1) {
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 */
394 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
395 execplinehandler(line
);
397 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
398 VA_END(); /* (see vpline) */
404 VA_DECL(const char *, s
)
406 char pbuf
[2 * BUFSZ
];
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
);
417 pline("Program in disorder - perhaps you'd better #quit.");
418 program_state
.in_impossible
= 0;
426 switch ((int) alignment
) {
441 register struct monst
*mtmp
;
443 aligntyp alignment
= mon_aligntyp(mtmp
);
444 char info
[BUFSZ
], monnambuf
[BUFSZ
];
448 Strcat(info
, ", tame");
450 Sprintf(eos(info
), " (%d", mtmp
->mtame
);
452 Sprintf(eos(info
), "; hungry %ld; apport %d",
453 EDOG(mtmp
)->hungrytime
, EDOG(mtmp
)->apport
);
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 */
466 Strcat(info
, ", single segment");
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 */
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
));
486 Strcat(info
, ", cancelled");
488 Strcat(info
, ", confused");
489 if (mtmp
->mblinded
|| !mtmp
->mcansee
)
490 Strcat(info
, ", blind");
492 Strcat(info
, ", stunned");
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");
500 else if (mtmp
->mfrozen
|| !mtmp
->mcanmove
)
501 Strcat(info
, ", can't move");
503 /* [arbitrary reason why it isn't moving] */
504 else if (mtmp
->mstrategy
& STRAT_WAITMASK
)
505 Strcat(info
, ", meditating");
507 Strcat(info
, ", scared");
509 Strcat(info
, ", trapped");
511 Strcat(info
, mtmp
->mspeed
== MFAST
? ", fast" : mtmp
->mspeed
== MSLOW
515 Strcat(info
, ", invisible");
516 if (mtmp
== u
.ustuck
)
517 Strcat(info
, sticks(youmonst
.data
)
519 : !u
.uswallow
? ", holding you"
520 : attacktype_fordmg(u
.ustuck
->data
,
523 : is_animal(u
.ustuck
->data
)
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
);
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");
556 Strcat(info
, ", solidifying");
558 Strcat(info
, ", becoming slimy");
560 Strcat(info
, ", being strangled");
562 Strcat(info
, ", nauseated"); /* !"nauseous" */
564 Strcat(info
, ", confused");
566 Strcat(info
, ", blind");
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 */
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
);
583 Sprintf(eos(info
), ", slippery %s", makeplural(body_part(HAND
)));
585 Strcat(info
, ", trapped");
587 Strcat(info
, Very_fast
? ", very fast" : ", fast");
589 Strcat(info
, ", concealed");
591 Strcat(info
, ", invisible");
593 if (sticks(youmonst
.data
))
594 Strcat(info
, ", holding ");
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
);
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");
616 piousness(showneg
, suffix
)
620 static char buf
[32]; /* bigger than "insufficiently neutral" */
623 /* note: piousness 20 matches MIN_QUEST_ALIGN (quest.h) */
624 if (u
.ualign
.record
>= 20)
626 else if (u
.ualign
.record
> 13)
628 else if (u
.ualign
.record
> 8)
630 else if (u
.ualign
.record
> 3)
632 else if (u
.ualign
.record
== 3)
634 else if (u
.ualign
.record
> 0)
636 else if (u
.ualign
.record
== 0)
639 pio
= "insufficiently";
640 else if (u
.ualign
.record
>= -3)
642 else if (u
.ualign
.record
>= -8)
645 pio
= "transgressed";
647 Sprintf(buf
, "%s", pio
);
648 if (suffix
&& (!showneg
|| u
.ualign
.record
>= 0)) {
649 if (u
.ualign
.record
!= 3)
657 pudding_merge_message(otmp
, otmp2
)
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
) {
670 You_see("parts of the floor melting!");
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" : "");
682 You_hear("a faint sloshing sound.");
686 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
687 static boolean use_pline_handler
= TRUE
;
689 execplinehandler(line
)
696 if (!use_pline_handler
)
699 if (!(env
= nh_getenv("ANETHACK_MSGHANDLER"))) {
700 use_pline_handler
= FALSE
;
705 if (f
== 0) { /* child */
709 (void) setgid(getgid());
710 (void) setuid(getuid());
711 (void) execv(args
[0], (char *const *) args
);
713 (void) fprintf(stderr
, "Exec to message handler %s failed.\n",
715 terminate(EXIT_FAILURE
);
718 waitpid(f
, &status
, 0);
719 } else if (f
== -1) {
721 use_pline_handler
= FALSE
;
722 pline("Fork to message handler failed.");
725 #endif /* defined(POSIX_TYPES) || defined(__GNUC__) */