/**************************************************************************/
/*																		  */
/*							VIERFREI.C									  */
/*																		  */
/*						Version vom 21.12.93							  */
/*																		  */
/**************************************************************************/


/*------------------------------------------------------------------------*/
/*																		  */
/*		Liste der zu '#include'nden Files								  */
/*																		  */
/*------------------------------------------------------------------------*/


#include <stdio.h>
#include <stdlib.h>
#include <aes.h>
#include <vdi.h>
#include <tos.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#include "VIERFREI.H"				/* Headerfile fr Resource			  */


/*------------------------------------------------------------------------*/
/*																		  */
/*		einige '#define's												  */
/*																		  */
/*------------------------------------------------------------------------*/


#define	N			52				/* Anzahl der Karten				  */
#define NUM_COLUMNS	8				/* Anzahl der Reihen				  */

#define CARD_WIDTH	64				/* Breite einer Karte (in Pixeln)	  */
#define	CARD_LENGTH	93				/* L„nge einer Karte (in Pixeln)	  */

#define CARD_SIZE	186L			/* fr Bild einer Karte ben”tigter	  */
									/* Speicher (in Byte)				  */
#define	CARD_X_SPAC	10				/* horizontaler Abstand der Karten	  */
#define	CARD_Y_SPAC	15				/* vertikaler Abstand der Karten	  */

#define	TOP_ROW		102				/* y-Position der Reihen (relativ zur */
									/* Arbeitsfl„che des Fensters)		  */
#define	NO_CARD		-1				/* 'Kartennummer' fr Stellen, wo	  */
									/* keine (!) Karte liegt			  */
#define	HIST_LEN	250L			/* Anzahl der Zge, fr die jeweils	  */
									/* Speicher alloziert wird (zwecks	  */
									/* Rcknahme ...)					  */
#define	ON			1
#define	OFF			0

#define	BLACK		1
#define	WHITE		0

#define	NORM		1
#define	INVERS		0

#define	F1			59				/* Scancodes verschiedener Tasten	  */
#define	F2			60
#define	F3			61
#define F4			62
#define	F10			68
#define	UNDO		97
#define	DELETE		83

#define ESC			0x1B			/* ASCII-Codes der Zeichen			  */
#define BACKSPACE	0x08
#define RETURN		0x0D			/* <RETURN> und <ENTER>				  */

#define S_TEXT	ob_spec.tedinfo->te_ptext		/* fr Text-Objekte		  */

#define MIN(x,y)	(((x) < (y)) ? (x) : (y))	/* Minimum zweier Zahlen  */
#define MAX(x,y)	(((x) > (y)) ? (x) : (y))	/* Maximum zweier Zahlen  */

#define	_hz_200		0x04BAL			/* Adresse des Z„hler des 200 Hz -	  */
									/* Interrupts						  */


/*------------------------------------------------------------------------*/
/*																		  */
/*		Funktionsdeklarationen											  */
/*																		  */
/*------------------------------------------------------------------------*/


int init_history(void);
int extend_history(void);
int shorten_history(void);
void take_move_back(void);
void save_move(int source, int dest);
int show_box(int ob_no);
int on_mouse_event(int mox, int moy, int mbutton);
int find_card(int x, int y);
int is_touch_possible(int card_no);
int get_move_length(int card_no);
int find_where_to(int x, int y);
int is_enough_room(int cards_to_move, int dest);
int get_max_no(int place, int temp);
int is_move_possible(int source, int dest);
void wait_for_left_release(void);
void move_card(int source, int dest, int mbox_flag);
void take_away(int source);
void move_multi_cards(int source, int dest);
void move_temp_to_column(int *free_temp, int *act_temp, int max_temp,
																  int dest);
int move_single_card(int source_column, int final_card, int *free_place,
														int act_free_place);
int move_column_to_temp(int source_column, int final_card, int *free_temp,
														int *act_free_temp);
void move_multi_to_temp(int card_no);
void show_card(int card_no, int clip_flag);
void shuffle(void);
double make_seed(void);
double random_generator(void);
void show_all(int all_flag);
void show_column(int col_no, int card_x, int clip_flag);
int automatic_remove(void);
void set_wind_header(char *title);
void paint_background(void);
void clear_column(int col, int clip_flag);
int gem_init(char *title);
void gem_exit(void);
int make_cards(void);
void reverse_card(long *source, long *dest);
int select_game(char *title);
void wind_redraw(int *rr, OBJECT *wind);
int intersect(int *rr, int *ar);
void move_window(int x, int y, OBJECT *wind);
int resize_window(char *title);
void i_to_a(char *s, long number, int n);


/*------------------------------------------------------------------------*/
/*																		  */
/*		Deklaration einiger struct's									  */
/*																		  */
/*------------------------------------------------------------------------*/


struct Card								/* Struktur fr Karten (und auch  */
{										/* fr End- und Zwischenablage)	  */
	int x;								/* x- und y- Position der Karte	  */
	int y;
	int h;								/* sichtbare H”he der Karte		  */
	int column;							/* Spalte oder Ablage, auf der	  */
										/* die Karte liegt				  */
	int prev_card;						/* Nummer der vorhergehenden und  */
	int next_card;						/* der n„chsten Karte			  */
};										

/* Achtung : Bei den Card-Strukturen fr End- und Zwischenablage enth„lt  */
/* 			 prev_card die Nummer der darauf liegenden (obersten) Karte ! */

struct Column							/* Struktur fr Reihen			  */
{
	int first_card;						/* Nummer der obersten Karte	  */
	int x;								/* x-Position der Reihe			  */
	int h;								/* L„nge der Reihe (in Pixeln)	  */
	int	del_y;							/* L„nge der gezeigten Teils von  */
};										/* Karten, die berlagert sind	  */
		

/*------------------------------------------------------------------------*/
/*																		  */
/*		globale Variable												  */
/*																		  */
/*------------------------------------------------------------------------*/


int ap_id;							/* ID der eigenen Applikation		  */
int wkst_handle;					/* Handle der Workstation			  */
int wind_handle;					/* Handle des Fensters				  */
int nplanes;						/* Anzahl der Bitplanes				  */
int wox, woy, wow, woh,				/* Aužen- und Innenmaže des Fensters  */
	wwx, wwy, www, wwh;
OBJECT *menu;						/* Adresse des Menues				  */
long *pic;							/* Adresse der Bilder der Karten	  */
double seed;						/* akt. Seed des Zufallgenerators	  */
double old_seed;					/* Startseed des Zufallgenerators	  */
struct Card card[N + 8];			/* Strukturen fr Karten sowie End-	  */
									/* und Zwischenablage (Endablage ab	  */
									/* N bis (N + 3), Zwischenablage ab	  */
									/* (N + 4) bis (N + 7)				  */
struct Column column[NUM_COLUMNS];	/* Strukturen fr die Reihen		  */
char *history;						/* Pointer auf Array fr Speicherung  */
									/* der bisherigen Zge				  */
int no_history = OFF;				/* gesetzt, wenn nicht die Zge nicht */
									/* gepeichert werden k”nnen			  */
long move_no;						/* Anzahl der bisherigen Zge		  */
int remaining_cards;				/* Anzahl der restlichen Karten		  */
int scr_x, scr_y;					/* maximale Bildschirmgr”že			  */
int clip_array[4];					/* Array fr Cliping				  */


/*------------------------------------------------------------------------*/
/*																		  */
/*		Hier f„ngt das eigentliche Programm (endlich) an ...			  */
/*																		  */
/*------------------------------------------------------------------------*/


main()
{
	int mwhich,						/* Variablen fr evnt_multi()		  */
		mbuf[8],
		mox, moy,
		mbutton, mokstate,
		mkreturn, mbreturn;
	int top_window;					/* Handle des obersten Fensters		  */
	char wind_name[] = " VIERFREI #       ";	/* Titel des Fensters	  */
	int quit_flag = OFF;			/* Flag, bei Programmende gesetzt	  */
	
	
	/* Applikation anmelden, Workstation anmelden und Fenster ”ffnen	  */

	if (!gem_init(wind_name))
		return(0);
	
	/* Resource-File einlesen und Objektadressen bestimmen				  */

	if (!rsrc_load("VIERFREI.RSC"))
	{
		wind_delete(wind_handle);
		gem_exit();
		return(0);
	}

	rsrc_gaddr(R_TREE, MENU, &menu);

	/* Menue-Zeile anzeigen												  */

	menu_bar(menu, ON);

	/* Bilder der Karten vorbereiten									  */

	if (!make_cards())
	{
		menu_bar(menu, OFF);
		rsrc_free();
		wind_delete(wind_handle);
		gem_exit();
		return(0);
	}

	/* Erstes Spiel vorbereiten											  */

	if (!init_history())
		menu_ienable(menu, T1E4, OFF);
	old_seed = seed = make_seed();
	set_wind_header(wind_name);
	shuffle();

	graf_mouse(ARROW, 0);

	/* In einer Loop auf Tastatur-, Maus- oder Message-Ereignisse warten  */
	/* und diesen entsprechend verzweigen ...							  */

	do
	{
		mwhich = evnt_multi(MU_KEYBD | MU_BUTTON | MU_MESAG,
							1, 1, 1,
							0, 0, 0, 0, 0,
							0, 0, 0, 0, 0,
							mbuf, 0, 0, &mox, &moy, &mbutton,
							&mokstate, &mkreturn, &mbreturn);

	/* Reaktion auf Messages ...										  */

		if (mwhich & MU_MESAG)
		{
			switch (mbuf[0])
			{
				case WM_CLOSED :				  /* Close-Box angeklickt */
					quit_flag = ON;
					break;

				case WM_REDRAW :				   /* REDRAW-Aufforderung */
					wind_redraw(mbuf + 4, (OBJECT *) 0L);
					break;

				case WM_TOPPED :		/* Fenster wurde oberstes Fenster */
					wind_set(wind_handle, WF_TOP);
					break;

				case WM_MOVED :				  /* Fenster wurde verschoben */
					move_window(mbuf[4], mbuf[5], (OBJECT *) 0L);
					break;

				case WM_FULLED :				   /* FULL-Box angeklickt */
					if (resize_window(wind_name))
						quit_flag = ON;
					break;
			}
		}

	/* Bei Tastatureingabe Anklicken von Menueeintr„gen simulieren ...	  */

		if (mwhich & MU_KEYBD)
		{
			switch(mkreturn >> 8)
			{
				case F1 :					  /* neues (zuf„lliges) Spiel */
					mwhich |= MU_MESAG;
					mbuf[0] = MN_SELECTED;
					mbuf[3] = T1;
					mbuf[4] = T1E0;
					menu_tnormal(menu, T1, INVERS);
					break;

				case F2 :			   /* neues Spiel (entsprechend Zahl) */
					if (menu[T1E1].ob_state & DISABLED)
						break;
					mwhich |= MU_MESAG;
					mbuf[0] = MN_SELECTED;
					mbuf[3] = T1;
					mbuf[4] = T1E1;
					menu_tnormal(menu, T1, INVERS);
					break;

				case F3 :					   /* altes Spiel wiederholen */
					mwhich |= MU_MESAG;
					mbuf[0] = MN_SELECTED;
					mbuf[3] = T1;
					mbuf[4] = T1E2;
					menu_tnormal(menu, T1, INVERS);
					break;

				case F4 :								 /* šber VIERFREI */
					mwhich |= MU_MESAG;
					mbuf[0] = MN_SELECTED;
					mbuf[3] = T0;
					mbuf[4] = T0E0;
					menu_tnormal(menu, T0, INVERS);
					break;

				case F10 :								 /* Spiel beenden */
					mwhich |= MU_MESAG;
					mbuf[0] = MN_SELECTED;
					mbuf[3] = T1;
					mbuf[4] = T1E5;
					menu_tnormal(menu, T1, INVERS);
					break;

				case DELETE :				   /* automatisches Entfernen */
					mwhich |= MU_MESAG;
					mbuf[0] = MN_SELECTED;
					mbuf[3] = T1;
					mbuf[4] = T1E3;
					menu_tnormal(menu, T1, INVERS);
					break;

				case UNDO :					  /* letzten Zug zurcknehmen */
					if (no_history)
						break;
					mwhich |= MU_MESAG;
					mbuf[0] = MN_SELECTED;
					mbuf[3] = T1;
					mbuf[4] = T1E4;
					menu_tnormal(menu, T1, INVERS);
					break;
			}
		}

	/* Handle des derzeitig obersten Fensters bestimmen - ist es nicht	  */
	/* das eigene wird auf die folgenden Events *nicht* reagiert		  */

		wind_get(wind_handle, WF_TOP, &top_window);

		if (wind_handle != top_window)
		{
			if ((mwhich & MU_MESAG) && (mbuf[0] == MN_SELECTED))
				menu_tnormal(menu, mbuf[3], NORM);
			continue;
		}

	/* Reaktion auf Anklicken eines Menu-Eintrags						  */

		if ((mwhich & MU_MESAG) && (mbuf[0] == MN_SELECTED))
		{
			switch (mbuf[4])
			{
				case T0E0 :							 /* šber VIERFREI ... */
					if (show_box(ABOUT))
						quit_flag = ON;
					break;

				case T1E0 :					  /* neues (zuf„lliges) Spiel */
					old_seed = seed;
					set_wind_header(wind_name);
					shuffle();
					show_all(ON);
					break;

				case T1E1 :			   /* neues Spiel (entsprechend Zahl) */
					if (!select_game(wind_name))
						break;
					shuffle();
					show_all(ON);
					break;

				case T1E2 :					   /* altes Spiel wiederholen */
					seed = old_seed;
					shuffle();
					show_all(ON);
					break;

				case T1E3 :					   /* automatisches Entfernen */
					if (automatic_remove())
					{
						if (show_box(GRAT))
							quit_flag = ON;
					}
					break;

				case T1E4 :					  /* letzten Zug zurcknehmem */
					take_move_back();
					break;

				case T1E5 :								 /* Spiel beenden */
					quit_flag = ON;
					break;
			}

			menu_tnormal(menu, mbuf[3], NORM);
		}

	/* Reaktion auf Drcken der linken Maustaste						  */

		if (mwhich & MU_BUTTON)
		{
			if (on_mouse_event(mox, moy, mbutton))
			{
				if (show_box(GRAT))
					quit_flag = ON;
			}
		}

	} while (!quit_flag);

	/* Speicherplatz fr Speicherung der Zge und die Bilder der Karten	  */
	/* freigeben														  */

	if (history)
	{
		while (!shorten_history())
			;
		Mfree(history);
	}

	Mfree(pic);

	/* Menue-Zeile entfernen und Speicher fr Resource freigeben		  */

	menu_bar(menu, OFF);
	rsrc_free();

	/* Fenster und Workstation schliežen								  */

	wind_delete(wind_handle);
	gem_exit();

	/* das war's ...													  */

	return(0);
}


/*------------------------------------------------------------------------*/
/* init_history()														  */
/*	Alloziert Speicher fr Speicherung der Zge							  */
/* <-  1 = alles ok, 0 : kein Speicher frei								  */
/*------------------------------------------------------------------------*/


int init_history(void)
{
	char **ptr;


	/* Platz fr Speichern der Zge allozieren - gespeichert wird jeweils */
	/* nur die Nummer der Ausgangs- und Zielreihe (bzw. des End- oder	  */
	/* Zwischenablageplatzes). Die letzten Byte in 'history' dienen zur	  */
	/* m”glichen Erweiterung des Speicherplatzes, wenn mehr als HIST_LEN  */
	/* Spielzge stattgefunden haben - sie werden zun„chst auf NULL ge-	  */
	/* setzt, so daž klar ist, daž noch kein weiterer Speicher alloziert  */
	/* ist.																  */

	if ((history = (char *) Malloc(2L * HIST_LEN * sizeof(char) + 
												   sizeof(char *))) == NULL)
	{
		menu_ienable(menu, T1E4, OFF);
		no_history = ON;
		return(0);
	}

	ptr = (char **) (history + 2L * HIST_LEN);
	*ptr = (char *) 0L;

	return(1);
}


/*------------------------------------------------------------------------*/
/* extend_history()														  */
/*	Alloziert zus„tzlichen Speicher fr Speicherung der Zge				  */
/* <-  1 = alles ok, 0 = kein weiterer Speicher mehr frei				  */
/*------------------------------------------------------------------------*/


int extend_history(void)
{
	char *ptr, **old_ptr;


	/* Zur Erweiterung des Platzes fr die Speicherung der bisherigen	  */
	/* Spielzge wird weiterer Speicherplatz alloziert, dessen Adresse	  */
	/* in die letzten Byte des vorher allozierten Speicherplatzes ge-	  */
	/* schrieben wird - die letzten Byte des neu allozierten Speichers	  */
	/* werden zun„chst auf NULL gesetzt, so daž klar ist, daž dies der	  */
	/* zuletzt allozierte Speicherplatz ist.							  */
	
	old_ptr = (char **) (history + 2L * HIST_LEN);
	ptr = (char *) *old_ptr;

	while (ptr != (char *) 0L)
	{
		old_ptr = (char **) (ptr + 2L * HIST_LEN);
		ptr = (char *) *old_ptr;
	};

	if ((*old_ptr = (char *) Malloc(2L * HIST_LEN *
									sizeof(char) + sizeof(char *))) == NULL)
	{
		menu_ienable(menu, T1E4, OFF);
		no_history = ON;
		while (!shorten_history())
			;
		return(0);
	}
	else
	{
		old_ptr = (char **) ((char *) *old_ptr + 2L * HIST_LEN);
		*old_ptr = (char *) 0L;
		return(1);
	}
}


/*------------------------------------------------------------------------*/
/* shorten_history()													  */
/*	Gibt das letzte zus„tzlich fr die Speicherung der Zge allozierte	  */
/*	Speicherstck wieder frei.											  */
/* <-  1 = aller zus„tzlicher Speicher ist bereits freigegeben, 0 = es	  */
/*	   gibt noch weitere allozierte Speicherbereiche					  */
/*------------------------------------------------------------------------*/


int shorten_history(void)
{
	char *ptr, *temp, **old_ptr;


	temp =  history;
	ptr = *((char **) (history + 2L * HIST_LEN));

	/* Falls die letzten Byte des zu Anfang allozierten Speicherbereichs  */
	/* NULL sind gibt es keine weiteren freizugebenden Speicherbereiche	  */

	if (ptr == (char *) 0L)
		return(1);

	/* Sonst muž man sich bis zum am letzten allozierten Speicherbereich  */
	/* durchhangeln (daran erkennbar, daž seine letzten Byte NULL sind)	  */

	do
	{
		old_ptr = (char **) (temp + 2L * HIST_LEN);
		temp = ptr;
		ptr = *((char **) (ptr + 2L * HIST_LEN));
	} while (ptr != (char *) 0L);

	/* Diesen Bereich freigeben und die letzten Byte des zuvor allozier-  */
	/* ten Speicherbereich auf NULL setzen								  */

	Mfree(temp);

	*old_ptr = (char *) 0L;

	return(0);
}


/*------------------------------------------------------------------------*/
/* save_move()															  */
/*	Speichert einen Zug ab												  */
/* ->  Nummer der Ausgangs- und der Zielreihe							  */
/*------------------------------------------------------------------------*/


void save_move(int source, int dest)
{
	char *ptr;
	long temp_move_no;


	if (no_history)
		return;

	/* Zuerst einmal durch die History durchhangeln (s.o.) ...			  */

	temp_move_no = move_no;

	ptr = history;

	while (temp_move_no >= HIST_LEN)
	{
		ptr = *((char **) (ptr + 2L * HIST_LEN));
		temp_move_no -= HIST_LEN;
	}

	/* Nun die Zge abspeichern ...										  */

	*(ptr + 2L * temp_move_no) = (char) source;
	*(ptr + 2L * temp_move_no + 1) = (char) dest;

	move_no++;

	/* Wenn die History voll ist neuen Speicher allozieren ...			  */

	if ((move_no % HIST_LEN) == 0)
		extend_history();
}


/*------------------------------------------------------------------------*/
/* take_move_back()														  */
/*	Nimmt den letzten Zug zurck										  */
/*------------------------------------------------------------------------*/


void take_move_back(void)
{
	char *ptr;
	long temp_move_no;
	int source_col, source, dest_col, dest;
	int dest_x, dest_y;


	/* Sind keine Zge gespeichert oder ist man beim ersten Zug angelangt */
	/* gehts natrlich nicht weiter zurck ...							  */

	if ((no_history) || (move_no == 0))
	{
		Cconout('\a');
		return;
	}

	move_no--;

	/* Sonst zuerst einmal durch die History durchhangeln (s.o.) ...	  */

	temp_move_no = move_no;

	ptr = history;

	while (temp_move_no >= HIST_LEN)
	{
		ptr = *((char **) (ptr + 2L * HIST_LEN));
		temp_move_no -= HIST_LEN;
	}

	/* ... die gespeicherten Zug holen ...								  */

	dest_col = (int) *(ptr + 2L * temp_move_no);
	source_col = (int) *(ptr + 2L * temp_move_no + 1);

	/* ... und die Karte erst entfernen und dann wieder an der alte Posi- */
	/* tion zeichnen :													  */

	/* Dazu die unterste Karte der Reihe bestimmen, von der die Karte	  */
	/* wieder entfernt werden muž ...									  */

	if (source_col >= N)
		source = card[source_col].prev_card;
	else
	{
		source = column[source_col].first_card;
		while (card[source].next_card != NO_CARD)
			source = card[source].next_card;
	}

	/* ... und das selbe fr die Zielreihe								  */

	if (dest_col >= N)
	{
		dest = card[dest_col].prev_card;

		dest_x = card[dest_col].x;
		dest_y = card[dest_col].y;
	}
	else
	{
		dest = column[dest_col].first_card;

		dest_x = column[dest_col].x;
		dest_y = wwy + TOP_ROW;

		if (dest != NO_CARD)
		{
			while (card[dest].next_card != NO_CARD)
				dest = card[dest].next_card;
			dest_y = card[dest].y;
		}
	}

	/* Karte von der alten Position l”schen, eine sich von ihrer alten	  */
	/* zur neuen Position bewegende Box zeichnen und die Karte an die	  */
	/* Zielreihe anh„ngen												  */

	take_away(source);

	graf_mbox(CARD_WIDTH, CARD_LENGTH, card[source].x, card[source].y,
															dest_x, dest_y);

	if (dest_col >= N)
	{
		card[dest_col].prev_card = source;

		card[source].x = card[dest_col].x;
		card[source].y = card[dest_col].y;
		card[source].h = CARD_LENGTH;
		card[source].column = dest_col;
		card[source].prev_card = card[source].next_card = NO_CARD;

		show_card(source, ON);
	}
	else
	{
		if (dest != NO_CARD)
		{
			card[dest].next_card = source;
			card[dest].h = column[dest_col].del_y;
			card[source].prev_card = dest;
			card[source].y = card[dest].y + column[dest_col].del_y;
		}
		else
		{
			card[source].prev_card = NO_CARD;
			column[dest_col].first_card = source;
			card[source].y = wwy + TOP_ROW;
		}

		card[source].next_card = NO_CARD;
		card[source].column = dest_col;
		card[source].x = column[dest_col].x;
		card[source].h = CARD_LENGTH;

		if ((card[source].y + CARD_LENGTH) < (wwy + wwh - 1))
		{
			show_card(source, ON);
			if (column[dest_col].h == 0)
				column[dest_col].h = CARD_LENGTH;
			else
				column[dest_col].h += column[dest_col].del_y;
		}
		else
			show_column(dest_col, column[dest_col].x, ON);
	}

	/* Wurde die Karte von der Endablage zurckgenommen, Anzahl der auf	  */
	/* der Spielfl„che verbliebenen Karten inkrementieren				  */

	if ((source_col >= N) && (source_col < N + 4))
		remaining_cards++;

	/* U. U. den Speicherplatz zur Speicherung der Zge verkleinern		  */

	if ((move_no % HIST_LEN) == HIST_LEN - 1)
		shorten_history();
}


/*------------------------------------------------------------------------*/
/* show_box()															  */
/*  Zeigt Gratulationsbox bei erfolgreicher Beendigung eines Spiels bzw.  */
/*	Box mit Informationen ber das Spiel.								  */
/* ->  Objektnummer der darzustellenden Box								  */
/* <-  1 = Spiel beenden, 0 = weitermachen								  */
/*------------------------------------------------------------------------*/


int show_box(int ob_no)
{
	int mwhich,							/* Variablen fr evnt_multi()	  */
		mbuf[8],
		mox, moy,
		mbutton, mokstate,
		mkreturn, mbreturn;
	OBJECT *box;						/* Pointer auf darzustellende Box */
	int quit_flag = OFF;				/* gesetzt, wenn alles fertig ist */


	/* Adresse der Box besorgen, Box vorbereiten und darstellen			  */

	rsrc_gaddr(R_TREE, ob_no, &box);

	if ((scr_x - wwx < box->ob_width + 10) ||
										(scr_y - wwy < box->ob_height + 10))
		return(0);

	box->ob_x = MIN(wwx + (www - box->ob_width) / 2,
										 (scr_x + wwx - box->ob_width) / 2);
	box->ob_y = MIN(wwy + (wwh - box->ob_height) / 2,
										(scr_y + wwy - box->ob_height) / 2);

	/* Wenn es sich um die Gratulationsbox handelt, die Anzahl der ben”-  */
	/* tigten Zge eintragen											  */

	if (ob_no == GRAT)
		i_to_a(box[GNUMBER].S_TEXT, move_no, 5);

	objc_draw(box, R_TREE, 1, MAX(box->ob_x - 5, 0),
					  MAX(box->ob_y - 5, 0), MIN(box->ob_width + 10, scr_x),
										   MIN(box->ob_height + 10, scr_y));

	/* MOVER und FULLER des Fensters ausschalten						  */

	wind_set(wind_handle, WF_KIND, CLOSER | NAME);

	/* Auf Messages, Anklicken von 'OK' oder Drcken der Return-Taste	  */
	/* warten															  */

	do
	{
		mwhich = evnt_multi(MU_BUTTON | MU_KEYBD | MU_MESAG,
							1, 1, 1,
							0, 0, 0, 0, 0,
							0, 0, 0, 0, 0,
							mbuf, 0, 0,
							&mox, &moy, &mbutton,
							&mokstate, &mkreturn, &mbreturn);

	/* Reaktion auf Messages ...										  */

		if (mwhich & MU_MESAG)
		{
			switch (mbuf[0])		   /* Nach Art der Message verzweigen */
			{
				case WM_REDRAW :			  /* REDRAW-Message empfangen */
					wind_redraw(mbuf + 4, (OBJECT *) 0L);
					wind_redraw(mbuf + 4, box);
					break;

				case WM_TOPPED :		/* Fenster wurde oberstes Fenster */
					wind_set(wind_handle, WF_TOP);
					break;

				case WM_CLOSED :						 /* Spiel beenden */
					return(1);

				case MN_SELECTED :	   /* nur auf Spiel beenden reagieren */
					menu_tnormal(menu, mbuf[3], NORM);
					if ((mbuf[3] == T1) && (mbuf[4] == T1E5))
						return(1);
					break;
			}
		}

	/* Abbrechen, wenn 'OK' angeklickt oder RETURN gedrckt wurde		  */

		if (mwhich & MU_BUTTON)
		{
			if (((ob_no == GRAT) &&
						(GREADY == objc_find(box, 0, 1, mox, moy))) ||
				((ob_no == ABOUT) &&
						(AREADY == objc_find(box, 0, 1, mox, moy))))
			quit_flag = ON;
		}

		if ((mwhich & MU_KEYBD) && ((mkreturn & 0xFF) == RETURN))
			quit_flag = ON;

	} while (!quit_flag);

	/* Von der Box berdeckten Teil des Spielfeld neu zeichnen ...		  */

	clip_array[0] = MAX(box->ob_x - 5, 0);
	clip_array[1] = MAX(box->ob_y - 5, 0);
	clip_array[2] = MIN(box->ob_x + box->ob_width + 4, scr_x);
	clip_array[3] = MIN(box->ob_y + box->ob_height + 4, scr_y);
	vs_clip(wkst_handle, ON, clip_array);

	show_all(OFF);

	/* ... und FULLER und MOVER des Fensters wieder anschalten			  */

	wind_set(wind_handle, WF_KIND, CLOSER | NAME | FULLER | MOVER);

	return(0);
}


/*------------------------------------------------------------------------*/
/* on_mouse_event()														  */
/*	1. Testen, ob eine Karte angeklickt wurde							  */
/*	2. Testen, ob die Karte berhaupt bewegt werden kann				  */
/*	3. Box um die Karte mit der Maus verschieben lassen					  */
/*	4. Zielkarte feststellen											  */
/*	5. bei mehreren zu verschiebenden Karten testen, ob gengend freie	  */
/*	   Pl„tze fr diese Aktion vorhanden sind							  */
/*	6. Funktionen zum Verschieben der Karte(n) aufrufen					  */
/*																		  */
/* ->  x- und y-Position der Maus beim Drcken des Buttons sowie Zustand  */
/*	   der Mausbuttons													  */
/* <-  1 = Spiel ist zu Ende, 0 = noch nicht alle Karten abgelegt		  */
/*------------------------------------------------------------------------*/


int on_mouse_event(int mox, int moy, int mbutton)
{
	int del_x, del_y;
	int touched_card, cards_to_move, where_to;


	/* Herausfinden welche Karte angeklickt wurde						  */

	if ((touched_card = find_card(mox, moy)) == NO_CARD)
	{
		wait_for_left_release();
		return(0);
	}

	/* Untersuchen ob die angeklickte Karte berhaupt bewegt werden kann  */
	/* und um wieviele es sich handelt (bei Zgen, bei denen beide Maus-  */
	/* buttons gedrckt sind, also eine Reihe auf die Zwischenablage ge-  */
	/* schoben werden soll, mssen die zu verschiebenden Karten *nicht*	  */
	/* aneinanderpassen)												  */

	if (((cards_to_move = is_touch_possible(touched_card)) == 0) &&
															 (mbutton != 3))
	{
		wait_for_left_release();
		return(0);
	}

	/* Wenn beide Mausbuttons gedrckt sind, versuchen, die Karte(n) in	  */
	/* die Zwischenablage zu verschieben								  */

	if (mbutton & 2)
	{
		move_multi_to_temp(touched_card);
		return(0);
	}

	/* Sonst die Karte(n) in Form von Dragbox (mit Gr”že entsprechend der */
	/* Maže der zu verschiebenden Karte(n)) verschieben lassen, Maus	  */
	/* dabei als Hand darstellen										  */

	graf_mouse(FLAT_HAND, 0);

	del_x = mox - card[touched_card].x - 2;
	del_y = moy - card[touched_card].y;

	graf_dragbox(CARD_WIDTH - 4, get_move_length(touched_card),
							 card[touched_card].x + 2, card[touched_card].y,
											wwx, wwy, www, wwh, &mox, &moy);
	graf_mouse(ARROW, 0);

	mox += del_x;
	moy += del_y;

	/* Ziel des Verschiebens feststellen								  */

	if ((where_to = find_where_to(mox, moy)) == NO_CARD)
		return(0);

	/* Untersuchen ob zu verschiebende Karte berhaupt an das Ziel pažt	  */

	if (!is_move_possible(touched_card, where_to))
	{
		if (card[touched_card].column != where_to)
			Cconout('\a');
		return(0);
	}

	/* Bei mehr als einer zu verschiebenden Karte untersuchen ob gengend */
	/* freie Pl„tze zur Verfgung stehen								  */

	if ((cards_to_move > 1) && !is_enough_room(cards_to_move, where_to))
	{
		Cconout('\a');
		return(0);
	}

	/* Wenn alle Bedingungen erfllt sind kann endlich verschoben werden  */

	if (cards_to_move == 1)
		move_card(touched_card, where_to, OFF);
	else
		move_multi_cards(touched_card, where_to);

	if (remaining_cards == 0)
		return(1);
	else
		return(0);
}


/*------------------------------------------------------------------------*/
/* find_card()															  */
/*	Stellt die Nummer der Karte an der Mausposition fest.				  */
/* ->  x- und y-Position der Maus										  */
/* <-  Nummer der angeklickten Karte (bzw. NO_CARD, wenn sich an der ent- */
/*	   sprechenden Stelle gar keine Karte befindet)						  */
/*------------------------------------------------------------------------*/


int find_card(int x, int y)
{
	int i;

	/* Positionen aller Karten mit der angeklickten Stelle vergleichen	  */

	for (i = 0; i < N; i++)
	{
		if ((x >= card[i].x) && (x < card[i].x + CARD_WIDTH) &&
							(y >= card[i].y) && (y < card[i].y + card[i].h))
			return(i);
	}

	return(NO_CARD);
}


/*------------------------------------------------------------------------*/
/* is_touch_possible()													  */
/*	Testet, ob die angeklickte Karte berhaupt den Regeln entsprechend	  */
/*	bewegt werden kann.													  */
/* ->  Nummer der zu bewegenden Karte									  */
/* <-  1 = alles ok, 0 = Karte kann nicht bewegt werden					  */
/*------------------------------------------------------------------------*/


int is_touch_possible(int card_no)
{
	int i, count;


	/* Zug von der Zwischenablage aus ist immer m”glich					  */

	if (card[card_no].column >= N + 4)
		return(1);

	/* Rcknahme eines Karte von der Endablage ist verboten !			  */

	if (card[card_no].column >= N)
		return(0);

	/* Zug mit der letzten Karte einer Reihe ist im Prinzip immer m”glich */

	if ((i = card[card_no].next_card) == NO_CARD)
		return(1);

	/* Sonst muž berprft werden ob die folgenden Karten in geordneter	  */
	/* Form vorliegen und dabei gleichzeitig z„hlen, wieviele Karten zu	  */
	/* verschieben sind													  */

	count = 1;
	do
	{

	/* (card_no & 1) ergibt die Farbe einer Karte (n„mlich 0 fr schwarz  */
	/* und 1 fr rot), w„hrend mit (card_no >> 2) ihr Wert bestimmt wird  */

		if (((card_no & 1) ^ (i & 1)) && ((card_no >> 2) - 1 == (i >> 2)))
		{
			card_no = i;
			i = card[i].next_card;
			count++;
		}
		else
			return(0);
	} while (i != NO_CARD);

	return(count);
}


/*------------------------------------------------------------------------*/
/* get_move_length()													  */
/*	Stellt fest, wie lang (in Pixeln) die zu bewegende Reihe von Karten	  */
/*	ist.																  */
/* ->  Nummer der obersten zu bewegenden Karte							  */
/* <-  L„nge (in Pixeln) der zu verschiebenden Reihe					  */
/*------------------------------------------------------------------------*/


int get_move_length(int card_no)
{
	if (card[card_no].column < N)
		return(column[card[card_no].column].h + TOP_ROW + wwy -
														   card[card_no].y);
	else
		return(CARD_LENGTH);
}


/*------------------------------------------------------------------------*/
/* find_where_to()														  */
/*	Stellt die Nummer der Reihe fest, an die die zu bewegende(n) Karte(n) */
/*	angelegt werden soll(en).											  */
/* ->  x- und y-Position der Zielkarte									  */
/* <-  Nummer der Zielreihe (bzw. NO_CARD wenn keine vorhanden)			  */
/*------------------------------------------------------------------------*/


int find_where_to(int x, int y)
{
	int i;


	/* Nach passender Reihe suchen ...									  */

	if (y >= wwy + TOP_ROW)
	{
		for (i = 0; i < NUM_COLUMNS; i++)
			if ((x >= column[i].x) && (x < column[i].x + CARD_WIDTH))
				return(i);
		return(NO_CARD);
	}

	/* ... sonst mit Positionen der End- und Zwischenablage vergleichen	  */

	for (i = N; i < N + 8; i++)
	{
		if ((x >= card[i].x) && (x < card[i].x + CARD_WIDTH) &&
							(y >= card[i].y) && (y < card[i].y + card[i].h))
			return(i);
	}

	return(NO_CARD);
}


/*------------------------------------------------------------------------*/
/* is_enough_room()														  */
/*	Testet, ob gengend freie Pl„tze in der Zwischenablage sowie leere	  */
/*	Reihen vorhanden sind, um die geplante Verschiebung durchzufhren.	  */
/* ->  Zahl der zu verschiebenden Karten und Nummer der Zielreihe		  */
/* <-  1 = alles ok, 0 = nicht gengend freie Pl„tze					  */
/*------------------------------------------------------------------------*/


int is_enough_room(int no, int dest)
{
	int free_temp = 0, free_place = 0;
	int i;


	/* Ein Mehrfachzug auf die End- oder Zwischenablage ist nicht m”glich */

	if (dest >= NUM_COLUMNS)
		return(0);

	/* Jetzt feststellen wieviele freie Reihen und leere Zwischenablage-  */
	/* pl„tze es gibt													  */

	for (i = 0; i < NUM_COLUMNS; i++)
		if (column[i].first_card == NO_CARD)
			free_place++;

	for (i = N + 4; i < N + 8; i++)
		if (card[i].prev_card == NO_CARD)
			free_temp++;

	/* Falls die Zielposition einer der freien Pl„tze ist, so kann er	  */
	/* nicht als Zwischenlagerplatz genutzt werden						  */

	if (column[dest].first_card == NO_CARD)
		free_place--;

	/* Nun berechnen wieviele Karten maximal bewegt werden k”nnen		  */

	if (get_max_no(free_place, free_temp) >= no)
		return(1);
	else
		return(0);
}


/*------------------------------------------------------------------------*/
/* get_max_no()															  */
/*	Berechnet, wieviele Karten bei gegebener Anzahl von freien Reihen und */
/*	freien Pl„tzen in der Zwischenablage verschoben werden k”nnen		  */
/* ->  Anzahl der freien Reihen und Zwischenablagepl„tze				  */
/* <-  Anzahl der maximal zu verschiebenden Karten						  */
/*------------------------------------------------------------------------*/


int get_max_no(int place, int temp)
{
	int max_no = 0;


	/* Bei bester Ausnutzung der Pl„tze passen auf die erste freie Reihe  */
	/* (temp + 1) Karten (temp ist die Anzahl der freien Zwischenablage-  */
	/* pl„tze), auf die zweite 2 * (temp + 1) usw., und zum Schluž k”n-	  */
	/* nen noch temp Karten in die Zwischenablage gelegt werden und eine  */
	/* weitere Karte verschoben werden.									  */

	while (place > 0)
		max_no += place-- * (temp + 1);

	return(max_no + temp + 1);
}



/*------------------------------------------------------------------------*/
/* is_move_possible()													  */
/*	Untersucht, ob ein Verschieben (den Spielregeln entsprechend) m”glich */
/*	ist.																  */
/* ->  Nummer der (obersten) zu bewegenden Karte und Nummer der Zielreihe */
/* <-  1 = alles ok, 0 = Zug ist nicht m”glich							  */
/*------------------------------------------------------------------------*/


int is_move_possible(int source, int dest)
{
	int j;


	/* Rcknahme von der Endablage ist nicht erlaubt !					  */

	if ((card[source].column >= N) && (card[source].column < N + 4))
		return(0);

	/* Bei Reihe als Ziel auf Kompatibilit„t mit dem Wert der letzten	  */
	/* Karte in der Reihe untersuchen									  */

	if (dest <= NUM_COLUMNS)
	{
		if ((j = column[dest].first_card) == NO_CARD)
			return(1);
		while (card[j].next_card != NO_CARD)
			j = card[j].next_card;

		return(((source & 1) ^ (j & 1)) &&
										 ((j >> 2) == ((source >> 2) + 1)));
	}

	/* Bei Ablage auf der Endablage untersuchen, ob die Karte pažt		  */

	if (dest < N + 4)
	{
		if (card[dest].prev_card == NO_CARD)
		{
			if (dest - N != source)
				return(0);
			else
				return(1);
		}

		if (((card[dest].prev_card & 3) == (source & 3)) &&
						 ((card[dest].prev_card >> 2) + 1 == (source >> 2)))
			return(1);
		else
			return(0);
	}

	/* Bei Zwischenablage als Ziel untersuchen, ob die Position frei ist  */

	if ((dest < (N + 8)) && (card[dest].prev_card == NO_CARD))
		return(1);
	else
		return(0);
}


/*------------------------------------------------------------------------*/
/* wait_for_left_release()												  */
/*	Wartet auf Loslassen der linken Maustaste.							  */
/*------------------------------------------------------------------------*/


void wait_for_left_release(void)
{
	int dummy;

	evnt_button(0, 1, 0, &dummy, &dummy, &dummy, &dummy);
}


/*------------------------------------------------------------------------*/
/* move_card()															  */
/*	Verschiebt einzelne Karte.											  */
/* ->  Nummer der zu verschiebenden Karte, Nummer der Zielreihe (bzw.	  */
/*	   End- oder Zwischenablage) sowie Flag, das anzeigt, ob MBOX beim	  */
/*	   Verschieben gezeigt werden soll									  */
/*------------------------------------------------------------------------*/


void move_card(int source, int dest, int mbox_flag)
{
	int j, dest_x, dest_y;


	/* AES an Aktionen auf dem Bildschirm hindern						  */

	wind_update(BEG_UPDATE);

	/* Falls erwnscht, MBOX von der Ausgangskarte zur Zielkarte anzeigen */

	if (mbox_flag)
	{
		if (dest >= N)				/* Karte auf End- oder Zwischenablage */
		{
			graf_mbox(CARD_WIDTH, CARD_LENGTH, card[source].x,
								card[source].y, card[dest].x, card[dest].y);
		}
		else									 /* Anlegen an eien Reihe */
		{
			j = column[dest].first_card;
			if (j != NO_CARD)					  /* Zielreihe nicht leer */
			{
				while (card[j].next_card != NO_CARD)
					j = card[j].next_card;

				dest_x = card[j].x;
				dest_y = card[j].y;
			}
			else								   /* Zielreihe noch leer */
			{
				dest_x = column[dest].x;
				dest_y = wwy + TOP_ROW;
			}

			graf_mbox(CARD_WIDTH, CARD_LENGTH, card[source].x,
										   card[source].y, dest_x , dest_y);
		}
	}

	/* Zug abspeichern ...												  */

	if (!(menu[T1E4].ob_state & DISABLED))
		save_move(card[source].column, dest);

	/* Karte von ihrer alten Position entfernen							  */

	take_away(source);

	/* bei Ablage auf End- oder Zwischenablage :						  */

	if (dest >= N)
	{
		card[source].x = card[dest].x;
		card[source].y = card[dest].y;
		card[source].h = CARD_LENGTH;
		card[source].column = dest;
		card[source].prev_card = card[source].next_card = NO_CARD;
		card[dest].prev_card = source;
		show_card(source, ON);

	/* Bei Ablage auf Endablage Restkartenzahl dekrementieren			  */

		if ((dest >= N) && (dest < N +4))
			remaining_cards--;

	/* AES-Aktionen auf dem Bildschirm wieder zulassen und fertig		  */

		wind_update(END_UPDATE);
		return;
	}
	
	/* bei Ablage am Ende einer Reihe									  */

	j = column[dest].first_card;
	if (j != NO_CARD)					  /* Reihe enth„lt schon Karte(n) */
	{
		while (card[j].next_card != NO_CARD)
			j = card[j].next_card;
		card[source].x = card[j].x;
		card[source].y = card[j].y + column[card[j].column].del_y;
		card[source].h = CARD_LENGTH;
		card[source].column = card[j].column;
		card[j].next_card = source; 
		card[source].prev_card = j;
		card[source].next_card = NO_CARD;
		card[j].h = column[dest].del_y;
	}
	else				  /* sonst bildet die neue Karte eine neue Reihe  */
	{
		card[source].x = column[dest].x;
		card[source].y = wwy + TOP_ROW;
		card[source].h = CARD_LENGTH;
		card[source].column = dest;
		card[source].prev_card = card[source].next_card = NO_CARD;
		column[dest].first_card = source;
	}

	/* Wenn die Reihe durch die neue Karte zu lang wird, um noch in das	  */
	/* Fenster zu passen, muž die ganze Reihe neu gezeichnet werden,	  */
	/* sonst nur die neue Karte ans Ende der Reihe zeichnen				  */

	if ((card[source].y + CARD_LENGTH) < (wwy + wwh - 1))
	{
		show_card(source, ON);
		if (column[card[source].column].h == 0)
			column[dest].h = CARD_LENGTH;
		else
			column[card[source].column].h +=
										  column[card[source].column].del_y;
	}
	else
		show_column(card[source].column, column[card[source].column].x, ON);

	/* AES-Aktionen auf dem Bildschirm wieder zulassen					  */

	wind_update(END_UPDATE);
}


/*------------------------------------------------------------------------*/
/* take_away()															  */
/*	Entfernt Karte von ihrer augenblicklichen Position.					  */
/* ->  Nummer der zu entfernenden Karte									  */
/*------------------------------------------------------------------------*/


void take_away(int card_no)
{
	int i;


	/* Clipping auf Bereich der Karte einstellen						  */

	clip_array[0] = MAX(card[card_no].x, 0);
	clip_array[1] = MAX(card[card_no].y, 0);
	clip_array[2] = MIN(card[card_no].x + CARD_WIDTH - 1, scr_x);
	clip_array[3] = MIN(card[card_no].y + CARD_LENGTH - 1, scr_y);

	vs_clip(wkst_handle, ON, clip_array);

	/* Wenn die Karte in einer Reihe liegt muž sie mit dem Muster des	  */
	/* Hintergrunds bermalt werden und die darberliegenden Karten z. T. */
	/* neu gemalt werden, u. U. muž bei gestauchten Reihen auch die ganze */
	/* Reihe neu gezeichnet werden										  */

	if (card[card_no].column < NUM_COLUMNS)
	{
		paint_background();

		i = card[card_no].prev_card;
		if (i != NO_CARD)
		{
			card[i].h = CARD_LENGTH;
			card[i].next_card = NO_CARD;
			if (column[card[card_no].column].del_y == CARD_Y_SPAC)
			{
				show_card(i, ON);
				column[card[card_no].column].h -= CARD_Y_SPAC;
			}
			else
				show_column(card[card_no].column,
										column[card[card_no].column].x, ON);
		}

	/* Bei der Zwischenablage muž nur diese neu (leer) gezeichnet werden, */
	/* bei der Endablage u. U. die darunterliegende Karte				  */

		else
		{
			column[card[card_no].column].first_card = NO_CARD;
			column[card[card_no].column].h = 0;
		}
	}
	else
	{
		if (card[card_no].column >= N + 4)
			card[card[card_no].column].prev_card = NO_CARD;
		else
		{
			if (card_no > 3)
				card[card[card_no].column].prev_card -= 4;
			else
				card[card[card_no].column].prev_card = NO_CARD;
		}

		if (card[card[card_no].column].prev_card == NO_CARD)
			show_card(card[card_no].column, ON);
		else
			show_card(card[card[card_no].column].prev_card, ON);
	}
}


/*------------------------------------------------------------------------*/
/* move_multi_cards()													  */
/*	Verschiebt eine ganze Reihe von Karten								  */
/* ->  Nummer der obersten Karte der zu verschiebenden Reihe und Nummer	  */
/*	   der Zielreihe													  */
/*------------------------------------------------------------------------*/


void move_multi_cards(int source, int dest)
{
	int free_place[NUM_COLUMNS], max_place = 0, num_place;
	int free_temp[4], max_temp = 0, num_temp;
	int i, k, place_first = OFF;


	/* Zuerst die Lage der freien Reihen sowie ihre Anzahl bestimmen -	  */
	/* eine leere Zielreihe darf aber nicht bercksichigt werden !		  */

	for (i = 0; i < NUM_COLUMNS; i++)
	{
		if (i == dest)
			continue;
		if (column[i].first_card == NO_CARD)
			free_place[max_place++] = i;
	}

	/* Ebenso die Position und Anzahl der freien Zwischenablagepl„tze	  */

	for (i = N + 4; i < N + 8; i++)
	{
		if (card[i].prev_card == NO_CARD)
			free_temp[max_temp++] = i;
	}

	/* Nun kommt die grože Hin- und Herschieberei ...					  */

	free_place[max_place] = dest;

	num_temp = max_temp;
	num_place = max_place;

	/* Solange noch nicht alle freien Pl„tze belegt sind ...			  */

	k = 0;
	while (k < max_place)
	{
		num_place = max_place;

	/* Alle freien Pl„tze ber die Zwischenablage mit Karten belegen -	  */
	/* wenn die letzte zu verschiebende Karte dabei erreicht wird ab-	  */
	/* brechen															  */

		while (num_place > k)
		{
			if (move_column_to_temp(card[source].column, source, free_temp,
																 &num_temp))
				goto reverse_multi;

			if (move_single_card(card[source].column, source, free_place,
															 num_place - 1))
			{
				place_first = ON;
				goto reverse_multi;
			}

			move_temp_to_column(free_temp, &num_temp, max_temp,
											 *(free_place + num_place - 1));
			num_place--;
		}

	/* Und diese auf die freien Pl„tze verteilten Karten wieder auf	den	  */
	/* am weitesten links liegenden freien Pl„tze umschichten			  */

		while (++num_place < max_place)
		{
			move_column_to_temp(*(free_place + num_place), NO_CARD,
													  free_temp, &num_temp);
			move_single_card(*(free_place + num_place), NO_CARD,
															 free_place, k);
			move_temp_to_column(free_temp, &num_temp, max_temp,
														 *(free_place + k));
		}

		k++;
	}

	/* Zum Schluž auch noch die wieder freien Zwischenablagepl„tze mit	  */
	/* Karten fllen													  */

	move_column_to_temp(card[source].column, source, free_temp, &num_temp);

reverse_multi:

	/* Jetzt als erstes die letzte (oberste) Karte der zu verschiebenden  */
	/*  Reihe an die Zielreihe anlegen ...								  */

	move_single_card(card[source].column, NO_CARD, free_place, max_place);

	/* ... und die Zwischenablage daran anlegen	(gegebenfalls vorher noch */
	/* die einzelne Karte vom freien Platz holen) 						  */

	if (place_first)
		move_single_card(*(free_place + num_place - 1), NO_CARD, free_place,
																 max_place);

	move_temp_to_column(free_temp, &num_temp, max_temp, dest);

	/* Zum Schluž die gesamten auf die freien Pl„tze umgeschichteten Kar- */
	/* ten anlegen. Zuerst die nicht vollst„ndig umgelegten Haufen wieder */
	/* in richtiger Ordnung an die Zielreihe anlegen ...				  */

	k = num_place;
	while (k < max_place)
	{
		if (column[*(free_place + k)].first_card == NO_CARD)
			continue;

		move_column_to_temp(*(free_place + k), NO_CARD,
													  free_temp, &num_temp);
		move_single_card(*(free_place + k), NO_CARD, free_place, max_place);
		move_temp_to_column(free_temp, &num_temp, max_temp,
												 *(free_place + max_place));
		k++;
	}

	/* ... dann die restlichen Haufen auseinandernehmen und an die Ziel-  */
	/* reihe anlegen													  */

	while (num_place-- > 0)
	{
		/* Wenn keine Karte auf der Reihe liegt, n„chste Reihe probieren  */

		if (column[*(free_place + num_place)].first_card == NO_CARD)
			continue;

		/* Reihe in Einzelteile zerlegen ...							  */

		k = max_place - 1;
		while (k > num_place)
		{
			move_column_to_temp(*(free_place + num_place), NO_CARD,
													  free_temp, &num_temp);
			move_single_card(*(free_place + num_place), NO_CARD, free_place,
																		 k);
			move_temp_to_column(free_temp, &num_temp, max_temp,
														 *(free_place + k));
			k--;
		}

		/* ... und nacheinander an der Zielreihe anlegen				  */

		k = num_place;
		while (k < max_place)
		{
			move_column_to_temp(*(free_place + k), NO_CARD,
													  free_temp, &num_temp);
			move_single_card(*(free_place + k), NO_CARD,
													 free_place, max_place);
			move_temp_to_column(free_temp, &num_temp, max_temp,
												 *(free_place + max_place));
			k++;
		}
	}
}


/*------------------------------------------------------------------------*/
/* move_temp_to_column()												  */
/*	Legt den Inhalt der Zwischenablage, der dort vorher mit der Funktion  */
/*	move_column_to_temp() abgelegt wurde in richtiger Reihenfolge wieder  */
/*	an einer Reihe an. (Diese Funktion wird nur von move_multi_cards()	  */
/*	ben”tigt.)															  */
/* ->  Pointer auf Liste der von move_column_to_temp() verwendeten freien */
/*	   Pl„tze in der Zwischenablage, Pointer auf Anzahl der zu verschie-  */
/*	   benden Karten, maximale Anzahl der freien Pl„tze in der Zwischen-  */
/*	   ablage sowie Nummer der Zielreihe								  */
/*------------------------------------------------------------------------*/


void move_temp_to_column(int *free_temp, int *act_temp, int max_temp,
																   int dest)
{
	int next;


	/* Zuerst die letzte Karte der Zielreihe bestimmen					  */

	next = column[dest].first_card;
	while (card[next].next_card != NO_CARD)
		next = card[next].next_card;

	/* Nun alle Karten von der Zwischenablage auf die Reihe schieben	  */

	while (++(*act_temp) < max_temp)
		move_card(card[*(free_temp + *act_temp)].prev_card, dest, ON);
}


/*------------------------------------------------------------------------*/
/* move_single_card()													  */
/*	Verschiebt eine einzelne Karte von einer Reihe zu einer anderen.	  */
/*	(Diese Funktion wird nur von move_multi_cards() ben”tigt.)			  */
/* ->  Nummer der Ausgangsreihe, Nummer der obersten Karte der insgesamt  */
/*	   zu bewegenden Reihe, Liste der leeren Reihen sowie Anzahl der	  */
/*	   freien Reihen													  */
/* <-  1 = Karte ber der bewegten Karte ist die oberste Karte der insge- */
/*	   samt zu bewegenden Reihe, 0 = sonst								  */
/*------------------------------------------------------------------------*/


int move_single_card(int source_column, int final_card, int *free_place,
														 int act_free_place)
{
	int next, inter;

	/* Wenn gar keine Karte da ist sofort abbrechen						  */

	if ((next = column[source_column].first_card) == NO_CARD)
		return(0);

	/* Sonst die zu verschiebende Karte bestimmen ...					  */
	
	while (card[next].next_card != NO_CARD)
		next = card[next].next_card;

	inter = card[next].prev_card;

	/* ... und verschieben												  */

	move_card(next, *(free_place + act_free_place), ON);

	if (inter == final_card)
		return(1);
	else
		return(0);
}


/*------------------------------------------------------------------------*/
/* move_column_to_temp()												  */
/*	Verschiebt die Karten aus einer Reihe in die Zwischenablage, bis die- */
/*	gefllt ist oder aber die n„chste zu verschiedene Karte die oberste	  */
/*	Karte der zu verschiebenden Reihe ist. (Diese Funktion wird nur von	  */
/*	move_multi_cards() ben”tigt.)										  */
/* ->  Nummer der Ausgangsreihe, Nummer der letzten berhaupt zu ver-	  */
/*	   schiebenden Karte, Array mit Nummern der freien Zwischenablage-	  */
/*	   pl„tze sowie Pointer auf deren Anzahl							  */
/* <-  1 = Karte ber der bewegten Karte ist die oberste Karte der insge- */
/*	   samt zu bewegenden Reihe, 0 = sonst								  */
/*------------------------------------------------------------------------*/


int move_column_to_temp(int source_column, int final_card, int *free_temp,
														 int *act_free_temp)
{
	int next, inter;


	/* Wenn gar keine Karte da ist sofort abbrechen						  */

	if ((next = column[source_column].first_card) == NO_CARD)
		return(0);

	/* Sonst erst einmal die unterste zu verschiebende Karte bestimmen	  */

	while (card[next].next_card != NO_CARD)
		next = card[next].next_card;

	/* Nun soviele Karten in die Zwischenablage schieben, bis sie entwe-  */
	/* der voll ist oder die n„chste zu verschiebende Karte die oberste	  */
	/* der zu verschiebenden Reihe ist (oder die Reihe leer ist)		  */

	(*act_free_temp)--;
	while (*act_free_temp >= 0)
	{
		inter = card[next].prev_card;

		move_card(next, *(free_temp + *act_free_temp), ON);
		(*act_free_temp)--;

		if (inter == NO_CARD)
			return(0);

		if (inter == final_card)
			return(1);
		else
			next = inter;
	}

	return(0);
}


/*------------------------------------------------------------------------*/
/* move_multi_to_temp()													  */
/*	Verschiebt mehrere Karten gleichzeitig in die Zwischenablage.		  */
/* ->  Nummer der obersten Karte der zu verschiebenden Reihe			  */
/*------------------------------------------------------------------------*/


void move_multi_to_temp(int card_no)
{
	int free_temp[4], num_temp = 0, i, no = 1, next;


	/* Zuerst Anzahl der freien Zwischenablagepl„tze bestimmen und Array  */
	/* mit ihren Nummern besetzen										  */

	for (i = N + 4; i < N + 8; i++)
	{
		if (card[i].prev_card == NO_CARD)
			free_temp[num_temp++] = i;
	}

	/* Anzahl der auf Zwischenablage zu verschienenden Karten bestimmen	  */

	next = card_no;
	while ((next = card[next].next_card) != NO_CARD)
		no++;

	/* Abbruch, wenn die Anzahl der freien Pl„tze nicht ausreicht		  */

	if (no > num_temp)
	{
		Cconout('\a');
		wait_for_left_release();
		return;
	}

	/* Karten auf die Zwischenablage verschieben						  */

	move_column_to_temp(card[card_no].column, card[card_no].prev_card,
													 free_temp, & num_temp);

	/* Warten, bis der linke Mausbutton wieder losgelassen ist (anderen-  */
	/* falls befindet sich die Maus u. U. noch auf einer Karte und die	  */
	/* gedrcken Mausbuttons wrden als Befehlswiederholung interpretiert */

	wait_for_left_release();
}


/*------------------------------------------------------------------------*/
/* show_card()															  */
/*	Dient zur tats„chliche Darstellung einer Karte auf dem Bildschirm.	  */
/* ->  Nummer der darzustellenden Karte sowie Flag, das anzeigt, ob Clip- */
/*	   ping-Bereich gesetzt werden soll (bei Aufruf dieser Funktion durch */
/*	   wind_redraw() ist Clipping bereits gesetzt)						  */
/*------------------------------------------------------------------------*/


void show_card(int card_no, int clip_flag)
{
	int copy_array[8];
	MFDB source, dest;


	/* MFDBs fr Kopieren der Karten vorbereiten						  */

	source.fd_w = CARD_WIDTH;
	source.fd_h = CARD_LENGTH;
	source.fd_wdwidth = CARD_WIDTH / 16;
	source.fd_stand = 0;
	source.fd_nplanes = nplanes;
	
	dest.fd_addr = 0L;

	/* Array mit Positionen frs Kopieren vorbereiten					  */

	copy_array[0] = copy_array[1] = 0;
	copy_array[2] = CARD_WIDTH - 1;
	copy_array[3] = CARD_LENGTH - 1;

	copy_array[4] = card[card_no].x;
	copy_array[5] = card[card_no].y;
	copy_array[6] = copy_array[4] + CARD_WIDTH - 1;
	copy_array[7] = copy_array[5] + CARD_LENGTH - 1;

	v_hide_c(wkst_handle);

	/* Wenn Clipping gesetzt werden soll, dies auf Bereich der Karte ein- */
	/* stellen															  */

	if (clip_flag)
	{
		clip_array[0] = MAX(copy_array[4], 0);
		clip_array[1] = MAX(copy_array[5], 0);
		clip_array[2] = MIN(copy_array[6], scr_x);
		clip_array[3] = MIN(copy_array[7], scr_y);
		vs_clip(wkst_handle, ON, copy_array + 4);
	}

	/* Maske auf neue Position (mit AND-Verknpfung) kopieren			  */

	source.fd_addr = (void *) (pic + 57 * CARD_SIZE * nplanes);
	vro_cpyfm(wkst_handle, S_AND_D, copy_array, &source, &dest);

	/* Karte (mit OR-Verknpfung) auf neue Position kopieren		  	  */

	source.fd_addr = (void *) (pic + MIN(card_no, N + 4) * CARD_SIZE *
																   nplanes);
	vro_cpyfm(wkst_handle, S_OR_D, copy_array, &source, &dest);

	v_show_c(wkst_handle, 0);
}


/*------------------------------------------------------------------------*/
/* shuffle()															  */
/*	Dient zum Mischen der Karten sowie der anschlieženden Verkettung der  */
/*	Karten zu Listen fr die einzelnen Reihen.							  */
/*------------------------------------------------------------------------*/


void shuffle(void)
{
	int s_array[N];					/* Array fr Mischen der Karten		  */
	int index, i, j, k;
	double f_index;


	/* Z„hler fr Anzahl der bereits erfolgten Zge auf Null setzen und	  */
	/* den fr Anzahl der Karten auf der Spielfl„che auf N				  */

	move_no = 0;
	remaining_cards = N;

	/* Speicherbereiche fr Speicherung der Zge des letzten Spiels frei- */
	/* geben															  */

	if (no_history)
	{
		if (history)
		{
			no_history = OFF;
			menu_ienable(menu, T1E4, ON);
		}
	}
	else
	{
		while (!shorten_history())
			;
	}

	/* Array frs Mischen (geordnet) vorbesetzen						  */

	for (i = 0; i < N; i++)
		*(s_array + i) = i;

	/* Mischen, indem zuerst die letzte Karte mit irgendeiner anderen	  */
	/* vertauscht wird, dann die vorletzte, dann die vorvorletzte ...	  */

	for (i = N - 1; i > 0; i--)
	{
		f_index = (double) i * random_generator();
		index = (int) (2.0 * f_index) - (int) f_index;
		k = *(s_array + i);
		*(s_array + i) = *(s_array + index);
		*(s_array + index) = k;
	}

	/* Liste der Karten verketten (erste Karte hat keinen Vorg„nger, die  */
	/* letzte keinen Nachfolger)										  */

	card[*s_array].prev_card = NO_CARD;
	card[*s_array].next_card = *(s_array + 1);

	for (i = 1; i < N - 1; i++)
	{
		card[*(s_array + i)].prev_card = *(s_array + i - 1);
		card[*(s_array + i)].next_card = *(s_array + i + 1);
	}

	card[*(s_array + N - 1)].prev_card = *(s_array + N - 2);
	card[*(s_array + N - 1)].next_card = NO_CARD;

	/* Liste in Teilketten fr einzelne Reihen zerlegen (die erste Karte  */
	/* in einer Reihe hat keinen Vorg„nger, die letzte keinen Nachfolger) */

	for (i = *s_array, j = 0; j < NUM_COLUMNS; j++)
	{
		column[j].first_card = i;
		card[i].prev_card = NO_CARD;

		for (k = 0; k < ((j < 4) ? 6 : 5); k++)
			i = card[i].next_card;

		k = card[i].next_card;
		card[i].next_card = NO_CARD;
		i = k;
	}

	/* Zeiger in den Strukturen fr End- und Zwischenablage auf die dar-  */
	/* auf liegende Karte auf 'keine Karte' setzen						  */

	for (i = N; i < N + 8; i++)
		card[i].prev_card = NO_CARD;
}


/*------------------------------------------------------------------------*/
/* show_all()															  */
/*	Neudarstellung des gesamten Spielfeldes, also Zeichnen des Hinter-	  */
/*	grundes, der Zwischen-und Endablage sowie der Karten. Aužerdem werden */
/*	dabei diverse Eintr„ge in den Strukturen fr die Karten gesetzt.	  */
/* ->  Flag, das anzeigt, ob die gesamte Spielfl„che oder nur ein Aus-	  */
/*	   schnitt neu gezeichnet werden muž - im ersten Fall muž vorher	  */
/*	   der entsprechende Clipping-Bereich gesetzt sein					  */
/*------------------------------------------------------------------------*/


void show_all(int all_flag)
{
	int i, j, card_x;


	/* Hintergrund zeichnen												  */

	if (all_flag)
	{
		wind_update(BEG_UPDATE);
		clip_array[0] = MAX(wwx, 0);
		clip_array[1] = MAX(wwy, 0);
		clip_array[2] = MIN(wwx + www - 1, scr_x);
		clip_array[3] = MIN(wwy + wwh - 1, scr_y);
		vs_clip(wkst_handle, ON, clip_array);
	}

	paint_background();

	/* Pl„tze fr die Zwischenablage zeichnen, vorher einige Eintr„ge in  */
	/* den Strukturen (neu) setzen										  */

	card_x = wwx + CARD_X_SPAC;
	for (i = N + 4; i < N + 8; i++, card_x += CARD_WIDTH + CARD_X_SPAC)
	{
		card[i].x = card_x;
		card[i].y = wwy + 3;
		card[i].h = CARD_LENGTH;
		if (card[i].prev_card == NO_CARD)
			show_card(i, all_flag);
		else
		{
			j = card[i].prev_card;
			card[j].x = card_x;
			card[j].y = wwy + 3;
			card[j].h = CARD_LENGTH;
			show_card(j, all_flag);
		}
	}

	/* Pl„tze fr Endablage zeichnen, ebenfalls einige Eintr„ge in den	  */
	/* Strukuren (neu) setzen											  */

	card_x = wwx + MAX((www - 4 * (CARD_WIDTH + CARD_X_SPAC)),
							  (www - 8 * CARD_WIDTH - 7 * CARD_X_SPAC) / 2);
	for (i = N; i < N + 4; i++, card_x += CARD_WIDTH + CARD_X_SPAC)
	{
		
		card[i].x = card_x;
		card[i].y = wwy + 3;
		card[i].h = CARD_LENGTH;
		if (card[i].prev_card == NO_CARD)
			show_card(i, all_flag);
		else
		{
			j = card[i].prev_card;
			while (j >= 0)
			{
				card[j].x = card_x;
				card[j].y = wwy + 3;
				card[j].h = CARD_LENGTH;
				j -= 4;
			}
			show_card(card[i].prev_card, all_flag);
		}
	}

	/* Karten auslegen indem alle Reihen gezeichnet werden				  */

	card_x = wwx + (www - NUM_COLUMNS * CARD_WIDTH - (NUM_COLUMNS - 1) *
														   CARD_X_SPAC) / 2;
	for (i = 0; i < NUM_COLUMNS; i++, card_x += CARD_WIDTH + CARD_X_SPAC)
		show_column(i, card_x, all_flag);

	/* AES-Aktionen auf dem Bildschirm wieder zulassen					  */

	if (all_flag)
		wind_update(END_UPDATE);
}


/*------------------------------------------------------------------------*/
/* show_column()														  */
/*	Darstellung einer ganzen Reihe inklusive der Berechnung der šber-	  */
/*	lappungsl„nge der Karten.											  */
/* ->  Nummer der Reihe, deren x-Position sowie Flag, das anzeigt, ob	  */
/*	   kein Clipping gesetzt wird (nur fr Weitergabe an show_card()	  */
/*	   und clear_column() von Bedeutung)	 							  */
/*------------------------------------------------------------------------*/


void show_column(int col_no, int card_x, int clip_flag)
{
	int j, k;
	int card_y, del_y;


	/* Paramter in Struktur fr Reihe setzen							  */

	column[col_no].x = card_x;
	column[col_no].h = 0;
	column[col_no].del_y = CARD_Y_SPAC;

	/* Schon fertig, wenn die Reihe keine Karte enth„lt					  */

	if ((k = column[col_no].first_card) == NO_CARD)
		return;

	/* Sonst Anzahl der Karten in der Reihe bestimmen und daraus die	  */
	/* šberlappungsbreite der Karten berechnen							  */

	j = 1;
	while ((k = card[k].next_card) != NO_CARD)
		j++;

	card_y = wwy + TOP_ROW;
	if (j > 1)
		del_y = MIN(CARD_Y_SPAC, (wwh - CARD_LENGTH - TOP_ROW) / (j - 1));

	column[col_no].del_y = del_y;

	/* Bereich der Reihe vollst„ndig l”schen ...						  */

	clear_column(col_no, clip_flag);

	/* ... und alle Karten der Reihe nach neu zeichnen					  */

	k = column[col_no].first_card;
	do
	{
		card[k].x = card_x;
		card[k].y = card_y;
		card[k].column = col_no;
		if (card[k].next_card != NO_CARD)
			card[k].h = del_y;
		else
			card[k].h = CARD_LENGTH;

		show_card(k, clip_flag);

		column[col_no].h += card[k].h;
		card_y += del_y;
		k = card[k].next_card;
	} while (k != NO_CARD);
}


/*------------------------------------------------------------------------*/
/* automatic_remove()													  */
/*	Entfernt alle Karten aus dem Spiel, fr die dies m”glich ist und die  */
/*	mit Sicherheit nicht mehr im Verlauf des Spiels gebraucht werden.	  */
/* <-  1 = Spiel ist zu Ende, 0 = noch nicht alle Karten abgelegt		  */
/*------------------------------------------------------------------------*/


int automatic_remove(void)
{
	int i, j;
	int value;
	int no_remove;


	/* Solange Karten entfernen, bis keine zu entfernende Karte mehr ge-  */
	/* funden wird														  */

	do
	{
		no_remove = ON;

		/* Zuerst m”gliche Zge von der Zwischenablage aus untersuchen	  */

		for (i = N + 4; i < N + 8; i++)
		{

		/* N„chsten Platz testen, wenn der aktuelle Zwischenablageplatz	  */
		/* keine Karte enth„lt											  */

			if (card[i].prev_card == NO_CARD)
				continue;

		/* Ebenfalls, wenn Karte nicht auf entsprechende Endablage pažt	  */

			value = card[i].prev_card >> 2;
			if (value - 1 !=
						 (card[(card[i].prev_card & 3) + N].prev_card >> 2))
				continue;

		/* Oder auch, wenn niedrigere Karten der Gegenfarbe noch nicht	  */
		/* abgelegt sind (aužer bei As und 2)							  */

			if (value > 1)
			{
				if (card[i].prev_card & 1)
				{
					if (((card[N].prev_card >> 2 ) < value - 1) ||
								((card[N + 2].prev_card >> 2 ) < value - 1))
						continue;
				}
				else
				{
					if (((card[N + 1].prev_card >> 2 ) < value - 1) ||
								((card[N + 3].prev_card >> 2 ) < value - 1))
						continue;
				}
			}

		/* Sonst Karte auf Endablage legen und Flag zurcksetzen		  */

			move_card(card[i].prev_card, N + (card[i].prev_card & 3), ON);
			no_remove = OFF;
		}

		/* Nun auch alle Reihen untersuchen								  */

		for (i = 0; i < NUM_COLUMNS; i++)
		{

		/* Weiter mit n„chster Reihe wenn die Reihe keine Karten enth„lt  */

			if ((j = column[i].first_card) == NO_CARD)
				continue;

		/* Letzte Karte der Reihe bestimmen								  */

			for ( ; card[j].next_card != NO_CARD; j = card[j].next_card)
				;

		/* Weiter mit n„chster Reihe, wenn Karte nicht auf die entspre-	  */
		/* chende Endablage pažt										  */

			value = j >> 2;
			if (value - 1 != (card[(j & 3) + N].prev_card >> 2))
				continue;

		/* Oder wenn niedrigere Karten der Gegenfarbe noch nicht abgelegt */
		/* sind (aužer bei As und 2)									  */

			if (value > 1)
			{
				if (j & 1)
				{
					if (((card[N].prev_card >> 2 ) < value - 1) ||
								((card[N + 2].prev_card >> 2 ) < value - 1))
						continue;
				}
				else
				{
					if (((card[N + 1].prev_card >> 2 ) < value - 1) ||
								((card[N + 3].prev_card >> 2 ) < value - 1))
						continue;
				}
			}

		/* Sonst Karte auf Endablage legen und Flag zurcksetzen		  */

			move_card(j, N + (j & 3), ON);
			no_remove = OFF;
		}
	} while (!no_remove);

	if (remaining_cards == 0)
		return(1);
	else
		return(0);
}


/*------------------------------------------------------------------------*/
/* make_seed()															  */
/*	Berechnet (aus Z„hler des 200-Hz-Timer-Interrupts und dem Datum) eine */
/*	sechsstellige Pseudozufallszahl (0 <= x < 1), die als Ausgangszahl	  */
/*	fr den Zufallsgenerators fr das Mischen verwendet wird.			  */
/* <-  Pseudozufallszahl												  */
/*------------------------------------------------------------------------*/


double make_seed(void)
{
	long old_stack, temp;


	/* Als Startwert des Zufallsgenerators die Summe aus Zahl der Timer-  */
	/* Interrupts und Datum (mod 1,000,000) verwenden					  */

	old_stack = Super(NULL);
	temp = *(long *) _hz_200;
	Super((void *) old_stack);

	temp += Gettime();

	return((double) (temp % 1000000L) * 1.E-6);
}


/*------------------------------------------------------------------------*/
/* random_generator()													  */
/*	Pseudozufallsgenerator fr Zahlen 0 <= x < 1						  */
/* <- neue Pseudozufallszahl											  */
/*------------------------------------------------------------------------*/


double random_generator(void)
{
	/* Zufallsgenerator aus dem Handbuch zum HP-41 (liefert	1,000,000	  */
	/* verschiedene Zahlen zwischen 0 und 0.999999), dabei sicherstellen, */
	/* daž nicht durch Rundungsfehler mehr als 6 Stellen auftreten		  */

	seed = fmod(9821.0 * seed + 0.211327, 1.0);
	seed = (floor(seed * 2.E6) - floor(seed * 1.E6)) * 1.E-6;
	return(seed);
}



/*------------------------------------------------------------------------*/
/* set_window_header()													  */
/*	Setzt die Startzufallszahl fr das aktuelle Spiel in die Titelzeile	  */
/*	des Fensters des Spiels (Wert der Zahl zwischen 0 und 999999).		  */
/* ->  Pointer auf String mit Titelzeile								  */
/*------------------------------------------------------------------------*/


void set_wind_header(char *title)
{
	ltoa((long) (old_seed * 1.E6), title + 11, 10);
	strcat(title, " ");
	wind_set(wind_handle, WF_NAME, (int) ((long) title >> 16),
										 (int) ((long) title & 0x0000FFFF));
}


/*------------------------------------------------------------------------*/
/* paint_background()													  */
/*	Zeichnen des Hintergrundes des Spielfeldes.							  */
/*------------------------------------------------------------------------*/


void paint_background(void)
{
	int clear_array[4];


	/* Array mit Mažen der sichtbaren Arbeitsfl„che des Fensters besetzen */

	clear_array[0] = wwx;
	clear_array[1] = wwy;
	clear_array[2] = MIN(wwx + www - 1, scr_x);
	clear_array[3] = MIN(wwy + wwh - 1, scr_y);

	/* Maus ausschalten und mit Muster des Hintergrundes bermalen		  */

	graf_mouse(M_OFF, 0);
	v_bar(wkst_handle, clear_array);
	graf_mouse(M_ON, 0);
}


/*------------------------------------------------------------------------*/
/* clear_column()														  */
/*	L”scht den Bereich einer Reihe durch šbermalen mit dem Muster des	  */
/*	Spielfeldhintergrundes.												  */
/* ->  Nummer der zu l”schenden Reihe und Flag, das anzeigt, ob Clipping- */
/*	   Bereich auf den gesamten Bereich der Reihe gesetzt werden soll	  */
/*	   oder auf nur Schnitfl„che mit bereits gesetzten Clipping-Bereich	  */
/*------------------------------------------------------------------------*/


void clear_column(int col, int clip_flag)
{
	int old_clip[4], i;


	/* Wenn Flag nicht gesetzt, alten Clippingbereich zwischenspeichern	  */
	/* und Clipping auf Schnittfl„che mit der Reihe setzen				  */

	if (!clip_flag)
	{
		if ((column[col].x + CARD_WIDTH - 1 < clip_array[0]) ||
				(column[col].x > clip_array[2]) ||
					(wwy + wwh - 1 < clip_array[1]) ||
						(wwy + TOP_ROW > clip_array[3]))
			return;

		for (i = 0; i < 4; i++)
			old_clip[i] = clip_array[i];

		clip_array[0] = MAX(MAX(column[col].x, clip_array[0]), 0);
		clip_array[1] = MAX(MAX(wwy + TOP_ROW, clip_array[1]), 0);
		clip_array[2] = MIN(MIN(column[col].x + CARD_WIDTH - 1,
													 clip_array[2]), scr_x);
		clip_array[3] = MIN(MIN(wwy + wwh - 1, clip_array[3]), scr_y);
	}
	else
	{
		clip_array[0] = MAX(column[col].x, 0);
		clip_array[1] = MAX(wwy + TOP_ROW, 0);
		clip_array[2] = MIN(column[col].x + CARD_WIDTH - 1, scr_x);
		clip_array[3] = MIN(wwy + wwh - 1, scr_y);
	}

	vs_clip(wkst_handle, ON, clip_array);

	/* šbermalen durch Hintergrundmuster								  */

	paint_background();

	/* U. U. wieder alten Clippingbereich setzen						  */

	if (!clip_flag)
	{
		for (i = 0; i < 4; i++)
			clip_array[i] = old_clip[i];
		vs_clip(wkst_handle, ON, clip_array);
	}
}


/*------------------------------------------------------------------------*/
/* gem_init()															  */
/*	Meldet eine Applikation an, ”ffnet eine virtuelle Workstation (mit	  */
/*	dem Handle 'wkst_handle') und erzeugt ein unterlegtes Fenster (mit	  */
/*  dem Handle 'wind_handle').											  */
/* ->  Pointer auf String mit Titel des Fensters						  */
/* <-  1 bei erfolgreichem Abschluž, 0 bei Fehler						  */
/*------------------------------------------------------------------------*/


int gem_init(char *title)
{
	int work_in[11],				  /* Eingabe- und Ausgabeparameter 	  */
		work_out[57];				  /* fr ™ffnen der Workstation		  */
	int gl_hchar,					  /* H”he und Breite eines Zeichens	  */
		gl_wchar,					  /* aus dem Standardzeichensatz	  */
		gl_hbox,					  /* und die einer Box, in die die    */
		gl_wbox;					  /* Zeichen hineinpassen			  */


	if ((ap_id = appl_init()) == -1)			/* Applikation anmelden   */
		return(0);								/* ... mižlungen		  */

	wkst_handle = graf_handle(&gl_wchar, &gl_hchar,	  /* Handle der Work- */
							  &gl_wbox, &gl_hbox);	  /* station besorgen */

	work_in[0]  = wkst_handle;	   /* verschiedene Parameter der zu ”ff-  */
								   /* nenden Workstation festsetzen		  */
	work_in[1]  = 1;			   /* Linientyp = durchgezogen			  */
	work_in[2]  = BLACK;		   /* Linienfarbe = schwarz				  */
	work_in[3]  = 1;			   /* Markertyp = Punkt					  */
	work_in[4]  = BLACK;		   /* Markerfarbe = schwarz				  */
	work_in[5]  = 0;			   /* Textstil = Normalschrift			  */
	work_in[6]  = BLACK;		   /* Textfarbe = schwarz				  */
	work_in[7]  = 2;			   /* Flltyp							  */
	work_in[8]  = 8;			   /* Fllmuster-Index					  */
	work_in[9]  = WHITE;		   /* Fllmuster-Farbe					  */
	work_in[10] = 2;			   /* Rasterkoordinaten					  */

	v_opnvwk(work_in, &wkst_handle, work_out);	 /* Workstation ”ffnen	  */

	/* Anzahl der Bitplanes berechnen									  */

	nplanes = 0;
	while (work_out[13] >>= 1)
		nplanes++;

	/* gr”žte m”gliche Bildschirmkoordinaten speichern					  */

	scr_x = work_out[0];
	scr_y = work_out[1];

	/* verschieden Attribute der Workstation setzen						  */

	vswr_mode(wkst_handle, MD_REPLACE);
	vsf_interior(wkst_handle, FIS_PATTERN);
	vsf_style(wkst_handle, 4);
	vsf_color(wkst_handle, BLACK);
	vsf_perimeter(wkst_handle, OFF);
	
	/* Fenster mit Mindestgrže des Arbeitsbereichs von 600 * 300 Punkten  */
	/* ”ffnen															  */	

	wind_get(0, WF_WORKXYWH, &wox, &woy, &wow, &woh);
	wind_calc(WC_WORK, NAME | CLOSER | FULLER | MOVER, wox, woy, wow, woh,
													&wwx, &wwy, &www, &wwh);

	if ((www < 600) || (wwh < 300))
	{
		gem_exit();
		return(0);
	}

	/* Arbeitsfl„che so setzen, daž das gesamte Fenster maximal den Bild- */
	/* schirm bei TT-Medium-Aufl”sung berdeckt - es w„re Bl”dsinn auch	  */
	/* bei Grožbildschirmen allen Platz zu okkupieren ...				  */

	www = MIN(www, 638);
	wwh = MIN(wwh, 442);

	wind_calc(WC_BORDER, NAME | CLOSER | FULLER | MOVER, wwx, wwy, www, wwh,
													&wox, &woy, &wow, &woh);

	wind_handle = wind_create(NAME | CLOSER | FULLER | MOVER, wox, woy, wow,
																	   woh);
	if (wind_handle < 0)		 /* kein Fenster mehr da ? -> Abbruch ... */
	{
		gem_exit();
		return(0);
	}

	wind_set(wind_handle, WF_NAME, (int) ((long) title >> 16),
										 (int) ((long) title & 0x0000FFFF));

	if (!wind_open(wind_handle, wox, woy, wow, woh))
	{
		wind_delete(wind_handle);
		gem_exit();
		return(0);
	}

	return(1);
}


/*------------------------------------------------------------------------*/
/* gem_exit() schliežt die virtuelle Workstation und meldet die Applika-  */
/* tion ab.																  */
/*------------------------------------------------------------------------*/


void gem_exit()
{
	v_clsvwk(wkst_handle);						/* Workstation schliessen */
	appl_exit();								/* Applikation abmelden	  */
}


/*------------------------------------------------------------------------*/
/* make_cards()															  */
/*	Dient zum Herstellen der Bilder der Karten - die Bilder sind zum Teil */
/*	im File 'VIERFREI.DAT' gespeichert, werden von dort in den vorher	  */
/*	allozierten Speicherbereich eingelesen und nachbearbeitet.			  */
/*	Ist eine ziemliche schweinische Bitfiddelei, deshalb auch nicht	son-  */
/*	derlich gut kommentiert - 'tschuldigung ...							  */
/*------------------------------------------------------------------------*/


int make_cards(void)
{
	int i, j, k;
	int value;
	long *act_pic;
	long card_pic[1316];
	int *from, *to;
	unsigned long kl_farbe[28];
	unsigned long gr_farbe[60];
	long act_card;
	FILE *fp;


	/* Speicherplatz fr Bilder der Karten allozieren					  */

	if ((pic = (long *) Malloc(CARD_SIZE * (N + 6) * nplanes *
													 sizeof(long))) == NULL)
		return(0);

	/* File mit Daten fr Bilder der Karten ”ffnen						  */

	if ((fp = fopen("VIERFREI.DAT", "rb")) == NULL)
	{
		Mfree(pic);
		return(0);
	}

	/* Halbbilder der Karten einlesen									  */

	if (fread((void *) card_pic, sizeof(long), 1316, fp) != 1316)
	{
		Mfree(pic);
		return(0);
	}

	/* Muster fr kleine Darstellung von Kreuz, Pik, Herz und Karo ein-	  */
	/* lesen ...														  */

	if (fread((void *) kl_farbe, sizeof(long), 28, fp) != 28)
	{
		Mfree(pic);
		return(0);
	}

	/* ... und das selbe fr die Muster fr die grože Darstellung		  */

	if (fread((void *) gr_farbe, sizeof(long), 60, fp) != 60)
	{
		Mfree(pic);
		return(0);
	}

	fclose(fp);

	/* Bilder aller Karten aus den Daten erzeugen						  */

	act_pic = pic;
	for (k = 0; k < 52; k++, act_pic += CARD_SIZE)
	{

		value = k & 3;
		act_card = (k >> 2) * 94;
		for (i = 0; i < 94; i++)
			act_pic[i] = card_pic[act_card++];

		/* linke kleine Farbe nach act_pic								  */

		switch (k >> 2)
		{
			case 0 : case 1 : case 2 : case 3 : case 4 :
			case 5 : case 6 : case 7 : case 8 :
				for (i = 0; i < 7; i++)
					act_pic[6 + 2 * i] |= kl_farbe[value * 7 + i] >> 13;
				break;

			case 9 :
				for (i = 0; i < 7; i++)
					act_pic[6 + 2 * i] |= kl_farbe[value * 7 + i] >> 14;
				break;

			case 10 : case 11 : case 12 :
				for (i = 0; i < 7; i++)
					act_pic[6 + 2 * i] |= kl_farbe[value * 7 + i] >> 12;
				break;
		}

		/* rechte kleine Farbe nach act_pic								  */

		switch (k >> 2)
		{
			case 0 : case 1 : case 2 : case 3 : case 4 :
			case 5 : case 6 : case 7 : case 8 :
				for (i = 0; i < 7; i++)
					act_pic[7 + 2 * i] |= kl_farbe[value * 7 + i] >> 12;
				break;

			case 9 :
				for (i = 0; i < 7; i++)
					act_pic[7 + 2 * i] |= kl_farbe[value * 7 + i] >> 11;
				break;

			case 10 : case 11 : case 12 :
				for (i = 0; i < 7; i++)
					act_pic[23 + 2 * i] |= kl_farbe[value * 7 + i] >> 20;
				break;
		}

		/* grože Farbe (teilweise) nach act_pic							  */

		switch (k >> 2)
		{
			case 1 :												 /* 2 */
				for (i = 0; i < 15; i++)
				{
					act_pic[46 + 2 * i] |= gr_farbe[value * 15 + i] >> 25;
					act_pic[47 + 2 * i] |= gr_farbe[value * 15 + i] << 7;
				}
				break;

			case 2 :												 /* 3 */
				for (i = 0; i < 15; i++)
				{
					act_pic[40 + 2 * i] |= gr_farbe[value * 15 + i] >> 25;
					act_pic[41 + 2 * i] |= gr_farbe[value * 15 + i] << 7;
				}
				break;

			case 3 :												 /* 4 */
				for (i = 0; i < 15; i++)
				{
					act_pic[46 + 2 * i] |= gr_farbe[value * 15 + i] >> 13;
					act_pic[47 + 2 * i] |= gr_farbe[value * 15 + i] >> 4;
				}
				break;

			case 4 :												 /* 5 */
				for (i = 0; i < 15; i++)
				{
					act_pic[46 + 2 * i] |= gr_farbe[value * 15 + i] >> 13;
					act_pic[47 + 2 * i] |= gr_farbe[value * 15 + i] >> 4;
				}
				break;

			case 5 :												 /* 6 */
				for (i = 0; i < 15; i++)
				{
					act_pic[40 + 2 * i] |= gr_farbe[value * 15 + i] >> 13;
					act_pic[41 + 2 * i] |= gr_farbe[value * 15 + i] >> 4;
				}
				break;

			case 6 :												 /* 7 */
				for (i = 0; i < 15; i++)
				{
					act_pic[40 + 2 * i] |= gr_farbe[value * 15 + i] >> 14;
					act_pic[41 + 2 * i] |= gr_farbe[value * 15 + i] >> 3;
				}
				break;

			case 7 :												 /* 8 */
				for (i = 0; i < 15; i++)
				{
					act_pic[26 + 2 * i] |= gr_farbe[value * 15 + i] >> 14;
					act_pic[27 + 2 * i] |= gr_farbe[value * 15 + i] >> 4;
					act_pic[60 + 2 * i] |= gr_farbe[value * 15 + i] >> 14;
					act_pic[61 + 2 * i] |= gr_farbe[value * 15 + i] >> 4;
				}
				break;

			case 8 :												 /* 9 */
				for (i = 0; i < 15; i++)
				{
					act_pic[22 + 2 * i] |= gr_farbe[value * 15 + i] >> 13;
					act_pic[23 + 2 * i] |= gr_farbe[value * 15 + i] >> 4;
					act_pic[56 + 2 * i] |= gr_farbe[value * 15 + i] >> 13;
					act_pic[57 + 2 * i] |= gr_farbe[value * 15 + i] >> 4;
				}
				break;

			case 9 :												/* 10 */
				for (i = 0; i < 15; i++)
				{
					act_pic[22 + 2 * i] |= gr_farbe[value * 15 + i] >> 13;
					act_pic[23 + 2 * i] |= gr_farbe[value * 15 + i] >> 4;
					act_pic[62 + 2 * i] |= gr_farbe[value * 15 + i] >> 13;
					act_pic[63 + 2 * i] |= gr_farbe[value * 15 + i] >> 4;
					act_pic[42 + 2 * i] |= gr_farbe[value * 15 + i] >> 25;
					act_pic[43 + 2 * i] |= gr_farbe[value * 15 + i] << 7;
				}
				break;

			case 10 :											  /* Bube */
				for (i = 0; i < 15; i++)
					act_pic[20 + 2 * i] |= gr_farbe[value * 15 + i] >> 6;
				break;

			case 11 :											  /* Dame */
				for (i = 0; i < 15; i++)
					act_pic[18 + 2 * i] |= gr_farbe[value * 15 + i] >> 11;
				break;

			case 12 :											 /* K”nig */
				for (i = 0; i < 15; i++)
					act_pic[20 + 2 * i] |= gr_farbe[value * 15 + i] >> 12;
				break;
		}

		/* Karte punktspiegeln											  */

		for (i = 94, j = i - 3; i < 186; j -= 2, i += 2)
			reverse_card(act_pic + j, act_pic + i);

		/* fr einige Karten nochmal grože Farbe nach act_pic			  */

		switch (k >> 2)
		{
			case 0 : case 2 : case 4 : case 8 :			   /* As, 3, 5, 9 */
				for (i = 0; i < 15; i++)
				{
					act_pic[78 + 2 * i] |= gr_farbe[value * 15 + i] >> 25;
					act_pic[79 + 2 * i] |= gr_farbe[value * 15 + i] << 7;
				}
				break;

			case 5 :												 /* 6 */
				for (i = 0; i < 15; i++)
				{
					act_pic[78 + 2 * i] |= gr_farbe[value * 15 + i] >> 13;
					act_pic[79 + 2 * i] |= gr_farbe[value * 15 + i] >> 4;
				}
				break;

			case 6 :												 /* 7 */
				for (i = 0; i < 15; i++)
				{
					act_pic[78 + 2 * i] |= gr_farbe[value * 15 + i] >> 8;
					act_pic[79 + 2 * i] |= gr_farbe[value * 15 + i] >> 10;
					act_pic[78 + 2 * i] |= gr_farbe[value * 15 + i] >> 25;
					act_pic[79 + 2 * i] |= gr_farbe[value * 15 + i] << 7;
				}
				break;
		}
	}

	/* Nun die Karten mit nur den Farbsymbolen (fr die Endablage) sowie  */
	/* die leere Karte (fr die Zwischenablagen) vorbereiten			  */

	for (k = 0; k < 5; k++, act_pic += CARD_SIZE)
	{
		act_card = 1222L;
		for (i = 0; i < 94; i++)
			act_pic[i] = card_pic[act_card++];
		for (i = 94, j = i - 3; i < 186; j -= 2, i += 2)
			reverse_card(act_pic + j, act_pic + i);

		if (k == 4)
			continue;
		else
		{
			for (i = 0; i < 15; i++)
			{
				act_pic[78 + 2 * i] |= gr_farbe[k * 15 + i] >> 25;
				act_pic[79 + 2 * i] |= gr_farbe[k * 15 + i] << 7;
			}
		}
	}

	/* Maske fr die Karten erzeugen - 'per Hand', da ich vergessen hatte */
	/* sie in den Datenfile einzubauen ...								  */

	act_pic[0] = act_pic[184] = 0xFE000000L;
	act_pic[1] = act_pic[185] = 0x0000007FL;
	act_pic[2] = act_pic[182] = 0xF8000000L;
	act_pic[3] = act_pic[183] = 0x0000001FL;
	act_pic[4] = act_pic[180] = 0xF0000000L;
	act_pic[5] = act_pic[181] = 0x0000000FL;
	act_pic[6] = act_pic[8] = act_pic[176] = act_pic[178] = 0xE0000000L;
	act_pic[7] = act_pic[9] = act_pic[177] = act_pic[179] = 0x00000007L;

	for (i = 10; i < 176; )
	{
		act_pic[i++] = 0xC0000000L;
		act_pic[i++] = 0x00000003L;
	}

	if (nplanes == 1)
		return(1);

	/* Fr Aufl”sungen mit mehr als einer Bitplane (z. B. TT-Medium) die  */
	/* Bilder der Karten fr die Bitplanes im Speicher umkopieren		  */

	from = (int *) (pic + CARD_SIZE * (N + 6));
	to = (int *) (pic + CARD_SIZE * (N + 6) * nplanes);

	while (from-- != (int *) pic)
	{
		to -= nplanes;
		for (i = 0; i < nplanes; i++)
			*(to + i) = *from;
	}

	return(1);
}


/*------------------------------------------------------------------------*/
/* reverse_card()														  */
/*	Dient zum Spiegeln einer Zeile einer Karte.							  */
/* ->  Pointer auf Ausgangszeile und Pointer auf Zielzeile				  */
/*------------------------------------------------------------------------*/


void reverse_card(long *source, long *dest)
{
	int k;

	*dest = *(dest + 1) = 0L;
	for (k = 0; k < 32; k++)
	{
		*dest <<= 1;
		*dest |= (*source >> k) & 1;
		*(dest + 1) <<= 1;
		*(dest + 1) |= (*(source - 1) >> k) & 1;
	}
}


/*------------------------------------------------------------------------*/
/* select_game()														  */
/*	Dient zur Auswahl eines Spiels entsprechend einer zu editierenden	  */
/*	Zahl. Beim Editieren k”nnen BACKSPACE- und ESCAPE-Taste verwendet	  */
/*	werden.																  */
/* ->  Pointer auf String mit Title des Fensters						  */
/* <-  1 = neues Spiel starten, 0 = Abbruch des Editierens, nichts tun	  */
/*------------------------------------------------------------------------*/


int select_game(char *title)
{
	int mwhich,						/* Variablen fr evnt_multi()		  */
		mbuf[8],
		mox, moy,
		mbutton, mokstate,
		mkreturn, mbreturn;
	OBJECT *form;					/* Pointre auf Auswahlbox			  */
	long number;
	char *str,						/* zwei Pointer auf (oder in) den	  */
		 *help_str;					/* zu editierenden String			  */
	int cur_pos = 0,				/* Position des Cursors im String	  */
		max_len;					/* maximale L„nge des Strings		  */
	int x, y, w, h;					/* Koordinaten und Maže des Strings	  */
	int cur_line[4];				/* Coordinaten des Cursors			  */
	int cell_width,					/* Breite eines Buchstabens			  */
		dummy;						/* Dummy-Variable					  */
	int top_window;
	int quit_flag = OFF;			/* gesetzt bei Ende des Editierens	  */



	/* Adresse der Auswahlbox besorgen und ihre Position berechnen		  */

	rsrc_gaddr(0, GETNUM, &form);

	form->ob_x = MIN((scr_x + wwx - form->ob_width) / 2,
										  wwx + (www - form->ob_width) / 2);
	form->ob_y = MIN((scr_y  + wwy - form->ob_height) / 2,
										 wwy + (wwh - form->ob_height) / 2);

	/* Neuen Seed in die Zahl eintragen									  */

	number = (long) (seed * 1.E6);
	str = help_str = form[NUMBER].S_TEXT;
	ltoa(number, str, 10);

	/* Falls noch Stellen frei sind, diese mit '_'s auffllen			  */

	while (*help_str++ != '\0')
		cur_pos++;

	max_len = form[NUMBER].ob_spec.tedinfo->te_txtlen - 1;
	while (help_str < str + max_len)
		*help_str++ = '_';

	*--help_str = '\0';

	/* Auswahlbox zeichnen												  */

	objc_draw(form, 0, 1, MAX(form->ob_x - 5, 0), MAX(form->ob_y - 5, 0),
		 MIN(form->ob_width + 10, scr_x), MIN(form->ob_height + 10, scr_y));

	str += cur_pos - 1;

	/* Koordinaten und Maže des Strings besorgen und Clipping setzen	  */

	x = form->ob_x + form[NUMBER].ob_x;
	y = form->ob_y + form[NUMBER].ob_y;
	w = form[NUMBER].ob_width;
	h = form[NUMBER].ob_height;

	clip_array[0] = MAX(form->ob_x - 5, 0);
	clip_array[1] = MAX(form->ob_y - 5, 0);
	clip_array[2] = MIN(form->ob_x + form->ob_width + 4, scr_x);
	clip_array[3] = MIN(form->ob_y + form->ob_height + 4, scr_y);

	vs_clip(wkst_handle, ON, clip_array);

	/* Zum Schluž der Vorbereitungen den Cursor zeichnen				  */

	vqt_width(wkst_handle, ' ', &cell_width, &dummy, &dummy);

	cur_line[0] = cur_line[2] = x + cell_width * cur_pos;
	cur_line[1] = y;
	cur_line[3] = y + h - 1;

	v_hide_c(wkst_handle);
	v_pline(wkst_handle, 2, cur_line);
	v_show_c(wkst_handle, OFF);

	/* Tastaturbuffer leeren											  */

	while (Cconis())
		Crawcin();

	/* Es folgt die Loop, in der der String editiert wird (als Reaktion	  */
	/* entweder auf einen Mausklick oder einen Tastendruck), aužerdem	  */
	/* mssen Messages behandelt werden									  */

	do
	{
		mwhich = evnt_multi(MU_KEYBD | MU_BUTTON | MU_MESAG,
							1, 1, 1,
							0, 0, 0, 0, 0,
							0, 0, 0, 0, 0,
							mbuf, 0, 0, &mox, &moy, &mbutton,
							&mokstate, &mkreturn, &mbreturn);

	/* Messages auswerten ...											  */

		if (mwhich & MU_MESAG)
		{
			switch (mbuf[0])
			{

	/* Bei REDRAW-Message mssen sowohl das Spielfeld mit Karten als auch */
	/* das darberliegende Formular neu gezeichnet werden - wenn das ei-  */
	/* gene Fenster dabei das oberste wird, muž aužerdem sicherheitshal-  */
	/* ber der Cursor neu gezeichnet werden								  */

				case WM_REDRAW :
					wind_redraw(mbuf + 4, (OBJECT *) 0L);
					wind_redraw(mbuf + 4, form);

					wind_get(wind_handle, WF_TOP, &top_window);
					if (wind_handle == top_window)
					{
						v_hide_c(wkst_handle);
						v_pline(wkst_handle, 2, cur_line);
						v_show_c(wkst_handle, OFF);
					}
					break;

	/* Bei TOPPED-Message muž das Fenster neu gesetzt werden, REDRAW ge-  */
	/* schieht daraufhin automatisch, weil das AES von sich aus eine	  */
	/* REDRAW-Message schickt											  */

				case WM_TOPPED :		/* Fenster wurde oberstes Fenster */
					wind_set(wind_handle, WF_TOP);
					break;

	/* Bei Anklicken von Menueeintr„gen nur den Menuetitel wieder normal  */
	/* darstellen, sonst nicht darauf reagieren							  */	

				case MN_SELECTED :
					menu_tnormal(menu, mbuf[3], NORM);
					break;
			}
		}

	/* Reaktion auf Tastatur-Ereignisse ...								  */

		if (mwhich & MU_KEYBD)
		{
			switch (mkreturn & 0xFF)
			{

	/* bei ESC den gesamten String l”schen (d.h. alle Eintr„ge durch '_'  */
	/* ersetzen und den Cursor an den Anfang malen						  */

				case ESC :
					str = form[NUMBER].S_TEXT;
					while (*str)
						*str++ = '_';
					str = form[NUMBER].S_TEXT - 1;
					objc_draw(form, NUMBER, 0, x, y, w, h);

					v_hide_c(wkst_handle);
					if (cur_pos == max_len)
					{
						vsl_color(wkst_handle, WHITE);
						v_pline(wkst_handle, 2, cur_line);
						vsl_color(wkst_handle, BLACK);
					}
					cur_pos = 0;

					cur_line[0] = cur_line[2] = x;
					v_pline(wkst_handle, 2, cur_line);
					v_show_c(wkst_handle, OFF);
					break;

	/* bei BACKSPACE das links vom Cursor stehende Zeichen l”schen (d.h.  */
	/* durch '_' ersetzen) und den Cursor verschieben					  */

				case BACKSPACE :
					if (cur_pos == 0)			  /* wenn String leer ist */
						break;

					v_hide_c(wkst_handle);

					*str-- = '_';
					objc_draw(form, NUMBER, 0, x, y, w, h);

					if (cur_pos-- == max_len)
					{
						vsl_color(wkst_handle, WHITE);
						v_pline(wkst_handle, 2, cur_line);
						vsl_color(wkst_handle, BLACK);
					}

					cur_line[0] = cur_line[2] -= cell_width;
					v_pline(wkst_handle, 2, cur_line);
					v_show_c(wkst_handle, OFF);
					break;

	/* Bei RETURN oder ENTER beenden des Editierens indem das Anklicken	  */
	/* von 'Fertig' simuliert wird										  */

				case RETURN :
					mwhich |= MU_BUTTON;
					objc_offset(form, NREADY, &mox, &moy);
					break;

	/* Neue Zeichen werden nur akzeptiert, wenn es sich um Zahlen handelt */
	/* und der String nicht bereits voll ist							  */

				default :
					if (!isdigit(mkreturn & 0xFF) || (cur_pos == max_len))
						break;

					*++str = mkreturn & 0xFF;
					objc_draw(form, NUMBER, 0, x, y, w, h);

					cur_pos++;
					cur_line[0] = cur_line[2] += cell_width;
					v_pline(wkst_handle, 2, cur_line);
					v_show_c(wkst_handle, OFF);
					break;
			}
		}

	/* Reaktion auf Mausklicks											  */

		if (mwhich & MU_BUTTON)
		{
			/* Testen, welcher Button angeklickt wurde und entsprechend	  */
			/* verzweigen												  */

			switch (objc_find(form, 0, 1, mox, moy))
			{
				case NREADY :									/* Fertig */
					quit_flag = 1;
					break;

				case QUIT :									   /* Abbruch */
					quit_flag = -1;
					break;
			}
		}

	} while (!quit_flag);

	/* Cursor l”schen													  */

	if (cur_pos == max_len)
	{
		v_hide_c(wkst_handle);
		vsl_color(wkst_handle, WHITE);
		v_pline(wkst_handle, 2, cur_line);
		vsl_color(wkst_handle, BLACK);
		v_show_c(wkst_handle, OFF);
	}

	/* Bei Abbruch Auswahlbox l”schen und fertig						  */

	if (quit_flag == -1)
	{
		show_all(OFF);
		return(0);
	}

	/* Sonst editierte Zahl auswerten und Seed entsprechend setzen		  */

	help_str = form[NUMBER].S_TEXT;
	while (isdigit(*help_str))
		help_str++;

	*help_str = '\0';

	old_seed = seed = ((double) atol(form[NUMBER].S_TEXT) * 1.E-6);
	set_wind_header(title);

	return(1);
}


/*------------------------------------------------------------------------*/
/* wind_redraw()														  */
/*	Dient sowohl zur Wiederherstellung des Fensterinhaltes als auch von	  */
/*	Objekten, wenn eine REDRAW-Message eingegangen ist.					  */
/* ->  Pointer auf Array mit Mažen des wiederherzustellenden Rechtecks	  */
/*	   sowie Pointer auf wiederherzustellendes Objekt (wenn dieser NULL	  */
/*	   ist, muž ein Fenster neu gezeichnet werden, sonst ein Objekt)	  */
/*------------------------------------------------------------------------*/


void wind_redraw(int *rr, OBJECT *wind)
{
	int ar[4];			/* Maže des aktuell neu zu zeichnenden Rechtecks  */


	/* AES-Aktionen auf dem Bildschirm unterbinden						  */

	wind_update(BEG_UPDATE);

	/* Maže des ersten neu zu zeichnenden Rechtecks aus der Rechteckliste */
	/* besorgen															  */

	wind_get(wind_handle, WF_FIRSTXYWH, ar, ar + 1, ar + 2, ar + 3);

	/* Neu zeichnen, solange wiederholen bis H”he oder Breite des n„ch-	  */
	/* sten wiederherzustellenden Rechtecks ungleich Null ist			  */	

	while (ar[2] || ar[3])
	{

	/* Wenn das aktuelle Rechteck sich mit dem gesamten wiederherzustel-  */
	/* lenden Rechteck berschneidet, dieses neu zeichenen				  */

		if (intersect(rr, ar))
		{
			clip_array[0] = MAX(ar[0], 0);
			clip_array[1] = MAX(ar[1], 0);
			clip_array[2] = MIN(ar[0] + ar[2] - 1, scr_x);
			clip_array[3] = MIN(ar[1] + ar[3] - 1, scr_y);
			vs_clip(wkst_handle, ON, clip_array);

	/* Je nach Wert des Objektpointers Fenster oder Formular neu zeichnen */

			if (wind == (OBJECT *) 0L)
				show_all(OFF);
			else
				objc_draw(wind, R_TREE, 1, ar[0], ar[1], ar[2], ar[3]);
		}

	/* Und Maže des n„chsten neu zu zeichnenden Rechtecks holen			  */

		wind_get(wind_handle, WF_NEXTXYWH, ar, ar + 1, ar + 2, ar + 3);
	}

	/* AES-Aktionen auf dem Bildschirm wieder zulassen					  */

	wind_update(END_UPDATE);
}


/*------------------------------------------------------------------------*/
/* intersect()															  */
/*	Stellt fest, ob sich zwei Rechtecke berschneiden und gibt die Maže	  */
/*	des šberschneidungsbereich im zweiten bergebenen Array zurck.		  */
/*	(Entsprechend 'ATARI Profibuch' von Jankowski/Rabich/Reschke)		  */
/* ->  Pointer auf Array's mit den Mažen zweier Rechtecke				  */
/* <-  1 = Rechtecke berschneiden sich, 0 = sie berschneiden sich nicht */
/*------------------------------------------------------------------------*/


int intersect(int *rr, int *ar)
{
	int ix, iy, iw, ih;


	/* Positionen des linken oberen und rechten unteren Punktes der		  */
	/* Schnittfl„che bestimmen											  */

	ix = MAX(rr[0], ar[0]);
	iy = MAX(rr[1], ar[1]);
	iw = MIN(rr[0] + rr[2], ar[0] + ar[2]);
	ih = MIN(rr[1] + rr[3], ar[1] + ar[3]);

	/* Daraus Position und Maže der Schittfl„che berechnen (und in den	  */
	/* zweiten bergebenen Array eintragen)								  */

	ar[0] = ix;
	ar[1] = iy;
	ar[2] = iw - ix;
	ar[3] = ih - iy;

	return((iw > ix) && (ih > iy));
}


/*------------------------------------------------------------------------*/
/* move_window()														  */
/*	Dient zur Ausfhrung aller notwendigen Aktionen beim Verschieben des  */
/*	Fensters. Der zus„tzliche bergebene Objektpointer ist nur beim Ver-  */
/*	schieben des auf kleinste Maže geschrumpften Fensters von Bedeutung,  */
/*	da dessen Inhalt durch ein Objekt gegeben ist.						  */
/* ->  neue x- und y-Position des Fensters sowie Objekt-Pointer			  */
/*------------------------------------------------------------------------*/


void move_window(int x, int y, OBJECT *wind)
{
	int del_x, del_y;
	OBJECT *ob;


	/* Gr”že der Verschiebung berechnen									  */

	del_x = x - wox;
	del_y = y - woy;

	/* Neue Position der linken oberen Ecke des Fensterinneren bestimmen  */

	wwx += del_x;
	wwy += del_y;

	/* Neue x- und y-Position des Fenster„užeren setzen					  */

	wox = x;
	woy = y;

	wind_set(wind_handle, WF_CURRXYWH, wox, woy, wow, woh);

	/* Objekt fr Fensterinneres bei kleinstm”glichem Fenster setzen	  */

	if (wind != (OBJECT *) 0L)
	{
		wind->ob_x = wwx;
		wind->ob_y = wwy;
	}

	/* Wenn die Verschiebung nur zu gr”žeren x- oder y-Werten erfolgt	  */
	/* durch Neuzeichnen des Fensters die Positionen der Karten usw.	  */
	/* neu berechnen (andernfalls erfolt dies bei der sonst automatisch	  */
	/* folgenden REDRAW-Message)										  */

	if (((del_x > 0) && (del_y >= 0)) || ((del_x >= 0) && (del_y > 0)))
	{
		if (wind == (OBJECT *) 0L)
			show_all(ON);
	}

	/* Nun noch bei nicht verkleinertem Fenster untersuchen ob die Boxen  */
	/* fr Starten eines Spiels entsprechend einer einzugebenden Zahl	  */
	/* bzw. mit Informationen ber das Spiel noch in das verschobene Fen- */
	/* ster passen und die zugeh”rigen Menueeintr„ge entsprechend setzen  */

	if (wind != (OBJECT *) 0L)
		return;

	rsrc_gaddr(R_TREE, GETNUM, &ob);
	if ((ob->ob_width + 10 > scr_x - wwx) ||
										 (ob->ob_height + 10 > scr_y - wwy))
		menu_ienable(menu, T1E1, OFF);
	else
		menu_ienable(menu, T1E1, ON);

	rsrc_gaddr(R_TREE, ABOUT, &ob);
	if ((ob->ob_width + 10 > scr_x - wwx) ||
										 (ob->ob_height + 10 > scr_y - wwy))
		menu_ienable(menu, T0E0, OFF);
	else
		menu_ienable(menu, T0E0, ON);
}


/*------------------------------------------------------------------------*/
/* resize_window()														  */
/*	Dient zum Verkleinern des Fensters und aller nachfolgenden Aktionen,  */
/*	bis das Fenster schliežlich wieder auf die ursprngliche Gr”že ge-	  */
/*	bracht wird.														  */
/* ->  Pointer auf normalen String mit Fenstertitel						  */
/* <-  1 = Spiel sofort beenden, 0 = Spiel normal fortsetzen			  */
/*------------------------------------------------------------------------*/


int resize_window(char *title)
{
	OBJECT *wind;					/* Pointer auf Objekt mit Inhalt des  */
									/* verkleinerten Fensters			  */
	int owow, owoh;					/* ursprngliche Maže des Fensters	  */
	char *new_title = "";			/* Titel des verleinerten Fensters	  */
	int mbuf[8];					/* Buffer fr Messages				  */


	/* Adresse des Objekts mit Inhalt des verkleinerten Fensters besorgen */

	rsrc_gaddr(R_TREE, WIND, &wind);

	/* Neue Maže des Fensters berechnen und Fenster entsprechend setzen	  */

	owow = wow;
	owoh = woh;
	wind_calc(WC_BORDER, CLOSER | FULLER | MOVER, wwx, wwy,
				   wind->ob_width, wind->ob_height, &wox, &woy, &wow, &woh);
	wind_set(wind_handle, WF_NAME, (int) ((long) new_title >> 16),
									 (int) ((long) new_title & 0x0000FFFF));
	wind_set(wind_handle, WF_KIND, CLOSER | FULLER | MOVER);
	wind_set(wind_handle, WF_CURRXYWH, wox, woy, wow, woh);

	/* Objekt mit Inhalt des verkleinerten Fensters zeichnen			  */

	wind->ob_x = wwx;
	wind->ob_y = wwy;
	objc_draw(wind, R_TREE, 1, wwx, wwy, wind->ob_width, wind->ob_height);

	/* Verschieden Menueeintr„ge disablen								  */

	menu_ienable(menu, T0E0, OFF);
	menu_ienable(menu, T1E0, OFF);
	menu_ienable(menu, T1E1, OFF);
	menu_ienable(menu, T1E2, OFF);
	menu_ienable(menu, T1E3, OFF);
	menu_ienable(menu, T1E4, OFF);

	/* Nun auf Messages warten, bis das Fenster wieder vergr”žert wird	  */

	do
	{
		evnt_mesag(mbuf);

		switch (mbuf[0])
		{
			case WM_REDRAW :				  /* Redraw-Message empfangen */
				wind_redraw(mbuf + 4, wind);
				break;

			case WM_MOVED :					  /* Fenster wurde verschoben */
				move_window(mbuf[4], mbuf[5], wind);
				break;

			case WM_TOPPED :			/* Fenster wurde oberstes Fenster */
				wind_set(wind_handle, WF_TOP);
				break;

			case WM_CLOSED :				/* CLOSE-Box wurde angeklickt */
				return(1);

			case MN_SELECTED :				   /* Menueeintrag angeklickt */
				if ((mbuf[3] == T1) && (mbuf[4] == T1E5))
					return(1);							 /* Spiel beenden */
				else
					menu_tnormal(menu, mbuf[3], NORM);
				break;
		}

	} while (mbuf[0] != WM_FULLED);

	/* Disablete Menueeintr„ge wieder enablen							  */

	menu_ienable(menu, T0E0, ON);
	menu_ienable(menu, T1E0, ON);
	menu_ienable(menu, T1E1, ON);
	menu_ienable(menu, T1E2, ON);
	menu_ienable(menu, T1E3, ON);
	menu_ienable(menu, T1E4, ON);

	/* Fenster wieder auf ursprngliche Gr”že bringen					  */

	wow = owow;
	woh = owoh;
	wind_set(wind_handle, WF_NAME, (int) ((long) title >> 16),
										 (int) ((long) title & 0x0000FFFF));
	wind_set(wind_handle, WF_KIND, NAME | CLOSER | FULLER | MOVER);
	wind_set(wind_handle, WF_CURRXYWH, wox, woy, wow, woh);

	return(0);
}


/*------------------------------------------------------------------------*/
/* i_to_a()																  */
/*	Wandelt eine positive Integerzahl rechtsbndig in einen String mit	  */
/*	festgelegter L„nge ein.												  */
/* ->  Pointer auf String fr die Zahl, zu wandelnde Zahl sowie L„nge des */
/*	   Strings															  */
/*------------------------------------------------------------------------*/


void i_to_a(char *s, long number, int n)
{
	*(s + n--) = '\0';					 /* Nullbyte ans Ende des Strings */
	
	for ( ; n >= 0; number /= 10) 				 /* String von hinten mit */
		*(s + n--) = number % 10 + '0';					/* Ziffern fllen */
	while (*s == '0')							 /* fhrende Nullen durch */
		*s++ = ' ';								   /* Leerzechen ersetzen */
}