#include <stdio.h>
#include <X11/Xlib.h>

#include "draw.h"
#include "main.h"
#include "data.h"
#include "data_colors.h"
#include "data_chars.h"
#include "data_neighb.h"
#include "data_sprite.h"
#include "data_monsters.h"
#include "widget.h"
#include "game.h"

#include "chars.h"
#include "monsters.h"
#include "flash.xbm"
#define NBFLASH (flash_height/16)

static Pixmap chars_pixmap;
static Pixmap monsters_pixmap;
static Pixmap flash_mask[NBFLASH];
static Pixmap floor_pixmap;
static Pixmap teleport_pixmap;

static int nb_rect;
static int max_rect = 40;
static Rectangle *rectangles;

bits32 mask[NMONSTERS][YSPRITE];
/*
 * Stored LSB first, i.e., bit 0 is the leftmost on screen.
 */

void printbin (unsigned int v)
{
    int i;
    for (i=0; i<32; i++) {
	if (v & 0x80000000)
	    putchar ('X');
	else
	    putchar (' ');
	v <<= 1;
    }
}

#define MAX(x,y) (((x)>(y))?(x):(y))
#define MIN(x,y) (((x)<(y))?(x):(y))

void add_rect (int x, int y, int w, int h)
{
    int i;
    Rectangle *rec;

    if (nb_rect >= max_rect) {
	max_rect = nb_rect + 10;
	rectangles = (Rectangle *) realloc (rectangles,
					    max_rect * sizeof (Rectangle));
	if (rectangles == NULL) {
	    perror ("realloc");
	    exit (2);
	}
    }

    while (nb_rect) {					 /* merge rectangles */
	rec = rectangles + nb_rect-1;			     /* start at end */
	for (i=nb_rect-1; i>=0; i--, rec--) {
	    int rx = rec->x;
	    int ry = rec->y;
	    int rw = rec->w;
	    int rh = rec->h;
	    if (x<=rx+rw && rx<=x+w && y<=ry+rh && ry<=y+h) {
		int nx = MIN(x,rx);
		int ny = MIN(y,ry);
		int nw = MAX(x+w,rx+rw)-nx;
		int nh = MAX(y+h,ry+rh)-ny;
		x = nx;
		y = ny;
		w = nw;
		h = nh;
		if (i != nb_rect-1)
		    rectangles[i] = rectangles[nb_rect-1];
		nb_rect--;
		break;
	    }
	}
	if (i < 0) /* none found */
	    break;
    }
    rectangles[nb_rect].x = x;
    rectangles[nb_rect].y = y;
    rectangles[nb_rect].w = w; 
    rectangles[nb_rect].h = h;
    nb_rect++;
}


void flush_rects ()
{
    int i;
    Rectangle *rec = rectangles;

    for (i=0; i<nb_rect; i++) {
	Update_Zone (canvas_widget, rec->x, rec->y, rec->w, rec->h);
	rec++;
    }
    nb_rect = 0;
}





void precompute_sprites ()
{
    int i, j;
    unsigned char *ptr;

    if (sizeof (bits32) != 4) {
	fprintf (stderr, "bits32 is %d bits !\n", 8*sizeof (bits32));
	exit (2);
    }
    ptr = (unsigned char *) monsters_bits;
    for (i=0; i<NMONSTERS; i++) {
	for (j=0; j<YSPRITE; j++) {
	    mask[i][j] = (   ptr[0] 
			  | (ptr[1] << 8)
			  | (ptr[2] << 16)
			  | (ptr[3] << 24));
	    ptr += 4;
	}
    }
}

static int cpc_col[32*3] = {
    0x29<<10, 0x29<<10, 0x29<<10,				    /* white */
    0x29<<10, 0x29<<10, 0x29<<10,			  /* (same as white) */
    0x1c<<10, 0x39<<10, 0x20<<10,				 /* seegreen */
    0x3c<<10, 0x31<<10, 0x14<<10,			       /* pastelgold */
    0x00<<10, 0x00<<10, 0x2b<<10,				     /* blue */
    0x3f<<10, 0x00<<10, 0x28<<10,				   /* purpur */
    0x00<<10, 0x28<<10, 0x32<<10,				/* bluegreen */
    0x3f<<10, 0x26<<10, 0x26<<10,			    /* purple (pink) */
    0x3f<<10, 0x00<<10, 0x38<<10,			 /* (same as purpur) */
    0x3c<<10, 0x31<<10, 0x14<<10,		   /* (same as 'pastelgold') */
    0x3a<<10, 0x3a<<10, 0x00<<10,			     /* light yellow */
    0x3e<<10, 0x3e<<10, 0x3e<<10,			      /* light white */
    0x3f<<10, 0x00<<10, 0x00<<10,				/* light red */
    0x3f<<10, 0x00<<10, 0x3a<<10,			    /* light magenta */
    0x3c<<10, 0x2a<<10, 0x00<<10,				   /* orange */
    0x3b<<10, 0x1a<<10, 0x3b<<10,			    /* past. magenta */
    0x00<<10, 0x00<<10, 0x2b<<10,			   /* (same as blue) */
    0x1c<<10, 0x39<<10, 0x20<<10,		     /* (same as 'seegreen') */
    0x00<<10, 0x39<<10, 0x00<<10,			       /* lightgreen */
    0x00<<10, 0x2f<<10, 0x3f<<10,			  /* light bluegreen */
    0x00<<10, 0x00<<10, 0x00<<10,				    /* black */
    0x00<<10, 0x00<<10, 0x3c<<10,				/* lightblue */
    0x00<<10, 0x2e<<10, 0x00<<10,				    /* green */
    0x1e<<10, 0x1e<<10, 0x3e<<10,				  /* skyblue */
    0x30<<10, 0x00<<10, 0x30<<10,				  /* magenta */
    0x20<<10, 0x3d<<10, 0x21<<10,			      /* past. green */
    0x2b<<10, 0x3c<<10, 0x00<<10,			       /* lemongreen */
    0x1f<<10, 0x1a<<10, 0x3f<<10,			  /* past. bluegreen */
    0x29<<10, 0x05<<10, 0x05<<10,				      /* red */
    0x30<<10, 0x00<<10, 0x3f<<10,			   /* mauve (violet) */
    0x30<<10, 0x2b<<10, 0x00<<10,				   /* yellow */
    0x26<<10, 0x26<<10, 0x3c<<10,			       /* past. blue */
};

#define floor_width 8
#define floor_height 16
static char floor_bits[] = {
   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};

void draw_init ()
{
    static int initialized = 0;
    if (!initialized) {
	int i;
	Pixmap tmp;
	XGCValues gcv;
	GC gc;

	initialized = 1;
	chars_pixmap = XCreateBitmapFromData (display, canvas,
					      chars_bits,
					      chars_width, chars_height);
	monsters_pixmap = XCreateBitmapFromData (display, canvas,
						 monsters_bits,
						 monsters_width,
						 monsters_height);
	floor_pixmap = XCreateBitmapFromData (display, canvas,
					      floor_bits,
					      floor_width,
					      floor_height);
	teleport_pixmap = XCreatePixmap (display, canvas, 32, 32, depth);

	tmp = XCreateBitmapFromData (display, canvas,
				     flash_bits,
				     flash_width,
				     flash_height);
	gcv.graphics_exposures = 0;
	gc = XCreateGC (display, tmp, GCGraphicsExposures, &gcv);
	for (i=0; i<NBFLASH; i++) {
	    flash_mask[i] = XCreatePixmap (display, canvas, 16, 16, 1);
	    XCopyArea (display, tmp, flash_mask[i], gc,
		       0, 16*i, 16, 16, 0, 0);
	}
	XFreeGC (display, gc);
	XFreePixmap (display, tmp);

	rectangles = (Rectangle *) malloc (max_rect * sizeof (Rectangle));
	if (rectangles == NULL) {
	    perror ("malloc");
	    exit (2);
	}
	nb_rect = 0;

	precompute_sprites ();
    }
}


static void draw_char (int c,
		       int x,
		       int y)
{
    XGCValues gcv;
    int fg = char_colors[c].fg;
    int bg = char_colors[c].bg;
    
    if (!color_display && fg != 0 && bg != 0) {
	if (fg == 3)
	    bg = 0;
	else if (bg == 3)
	    fg = 0;
	else {
	    int a, f, b;
	    a = 2-fg;
	    f = cpc_col[3*a] + cpc_col[3*a+1] + cpc_col[3*a+2];
	    a = 2-bg;
	    b = cpc_col[3*a] + cpc_col[3*a+1] + cpc_col[3*a+2];
	    if (f > b)
		bg = 0;
	    else
		fg = 0;
	}
    }
    gcv.foreground = pixels[fg];
    gcv.background = pixels[bg];
    XChangeGC (display, gc_copyplane, GCForeground | GCBackground, &gcv);
    XCopyPlane (display, chars_pixmap, canvas, gc_copyplane,
		0, c*16, 16, 16, x, y, 1);
}


void xor_floor (int x, int y)
{
    XGCValues gcv;
    gcv.foreground = pixels[1] ^ pixels[0];
    gcv.background = pixels[2] ^ pixels[0];
    gcv.clip_mask = None;
    XChangeGC (display, gc_xorchar,
	       GCForeground | GCBackground | GCClipMask,
	       &gcv);
    XCopyPlane (display, floor_pixmap, canvas, gc_xorchar,
		0, 0, 8, 16, 2*x, 2*y, 1);
    add_rect (2*x, 2*y, 8, 16);
}



void xor_char (int c,
	       int x,
	       int y,
	       Pixmap mask,
	       int forcecolor)
{
    XGCValues gcv;
    if (forcecolor) {
	gcv.foreground = pixels[3] ^ pixels[0];
	gcv.background = 0;
    } else {
	gcv.foreground = pixels[char_colors[c].fg] ^ pixels[0];
	gcv.background = pixels[char_colors[c].bg] ^ pixels[0];
    }
    gcv.clip_mask = mask;
    gcv.clip_x_origin = x;
    gcv.clip_y_origin = y;
    XChangeGC (display, gc_xorchar,
	       GCForeground | GCBackground | GCClipMask
	       | GCClipXOrigin | GCClipYOrigin, &gcv);
    XCopyPlane (display, chars_pixmap, canvas, gc_xorchar,
		0, c*16, 16, 16, x, y, 1);
}

void xor_item (int val,
	       int x,
	       int y,
	       int maskno,
	       int forcecolor)
{
    xor_char (room_chars[room_no][val-1], 2*x, 2*y,
	      flash_mask[maskno%NBFLASH], forcecolor);
    add_rect (2*x, 2*y, 16, 16);
}


int make_teleport_sprite (int sprite)
{
    int x, y, i;

    XFillRectangle (display, teleport_pixmap, gc_zero, 0, 0, 32, 32);
    XCopyPlane (display, monsters_pixmap, teleport_pixmap, gc_three,
		0, sprite*32, 32, 32, 0, 0, 1);
    for (x=0; x<16; x++)
	for (y=0; y<16; y++) {
#ifdef NO_RAND48
	    extern int rand (void);
	    int a = rand () % 4;
#else
	    extern long lrand48 (void);
	    long a = lrand48 () % 4;
#endif
	    XPoint p[4];
	    p[0].x = 2*x;
	    p[0].y = 2*y;
	    p[1].x = 2*x+1;
	    p[1].y = 2*y;
	    p[2].x = 2*x;
	    p[2].y = 2*y+1;
	    p[3].x = 2*x+1;
	    p[3].y = 2*y+1;
	    XDrawPoints (display, teleport_pixmap, gc_and[a], p, 4,
			 CoordModeOrigin);
	}
    return -2;
}


void put_sprite (int sprite,
		 int x,
		 int y,
		 int c)
{
    if (sprite == -2) {
	XCopyArea (display, teleport_pixmap, canvas, gc_xorplane[3],
		    0, 0, 32, 32, x, y);
    } else {
	XCopyPlane (display, monsters_pixmap, canvas, gc_xorplane[c&3],
		    0, sprite*32, 32, 32, x, y, 1);
    }
    add_rect (x, y, 32, 32);
}

void put_sprite_noxor (int sprite,
		       int x,
		       int y,
		       int c)
{
    XCopyPlane (display, monsters_pixmap, canvas, gcs[c&3],
		0, sprite*32, 32, 32, x, y, 1);
    add_rect (x, y, 32, 32);
}


static void setink (int ink,
		    int val)
{
    XColor col;

    if (!color_display)
	return;

    col.pixel = pixels[ink & 3];
    col.flags = DoRed | DoGreen | DoBlue;
    val &= 0x1f;
    col.red = cpc_col[3*val];
    col.green = cpc_col[3*val+1];
    col.blue = cpc_col[3*val+2];
    XStoreColor (display, colormap, &col);
}


void flash_dead (int x, int y, int n)
{
    if (color_display) {
	XColor col;
	int val = 3*(room_colors[room_no][2] & 0x1f);
	int red = cpc_col[val];
	int green = cpc_col[val+1];
	int blue = cpc_col[val+2];
	int i = NB_FLASHES_COLOR - n;
	col.pixel = pixels[0];
	col.flags = DoRed | DoGreen | DoBlue;
	col.red = red+(0xffff-red)*i/(NB_FLASHES_COLOR-1);
	col.green = green+(0xffff-green)*i/(NB_FLASHES_COLOR-1);
	col.blue = blue+(0xffff-blue)*i/(NB_FLASHES_COLOR-1);
	XStoreColor (display, colormap, &col);
	XFlush (display);
    } else {
	int i = 5*(NB_FLASHES_MONO - n);
	XDrawRectangle (display, XtWindow (canvas_widget),
			gc_xorplane[3],
			x+16-i, y+16-i, 2*i, 2*i);
    }
}

void draw_automove (int x,
		    int y,
		    int len,
		    int dir,
		    int offset)
{
    int of = (dir == -1) ? offset : (4-offset);
    XDrawLine (display, canvas, gc_dash[of&3],
	       x, y, x+len-1, y);
    XDrawLine (display, canvas, gc_dash[of&3],
	       x, y+1, x+len-1, y+1);
    XDrawLine (display, XtWindow (canvas_widget), gc_dash[of&3],
	       x, y, x+len-1, y);
    XDrawLine (display, XtWindow (canvas_widget), gc_dash[of&3],
	       x, y+1, x+len-1, y+1);
    /*add_rect (x, y, len, 2);*/
}


void draw_liane_pixels (XPoint *points, int n)
{
    
    XDrawPoints (display, canvas, gc_xorplane[3],
		 points, n, CoordModeOrigin);
    XDrawPoints (display, XtWindow (canvas_widget), gc_xorplane[3],
		 points, n, CoordModeOrigin);
}

void draw_thread (int x, int y, int cnt)
{
    if (cnt) {
	XSegment seg[2];

	seg[0].x1 = 4*x+16;
	seg[0].y1 = 2*y;
	seg[0].x2 = 4*x+16;
	seg[0].y2 = 2*(y+cnt)-1;
	seg[1].x1 = 4*x+17;
	seg[1].y1 = 2*y;
	seg[1].x2 = 4*x+17;
	seg[1].y2 = 2*(y+cnt)-1;
	XDrawSegments (display, canvas, gc_xorplane[1],
		       seg, 2);
	add_rect (seg[0].x1, seg[0].y1, 2, 2*cnt);
	/*XDrawSegments (display, XtWindow (canvas_widget), gc_xorplane[1],
		       seg, 2);*/
    }
}




void draw_room ()
{
    int i, j;
    XGCValues gcv;

    gcv.foreground = pixels[0];
    XChangeGC (display, gc_copyplane, GCForeground | GCBackground, &gcv);
    XFillRectangle (display, canvas, gc_copyplane, 0, 0, 512, 256);
    for (j=0; j<YROOM; j++) {
	int y;
	y = j*16;
	for (i=0; i<XROOM; i++) {
	    int x;
	    int val;
	    x = i*16;
	    val = room_data[j][i];
	    if (val != I_NOTHING && val != I_ITEM) {
		draw_char (room_chars[room_no][val-1], x, y);
	    }
	}
    }

    XFlush (display);
    Update_Screen (canvas_widget);
    setink (3, 0x0b);
    for (i=0; i<3; i++)
	setink (2-i, room_colors[room_no][i]);
    XtVaSetValues (roomname_widget,
                   XtNlabel, (XtArgVal) room_name[room_no],
                   NULL);
    XFlush (display);
}


void show_nb_items_taken ()
{
    char buf[10];
    extern int nb_items_taken;

    if (discovery_mode)
	*buf='\0';
    else
	sprintf (buf, "%d", nb_items_taken);
    XtVaSetValues (itemstaken_widget,
                   XtNlabel, (XtArgVal) buf,
                   NULL);
}

void show_nb_lives ()
{
    char buf[10];
    extern int nb_lives;

    if (discovery_mode)
	*buf='\0';
    else
	sprintf (buf, "%d", nb_lives);
    XtVaSetValues (lives_widget,
                   XtNlabel, (XtArgVal) buf,
                   NULL);
}

void show_tripswitch (int state)
{
    char *s;

    switch (state) {
    case 0: s = " "; break;
    case 1: s = "Trip Switch On"; break;
    case 2: s = "Trip Switch Off"; break;
    }
    XtVaSetValues (tripswitch_widget,
                   XtNlabel, (XtArgVal) s,
                   NULL);
}


#if 0
void PutPlayer (w, event, params, num_params)
    MyWidget w;
    XEvent *event;
    String *params;
    Cardinal *num_params;
{
    extern void newpos ();
    extern int debugging;

    if (debugging)
	newpos (event->xbutton.x/2, event->xbutton.y/2);
}
#endif



int sprites_collide (int spr1,
		     int x1,
		     int y1,
		     int spr2,
		     int x2,
		     int y2)
{
    int yb1, yb2, ny;
    int shift;
    int i;
    bits32 *p1, *p2;

    if (   x1>=x2+XSPRITE || x2>=x1+XSPRITE
	|| y1>=y2+YSPRITE || y2>=y1+YSPRITE
	|| spr1 == -1 || spr2 == -1)
	return 0;
    if (x1>x2) {
	int t;
	t = x1; x1 = x2; x2 = t;
	t = y1; y1 = y2; y2 = t;
	t = spr1; spr1 = spr2; spr2 = t;
    }
    shift = x2-x1;
    if (y1>y2) {
	yb1 = 0;
	yb2 = y1-y2;
	ny = YSPRITE-yb2;
    } else {
	yb2 = 0;
	yb1 = y2-y1;
	ny = YSPRITE-yb1;
    }
    p1 = mask[spr1] + yb1;
    p2 = mask[spr2] + yb2;
    if (shift == 0) {
	for (i=ny; i>0; i--) {
	    if ((*p1++) & (*p2++))
		return 1;
	}
    } else {
	for (i=ny; i>0; i--) {
	    if (((*p1++) >> shift) & (*p2++))		    /* LSB first !!! */
		return 1;
	}
    }
    return 0;
}

int pixel_collide (int spr, int xs, int ys, int x, int y)
{
    int shift;
    bits32 v;

    if (   x<xs || x>=xs+XSPRITE
	|| y<ys || y>=ys+YSPRITE
	|| spr == -1)
	return 0;
    v = mask[spr][y-ys];
    shift = x-xs;
    if (shift == 0)
	return (v & 3);
    else
	return ((v >> shift) & 3);
}

void draw_rescue (int cnt)
{
    static char buf[20];

    sprintf (buf, "%4d To Rescue   ", cnt);
    XDrawImageString (display, canvas,
		      gc_string, 180, 32, buf, strlen (buf));
    XDrawImageString (display, XtWindow (canvas_widget),
		      gc_string, 180, 32, buf, strlen (buf));
}



void move_boat ()
{
    XCopyArea (display, canvas, canvas,
	       gc_black,
	       8, 80, 8*0x2a, 2*0x50, 0, 80);
    XCopyArea (display, XtWindow (canvas_widget), XtWindow (canvas_widget),
	       gc_black,
	       8, 80, 8*0x2a, 2*0x50, 0, 80);
    XFlush (display);
}

void move_rocket ()
{
    XCopyArea (display, canvas, canvas,
	       gc_black,
	       160, 2, 192, 254, 160, 0);
    XCopyArea (display, XtWindow (canvas_widget), XtWindow (canvas_widget),
	       gc_black,
	       160, 2, 192, 254, 160, 0);
    XFlush (display);
}

void move_tree ()
{
    XCopyArea (display, canvas, canvas,
	       gc_black,
	       8*0x12, 44, 8*0x18, 2*0x49, 8*0x12, 46);
    XCopyArea (display, XtWindow (canvas_widget), XtWindow (canvas_widget),
	       gc_black,
	       8*0x12, 44, 8*0x18, 2*0x49, 8*0x12, 46);
    XFlush (display);
}

void clear_tree ()
{
    XFillRectangle (display, canvas, gcs[0], 8*0x12, 44, 8*0x18, 2+2*0x49);
    Update_Screen (canvas_widget);
    XFlush (display);
}

void show_time (int cnt)
{
    static char buf[20];
    extern int time_ascent;

    if (discovery_mode) {
	extern GC gc_time_white;
	extern int time_widget_w, time_widget_h;

	XFillRectangle (display, time_widget->my.store, gc_time_white,
			0, 0, time_widget_w, time_widget_h);
	XFillRectangle (display, XtWindow (time_widget), gc_time_white,
			0, 0, time_widget_w, time_widget_h);
    } else {
	sprintf (buf, "%02d:%02d:%02d", cnt/3600, (cnt/60)%60, cnt%60);
	XDrawImageString (display, time_widget->my.store,
			  gc_time, 5, time_ascent, buf, strlen (buf));
	XDrawImageString (display, XtWindow (time_widget),
			  gc_time, 5, time_ascent, buf, strlen (buf));
    }
}





unsigned char dr_jetset[] = { /* a600 */
    0x54, 0x56, 0x35, 0x25, 0x25, 0x25, 0x13, 0x91,
    0x90, 0x99, 0x0b, 0x0d, 0x1d, 0x2a, 0x39, 0x31,
    0x42, 0x45, 0x92, 0xa0, 0xba, 0x9a, 0x9c, 0x2c,
    0x2a, 0x29, 0x31, 0x32, 0x22, 0x23, 0x27, 0x27,
    0x03, 0x9b, 0x9a, 0x0f, 0x0d, 0x1b, 0x2d, 0x3d,
    0x3f, 0x37, 0x04, 0x76, 0x0b, 0x3a, 0x4c, 0x9a,
    0x0a, 0x21, 0x13, 0x29, 0x02, 0x10, 0x2a, 0x3a,
    0x71, 0x11, 0x37, 0x04, 0x92, 0xa2, 0xc3, 0xb4,
    0x01, 0x12, 0x67, 0x30, 0x29, 0x1b, 0x1c, 0x0b,
    0x90, 0x92, 0x94, 0x11, 0x19, 0x2f, 0x3b, 0x1a,
    0x59, 0x73, 0x11, 0x24, 0x01, 0x91, 0xa9, 0xaa,
    0xba, 0xac, 0x0f, 0x1a, 0x19, 0x30, 0x40, 0x62,
    0x22, 0x17, 0x17, 0x17, 0x12, 0x9f, 0xaf, 0xaf,
    0x0c, 0x39, 0x30, 0x72, 0x52, 0x7a, 0x39, 0x2a,
    0xa0, 0x99, 0xf9, 0xc9, 0xda, 0xca, 0xbb, 0xfa,
    0xdb, 0xa0, 0xd0, 0xda, 0xba, 0xb1, 0x90, 0xdb,
    0xa0, 0xba, 0x0a, 0x39, 0x32, 0x11, 0x04, 0x91,
    0x99, 0x0d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
    0x19, 0x10, 0x11, 0x55, 0x45, 0x15, 0x15, 0x15,
    0x02, 0xaf, 0x9d, 0x0d, 0x0d, 0x1a, 0x30, 0x22,
    0x35, 0x25, 0x25, 0x02, 0x91, 0x99, 0x0a, 0x2b,
    0x19, 0x20, 0x21, 0x23, 0x9e, 0x0c, 0x2b, 0x20,
    0x21, 0x37, 0x27, 0x17, 0x14, 0xaf, 0x0f, 0x0f,
    0x0a, 0x19, 0x29, 0x31, 0x47, 0x27, 0x27, 0x04,
    0xaf, 0x9e, 0x0f, 0x1c, 0x39, 0x41, 0x12, 0x07,
    0x14, 0x44, 0xdf, 0x9f, 0x1a, 0x2a, 0x40, 0x77,
    0x27, 0x15, 0xaf, 0xaf, 0xaf, 0xcf, 0xba, 0xa9,
    0xa2, 0x23, 0x72, 0x41, 0x21, 0x20, 0x9b, 0x2c,
    0x0f, 0xff, 0xaf, 0x43, 0x30, 0x4b, 0x00,
};

unsigned char dr_dot[] = { /* a6e7 */
    0x20, 0x19, 0x0a, 0x99, 0xa0, 0x91, 0x02, 0x11, 0x00,
};

unsigned char dr_line[] = { /* a6f0 */
    0x71, 0x71, 0x71, 0x00,
};

#define XSCR 512
#define YSCR 256

static void draw_rel (int x, int y, unsigned char *array)
{
    unsigned char c;
    while (c = *array++) {
	int dx, dy;
	dx = (c >> 4) & 0xf;
	if (dx & 8)
	    dx = -(dx & 7);
	dy = c & 0xf;
	if (dy & 8)
	    dy = -(dy & 7);
	dx *= 2;
	dy *= 2;
	XDrawLine (display, canvas, gc_white, x, 400-y, x+dx, 400-(y+dy));
	x += dx;
	y += dy;
    }
}



static void draw_present ()
{
    draw_rel (0x064, 0x12c, dr_jetset);
    draw_rel (0x08c, 0x186, dr_dot);
    draw_rel (0x14a, 0x113, dr_dot);
    draw_rel (0x0b4, 0x163, dr_line);
    draw_rel (0x154, 0x163, dr_line);
}



static int scroll_pos;
static char scroll_mess[] = " \
JET SET WILLY  -  The Final Frontier  .  .  .  .  .  .  .  .  .  .  .  .  .  \
version 1.0.1, by <Florent.Guillaume@ens.fr>  .  .  Linux port by <jmd@dcs.ed.ac.uk>  .  .  .  .  \
Control keys are:   \
LEFT=,/z/o/j/leftarrow    \
RIGHT=./x/p/l/rightarrow    \
JUMP=k/space/uparrow  \
.  .  .  .  .  .  \
Right!  .  .  .  .  .  .  \
This is the adventure of MINER WILLY retold.  \
Strange things did happen to Willy when he explored his huge mansion.  \
No expense has been spared in allowing you to recreate the \
magical escapade of Willy on his travels.  All you need is stamina and guts \
to overcome all the seemingly impossible odds  .  .  .  \
GOOD LUCK!  \
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  \
Watch out for the Yacht!  .  .  .  .  .  .  .  \
Beware of the Sewers!  .  .  .  .  .  .  .  \
Willy in Space?  .  .  .  .  .  .  .  \
Willy or won't he?  .  .  .  .  .  .  .  \
Boldly go where no Willy has gone before!  .  .  .  .  .  .  \
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  \
Original Amstrad version copyright 1985 Software Projects, \
programming by D.P.Rowson & S.Wetherill, \
from an idea by Matthew-JET-SET-Smith  \ 
.   .   .    .    .     .      .         .           .              . \
                      \
  \0\0";

extern int scroll_ascent;
extern int scroll_w;
extern int scroll_h;

void do_scroll ()
{
    static Pixmap tmp_pix = 0;

    if (tmp_pix == 0) {
	tmp_pix = XCreatePixmap (display, canvas, 2*scroll_w, scroll_h, depth);
    }
#define SPD 2
    XCopyArea (display, canvas, canvas, gc_black, SPD, YSCR-scroll_h,
	       XSCR-SPD, scroll_h, 0, YSCR-scroll_h);
    scroll_pos += SPD;
    if ((scroll_pos%scroll_w) < SPD) {
	if (scroll_mess[scroll_pos/scroll_w] == '\0')
	    scroll_pos = 0;
	XDrawImageString (display, tmp_pix, gc_scroll,
			  0, scroll_ascent, scroll_mess+(scroll_pos/scroll_w), 2);
    }
    XCopyArea (display, tmp_pix, canvas, gc_black,
	       scroll_pos%scroll_w, 0, SPD, scroll_h, XSCR-SPD, YSCR-scroll_h);
    Update_Zone (canvas_widget, 0, YSCR-scroll_h, XSCR, scroll_h);
    XFlush (display);
}


void present ()
{
    enable_colormap (0);
    XFillRectangle (display, canvas, gc_black, 0, 0, XSCR, YSCR);
    draw_present ();
    Update_Screen (canvas_widget);
    XtVaSetValues (roomname_widget,
                   XtNlabel, (XtArgVal) "",
                   NULL);
    scroll_pos = -SPD;
}


static int colormap_enabled = 0;
static int colormap_wanted = 0;

static void switch_colormap (int on)
{
    extern Widget toplevel;
    extern int private_colormap;

    if (private_colormap) {
	Window wins[2];

	wins[on?0:1] = XtWindow (canvas_widget);
	wins[on?1:0] = XtWindow (toplevel);
	XSetWMColormapWindows (display, XtWindow (toplevel), wins, 2);
    }
}

void set_colormaps (int on)
{
    if (on && !colormap_wanted && colormap_enabled)
	switch_colormap (1);
    else if (!on && colormap_wanted && colormap_enabled)
	switch_colormap (0);
    colormap_wanted = on;
}

void enable_colormap (int enable)
{
    if (enable && !colormap_enabled && colormap_wanted) {
	set_colormaps (1);
    } else if (!enable && colormap_enabled && colormap_wanted) {
	set_colormaps (0);
    }
    colormap_enabled = enable;
}







int errorhandler (Display *disp, XErrorEvent *err)
{
    extern void emergency(int);
    char msg[100];
    XGetErrorText (disp, err->error_code, msg, 98);
    strcat(msg, "\n");
    write (2, msg, strlen(msg));
    emergency (0);
}

int ioerrorhandler (Display *disp)
{
    extern void emergency(int);
    emergency (0);
}

