Files
nethack/src/pline.c
nethack.rankin 283b681072 fix #H348 - "you trip over it" after non-"it" message (trunk only)
Reported to us by <email deleted>:
  'You are beginning to feel hungry.  You trip over it.'
and also recently in the newsgroup by "<Someone>":
  There is ice here.  *You see here an electric eel corpse.*
  Bib hits the electric eel.  Bib misses the electric eel.
  Bib misses the electric eel.  The electric eel misses Bib.
  The electric eel misses Bib.  *You trip over it.*

     slip_or_trip() was oversimplifying things by assuming that if there
is one object at the hero's location, a message about what that object is
has just been given.  Any timeout message which precedes Fumbling (lots
of candiates besides hunger) could intervene, as could monster activity
between the hero's move and timeout handling.  Aside from the reported
cases, that code hadn't been updated to account for the new pile_limit
option which could be set to 1 and force a popup display instead of the
usual "you see <an item> here".  This fix adds a flag that can be used
to track the most recent message.  It is cleared by pline for every
message, so pline's caller sets it _after_ the message of interest has
been displayed.
2007-06-10 03:01:31 +00:00

503 lines
12 KiB
C

/* SCCS Id: @(#)pline.c 3.5 2007/02/09 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
#define NEED_VARARGS /* Uses ... */ /* comment line for pre-compiled headers */
#include "hack.h"
static boolean no_repeat = FALSE;
static char *FDECL(You_buf, (int));
/*VARARGS1*/
/* Note that these declarations rely on knowledge of the internals
* of the variable argument handling stuff in "tradstdc.h"
*/
#if defined(USE_STDARG) || defined(USE_VARARGS)
static void FDECL(vpline, (const char *, va_list));
void
pline VA_DECL(const char *, line)
VA_START(line);
VA_INIT(line, char *);
vpline(line, VA_ARGS);
VA_END();
}
# ifdef USE_STDARG
static void
vpline(const char *line, va_list the_args) {
# else
static void
vpline(line, the_args) const char *line; va_list the_args; {
# endif
#else /* USE_STDARG | USE_VARARG */
#define vpline pline
void
pline VA_DECL(const char *, line)
#endif /* USE_STDARG | USE_VARARG */
char pbuf[3*BUFSZ];
int ln;
/* Do NOT use VA_START and VA_END in here... see above */
if (!line || !*line) return;
#ifdef HANGUPHANDLING
if (program_state.done_hup) return;
#endif
#ifdef WIZARD
if (program_state.wizkit_wishing) return;
#endif
if (index(line, '%')) {
Vsprintf(pbuf,line,VA_ARGS);
line = pbuf;
}
if ((ln = (int)strlen(line)) > BUFSZ-1) {
if (line != pbuf) /* no '%' was present */
(void)strncpy(pbuf, line, BUFSZ-1); /* caveat: unterminated */
/* truncate, preserving the final 3 characters:
"___ extremely long text" -> "___ extremely l...ext"
(this may be suboptimal if overflow is less than 3) */
(void)strncpy(pbuf + BUFSZ-1 - 6, "...", 3);
/* avoid strncpy; buffers could overlap if excess is small */
pbuf[BUFSZ-1 - 3] = line[ln - 3];
pbuf[BUFSZ-1 - 2] = line[ln - 2];
pbuf[BUFSZ-1 - 1] = line[ln - 1];
pbuf[BUFSZ-1] = '\0';
line = pbuf;
}
if (!iflags.window_inited) {
raw_print(line);
iflags.last_msg = PLNMSG_UNKNOWN;
return;
}
#ifndef MAC
# ifdef UNICODE_WIDEWINPORT
if (no_repeat && !nhwstrcmp(toplines, line))
# else
if (no_repeat && !strcmp(toplines, line))
# endif
return;
#endif /* MAC */
if (vision_full_recalc) vision_recalc(0);
if (u.ux) flush_screen(1); /* %% */
putstr(WIN_MESSAGE, 0, line);
/* this gets cleared after every pline message */
iflags.last_msg = PLNMSG_UNKNOWN;
}
/*VARARGS1*/
void
Norep VA_DECL(const char *, line)
VA_START(line);
VA_INIT(line, const char *);
no_repeat = TRUE;
vpline(line, VA_ARGS);
no_repeat = FALSE;
VA_END();
return;
}
/* work buffer for You(), &c and verbalize() */
static char *you_buf = 0;
static int you_buf_siz = 0;
static char *
You_buf(siz)
int siz;
{
if (siz > you_buf_siz) {
if (you_buf) free((genericptr_t) you_buf);
you_buf_siz = siz + 10;
you_buf = (char *) alloc((unsigned) you_buf_siz);
}
return you_buf;
}
void
free_youbuf()
{
if (you_buf) free((genericptr_t) you_buf), you_buf = (char *)0;
you_buf_siz = 0;
}
/* `prefix' must be a string literal, not a pointer */
#define YouPrefix(pointer,prefix,text) \
Strcpy((pointer = You_buf((int)(strlen(text) + sizeof prefix))), prefix)
#define YouMessage(pointer,prefix,text) \
strcat((YouPrefix(pointer, prefix, text), pointer), text)
/*VARARGS1*/
void
You VA_DECL(const char *, line)
char *tmp;
VA_START(line);
VA_INIT(line, const char *);
vpline(YouMessage(tmp, "You ", line), VA_ARGS);
VA_END();
}
/*VARARGS1*/
void
Your VA_DECL(const char *,line)
char *tmp;
VA_START(line);
VA_INIT(line, const char *);
vpline(YouMessage(tmp, "Your ", line), VA_ARGS);
VA_END();
}
/*VARARGS1*/
void
You_feel VA_DECL(const char *,line)
char *tmp;
VA_START(line);
VA_INIT(line, const char *);
if (Unaware)
YouPrefix(tmp, "You dream that you feel ", line);
else
YouPrefix(tmp, "You feel ", line);
vpline(strcat(tmp, line), VA_ARGS);
VA_END();
}
/*VARARGS1*/
void
You_cant VA_DECL(const char *,line)
char *tmp;
VA_START(line);
VA_INIT(line, const char *);
vpline(YouMessage(tmp, "You can't ", line), VA_ARGS);
VA_END();
}
/*VARARGS1*/
void
pline_The VA_DECL(const char *,line)
char *tmp;
VA_START(line);
VA_INIT(line, const char *);
vpline(YouMessage(tmp, "The ", line), VA_ARGS);
VA_END();
}
/*VARARGS1*/
void
There VA_DECL(const char *,line)
char *tmp;
VA_START(line);
VA_INIT(line, const char *);
vpline(YouMessage(tmp, "There ", line), VA_ARGS);
VA_END();
}
/*VARARGS1*/
void
You_hear VA_DECL(const char *,line)
char *tmp;
if (Deaf || !flags.acoustics) return;
VA_START(line);
VA_INIT(line, const char *);
if (Underwater)
YouPrefix(tmp, "You barely hear ", line);
else if (Unaware)
YouPrefix(tmp, "You dream that you hear ", line);
else
YouPrefix(tmp, "You hear ", line);
vpline(strcat(tmp, line), VA_ARGS);
VA_END();
}
/*VARARGS1*/
void
You_see VA_DECL(const char *,line)
char *tmp;
VA_START(line);
VA_INIT(line, const char *);
if (Unaware)
YouPrefix(tmp, "You dream that you see ", line);
else if (Blind) /* caller should have caught this... */
YouPrefix(tmp, "You sense ", line);
else
YouPrefix(tmp, "You see ", line);
vpline(strcat(tmp, line), VA_ARGS);
VA_END();
}
/* Print a message inside double-quotes.
* The caller is responsible for checking deafness.
* Gods can speak directly to you in spite of deafness.
*/
/*VARARGS1*/
void
verbalize VA_DECL(const char *,line)
char *tmp;
VA_START(line);
VA_INIT(line, const char *);
tmp = You_buf((int)strlen(line) + sizeof "\"\"");
Strcpy(tmp, "\"");
Strcat(tmp, line);
Strcat(tmp, "\"");
vpline(tmp, VA_ARGS);
VA_END();
}
/*VARARGS1*/
/* Note that these declarations rely on knowledge of the internals
* of the variable argument handling stuff in "tradstdc.h"
*/
#if defined(USE_STDARG) || defined(USE_VARARGS)
static void FDECL(vraw_printf,(const char *,va_list));
void
raw_printf VA_DECL(const char *, line)
VA_START(line);
VA_INIT(line, char *);
vraw_printf(line, VA_ARGS);
VA_END();
}
# ifdef USE_STDARG
static void
vraw_printf(const char *line, va_list the_args) {
# else
static void
vraw_printf(line, the_args) const char *line; va_list the_args; {
# endif
#else /* USE_STDARG | USE_VARARG */
void
raw_printf VA_DECL(const char *, line)
#endif
char pbuf[3*BUFSZ];
int ln;
/* Do NOT use VA_START and VA_END in here... see above */
if (index(line, '%')) {
Vsprintf(pbuf,line,VA_ARGS);
line = pbuf;
}
if ((ln = (int)strlen(line)) > BUFSZ-1) {
if (line != pbuf) line = strncpy(pbuf, line, BUFSZ-1);
/* unlike pline, we don't futz around to keep last few chars */
pbuf[BUFSZ-1] = '\0'; /* terminate strncpy or truncate vsprintf */
}
raw_print(line);
}
/*VARARGS1*/
void
impossible VA_DECL(const char *, s)
VA_START(s);
VA_INIT(s, const char *);
if (program_state.in_impossible)
panic("impossible called impossible");
program_state.in_impossible = 1;
{
char pbuf[BUFSZ];
Vsprintf(pbuf,s,VA_ARGS);
paniclog("impossible", pbuf);
}
vpline(s,VA_ARGS);
pline("Program in disorder - perhaps you'd better #quit.");
program_state.in_impossible = 0;
VA_END();
}
const char *
align_str(alignment)
aligntyp alignment;
{
switch ((int)alignment) {
case A_CHAOTIC: return "chaotic";
case A_NEUTRAL: return "neutral";
case A_LAWFUL: return "lawful";
case A_NONE: return "unaligned";
}
return "unknown";
}
void
mstatusline(mtmp)
register struct monst *mtmp;
{
aligntyp alignment = mon_aligntyp(mtmp);
char info[BUFSZ], monnambuf[BUFSZ];
info[0] = 0;
if (mtmp->mtame) { Strcat(info, ", tame");
#ifdef WIZARD
if (wizard) {
Sprintf(eos(info), " (%d", mtmp->mtame);
if (!mtmp->isminion)
Sprintf(eos(info), "; hungry %ld; apport %d",
EDOG(mtmp)->hungrytime, EDOG(mtmp)->apport);
Strcat(info, ")");
}
#endif
}
else if (mtmp->mpeaceful) Strcat(info, ", peaceful");
if (mtmp->meating) Strcat(info, ", eating");
if (mtmp->meating && (mtmp->cham == NON_PM) &&
mtmp->mappearance && mtmp->m_ap_type) {
Sprintf(eos(info), ", mimicing %s",
(mtmp->m_ap_type == M_AP_FURNITURE) ?
an(defsyms[mtmp->mappearance].explanation) :
(mtmp->m_ap_type == M_AP_OBJECT &&
OBJ_DESCR(objects[mtmp->mappearance])) ?
an(OBJ_DESCR(objects[mtmp->mappearance])) :
(mtmp->m_ap_type == M_AP_OBJECT &&
OBJ_NAME(objects[mtmp->mappearance])) ?
an(OBJ_NAME(objects[mtmp->mappearance])) :
(mtmp->m_ap_type == M_AP_MONSTER) ?
an(mons[mtmp->mappearance].mname) :
something);
}
if (mtmp->mcan) Strcat(info, ", cancelled");
if (mtmp->mconf) Strcat(info, ", confused");
if (mtmp->mblinded || !mtmp->mcansee)
Strcat(info, ", blind");
if (mtmp->mstun) Strcat(info, ", stunned");
if (mtmp->msleeping) Strcat(info, ", asleep");
#if 0 /* unfortunately mfrozen covers temporary sleep and being busy
(donning armor, for instance) as well as paralysis */
else if (mtmp->mfrozen) Strcat(info, ", paralyzed");
#else
else if (mtmp->mfrozen || !mtmp->mcanmove)
Strcat(info, ", can't move");
#endif
/* [arbitrary reason why it isn't moving] */
else if (mtmp->mstrategy & STRAT_WAITMASK)
Strcat(info, ", meditating");
else if (mtmp->mflee) Strcat(info, ", scared");
if (mtmp->mtrapped) Strcat(info, ", trapped");
if (mtmp->mspeed) Strcat(info,
mtmp->mspeed == MFAST ? ", fast" :
mtmp->mspeed == MSLOW ? ", slow" :
", ???? speed");
if (mtmp->mundetected) Strcat(info, ", concealed");
if (mtmp->minvis) Strcat(info, ", invisible");
if (mtmp == u.ustuck) Strcat(info,
sticks(youmonst.data) ? ", held by you" :
!u.uswallow ? ", holding you" :
attacktype_fordmg(u.ustuck->data, AT_ENGL, AD_DGST) ?
", digesting you" :
is_animal(u.ustuck->data) ? ", swallowing you" :
", engulfing you");
#ifdef STEED
if (mtmp == u.usteed) Strcat(info, ", carrying you");
#endif
/* avoid "Status of the invisible newt ..., invisible" */
/* and unlike a normal mon_nam, use "saddled" even if it has a name */
Strcpy(monnambuf, x_monnam(mtmp, ARTICLE_THE, (char *)0,
(SUPPRESS_IT|SUPPRESS_INVISIBLE), FALSE));
pline("Status of %s (%s): Level %d HP %d(%d) AC %d%s.",
monnambuf,
align_str(alignment),
mtmp->m_lev,
mtmp->mhp,
mtmp->mhpmax,
find_mac(mtmp),
info);
}
void
ustatusline()
{
char info[BUFSZ];
info[0] = '\0';
if (Sick) {
Strcat(info, ", dying from");
if (u.usick_type & SICK_VOMITABLE)
Strcat(info, " food poisoning");
if (u.usick_type & SICK_NONVOMITABLE) {
if (u.usick_type & SICK_VOMITABLE)
Strcat(info, " and");
Strcat(info, " illness");
}
}
if (Stoned) Strcat(info, ", solidifying");
if (Slimed) Strcat(info, ", becoming slimy");
if (Strangled) Strcat(info, ", being strangled");
if (Vomiting) Strcat(info, ", nauseated"); /* !"nauseous" */
if (Confusion) Strcat(info, ", confused");
if (Blind) {
Strcat(info, ", blind");
if (u.ucreamed) {
if ((long)u.ucreamed < Blinded || Blindfolded
|| !haseyes(youmonst.data))
Strcat(info, ", cover");
Strcat(info, "ed by sticky goop");
} /* note: "goop" == "glop"; variation is intentional */
}
if (Stunned) Strcat(info, ", stunned");
#ifdef STEED
if (!u.usteed)
#endif
if (Wounded_legs) {
const char *what = body_part(LEG);
if ((Wounded_legs & BOTH_SIDES) == BOTH_SIDES)
what = makeplural(what);
Sprintf(eos(info), ", injured %s", what);
}
if (Glib) Sprintf(eos(info), ", slippery %s",
makeplural(body_part(HAND)));
if (u.utrap) Strcat(info, ", trapped");
if (Fast) Strcat(info, Very_fast ?
", very fast" : ", fast");
if (u.uundetected) Strcat(info, ", concealed");
if (Invis) Strcat(info, ", invisible");
if (u.ustuck) {
if (sticks(youmonst.data))
Strcat(info, ", holding ");
else
Strcat(info, ", held by ");
Strcat(info, mon_nam(u.ustuck));
}
pline("Status of %s (%s%s): Level %d HP %d(%d) AC %d%s.",
plname,
(u.ualign.record >= 20) ? "piously " :
(u.ualign.record > 13) ? "devoutly " :
(u.ualign.record > 8) ? "fervently " :
(u.ualign.record > 3) ? "stridently " :
(u.ualign.record == 3) ? "" :
(u.ualign.record >= 1) ? "haltingly " :
(u.ualign.record == 0) ? "nominally " :
"insufficiently ",
align_str(u.ualign.type),
Upolyd ? mons[u.umonnum].mlevel : u.ulevel,
Upolyd ? u.mh : u.uhp,
Upolyd ? u.mhmax : u.uhpmax,
u.uac,
info);
}
void
self_invis_message()
{
pline("%s %s.",
Hallucination ? "Far out, man! You" : "Gee! All of a sudden, you",
See_invisible ? "can see right through yourself" :
"can't see yourself");
}
/*pline.c*/