#define DEBUGPRINTF

#include "defs.h"
#include "noctis-d.h"
#include "noctis-0.h"
#include <math.h>
#include <stdarg.h>	//for DebugPrintf and RunCommand (SL)
#define NOCTIS2CPP
#include "noctis-2.h"
#undef NOCTIS2CPP
#ifdef WINDOWS
#include "randalg.h"
#include "win.h"
#include <assert.h>
#include "noctis-ng.h"
#endif
#include <errno.h>
int nzi;
extern Bool ape_zombie;	//from noctis-1
extern Bool ape_stargazer; //from noctis-1
#ifdef WINDOWS
clock_t last_ticks = 0;
double clk_convert = CLK_TCK/18.0;
#endif

const char * r9aBugMessages = "                     UH-OH! YOU'VE GOT THE R9A BUG OR ARE AT AN IMAGINARY SYSTEM. PLEASE RE-TARGET THIS SYSTEM AND THEN RE-VIMANA TO IT - THEN THIS MESSAGE SHOULD STOP. IF THAT DOESN'T WORK, THEN VIMANA TO ANOTHER SYSTEM - THIS ONE MAY BE 'IMAGINARY'!                       P.S. YOU APPEAR TO HAVE HAD TOO MUCH CATNIP!                     ";
char r9aBugMessageBuffer[30];

Dword r9aBugCounter = 5;
Dword r9aBugMessageNum = -1;
Bool r9aBugMessageEnabled = False;

void enableR9aBugMessage() {
	r9aBugMessageEnabled = True;
}
void disableR9aBugMessage() {
	if (r9aBugMessageEnabled) {
		r9aBugMessageEnabled = False;
		status("OK, FIXED :)", 202);
	}
}
void r9aBugMessage() {
	static Dword r9aBugMessagesLen = strlen(r9aBugMessages);
	if (r9aBugMessageEnabled==True) {
		r9aBugCounter++;
		if (r9aBugCounter>=5) {
			r9aBugMessageNum++;
			r9aBugCounter=0;
			if (r9aBugMessageNum>r9aBugMessagesLen) r9aBugMessageNum=0;
			Dword diff = r9aBugMessagesLen-r9aBugMessageNum;
			if (diff>=20) {
				memcpy(r9aBugMessageBuffer, &(r9aBugMessages[r9aBugMessageNum]), 20);
			} else {
				memcpy(r9aBugMessageBuffer, &(r9aBugMessages[r9aBugMessageNum]), diff);
			}
			r9aBugMessageBuffer[20]=0;
			status (r9aBugMessageBuffer, 202);
		}
	}
}

char *comm = "..\\DATA\\COMM.BIN";	// File di comunicazione dei moduli.
Word lastXFps=-1;		//holds the fraction-of-a-second of the last x command.
double lastXSecs=-1;	//holds the seconds of the last x command.
double timeAdjustment = 0;	//Also used by the irc-server-time code (SL):
char dontDraw=0;
extern char blinkhudlights;
extern char blinkhudlights_stay;
extern int movieexists;
extern char movie;
char terrainTypeBuffer[13];
Word secspassed=0, prevsecs=0;
double speedCentiDyamsPerS, speedMetersPerS, speedPC, speedWarp;
Dword treeDensity;
Word maxDepth;

/*
XMS stuff for testing. (SL)
int xms_objectschart;
int xms_p_background;
int xms_s_background;
*/

/*
//UTC time stuff (SL)
//It doesn't work, alas.
int useUTC;
unsigned long secs1984[2];
unsigned long timebuff[2];
unsigned far long *secs1984Low = &secs1984[0];
unsigned far long *timebuffLow = &timebuff[0];
*/
//IRC-server time stuff (SL)
//double timeAdjustment = 0;

//int lastXFps = -1;	//This is used to determine when to call X again if x.txt hasn't been deleted.
//double lastXSecs = -1;

/*
//An experiment which does not work (SL)
void from_both ()
{
	cam_x = dzat_x+pos_x; cam_y = dzat_y+pos_y; cam_z = dzat_z+pos_z;
	alfa = user_alfa; beta = user_beta + navigation_beta + 180;
	if (beta>=360) beta -= 360;
	change_angle_of_view ();
}
*/

/*double DebugSqrt(double x, char maybefar * filename, int line) {
	double result = sqrt(x);
	//if (IsNaN((long double)result)) {
	//	DebugPrintf(0, "SQRT(double) returned NaN. (%s, line %i, x=%f)\n", filename, line, x);
	//}
	return result;
}*/

extern float starmass_correction[star_classes];
//Methods used for & by the new temperature code (SL)
double GetStarMass(Word starClass, float starRay, double starX) {
	double tmp_float = 1e-3 * qt_M_PI * starRay * starRay * starRay;
	tmp_float *= starmass_correction[starClass];
	if (starClass == 8 || starClass == 9) {
		fast_srand ((Dword)starX%32000);
		switch (fast_random(5)) {
			case 0: tmp_float /= 1 + 5 * fast_flandom(); break;
			case 1: tmp_float /= 1 + fast_flandom(); break;
			case 2: tmp_float *= 1 + fast_flandom(); break;
			case 3: tmp_float *= 1 + 20 * fast_flandom(); break;
			case 4: tmp_float *= 1 + 50 * fast_flandom();
		}
	}
	return tmp_float;
}

#ifndef WINDOWS
#pragma argsused
#endif
double GetStarSurfaceTemperature(Word starClass, float starRay, double starMass) {
	//Old method:
	#if defined(ALL)
	if (option_temperature==0) {
	#endif
	#if defined(ALL) || !defined(TEMPERATURE)
		double tmp_float = starMass / (0.38e-4 * starRay);
		if (starClass == 6) tmp_float *= 0.0022;
		return tmp_float;
	#endif
	#if defined(ALL)
	} else {
	#endif
	#if defined(ALL) || defined(TEMPERATURE)
		//New method:
		return (starMass * class_surfacetemp_mult[starClass]) + class_surfacetemp_add[starClass];
	#endif
	#if defined(ALL)
	}
	#endif
}

extern double cur_eclipse;

#if defined(ALL) || defined(TEMPERATURE)
void PrecalcTemperatureTransmission(double ray, double surfaceTemp) {
	double surfaceTempK = surfaceTemp + 273.15;
	//double rayMeters = ray * CentidyamsToMeters;
	//nearstar_temperatureTransmission = (StefanBoltzmanConstant*rayMeters*rayMeters*surfaceTempK*surfaceTempK*surfaceTempK*surfaceTempK);
	nearstar_temperatureTransmission = (StefanBoltzmanConstant*ray*ray*surfaceTempK*surfaceTempK*surfaceTempK*surfaceTempK);
}

double GetRawTemperatureAt(double distance) {
	if (nearstar_mass==-1) {
		nearstar_mass = GetStarMass(nearstar_class, nearstar_ray, nearstar_x);
	}
	if (nearstar_surfaceTemperature==-1) {
		nearstar_surfaceTemperature = GetStarSurfaceTemperature(nearstar_class, nearstar_ray, nearstar_mass);
	}
	if (nearstar_temperatureTransmission==-1) {
		PrecalcTemperatureTransmission(nearstar_ray, nearstar_surfaceTemperature);
	}
	//double distanceMeters = distance * CentidyamsToMeters;
	double brightness=0;
	//brightness = (nearstar_temperatureTransmission/(distanceMeters*distanceMeters))*WattsPerMeterSquaredToKelvin;
	brightness = (nearstar_temperatureTransmission/(distance*distance))*WattsPerMeterSquaredToKelvin;
	brightness -= brightness*cur_eclipse;
	
	for (int n=0; n<nearstar_nob; n++) {
		if (nearstar_p_type[n]==10 || nearstar_p_type[n]==9) {
			double body_mass, body_surfaceTemperature, tmp_nearstar_temperatureTransmission;
			Word starType = (nearstar_p_type[n]==10)?8:5;	//8 is the varying-size (multiple-star-system) star type, 5 is a substellar object.
			planet_xyz (n);
			double xx = plx - dzat_x;
			double yy = ply - dzat_y;
			double zz = plz - dzat_z;
			double pdistance = SQRT (xx*xx+yy*yy+zz*zz);
			
			tmp_nearstar_temperatureTransmission=nearstar_temperatureTransmission;
			body_mass = GetStarMass(starType, nearstar_p_ray[n], nearstar_p_orb_ray[n]);	//8 is the multiple-star system type.
			body_surfaceTemperature = GetStarSurfaceTemperature(starType, nearstar_p_ray[n], body_mass);
			PrecalcTemperatureTransmission(nearstar_p_ray[n], body_surfaceTemperature);
			//Calculate pdistance
			double sbrightness = (nearstar_temperatureTransmission/(pdistance*pdistance))*WattsPerMeterSquaredToKelvin;

			double sunX = plx;
			double sunY = ply;
			double sunZ = plz;
			
			double ras = (105 * nearstar_p_ray[n]) / dsd;
			if (ras>66) ras = 66;
			if (ras<1) ras = 1;
			for (Word c=0; c<nearstar_nob; c++) {
				if (c!=n && nearstar_p_type[c]!=-1) {
					planet_xyz (c);
					double dxx = dzat_x - plx;
					double dyy = dzat_y - ply;
					double dzz = dzat_z - plz;
					double dpz = SQRT (dxx*dxx + dyy*dyy + dzz*dzz) + 0.001;
					if (dpz<10*nearstar_p_ray[c]) {
						watch (dzat_x, dzat_y, dzat_z,
							   sunX, sunY, sunZ);
						//change_angle_of_view ();
						if (xy (dzat_x, dzat_y, dzat_z, plx, ply, plz)) {
							double dasp = SQRT (delta_x * delta_x + delta_y * delta_y);
							double rap = (105 * nearstar_p_ray[c]) / dpz;
							if (rap>66) rap = 66;
							double eclipse = (dasp + ras - rap) / (2 * ras);
							if (eclipse>1) eclipse = 1;
							if (eclipse<0) eclipse = 0;
							eclipse = 1 - eclipse;
							sbrightness-=(sbrightness*eclipse);
						}
					}
				}
			}
			
			brightness+=sbrightness;
			nearstar_temperatureTransmission=tmp_nearstar_temperatureTransmission;
		}
	}
	
	return brightness - 273.15;
}
#endif

double GetOrbitingTemperatureAt(double distance) {
	fast_srand (secs/2);
	double tmp_float = 0;
	#if defined(ALL)
	if (option_temperature==0) {
	#endif
	#if defined(ALL) || !defined(TEMPERATURE)
		tmp_float = 16 - distance * 0.044;
		tmp_float *= fabs(tmp_float);
		tmp_float -= (tmp_float + 273.15) * cur_eclipse;
	#endif
	#if defined(ALL)
	} else if (option_temperature==1) {
	#endif
	#if defined(ALL) || defined(TEMPERATURE)
		tmp_float = GetRawTemperatureAt(distance);
	#endif
	#if defined(ALL)
	}
	#endif
	if (nearstar_x==0) {
		tmp_float=-280;
	}
	return tmp_float;
}


void RecordXTime() {
	FILE * xfile = fopen(x_txt,"rt");
	if (xfile!=NULL) {
		lastXFps = fps;
		lastXSecs = secs-timeAdjustment;	//we want the raw time, not adjusted time. This is in case the adjusted time is set AFTER X is run, which it most assuredly will be :P. (That wouldn't necessarily cause a problem, but one would be possible in certain situations, ergo, we do this to prevent it from screwing up) (SL)
		fclose(xfile);
	}
}

char commandStringBuffer[256];
extern char goesnet_command[];
extern char gnc_pos;
extern void ReadCommFile(FastBool deleteIt);
extern char force_update;
extern Dword goesfile_pos;
extern void freeze();
//This runs a GOES command without updating the GOES output screen or causing any visual glitches. It's used to send CTCPs to X for hacirc and
//to run X when it needs to take something out of its buffer.
//	Alternately we could embed the functionality of X into NIV, which would be much faster than the way X is being used now...
//(SL)

//If format is NULL the command is run as a normal command, otherwise it is run silent.
void __cdecl RunCommand(const char * format, ...) {
	char *stringBuffer = (format==NULL?goesnet_command:commandStringBuffer);
	FILEPTR ch;
	Uword bqw = QUADWORDS;
	char isX=0;
	int retval=0;
	FILE * chf = NULL;
	// Salva la situazione perch‚ alcuni moduli ne hanno bisogno.
	freeze ();

	// Libera circa 60 kilobytes per il lancio del modulo eseguibile.
	#ifndef WINDOWS
	FARFREE (adapted);
	#else
	HANDLE goesfileHandle = NULL;
	#endif
	
	if (format==NULL) {
		if (!memcmp (stringBuffer, "CLR", 3)) {
			remove (goesoutputfile);
			goto solong;
		}
		ch = CREATEFILE (goesoutputfile);
		if (ch ISFILEVALID) {
			WRITEFILE (ch, "(UNKNOWN MODULE)", 16);
			CLOSEFILE (ch);
		}
		
		// Cancella l'ultimo carattere (che Š il _ cursore) dalla command
		// line, poi aggiunge la redirezione sul file "goesfile.txt"
		//Remember: goesnet_command is pointed to by stringBuffer if format is NULL. So this is legal. (SL)
		goesnet_command[gnc_pos] = 0;
		#ifdef WINDOWS
		int converted = ConvertCommand(goesnet_command, commandStringBuffer);
		if (converted!=0) {
			fprintf(stderr, "Converted command '%s' to '%s'\n", goesnet_command, commandStringBuffer);
			stringBuffer = commandStringBuffer;
		}
		#endif
		strcat (stringBuffer, " >");
		strcat (stringBuffer, goesoutputfile);
	} else {
		// Verifica comandi residenti.
		// Cancella l'ultimo carattere (che Š il _ cursore) dalla command
		// line, poi aggiunge la redirezione sul file "goesfile.txt"
		va_list argptr;
		va_start(argptr, format);
		vsprintf(stringBuffer, format, argptr);
		va_end(argptr);
		
		//#ifndef WINDOWS
		strcat (stringBuffer, " > nul");
		//#endif
	}
	isX = (stringBuffer[0]=='x' || stringBuffer[0]=='X') && (stringBuffer[1]==0 || stringBuffer[1]==' ');
	#ifdef WINDOWS
	#ifdef DEBUG
	printf("calling (%s), then reading (%s)\n", stringBuffer, goesoutputfile);
	#endif
	#endif
	/*
	#ifdef WINDOWS
	STARTUPINFO startupinfo;
	ZeroMemory(&startupinfo, sizeof(startupinfo));
	startupinfo.cb = sizeof(STARTUPINFO);
	//startupinfo.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
  	
	//goesfileHandle = CreateFile("..\\DATA\\GOESFILE.TXT", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	//startupinfo.hStdInput = stdin;
  	//startupinfo.hStdOutput = goesfileHandle;
  	//startupinfo.hStdError = goesfileHandle;
	PROCESS_INFORMATION processinfo;
	
	char stringBuffer2[1024];
	sprintf(stringBuffer2, "c:\\noctis\\modules\\%s.exe", stringBuffer);
		
	//printf("precall errno is %i\n", errno);
	retval = CreateProcess("cmd", stringBuffer2, NULL, NULL, FALSE, CREATE_NO_WINDOW|NORMAL_PRIORITY_CLASS, NULL, NULL, &startupinfo, &processinfo);
	if (retval==0) {
		WaitForInputIdle(processinfo.hProcess, INFINITE);
		//Sleep(5);
		unsigned long exitCode = 0;
		Dword result = GetExitCodeProcess(processinfo.hProcess, &exitCode);
		fprintf(stderr, "Result is %i and exitCode is %lu.\n", result, exitCode);
		while (exitCode==STILL_ACTIVE) {
			Sleep(5);
			result = GetExitCodeProcess(processinfo.hProcess, &exitCode);
			fprintf(stderr, "Result is %i and exitCode is %lu.\n", result, exitCode);
			Dword errorCode = GetLastError();
			TCHAR buff[1024];
			Dword fmretval = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorCode, 0, buff, 1024, NULL);
			fprintf(stderr, "Error from GetExitCodeProcess: Code(%i), Translation(%s)", errorCode, buff);
			//fwprintf(stderr, buff);
		}
		CloseHandle(processinfo.hProcess);
		CloseHandle(processinfo.hThread);
	} else {
		Dword errorCode = GetLastError();
		TCHAR buff[1024];
		Dword fmretval = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorCode, 0, buff, 1024, NULL);
		fprintf(stderr, "Error from CreateProcess: Code(%i), Translation(%s)", errorCode, buff);
		//fwprintf(stderr, buff);
	}
	//CloseHandle(goesfileHandle);
	#else*/
	retval = system (stringBuffer);
	if (retval==-1) {	//error
		chf = fopen (goesoutputfile, "wt");
		if (chf != NULL) {
			if (errno==E2BIG) {
				sprintf(commandStringBuffer, "Argument list is too big.\n");
				strupr(commandStringBuffer); fprintf(chf, commandStringBuffer);
			} else if (errno==ENOENT) {
				sprintf(commandStringBuffer, "Could not find command interpreter.\n");
				strupr(commandStringBuffer); fprintf(chf, commandStringBuffer);
			} else if (errno==ENOEXEC) {
				sprintf(commandStringBuffer, "Command-interpreter file has invalid format and is not executable.\n");
				strupr(commandStringBuffer); fprintf(chf, commandStringBuffer);
			} else if (errno==ENOMEM) {
				sprintf(commandStringBuffer, "Not enough memory is available to execute command; or available memory has been corrupted; or invalid block exists, indicating that process making call was not allocated properly.\n");
				strupr(commandStringBuffer); fprintf(chf, commandStringBuffer);
			} else {
				sprintf(commandStringBuffer, "System call didn't work, but we don't know why. Error number is %i.\n", errno);
				strupr(commandStringBuffer); fprintf(chf, commandStringBuffer);
			}
			fclose(chf);
		}

	}
	//#endif
	#ifdef WINDOWS
	#ifdef DEBUG
	printf("retval is %i, errno is %i\n", retval, errno);
	#endif
	#endif
	
	// Ri-alloca l'area temporaneamente liberata.
solong:	
	#ifndef WINDOWS
	adapted = (Uchar far  *) FARMALLOC (sc_bytes, adapted);
	if (!adapted) {
		_80_25_C ();
		printf ("Sorry, GOES Net crashed.\n");
		printf ("System integrity compromised: any key to quit.\n\n");
		attendi_pressione_tasto();
		exit (0xFF);
	} else if (format==NULL) {
		QUADWORDS = 80 * 10;
		pclear (adaptor, 0);
		QUADWORDS = 16000 - 80 * 12;
		pcopy (adapted + 2*320, adaptor);
		// Reagisce alla presenza di dati nel file di comunicazione.

	} else {
		QUADWORDS = 16000;
		pcopy(adapted, adaptor);
	}
	#endif
	if (format==NULL) {
		ReadCommFile(0);
		force_update = 1;
		goesfile_pos = 0;
	}
	QUADWORDS = bqw;
		
	if (isX) {
		RecordXTime();
	}
}


/*
//The UTC code behaves bizarrely. Change useUTC to 1 and uncomment it (and the other commented section; just search for useUTC to find it) if you want to see what happens. (SL)
void TestForUTC() {
	secs1984[0]=0;
	secs1984[1]=0;
	useUTC=0;
	if (useUTC) {
		asm {
			mov dx, 0x0821	//Jan 1, 1984
			xor cx, cx
			//dx now holds the packed date
			//cx now holds the packed time
			xor bh, bh
			mov bl, 1
			mov ax, 0x71a7
			push es
			les di, dword ptr secs1984Low
			int 0x21
			pop es
			//mov retval, ax
			
			//64-bit divide by 100
			//[timebuff] is the low bytes, [timebuff+4] the high bytes
		}
		//unsigned long sb0 = secs1984[0];
		//unsigned long sb1 = secs1984[1];
		//unsigned long rem0 = 0;
		//unsigned long rem4 = 0;
		_EDX = 0;
		_EAX = secs1984[1];
		_EBX = 10000000;
		asm { db 0x66; div bx }
		//rem4 = _EDX;
		secs1984[1]=_EAX;
		_EAX=secs1984[0];
		_EBX = 10000000;
		asm { db 0x66; div bx }
		//rem0 = _EDX;
		secs1984[0]=_EAX;

		//DebugPrintf("Numbers are (Low, High). Before divide, secs1984 is (%lx,%lx). After divide it is (%lx,%lx). Remainders:(%lx,%lx)", sb0, sb1, secs1984[0], secs1984[1], rem0, rem4);
	} else {
		noutc:
		//DebugPrintf("No UTC: %i", retval);
		useUTC=0;
	}
}
*/


//DebugPrintf puts the screen into 80x25 text mode, displays the message, waits for a keypress, and resets the screen and returns.

//This is commented out to save RAM. It'll be uncommented when needed for debugging. (SL)

void Pause() {
	while (!tasto_premuto()) {}
	attendi_pressione_tasto();
}
Word __cdecl DebugPrintf(Word toScreen, const char * format, ...) {
	#ifndef DEBUGPRINTF
	if (!toScreen) {
		return 0;
	}
	#endif
	#ifndef WINDOWS
	if (toScreen) _80_25_C();
	#endif
	static Uchar firstRun = 1;
	char stringBuffer[1024];
	va_list argptr;
	Word retVal;
	va_start(argptr, format);
	if (toScreen) {
		vprintf(format, argptr);
		printf("\n");
	}
	retVal=vsprintf(stringBuffer, format, argptr);
	if (firstRun) {
		firstRun=0;
		FILE* file = fopen("..\\NIVdebug.log", "wt");
		fprintf(file, "%s\n", stringBuffer);
		fclose(file);

	} else {
		FILE* file = fopen("..\\NIVdebug.log", "at");
		fprintf(file, "%s\n", stringBuffer);
		fclose(file);
	}
	//retVal=sprintf(stringBuffer2, "echo %s", stringBuffer);
	va_end(argptr);

	#ifndef WINDOWS
	if (toScreen) {
		Pause();
		_320_200_256 ();
	}
	#endif
	return retVal;
}

//MULTIPLAYER_DISABLED (This comment exists to make it is to find all the code that need to be reenabled to reenable multiplayer) (SL)
#if defined(MULTIPLAYERNOTDISABLED)
#if defined(ALL) || defined(MULTIPLAYER)
char *ctcp = "..\\DATA\\CTCP.TXT";

#define INCOMPLETE_WORD 2
#define COMPLETE_WORD 1
#define NO_MORE_WORDS 0

int ReadWord(FILE *inFile, char *string, Word max) {
	//Reads a line of text from a text file:
	int nextByte = fgetc(inFile);
	if (nextByte==EOF) {
		string[0]='\0';
		return NO_MORE_WORDS;
	}
	int pos = 0;

	//Loop until we reach a new-line character:
	while (nextByte!='\n' && nextByte!='\r' && nextByte!=' ' && pos<max) {
		//Check for end of file:
		if (nextByte==EOF) {
			string[pos]='\0';
			return NO_MORE_WORDS;
		}
		string[pos++]=(char)nextByte;
		nextByte = fgetc(inFile);
	}
	string[pos]='\0';
	if (pos==max) return INCOMPLETE_WORD;
	return COMPLETE_WORD;
}

#define MAXUSERNAMELEN 32
#pragma pack(1)
struct DrifterInfo {
	char username[MAXUSERNAMELEN];
	double x, y, z, angle;
	double lastSeenTime;
	Uchar depolarize;
};
#pragma pack()

#define MaxDrifters 10
#ifdef MaxDrifters
DrifterInfo drifterInfo[MaxDrifters];
Uword curMaxDrifters = MaxDrifters;
#else
DrifterInfo far *drifterInfo = NULL;
Uword curMaxDrifters = 0;
#endif
Uword numDrifters = 0;
double lastSendTime = 0;

extern void other_vehicle_at (double ovhx, double ovhy, double ovhz);	//This is the drawing method used for the drifters which arrive after you call for help. We don't use it for multiplayer, but used it for early tests.


void SendInfoTo(Word id) {
	RunCommand("x ctcp %s i %f %f %f %f %i", drifterInfo[id].username, dzat_x, dzat_y, dzat_z, navigation_beta, (int)depolarize);
}

void SendRequestInfo() {
	RunCommand("x ctcp #noctistats r");
}

void SendInfo() {
	RunCommand("x ctcp #noctistats i %f %f %f %f %i", dzat_x, dzat_y, dzat_z, navigation_beta, (int)depolarize);
}

void RequestServerTime() {
	RunCommand("x time");
}

int GetDrifterId(char * username) {
	Word empty = -1;
	for (Word a=0; a<numDrifters; a++) {
		if (!strcmp(username, drifterInfo[a].username)) {
			return a;
		} else if (drifterInfo[a].username[0]==NULL) {
			empty=a;
		}
	}
	Word id;
	if (empty>-1) {
		id=empty;
	} else if (numDrifters>=curMaxDrifters) {
		#ifdef MaxDrifters
		return -1;
		#else
		if (curMaxDrifters==0) {
			curMaxDrifters=10;
			drifterInfo = NULL;
			drifterInfo = (DrifterInfo far *) farmalloc(sizeof(DrifterInfo)*(curMaxDrifters));
			//DebugPrintf("drifterInfo is %lx", drifterInfo);
			id=0;
			numDrifters++;
		} else {
			//resize it
			curMaxDrifters+=curMaxDrifters;
			DrifterInfo* tmp = (DrifterInfo far *) FARMALLOC(sizeof(DrifterInfo)*(curMaxDrifters), "DrifterInfo* tmp");
			if (tmp==NULL) {	//out of memory, though this probably won't ever be true, because new will probably throw an exception instead.
				return -1;
			}
			FARMEMCPY(tmp, drifterInfo, sizeof(DrifterInfo)*numDrifters);
			farfree(drifterInfo);
			
		}
		id=numDrifters;
		numDrifters++;
		#endif
	} else {
		id = numDrifters;
		numDrifters++;
	}
	//drifterInfo[id].username = (char *) malloc(namelen+1);
	Word namelen = strlen(username);
	strcpy(drifterInfo[id].username, username);
	drifterInfo[id].username[namelen]=0;	//I don't remember if strcpy writes a trailing NULL or not, so we do it here.
	drifterInfo[id].x=0;
	drifterInfo[id].y=0;
	drifterInfo[id].z=0;
	drifterInfo[id].angle=0;
	drifterInfo[id].depolarize=0;
	drifterInfo[id].lastSeenTime=0;
	return id;
}

void ForgetDrifter(Word id) {
	if (id>-1) {
		//free(drifterInfo[id].username);
		drifterInfo[id].username[0] = NULL;
	}
}


char stringBuffer[256];
Uchar firstTime = 1;

void Multiplayer() {
	if (firstTime) {
		for (Word a=0; a<curMaxDrifters; a++) {
			drifterInfo[a].username[0] = 0;
		}
		firstTime=0;
	}
	FILE * ch = fopen (ctcp, "rt");
	if (ch != NULL) {
		int result = ReadWord(ch, stringBuffer, 255);	//username
		if (result == COMPLETE_WORD || result == NO_MORE_WORDS) {
			if (stringBuffer[0]=='.') {	//special commands
				//.TIME MONDAY JANUARY 3 2005 -- 18:53:54 blah blah
				if (!strcmp(stringBuffer, ".TIME")) {
					result = ReadWord(ch, stringBuffer, 255);	//the day name
					//DebugPrintf(0, "Day Result %i Word %s", result, stringBuffer);
					if (result == COMPLETE_WORD || result == NO_MORE_WORDS) {
						result = ReadWord(ch, stringBuffer, 255);
						//DebugPrintf(0, "Month Result %i Word %s", result, stringBuffer);
						if (result == COMPLETE_WORD || result == NO_MORE_WORDS) {	//the month name
							//This is a fast check, but it will mess up if the server sends a nonexistant day-name.
							Word monthnum = 0;
							if (stringBuffer[0]=='J' || stringBuffer[0]=='j') {	//january
								monthnum=1;
							} else if (stringBuffer[0]=='F' || stringBuffer[0]=='f') {	//february
								monthnum=2;
							} else if (stringBuffer[0]=='M' || stringBuffer[0]=='m') {	//march or may
								if (stringBuffer[2]=='R' || stringBuffer[2]=='r') {	//march
									monthnum=3;
								} else {	//may
									monthnum=5;
								}
							} else if (stringBuffer[0]=='A' || stringBuffer[0]=='a') {	//april or august
								if (stringBuffer[1]=='P' || stringBuffer[1]=='p') {	//april
									monthnum=4;
								} else {	//august
									monthnum=8;
								}
							} else if (stringBuffer[0]=='J' || stringBuffer[0]=='j') {	//june or july
								if (stringBuffer[2]=='N' || stringBuffer[2]=='n') {	//june
									monthnum=6;
								} else {	//july
									monthnum=7;
								}
							} else if (stringBuffer[0]=='S' || stringBuffer[0]=='s') {	//september
								monthnum=9;
							} else if (stringBuffer[0]=='O' || stringBuffer[0]=='o') {	//october
								monthnum=10;
							} else if (stringBuffer[0]=='N' || stringBuffer[0]=='n') {	//november
								monthnum=11;
							} else if (stringBuffer[0]=='D' || stringBuffer[0]=='d') {	//december
								monthnum=12;
							}
							//DebugPrintf(0, "Monthnum %i", monthnum);
							if (monthnum>0) {
								//day 
								result = ReadWord(ch, stringBuffer, 255);
								//DebugPrintf(0, "Day Result %i Word %s", result, stringBuffer);
								if (result == COMPLETE_WORD || result == NO_MORE_WORDS) {
									int daynum = atoi(stringBuffer);
									//year
									result = ReadWord(ch, stringBuffer, 255);
									//DebugPrintf(0, "Year Result %i Word %s", result, stringBuffer);
									if (result == COMPLETE_WORD || result == NO_MORE_WORDS) {
										int yearnum = atoi(stringBuffer);
										//--
										result = ReadWord(ch, stringBuffer, 255);
										//DebugPrintf(0, "-- Result %i Word %s", result, stringBuffer);
										if (result == COMPLETE_WORD || result == NO_MORE_WORDS) {
											if (stringBuffer[0]=='-' && stringBuffer[1]=='-') {
												//hh:mm:ss
												result = ReadWord(ch, stringBuffer, 255);
												//DebugPrintf(0, "HH:MM:SS Result %i Word %s", result, stringBuffer);
												if (result == COMPLETE_WORD || result == NO_MORE_WORDS) {
													char * thrs = stringBuffer;
													char * tmins = 0;
													char * tsecs = 0;
													if (thrs[1]==':') {
														thrs[1]=0;
														tmins=thrs+2;
													} else if (thrs[2]==':') {
														thrs[2]=0;
														tmins=thrs+3;
													}
													if (tmins!=0) {
														if (tmins[1]==':') {
															tmins[1]=0;
															tsecs=tmins+2;
														} else if (tmins[2]==':') {
															tmins[2]=0;
															tsecs=tmins+3;
														} else {
															tsecs = "00";
														}
														if (tsecs!=0) {
															//DebugPrintf(0, "h[%s]m[%s]s[%s]", thrs, tmins, tsecs);
															/*
																secs = atol (year) - 1984;		      // anni dal 1984
																secs = secs * 365 + (long)(secs / 4);	      // anni * 365 + anni / 4
															
																for (m=1; m<atoi(month); m++)
																	secs += dfm[m];			      // giorni per mese
															
																if (atoi(month)>2 && !(atol(year)%4)) secs++; // se l'anno Š bisestile
															
																secs += atoi(day) - 1;			      // + giorni mese attuale
																secs *= 86400;                                // secondi in un giorno
															
																secs += 3600 * atol(hours);		      // ora corrente in sec.
																secs += 60 * atoi(minutes);		      // minuti corr. in sec.
															
																isecs = atoi(seconds);			      // secondi correnti.
																secs += isecs;
															*/
															Word hoursnum = (Word) atoi(thrs);
															Word minutesnum = (Word) atoi(tmins);
															Word secondsnum = (Word) atoi(tsecs);
															
															double mysecs = yearnum - 1984.0;		      // anni dal 1984
															mysecs = mysecs * 365 + (long)(mysecs / 4);	      // anni * 365 + anni / 4
														
															for (Word m=1; m<monthnum; m++)
																mysecs += dfm[m];			      // giorni per mese
														
															if (monthnum>2 && !(yearnum%4)) mysecs++; // se l'anno Š bisestile
														
															mysecs += daynum - 1;			      // + giorni mese attuale
															mysecs *= 86400;                                // secondi in un giorno
														
															mysecs += 3600.0 * hoursnum;		      // ora corrente in sec.
															mysecs += 60.0 * minutesnum;		      // minuti corr. in sec.
														
															mysecs += secondsnum;
															
															//set timeAdjustment and change the time variables
															//double prevsecs = secs;
															//timeAdjustment=0;
															//getsecs();
															//double cursecs = secs;
															//double osecs = secs;
															timeAdjustment = mysecs - secs;
															//getsecs();
															//double newsecs = secs;
															//DebugPrintf(0, "Adjusted time, prev %f cur %f new %f.", prevsecs, cursecs, newsecs);
															
															//DebugPrintf("Y%i M%i D%i h%i m%i s%i secs %f mysecs %f timeAdjustment %f secs now %f", yearnum, monthnum, daynum, hoursnum, minutesnum, secondsnum, osecs, mysecs, timeAdjustment, secs);
														}
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			} else {
				int namelen = strlen(stringBuffer);
				if (namelen>=MAXUSERNAMELEN) {
					namelen = MAXUSERNAMELEN-1;
					stringBuffer[namelen+1]=0;
				}
				Word id = GetDrifterId(stringBuffer);
				//DebugPrintf("Username [%s]. id %i", stringBuffer, id);
				if (id>-1) {
					char code = fgetc(ch);
					fgetc(ch);	//discard the space after the code
					switch (code) {
						case 'i':	//info
							double sdx=0, sdy=0, sdz=0, sdangle=0;
							Uchar sddepolarize=0;
							Uchar sdvalid=0;
							result = ReadWord(ch, stringBuffer, 255);
							//DebugPrintf("First word [%s]. Result %i", stringBuffer, result);
							if (result == COMPLETE_WORD || result == NO_MORE_WORDS) {
								sdx = atof(stringBuffer);
								sdvalid++;
							}
							result = ReadWord(ch, stringBuffer, 255);
							//DebugPrintf("Second word [%s]. Result %i", stringBuffer, result);
							if (result == COMPLETE_WORD || result == NO_MORE_WORDS) {
								sdy = atof(stringBuffer);
								sdvalid++;
							}
							result = ReadWord(ch, stringBuffer, 255);
							//DebugPrintf("Third word [%s]. Result %i", stringBuffer, result);
							if (result == COMPLETE_WORD || result == NO_MORE_WORDS) {
								sdz = atof(stringBuffer);
								sdvalid++;
							}
							result = ReadWord(ch, stringBuffer, 255);
							//DebugPrintf("Fourth word [%s]. Result %i", stringBuffer, result);
							if (result == COMPLETE_WORD || result == NO_MORE_WORDS) {
								sdangle = atof(stringBuffer);
								sdvalid++;
							}
							result = ReadWord(ch, stringBuffer, 255);
							//DebugPrintf("Fifth word [%s]. Result %i", stringBuffer, result);
							if (result == COMPLETE_WORD || result == NO_MORE_WORDS) {
								sddepolarize = atoi(stringBuffer);
								sdvalid++;
							}
							if (sdvalid==5) {
								drifterInfo[id].x=sdx;
								drifterInfo[id].y=sdy;
								drifterInfo[id].z=sdz;
								drifterInfo[id].angle=sdangle;
								drifterInfo[id].depolarize=sddepolarize;
								drifterInfo[id].lastSeenTime=secs;
								//DebugPrintf("1 x=%f y=%f z=%f angle=%f depolarize=%i lastSeenTime=%f", sdx, sdy, sdz, sdangle, (int) sddepolarize, secs);
								//DebugPrintf("2 x=%f y=%f z=%f angle=%f depolarize=%i lastSeenTime=%f", drifterInfo[id].x, drifterInfo[id].y, drifterInfo[id].z, drifterInfo[id].angle, (int) drifterInfo[id].depolarize, drifterInfo[id].lastSeenTime);
							}
							break;
						case 'r':	//request info
							SendInfoTo(id);
							break;
						
					}
				}
			}
			fclose(ch);
			remove(ctcp);
		}
	}
	if (secs-timeAdjustment-lastSendTime>2) {
		SendInfo();
		lastSendTime=secs-timeAdjustment;
	}
}

void DrawVehicleAt (double ovhx, double ovhy, double ovhz, Uchar depolarize)
{
	double dist = ovhx*ovhx + ovhy*ovhy + ovhz*ovhz;
	if (dist > 1e12) {
		return;
	}
	
	cam_x = - ovhx;
	cam_y = - ovhy;
	cam_z = - ovhz;

	cam_z += 3100;
	cam_y -= 550;
	if (ovhy>=0) {
		polycupola (0, 0);
		setfx(0); cupola (0, 8); resetfx();
	}
	cam_y += 550;
	if (ovhy<0) {
		polycupola (1, 0);
		setfx(0); cupola (1, 8); resetfx();
	}
	cam_z -= 3100;
	
	/*cam_z += 3100;
	setfx (2);
	if (ovhy>-375) cupola (+1, 8);
	if (ovhy<+375) cupola (-1, 8);
	resetfx ();
	cam_z -= 3100;
	*/
	cam_x = 0;
	cam_y = 0;
	cam_z = 0;
	if (depolarize) {
		setfx(2);
	}
	drawpv (vehicle_handle, 0, 0, ovhx, ovhy, ovhz, 1);
	if (depolarize) {
		resetfx();
	}
	cam_x = - ovhx;
	cam_y = - ovhy;
	cam_z = - ovhz;

	/*
	cam_z += 3100;
	setfx (2);
	if (ovhy>+375) cupola (+1, 8);
	if (ovhy<-375) cupola (-1, 8);
	resetfx ();
	cam_z -= 3100;
	*/
	cam_z += 3100;
	if (ovhy>=0) {
		polycupola (1, 0);
		setfx(0); cupola (1, 8); resetfx();
	}
	cam_y -= 550;
	if (ovhy<0) {
		polycupola (0, 0);
		setfx(0); cupola (0, 8); resetfx();
	}
	cam_y += 550;
	cam_z -=3100;
	/*cam_z += 3100;
	polycupola (+1, 0);
	setfx(0);
	cupola (+1, 8);
	resetfx();
	cam_z -= 3100;
	*/
	/*lens_flares_for (cam_x, cam_y, cam_z, 3225, 0, 0, -5e5, 3, 1, 1, 1, 1);
	lens_flares_for (cam_x, cam_y, cam_z, -3225, 0, 0, -5e5, 3, 1, 1, 1, 1);
	lens_flares_for (cam_x, cam_y, cam_z, 3225, 0, -6150, -5e5, 3, 1, 1, 1, 1);
	lens_flares_for (cam_x, cam_y, cam_z, -3225, 0, -6150, -5e5, 3, 1, 1, 1, 1);*/
}

void DrawMultiplayerVehicles() {
	double closestpx, closestpy, closestpz, closestpdist, closestpray;
	char closest_p = -1;
	for (int n=0; n<nearstar_nob; n++) {
		planet_xyz (n);
		double xx = plx - dzat_x;
		double yy = ply - dzat_y;
		double zz = plz - dzat_z;
		double dist = SQRT (xx*xx+yy*yy+zz*zz);
		if (closest_p==-1 || dist<closestpdist) {
			closest_p=n;
			closestpx=xx; closestpy=yy; closestpz=zz; closestpdist = dist; closestpray=nearstar_p_ray[n];
		}
	}
	for (Word a=0; a<numDrifters; a++) {
		if (secs-drifterInfo[a].lastSeenTime>120) {
			ForgetDrifter(a);
		} else {
			//draw them
			/*if (drifterInfo[a].depolarize) {
				setfx (2);
				drawpv (vehicle_handle, 2, 3, 0.0, 0.0, 0.0, 0);
				resetfx ();
			}
			else
				drawpv (vehicle_handle, 2, 2, 0.0, 0.0, 0.0, 0);*/
			//other_vehicle_at (4000, 0, 0);
			/*double posz = pos_z - 2750;
			double angle = atan2(pos_x, posz);
			double dist = SQRT(pos_x*pos_x + posz*posz);
			angle-=user_beta/180*M_PI;
			double px = cos(angle)*dist;
			double pz = sin(angle)*dist;*/
			Uchar visible = 1;
			double distx = drifterInfo[a].x-dzat_x;
			double disty = drifterInfo[a].y-dzat_y;
			double distz = drifterInfo[a].z-dzat_z;
			double dist = SQRT(distx*distx + disty*disty + distz*distz);
			
			if (closest_p>-1) {
				double pdistx = drifterInfo[a].x-closestpx;
				double pdisty = drifterInfo[a].y-closestpy;
				double pdistz = drifterInfo[a].z-closestpz;
				double pdist = SQRT(pdistx*pdistx + pdisty*pdisty + pdistz*pdistz);
				if (pdist<closestpray || dist>=closestpdist+closestpray) {
					//1. pdist<closestpray checks if the drifter would be INSIDE the planet
					//2. dist>=closestpdist+closestpray checks if the drifter is farther away than the planet + the planet's radius
					//	#2 isn't great, but ATM I don't know how to check if something would be BEHIND a sphere, except perhaps
					//	by calculating the angles to the sphere, the angles to the other drifter, the angles to a point on the edge
					//	of the visible circle which the sphere appears to be, and from that, determine the angle-difference within which
					//	something would be behind the sphere.
					//(SL)
					visible=0;
				}
			}
			if (visible) {
				DrawVehicleAt((drifterInfo[a].x-dzat_x)*8E6, (drifterInfo[a].y-dzat_y)*8E6-pos_y/8E6, (drifterInfo[a].z-dzat_z)*8E6, drifterInfo[a].depolarize);			
			}
		}
		//drawpv (vehicle_handle, 0, 0, ovhx, ovhy, ovhz, 1);

	}
	//DrawVehicleAt(5000*sin(secs*.1), 12000*sin(secs*.2), 5000*cos(secs*.1), ((int)secs)&1);
}
#endif
//MULTIPLAYER_DISABLED (This comment exists to make it is to find all the code that need to be reenabled to reenable multiplayer) (SL)	
#endif

char mooseS[1825];
char mooseStr[2];
void Moose(char *filetoread, int page) {
	*mooseS=NULL;
	FILE *f;
	int i=0;

    f=fopen(filetoread,"r");
	if (f!=NULL) {
		fseek(f, long(1810*(page-1)), SEEK_SET);
		while (fgets(mooseStr,2,f)!=NULL && i<1810) {
			strcat(mooseS , &mooseStr[0]);
			i++;
		}
		fclose(f);
	} else {
		sprintf(mooseS, "Unable to read file '%s'.", filetoread);
	}
}

void ShowMoose(int page, char standardtext, char html, char background) {
	char shortStr[2];
	shortStr[1]=NULL;
	int curchar=(22*28*3)-21;
	int line=0;
	int hori=0;
	int row=0;
	int dontwrite=0;

	curchar=(22*28*3*page)-(21*page);

	if (background)
		areaclear(adapted, 1, 1, 319, 199, 0, 0, 64);
	shortStr[0]=mooseS[curchar];
	while (shortStr[0]!=NULL && row<=2) {
 		hori++;

		if (line >= 28 && standardtext) {		//for when standardtext is on, we don't want the thing to get over 27 lines.
			return;
		}
		if (hori==22 && !standardtext) {
			hori=1;
			line++;
		}
		if (hori==70 && standardtext) {
			hori=1;
			line++;
		}
		if (line==29 && !standardtext) {
			line=0;
			row++;
		}
		if (mooseS[curchar] >=97 && mooseS[curchar] <=122) mooseS[curchar]=mooseS[curchar]-32;	//correct the case if nessecary. (uppercase -> lowercase)
		if (html) {		//HTML is on? Do some basic formatting like <BR> and don't show HTML tags.
			if (mooseS[curchar] == '<' && mooseS[curchar+1] == 'b' && mooseS[curchar+2] == 'r' && mooseS[curchar+3] == '>') {
				line++;
				curchar = curchar + 3;
				hori = 1;
				goto endmoose;
			}
			if (mooseS[curchar] == '<') dontwrite=1;
			if (mooseS[curchar] == '>') {dontwrite=0; goto endmoose;}
			if (dontwrite) {
				hori--;
				goto endmoose;
			}
			if (mooseS[curchar] == 10 && standardtext) {		//UNIX (!!!!!) lineending? Do an enter here! :)
				line++;
				hori = 0;
				goto endmoose;
			}
		}



		shortStr[0]=mooseS[curchar];
		if (!standardtext) wrouthud ((4*hori)+(row*90)+10,(6*line)+11, NULL, shortStr);
		if (standardtext) wrouthud ((4*hori)+10,(6*line)+11, NULL, shortStr);
//		writechar = str[curchar];


		endmoose:
		curchar++;
	}
}


/*void showsurftexture() {
		ptr=0;
		int mapx;
		int mapy;
		mapx = 0; mapy = 0;
		for (ptr=0; ptr<51200; ptr++) {			//only shows 200x256 area, not the full 256x256 texture!
									adapted[mapy]=p_background[ptr];
			mapx++;
			mapy++;
			if (mapx>= 256) {
				mapx=0;
				mapy = mapy + 320;
				mapy = mapy - 256;
			}

		}
	}
*/
#define WHITE_COLOR 63, 63, 63
#define BLACK_COLOR  0,  0,  0
#define RED_COLOR 63, 0, 0
int poosx;
int poosz;
int lifx;
int lifz;
extern float mapmod;
extern float mapcontr;
extern float pos_x;
extern float formalappr;
extern float pos_z;
extern float liftx;
extern float liftz;
extern Uchar maybehuge *p_surfacemap;
extern Uchar maybefar *ruinschart;
extern Dword star_label_pos;
extern char star_label[25];
extern Dword planet_label_pos;
extern char planet_label[25];
extern char showmapcol;
extern char sctype;
void showmap(int showmaptype) {
	ptr=0;
	lifx=(liftx/3276800)*200;
	lifz=(liftz/3276800)*200;
	poosx=(pos_x/3276800)*200;
	poosz=(pos_z/3276800)*200;

		int mapx;
		int mapy;
		int nziz;
		float mapmodres;
		mapx = 0; mapy = 0;
		for (ptr=0; ptr<40000; ptr++) {
                        if (showmaptype == 1)
			    if (p_surfacemap[ptr] == 0 && sctype == OCEAN) {
			       mapmodres = 0;
			    } else {
				mapmodres = (p_surfacemap[ptr] * mapcontr) + ((mapmod-1) * 10);
			    }				    
		        if (showmaptype == -1)
                            mapmodres = (ruinschart[ptr] * mapcontr) + ((mapmod-1) * 10);
			if (mapmodres > 127) mapmodres = 127;
			if (mapmodres < 0) mapmodres = 0;
			if (mapmodres < 1 && sctype == OCEAN && p_surfacemap[ptr] > 0) mapmodres = 1;
			adapted[mapy]=int (mapmodres);
			mapx++;
			mapy++;
			if (mapx>= 200) {
				mapx=0;
				mapy = mapy + 320;
				mapy = mapy - 200;
			}

		}
		areaclear(adapted, 200, 10, 320, 200, 0, 0, 40);
		adapted[(int(lifz*320))+int(lifx)]=130;
		adapted[(int(poosz*320))+int(poosx)]=129;


		wrouthud (202, 20, NULL, "(+) INCREASE MAPBRIGHTNESS");
		wrouthud (202, 26, NULL, "(-) DECREASE MAPBRIGHTNESS");
		sprintf (outhudbuffer, "  MAPBRIGHTNESS: %1.1f" , mapmod);
		wrouthud (202, 32, NULL, outhudbuffer);
		wrouthud (202, 40, NULL, "(CTRL +) INCREASE MAPCONTRAST");
		wrouthud (202, 46, NULL, "(CTRL -) DECREASE MAPCONTRAST");
		sprintf (outhudbuffer, "    MAPCONTRAST: %1.1f" , mapcontr);
		wrouthud (202, 52, NULL, outhudbuffer);
		wrouthud (202, 60, NULL, "(CTRL ASTERISK) RESET MAPVIEW");
		wrouthud (202, 70, NULL, "(M) CHANGE MAPTYPE");
		wrouthud (202, 76, NULL, "(N) CHANGE MAPCOLORS");

		wrouthud (202, 90, NULL, "COORDINATES USER (RED):");
		//sprintf (outhudbuffer, "X: %1.1f" , (pos_x/3276800)*200);
		sprintf (outhudbuffer, "X: %10.0f" , pos_x / 1000);
		wrouthud (202, 97, NULL, outhudbuffer);
		//sprintf (outhudbuffer, "Y: %1.1f" , (pos_z/3276800)*200);
		sprintf (outhudbuffer, "Y: %10.0f" , pos_z / 1000);
		wrouthud (202, 103, NULL, outhudbuffer);
		outhudbuffer[0]=NULL;
		strcat (outhudbuffer, "SQC ");
		strcat (outhudbuffer, alphavalue(landing_pt_lon));
		strcat (outhudbuffer, ".");
		strcat (outhudbuffer, alphavalue(landing_pt_lat));
		strcat (outhudbuffer, ":");
		strcat (outhudbuffer, alphavalue((((Dword)(pos_x)) >> 14) - 100));
		strcat (outhudbuffer, ".");
		strcat (outhudbuffer, alphavalue((((Dword)(pos_z)) >> 14) - 100));
		wrouthud (202, 110, NULL, outhudbuffer);

		wrouthud (202, 130, NULL, "COORDINATES LANDER (BLUE):");
		//sprintf (outhudbuffer, "X: %1.1f" , (pos_x/3276800)*200);
		sprintf (outhudbuffer, "X: %10.0f" , liftx / 1000);
		wrouthud (202, 137, NULL, outhudbuffer);
		//sprintf (outhudbuffer, "Y: %1.1f" , (pos_z/3276800)*200);
		sprintf (outhudbuffer, "Y: %10.0f" , liftz / 1000);
		wrouthud (202, 143, NULL, outhudbuffer);
		outhudbuffer[0]=NULL;
		strcat (outhudbuffer, "SQC ");
		strcat (outhudbuffer, alphavalue(landing_pt_lon));
		strcat (outhudbuffer, ".");
		strcat (outhudbuffer, alphavalue(landing_pt_lat));
		strcat (outhudbuffer, ":");
		strcat (outhudbuffer, alphavalue((((Dword)(liftx)) >> 14) - 100));
		strcat (outhudbuffer, ".");
		strcat (outhudbuffer, alphavalue((((Dword)(liftz)) >> 14) - 100));
		wrouthud (202, 150, NULL, outhudbuffer);
		if (ap_targetted == 1 && star_label_pos != -1) {
			wrouthud (202, 180, 20, star_label);
		}
		if (ip_targetted != -1 && planet_label_pos != -1) {
			wrouthud (202, 186, 20, planet_label);
		}


		for (nzi=0; nzi<129; nzi++) {
			nziz = (nzi*mapcontr)+((mapmod-1) * 10);
			if (nziz > 127) nziz = 127;
			if (nziz < 0) nziz = 0;
			if (nziz < 1 && sctype == OCEAN && nzi > 0) nziz = 1;
			if (nzi == 0 && sctype == OCEAN) nziz = 0;
			adapted[((nzi+67)*320)+310] = nziz;
			adapted[((nzi+67)*320)+311] = nziz;
		}
	}

void showpalette() {
		ptr=0;
		for (ptr=0; ptr<256; ptr++) {
			adapted[ptr]=ptr;
		}
	}


/*void showsky() {
		ptr=0;
		int mapx;
		int mapy;
		mapx = 0; mapy = 0;
		for (ptr=0; ptr<40000; ptr++) {
									adapted[mapy]=s_background[ptr]+64;
			mapx++;
			mapy++;
			if (mapx>= 360) {
				mapx=0;
				mapy = mapy + 320;
				mapy = mapy - 360;
			}

		}
	} */

void showplansurf(int page, int mode, int add) {
		ptr=0;
		int mapx;
		int mapy;
		mapx = 0; mapy = 0;
		/*for (mapy=0; mapy<=10; mapy++) {
                  for (mapx=0; mapx<=10; mapx++) {
                    adapted[(mapy*320)+mapx]=p_background[(mapy*320)+mapx];
                  }
                }*/

                //areaclear(adapted, 0, 0, 320, 200, 0, 0, 0);
                for (ptr=0; ptr<43200; ptr++) {
                  mapy = ptr / 360;
                  mapx = ptr - (360 * mapy);

                if (mode == 0) {
                  if (page == 0 && (mapx < 320 && mapy < 160)) {
                     adapted[(mapy*320)+mapx]=p_background[ptr] + 32 * add;
                  }

                  if (page == 1 && (mapx >= 320 && mapy < 160)) {
                     adapted[(mapy*320)+(mapx-320)]=p_background[ptr] + 32 * add;
                  }
                }
                if (mode == 1) {
                  if (page == 0 && (mapx < 320 && mapy < 160)) {
                     adapted[(mapy*320)+mapx]=s_background[ptr] + 32 * add;
                  }

                  if (page == 1 && (mapx >= 320 && mapy < 160)) {
                     adapted[(mapy*320)+(mapx-320)]=s_background[ptr] + 32 * add;
                  }
                }


               	/*wrouthud (20, 170, 20, "SURFACE ANALYZER");
		sprintf (outhudbuffer, "PAGE: %1i" , page);
		wrouthud (20, 180, NULL, outhudbuffer);
		sprintf (outhudbuffer, "MODE: %1i" , mode);
		wrouthud (20, 187, NULL, outhudbuffer);
		sprintf (outhudbuffer, "ADD: %1i" , add);
		wrouthud (20, 192, NULL, outhudbuffer);  */


         }
	/*	for (ptr=0; ptr<40000; ptr++) {
									adapted[mapy]=s_background[ptr]+64;
			mapx++;
			mapy++;
			if (mapx>= 360) {
				mapx=0;
				mapy = mapy + 320;
				mapy = mapy - 360;
			}

		} */
	}

/*void show_newmessage(int helpmessageID) {
  areaclear(adapted, 87, 70, 248, 77, 0, 0, 50);
  areaclear(adapted, 87, 77, 248, 177, 0, 0, 112);
areaclear(adapted, 51, 19, 275, 32, 0, 0, 64);
  wrouthud (52, 20, NULL, "");
  wrouthud (52, 26, NULL, "HTTP://WWW.ANYWHEREBB.COM - NOCTIS IV'S SITE AND FORUMS.");
if (helpmessageID == 0) {
   wrouthud (88, 71, NULL, "             NEW TO NOCTIS?             ");
 //wrouthud (##,###, NULL, "----------------------------------------");
   wrouthud (88, 81, NULL, "HELLO THERE. IT SEEMS AS IF YOU ARE NEW ");
   wrouthud (88, 87, NULL, "TO NOCTIS OR NOCTIS IV CE, BECAUSE THIS ");
   wrouthud (88, 93, NULL, "COPY OF NOCTIS IV CE YOU ARE CURRENTLY  ");
   wrouthud (88, 99, NULL, "USING APPEARS TO BE A FRESH INSTALL OF  ");
   wrouthud (88,105, NULL, "IT. HOWEVER, THIS COULD ALSO NOT BE TRUE");
   wrouthud (88,111, NULL, "AND YOU MIGHT ALREADY KNOW THE BASICS OF");
   wrouthud (88,117, NULL, "NOCTIS AND/OR NOCTIS IV CE. PLEASE TYPE ");
   wrouthud (88,123, NULL, "THE LETTER THAT CORRESPONDS WITH YOUR   ");
   wrouthud (88,129, NULL, "CURRENT SITUATION...                    ");
   wrouthud (88,135, NULL, "(A) - I AM NEW TO NOCTIS.               ");
   wrouthud (88,141, NULL, "(B) - I KNOW THE BASICS OF NOCTIS ITSELF");
   wrouthud (88,147, NULL, "      BUT NOT YET THOSE OF NOCTIS IV CE.");
   wrouthud (88,153, NULL, "(C) - I ALREADY KNOW EVERYTHING NEEDED  ");
   wrouthud (88,159, NULL, "      AND DO NOT NEED ANY FURTHER HELP. ");
   wrouthud (88,165, NULL, "                                        ");
   wrouthud (88,171, NULL, "                                        ");
}
if (helpmessageID == 1) {
   wrouthud (88 ,71, NULL, "   NEW TO NOCTIS AND NOCTIS IV CE(1/4)  ");
 //wrouthud (##,###, NULL, "----------------------------------------");
   wrouthud (88, 81, NULL, "SINCE YOU ARE NEW TO NOCTIS AND NOCTIS  ");
   wrouthud (88, 87, NULL, "IV CE, IT WOULD PROBABLY BE BEST TO READ");
   wrouthud (88, 93, NULL, "NOCTIS_IV_MANUAL.HTML, FOLLOWED BY THE  ");
   wrouthud (88, 99, NULL, "OTHER HELP FILES WHICH YOU CAN FIND IN  ");
   wrouthud (88,105, NULL, "THE 'DOCS' FOLDER. FIXES.HTML SHOULD BE ");
   wrouthud (88,111, NULL, "AN INTERESTING READ, TOO, SINCE IT SHOWS");
   wrouthud (88,117, NULL, "THE FIXES AND NEW FEATURES NOCTIS IV CE ");
   wrouthud (88,123, NULL, "MADE TO THE REGULAR NOCTIS IV.          ");
   wrouthud (88,129, NULL, "                                        ");
   wrouthud (88,135, NULL, "A BASIC RUNDOWN OF HOW TO PLAY NOCTIS:  ");
   wrouthud (88,141, NULL, "YOU CAN EITHER OPERATE THE SHIP'S MENU  ");
   wrouthud (88,147, NULL, "BY LOOKING AT THE MAIN SCREEN AND USING ");
   wrouthud (88,153, NULL, "YOUR MOUSE TO SELECT A MENU OPTION TO   ");
   wrouthud (88,159, NULL, "THE LEFT OF THE SCREEN, AND THEN SELECT ");
   wrouthud (88,165, NULL, "AN OPTION IN THE MIDDLE OF THE SCREEN.  ");
   wrouthud (88,171, NULL, "(A) - GO TO NEXT PAGE                   ");//next page ID: 4
}
if (helpmessageID == 4) {
   wrouthud (88 ,71, NULL, "   NEW TO NOCTIS AND NOCTIS IV CE(2/4)  ");
 //wrouthud (##,###, NULL, "----------------------------------------");
   wrouthud (88, 81, NULL, "NOTE THAT YOU CAN HOLD THE RIGHT MOUSE  ");
   wrouthud (88, 87, NULL, "BUTTON WHILE MOVING THE MOUSE TO STRAFE ");
   wrouthud (88, 93, NULL, "AND TO PITCH VERTICALLY. YOU CAN ALSO   ");
   wrouthud (88, 99, NULL, "OPERATE THE SHIP'S MENU BY USING THE    ");
   wrouthud (88,105, NULL, "KEYBOARD. LOOK AT THE TOP OF THE SCREEN ");
   wrouthud (88,111, NULL, "TO KNOW WHICH KEYS YOU HAVE TO PRESS.   ");
   wrouthud (88,117, NULL, "HIT '4' TO GO BACK TO THE 'MAIN MENU'.  ");
   wrouthud (88,123, NULL, "                                        ");
   wrouthud (88,129, NULL, "NOCTIS IV CE PROVIDES YOU WITH SHORTCUTS");
   wrouthud (88,135, NULL, "TO MANY IMPORTANT ACTIONS YOU WILL OFTEN");
   wrouthud (88,141, NULL, "HAVE TO USE, SUCH AS 'SET REMOTE TARGET'");
   wrouthud (88,147, NULL, "OR 'SET LOCAL TARGET'. HIT F1 TO SEE A  ");
   wrouthud (88,153, NULL, "COMPLETE LIST OF SHORTCUT KEYS.         ");
   wrouthud (88,159, NULL, "                                        ");
   wrouthud (88,165, NULL, "                                        ");
   wrouthud (88,171, NULL, "(A) - GO TO NEXT PAGE                   ");//next page ID: 6
}
if (helpmessageID == 6) {
   wrouthud (88 ,71, NULL, "   NEW TO NOCTIS AND NOCTIS IV CE(3/4)  ");
 //wrouthud (##,###, NULL, "----------------------------------------");
   wrouthud (88, 81, NULL, "A QUICK GUIDE TO LANDING ON A PLANET:   ");
   wrouthud (88, 87, NULL, "HIT 'R', MOVE THE MOUSE AROUND AND HIT  ");
   wrouthud (88, 93, NULL, "THE RIGHT MOUSE BUTTON TWICE WHEN YOU   ");
   wrouthud (88, 99, NULL, "WANT TO FIX THE SELECTED STAR, HIT V,   ");
   wrouthud (88,105, NULL, "WAIT UNTIL YOU ARRIVE, HIT L, MOVE THE  ");
   wrouthud (88,111, NULL, "MOUSE AROUND AND HIT THE RIGHT MOUSE    ");
   wrouthud (88,117, NULL, "BUTTON TWICE WHEN YOU WANT TO FIX TARGET");
   wrouthud (88,123, NULL, "AND HIT L AGAIN TO APPROACH IT. WHEN    ");
   wrouthud (88,129, NULL, "YOU ARRIVE AT THE PLANET, HIT 1, HIT 9, ");
   wrouthud (88,135, NULL, "TURN TO YOUR RIGHT, FACE THE 3RD SCREEN ");
   wrouthud (88,141, NULL, "FROM THE LEFT UNTIL THE LIGHT ABOVE IT  ");
   wrouthud (88,147, NULL, "IS LIT (APPROACH THE SCREEN), HIT ENTER,");
   wrouthud (88,153, NULL, "AND YOU'LL LAND ON THE PLANET! NOTE THAT");
   wrouthud (88,159, NULL, "SOME STARS DON'T HAVE PLANETS, AND SOME ");
   wrouthud (88,165, NULL, "PLANETS ARE UNLANDABLE...               ");
   wrouthud (88,171, NULL, "(A) - GO TO NEXT PAGE                   ");//next page ID: 8
}
if (helpmessageID == 8) {
   wrouthud (88 ,71, NULL, "   NEW TO NOCTIS AND NOCTIS IV CE(4/4)  ");
 //wrouthud (##,###, NULL, "----------------------------------------");
   wrouthud (88, 81, NULL, "SINCE YOU ARE NEW, IT WOULD BE A GOOD   ");
   wrouthud (88, 87, NULL, "IDEA TO READ THE NOCTIS MANUAL NOW...   ");
   wrouthud (88, 93, NULL, "IT MIGHT BE A LONGISH READ, BUT IT GIVES");
   wrouthud (88, 99, NULL, "YOU A LOT OF INFORMATION ABOUT THE LARGE");
   wrouthud (88,105, NULL, "UNIVERSE THAT NOCTIS SHOWS YOU. IF YOU  ");
   wrouthud (88,111, NULL, "SOMEHOW CANNOT FIGURE STUFF OUT, THOUGH,");
   wrouthud (88,117, NULL, "THE PEOPLE AT ANYWHEREBB.COM'S FORUMS   ");
   wrouthud (88,123, NULL, "WOULD BE GLAD TO ANSWER ANY QUESTIONS   ");
   wrouthud (88,129, NULL, "YOU MIGHT HAVE.                         ");
   wrouthud (88,135, NULL, "                                        ");
   wrouthud (88,141, NULL, "GOOD LUCK, AND MAY PARSIS ALWAYS BE WITH");
   wrouthud (88,147, NULL, "YOU, ESPECIALLY WHEN YOU RUN OUT OF FUEL");
   wrouthud (88,153, NULL, "OR WHEN YOU FIND YOURSELF LANDING ON A  ");
   wrouthud (88,159, NULL, "DARK AND HOSTILE WORLD...               ");
   wrouthud (88,165, NULL, "                                        ");
   wrouthud (88,171, NULL, "(A) - THANKS FOR THE INFO, NOW LET'S GO!");
} 
  
if (helpmessageID == 2) {
   wrouthud (88 ,71, NULL, "       NEW TO NOCTIS IV CE ONLY(1/3)    ");
 //wrouthud (##,###, NULL, "----------------------------------------");
   wrouthud (88, 81, NULL, "HELLO, AND WELCOME TO NOCTIS IV CE, A   ");
   wrouthud (88, 87, NULL, "MOD OF NOCTIS IV, WHICH YOU SHOULD KNOW ");
   wrouthud (88, 93, NULL, "ABOUT ALREADY (AT LEAST THE BASICS).    ");
   wrouthud (88, 99, NULL, "NOCTIS IV CE SHOULD ENHANCE THE NOCTIS  ");
   wrouthud (88,105, NULL, "EXPERIENCE, BY ADDING A FEW NEW FEATURES");
   wrouthud (88,111, NULL, "AND BY FIXING SOME BUGS. FOR A COMPLETE ");
   wrouthud (88,117, NULL, "LIST OF FEATURES AND FIXES, PLEASE READ ");
   wrouthud (88,123, NULL, "'FIXES.HTML' IN THE 'DOCS' FOLDER. A FEW");
   wrouthud (88,129, NULL, "IMPORTANT THINGS TO NOTE ABOUT 'NICE':  ");
   wrouthud (88,135, NULL, "1 - NEW STARMAP SYSTEM, WHICH ALLOWS YOU");
   wrouthud (88,141, NULL, "    TO ACCESS GUIDE ENTIRES WHEREVER YOU");
   wrouthud (88,147, NULL, "    ARE (NO MORE 'OUTOFRANGE' ERRORS).  ");
   wrouthud (88,153, NULL, "2 - A LOT OF BUGFIXES ARE MADE IN NOCTIS");
   wrouthud (88,159, NULL, "    IV'S PLANET GENERATION CODE.        ");
   wrouthud (88,165, NULL, "                                        ");
   wrouthud (88,171, NULL, "(A) - GO TO NEXT PAGE                   ");	//next page ID: 5 
}
if (helpmessageID == 5) {
   wrouthud (88 ,71, NULL, "       NEW TO NOCTIS IV CE ONLY(2/3)    ");
 //wrouthud (##,###, NULL, "----------------------------------------");
   wrouthud (88, 81, NULL, "3 - HELPFUL TOOLS THAT ALLOW YOU TO SEE ");
   wrouthud (88, 87, NULL, "    AND EXPLORE PLANET SURFACES IN A LOT");
   wrouthud (88, 93, NULL, "    OF DIFFERENT WAYS, EXAMPLES OF WHICH");
   wrouthud (88, 99, NULL, "    ARE THE JETPACK, THE MOVIEMAKING    ");
   wrouthud (88,105, NULL, "    FEATURE, AND THE ADVANCED STATISTICS");
   wrouthud (88,111, NULL, "4 - INBUILT HELP SYSTEM, IN THE FORM OF ");
   wrouthud (88,117, NULL, "    THE GUIDE YOU ARE CURRENTLY READING ");
   wrouthud (88,123, NULL, "    AND THE KEYLIST WHICH YOU SEE WHEN  ");
   wrouthud (88,129, NULL, "    YOU PRESS THE F1 KEY.               ");
   wrouthud (88,135, NULL, "5 - NEW CREATURES ON PLANET SURFACES!   ");
   wrouthud (88,141, NULL, "PLEASE TAKE SOME TIME TO LEARN ALL THE  ");
   wrouthud (88,147, NULL, "NEW FEATURES THAT ARE PRESENT IN NOCTIS ");
   wrouthud (88,153, NULL, "IV CE. IF THERE IS ANYTHING YOU NEED TO ");
   wrouthud (88,159, NULL, "KNOW, OR IF YOU NOTICE SOME BUGS OR ODD ");
   wrouthud (88,165, NULL, "THINGS, PLEASE GO TO ANYWHEREBB.COM AND ");
   wrouthud (88,171, NULL, "(A) - GO TO NEXT PAGE                   ");//next page ID: 7
}
if (helpmessageID == 7) {
   wrouthud (88 ,71, NULL, "       NEW TO NOCTIS IV CE ONLY(3/3)    ");
 //wrouthud (##,###, NULL, "----------------------------------------");
   wrouthud (88, 81, NULL, "POST A MESSAGE IN THE FORUMS. WE WOULD  ");
   wrouthud (88, 87, NULL, "BE GLAD TO ASK ANY QUESTIONS YOU HAVE,  ");
   wrouthud (88, 93, NULL, "AND TELL YOU ANYTHING YOU WISH TO KNOW. ");
   wrouthud (88, 99, NULL, "(AT LEAST, EVERYTHING BUT THE MORE FINE ");
   wrouthud (88,105, NULL, "DETAILS AND MYSTERIES IN THE NOCTIS IV  ");
   wrouthud (88,111, NULL, "CE UNIVERSE... :P )                     ");
   wrouthud (88,117, NULL, "                                        ");
   wrouthud (88,123, NULL, "OH, ONE LAST NOTICE: THE SHORTCUT KEYS  ");
   wrouthud (88,129, NULL, "HAVE CHANGED A BIT SINCE NOCTIS IV. THEY");
   wrouthud (88,135, NULL, "NOW USE 1/2/3/4/5 INSTEAD OF THE OLD    ");
   wrouthud (88,141, NULL, "5/R/D/X SYSTEM NOCTIS IV USED. PLEASE   ");
   wrouthud (88,147, NULL, "TAKE SOME TIME TO GET USED TO THIS.     ");
   wrouthud (88,153, NULL, "                                        ");
   wrouthud (88,159, NULL, "                                        ");
   wrouthud (88,165, NULL, "                                        ");
   wrouthud (88,171, NULL, "(A) - THANKS FOR THE INFO, NOW LET'S GO!");
} 

if (helpmessageID == 3) {
   wrouthud (88, 71, NULL, "     NEW TO NOTHING. READY TO PLAY!     ");
 //wrouthud (##,###, NULL, "----------------------------------------");
   wrouthud (88, 81, NULL, "YOU HAVE SPECIFIED THAT YOU ALREADY KNOW");
   wrouthud (88, 87, NULL, "WHAT IS NEEDED TO PLAY NOCTIS IV CE..   ");
   wrouthud (88, 93, NULL, "HOWEVER, SINCE NOCTIS IV CE IS ACTIVELY ");
   wrouthud (88, 99, NULL, "IMPROVED ALL THE TIME, YOU MIGHT NOT YET");
   wrouthud (88,105, NULL, "ABOUT ALL THE NEW FEATURES ADDED IN THE ");
   wrouthud (88,111, NULL, "LATEST RELEASES. PLEASE READ THE FILE   ");
   wrouthud (88,117, NULL, "NAMED CHANGES.HTML (IN THE SAME FOLDER  ");
   wrouthud (88,123, NULL, "WHERE YOU CAN FIND THE GO!.EXE FILES)   ");
   wrouthud (88,129, NULL, "AND MAYBE READ FIXES.HTML IN THE DOCS   ");
   wrouthud (88,135, NULL, "FOLDER. IF THERE IS ANYTHING ELSE YOU   ");
   wrouthud (88,141, NULL, "NEED TO KNOW, PLEASE READ THE VARIOUS   ");
   wrouthud (88,147, NULL, "HELP FILES INSIDE THE DOCS FOLDER, OR   ");
   wrouthud (88,153, NULL, "GO TO ANYWHEREBB.COM TO ASK A QUESTION  ");
   wrouthud (88,159, NULL, "ON THE FORUMS, WE'D BE GLAD TO ANSWER.  ");
   wrouthud (88,165, NULL, "F1 BRINGS UP A KEYLIST WITH COMMANDS.   ");	
   wrouthud (88,171, NULL, "(A) - THANKS FOR THE INFO. NOW LET'S GO!");
	
}

}*/


void ShowAboutPage(FastBool surface) {
	areaclear(adapted, 5, 5, 315, 195, 0, 0, 0);
	//if (abcd>1024) abcd=0;
	areaclear (adapted, 11, 10, 310, 32, 0, 0, 80);
	wrouthud (14, 12, NULL, "                               NOCTIS IV CE");
	wrouthud (14, 19, NULL, "                         BY MEGAGUN AND SHADOWLORD");
	wrouthud (14, 25, NULL, "     MODIFIED VERSION OF NOCTIS - A PROGRAM MADE BY ALESSANDRO GHIGNOLA.");
	//wrouthud (14, 29, NULL, "");
	//wrouthud (14, 35, NULL, "");
	areaclear (adapted, 11, 35, 310, 44, 0, 0, 72);
	areaclear (adapted, 11, 45, 310, 168, 0, 0, 112);
	if (surface) {
		//areaclear (adapted, 11, 94, 310, 103, 0, 0, 72);
		wrouthud (14, 37, NULL, "SURFACE SHORTCUT KEYS:");
		//areaclear (adapted, 11, 104, 310, 168, 0, 0, 112);
		wrouthud (14,48, NULL,   "F - ANTIFOG (THICK ATMOSPHERE ONLY)         ");
		wrouthud (14,54, NULL,   "O - SUPERVISION                             P - VISION MODES");
		wrouthud (14,60, NULL,   "J - JUMP                                    C - DISENGAGE JETPACK CONTROL");
		wrouthud (14,66, NULL,   "SPACE - JETPACK                             L - LAND ON SURFACE");
		wrouthud (14,72, NULL,   "F2 - ADVANCED DATA                          F3 - MOVIEMAKER");
		wrouthud (14,78, NULL,   "F5 - PORTABLE GOES                          F10 - SECTOR MAP");
		wrouthud (14,84, NULL,   "CTRL + - INCREASE BRIGHTNESS                CTRL - - DECREASE BRIGHTNESS");
		wrouthud (14,90, NULL,   "CTRL ASTERISK - RESET BRIGHTNESS            B OR DELETE - RAW SNAPSHOT");
		wrouthud (14,96, NULL,  "M OR STAR (ASTERISK) - SNAPSHOT             N OR / - WIDE SNAPSHOT");
		wrouthud (14,102, NULL,  "H - HIGHLIGHT ANIMALS                       D - HIDE/SHOW DEBRIS");
		wrouthud (14,108, NULL,  "A - CALL LANDER                             Z - CANCEL CALLING LANDER");
		wrouthud (14,114, NULL,  "LEFT ARROW - SLIDE LEFT                     RIGHT ARROW - SLIDE RIGHT");
		wrouthud (14,120, NULL,  "DOWN ARROW - STOP SLIDING                   UP ARROW - TOGGLE MOUSELOOK");
		wrouthud (14,126, NULL,  "V - HIDE/SHOW VERTICAL PODLOCATOR-LINE      , . 0-9 - MOVEMENT SPEED");
		wrouthud (14,132, NULL,  "G - HIDE/SHOW GRASS");
		
		wrouthud (14,148, NULL,  "  IF IN LANDER:");
		wrouthud (14,154, NULL,  "Q - RETURN TO STARDRIFTER                   A - HOVER ABOVE SURFACE");
		wrouthud (14,160, NULL,  "Z - LAND ON SURFACE");
	} else {
		//areaclear (adapted, 11, 35, 310, 44, 0, 0, 72);
		wrouthud (14, 37, NULL, "SHORTCUT KEYS (WHEN IN SPACE):");
		
                if (option_controlset == 1) {
                  wrouthud (14, 47, NULL, "R - SET REMOTE TGT                          V - VIMANA FLIGHT");
		  wrouthud (14, 84, NULL, "X - TERRAIN FILTER (FOR FELISIAN PLANETS)   C - CLOUD FILTER");
                } else {
                  wrouthud (14, 47, NULL, "T - SET REMOTE TGT                          V - VIMANA FLIGHT");
		  wrouthud (14, 84, NULL, "F - TERRAIN FILTER (FOR FELISIAN PLANETS)   C - CLOUD FILTER");
                }
		wrouthud (14, 53, NULL, "Q - REMOTE TGT DATA                         W - LOCAL TGT DATA");
		wrouthud (14, 59, NULL, "L - SET/APPRCH/REMOVE LOCAL TGT             E - ENVIRONMENT DATA");
		wrouthud (14, 65, NULL, "TAB - CHANGE ANTIALIASING                   I - CHANGE INTERNL LIGHT");
		wrouthud (14, 71, NULL, "S - SCOPE FOR LITHIUM                       H - SEND HELP REQUEST");
		wrouthud (14, 78, NULL, "/ - LIGHT SCALING, SHIFT / - ADV LIGHTSC.   BACKSLASH - ROOF-HUD");

		wrouthud (14, 90, NULL, "B OR DELETE - RAW SNAPSHOT                  Z - SPEED INFO");
		wrouthud (14, 97, NULL, "F2 - SOLAR SYSTEM ANALYZER");
	}
	//wrouthud (14, 90, NULL, "");
	
	areaclear (adapted, 11, 178, 310, 188, 0, 0, 72);
	wrouthud (14,180, NULL, "CONTRIBUTORS: BENSEL, THE REFLECTION, EES33 - HTTP://ANYWHEREBB.COM");
	
}

void ShowPTYPE () {
	char hasmoon;
	int ypos = 5;
	int xpos = 11;
	int bcount = 0;
	char typecount[12];
//	Udword tempuw;

	char *btype[] = { 
		                   "INT_HOT",
				   "CRATER",
				   "THICK_AT",
				   "FELISIAN",
				   "CREASED",
				   "THIN_AT",
				   "NOTCONS",
				   "ICY",
				   "QUARTZ",
				   "SUBSTLLR",
				   "COMP_ST"
#ifdef WINDOWS
				, "GATEWAY"
#endif
	};
	
	areaclear (adapted, 1, 1, 319, 199, 0,  0, 33);
	areaclear (adapted, 89, 1, 90, 199, 0,  0, 50);
	areaclear (adapted, 169, 1, 170, 199, 0,  0, 50);
	areaclear (adapted, 249, 1, 250, 199, 0,  0, 50);
		
	for (int xc=0; xc<79; xc++) {
		if (bcount >= nearstar_nob) break;
		
		if (ypos > 176) {
			ypos = 5;
			xpos = xpos + 80;			
		}
		ypos = ypos + 6;
		
		hasmoon = 0;
		for (int yc=0; yc<79; yc++) {
			if (yc >= nearstar_nob) break;
			if (nearstar_p_owner[yc] == xc) {
				hasmoon = 1;
				break;
			}		
		}

		//tempuw = system_labelled(nearstar_sx, nearstar_sy, nearstar_sz, current_galaxy);

		if (hasmoon == 0) { // no moons
			sprintf(outhudbuffer, "-P %2i     (%8s)" , xc + 1, btype[nearstar_p_type[xc]]);
			bcount++;
			/*if (planet_labelled (tempuw, xc, nearstar_p_owner[xc], nearstar_p_moonid[xc], nearstar_p_type[xc], False, current_galaxy) > -1) {
				areaclear (adapted, xpos, ypos, xpos + 78, ypos + 5, 0,  0, 50);
			}*/
			wrouthud(xpos, ypos, NULL, outhudbuffer);
				areaclear (adapted, xpos, ypos + 5, xpos + 76, ypos + 6, 0,  0, 40);
		}
		if (hasmoon == 1) { // has moons
			sprintf(outhudbuffer, "+P %2i     (%8s)" , xc + 1, btype[nearstar_p_type[xc]]);
			bcount++;
			/*if (planet_labelled (tempuw, xc, nearstar_p_owner[xc], nearstar_p_moonid[xc], nearstar_p_type[xc], False, current_galaxy) > -1) {
				areaclear (adapted, xpos, ypos, xpos + 78, ypos + 5, 0,  0, 50);
			}*/
			wrouthud(xpos, ypos, NULL, outhudbuffer);
			for (int zc=0; zc<79; zc++) {
				if (nearstar_p_owner[zc] == xc) { //is moon
					if (ypos > 176) {
						ypos = 5;
						xpos = xpos + 80;			
					}	
					ypos = ypos + 6;
					sprintf(outhudbuffer, "& M %2i/%2i (%8s)" , nearstar_p_moonid[zc] + 1, nearstar_p_owner[zc] + 1, btype[nearstar_p_type[zc]]);
					bcount++;
					/*if (planet_labelled (tempuw, zc, nearstar_p_owner[zc], nearstar_p_moonid[zc], nearstar_p_type[zc], False, current_galaxy) > -1) {
						areaclear (adapted, xpos, ypos, xpos + 78, ypos + 5, 0,  0, 50);
					}*/
					wrouthud(xpos, ypos, NULL, outhudbuffer);
				}
			}
			areaclear (adapted, xpos, ypos + 5, xpos + 77, ypos + 6, 0,  0, 40);					
		}
	}
	areaclear(adapted, 250,10,310,17,0,0,20);
	areaclear(adapted, 250,18,310,24,0,0,24);
	areaclear(adapted, 250,25,310,39,0,0,27);
	if (star_label_pos != -1) {
		wrouthud (251, 11, 15, star_label);
	}
	if (nearstar_class < 10) {
		sprintf(outhudbuffer, "S0%1i" , nearstar_class);
	} else {
		sprintf(outhudbuffer, "S%2i" , nearstar_class);
	}
	wrouthud(251,19,NULL, outhudbuffer);
	sprintf(outhudbuffer, "%2i MAJOR BODIES",nearstar_nop);
	wrouthud(251,26,NULL, outhudbuffer);
	sprintf(outhudbuffer, "%2i MINOR BODIES",nearstar_nob - nearstar_nop);
	wrouthud(251,32,NULL, outhudbuffer);
	
	for (int rc=0; rc<12; rc++) {
		typecount[rc]=0;
	}
	for (int pc=0; pc<79; pc++) {
		if (pc >= nearstar_nob) break;
		typecount[nearstar_p_type[pc]] = typecount[nearstar_p_type[pc]] + 1;			
	}
	for (int qc=0; qc<11; qc++) {
		sprintf(outhudbuffer, "%8s  %2i" , btype[qc], typecount[qc]);
		wrouthud(255, 100 + (6*qc), NULL, outhudbuffer);
	}
	
	
}

void RecalcPlanetStuff(Word onp) {
	dontDraw=onp;
	if (onp) {
		if (cloudFilter==1) cloudFilter=0;
		terrainFilter=0;
	}
	getsecs ();
	planet_xyz (ip_targetted);
	dzat_x = plx;
	dzat_y = ply;
	dzat_z = plz;
	// calcolo della distanza dalla stella primaria
	double dxx = dzat_x - nearstar_x;
	double dyy = dzat_y - nearstar_y;
	double dzz = dzat_z - nearstar_z;
	dsd = SQRT (dxx*dxx + dyy*dyy + dzz*dzz) + 1;
	dsdSq = dsd*dsd;
	// rielaborazione superficie planetaria
	from_vehicle ();
	npcs=-12345;
	resident_map1=-1; resident_map2=-1;
	landing_point = 1; planets (); landing_point = 0;
	// ripresa del ciclo di esplorazione planetaria
	dontDraw=0;
	terrainFilter=0;
}

//in noctis-1 (F4 - show binocuscope stats (MG)
extern Word visionmode;
extern char supvision;
extern Word brightencnt;

void ShowBinStat () {
	areaclear (adapted, 235, 180, 305, 187, 0, 0, 72);
	if (visionmode == 2)
		wrouthud (236, 181, NULL, "INFRARED");
	if (visionmode == 1)
		wrouthud (236, 181, NULL, "ULTRACOLOUR");
	if (visionmode == 3)
		wrouthud (236, 181, NULL, "OCEANVISION");
	if (visionmode == 4)
		wrouthud (236, 181, NULL, "PLANTVISION");
	if (visionmode == 0)
		wrouthud (236, 181, NULL, "NORMAL");
        if (supvision == 0) {
                sprintf (outhudbuffer, "  %3i" , brightencnt - 63);
        } else {
                sprintf (outhudbuffer, "S:%3i" , brightencnt - 63);
        }
        wrouthud (285, 181, NULL, outhudbuffer);
}


//In noctis-1
float hpoint (Dword px, Dword pz);

#define earth1 200
#define earth2 100
void Earthquake(Word *earthtime) {
	float q, q2;
	q = RANDOM (earth1);
	q = q / 100;
	q2 = RANDOM (earth1);
	q2 = q2 / 100;
	user_beta = user_beta + q;
	user_beta = user_beta - q2;
	q = RANDOM (earth2);
	q = q / 100;
	q2 = RANDOM (earth2);
	q2 = q2 / 100;
	user_alfa = user_alfa + q;
	user_alfa = user_alfa - q2;
	*earthtime = *earthtime - 1;
	double ogy = hpoint (pos_x, pos_z);
	double opx = pos_x; double opz = pos_z;
	pos_x += (RANDOM(1000)-500);
	pos_z += (RANDOM(1000)-500);
	double ngy = hpoint (pos_x, pos_z);
	if (ngy<ogy) {
		pos_x=opx+(pos_x-opx)/(ogy-ngy);
		pos_z=opz+(pos_z-opz)/(ogy-ngy);
	} else if (ngy>ogy) {
		if (ogy!=0) {
			pos_x=opx+(pos_x-opx)*(ngy/ogy);
			pos_z=opz+(pos_z-opz)*(ngy/ogy);
		}
	}
}

extern char jetpack;
extern float fixed_step;
extern float buildingcount;
extern float pp_pulse;

void ShowAdvStat(float crcy, char sctype, float formalappr, float formalbob) {
	
	areaclear (adapted, 13, 11, 153, 61, 0, 0, 72);
	if (jetpack) {
		sprintf (outhudbuffer, "HORIZONTAL SPEED           %2f" , step);
		FitOutHudBuffer(34, 34);
		wrouthud (14, 12, NULL, outhudbuffer);

		sprintf (outhudbuffer, "HORIZONTAL INCREASE        %2f" , fixed_step);
		FitOutHudBuffer(34, 34);
		wrouthud (14, 19, NULL, outhudbuffer);
	} else {
		sprintf (outhudbuffer, "WALKING SPEED              %2f" , step);
		FitOutHudBuffer(34, 34);
		wrouthud (14, 12, NULL, outhudbuffer);
		sprintf (outhudbuffer, "SLIDING SPEED              %2f" , slide);
		FitOutHudBuffer(34, 34);
		wrouthud (14, 19, NULL, outhudbuffer);
	}
	sprintf (outhudbuffer, "POSITION ABOVE SURFACE     %2f" , ((0 - (pos_y) / 1000) + 0.01) - (0 - ((crcy + formalappr) / 1000 - (-1 *formalbob/100)) - 0.009999)       );
	FitOutHudBuffer(34, 34);
	wrouthud (14, 30, NULL, outhudbuffer);
	sprintf (outhudbuffer, "POSITION ABOVE SEALEVEL    %2f" , (0 - (pos_y) / 1000) + 0.01);
	FitOutHudBuffer(34, 34);
	wrouthud (14, 37, NULL, outhudbuffer);
	sprintf (outhudbuffer, "SURFACE HEIGHT             %2f" , 0 - ((crcy + formalappr) / 1000 - (-1 *formalbob/100)) - 0.009999);
	FitOutHudBuffer(34, 34);
	wrouthud (14, 45, NULL, outhudbuffer);
	WriteTerrainType(sctype, False);
	sprintf(outhudbuffer, "TERRAIN TYPE                  ");
	sprintf(outhudbuffer+34-strlen(terrainTypeBuffer), "%s", terrainTypeBuffer);
	wrouthud (14, 53, NULL, outhudbuffer);
	sprintf(outhudbuffer, "CITYSIZE %f" , buildingcount);  //(debug Citysize) (MEGA)
	wrouthud (14, 62, NULL, outhudbuffer);
	sprintf(outhudbuffer, "CAM_X %f" , cam_x);  //(debug CAMX) (MEGA)
	wrouthud (14, 72, NULL, outhudbuffer);
	sprintf(outhudbuffer, "CAM_Y %f" , cam_y);  //(debug CAMY) (MEGA)
	wrouthud (14, 82, NULL, outhudbuffer);
	sprintf(outhudbuffer, "CAM_Z %f" , cam_z);  //(debug CAMZ) (MEGA)
	wrouthud (14, 92, NULL, outhudbuffer);
}

extern  float moviefps;
void CalculatemovieFPS(int moviefscap, int moviestime) {
     float moviesecs;
     moviesecs = (float(gtime) - float(moviestime)) / 18;
     /*sprintf (outhudbuffer, "%2.1f MOVSECS" , moviesecs);
     wrouthud (10, 50, NULL, outhudbuffer);*/
     if (moviesecs != 0) {
        moviefps = float(moviefscap) / float(moviesecs);
     }
}

extern float moviefps;
void ShowMovieSetup(int moviefsec, char movieflashoff, int moviedeck) {
	//float moviesecs;
	//float moviefps;
	char tempsnapfile[24];

	if (movieexists == 0) {								//let's check if the moviedeck exists already.... But only if the moviedeck has been changed lately.
		//wrouthud (10,90, NULL, "CHECKING!!!");
		sprintf (tempsnapfile, "..\\MOVIES\\%03i\\00000001.BMP", moviedeck);
		FILE * tmpFile = fopen(tempsnapfile, "r");
		if (tmpFile == NULL) {
			movieexists = 1;
		} else {
			movieexists = 2;
		}
		fclose(tmpFile);
	}

	areaclear (adapted, 13, 131, 175, 180, 0, 0, 72);
	areaclear (adapted, 13, 138, 175, 139, 0, 0, 90);

	sprintf (outhudbuffer, "MOVIEDECK %3i                 (CTRL +/-)" , moviedeck);
	wrouthud (14, 141, NULL, outhudbuffer);

	sprintf (outhudbuffer, "CAPTURE EVERY %3i FRAMES           (+/-)" , moviefsec);
	wrouthud (14, 148, NULL, outhudbuffer);
	if (movieflashoff == 1) { wrouthud (14, 155, NULL, "NO FLASH WHEN SCREENCAPTURE          (F)"); } else {wrouthud (14, 155, NULL, "BLACK FLASH WHEN SCREENCAPTURE       (F)"); }

	//warning signals
	if ((moviefsec == 1 && movieflashoff == 0) || movieexists == 2) {
		areaclear (adapted, 14, 161, 174, 168, 0, 0, 85);
		wrouthud (15, 162, NULL, "WARNING:");
	}
	if (moviefsec == 1 && movieflashoff == 0) wrouthud (51, 162, NULL, "BLACK SCREEN!");
	if (movieexists == 2) wrouthud (106,162, NULL, "MOVIEDECK EXISTS!");

	//Recording-state-specific-displayings, including FPS-o-meter.
	if (!movie)	{
		wrouthud (14,169, NULL, "START RECORDING                  (ENTER)");

	}
	if (movie) {
	//areaclear (adapted, 13, 131, 175, 180, 0, 0, 72);
	//areaclear (adapted, 13, 138, 175, 139, 0, 0, 90);


		areaclear(adapted, 15, 132, 130, 137, 0, 0, 82);
		wrouthud (14,169, NULL, "STOP RECORDING                   (ENTER)");
		/*sprintf (outhudbuffer, "%i MOVSTIME" , moviestime);
		wrouthud (10, 20, NULL, outhudbuffer);
		sprintf (outhudbuffer, "%i MOVFSCAP" , moviefscap);
		wrouthud (10, 30, NULL, outhudbuffer);*/
		//moviesecs = (float(gtime) - float(moviestime)) / 18;
		/*sprintf (outhudbuffer, "%2.1f MOVSECS" , moviesecs);
		wrouthud (10, 50, NULL, outhudbuffer);*/
		//if (moviesecs != 0) {
                        //moviefps = float(moviefscap) / float(moviesecs);
			areaclear(adapted, 131, 131, 132, 138, 0, 0, 90);
			sprintf (outhudbuffer, "FPS: %2.2f" , moviefps);
			wrouthud (133, 132, NULL, outhudbuffer);
		//}
	}

	wrouthud (14,132, NULL, "  NOCTIS IV CE MOVIEMAKER"); 				//The title. We do this at the end to allow colorings.

	/*if (movieexists == 2) {
		wrouthud (10,80, NULL, "MOVIE EXISTS ALREADY");
	} else if (movieexists == 1) {
		wrouthud (10,80, NULL, "MOVIE DOES NOT EXIST ALREADY - THIS IS GOOD");
	}*/
}

void WriteTerrainType(char sctype, FastBool simplify) {
	switch (sctype) {
		case OCEAN:
			sprintf (terrainTypeBuffer, "OCEAN");
			break;
		case PLAINS:
			sprintf (terrainTypeBuffer, "PLAINS");
			break;
		case DESERT:
			sprintf (terrainTypeBuffer, "DESERT");
			break;
		case ICY:
			Word abslat = 60-abs(landing_pt_lat-60);
			if (abslat<=2) {
				sprintf (terrainTypeBuffer, "ICE CAP");
			} else if (simplify) {
				sprintf (terrainTypeBuffer, "MAYBE TAIGA");
			} else if (treeDensity>30000) {
				sprintf (terrainTypeBuffer, "VERY DENSE TAIGA");
			} else if (treeDensity>20000) {
				sprintf (terrainTypeBuffer, "DENSE TAIGA");
			} else if (treeDensity>8000) {
				sprintf (terrainTypeBuffer, "TAIGA");
			} else if (treeDensity>1000) {
				sprintf (terrainTypeBuffer, "SPARSE TAIGA");
			} else if (treeDensity>=500) {
				sprintf (terrainTypeBuffer, "TUNDRA");
			} else {
				sprintf (terrainTypeBuffer, "ICY");
			}
			break;
		case MOUNTAIN:
			sprintf (terrainTypeBuffer, "MOUNTAIN");
			break;
		case MOUNTAINTOP:
			sprintf (terrainTypeBuffer, "MOUNTAINTOP");
			break;
		case SHRUBLAND:
			sprintf (terrainTypeBuffer, "SHRUBLAND");
			break;
		case RAINFOREST:
			sprintf (terrainTypeBuffer, "RAINFOREST");
			break;
		case JUNGLE:
			sprintf (terrainTypeBuffer, "JUNGLE");
			break;
		case FOREST:
			sprintf (terrainTypeBuffer, "FOREST");
			break;
		case FORESTBORDER:
			sprintf (terrainTypeBuffer, "FORESTBORDER");
			break;
		case GLASSIFIED:
			sprintf (terrainTypeBuffer, "GLASSIFIED");
			break;
		case LAVA:
			sprintf (terrainTypeBuffer, "LAVA");
			break;
		case ALLLAVA:
			sprintf (terrainTypeBuffer, "ALLLAVA");
			break;
		case UNSTABLE:
			sprintf (terrainTypeBuffer, "UNSTABLE");
			break;
		case DUSTY:
			sprintf (terrainTypeBuffer, "DUSTY");
			break;
		case THICKATMO:
			sprintf (terrainTypeBuffer, "THICK ATMOSPHERE");
			break;
		//Also 'FELISIAN' wouldn't really be used, since the first 4 would be used instead.
		case FELISIAN:
			sprintf (terrainTypeBuffer, "FELISIAN");
			break;
		case ROCKY:
			sprintf (terrainTypeBuffer, "ROCKY");
			break;
		case THINATMO:
			sprintf (terrainTypeBuffer, "THIN ATMOSPHERE");
			break;
		case NOTCONSISTANT:
			sprintf (terrainTypeBuffer, "NOT CONSISTANT");
			break;
		case ICYNOATMO:
			sprintf (terrainTypeBuffer, "ICY NO ATMOSPHERE");
			break;
		case QUARTZ:
			sprintf (terrainTypeBuffer, "QUARTZ");
			break;
		case SUBSTELLAR:
			sprintf (terrainTypeBuffer, "SUBSTELLAR");
			break;
		case COMPANIONSTAR:
			sprintf (terrainTypeBuffer, "COMPANION STAR");
			break;
	}
	//sprintf(terrainTypeBuffer+strlen(terrainTypeBuffer), " %li", treeDensity);	//TEMPORARY: For debugging purposes.
}
//Vars in noctis-1:
extern char hopfind;
extern Word animals;
extern float ani_x[];
extern float ani_z[];
extern float ani_quote[];
extern char ani_type[];
extern Uword ani_sqc[];
extern char ani_mtype[];


void BracketAnims() {	//By Bensel (modified slightly by SL, mostly just to make the code more readable (at least to me :P))
	//if (hopfind<=0 || hopfind==2) return;
	if (hopfind==0 || hopfind==2) return;
	flares = 0;
	char range_s[10];
	float ax,ay,az;
	for (Word n = 0; n < animals; n++) {
		if (ani_type[n]!=0) {	//Is this animal valid? (We set it negative each cycle to indicate this is a valid animal). (SL) 
			if (ani_type[n]<0) ani_type[n]=-ani_type[n];
	
			ax = ani_x[n]; az = ani_z[n];
			ay = hpoint (ani_x[n], ani_z[n]) - ani_quote[n];
			double dx = ax - cam_x;
			double dy = ay - cam_y;
			double dz = az - cam_z;
			Dword animal_distance = SQRT (dx*dx + dy*dy + dz*dz) / 1000;
			
			//IMPORTANT: If you increase the range above 9999, change the declaration of range_s above (It needs to be at least 1 plus the number of digits in the maximum valid range, e.g. for 100-999 it should be at least 4, for 1000-9999 it should be at least 5, etc). (SL)
			if (animal_distance<300) {	//Note to self: If this is shows glitchy brackets, lower it again (It was 300 before) (SL)
				if (getcoords(ax,ay,az) && _x_ < 310 && _y_ < 190 && _x_ > 10 && _y_ > 17) { //Make sure to keep these up to date: extreme influence on stability (bensel)
					if (ani_type[n] == MAMMAL) {	// || ani_type[n]==REPTIL) {
						Stick(_x_ - 7, _y_ - 7, _x_ + 7, _y_ - 7);
						Stick(_x_ - 7, _y_ - 7, _x_ - 7, _y_ + 7);
						Stick(_x_ - 7, _y_ + 7, _x_ + 7, _y_ + 7);
						Stick(_x_ + 7, _y_ - 7, _x_ + 7, _y_ + 7);
						char chtype = '!';
						if (ani_type[n]==MAMMAL) {
							switch (ani_mtype[n]) {
								case FELINE_LIKE:	//Charger
									chtype='C';
									break;
								case RABBIT_LIKE:	//Hopper
									chtype='H';
									break;
								case KANGAROO_LIKE:	//Larger, stronger, faster hopper (kangaroo)
									chtype='H';
									break;
								case APE_LIKE:		//Unnamed
									chtype='!';
									break;
								case BULL_LIKE:		//Glider
									chtype='G';
									break;
								default:
									chtype='.';
									break;
							}
						} else if (ani_type[n]==REPTIL) {
							chtype='R';
							//switch (ani_mtype[n]) {
								//none yet
							//}
						}
						#ifdef DEBUG
						sprintf(range_s,"%c%i.%i",chtype,animal_distance,n);
						#else
						sprintf(range_s,"%c%i",chtype,animal_distance);
						#endif
						//itoa(animal_distance, range_s, 10);	
						//itoa does the same thing, but should be faster. (SL)
						Word hl = strlen(range_s);
						hl+=hl;
						Word l = hl+hl;
						
						areaclear (adapted, _x_-hl, _y_-14, 0, 0, l+1, 7, 12); //20
						wrouthud (_x_-hl+1, _y_-13, NULL, range_s, 44);
					} else if (ani_type[n] == BIRD) {
						Stick(_x_, _y_ - 5, _x_ + 5, _y_);
						Stick(_x_ + 5, _y_, _x_, _y_ + 5);
						Stick(_x_, _y_ + 5, _x_ - 5, _y_);
						Stick(_x_ - 5, _y_, _x_, _y_ - 5);
						#ifdef DEBUG
						sprintf(range_s,"%c%i.%i",'B',animal_distance,n);
						#else
						sprintf(range_s,"%c%i",'B',animal_distance);
						#endif
						Word hl = strlen(range_s);
						hl+=hl;
						Word l = hl+hl;
						
						areaclear (adapted, _x_-hl, _y_-14, 0, 0, l+1, 7, 12); //20
						wrouthud (_x_-hl+1, _y_-13, NULL, range_s, 44);
					}
				}
			}
			/*
			-,-;0,-;+,-
	
			-,0;0,0;+,0	
	
			-,+;0,+;+,+
			*/
		}
	}
}

void FitOutHudBuffer(Word min, Word max) {
	Word len = strlen(outhudbuffer);
	//sprintf (outhudbuffer, "            %i", len);
	if (len<max) {
		Word a;
		movetextforward:
		Word move = max-len;
		for (a=len; a>=min; a--) {
			outhudbuffer[a+move]=outhudbuffer[a];
			outhudbuffer[a]=' ';
		}
		for (a=len+1; a<max-len; a++) {
			outhudbuffer[a]=' ';
		}
		//sprintf(outhudbuffer+27,"#A");
	} else if (len>max) {
		for (Word a=len-1; a>=min; a--) {
			if (outhudbuffer[a]=='.') {
				if (a==max-1) {
					outhudbuffer[max]=0;
					len=max-1;
					goto movetextforward;
				} else if (a>=max) {	//34567.23 where '5' is chr 27. a is 30.
					trimtext:
					Word missingdigits = a-(max-2);
					Word writeEAt = (max-2);
					while (missingdigits>9) {
						missingdigits++;
						writeEAt--;
					}
					if (writeEAt<min) {
						sprintf(outhudbuffer+min, "     !!!!!!");
					} else {
						sprintf(outhudbuffer+writeEAt, "E%i", (int)missingdigits);
					}
					outhudbuffer[max]=0;
					break;
				} else { //a<27
					outhudbuffer[max]=0;
					break;
				}
			}
		}
		if (a==min-1) {	//we finished the for loop instead of running break
			a=len;
			goto trimtext;
		}
		//sprintf(outhudbuffer+28,"#B");
	}
}

int IsNaN(long double value) {
	union RealNumber rn;
	rn.value = value;
	return ((rn.parted.exponent & 0x7FFF) == 0x7FFF);
}

extern void fix_local_target();
extern void fix_remote_target();
extern char manual_target;
extern char nsnp;

void ReadCommFile(FastBool deleteIt) {
	/*
		This reads comm.bin to do one of three things:
		1. Force an update of the goes output file. This is used by Hacirc.
		2. Set the local target to specific body.
		3. Set the remote target to a specific system.
		(SL)
		
		This has been modified to use standard file IO instead of DOS file IO, so
		hopefully there can't possibly be any evil abort/retry/fail errors - If the
		file is locked, fopen should simply return NULL, which is the same thing it does
		when the file doesn't exist.
		(SL)
	*/
	FILE * ch = fopen (comm, "rt");
	if (ch != NULL) {
		Word flen = 0;
		FileLength(flen, ch);	//FileLength is a macro.
		if (flen == 1) {
			force_update = 1;
			blinkhudlights = 1;
			blinkhudlights_stay = 1;
		} else if (flen == 2) {
			if (ap_reached) {
				if (pwr > 15000) {
					fread (&ip_targetted, 2, 1, ch);
					ip_targetted--;
					fix_local_target ();
					ip_targetting = 0;
					ip_reached = 0;
					ip_reaching = 1; // partenza automatica
				}
			}
			else
				status ("NEED RECAL", 75);
		}
		if (flen == 24) {
			fread(&ap_target_x, 8, 1, ch);
			fread(&ap_target_y, 8, 1, ch);
			fread(&ap_target_z, 8, 1, ch);
			#ifndef OLD_STARMAP
			ap_target_sx=0x7fffffff;
			ap_target_sy=0x7fffffff;
			ap_target_sz=0x7fffffff;
			if (ap_target_sx==0x7FFFFFFF && ap_target_sy==0x7FFFFFFF && ap_target_sz==0x7FFFFFFF) {
				//Sector coords unknown; find them!
				Dword retval=getSectorCoords(ap_target_x, ap_target_y, ap_target_z, &ap_target_sx, &ap_target_sy, &ap_target_sz);
				if (retval==-1) {
					status ("ACK! CAN'T FIND SYSTEM", 200);
				} else if (retval==-2) {
					status ("WARNING: CATNIP OVERDOSE! SYSTEM IMAGINARY.", 201);
				}
			}
			#endif
			
			landing_point = 0;	//FIXED the 'I can land on unlandable objects if I start the landing request on a landable one and change target before landing!' bug (SL)
			ap_targetting = 1;
			extract_ap_target_infos ();
			fix_remote_target ();
			ap_targetting = 0;
			if (lithium_collector || manual_target)
				status ("CONFLICT", 50);
			else {
				if (pwr > 15000) {
					stspeed = 1; // partenza automatica
					if (ap_targetted) {
						nsnp = 1;
						ap_reached = 0;
						ip_reached = 0;
						ip_targetted = -1;
					}
				}
			}
		}
		fclose (ch);
		if (deleteIt) {
			remove (comm);
		}
	}
}


Dword ImulAndAdd(Dword main, Dword secondary) {
	Dword result = 0;
	asm {
		MOV_EDX_DWORD_PTR main
		MOV_EAX_DWORD_PTR secondary
		DB_0x66; imul MAYBE_EDX
		DB_0x66; add MAYBE_EDX, MAYBE_EAX
		MOV_DWORD_PTR result, MAYBE_EDX
	}
	return result;
}
#define advance 100000
#define halfadvance 50000
#define analyzed_sectors_range 9

double FSAT (double center_x, double center_y, double center_z, Dword *ret_sx, Dword *ret_sy, Dword *ret_sz) {

	//double laststar_id;
	double laststar_dist;
	Dword laststar_x, laststar_y, laststar_z, beststar_x, beststar_y, beststar_z, beststar_sx, beststar_sy, beststar_sz;
	double beststar_dist;
	
	Uword visible_sectors_x = 10;
	Uword visible_sectors_y = 10;
	Uword visible_sectors_z = 10;

	Dword   	sect_x, sect_y, sect_z;

	sect_x = (center_x - visible_sectors_x*halfadvance) / advance; sect_x *= advance;
	sect_y = (center_y - visible_sectors_y*halfadvance) / advance; sect_y *= advance;
	sect_z = (center_z - visible_sectors_z*halfadvance) / advance; sect_z *= advance;
	beststar_dist = -1;
	Word rarity_factor;
	Dword xz, mixer;
	Dword start_y = sect_y;
	Dword start_z = sect_z;
	Bool thisIsIt = False;
	
	for (Uword sx = visible_sectors_x; sx>0; sect_y=start_y, sect_x+=advance, sx--) {
		DOUBLE_ dsect_x = sect_x; dsect_x*=dsect_x;
		for (Uword sy = visible_sectors_y; sy>0; sect_z=start_z, sect_y+=advance, sy--) {
			DOUBLE_ dsect_y = sect_y; dsect_y=30*fabs(dsect_y);
			for (Uword sz = visible_sectors_z; sz>0; sect_z+=advance, sz--) {
				//asm { PUSHAD }
				DOUBLE_ dsect_z = sect_z; dsect_z*=dsect_z;
				DOUBLE_ distance_from_home = SQRT (dsect_x + dsect_z) + dsect_y;
				rarity_factor = (Dword)(distance_from_home * 0.25e-8);
				rarity_factor = 1 << rarity_factor;
				rarity_factor--;
				//asm { POPAD }
				/*asm { PUSHAD 
					fild dword ptr sect_x
					fild dword ptr sect_x
					fmulp
					fild dword ptr sect_z
					fild dword ptr sect_z
					fmulp
					faddp
					
					
					POPAD
					}
				*/
				xz = sect_x + sect_z;
				laststar_x = (xz & 0x1ffff) + sect_x - halfadvance;
				if (laststar_x!=0) {
					laststar_y = ImulAndAdd(laststar_x, xz);
					mixer = xz + laststar_y;
					laststar_y = (laststar_y & 0x1ffff) + sect_y - halfadvance;
					if (laststar_y!=0) {
						laststar_z = ImulAndAdd(laststar_y, mixer);
						laststar_z = (laststar_z & 0x1ffff) + sect_z - halfadvance;
						if (laststar_z!=0) {
							if (center_x==(double)laststar_x && center_y==(double)laststar_y && center_z==(double)laststar_z) {
								thisIsIt=True;
							}
							Word rarity = (((Word)(laststar_x)) + ((Word)(laststar_y)) + ((Word)(laststar_z)));
							
							if ((rarity & rarity_factor) == 0) {
								/*if (thisIsIt) {
									#ifdef WINDOWS
									fprintf(stderr, 
									#else
									DebugPrintf(0,
									#endif
									"FSAT succeeded: sect_x(%li) sect_y(%li) sect_z(%li) dsect_x(%f) dsect_y(%f) dsect_z(%f) distance_from_home(%f) rarity(%li) rarity_factor(%li) x(%li) y(%li) z(%li) dsect_xli(%li) dsect_yli(%li) dsect_zli(%li) distance_from_homeli(%li)\n", (long)sect_x, (long)sect_y, (long)sect_z, (double)dsect_x, (double)dsect_y, (double)dsect_z, (double)distance_from_home, (long) rarity, (long) rarity_factor, (long) laststar_x, (long) laststar_y, (long) laststar_z, (long)dsect_x, (long)dsect_y, (long)dsect_z, (long) distance_from_home);
								}*/
								double dlsx = (double) laststar_x;
								double dlsy = (double) laststar_y;
								double dlsz = (double) laststar_z;
								laststar_dist = (dlsx-center_x)*(dlsx-center_x) + (dlsy-center_y)*(dlsy-center_y) + (dlsz-center_z)*(dlsz-center_z);
								//laststar_id = ((double)laststar_x)/advance*((double)laststar_y)/advance*((double)laststar_z)/advance;
								//DebugPrintf(0, "Star x=%li (%f) y=%li (%f) z=%li (%f) d=%f", laststar_x, center_x, laststar_y, center_y, laststar_z, center_z, laststar_dist);
								if (beststar_dist==-1 || laststar_dist<beststar_dist) {
									beststar_dist = laststar_dist; beststar_x=laststar_x; beststar_y=laststar_y; beststar_z=laststar_z;
									beststar_sx=sect_x;
									beststar_sy=sect_y;
									beststar_sz=sect_z;
								}
							/*} else if (thisIsIt) {
								#ifdef WINDOWS
								fprintf(stderr, 
								#else
								DebugPrintf(0,
								#endif
								"FSAT cancelled: sect_x(%li) sect_y(%li) sect_z(%li) dsect_x(%f) dsect_y(%f) dsect_z(%f) distance_from_home(%f) rarity(%li) rarity_factor(%li) x(%li) y(%li) z(%li) dsect_xli(%li) dsect_yli(%li) dsect_zli(%li) distance_from_homeli(%li)\n", (long)sect_x, (long)sect_y, (long)sect_z, (double)dsect_x, (double)dsect_y, (double)dsect_z, (double)distance_from_home, (long) rarity, (long) rarity_factor, (long) laststar_x, (long) laststar_y, (long) laststar_z, (long)dsect_x, (long)dsect_y, (long)dsect_z, (long) distance_from_home);
								return -2;
								//DebugPrintf(1, "The star you're at should not exist!");
								*/
							}
							thisIsIt=False;
						} else if (thisIsIt) {
							DebugPrintf(0, "FSAT cancelled due to z=0.");
						}
					} else if (thisIsIt) {
						DebugPrintf(0, "FSAT cancelled due to u=0.");
					}
				} else if (thisIsIt) {
					DebugPrintf(0, "FSAT cancelled due to x=0.");
				}
			}
		}
	}
	if (ret_sx!=NULL && ret_sy!=NULL && ret_sz!=NULL) {
		if (center_x==(double)beststar_x && center_y==(double)beststar_y && center_z==(double)beststar_z) {
			*ret_sx = beststar_sx;
			*ret_sy = beststar_sy;
			*ret_sz = beststar_sz;
			return 0;
		} else {
			return -1;
		}
	} else {
		if (beststar_dist>-1 && beststar_dist<=346411) {
			ap_target_x = (double)beststar_x;
			ap_target_y = (double)beststar_y;
			ap_target_z = (double)beststar_z;
			ap_target_sx = beststar_sx;
			ap_target_sy = beststar_sy;
			ap_target_sz = beststar_sz;
			extract_ap_target_infos ();
			fix_remote_target ();
			return beststar_dist;
		} else {
			return -1;
		}
	}
}
double FindStarAndTarget (double center_x, double center_y, double center_z) {
	return FSAT(center_x, center_y, center_z, NULL, NULL, NULL);
}

//Determine if there is a star from sector (sect_x, sect_y, sect_z), and if so store its coords in laststar_x/y/z.
FastBool isthere3(Dword sect_x, Dword sect_y, Dword sect_z) {
	double dsect_x = sect_x; dsect_x*=dsect_x;
	double dsect_y = sect_y; dsect_y=30*fabs(dsect_y);
	double dsect_z = sect_z; dsect_z*=dsect_z;
	double distance_from_home = SQRT (dsect_x + dsect_z) + dsect_y;
	Dword rarity_factor;
	Dword xz, mixer;
	rarity_factor = distance_from_home * 0.25e-8;
	rarity_factor = 1 << rarity_factor;
	rarity_factor--;
	Dword lsx, lsy, lsz;
	xz = sect_x + sect_z;
	lsx = (xz & 0x1ffff) + sect_x - halfadvance;
	if (lsx!=0) {
		lsy = ImulAndAdd(lsx, xz);
		mixer = xz + lsy;
		lsy = (lsy & 0x1ffff) + sect_y - halfadvance;
		if (lsy!=0) {
			lsz = ImulAndAdd(lsy, mixer);
			lsz = (lsz & 0x1ffff) + sect_z - halfadvance;
			if (lsz!=0) {
				Word rarity = (((Word)lsx) + ((Word)lsy) + ((Word)lsz));
				if ((rarity & rarity_factor) == 0) {
					laststar_x = (double) lsx;
					laststar_y = (double) lsy;
					laststar_z = (double) lsz;
					laststar_sx = sect_x;
					laststar_sy = sect_y;
					laststar_sz = sect_z;
					return 1;
				}
			}
		}
	}
	return 0;
}

#undef advance
#undef halfadvance
#undef analyzed_sectors_range

void TargetParsisOrStar() {
	//if (distance>346411) {	//It shouldn't be able to be farther away than 346410.16151377545870548926830117 without being outside the sector.
	//(That might be wrong)
	double distance = FindStarAndTarget(ap_target_x, ap_target_y, ap_target_z);
	if (distance==-1) {
		fix_remote_target ();
		if (ap_targetted==1) {	//FIXED: Able to fly to OUTOFRANGE parsis-targets. (SL)
			ap_targetted = -1;
		}
	} else {	//We've already targetted it then
		
	}
	/*
	} else {
		ap_target_x = targetStarX;
		ap_target_y = targetStarY;
		ap_target_z = targetStarZ;
		extract_ap_target_infos ();
		fix_remote_target ();
	}
	*/
	//sprintf(goesnet_command , " x say Distance %f.", distance);
	
}

Dword getSectorCoords(double x, double y, double z, Dword *sx, Dword *sy, Dword *sz) {
	double retval=FSAT(x, y, z, sx, sy, sz);
	if (retval==-1 || retval==-2) {
		//failure
		*sx=0x7FFFFFFF;
		*sy=0x7FFFFFFF;
		*sz=0x7FFFFFFF;
		return (Dword) retval;
	} else {
		//success, and sx, sy, and sz were set by FSAT.
		//We don't need to do anything special here.
		return 0;
	}
}

void tavola_colori_brightness (Uchar *nuova_tavolozza,
		    Uword colore_di_partenza, Uword nr_colori,
		    char filtro_rosso, char filtro_verde, char filtro_blu, char brightness) {
	if (brightness==63) {
		tavola_colori(nuova_tavolozza, colore_di_partenza, nr_colori, filtro_rosso, filtro_verde, filtro_blu);
	} else {
		Dword red, green, blue;
		red = (Dword)((filtro_rosso/63.0)*(brightness/63.0)*63.0);
		if (red>127) red=127;
		if (filtro_verde==filtro_rosso) {
			green = red;
		} else {
			green = (Dword)((filtro_verde/63.0)*(brightness/63.0)*63.0);
			if (green>127) green=127;
		}
		
		if (filtro_blu==filtro_rosso) {
			blue = red;
		} else if (filtro_blu==filtro_verde) {
			blue = green;
		} else {
			blue = (Dword)((filtro_blu/63.0)*(brightness/63.0)*63.0);
			if (blue>127) blue=127;
		}
		tavola_colori(nuova_tavolozza, colore_di_partenza, nr_colori, (char)red, (char)green, (char)blue);
	}
}


Word MixColors (Uchar *orig_tavolozza, Uchar *nuova_tavolozza,
		    Uword firstColor, Uword numColors,
		    Word filtro, Word brightness)
{
	Word c, cc = 0, match=1;
	Udword temp;
	Uword tripledNumColors = numColors * 3;
	Uword tripledFirstColor = firstColor * 3;
	
	Dword retval = filtro+1;
	if (retval>1024) retval=-1;
		
	c = tripledFirstColor;
	Dword revfiltro = 1024-filtro;
	while (cc<tripledNumColors) {
		if (nuova_tavolozza[cc]!=orig_tavolozza[cc]) {
			match=0;
			temp = ((((Dword)nuova_tavolozza[cc])*filtro + ((Dword)orig_tavolozza[cc])*revfiltro))>>10;
			if (brightness!=63) {
				temp=(temp*brightness)/63;
			}
			if (temp > 63) temp = 63;
		} else {
			temp = orig_tavolozza[cc];
		}
		tmppal[c] = temp;
		cc++;
		c++;
	}
	
	if (match) {
		retval=-1;
	} else {
		#ifdef WINDOWS
		WinStorePalette(firstColor, numColors, tmppal);
		#else
		asm {
			push si
			push ax
			push cx
			push dx
			mov cx, tripledFirstColor
			add cx, tripledNumColors
			lea si, tmppal
			mov ax, seg tmppal
			mov ds, ax
			mov dx, 0x3c8
			mov al, 0
			out dx, al
			inc dx
		}
	dzap:	asm {
			mov al, [si]
			out dx, al
			inc si
			loop dzap
			pop dx
			pop cx
			pop ax
			pop si
		}
		#endif
	}
	return retval;
}

//ShadeMatrix takes a set of defined colors and creates a palette composed of transitions between those colors (and the colors themselves)

//The purpose of passing numColors and then checking it is to make sure that person calling ShadeMatrix knows how many palette entries
//will actually be used, because if they don't, they're probably going to overwrite some palette entries they shouldn't, or overflow the palette
//buffer, or something similarly bad. (SL)
/*
void ShadeMatrix (Uchar *palette, int colorbase, int numElements, int numTransitionShades, int numColors, char *terrain_color_feed) {
	int accurateNumColors = numElements + numElements*(numElements-1)*numTransitionShades;
	if (accurateNumColors!=numColors) {
		status("SHADEMATRIX ERROR - INCORRECT NUMCOLORS", 200);
		DebugPrintf(1, "Incorrect numcolors: accurateNumColors=%i, numColors=%i", accurateNumColors, numColors);
	}
	int colorPalettePos = 0;
	int colorBasePalettePos = colorbase*3;
	float fshadeChange = 1.0/(numTransitionShades+1.0);
	int transitionPalettePos = (colorbase+numElements)*3;
	for (int firstElement = 0; firstElement<numElements; firstElement++, colorPalettePos+=3, colorBasePalettePos+=3) {
		palette[colorBasePalettePos]=terrain_color_feed[colorPalettePos];
		palette[colorBasePalettePos+1]=terrain_color_feed[colorPalettePos+1];
		palette[colorBasePalettePos+2]=terrain_color_feed[colorPalettePos+2];
		for (int secondElement = 0, secondColorPalettePos=0; secondElement<numElements; secondElement++, secondColorPalettePos+=3) {
			if (secondElement!=firstElement) {
				float fshade = 0+fshadeChange;
				float sshade = 1-fshadeChange;
				for (int shade = 0; shade<numTransitionShades; shade++, transitionPalettePos+=3, fshade+=fshadeChange, sshade-=fshadeChange) {
					palette[transitionPalettePos] = terrain_color_feed[colorPalettePos]*fshade + terrain_color_feed[secondColorPalettePos]*sshade;
					palette[transitionPalettePos+1] = terrain_color_feed[colorPalettePos+1]*fshade + terrain_color_feed[secondColorPalettePos+1]*sshade;
					palette[transitionPalettePos+2] = terrain_color_feed[colorPalettePos+2]*fshade + terrain_color_feed[secondColorPalettePos+2]*sshade;
				}
			} else {
				
			}
		}
	}
	
	if (transitionPalettePos/3-colorbase!=(accurateNumColors)) {
		status("SHADEMATRIX ERROR - INCORRECT FORMULA", 205);
		DebugPrintf(1, "Incorrect numcolors: transitionPalettePos/3-colorbase=%i, accurateNumColors=%i", transitionPalettePos/3-colorbase, accurateNumColors);
	}
	
}*/

//Mode 0 is simulating the entire planet surface, changing background as we go (that's done elsewhere). Mode 1 is just getting one sector.

/*
This should scan albedo near our current sector:

Base values:
60-63: Mountainous snowy
45-59: Mountainous rocky
25-44: Plains
0-24: Ocean

Flags:
64: Desert
128: Forest

Mountains will tend to create desert to their immediate east, and fertile lands (whatever those are) to their west.
*/
#pragma pack(1)
struct TerrainFeatureInfo {
	Uchar lat;
	Uword lon;
	Uchar size;
};
#pragma pack()

TerrainFeatureInfo *mountains;
TerrainFeatureInfo *deserts;
TerrainFeatureInfo *forests;
TerrainFeatureInfo *rainforests;

//long numRandom;
//long numNormal;

//TerrainType currentTerrainType;
Word numMountains, numDeserts, numForests,numRainforests;

void CalculateTerrainTypes(Word replaceMap, Word logical_id, Uchar * background) {
	Dword global_surface_seed = (nearstar_p_ray[logical_id]
			  + nearstar_p_orb_ray[logical_id]
			  + nearstar_p_orb_orient[logical_id]) * 4112;	//I wonder what the 4112 is for (SL)
	fast_srand (global_surface_seed);
	SRAND (global_surface_seed);
	if (replaceMap) {
		Dword ptr=0;
		//numRandom=0; numNormal=0;
		for (Word lat = 0; lat <= 120; lat++) {
			for (Word lon = 0; lon < 360; lon++, ptr++) {
				Word sctype = GetTerrainType(-1, logical_id, background, lat, lon, ptr);
				switch (sctype) {
					case OCEAN:
						background[ptr]=1;
						break;
					case FOREST:
						background[ptr]=2;
						break;
					case PLAINS:
						background[ptr]=3;
						break;
					case SHRUBLAND:
						background[ptr]=4;
						break;
					case DESERT:
						background[ptr]=5;
						break;
					case ICY:
						background[ptr]=6;
						break;
					
				}
				background[ptr]+=8;
			}
		}
	}
}

//lon ranges from 0-359, lat from 1-119.
Dword MixSeeds(Dword global, Word lon, char lat) {
	Dword result = 0;
	result = (((lon&0x1)&(lat&0x1)) + ((lon&0x2)|(lat&0x2)) + ((lon&0x3)^(lat&0x3)) + ((lon&0x4)&(lat&0x4)) + ((lon&0x5)|(lat&0x5)) + ((lon&0x6)^(lat&0x6))) + ((global*42840 + (119*lon + (lat-1))))<<6;
	return result;
}

Word GetTerrainType(Word rawAlbedoParam, Word logical_id, Uchar * background, Word lat, Word lon, Dword ptr) {
	static Dword last_global_surface_seed_raw=-1;
	
	Word raw_albedo;
	if (rawAlbedoParam>-1) {
		raw_albedo = rawAlbedoParam;
	} else {
		raw_albedo = background[ptr];
	}
	//double avgMoisture, moistureRange;
	//local_surface_seed = global_surface_seed*42840 + (119*landing_pt_lon + (landing_pt_lat-1));	//This should prevent duplication of sectors. (SL)
	//Ranges: lon = 0 to 359, lat = 1 to 119, and 360*119 = 42840 (SL)
	float latitude = (float)(abs (lat - 60)) * 1.5;
	Dword global_surface_seed = (nearstar_p_ray[logical_id]
			  + nearstar_p_orb_ray[logical_id]
			  + nearstar_p_orb_orient[logical_id]) * 4112;	//I wonder what the 4112 is for (SL)
	Dword global_surface_seed_raw = global_surface_seed;
	fast_srand(global_surface_seed_raw);
	SRAND(global_surface_seed_raw);
	static double icelat = 0;
	static Dword oceanRange = 25;
	static Dword forestRange = 5;
	static Dword plainsRange = 10;
	static Dword shrublandRange = 10;
	if (global_surface_seed_raw!=last_global_surface_seed_raw) {
		//DebugPrintf(1, "Drawing %li.", global_surface_seed_raw);
		Dword randTemp = 0;
		Word a;
		for (a=0; a<8; a++) {
			randTemp+=fast_random(1023)+1;
		}
		//icelat = (double)randTemp/8192.0;
		icelat = (randTemp%200)*0.01;
		//#ifdef WINDOWS
		//fprintf(stderr, "icelat is %f", icelat);
		//#endif
		randTemp=0;
		for (a=0; a<8; a++) {
			randTemp+=fast_random(1023)+1;
		}
		oceanRange = (Dword) (25*((double)randTemp/4096.0));
		randTemp=0;
		for (a=0; a<8; a++) {
			randTemp+=fast_random(1023)+1;
		}
		forestRange = (Dword) (5*((double)randTemp/4096.0));
		randTemp=0;
		for (a=0; a<8; a++) {
			randTemp+=fast_random(1023)+1;
		}
		plainsRange = (Dword) (10*((double)randTemp/4096.0));
		randTemp=0;
		for (a=0; a<8; a++) {
			randTemp+=fast_random(1023)+1;
		}
		shrublandRange = (Dword) (10*((double)randTemp/4096.0));
		Dword * nsid = (Dword*) (&nearstar_identity);
		Dword starSeed1 = *nsid;
		Dword starSeed2 = *(nsid+1);
		
		if (starSeed1==0x490600E4 && starSeed2==0x409F7D6F && logical_id==3) {	//Piece of Ice
			icelat = 0;
		}
	}
	
	SRAND (global_surface_seed + lon);
	
	if (latitude > 25 + (global_surface_seed % 15) + RANDOM(5))
		global_surface_seed++;
	
	Dword local_surface_seed = (MixSeeds(global_surface_seed, lon, lat));
	fast_srand (local_surface_seed);
	SRAND (local_surface_seed);
	char sctype = 0;
	if (RANDOM(100) > 5) {
		Dword cpos   = 555 * nearstar_p_orb_orient[ip_targetted];
		sctype = (cpos % 4) + 1;
	}
	else {
		sctype = RANDOM(4)+1;
	}
	// correzioni per le regioni oceaniche
	if (raw_albedo < oceanRange)
		sctype = OCEAN;
	else if (raw_albedo < oceanRange+forestRange)
		sctype = FOREST;
	else if (raw_albedo < oceanRange+forestRange+plainsRange)
		sctype = PLAINS;
	else if (raw_albedo < oceanRange+forestRange+plainsRange+shrublandRange)
		sctype = SHRUBLAND;
	else
		sctype = DESERT;
	Dword icelatmod = (Dword) (icelat*60);
	// correzioni per le regioni polari
	if (latitude > (20+icelatmod)) {
		sctype = ICY;
	}
	if (latitude > (-10+icelatmod)) {
		if (RANDOM (6)==0) sctype = ICY;
	}
	if (latitude > (-5+icelatmod)) {
		if (RANDOM (5)==0) sctype = ICY;
	}
	if (latitude > (0+icelatmod)) {
		if (RANDOM (4)==0) sctype = ICY;
	}
	if (latitude > (5+icelatmod)) {
		if (RANDOM (3)==0) sctype = ICY;
	}
	if (latitude > (10+icelatmod)) {
		if (RANDOM (2)==0) sctype = ICY;
	}
	if (latitude > (15+icelatmod)) {
		if (RANDOM (3)>0) sctype = ICY;
	}
	
	last_global_surface_seed_raw = global_surface_seed_raw;
	return sctype;
}

double WrappedDist(Dword a, Dword b, Dword min, Dword max) {
	Dword result = a-b;
	Dword range = max-min;
	if (abs(result)>(range>>1)) {
		if (a<b) {
			result = (a+range-b);
		} else {
			result = (a-range-b);
		}
	}
	return result;
}
extern void cloudy_sky (Word density, Word smooths);
void SetFelisianPlanetColors(float *fr, float *fg, float *fb, float tr, float tg, float tb, float br, float bg, float bb, Word sctype) {
	// colori per il cielo.
	fr[1] = br * 0.5 + 0.5 * flandom();
	fg[1] = bg * 0.5 + 0.5 * flandom();
	fb[1] = bb * 0.5 + 0.5 * flandom();
	
	
	switch (sctype) {
		case OCEAN:
			// albedo bassa (32-39): oceani liquidi
			// ------------------------------------
			// colori per le terre emerse.
			fr[0] = 0.65 + 0.5 * flandom();
			fg[0] = 0.45 + 0.4 * flandom();
			fb[0] = 0.25 + 0.3 * flandom();
			if (fg[0] < 0.6) fg[0] *= 2;
			// colori per il mare.
			fr[2] = 0.8 * flandom();
			fg[2] = 0.8 * flandom();
			fb[2] = fb[0] * 2 + 0.4;
			// colori per la vegetazione.
			fr[3] = 0.2 + flandom();
			fg[3] = 0.4 + flandom();
			fb[3] = flandom() * 0.6;
			// cielo (solitamente) gremito di nubi.
			cloudy_sky (50, 1);
			break;
		case FOREST:
			fr[0] = 0.45 + 0.5 * flandom();
			fg[0] = 0.475 + 0.4 * flandom();
			fb[0] = 0.25 + 0.3 * flandom();
			if (fg[0] < 0.675) fg[0] *= 1.75;
			fr[2] = (flandom() * 0.6) + fr[0] * 0.3;
			fr[2] = (flandom() * 0.75) + fg[0] * 0.3;
			fr[2] = (flandom() * 0.3) + fb[0] * 0.3;
			fr[3] = flandom();
			fg[3] = flandom();
			fb[3] = flandom();
			cloudy_sky (42, 1);
			break;
		case PLAINS: // albedo media (40-47): prateria stepposa e zone "verdi".
			// ------------------------------------
			// colori per le terre emerse.
			fr[0] = 0.25 + 0.5 * flandom();
			fg[0] = 0.50 + 0.4 * flandom();
			fb[0] = 0.25 + 0.3 * flandom();
			if (fg[0] < 0.75) fg[0] *= 1.5;
			// colori per l'orizzonte.
			fr[2] = (flandom() * 0.4) + fr[0] * 0.3;
			fr[2] = (flandom() * 0.7) + fg[0] * 0.3;
			fr[2] = (flandom() * 0.2) + fb[0] * 0.3;
			// colori per la vegetazione.
			fr[3] = flandom();
			fg[3] = flandom();
			fb[3] = flandom();
			// cielo mediamente nuvoloso, pioggie in normali quantit…...
			cloudy_sky (33, 1);
			break;
		case SHRUBLAND:
			fr[0] = tr + flandom() * 0.33 + 0.25;
			fg[0] = tg + flandom() * 0.25 + 0.50;
			fb[0] = tb + flandom() * 0.12 + 0.25;
			if (fg[0] < 0.75) fg[0] *= 1.5;
			fr[2] = (flandom() * 0.2) + tr;
			fr[2] = (flandom() * 0.35) + tg;
			fr[2] = (flandom() * 0.1) + tb;
			fr[3] = 0.75 * flandom();
			fg[3] = 0.95 * flandom();
			fb[3] = 0.70 * flandom();
			cloudy_sky (20, 1);
			break;
		case DESERT: // albedo medio-alta (48-55): aree (semi)desertiche
			// ------------------------------------
			// colori per le terre emerse.
			fr[0] = tr + flandom() * 0.33;
			fg[0] = tg + flandom() * 0.25;
			fb[0] = tb + flandom() * 0.12;
			// colori per l'orizzonte.
			fr[2] = tr;
			fg[2] = tg;
			fb[2] = tb;
			// colori per la vegetazione.
			fr[3] = 2.0 * flandom();
			fg[3] = 2.0 * flandom();
			fb[3] = 2.0 * flandom();
			// cielo molto pulito, pioggie molto scarse...
			cloudy_sky (10, 1);
			break;
		case ICY: // albedo alta (56-63): nevi perenni e ghiacciai.
			// ------------------------------------
			// colori per le terre emerse.
			fr[0] = 0.25 + flandom ();
			fg[0] = 0.55 + flandom ();
			fb[0] = 1.00 + flandom ();
			// colori per l'orizzonte.
			fr[2] = fr[0] * 0.6;
			fg[2] = fg[0] * 0.8;
			fb[2] = fb[0];
			// colori per la vegetazione.
			fr[3] = 0.95 * flandom ();
			fg[3] = 0.95 * flandom ();
			fb[3] = 0.95 * flandom ();
			// cielo pulito (poca umidit… nell'aria)
			cloudy_sky (15, 1);
			break;
	}

}

extern char hideplants;
extern Word detail_seed;
extern float rootheight;

void cacti (float x, float y, float z, Dword depth)
{ // draws a cactus

	// da 48 mt in poi: ammasso di foglie.
	if (!hideplants) {
		switch (depth) {
			case 2: // 32 -- 48 mt: visibili i ramoscelli pi— grandi.
				build_cacti (x, y, z, 3000, 0.75, 0.15, fast_random(7), 1, 1.5, 0x00, 0xC0, 180, 1, 0);
				break;
			case 1: // 16 -- 32 mt: visibili il 50% delle ramificazioni.
				build_cacti (x, y, z, 3000, 0.75, 0.15, fast_random(7), 1, 1.5, 0x00, 0xC0, 120, 1, 0);
				break;
			case 0: //  0 -- 16 mt: cespuglio completo.
				build_cacti (x, y, z, 3000, 0.75, 0.15, fast_random(7), 1, 1.5, 0x00, 0xC0, 120, 1, 0);
		}
	}
}

void build_cacti (float x, float y, float z,
			 float scaling, float reduction, float globalwidth,
			 Dword layers, Dword divisions, float distance_from_perfection,
			 Uchar rootcolormask, Uchar leafcolormask,
			 float branchdetail, char isrootnode, char occurrence)
{ // funzione ricorsiva: eventualmente traccia l'intero albero pseudo-casuale,
  // con una struttura multilivello, ma va usata con parsimonia perch‚ Š
  // ovviamente una cosa piuttosto laboriosa in termini di tempo.
  //
  // serve qualche spiegazione per i parametri, che sono davvero tantini...
  //
  // P(x;y;z)    - origine del tronco (punto medio della base del tronco)
  // scaling     - altezza del tronco (determina anche la lunghezza dei rami)
  // reduction   - coefficiente di riduzione della lunghezza dei rami,
  //		   calcolata rispetto a quella del tronco livello per livello
  // globalwidth - coefficiente che determina la larghezza dei rami,
  //		   calcolata rispetto alla loro lunghezza
  // layers	 - numero di processi ricorsivi di suddivisione del tronco
  //		   una buona tattica per disegnare ciuffi d'erba Š porre
  //		   questo parametro E il successivo entrambi a zero...
  // divisions   - maschera delle ramificazioni della cima di ogni ramo
  // branchdetail- step di rotazione nel tracciamento dei rami
  //		   (p.es. 120 traccia 360/120 = 3 poligoni per ramo)...
  //		   il minimo livello di dettaglio Š 360, un poligono per ramo,
  //		   180 fa i rami piatti ma visibili da ogni lato,
  //		   e infine 120 approssima piuttosto bene...
  // isrootnode  - chiamare la funzione con questo parametro impostato a 1
  //		   per ottenere un albero normale, anche se si possono
  //		   agevolmente disegnare dei cespugli semplicemente ponendo
  //		   questo flag a zero, poich‚ un cespuglio pu• essere anche
  //		   approssimato, in effetti, come un albero senza tronco
  // occurrence  - Š un contatore, va semplicemente posto a zero.
  //
  // rootcolormask Š il colore di base per il tronco e per i rami.
  // leafcolormask Š il colore di base per le foglie.
  //
  // distance_from_perfection, infine, Š un coefficiente in gradi, che
  // esprime di quanto i rami "figli" possono divaricarsi ad ogni ulteriore
  // suddivisione dei rami "padri": in pratica, distance_from_perfection
  // rappresenta l'irregolarit… generale dell'albero - ad esempio, per le
  // latifoglie bisognerebbe impostare questo parametro ad un valore alto
  // (diciamo all'incirca 1.25), mentre per qualcosa di pi— simile ad una
  // conifera questo valore andrebbe abbassato all'incirca a 0.3 .. 0.4
  // (ponendolo a zero si otterrebbero rami in una colonna verticale).

	
	Word   subdivs;
	char  polycolor;
	char  pf = flares;

	float x2, y2, z2;
	float fx[4], fy[4], fz[4];
	float widthscale1, widthscale2;
	float b_angle, b_angle_delta, range, rotation, rlimit;
	//float rot2, rot3;

	Dword hm, vm, lseed;
	Uchar huge *previoustexture;

	widthscale1 = scaling * globalwidth;
	widthscale2 = reduction * scaling * globalwidth;

	if (isrootnode) {
		subdivs = 1;
		range = scaling * distance_from_perfection * 0.2;
	}
	else {
		subdivs = 1 + fast_random (layers);
		if (subdivs>3) subdivs=3;
		range = scaling * distance_from_perfection * 0.5;
	}
	//range=1;
	//branchdetail=1;
	rlimit = 360 - branchdetail;
	//b_angle = -M_PI/2;
	//subdivs=1;
	//b_angle_delta=0;
	if (subdivs>1) {
		b_angle = 0;
	} else {
		range=0;
		b_angle_delta = 0;
		b_angle = 0;
	}

	lseed = x + y + z + detail_seed;
	while (subdivs) {
		fast_srand (lseed);
		lseed += 3;
		//
		if (layers) {
			if (subdivs==1) {
				range=0;
			} else {
				b_angle=fast_flandom()*(M_PI*2);
				
			}
			flares = 8;
			hm = H_MATRIXS; vm = V_MATRIXS;
			H_MATRIXS = 3; V_MATRIXS = 8;
			change_txm_repeating_mode();
			previoustexture = txtr;
			x2 = x + cos(b_angle) * range;
			z2 = z + sin(b_angle) * range;
			if (isrootnode)
				y2 = y - (fast_flandom() * rootheight + 0.1) * scaling;
			else
				y2 = y - (fast_flandom() + 0.25) * scaling * 0.25;
			fy[0] = y;
			fy[1] = y;
			fy[2] = y2;
			fy[3] = y2;
			polycolor = 9 * occurrence;
			for (rotation = 0; rotation <= rlimit; rotation += branchdetail) {
				fx[0] = x  + lft_cos[(Dword)rotation		] * widthscale1;
				fz[0] = z  + lft_sin[(Dword)rotation               ] * widthscale1;
				fx[1] = x  + lft_cos[(Dword)(rotation + branchdetail)] * widthscale1;
				fz[1] = z  + lft_sin[(Dword)(rotation + branchdetail)] * widthscale1;
				fx[2] = x2 + lft_cos[(Dword)(rotation + branchdetail)] * widthscale2;
				fz[2] = z2 + lft_sin[(Dword)(rotation + branchdetail)] * widthscale2;
				fx[3] = x2 + lft_cos[(Dword)rotation               ] * widthscale2;
				fz[3] = z2 + lft_sin[(Dword)rotation               ] * widthscale2;
				if (isrootnode) {
					fy[0] = hpoint (fx[0], fz[0]);
					fy[1] = hpoint (fx[1], fz[1]);
				}
				if (facing (fx, fy, fz))
					polymap (fx, fy, fz, 4, polycolor + rootcolormask);
				txtr += 48;
				polycolor += 4;
			}
			H_MATRIXS = hm; V_MATRIXS = vm;
			change_txm_repeating_mode();
			txtr = previoustexture;
			if (subdivs==1) {
				build_cacti (x2, y2, z2, scaling * reduction, reduction,
					    globalwidth, layers - 1, divisions, distance_from_perfection,
					    rootcolormask, leafcolormask, branchdetail, 0, occurrence + 1);
			}
		}
		
		//
		subdivs--;
		b_angle += b_angle_delta;
	}

	flares = pf;
}

extern float tgt_quote[];
extern float tgt_pitch[];
extern float tgt_speed[];
extern float ani_scale[];
extern char ani_lcount[];
extern float refx, refz;
extern float ani_speed[];
extern float ani_pitch[];
const double deg = M_PI / 180;
extern Dword T_SCALE;
extern char sctype;
const Word bird_wings_center_p = 1;
const Word bird_wings_center_v = 0;
extern pvlist bird_wing1[];
extern pvlist bird_wing2[];
const Word bird_legs_center_p  = 18;
const Word bird_legs_center_v  = 1;
extern pvlist bird_legs[];
extern pvlist mamm_legs[];
const Word mamm_lleg_center_p = 14;
const Word mamm_lleg_center_v = 0;
extern pvlist mamm_lleg[];
const Word mamm_rleg_center_p = 12;
const Word mamm_rleg_center_v = 0;
extern pvlist mamm_rleg[];
const Word mamm_tail_center_p  = 46;
const Word mamm_tail_center_v  = 1;
extern pvlist mamm_tail[];
const Word mamm_wrap_center_p  = 16;
const Word mamm_wrap_center_v  = 2;
extern pvlist mamm_reartoto[];
const Word mamm_larm_center_p = 22;
const Word mamm_larm_center_v = 2;
extern pvlist mamm_larm[];
const Word mamm_rarm_center_p = 23;
const Word mamm_rarm_center_v = 0;
extern pvlist mamm_rarm[];
const Word mamm_lfoot_center_p = 15;
const Word mamm_lfoot_center_v = 1;
extern pvlist mamm_lfoot[];
const Word mamm_rfoot_center_p = 11;
const Word mamm_rfoot_center_v = 1;
extern pvlist mamm_rfoot[];

const Word bull_wrap_center_p  = 7;
const Word bull_wrap_center_v  = 0;

const Word bull_larm_center_p = 28;
const Word bull_larm_center_v = 2;

pvlist bull_larm[8] = {
{ 25, 1,1,1,0 }, // F-L
{ 26, 1,1,1,0 },
{ 27, 1,1,1,0 },
//{ 28, 1,1,1,0 },
{ 28, 0,1,0,0 },
//{ 29, 1,1,1,0 },
{ 29, 0,1,1,0 },
//{ 30, 1,1,1,0 },
{ 30, 1,0,1,0 },
//{ 31, 1,1,1,0 },
{ 31, 0,0,1,0 },
{0xFFF,0,0,0,0}
};

const Word bull_rarm_center_p = 24;
const Word bull_rarm_center_v = 2;
pvlist bull_rarm[8] = {
{ 21, 1,1,1,0 }, // F-R
{ 22, 1,1,1,0 },
{ 23, 1,1,1,0 },
//{ 24, 1,1,1,0 },
{ 24, 0,1,0,0 },
//{ 34, 1,1,1,0 },
{ 34, 0,0,1,0 },
//{ 33, 1,1,1,0 },
{ 33, 0,0,1,0 },
//{ 32, 1,1,1,0 },
{ 32, 0,0,1,0 },
{0xFFF,0,0,0,0}
};

const Word bull_rleg_center_p = 35;
const Word bull_rleg_center_v = 1;
pvlist bull_rleg[7] = {
{ 18, 1,1,1,0 }, // B-R
{ 11, 1,1,0,0 },
{ 12, 1,1,1,0 },
{ 35, 1,1,0,0 },
{ 20, 1,1,1,0 },
{ 19, 1,1,1,0 },
{0xFFF,0,0,0,0}
};

const Word bull_lleg_center_p = 36;
const Word bull_lleg_center_v = 0;
pvlist bull_lleg[7] = {
{ 13, 1,1,0,0 }, // B-L
{ 14, 1,1,1,0 },
{ 15, 1,1,1,0 },
{ 16, 1,1,1,0 },
{ 17, 1,1,1,0 },
{ 36, 1,1,0,0 },
{0xFFF,0,0,0,0}
};

pvlist mamm_head[26] = {
	{ 38, 1,1,1,0 },
	{ 26, 1,1,1,1 },
	{ 25, 1,1,1,1 },
	{ 36, 1,1,1,1 },
	{ 52, 1,1,1,1 },
	{ 37, 1,1,1,1 },
	{ 35, 1,1,1,1 },
	{ 27, 1,1,1,1 },
	{ 53, 1,1,1,0 },
	{ 54, 1,1,1,0 },
	{ 42, 1,1,1,0 },
	{ 45, 1,1,1,0 },
	{ 43, 1,1,1,0 },
	{ 44, 1,1,1,0 },
	{ 41, 1,1,0,0 },
	{ 40, 1,0,1,0 },
	{ 39, 1,0,1,0 },
	{ 24, 0,0,1,1 },
	{ 34, 1,0,0,0 },
	{ 30, 0,0,1,0 },
	{ 32, 0,0,1,0 },
	{ 33, 1,1,0,0 },
	{ 31, 1,0,0,0 },
	{ 29, 1,0,0,0 },
	{ 28, 1,1,0,0 },
	{0xFFF,0,0,0,0}
};

const Word mamm_head_wrap_center_p  = 31;
const Word mamm_head_wrap_center_v  = 2;

Uchar pBullType = BULL_JUMPER;
double pBullAngle = 1;

/* Funzione di tracciamento ed animazione delle forme di vita animali. */

void live_animal (Word n)
{
	//return;
	//DebugPrintf(0, "live_animal(%i) ani_type[n]=%i", (int)n, (int)ani_type[n]);
	if (ani_type[n]<0) ani_type[n]=-ani_type[n];	//Just in case (We set it negative each cycle to indicate this is a valid animal). (SL)
	//if (ani_type[n]==MAMMAL && ani_mtype[n]==BULL_LIKE) return;
		
	const double an_incl_prec = 50;

	double	incl;
	float	period;
	Word	sqc_x, sqc_z;
	Dword 	tick = 18 * secs;
	float	dx, dy, dz, ax, ay, ay2, az;

	float	update_ratio, tendence_to_stop; // mammals

	char	perform_depth_sort = 0;
	char	texture_skin_map   = 0;
	float	animal_distance    = 0;

	float	quote    = tgt_quote[n];
	float	pitch    = tgt_pitch[n];
	float	velocity = tgt_speed[n];
	float	reaction = 0.5 / ani_scale[n];

	ax = ani_x[n]; az = ani_z[n];
	ay = hpoint (ani_x[n], ani_z[n]) - ani_quote[n];
	

	
	if (ani_lcount[n] < 0) {
		dx 	      = 1 / (float)(-ani_lcount[n]);
		ani_x[n]     += dx * (refx - ax);
		ani_z[n]     += dx * (refz - az);
		ani_quote[n] -= dx * ani_quote[n];
		if (ani_lcount[n] > -10) {
			stick3d (ax, ay, az, pos_x,      pos_y - 50, pos_z);
			stick3d (ax, ay, az, pos_x - 50, pos_y - 50, pos_z);
			stick3d (ax, ay, az, pos_x + 50, pos_y - 50, pos_z);
			stick3d (ax, ay, az, pos_x,      pos_y - 50, pos_z + 50);
			stick3d (ax, ay, az, pos_x,      pos_y - 50, pos_z - 50);
		}
		if (ani_lcount[n] < -1) {
			step += 2 * ani_lcount[n];
			ani_lcount[n]++;
		}
		goto inactive;
	//} else {
		//DebugPrintf(0, "ani_lcount[%i] is %i.", (int)n, (int)ani_lcount[n]);
	}

	/* Comportamento in distanza. */

	if (ani_type[n] == BIRD) {
		if (quote >= 1500) {
			velocity = 800;
			fast_srand (n + (tick / 50));
			pitch   += 5 * fast_flandom() - 2.5;
			quote   += 1000 * fast_flandom() - 500;
			goto end_far;
		}
		if (quote > 750) {
			velocity = 400;
			quote   *= 0.5;
			fast_srand (n + (tick / 15));
			pitch   += 10 * fast_flandom() - 5;
			goto end_far;
		}
		if (quote > 250) {
			velocity = 0;
			quote    = 0;
			goto end_far;
		}
		fast_srand (n + (tick / 10));
		if (quote < 50)
			velocity = 0;
		else {
			velocity = 100 * fast_flandom();
			pitch += 10 * fast_flandom() - 5;
		}
		if (fast_random(7) == 3)
			quote = 1500 + 1000 * fast_flandom();
		else
			quote += 500 * fast_flandom() - 250;
	}

	if (ani_type[n] == MAMMAL) {
		//stick3d (ax, ay, az, ax, ay - 50000, az);
		fast_srand (n);
		update_ratio = fast_random (31) + 3;
		tendence_to_stop = fast_flandom () * 0.8;
		fast_srand (n + tick / update_ratio);
		if (fast_flandom() < tendence_to_stop) {
			velocity = 0;
			fast_srand (n + tick);
			if (fast_flandom() < 0.1 * tendence_to_stop) {
				pitch += 100 * fast_flandom();
				pitch -= 100 * fast_flandom();
			}
		}
		else {
			fast_srand (n + tick / 18);
			switch (ani_mtype[n]) {
				case FELINE_LIKE:
					velocity = 350 + fast_flandom() * 350;
					break;
				case RABBIT_LIKE:
					velocity = 200 + fast_flandom() * 200;
					break;
				case KANGAROO_LIKE:
					velocity = 400 + fast_flandom() * 100;
					break;
				case APE_LIKE:
					velocity = 100 + fast_flandom() * 100;
					break;
				case BULL_LIKE:
					if (pBullType==BULL_JUMPER) {
						velocity = 100 + fast_flandom() * 300;
					} else if (pBullType==BULL_SPINNER) {
						velocity = 300 + fast_flandom() * 300;
					} else if (pBullType==BULL_RUNNER) {
						velocity = 200 + fast_flandom() * 100;
					} else if (pBullType==BULL_SOMERSAULTER) {
						velocity = 200 + fast_flandom() * 300;
					}
					break;
			}
			fast_srand (n + (tick / 5));
			dx = 300 * fast_flandom() - 5;
			pitch += dx / velocity;
		}
		quote = 0;
	}

    end_far:
	if (quote < 0) quote = 0;

	tgt_speed[n] = velocity;
	tgt_quote[n] = quote;
	tgt_pitch[n] = pitch;

	dx = velocity - ani_speed[n];
	dy = quote    - ani_quote[n];
	dz = pitch    - ani_pitch[n];

	ani_speed[n] += 3 * reaction * dx;
	ani_pitch[n] += 2 * reaction * dz;
	ani_quote[n] += 1 * reaction * dy;

	ani_x[n] -= ani_speed[n] * sin (deg * ani_pitch[n]);
	ani_z[n] -= ani_speed[n] * cos (deg * ani_pitch[n]);
	if (ani_x[n] < 0 || ani_x[n] > 3276800 || ani_z[n] < 0 || ani_z[n] > 3276800) {
		ani_x[n]     += ani_speed[n] * sin (deg * ani_pitch[n]);
		ani_z[n]     += ani_speed[n] * sin (deg * ani_pitch[n]);
		ani_pitch[n] += 180;
	}

	inactive:

	dx = ax - cam_x;
	dy = ay - cam_y;
	dz = az - cam_z;
	animal_distance = SQRT (dx*dx + dy*dy + dz*dz);

	if (animal_distance > 250000) {
		ani_x[n] = pos_x + 100000 * fast_flandom() - 100000 * fast_flandom();
		ani_z[n] = pos_z + 100000 * fast_flandom() - 100000 * fast_flandom();
		if (ani_type[n] == BIRD)
			ani_quote[n] = 25000 * fast_flandom();
		else
			ani_quote[n] = 0;
		tgt_quote[n] = ani_quote[n];
	}

	sqc_x = ani_x[n] / 16384;
	sqc_z = ani_z[n] / 16384;
	if (sqc_x < 0) sqc_x = 0;
	if (sqc_x > 199) sqc_x = 199;
	if (sqc_z < 0) sqc_z = 0;
	if (sqc_z > 199) sqc_z = 199;
	ani_sqc[n] = m200[sqc_z] + sqc_x;

	if (ani_type[n]>0) ani_type[n]=-ani_type[n];	//We set it negative each cycle to indicate this is a valid animal. (SL)
	
	
	if (animal_distance > 150000) {
		//DebugPrintf(0, "animal_distance for %i is %f > 150000.", n, animal_distance);
		return;
	}

	if (animal_distance <  75000) perform_depth_sort = 1;
	if (animal_distance <  12500) texture_skin_map   = 1 + (n % 2);

	/* Comportamento in vicinanza e tracciamento. */

	// impostazione texture per forme di vita.
	flares = 0;
	txtr = p_background;
	XSIZE = TEXTURE_XSIZE * 256;
	YSIZE = TEXTURE_YSIZE * T_SCALE;

	if (abs(ani_type[n]) == BIRD) {
		// preparazione forma di base:
		copypv (bird_result, bird_base);
		modpv (bird_result, -1, -1, ani_scale[n], ani_scale[n], ani_scale[n], 0, 0, 0, NULL);
		// modifiche alla forma di base:
		if (ani_lcount[n] < 0) {
			// bŠ, questo non Š pi— vivo:
			// Š stato catturato e giace legato
			// ad alcune cordicelle, mentre viene
			// trascinato dietro al player...
			dz = 180 / (float)ani_lcount[n];
			goto bird_trace;
		}
		if (ani_quote[n] < 500) {
			// si inclina all'indietro, prima di
			// atterrare o decollare. Š normale.
			dz = -0.1 * fabs (250 - ani_quote[n]);
		}
		else
			dz = 0;
		if (ay < 0 || sctype != OCEAN) {
			if (ani_quote[n] < 50) {
				// modello di comportamento:
				// quando Š a terra. ali chiuse o semiaperte.
				// non succede quando al posto della terraferma
				// c'Š l'acqua...
				dy = 1 - (ani_quote[n] * reaction); if (dy < 0) dy = 0;
				modpv (bird_result, bird_wings_center_p, bird_wings_center_v, 1, 1, 1, 0, +45*dy, +75*dy, bird_wing1);
				modpv (bird_result, bird_wings_center_p, bird_wings_center_v, 1, 1, 1, 0, -45*dy, -75*dy, bird_wing2);
				goto bird_trace;
			}
		}
		modpv (bird_result, bird_legs_center_p, bird_legs_center_v, 1, 1, 1, -75, 0, 0, bird_legs);
		if (ani_scale[n] > 10) {
			// modello di comportamento:
			// in volo, grandi uccelli.
			modpv (bird_result, bird_wings_center_p, bird_wings_center_v, 1, 1, 1,
			       0, 0, fabs(10 - (tick % 20)) * -4.5, bird_wing1);
			modpv (bird_result, bird_wings_center_p, bird_wings_center_v, 1, 1, 1,
			       0, 0, fabs(10 - (tick % 20)) * +4.5, bird_wing2);
		}
		else {
			// modello di comportamento:
			// in volo, piccoli uccelli.
			modpv (bird_result, bird_wings_center_p, bird_wings_center_v, 1, 1, 1,
			       0, 0, fabs(3 - (tick % 6)) * -15, bird_wing1);
			modpv (bird_result, bird_wings_center_p, bird_wings_center_v, 1, 1, 1,
			       0, 0, fabs(3 - (tick % 6)) * +15, bird_wing2);
		}
		// visualizzazione:
		bird_trace:
		modpv (bird_result, bird_wings_center_p, bird_wings_center_v, 1, 1, 1,
		       dz, 0, 0, 0);
		modpv (bird_result, bird_wings_center_p, bird_wings_center_v, 1, 1, 1,
		       0, ani_pitch[n], 0, 0);
		drawpv (bird_result, texture_skin_map, 3, ax, ay
			- 9 * ani_scale[n], az, perform_depth_sort);
		// reazioni alla vicinanza.
		// bŠ, gli uccelli tendono a scappare,
		// a meno che non ci si avvicini ad essi
		// con molta cautela. tuttavia, si possono
		// catturare: concettualmente, Š semplice,
		// dato che basta tender loro un agguato,
		// e saltargli addosso da molto vicino.
		// praticamente Š piuttosto difficile...
		if (ani_lcount[n] >= 0) {
			if (animal_distance < 5000 && step > 250)
				tgt_quote[n] += 2500;
			if (animal_distance < 3000 && step > 100)
				tgt_quote[n] += 2000;
			if (animal_distance < 1000) {
				tgt_speed[n] = 500 * reaction;
				tgt_quote[n] = 250 * reaction;
				if (animal_distance < 500)
					ani_lcount[n] = -25;
			}
		}
	}

	if (abs(ani_type[n]) == MAMMAL) {
		// preparazione forma di base:
		switch (ani_mtype[n]) {
			case BULL_LIKE:
				copypv (bull_result, bull_base);
				modpv (bull_result, -1, -1, ani_scale[n], ani_scale[n], ani_scale[n], 0, 0, 0, NULL);
				break;
			default:
				copypv (mamm_result, mamm_base);
				modpv (mamm_result, -1, -1, ani_scale[n], ani_scale[n], ani_scale[n], 0, 0, 0, NULL);
				break;
		}
		
		//modpv (mamm_result, -1, -1, ani_scale[n], ani_scale[n], ani_scale[n], 0, 0, 0, NULL);
		if (ay > -10 && sctype == OCEAN) {
			// nell'acqua...
			// se alcuni ci si avventurano, bŠ,
			// possono sempre nuotare...
			//modpv (mamm_result, -1, -1, 1, 0.7, 1, 0, 0, 0, NULL);
			if (ani_mtype[n]==BULL_LIKE) {
				//for now we don't do this on bulls.
				modpv (bull_result, -1, -1, 1, 0.7, 1, 0, 0, 0, NULL);
				period = fabs (fsecs - 0.5);
				modpv (bull_result, -1, -1, 1, 1, 1, 15, 0, 50 * period, NULL);
			} else {
				modpv (mamm_result, -1, -1, 1, 0.7, 1, 0, 0, 0, NULL);
				modpv (mamm_result, -1, -1, 1, 0.0, 1, 0, 0, 0, mamm_legs);
				period = fabs (fsecs - 0.5);
				modpv (mamm_result, -1, -1, 1, 1, 1, 15, 0, 50 * period, NULL);
			}
			
		}
		else {
			// Sulla terraferma...
			if (ani_mtype[n] != FELINE_LIKE && ani_mtype[n] != BULL_LIKE) {
				if (ani_mtype[n] == APE_LIKE) {
					//modpv (mamm_result, -1, -1, 1, 1, 1, -120, 0, 0, mamm_legs);
					modpv (mamm_result, -1, -1, 1, 1, 1, 90, 0, 0, NULL);
					modpv (mamm_result, mamm_lleg_center_p, mamm_lleg_center_v, -1.5, 2, 1.5, -55, 0, 0, mamm_lleg);
					modpv (mamm_result, mamm_rleg_center_p, mamm_rleg_center_v, -1.5, 2, 1.5, -55, 0, 0, mamm_rleg);
					
					if (ape_zombie) {
						modpv (mamm_result, mamm_larm_center_p, mamm_larm_center_v, 1, 5, 1, -45, 0, 0, mamm_larm);
						modpv (mamm_result, mamm_rarm_center_p, mamm_rarm_center_v, 1, 5, 1, -45, 0, 0, mamm_rarm);
					} else {
						modpv (mamm_result, mamm_larm_center_p, mamm_larm_center_v, 1, 1, 1, -100, 0, 0, mamm_larm);
						modpv (mamm_result, mamm_rarm_center_p, mamm_rarm_center_v, 1, 1, 1, -100, 0, 0, mamm_rarm);
					}
					
					
					//if (!ape_stargazer) {
						//modpv(mamm_result, mamm_head_wrap_center_p, mamm_head_wrap_center_v, 1, 1, 1, -160, 0, 0, mamm_head);
					//}
					//ani_speed[n]=0;	//TEMPORARY
					
					
					//modpv (mamm_result, -1, -1, 1, 1, 1, -20, 0, 0, mamm_reartoto);
					//modpv (mamm_result, -1, -1, 1, 1, 1, -10, 0, 0, mamm_legs);
					//modpv (mamm_result, -1, -1, 1, 1.5, 1, 0, 0, 0, mamm_legs);
					//modpv (mamm_result, -1, -1, 2, .5, 2, 0, 0, 0, mamm_reartoto);
					modpv (mamm_result, mamm_tail_center_p, mamm_tail_center_v, 1, 1, 1, -100, 0, 0, mamm_tail);
					modpv (mamm_result, -1, -1, 0.4, 0.4, 0.4, 0, 0, 0, NULL);
				} else {
					modpv (mamm_result, -1, -1, 2, 2, 0.75, 0, 0, 0, mamm_reartoto);
					modpv (mamm_result, -1, -1, 1, 1, 1, 60, 0, 0, NULL);
					modpv (mamm_result, mamm_tail_center_p, mamm_tail_center_v, 1, 1, 1, -100, 0, 0, mamm_tail);
					if (ani_mtype[n] != KANGAROO_LIKE)
						modpv (mamm_result, -1, -1, 0.33, 0.33, 0.33, 0, 0, 0, NULL);
				}
			}
			ay2 = hpoint (ax - an_incl_prec * sin (deg * ani_pitch[n]),
				      az - an_incl_prec * cos (deg * ani_pitch[n]))
				      - ani_quote[n];
			incl = ay - ay2;
			incl /= an_incl_prec;
			if (incl < -1) incl = -1;
			if (incl > +1) incl = +1;
			incl = ((double)180 * atan(incl)) / M_PI;
			if (ani_mtype[n] != APE_LIKE && ani_mtype[n] != BULL_LIKE){
				modpv (mamm_result, mamm_wrap_center_p,
			       mamm_wrap_center_v, 1, 1, 1, incl, 0, 0, NULL);
			} else if (ani_mtype[n] == APE_LIKE) {
				//modpv (mamm_result, mamm_wrap_center_p,
			    //   mamm_wrap_center_v, 1, 1, 1, incl, 0, 0, NULL);
				modpv (mamm_result, mamm_lfoot_center_p, mamm_lfoot_center_v, 1, 1, 1, incl*1.5, 0, 0, mamm_lfoot);
				modpv (mamm_result, mamm_rfoot_center_p, mamm_rfoot_center_v, 1, 1, 1, incl*1.5, 0, 0, mamm_rfoot);
			} else if (ani_mtype[n] == BULL_LIKE) {
				modpv (bull_result, bull_wrap_center_p,
			       bull_wrap_center_v, 1, 1, 1, incl, 0, 0, NULL);
			}
			//It's -165 from 0 (which is at the back of the head) to the rear end of the feline.
			/*switch (ani_mtype[n]) {
				case FELINE_LIKE:	//The bottom is 50 below 0, and 50/3 is 16.666...
					ay -= ani_scale[n] * 16;
					break;
				case RABBIT_LIKE:
					ay -= ani_scale[n] * 48;
					break;
				case KANGAROO_LIKE:
					ay -= ani_scale[n] * 115;
					break;
				case APE_LIKE:
					//165 from 0 to rear end, then for legs... Should come out to 225 if /3 is accurate?
					ay -= ani_scale[n]*75;
					break;
				case BULL_LIKE:		//25 for front legs, or 15 for back legs. Div 3 = 5 or 8.33
					ay -= ani_scale[n]*5;
					break;
			}*/
			float dmy = getDownmostYFromPV(generic_result);
			ay-=dmy;
			
			//baseay=ay;
			if (ani_mtype[n]==BULL_LIKE) {
				if (pBullType==BULL_JUMPER) {
					modpv (bull_result, bull_lleg_center_p, bull_lleg_center_v, 1+pBullAngle, 1+pBullAngle, 1+pBullAngle, 0, 0, 0, bull_lleg);
					modpv (bull_result, bull_rleg_center_p, bull_rleg_center_v, 1+pBullAngle, 1+pBullAngle, 1+pBullAngle, 0, 0, 0, bull_rleg);
				}
			}
			
			if (ani_speed[n] < 50) {
				// se sono fermi possono comunque
				// scondinzolare, mentre pensano
				// a cosa fare...
				fast_srand (4*n);
				if (fast_random(1)) {
					period = fabs (fsecs - 0.5);
					period=0.2;
					if (ani_mtype[n]==BULL_LIKE) {
						
					//	modpv (bull_result, mamm_tail_center_p, mamm_tail_center_v,
					 //      1, 1, 1, 0, 240 * period - 60, 0, mamm_tail);
					} else {
						modpv (mamm_result, mamm_tail_center_p, mamm_tail_center_v,
					       1, 1, 1, 0, 240 * period - 60, 0, mamm_tail);
					}
				}
			}
			else {
				// se corrono, si fa semplicemente
				// un'animazione del tronco per dare
				// l'idea che stiano appunto correndo.
				FastBool runner=False;
				if (ani_mtype[n] == FELINE_LIKE)
					period = 45;
				if (ani_mtype[n] == RABBIT_LIKE)
					period = 60;
				if (ani_mtype[n] == KANGAROO_LIKE)
					period = 22;
				if (ani_mtype[n] == BULL_LIKE) {
					if (pBullType==BULL_SOMERSAULTER) {
						period=90;
					} else {
						period = 45;
					}
					if (pBullType==BULL_RUNNER) {
						runner=True;
					}
				}
				if (ani_mtype[n] == APE_LIKE) {
					period = 45;
					runner=True;
				}
				if (!runner) {
					period *= fabs (fsecs - 0.5);
					period /= ani_scale[n];
				}
				if (ani_mtype[n] == FELINE_LIKE)
					ay -= 35 * ani_scale[n] * period;
				if (ani_mtype[n] == BULL_LIKE) {
					if (pBullType==BULL_JUMPER || pBullType==BULL_SOMERSAULTER) {
						ay -= 35 * ani_scale[n] * period;
					} else if (pBullType==BULL_SPINNER) {
						ay -= 5 * ani_scale[n] * period;
					//} else if (pBullType==BULL_RUNNER) {
					}
				}
				if (ani_mtype[n] == RABBIT_LIKE)
					ay -= 50 * ani_scale[n] * period;
				if (ani_mtype[n] == KANGAROO_LIKE)
					ay -= 300 * ani_scale[n] * period;
				if (ani_mtype[n] == APE_LIKE) {
					//double ptmp = ani_scale[n] * period;
					//ay -= 10 * ptmp;
					if (fsecs<.25) {
						double ps = period/ani_scale[n]*50*(fsecs);
						modpv (mamm_result, mamm_lleg_center_p, mamm_lleg_center_v, 1, 1, 1, ps, 0, 0, mamm_lleg);
						modpv (mamm_result, mamm_rleg_center_p, mamm_rleg_center_v, 1, 1, 1, -ps, 0, 0, mamm_rleg);
						if (!ape_zombie) {
							modpv (mamm_result, mamm_larm_center_p, mamm_larm_center_v, 1, 1, 1, -ps, 0, 0, mamm_larm);
							modpv (mamm_result, mamm_rarm_center_p, mamm_rarm_center_v, 1, 1, 1, ps, 0, 0, mamm_rarm);
						}					
					} else if (fsecs<.5) {
						double ps = period/ani_scale[n]*50*(.5-fsecs);
						modpv (mamm_result, mamm_lleg_center_p, mamm_lleg_center_v, 1, 1, 1, ps, 0, 0, mamm_lleg);
						modpv (mamm_result, mamm_rleg_center_p, mamm_rleg_center_v, 1, 1, 1, -ps, 0, 0, mamm_rleg);
						if (!ape_zombie) {
							modpv (mamm_result, mamm_larm_center_p, mamm_larm_center_v, 1, 1, 1, -ps, 0, 0, mamm_larm);
							modpv (mamm_result, mamm_rarm_center_p, mamm_rarm_center_v, 1, 1, 1, ps, 0, 0, mamm_rarm);
						}					
					} else if (fsecs<.75) {
						double ps = period/ani_scale[n]*50*(fsecs-.5);
						modpv (mamm_result, mamm_lleg_center_p, mamm_lleg_center_v, 1, 1, 1, -ps, 0, 0, mamm_lleg);
						modpv (mamm_result, mamm_rleg_center_p, mamm_rleg_center_v, 1, 1, 1, ps, 0, 0, mamm_rleg);
						if (!ape_zombie) {
							modpv (mamm_result, mamm_larm_center_p, mamm_larm_center_v, 1, 1, 1, ps, 0, 0, mamm_larm);
							modpv (mamm_result, mamm_rarm_center_p, mamm_rarm_center_v, 1, 1, 1, -ps, 0, 0, mamm_rarm);
						}
					} else {
						double ps = period/ani_scale[n]*50*(1.0-fsecs);
						modpv (mamm_result, mamm_lleg_center_p, mamm_lleg_center_v, 1, 1, 1, -ps, 0, 0, mamm_lleg);
						modpv (mamm_result, mamm_rleg_center_p, mamm_rleg_center_v, 1, 1, 1, ps, 0, 0, mamm_rleg);
						if (!ape_zombie) {
							modpv (mamm_result, mamm_larm_center_p, mamm_larm_center_v, 1, 1, 1, ps, 0, 0, mamm_larm);
							modpv (mamm_result, mamm_rarm_center_p, mamm_rarm_center_v, 1, 1, 1, -ps, 0, 0, mamm_rarm);
						}
					}
					
				} else if (ani_mtype[n]==BULL_LIKE) {
					if (pBullType==BULL_JUMPER) {
						modpv (bull_result, bull_wrap_center_p, bull_wrap_center_v,
						   1, 1, 1, - 180*pBullAngle * fabs(0.5-fsecs), 0, 0, NULL);
					} else if (pBullType==BULL_SOMERSAULTER) {
						modpv (bull_result, bull_wrap_center_p, bull_wrap_center_v,
						   1, 1, 1, -360 * fsecs, 0, 0, NULL);
					} else if (pBullType==BULL_SPINNER) {
						double ps = fsecs*360;
						modpv (bull_result, bull_lleg_center_p, bull_lleg_center_v, 1, 1, 1, -ps, 0, 0, bull_lleg);
						modpv (bull_result, bull_rleg_center_p, bull_rleg_center_v, 1, 1, 1, -ps, 0, 0, bull_rleg);
						modpv (bull_result, bull_larm_center_p, bull_larm_center_v, 1, 1, 1, -ps, 0, 0, bull_larm);
						modpv (bull_result, bull_rarm_center_p, bull_rarm_center_v, 1, 1, 1, -ps, 0, 0, bull_rarm);
					} else if (pBullType==BULL_RUNNER) {
						if (fsecs<.25) {
							double ps = period/ani_scale[n]*20*(fsecs);
							modpv (bull_result, bull_lleg_center_p, bull_lleg_center_v, 1, 1, 1, ps, 0, 0, bull_lleg);
							modpv (bull_result, bull_rleg_center_p, bull_rleg_center_v, 1, 1, 1, -ps, 0, 0, bull_rleg);
							modpv (bull_result, bull_larm_center_p, bull_larm_center_v, 1, 1, 1, -ps, 0, 0, bull_larm);
							modpv (bull_result, bull_rarm_center_p, bull_rarm_center_v, 1, 1, 1, ps, 0, 0, bull_rarm);
						
						} else if (fsecs<.5) {
							double ps = period/ani_scale[n]*20*(.5-fsecs);
							modpv (bull_result, bull_lleg_center_p, bull_lleg_center_v, 1, 1, 1, ps, 0, 0, bull_lleg);
							modpv (bull_result, bull_rleg_center_p, bull_rleg_center_v, 1, 1, 1, -ps, 0, 0, bull_rleg);
							modpv (bull_result, bull_larm_center_p, bull_larm_center_v, 1, 1, 1, -ps, 0, 0, bull_larm);
							modpv (bull_result, bull_rarm_center_p, bull_rarm_center_v, 1, 1, 1, ps, 0, 0, bull_rarm);
						
						} else if (fsecs<.75) {
							double ps = period/ani_scale[n]*20*(fsecs-.5);
							modpv (bull_result, bull_lleg_center_p, bull_lleg_center_v, 1, 1, 1, -ps, 0, 0, bull_lleg);
							modpv (bull_result, bull_rleg_center_p, bull_rleg_center_v, 1, 1, 1, ps, 0, 0, bull_rleg);
							modpv (bull_result, bull_larm_center_p, bull_larm_center_v, 1, 1, 1, ps, 0, 0, bull_larm);
							modpv (bull_result, bull_rarm_center_p, bull_rarm_center_v, 1, 1, 1, -ps, 0, 0, bull_rarm);
						
						} else {
							double ps = period/ani_scale[n]*20*(1.0-fsecs);
							modpv (bull_result, bull_lleg_center_p, bull_lleg_center_v, 1, 1, 1, -ps, 0, 0, bull_lleg);
							modpv (bull_result, bull_rleg_center_p, bull_rleg_center_v, 1, 1, 1, ps, 0, 0, bull_rleg);
							modpv (bull_result, bull_larm_center_p, bull_larm_center_v, 1, 1, 1, ps, 0, 0, bull_larm);
							modpv (bull_result, bull_rarm_center_p, bull_rarm_center_v, 1, 1, 1, -ps, 0, 0, bull_rarm);
						
						}
					}
				} else {
					
					modpv (mamm_result, mamm_wrap_center_p, mamm_wrap_center_v,
						   1, 1, 1, - 50 * period, 0, 0, NULL);
					modpv (mamm_result, mamm_wrap_center_p, mamm_wrap_center_v,
						   1, 1, 1, 100 * period, 0, 0, mamm_reartoto);	
				}
			}
		}
		if (ani_mtype[n] == APE_LIKE) {
			modpv (mamm_result, mamm_lfoot_center_p, mamm_lfoot_center_v,
		       1, 1, 1, 0, ani_pitch[n] + 180, 0, NULL);
		} else if (ani_mtype[n] == BULL_LIKE) {
			modpv (bull_result, bull_wrap_center_p, bull_wrap_center_v,
		       1, 1, 1, 0, ani_pitch[n] + 180, 0, NULL);
		} else {
			modpv (mamm_result, mamm_wrap_center_p, mamm_wrap_center_v,
		       1, 1, 1, 0, ani_pitch[n] + 180, 0, NULL);
		}
		// visualizzazione:
		mamm_trace:
		
		float coords[6];
		#define LOWX 0
		#define HIGHX 1
		#define LOWY 2
		#define HIGHY 3
		#define LOWZ 4
		#define HIGHZ 5
		coords[HIGHY] = getDownmostYFromPV(generic_result);
		coords[LOWY] = getUpmostYFromPV(generic_result);
		coords[LOWX] = getLowXFromPV(generic_result);
		coords[HIGHX] = getHighXFromPV(generic_result);
		coords[LOWZ] = getLowZFromPV(generic_result);
		coords[HIGHZ] = getHighZFromPV(generic_result);
		coords[LOWY]+=ay;
		coords[HIGHY]+=ay;
		coords[LOWX]+=ax;
		coords[HIGHX]+=ax;
		coords[LOWZ]+=az;
		coords[HIGHZ]+=az;
		
		float dists[8];
		int xvert, yvert, zvert;
		double dlx = ax - cam_x;
		double dly = ay - cam_y;
		double dlz = az - cam_z;
		double adist;
		int distidx, distidxx, distidxy;
		if (option_3dbrackets) {
			adist = dlx*dlx + dly*dly + dlz*dlz;
			for (xvert = 0; xvert<2; xvert++) {
				distidxx=xvert;
				for (yvert = 0; yvert<2; yvert++) {
					distidxy=distidxx+yvert+yvert;
					for (zvert = 0; zvert<2; zvert++) {
						distidx=distidxy+zvert+zvert+zvert+zvert;
						dlx = (coords[LOWX+xvert]-cam_x); dlx*=dlx;
						dly = (coords[LOWY+yvert]-cam_y); dly*=dly;
						dlz = (coords[LOWZ+zvert]-cam_z); dlz*=dlz;
						dists[distidx] = dlx+dly+dlz;
						if (dists[distidx]>=adist) {
							if (xvert==0) stick3d(coords[LOWX+xvert], coords[LOWY+yvert], coords[LOWZ+zvert], coords[HIGHX+xvert], coords[LOWY+yvert], coords[LOWZ+zvert]);
							if (yvert==0) stick3d(coords[LOWX+xvert], coords[LOWY+yvert], coords[LOWZ+zvert], coords[LOWX+xvert], coords[HIGHY+yvert], coords[LOWZ+zvert]);
							if (zvert==0) stick3d(coords[LOWX+xvert], coords[LOWY+yvert], coords[LOWZ+zvert], coords[LOWX+xvert], coords[LOWY+yvert], coords[HIGHZ+zvert]);
						}
					}
				}
			}
		}
		
		if (ani_mtype[n] == BULL_LIKE) {
			drawpv (bull_result, 1, 0, ax, ay, az, perform_depth_sort);
		} else {
			drawpv (mamm_result, 1, 0, ax, ay, az, perform_depth_sort);
		}
			
		//dmy = 10
		//umy = -20
		//ay = 0
		//coords[LOWY]-=coords[HIGHY];
		//coords[HIGHY]=ay;
		//coords[LOWY]+=ay;
		//dmy now = 0
		//umy now = -30
		
		
		if (option_3dbrackets) {
			for (xvert = 0; xvert<2; xvert++) {
				distidxx=xvert;
				for (yvert = 0; yvert<2; yvert++) {
					distidxy=distidxx+yvert+yvert;
					for (zvert = 0; zvert<2; zvert++) {
						distidx=distidxy+zvert+zvert+zvert+zvert;
						if (dists[distidx]<adist) {
							if (xvert==0) stick3d(coords[LOWX+xvert], coords[LOWY+yvert], coords[LOWZ+zvert], coords[HIGHX+xvert], coords[LOWY+yvert], coords[LOWZ+zvert]);
							if (yvert==0) stick3d(coords[LOWX+xvert], coords[LOWY+yvert], coords[LOWZ+zvert], coords[LOWX+xvert], coords[HIGHY+yvert], coords[LOWZ+zvert]);
							if (zvert==0) stick3d(coords[LOWX+xvert], coords[LOWY+yvert], coords[LOWZ+zvert], coords[LOWX+xvert], coords[LOWY+yvert], coords[HIGHZ+zvert]);
						}
					}
				}
				
			}
		}
		//TODO: Don't show 3d brackets for faraway stuff, show 2d instead, otherwise don't show 2d if we're showing 3d.
	}

    end_vicinity:
	
	if ((abs(ani_type[n])==MAMMAL || abs(ani_type[n])==BIRD) && hopfind >= 2) {
		double gheight = hpoint (ax, az);
		//stick3d(ax,ay-3000,az,ax,-4E5,az);
		
		//The commented out code below doesn't work right in some places, because height in hilly areas can get up that high. (SL):
		
		if (ay-3000 > gheight-1E5) {	//This means: if (the bottom of the line would be at below gheight-1E5) then we draw it from bird up to gheight-1E5 (SL)
			stick3d(ax,ay-3000,az,ax,gheight-1E5,az);
		//} else if (ay+3000 < gheight-1E5) {	//This means: if (the top of the line would be below gheight-1E5) then we draw it from bird down to gheight-1E5 (SL)
		//	stick3d(ax,gheight-1E5,az,ax,ay+3000,az);
		//} else {	//If the bird is right close to gheight-1E5 (within 3000 of it) then we'll draw a line from 0 to gheight-1E5 for testing purposes.
		//	stick3d(ax,0,az,ax,gheight-1E5,az);
		}
	}
	
	//DebugPrintf(0, "Done live_animal(%i) ani_type[n]=%i", (int)n, (int)ani_type[n]);
	
}

#ifdef WINDOWS
#ifdef DOBUFFERCHECK
class PtrWrapper {
	public:
	void * ptr;
	char * name;
	int len;
	PtrWrapper(void * iptr, int ilen, char * iname) {
		ptr=iptr;
		len=ilen;
		name=iname;
	}
};

class CVector {
	private:
		int numBuffers;
		int maxBuffers;
		PtrWrapper ** buffers;
	public:
	CVector() : numBuffers(0), maxBuffers(0), buffers(NULL) {
		maxBuffers=32;
		buffers = new (PtrWrapper*)[maxBuffers];
	}
	void Add(PtrWrapper* wrapper) {
		if (numBuffers>=maxBuffers) {
			PtrWrapper ** nbuffers = new (PtrWrapper*)[maxBuffers+maxBuffers];
			for (int i=0; i<numBuffers; i++) {
				nbuffers[i]=buffers[i];
			}
			delete [] buffers;
			buffers=nbuffers;
			maxBuffers+=maxBuffers;
		}
		buffers[numBuffers]=wrapper;
		numBuffers++;
	}
	PtrWrapper * Get(int i) {
		assert(i>=0);
		assert(i<numBuffers);
		return buffers[i];
	}
	int Size() {
		return numBuffers;
	}
	~CVector() {
		delete [] buffers;
		buffers=NULL;
		maxBuffers=0;
		numBuffers=0;
	}
};

//Using this instead of the STL vector because I didn't really want to install STLport (http://cmeerw.org/prog/dm/stl.html)
//	or make it a reqired dependency
//-SL
CVector buffers;

void * customMalloc(int size, char* name) {
	void * ptr = malloc(size+512);
	buffers.Add(new PtrWrapper(ptr, size, name)); 
	Uchar * ucbuff = reinterpret_cast<Uchar*>(ptr);
	for (int i=0; i<256; i++) {
		ucbuff[i]=i;
		ucbuff[256+size+i]=i;
	}
	return reinterpret_cast<void*>(reinterpret_cast<Uchar*>(ptr)+256);
}

void bufferCheck(char * filename, int line) {
	//DebugPrintf(0, "BufferCheck(%s, %i)\n", filename, line);
	for (int i=0; i<buffers.Size(); i++ ) {
		PtrWrapper * wrapper = buffers.Get(i);
		Uchar * ucbuff = reinterpret_cast<Uchar*>(wrapper->ptr);
		for (int i=0; i<256; i++) {
			if (ucbuff[i]!=i) {
				//buffer underflow!
				DebugPrintf(0, "BufferCheck(%s, %i) underflow detected with '%s'\n", filename, line, wrapper->name);
				#ifdef WINDOWS
				printf("BufferCheck(%s, %i) underflow detected with '%s'\n", filename, line, wrapper->name);
				asm int 3;
				#endif
				break;
			}
			if (ucbuff[256+wrapper->len+i]!=i) {
				//buffer overflow!
				DebugPrintf(0, "BufferCheck(%s, %i) overflow detected with '%s'\n", filename, line, wrapper->name);
				#ifdef WINDOWS
				printf("BufferCheck(%s, %i) overflow detected with '%s'\n", filename, line, wrapper->name);
				asm int 3;
				#endif
				break;
			}
		}
	}		
}
#endif
#endif

#ifdef WINDOWS
extern int fbread(void * var, int size, int amount);
extern char star_label[];
extern char planet_label[];

void FindCritter0() {
	static Udword systemNumber = 17;
	static Dword bodyNumber = 0;
	if (systemNumber==0 || (ap_reached && bodyNumber>=nearstar_nob)) {
		status("STAR",280);
		//Vimana to the next star
		systemNumber++;
		#ifdef WINDOWS
		Dword pos = labelled_system_number(systemNumber, current_galaxy);
		#else
		Dword pos = labelled_system_number(systemNumber);
		#endif
		if (pos>0) {
			Dword sx, sy, sz;
			#ifdef WINDOWS
			FILE* tmpFile ;
			if (current_galaxy==0) {
				tmpFile = fopen(starmap3_file, "rb");
			} else {
				tmpFile = fopen(starmap3b_file, "rb");
			}
			#else
			FILE * tmpFile = fopen(starmap3_file, "rb");
			#endif
			fseek(tmpFile, pos+25, SEEK_SET);
			fread(&sx, 4, 1, tmpFile);
			fread(&sy, 4, 1, tmpFile);
			fread(&sz, 4, 1, tmpFile);
			//DebugPrintf(0, "Going to sx=%li sy=%li sz=%li", sx, sy, sz);
			fclose(tmpFile);
			if (isthere3(sx, sy, sz)) {
				ap_target_x = laststar_x;
				ap_target_y = laststar_y;
				ap_target_z = laststar_z;
				ap_target_sx = laststar_sx;
				ap_target_sy = laststar_sy;
				ap_target_sz = laststar_sz;
				extract_ap_target_infos ();
				fix_remote_target ();
				ap_targetting = 0;
				if (lithium_collector || manual_target)
					status ("CONFLICT", 550);
				else {
					bodyNumber=0;
					stspeed = 1; // partenza automatica
					if (ap_targetted) {
						nsnp = 1;
						ap_reached = 0;
						ip_reached = 0;
						ip_targetted = -1;
						ip_reaching = 0;
						//update_star_label();
					}
				}
			}
		}
		status("NO SYSTEMS LEFT.", 100);
	} else if (ap_reached==0) {
		//wait until we arrive at the system
		status("WAIT",285);
		
	} else if (ip_reaching==0) {
		status("PLNT",290);
		//Check this body, if it isn't felisian and doesn't have C0s, target the next one.
		if (pwr > 15000) {
			//DebugPrintf(0, "BodyNumber=%li.", bodyNumber);
			if (bodyNumber<nearstar_nob) {
				ip_targetted = bodyNumber;
				fix_local_target ();
				ip_targetting = 0;
				ip_reached = 0;
				ip_reaching = 1; // partenza automatica
				
				//DebugPrintf(0, "NPT[%i]=%i", bodyNumber, nearstar_p_type[bodyNumber]);
				/*if (nearstar_p_type[bodyNumber]==3) {
					ip_targetted = bodyNumber;
					fix_local_target ();
					ip_targetting = 0;
					ip_reached = 0;
					ip_reaching = 1; // partenza automatica
					Dword gss_seed = (nearstar_p_ray[ip_targetted]
					 + nearstar_p_orb_ray[ip_targetted]
					 + nearstar_p_orb_orient[ip_targetted]) * 4112;	//I wonder what the 4112 is for (SL)
					SRAND (gss_seed);
					fast_srand (gss_seed);
					Dword ape_probability = 0; //(RANDOM(6)+RANDOM(6)+modRandom(6)+modRandom(6)+modRandom(6));
					for (Word a=0; a<8; a++) {
						ape_probability+=fast_random(1023)+1;
					}
					ape_probability-=4192;
					if (ape_probability>0) {
						ape_probability/=364;
						//ape_probability/=745;
						if (ape_probability>20) ape_probability=20;
					} else {
						ape_probability=0;
					}
					if (ape_probability>0) {
						if (nearstar_p_owner[ip_targetted] > -1) {
							DebugPrintf(0, "Critter 0 (density %i) found at moon %i orbiting planet %i in the '%s' system (#%li).", ape_probability, (int)(nearstar_p_moonid[ip_targetted]+1), (int)(nearstar_p_owner[ip_targetted]+1), star_label, systemNumber);
							
						} else {
							DebugPrintf(0, "Critter 0 (density %i) found at planet %i in the '%s' system (#%li).", ape_probability, ip_targetted+1, star_label, systemNumber);
						}
						DebugPrintf(0, "System coords x=%f y=%f z=%f", nearstar_x, -nearstar_y, nearstar_z);
						status("FOUND CRITTER 0! LOGGING.", 100);
					}
					
				}*/
			}
			bodyNumber++;
			
		}
		
	} else {
		//status("UH OH.", 500);
		
	}
}
#endif