Introduce a new set of functions to manage delayed killers in the trunk, used in addressing the various reports of delayed killer confusion. Since existing delayed killers are related to player properties, the delayed killers are keyed by uprop indexes. I did this to avoid adding yet another set of similar identifiers. - the new delayed_killer() is used for stoning, sliming, sickness, and delayed self-genocide while polymorphed. Some other timed events don't use it (and didn't use the old delayed_killer variable) because they use a fixed message when the timeout occurs. - A new data structure, struct kinfo, is used to track both delayed and immediate killers. This encapsulates all the info involved with identifying a killer. The structure contains a buffer, which subsumes the old killer_buf and several other buffers that didn't/couldn't use killer_buf. - the killer list is saved and restored as part of the game state. - the special case of usick_cause was removed and a delayed killer list entry is now used in its place - common code dealing with (un)sliming is moved to a new make_slimed function - attempted to update all make dependencies for new end.c -> lev.h dependency, sorry if I messed any up
627 lines
16 KiB
C
627 lines
16 KiB
C
/* SCCS Id: @(#)wintext.c 3.4 1996/04/05 */
|
|
/* Copyright (c) Dean Luick, 1992 */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
/*
|
|
* File for dealing with text windows.
|
|
*
|
|
* + No global functions.
|
|
*/
|
|
|
|
#ifndef SYSV
|
|
#define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
|
|
#endif
|
|
|
|
#include <X11/Intrinsic.h>
|
|
#include <X11/StringDefs.h>
|
|
#include <X11/Shell.h>
|
|
#include <X11/Xos.h>
|
|
#include <X11/Xaw/Form.h>
|
|
#include <X11/Xaw/AsciiText.h>
|
|
#include <X11/Xaw/Cardinals.h>
|
|
#include <X11/Xatom.h>
|
|
|
|
#ifdef PRESERVE_NO_SYSV
|
|
# ifdef SYSV
|
|
# undef SYSV
|
|
# endif
|
|
# undef PRESERVE_NO_SYSV
|
|
#endif
|
|
|
|
#include "hack.h"
|
|
#include "winX.h"
|
|
#include "xwindow.h"
|
|
|
|
#ifdef GRAPHIC_TOMBSTONE
|
|
#include <X11/xpm.h>
|
|
#endif
|
|
|
|
|
|
#define TRANSIENT_TEXT /* text window is a transient window (no positioning) */
|
|
|
|
static const char text_translations[] =
|
|
"#override\n\
|
|
<BtnDown>: dismiss_text()\n\
|
|
<Key>: key_dismiss_text()";
|
|
|
|
#ifdef GRAPHIC_TOMBSTONE
|
|
static const char rip_translations[] =
|
|
"#override\n\
|
|
<BtnDown>: rip_dismiss_text()\n\
|
|
<Key>: rip_dismiss_text()";
|
|
|
|
static Widget FDECL(create_ripout_widget, (Widget));
|
|
#endif
|
|
|
|
/*ARGSUSED*/
|
|
void
|
|
delete_text(w, event, params, num_params)
|
|
Widget w;
|
|
XEvent *event;
|
|
String *params;
|
|
Cardinal *num_params;
|
|
{
|
|
struct xwindow *wp;
|
|
struct text_info_t *text_info;
|
|
|
|
wp = find_widget(w);
|
|
text_info = wp->text_information;
|
|
|
|
nh_XtPopdown(wp->popup);
|
|
|
|
if (text_info->blocked) {
|
|
exit_x_event = TRUE;
|
|
} else if (text_info->destroy_on_ack) {
|
|
destroy_text_window(wp);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Callback used for all text windows. The window is poped down on any key
|
|
* or button down event. It is destroyed if the main nethack code is done
|
|
* with it.
|
|
*/
|
|
/*ARGSUSED*/
|
|
void
|
|
dismiss_text(w, event, params, num_params)
|
|
Widget w;
|
|
XEvent *event;
|
|
String *params;
|
|
Cardinal *num_params;
|
|
{
|
|
struct xwindow *wp;
|
|
struct text_info_t *text_info;
|
|
|
|
wp = find_widget(w);
|
|
text_info = wp->text_information;
|
|
|
|
nh_XtPopdown(wp->popup);
|
|
|
|
if (text_info->blocked) {
|
|
exit_x_event = TRUE;
|
|
} else if (text_info->destroy_on_ack) {
|
|
destroy_text_window(wp);
|
|
}
|
|
}
|
|
|
|
/* Dismiss when a non-modifier key pressed. */
|
|
void
|
|
key_dismiss_text(w, event, params, num_params)
|
|
Widget w;
|
|
XEvent *event;
|
|
String *params;
|
|
Cardinal *num_params;
|
|
{
|
|
char ch = key_event_to_char((XKeyEvent *) event);
|
|
if (ch) dismiss_text(w, event, params, num_params);
|
|
}
|
|
|
|
#ifdef GRAPHIC_TOMBSTONE
|
|
/* Dismiss from clicking on rip image. */
|
|
void
|
|
rip_dismiss_text(w, event, params, num_params)
|
|
Widget w;
|
|
XEvent *event;
|
|
String *params;
|
|
Cardinal *num_params;
|
|
{
|
|
dismiss_text(XtParent(w), event, params, num_params);
|
|
}
|
|
#endif
|
|
|
|
|
|
/* ARGSUSED */
|
|
void
|
|
add_to_text_window(wp, attr, str)
|
|
struct xwindow *wp;
|
|
int attr; /* currently unused */
|
|
const char *str;
|
|
{
|
|
struct text_info_t *text_info = wp->text_information;
|
|
int width;
|
|
|
|
append_text_buffer(&text_info->text, str, FALSE);
|
|
|
|
/* Calculate text width and save longest line */
|
|
width = XTextWidth(text_info->fs, str, (int) strlen(str));
|
|
if (width > text_info->max_width)
|
|
text_info->max_width = width;
|
|
}
|
|
|
|
void
|
|
display_text_window(wp, blocking)
|
|
struct xwindow *wp;
|
|
boolean blocking;
|
|
{
|
|
struct text_info_t *text_info;
|
|
Arg args[8];
|
|
Cardinal num_args;
|
|
Dimension width, height, font_height;
|
|
int nlines;
|
|
|
|
text_info = wp->text_information;
|
|
width = text_info->max_width + text_info->extra_width;
|
|
text_info->blocked = blocking;
|
|
text_info->destroy_on_ack = FALSE;
|
|
font_height = nhFontHeight(wp->w);
|
|
|
|
/*
|
|
* Calculate the number of lines to use. First, find the number of
|
|
* lines that would fit on the screen. Next, remove four of these
|
|
* lines to give room for a possible window manager titlebar (some
|
|
* wm's put a titlebar on transient windows). Make sure we have
|
|
* _some_ lines. Finally, use the number of lines in the text if
|
|
* there are fewer than the max.
|
|
*/
|
|
nlines = (XtScreen(wp->w)->height - text_info->extra_height) / font_height;
|
|
nlines -= 4;
|
|
|
|
if (nlines > text_info->text.num_lines)
|
|
nlines = text_info->text.num_lines;
|
|
if (nlines <= 0) nlines = 1;
|
|
|
|
height = nlines * font_height + text_info->extra_height;
|
|
|
|
num_args = 0;
|
|
|
|
if (nlines < text_info->text.num_lines) {
|
|
/* add on width of scrollbar. Really should look this up,
|
|
* but can't until the window is realized. Chicken-and-egg problem.
|
|
*/
|
|
width += 20;
|
|
}
|
|
|
|
#ifdef GRAPHIC_TOMBSTONE
|
|
if (text_info->is_rip) {
|
|
Widget rip = create_ripout_widget(XtParent(wp->w));
|
|
XtSetArg(args[num_args], XtNfromVert, rip); num_args++;
|
|
}
|
|
#endif
|
|
|
|
if (width > (Dimension) XtScreen(wp->w)->width) { /* too wide for screen */
|
|
/* Back off some amount - we really need to back off the scrollbar */
|
|
/* width plus some extra. */
|
|
width = XtScreen(wp->w)->width - 20;
|
|
}
|
|
XtSetArg(args[num_args], XtNstring, text_info->text.text); num_args++;
|
|
XtSetArg(args[num_args], XtNwidth, width); num_args++;
|
|
XtSetArg(args[num_args], XtNheight, height); num_args++;
|
|
XtSetValues(wp->w, args, num_args);
|
|
|
|
#ifdef TRANSIENT_TEXT
|
|
XtRealizeWidget(wp->popup);
|
|
XSetWMProtocols(XtDisplay(wp->popup), XtWindow(wp->popup),
|
|
&wm_delete_window, 1);
|
|
positionpopup(wp->popup, FALSE);
|
|
#endif
|
|
|
|
nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w);
|
|
|
|
/* Kludge alert. Scrollbars are not sized correctly by the Text widget */
|
|
/* if added before the window is displayed, so do it afterward. */
|
|
num_args = 0;
|
|
if (nlines < text_info->text.num_lines) { /* add vert scrollbar */
|
|
XtSetArg(args[num_args], XtNscrollVertical, XawtextScrollAlways);
|
|
num_args++;
|
|
}
|
|
if (width >= (Dimension) (XtScreen(wp->w)->width-20)) { /* too wide */
|
|
XtSetArg(args[num_args], XtNscrollHorizontal, XawtextScrollAlways);
|
|
num_args++;
|
|
}
|
|
if (num_args) XtSetValues(wp->w, args, num_args);
|
|
|
|
/* We want the user to acknowlege. */
|
|
if (blocking) {
|
|
(void) x_event(EXIT_ON_EXIT);
|
|
nh_XtPopdown(wp->popup);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
create_text_window(wp)
|
|
struct xwindow *wp;
|
|
{
|
|
struct text_info_t *text_info;
|
|
Arg args[8];
|
|
Cardinal num_args;
|
|
Position top_margin, bottom_margin, left_margin, right_margin;
|
|
Widget form;
|
|
|
|
wp->type = NHW_TEXT;
|
|
|
|
wp->text_information = text_info =
|
|
(struct text_info_t *) alloc(sizeof(struct text_info_t));
|
|
|
|
init_text_buffer(&text_info->text);
|
|
text_info->max_width = 0;
|
|
text_info->extra_width = 0;
|
|
text_info->extra_height = 0;
|
|
text_info->blocked = FALSE;
|
|
text_info->destroy_on_ack = TRUE; /* Ok to destroy before display */
|
|
#ifdef GRAPHIC_TOMBSTONE
|
|
text_info->is_rip = FALSE;
|
|
#endif
|
|
|
|
num_args = 0;
|
|
XtSetArg(args[num_args], XtNallowShellResize, True); num_args++;
|
|
XtSetArg(args[num_args], XtNtranslations,
|
|
XtParseTranslationTable(text_translations)); num_args++;
|
|
|
|
#ifdef TRANSIENT_TEXT
|
|
wp->popup = XtCreatePopupShell("text", transientShellWidgetClass,
|
|
toplevel, args, num_args);
|
|
#else
|
|
wp->popup = XtCreatePopupShell("text", topLevelShellWidgetClass,
|
|
toplevel, args, num_args);
|
|
#endif
|
|
XtOverrideTranslations(wp->popup,
|
|
XtParseTranslationTable("<Message>WM_PROTOCOLS: delete_text()"));
|
|
|
|
num_args = 0;
|
|
XtSetArg(args[num_args], XtNallowShellResize, True); num_args++;
|
|
form = XtCreateManagedWidget("form", formWidgetClass, wp->popup,
|
|
args, num_args);
|
|
|
|
num_args = 0;
|
|
XtSetArg(args[num_args], XtNdisplayCaret, False); num_args++;
|
|
XtSetArg(args[num_args], XtNresize, XawtextResizeBoth); num_args++;
|
|
XtSetArg(args[num_args], XtNtranslations,
|
|
XtParseTranslationTable(text_translations)); num_args++;
|
|
|
|
wp->w = XtCreateManagedWidget(
|
|
killer.name[0] && WIN_MAP == WIN_ERR ?
|
|
"tombstone" : "text_text", /* name */
|
|
asciiTextWidgetClass,
|
|
form, /* parent widget */
|
|
args, /* set some values */
|
|
num_args); /* number of values to set */
|
|
|
|
/* Get the font and margin information. */
|
|
num_args = 0;
|
|
XtSetArg(args[num_args], XtNfont, &text_info->fs); num_args++;
|
|
XtSetArg(args[num_args], XtNtopMargin, &top_margin); num_args++;
|
|
XtSetArg(args[num_args], XtNbottomMargin, &bottom_margin); num_args++;
|
|
XtSetArg(args[num_args], XtNleftMargin, &left_margin); num_args++;
|
|
XtSetArg(args[num_args], XtNrightMargin, &right_margin); num_args++;
|
|
XtGetValues(wp->w, args, num_args);
|
|
|
|
text_info->extra_width = left_margin + right_margin;
|
|
text_info->extra_height = top_margin + bottom_margin;
|
|
}
|
|
|
|
void
|
|
destroy_text_window(wp)
|
|
struct xwindow *wp;
|
|
{
|
|
/* Don't need to pop down, this only called from dismiss_text(). */
|
|
|
|
struct text_info_t *text_info = wp->text_information;
|
|
|
|
/*
|
|
* If the text window was blocked, then the user has already ACK'ed
|
|
* it and we are free to really destroy the window. Otherwise, don't
|
|
* destroy until the user dismisses the window via a key or button
|
|
* press.
|
|
*/
|
|
if (text_info->blocked || text_info->destroy_on_ack) {
|
|
XtDestroyWidget(wp->popup);
|
|
free_text_buffer(&text_info->text);
|
|
free((genericptr_t)text_info), wp->text_information = 0;
|
|
wp->type = NHW_NONE; /* allow reuse */
|
|
} else {
|
|
text_info->destroy_on_ack = TRUE; /* destroy on next ACK */
|
|
}
|
|
}
|
|
|
|
void
|
|
clear_text_window(wp)
|
|
struct xwindow *wp;
|
|
{
|
|
clear_text_buffer(&wp->text_information->text);
|
|
}
|
|
|
|
|
|
/* text buffer routines ---------------------------------------------------- */
|
|
|
|
/* Append a line to the text buffer. */
|
|
void
|
|
append_text_buffer(tb, str, concat)
|
|
struct text_buffer *tb;
|
|
const char *str;
|
|
boolean concat;
|
|
{
|
|
char *copy;
|
|
int length;
|
|
|
|
if (!tb->text) panic("append_text_buffer: null text buffer");
|
|
|
|
if (str) {
|
|
length = strlen(str);
|
|
} else {
|
|
length = 0;
|
|
}
|
|
|
|
if (length + tb->text_last + 1 >= tb->text_size) {
|
|
/* we need to go to a bigger buffer! */
|
|
#ifdef VERBOSE
|
|
printf("append_text_buffer: text buffer growing from %d to %d bytes\n",
|
|
tb->text_size, 2*tb->text_size);
|
|
#endif
|
|
copy = (char *) alloc((unsigned)tb->text_size*2);
|
|
(void) memcpy(copy, tb->text, tb->text_last);
|
|
free(tb->text);
|
|
tb->text = copy;
|
|
tb->text_size *= 2;
|
|
}
|
|
|
|
if (tb->num_lines) { /* not first --- append a newline */
|
|
char appchar = '\n';
|
|
|
|
if(concat && !index("!.?'\")", tb->text[tb->text_last-1])) {
|
|
appchar = ' ';
|
|
tb->num_lines--; /* offset increment at end of function */
|
|
}
|
|
|
|
*(tb->text + tb->text_last) = appchar;
|
|
tb->text_last++;
|
|
}
|
|
|
|
if (str) {
|
|
(void) memcpy((tb->text+tb->text_last), str, length+1);
|
|
if(length) {
|
|
/* Remove all newlines. Otherwise we have a confused line count. */
|
|
copy = (tb->text+tb->text_last);
|
|
while ((copy = index(copy, '\n')) != (char*)0)
|
|
*copy = ' ';
|
|
}
|
|
|
|
tb->text_last += length;
|
|
}
|
|
tb->text[tb->text_last] = '\0';
|
|
tb->num_lines++;
|
|
}
|
|
|
|
/* Initialize text buffer. */
|
|
void
|
|
init_text_buffer(tb)
|
|
struct text_buffer *tb;
|
|
{
|
|
tb->text = (char *) alloc(START_SIZE);
|
|
tb->text[0] = '\0';
|
|
tb->text_size = START_SIZE;
|
|
tb->text_last = 0;
|
|
tb->num_lines = 0;
|
|
}
|
|
|
|
/* Empty the text buffer */
|
|
void
|
|
clear_text_buffer(tb)
|
|
struct text_buffer *tb;
|
|
{
|
|
tb->text_last = 0;
|
|
tb->text[0] = '\0';
|
|
tb->num_lines = 0;
|
|
}
|
|
|
|
/* Free up allocated memory. */
|
|
void
|
|
free_text_buffer(tb)
|
|
struct text_buffer *tb;
|
|
{
|
|
free(tb->text);
|
|
tb->text = (char *) 0;
|
|
tb->text_size = 0;
|
|
tb->text_last = 0;
|
|
tb->num_lines = 0;
|
|
}
|
|
|
|
|
|
#ifdef GRAPHIC_TOMBSTONE
|
|
|
|
static void FDECL(rip_exposed, (Widget,XtPointer,XtPointer));
|
|
|
|
static XImage* rip_image=0;
|
|
|
|
|
|
#define STONE_LINE_LEN 16 /* # chars that fit on one line */
|
|
#define NAME_LINE 0 /* line # for player name */
|
|
#define GOLD_LINE 1 /* line # for amount of gold */
|
|
#define DEATH_LINE 2 /* line # for death description */
|
|
#define YEAR_LINE 6 /* line # for year */
|
|
|
|
static char rip_line[YEAR_LINE+1][STONE_LINE_LEN+1];
|
|
|
|
extern const char *killed_by_prefix[];
|
|
|
|
void
|
|
calculate_rip_text(int how)
|
|
{
|
|
/* Follows same algorithm as genl_outrip() */
|
|
|
|
char buf[BUFSZ];
|
|
char *dpx;
|
|
int line;
|
|
|
|
/* Put name on stone */
|
|
Sprintf(rip_line[NAME_LINE], "%s", plname);
|
|
|
|
/* Put $ on stone */
|
|
Sprintf(rip_line[GOLD_LINE], "%ld Au",
|
|
#ifndef GOLDOBJ
|
|
u.ugold);
|
|
#else
|
|
done_money);
|
|
#endif
|
|
/* Put together death description */
|
|
switch (killer.format) {
|
|
default: impossible("bad killer format?");
|
|
case KILLED_BY_AN:
|
|
Strcpy(buf, killed_by_prefix[how]);
|
|
Strcat(buf, an(killer.name));
|
|
break;
|
|
case KILLED_BY:
|
|
Strcpy(buf, killed_by_prefix[how]);
|
|
Strcat(buf, killer.name);
|
|
break;
|
|
case NO_KILLER_PREFIX:
|
|
Strcpy(buf, killer.name);
|
|
break;
|
|
}
|
|
|
|
/* Put death type on stone */
|
|
for (line=DEATH_LINE, dpx = buf; line<YEAR_LINE; line++) {
|
|
register int i,i0;
|
|
char tmpchar;
|
|
|
|
if ( (i0=strlen(dpx)) > STONE_LINE_LEN) {
|
|
for(i = STONE_LINE_LEN;
|
|
((i0 > STONE_LINE_LEN) && i); i--)
|
|
if(dpx[i] == ' ') i0 = i;
|
|
if(!i) i0 = STONE_LINE_LEN;
|
|
}
|
|
tmpchar = dpx[i0];
|
|
dpx[i0] = 0;
|
|
strcpy(rip_line[line], dpx);
|
|
if (tmpchar != ' ') {
|
|
dpx[i0] = tmpchar;
|
|
dpx= &dpx[i0];
|
|
} else dpx= &dpx[i0+1];
|
|
}
|
|
|
|
/* Put year on stone */
|
|
Sprintf(rip_line[YEAR_LINE], "%4d", getyear());
|
|
}
|
|
|
|
|
|
/*
|
|
* RIP image expose callback.
|
|
*/
|
|
/*ARGSUSED*/
|
|
static void
|
|
rip_exposed(w, client_data, widget_data)
|
|
Widget w;
|
|
XtPointer client_data; /* unused */
|
|
XtPointer widget_data; /* expose event from Window widget */
|
|
{
|
|
XExposeEvent *event = (XExposeEvent *) widget_data;
|
|
Display* dpy=XtDisplay(w);
|
|
Arg args[8];
|
|
XGCValues values;
|
|
XtGCMask mask;
|
|
GC gc;
|
|
static Pixmap rip_pixmap=None;
|
|
int i, x, y;
|
|
|
|
if (!XtIsRealized(w) || event->count > 0) return;
|
|
|
|
if (rip_pixmap == None && rip_image) {
|
|
rip_pixmap = XCreatePixmap(dpy, XtWindow(w),
|
|
rip_image->width,
|
|
rip_image->height,
|
|
DefaultDepth(dpy, DefaultScreen(dpy)));
|
|
XPutImage(dpy, rip_pixmap,
|
|
DefaultGC(dpy, DefaultScreen(dpy)),
|
|
rip_image,
|
|
0,0, 0,0, /* src, dest top left */
|
|
rip_image->width,
|
|
rip_image->height);
|
|
XDestroyImage(rip_image); /* data bytes free'd also */
|
|
}
|
|
|
|
mask = GCFunction | GCForeground | GCGraphicsExposures | GCFont;
|
|
values.graphics_exposures = False;
|
|
XtSetArg(args[0], XtNforeground, &values.foreground);
|
|
XtGetValues(w, args, 1);
|
|
values.function = GXcopy;
|
|
values.font = WindowFont(w);
|
|
gc = XtGetGC(w, mask, &values);
|
|
|
|
if (rip_pixmap != None) {
|
|
XCopyArea(dpy, rip_pixmap, XtWindow(w), gc,
|
|
event->x, event->y, event->width, event->height,
|
|
event->x, event->y);
|
|
}
|
|
|
|
x=appResources.tombtext_x;
|
|
y=appResources.tombtext_y;
|
|
for (i=0; i<=YEAR_LINE; i++) {
|
|
int len=strlen(rip_line[i]);
|
|
XFontStruct* font=WindowFontStruct(w);
|
|
int width=XTextWidth(font, rip_line[i], len);
|
|
XDrawString(dpy, XtWindow(w), gc,
|
|
x-width/2, y, rip_line[i], len);
|
|
x+=appResources.tombtext_dx;
|
|
y+=appResources.tombtext_dy;
|
|
}
|
|
|
|
XtReleaseGC(w, gc);
|
|
}
|
|
|
|
/*
|
|
* The ripout window creation routine.
|
|
*/
|
|
static Widget
|
|
create_ripout_widget(Widget parent)
|
|
{
|
|
Widget imageport;
|
|
Arg args[16];
|
|
Cardinal num_args;
|
|
|
|
static int rip_width, rip_height;
|
|
|
|
if (!rip_image) {
|
|
XpmAttributes attributes;
|
|
int errorcode;
|
|
|
|
attributes.valuemask = XpmCloseness;
|
|
attributes.closeness = 65535; /* Try anything */
|
|
errorcode = XpmReadFileToImage(XtDisplay(parent), appResources.tombstone, &rip_image, 0, &attributes);
|
|
if (errorcode != XpmSuccess) {
|
|
char buf[BUFSZ];
|
|
Sprintf(buf, "Failed to load %s: %s", appResources.tombstone,
|
|
XpmGetErrorString(errorcode));
|
|
X11_raw_print(buf);
|
|
}
|
|
rip_width = rip_image->width;
|
|
rip_height = rip_image->height;
|
|
}
|
|
|
|
num_args = 0;
|
|
XtSetArg(args[num_args], XtNwidth, rip_width); num_args++;
|
|
XtSetArg(args[num_args], XtNheight, rip_height); num_args++;
|
|
XtSetArg(args[num_args], XtNtranslations,
|
|
XtParseTranslationTable(rip_translations)); num_args++;
|
|
|
|
imageport = XtCreateManagedWidget("rip", windowWidgetClass,
|
|
parent, args, num_args);
|
|
|
|
XtAddCallback(imageport, XtNexposeCallback, rip_exposed, (XtPointer) 0);
|
|
|
|
return imageport;
|
|
}
|
|
|
|
#endif /* GRAPHIC_TOMBSTONE */
|
|
|
|
/*wintext.c*/
|