www.pudn.com > Wolf3dc.arj > WL_STATE.C


// WL_STATE.C 
 
#include "WL_DEF.H" 
#pragma hdrstop 
 
/* 
============================================================================= 
 
						 LOCAL CONSTANTS 
 
============================================================================= 
*/ 
 
 
/* 
============================================================================= 
 
						 GLOBAL VARIABLES 
 
============================================================================= 
*/ 
 
 
dirtype opposite[9] = 
	{west,southwest,south,southeast,east,northeast,north,northwest,nodir}; 
 
dirtype diagonal[9][9] = 
{ 
/* east */	{nodir,nodir,northeast,nodir,nodir,nodir,southeast,nodir,nodir}, 
			{nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir}, 
/* north */ {northeast,nodir,nodir,nodir,northwest,nodir,nodir,nodir,nodir}, 
			{nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir}, 
/* west */  {nodir,nodir,northwest,nodir,nodir,nodir,southwest,nodir,nodir}, 
			{nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir}, 
/* south */ {southeast,nodir,nodir,nodir,southwest,nodir,nodir,nodir,nodir}, 
			{nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir}, 
			{nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir} 
}; 
 
 
 
void	SpawnNewObj (unsigned tilex, unsigned tiley, statetype *state); 
void	NewState (objtype *ob, statetype *state); 
 
boolean TryWalk (objtype *ob); 
void	MoveObj (objtype *ob, long move); 
 
void	KillActor (objtype *ob); 
void	DamageActor (objtype *ob, unsigned damage); 
 
boolean CheckLine (objtype *ob); 
void FirstSighting (objtype *ob); 
boolean	CheckSight (objtype *ob); 
 
/* 
============================================================================= 
 
						 LOCAL VARIABLES 
 
============================================================================= 
*/ 
 
 
 
//=========================================================================== 
 
 
/* 
=================== 
= 
= SpawnNewObj 
= 
= Spaws a new actor at the given TILE coordinates, with the given state, and 
= the given size in GLOBAL units. 
= 
= new			= a pointer to an initialized new actor 
= 
=================== 
*/ 
 
void SpawnNewObj (unsigned tilex, unsigned tiley, statetype *state) 
{ 
	GetNewActor (); 
	new->state = state; 
	if (state->tictime) 
		new->ticcount = US_RndT () % state->tictime; 
	else 
		new->ticcount = 0; 
 
	new->tilex = tilex; 
	new->tiley = tiley; 
	new->x = ((long)tilex<y = ((long)tiley<dir = nodir; 
 
	actorat[tilex][tiley] = new; 
	new->areanumber = 
		*(mapsegs[0] + farmapylookup[new->tiley]+new->tilex) - AREATILE; 
} 
 
 
 
/* 
=================== 
= 
= NewState 
= 
= Changes ob to a new state, setting ticcount to the max for that state 
= 
=================== 
*/ 
 
void NewState (objtype *ob, statetype *state) 
{ 
	ob->state = state; 
	ob->ticcount = state->tictime; 
} 
 
 
 
/* 
============================================================================= 
 
				ENEMY TILE WORLD MOVEMENT CODE 
 
============================================================================= 
*/ 
 
 
/* 
================================== 
= 
= TryWalk 
= 
= Attempts to move ob in its current (ob->dir) direction. 
= 
= If blocked by either a wall or an actor returns FALSE 
= 
= If move is either clear or blocked only by a door, returns TRUE and sets 
= 
= ob->tilex			= new destination 
= ob->tiley 
= ob->areanumber    = the floor tile number (0-(NUMAREAS-1)) of destination 
= ob->distance  	= TILEGLOBAl, or -doornumber if a door is blocking the way 
= 
= If a door is in the way, an OpenDoor call is made to start it opening. 
= The actor code should wait until 
= 	doorobjlist[-ob->distance].action = dr_open, meaning the door has been 
=	fully opened 
= 
================================== 
*/ 
 
#define CHECKDIAG(x,y)								\ 
{                                                   \ 
	temp=(unsigned)actorat[x][y];                   \ 
	if (temp)                                       \ 
	{                                               \ 
		if (temp<256)                               \ 
			return false;                           \ 
		if (((objtype *)temp)->flags&FL_SHOOTABLE)  \ 
			return false;                           \ 
	}                                               \ 
} 
 
#define CHECKSIDE(x,y)								\ 
{                                                   \ 
	temp=(unsigned)actorat[x][y];                   \ 
	if (temp)                                       \ 
	{                                               \ 
		if (temp<128)                               \ 
			return false;                           \ 
		if (temp<256)                               \ 
			doornum = temp&63;                      \ 
		else if (((objtype *)temp)->flags&FL_SHOOTABLE)\ 
			return false;                           \ 
	}                                               \ 
} 
 
 
boolean TryWalk (objtype *ob) 
{ 
	int			doornum; 
	unsigned	temp; 
 
	doornum = -1; 
 
	if (ob->obclass == inertobj) 
	{ 
		switch (ob->dir) 
		{ 
		case north: 
			ob->tiley--; 
			break; 
 
		case northeast: 
			ob->tilex++; 
			ob->tiley--; 
			break; 
 
		case east: 
			ob->tilex++; 
			break; 
 
		case southeast: 
			ob->tilex++; 
			ob->tiley++; 
			break; 
 
		case south: 
			ob->tiley++; 
			break; 
 
		case southwest: 
			ob->tilex--; 
			ob->tiley++; 
			break; 
 
		case west: 
			ob->tilex--; 
			break; 
 
		case northwest: 
			ob->tilex--; 
			ob->tiley--; 
			break; 
		} 
	} 
	else 
		switch (ob->dir) 
		{ 
		case north: 
			if (ob->obclass == dogobj || ob->obclass == fakeobj) 
			{ 
				CHECKDIAG(ob->tilex,ob->tiley-1); 
			} 
			else 
			{ 
				CHECKSIDE(ob->tilex,ob->tiley-1); 
			} 
			ob->tiley--; 
			break; 
 
		case northeast: 
			CHECKDIAG(ob->tilex+1,ob->tiley-1); 
			CHECKDIAG(ob->tilex+1,ob->tiley); 
			CHECKDIAG(ob->tilex,ob->tiley-1); 
			ob->tilex++; 
			ob->tiley--; 
			break; 
 
		case east: 
			if (ob->obclass == dogobj || ob->obclass == fakeobj) 
			{ 
				CHECKDIAG(ob->tilex+1,ob->tiley); 
			} 
			else 
			{ 
				CHECKSIDE(ob->tilex+1,ob->tiley); 
			} 
			ob->tilex++; 
			break; 
 
		case southeast: 
			CHECKDIAG(ob->tilex+1,ob->tiley+1); 
			CHECKDIAG(ob->tilex+1,ob->tiley); 
			CHECKDIAG(ob->tilex,ob->tiley+1); 
			ob->tilex++; 
			ob->tiley++; 
			break; 
 
		case south: 
			if (ob->obclass == dogobj || ob->obclass == fakeobj) 
			{ 
				CHECKDIAG(ob->tilex,ob->tiley+1); 
			} 
			else 
			{ 
				CHECKSIDE(ob->tilex,ob->tiley+1); 
			} 
			ob->tiley++; 
			break; 
 
		case southwest: 
			CHECKDIAG(ob->tilex-1,ob->tiley+1); 
			CHECKDIAG(ob->tilex-1,ob->tiley); 
			CHECKDIAG(ob->tilex,ob->tiley+1); 
			ob->tilex--; 
			ob->tiley++; 
			break; 
 
		case west: 
			if (ob->obclass == dogobj || ob->obclass == fakeobj) 
			{ 
				CHECKDIAG(ob->tilex-1,ob->tiley); 
			} 
			else 
			{ 
				CHECKSIDE(ob->tilex-1,ob->tiley); 
			} 
			ob->tilex--; 
			break; 
 
		case northwest: 
			CHECKDIAG(ob->tilex-1,ob->tiley-1); 
			CHECKDIAG(ob->tilex-1,ob->tiley); 
			CHECKDIAG(ob->tilex,ob->tiley-1); 
			ob->tilex--; 
			ob->tiley--; 
			break; 
 
		case nodir: 
			return false; 
 
		default: 
			Quit ("Walk: Bad dir"); 
		} 
 
	if (doornum != -1) 
	{ 
		OpenDoor (doornum); 
		ob->distance = -doornum-1; 
		return true; 
	} 
 
 
	ob->areanumber = 
		*(mapsegs[0] + farmapylookup[ob->tiley]+ob->tilex) - AREATILE; 
 
	ob->distance = TILEGLOBAL; 
	return true; 
} 
 
 
 
/* 
================================== 
= 
= SelectDodgeDir 
= 
= Attempts to choose and initiate a movement for ob that sends it towards 
= the player while dodging 
= 
= If there is no possible move (ob is totally surrounded) 
= 
= ob->dir			=	nodir 
= 
= Otherwise 
= 
= ob->dir			= new direction to follow 
= ob->distance		= TILEGLOBAL or -doornumber 
= ob->tilex			= new destination 
= ob->tiley 
= ob->areanumber    = the floor tile number (0-(NUMAREAS-1)) of destination 
= 
================================== 
*/ 
 
void SelectDodgeDir (objtype *ob) 
{ 
	int 		deltax,deltay,i; 
	unsigned	absdx,absdy; 
	dirtype 	dirtry[5]; 
	dirtype 	turnaround,tdir; 
 
	if (ob->flags & FL_FIRSTATTACK) 
	{ 
	// 
	// turning around is only ok the very first time after noticing the 
	// player 
	// 
		turnaround = nodir; 
		ob->flags &= ~FL_FIRSTATTACK; 
	} 
	else 
		turnaround=opposite[ob->dir]; 
 
	deltax = player->tilex - ob->tilex; 
	deltay = player->tiley - ob->tiley; 
 
// 
// arange 5 direction choices in order of preference 
// the four cardinal directions plus the diagonal straight towards 
// the player 
// 
 
	if (deltax>0) 
	{ 
		dirtry[1]= east; 
		dirtry[3]= west; 
	} 
	else 
	{ 
		dirtry[1]= west; 
		dirtry[3]= east; 
	} 
 
	if (deltay>0) 
	{ 
		dirtry[2]= south; 
		dirtry[4]= north; 
	} 
	else 
	{ 
		dirtry[2]= north; 
		dirtry[4]= south; 
	} 
 
// 
// randomize a bit for dodging 
// 
	absdx = abs(deltax); 
	absdy = abs(deltay); 
 
	if (absdx > absdy) 
	{ 
		tdir = dirtry[1]; 
		dirtry[1] = dirtry[2]; 
		dirtry[2] = tdir; 
		tdir = dirtry[3]; 
		dirtry[3] = dirtry[4]; 
		dirtry[4] = tdir; 
	} 
 
	if (US_RndT() < 128) 
	{ 
		tdir = dirtry[1]; 
		dirtry[1] = dirtry[2]; 
		dirtry[2] = tdir; 
		tdir = dirtry[3]; 
		dirtry[3] = dirtry[4]; 
		dirtry[4] = tdir; 
	} 
 
	dirtry[0] = diagonal [ dirtry[1] ] [ dirtry[2] ]; 
 
// 
// try the directions util one works 
// 
	for (i=0;i<5;i++) 
	{ 
		if ( dirtry[i] == nodir || dirtry[i] == turnaround) 
			continue; 
 
		ob->dir = dirtry[i]; 
		if (TryWalk(ob)) 
			return; 
	} 
 
// 
// turn around only as a last resort 
// 
	if (turnaround != nodir) 
	{ 
		ob->dir = turnaround; 
 
		if (TryWalk(ob)) 
			return; 
	} 
 
	ob->dir = nodir; 
} 
 
 
/* 
============================ 
= 
= SelectChaseDir 
= 
= As SelectDodgeDir, but doesn't try to dodge 
= 
============================ 
*/ 
 
void SelectChaseDir (objtype *ob) 
{ 
	int deltax,deltay,i; 
	dirtype d[3]; 
	dirtype tdir, olddir, turnaround; 
 
 
	olddir=ob->dir; 
	turnaround=opposite[olddir]; 
 
	deltax=player->tilex - ob->tilex; 
	deltay=player->tiley - ob->tiley; 
 
	d[1]=nodir; 
	d[2]=nodir; 
 
	if (deltax>0) 
		d[1]= east; 
	else if (deltax<0) 
		d[1]= west; 
	if (deltay>0) 
		d[2]=south; 
	else if (deltay<0) 
		d[2]=north; 
 
	if (abs(deltay)>abs(deltax)) 
	{ 
		tdir=d[1]; 
		d[1]=d[2]; 
		d[2]=tdir; 
	} 
 
	if (d[1]==turnaround) 
		d[1]=nodir; 
	if (d[2]==turnaround) 
		d[2]=nodir; 
 
 
	if (d[1]!=nodir) 
	{ 
		ob->dir=d[1]; 
		if (TryWalk(ob)) 
			return;     /*either moved forward or attacked*/ 
	} 
 
	if (d[2]!=nodir) 
	{ 
		ob->dir=d[2]; 
		if (TryWalk(ob)) 
			return; 
	} 
 
/* there is no direct path to the player, so pick another direction */ 
 
	if (olddir!=nodir) 
	{ 
		ob->dir=olddir; 
		if (TryWalk(ob)) 
			return; 
	} 
 
	if (US_RndT()>128) 	/*randomly determine direction of search*/ 
	{ 
		for (tdir=north;tdir<=west;tdir++) 
		{ 
			if (tdir!=turnaround) 
			{ 
				ob->dir=tdir; 
				if ( TryWalk(ob) ) 
					return; 
			} 
		} 
	} 
	else 
	{ 
		for (tdir=west;tdir>=north;tdir--) 
		{ 
			if (tdir!=turnaround) 
			{ 
			  ob->dir=tdir; 
			  if ( TryWalk(ob) ) 
				return; 
			} 
		} 
	} 
 
	if (turnaround !=  nodir) 
	{ 
		ob->dir=turnaround; 
		if (ob->dir != nodir) 
		{ 
			if ( TryWalk(ob) ) 
				return; 
		} 
	} 
 
	ob->dir = nodir;		// can't move 
} 
 
 
/* 
============================ 
= 
= SelectRunDir 
= 
= Run Away from player 
= 
============================ 
*/ 
 
void SelectRunDir (objtype *ob) 
{ 
	int deltax,deltay,i; 
	dirtype d[3]; 
	dirtype tdir, olddir, turnaround; 
 
 
	deltax=player->tilex - ob->tilex; 
	deltay=player->tiley - ob->tiley; 
 
	if (deltax<0) 
		d[1]= east; 
	else 
		d[1]= west; 
	if (deltay<0) 
		d[2]=south; 
	else 
		d[2]=north; 
 
	if (abs(deltay)>abs(deltax)) 
	{ 
		tdir=d[1]; 
		d[1]=d[2]; 
		d[2]=tdir; 
	} 
 
	ob->dir=d[1]; 
	if (TryWalk(ob)) 
		return;     /*either moved forward or attacked*/ 
 
	ob->dir=d[2]; 
	if (TryWalk(ob)) 
		return; 
 
/* there is no direct path to the player, so pick another direction */ 
 
	if (US_RndT()>128) 	/*randomly determine direction of search*/ 
	{ 
		for (tdir=north;tdir<=west;tdir++) 
		{ 
			ob->dir=tdir; 
			if ( TryWalk(ob) ) 
				return; 
		} 
	} 
	else 
	{ 
		for (tdir=west;tdir>=north;tdir--) 
		{ 
			ob->dir=tdir; 
			if ( TryWalk(ob) ) 
			  return; 
		} 
	} 
 
	ob->dir = nodir;		// can't move 
} 
 
 
/* 
================= 
= 
= MoveObj 
= 
= Moves ob be move global units in ob->dir direction 
= Actors are not allowed to move inside the player 
= Does NOT check to see if the move is tile map valid 
= 
= ob->x			= adjusted for new position 
= ob->y 
= 
================= 
*/ 
 
void MoveObj (objtype *ob, long move) 
{ 
	long	deltax,deltay; 
 
	switch (ob->dir) 
	{ 
	case north: 
		ob->y -= move; 
		break; 
	case northeast: 
		ob->x += move; 
		ob->y -= move; 
		break; 
	case east: 
		ob->x += move; 
		break; 
	case southeast: 
		ob->x += move; 
		ob->y += move; 
		break; 
	case south: 
		ob->y += move; 
		break; 
	case southwest: 
		ob->x -= move; 
		ob->y += move; 
		break; 
	case west: 
		ob->x -= move; 
		break; 
	case northwest: 
		ob->x -= move; 
		ob->y -= move; 
		break; 
 
	case nodir: 
		return; 
 
	default: 
		Quit ("MoveObj: bad dir!"); 
	} 
 
// 
// check to make sure it's not on top of player 
// 
	if (areabyplayer[ob->areanumber]) 
	{ 
		deltax = ob->x - player->x; 
		if (deltax < -MINACTORDIST || deltax > MINACTORDIST) 
			goto moveok; 
		deltay = ob->y - player->y; 
		if (deltay < -MINACTORDIST || deltay > MINACTORDIST) 
			goto moveok; 
 
		if (ob->obclass == ghostobj || ob->obclass == spectreobj) 
			TakeDamage (tics*2,ob); 
 
	// 
	// back up 
	// 
		switch (ob->dir) 
		{ 
		case north: 
			ob->y += move; 
			break; 
		case northeast: 
			ob->x -= move; 
			ob->y += move; 
			break; 
		case east: 
			ob->x -= move; 
			break; 
		case southeast: 
			ob->x -= move; 
			ob->y -= move; 
			break; 
		case south: 
			ob->y -= move; 
			break; 
		case southwest: 
			ob->x += move; 
			ob->y -= move; 
			break; 
		case west: 
			ob->x += move; 
			break; 
		case northwest: 
			ob->x += move; 
			ob->y += move; 
			break; 
 
		case nodir: 
			return; 
		} 
		return; 
	} 
moveok: 
	ob->distance -=move; 
} 
 
/* 
============================================================================= 
 
							STUFF 
 
============================================================================= 
*/ 
 
/* 
=============== 
= 
= DropItem 
= 
= Tries to drop a bonus item somewhere in the tiles surrounding the 
= given tilex/tiley 
= 
=============== 
*/ 
 
void DropItem (stat_t itemtype, int tilex, int tiley) 
{ 
	int	x,y,xl,xh,yl,yh; 
 
// 
// find a free spot to put it in 
// 
	if (!actorat[tilex][tiley]) 
	{ 
		PlaceItemType (itemtype, tilex,tiley); 
		return; 
	} 
 
	xl = tilex-1; 
	xh = tilex+1; 
	yl = tiley-1; 
	yh = tiley+1; 
 
	for (x=xl ; x<= xh ; x++) 
		for (y=yl ; y<= yh ; y++) 
			if (!actorat[x][y]) 
			{ 
				PlaceItemType (itemtype, x,y); 
				return; 
			} 
} 
 
 
 
/* 
=============== 
= 
= KillActor 
= 
=============== 
*/ 
 
void KillActor (objtype *ob) 
{ 
	int	tilex,tiley; 
 
	tilex = ob->tilex = ob->x >> TILESHIFT;		// drop item on center 
	tiley = ob->tiley = ob->y >> TILESHIFT; 
 
	switch (ob->obclass) 
	{ 
	case guardobj: 
		GivePoints (100); 
		NewState (ob,&s_grddie1); 
		PlaceItemType (bo_clip2,tilex,tiley); 
		break; 
 
	case officerobj: 
		GivePoints (400); 
		NewState (ob,&s_ofcdie1); 
		PlaceItemType (bo_clip2,tilex,tiley); 
		break; 
 
	case mutantobj: 
		GivePoints (700); 
		NewState (ob,&s_mutdie1); 
		PlaceItemType (bo_clip2,tilex,tiley); 
		break; 
 
	case ssobj: 
		GivePoints (500); 
		NewState (ob,&s_ssdie1); 
		if (gamestate.bestweapon < wp_machinegun) 
			PlaceItemType (bo_machinegun,tilex,tiley); 
		else 
			PlaceItemType (bo_clip2,tilex,tiley); 
		break; 
 
	case dogobj: 
		GivePoints (200); 
		NewState (ob,&s_dogdie1); 
		break; 
 
#ifndef SPEAR 
	case bossobj: 
		GivePoints (5000); 
		NewState (ob,&s_bossdie1); 
		PlaceItemType (bo_key1,tilex,tiley); 
		break; 
 
	case gretelobj: 
		GivePoints (5000); 
		NewState (ob,&s_greteldie1); 
		PlaceItemType (bo_key1,tilex,tiley); 
		break; 
 
	case giftobj: 
		GivePoints (5000); 
		gamestate.killx = player->x; 
		gamestate.killy = player->y; 
		NewState (ob,&s_giftdie1); 
		break; 
 
	case fatobj: 
		GivePoints (5000); 
		gamestate.killx = player->x; 
		gamestate.killy = player->y; 
		NewState (ob,&s_fatdie1); 
		break; 
 
	case schabbobj: 
		GivePoints (5000); 
		gamestate.killx = player->x; 
		gamestate.killy = player->y; 
		NewState (ob,&s_schabbdie1); 
		A_DeathScream(ob); 
		break; 
	case fakeobj: 
		GivePoints (2000); 
		NewState (ob,&s_fakedie1); 
		break; 
 
	case mechahitlerobj: 
		GivePoints (5000); 
		NewState (ob,&s_mechadie1); 
		break; 
	case realhitlerobj: 
		GivePoints (5000); 
		gamestate.killx = player->x; 
		gamestate.killy = player->y; 
		NewState (ob,&s_hitlerdie1); 
		A_DeathScream(ob); 
		break; 
#else 
	case spectreobj: 
		GivePoints (200); 
		NewState (ob,&s_spectredie1); 
		break; 
 
	case angelobj: 
		GivePoints (5000); 
		NewState (ob,&s_angeldie1); 
		break; 
 
	case transobj: 
		GivePoints (5000); 
		NewState (ob,&s_transdie0); 
		PlaceItemType (bo_key1,tilex,tiley); 
		break; 
 
	case uberobj: 
		GivePoints (5000); 
		NewState (ob,&s_uberdie0); 
		PlaceItemType (bo_key1,tilex,tiley); 
		break; 
 
	case willobj: 
		GivePoints (5000); 
		NewState (ob,&s_willdie1); 
		PlaceItemType (bo_key1,tilex,tiley); 
		break; 
 
	case deathobj: 
		GivePoints (5000); 
		NewState (ob,&s_deathdie1); 
		PlaceItemType (bo_key1,tilex,tiley); 
		break; 
#endif 
	} 
 
	gamestate.killcount++; 
	ob->flags &= ~FL_SHOOTABLE; 
	actorat[ob->tilex][ob->tiley] = NULL; 
	ob->flags |= FL_NONMARK; 
} 
 
 
 
/* 
=================== 
= 
= DamageActor 
= 
= Called when the player succesfully hits an enemy. 
= 
= Does damage points to enemy ob, either putting it into a stun frame or 
= killing it. 
= 
=================== 
*/ 
 
void DamageActor (objtype *ob, unsigned damage) 
{ 
	madenoise = true; 
 
// 
// do double damage if shooting a non attack mode actor 
// 
	if ( !(ob->flags & FL_ATTACKMODE) ) 
		damage <<= 1; 
 
	ob->hitpoints -= damage; 
 
	if (ob->hitpoints<=0) 
		KillActor (ob); 
	else 
	{ 
		if (! (ob->flags & FL_ATTACKMODE) ) 
			FirstSighting (ob);		// put into combat mode 
 
		switch (ob->obclass)		// dogs only have one hit point 
		{ 
		case guardobj: 
			if (ob->hitpoints&1) 
				NewState (ob,&s_grdpain); 
			else 
				NewState (ob,&s_grdpain1); 
			break; 
 
		case officerobj: 
			if (ob->hitpoints&1) 
				NewState (ob,&s_ofcpain); 
			else 
				NewState (ob,&s_ofcpain1); 
			break; 
 
		case mutantobj: 
			if (ob->hitpoints&1) 
				NewState (ob,&s_mutpain); 
			else 
				NewState (ob,&s_mutpain1); 
			break; 
 
		case ssobj: 
			if (ob->hitpoints&1) 
				NewState (ob,&s_sspain); 
			else 
				NewState (ob,&s_sspain1); 
 
			break; 
 
		} 
	} 
} 
 
/* 
============================================================================= 
 
							CHECKSIGHT 
 
============================================================================= 
*/ 
 
 
/* 
===================== 
= 
= CheckLine 
= 
= Returns true if a straight line between the player and ob is unobstructed 
= 
===================== 
*/ 
 
boolean CheckLine (objtype *ob) 
{ 
	int	x1,y1,xt1,yt1,x2,y2,xt2,yt2; 
	int	x,y; 
	int	xdist,ydist,xstep,ystep; 
	int	temp; 
	int	partial,delta; 
	long	ltemp; 
	int	xfrac,yfrac,deltafrac; 
	unsigned	value,intercept; 
 
	x1 = ob->x >> UNSIGNEDSHIFT;		// 1/256 tile precision 
	y1 = ob->y >> UNSIGNEDSHIFT; 
	xt1 = x1 >> 8; 
	yt1 = y1 >> 8; 
 
	x2 = plux; 
	y2 = pluy; 
	xt2 = player->tilex; 
	yt2 = player->tiley; 
 
 
	xdist = abs(xt2-xt1); 
 
	if (xdist > 0) 
	{ 
		if (xt2 > xt1) 
		{ 
			partial = 256-(x1&0xff); 
			xstep = 1; 
		} 
		else 
		{ 
			partial = x1&0xff; 
			xstep = -1; 
		} 
 
		deltafrac = abs(x2-x1); 
		delta = y2-y1; 
		ltemp = ((long)delta<<8)/deltafrac; 
		if (ltemp > 0x7fffl) 
			ystep = 0x7fff; 
		else if (ltemp < -0x7fffl) 
			ystep = -0x7fff; 
		else 
			ystep = ltemp; 
		yfrac = y1 + (((long)ystep*partial) >>8); 
 
		x = xt1+xstep; 
		xt2 += xstep; 
		do 
		{ 
			y = yfrac>>8; 
			yfrac += ystep; 
 
			value = (unsigned)tilemap[x][y]; 
			x += xstep; 
 
			if (!value) 
				continue; 
 
			if (value<128 || value>256) 
				return false; 
 
			// 
			// see if the door is open enough 
			// 
			value &= ~0x80; 
			intercept = yfrac-ystep/2; 
 
			if (intercept>doorposition[value]) 
				return false; 
 
		} while (x != xt2); 
	} 
 
	ydist = abs(yt2-yt1); 
 
	if (ydist > 0) 
	{ 
		if (yt2 > yt1) 
		{ 
			partial = 256-(y1&0xff); 
			ystep = 1; 
		} 
		else 
		{ 
			partial = y1&0xff; 
			ystep = -1; 
		} 
 
		deltafrac = abs(y2-y1); 
		delta = x2-x1; 
		ltemp = ((long)delta<<8)/deltafrac; 
		if (ltemp > 0x7fffl) 
			xstep = 0x7fff; 
		else if (ltemp < -0x7fffl) 
			xstep = -0x7fff; 
		else 
			xstep = ltemp; 
		xfrac = x1 + (((long)xstep*partial) >>8); 
 
		y = yt1 + ystep; 
		yt2 += ystep; 
		do 
		{ 
			x = xfrac>>8; 
			xfrac += xstep; 
 
			value = (unsigned)tilemap[x][y]; 
			y += ystep; 
 
			if (!value) 
				continue; 
 
			if (value<128 || value>256) 
				return false; 
 
			// 
			// see if the door is open enough 
			// 
			value &= ~0x80; 
			intercept = xfrac-xstep/2; 
 
			if (intercept>doorposition[value]) 
				return false; 
		} while (y != yt2); 
	} 
 
	return true; 
} 
 
 
 
/* 
================ 
= 
= CheckSight 
= 
= Checks a straight line between player and current object 
= 
= If the sight is ok, check alertness and angle to see if they notice 
= 
= returns true if the player has been spoted 
= 
================ 
*/ 
 
#define MINSIGHT	0x18000l 
 
boolean CheckSight (objtype *ob) 
{ 
	long		deltax,deltay; 
 
// 
// don't bother tracing a line if the area isn't connected to the player's 
// 
	if (!areabyplayer[ob->areanumber]) 
		return false; 
 
// 
// if the player is real close, sight is automatic 
// 
	deltax = player->x - ob->x; 
	deltay = player->y - ob->y; 
 
	if (deltax > -MINSIGHT && deltax < MINSIGHT 
	&& deltay > -MINSIGHT && deltay < MINSIGHT) 
		return true; 
 
// 
// see if they are looking in the right direction 
// 
	switch (ob->dir) 
	{ 
	case north: 
		if (deltay > 0) 
			return false; 
		break; 
 
	case east: 
		if (deltax < 0) 
			return false; 
		break; 
 
	case south: 
		if (deltay < 0) 
			return false; 
		break; 
 
	case west: 
		if (deltax > 0) 
			return false; 
		break; 
	} 
 
// 
// trace a line to check for blocking tiles (corners) 
// 
	return CheckLine (ob); 
 
} 
 
 
 
/* 
=============== 
= 
= FirstSighting 
= 
= Puts an actor into attack mode and possibly reverses the direction 
= if the player is behind it 
= 
=============== 
*/ 
 
void FirstSighting (objtype *ob) 
{ 
// 
// react to the player 
// 
	switch (ob->obclass) 
	{ 
	case guardobj: 
		PlaySoundLocActor(HALTSND,ob); 
		NewState (ob,&s_grdchase1); 
		ob->speed *= 3;			// go faster when chasing player 
		break; 
 
	case officerobj: 
		PlaySoundLocActor(SPIONSND,ob); 
		NewState (ob,&s_ofcchase1); 
		ob->speed *= 5;			// go faster when chasing player 
		break; 
 
	case mutantobj: 
		NewState (ob,&s_mutchase1); 
		ob->speed *= 3;			// go faster when chasing player 
		break; 
 
	case ssobj: 
		PlaySoundLocActor(SCHUTZADSND,ob); 
		NewState (ob,&s_sschase1); 
		ob->speed *= 4;			// go faster when chasing player 
		break; 
 
	case dogobj: 
		PlaySoundLocActor(DOGBARKSND,ob); 
		NewState (ob,&s_dogchase1); 
		ob->speed *= 2;			// go faster when chasing player 
		break; 
 
#ifndef SPEAR 
	case bossobj: 
		SD_PlaySound(GUTENTAGSND); 
		NewState (ob,&s_bosschase1); 
		ob->speed = SPDPATROL*3;	// go faster when chasing player 
		break; 
 
	case gretelobj: 
		SD_PlaySound(KEINSND); 
		NewState (ob,&s_gretelchase1); 
		ob->speed *= 3;			// go faster when chasing player 
		break; 
 
	case giftobj: 
		SD_PlaySound(EINESND); 
		NewState (ob,&s_giftchase1); 
		ob->speed *= 3;			// go faster when chasing player 
		break; 
 
	case fatobj: 
		SD_PlaySound(ERLAUBENSND); 
		NewState (ob,&s_fatchase1); 
		ob->speed *= 3;			// go faster when chasing player 
		break; 
 
	case schabbobj: 
		SD_PlaySound(SCHABBSHASND); 
		NewState (ob,&s_schabbchase1); 
		ob->speed *= 3;			// go faster when chasing player 
		break; 
 
	case fakeobj: 
		SD_PlaySound(TOT_HUNDSND); 
		NewState (ob,&s_fakechase1); 
		ob->speed *= 3;			// go faster when chasing player 
		break; 
 
	case mechahitlerobj: 
		SD_PlaySound(DIESND); 
		NewState (ob,&s_mechachase1); 
		ob->speed *= 3;			// go faster when chasing player 
		break; 
 
	case realhitlerobj: 
		SD_PlaySound(DIESND); 
		NewState (ob,&s_hitlerchase1); 
		ob->speed *= 5;			// go faster when chasing player 
		break; 
 
	case ghostobj: 
		NewState (ob,&s_blinkychase1); 
		ob->speed *= 2;			// go faster when chasing player 
		break; 
#else 
 
	case spectreobj: 
		SD_PlaySound(GHOSTSIGHTSND); 
		NewState (ob,&s_spectrechase1); 
		ob->speed = 800;			// go faster when chasing player 
		break; 
 
	case angelobj: 
		SD_PlaySound(ANGELSIGHTSND); 
		NewState (ob,&s_angelchase1); 
		ob->speed = 1536;			// go faster when chasing player 
		break; 
 
	case transobj: 
		SD_PlaySound(TRANSSIGHTSND); 
		NewState (ob,&s_transchase1); 
		ob->speed = 1536;			// go faster when chasing player 
		break; 
 
	case uberobj: 
		NewState (ob,&s_uberchase1); 
		ob->speed = 3000;			// go faster when chasing player 
		break; 
 
	case willobj: 
		SD_PlaySound(WILHELMSIGHTSND); 
		NewState (ob,&s_willchase1); 
		ob->speed = 2048;			// go faster when chasing player 
		break; 
 
	case deathobj: 
		SD_PlaySound(KNIGHTSIGHTSND); 
		NewState (ob,&s_deathchase1); 
		ob->speed = 2048;			// go faster when chasing player 
		break; 
 
#endif 
	} 
 
	if (ob->distance < 0) 
		ob->distance = 0;	// ignore the door opening command 
 
	ob->flags |= FL_ATTACKMODE|FL_FIRSTATTACK; 
} 
 
 
 
/* 
=============== 
= 
= SightPlayer 
= 
= Called by actors that ARE NOT chasing the player.  If the player 
= is detected (by sight, noise, or proximity), the actor is put into 
= it's combat frame and true is returned. 
= 
= Incorporates a random reaction delay 
= 
=============== 
*/ 
 
boolean SightPlayer (objtype *ob) 
{ 
	if (ob->flags & FL_ATTACKMODE) 
		Quit ("An actor in ATTACKMODE called SightPlayer!"); 
 
	if (ob->temp2) 
	{ 
	// 
	// count down reaction time 
	// 
		ob->temp2 -= tics; 
		if (ob->temp2 > 0) 
			return false; 
		ob->temp2 = 0;					// time to react 
	} 
	else 
	{ 
		if (!areabyplayer[ob->areanumber]) 
			return false; 
 
		if (ob->flags & FL_AMBUSH) 
		{ 
			if (!CheckSight (ob)) 
				return false; 
			ob->flags &= ~FL_AMBUSH; 
		} 
		else 
		{ 
			if (!madenoise && !CheckSight (ob)) 
				return false; 
		} 
 
 
		switch (ob->obclass) 
		{ 
		case guardobj: 
			ob->temp2 = 1+US_RndT()/4; 
			break; 
		case officerobj: 
			ob->temp2 = 2; 
			break; 
		case mutantobj: 
			ob->temp2 = 1+US_RndT()/6; 
			break; 
		case ssobj: 
			ob->temp2 = 1+US_RndT()/6; 
			break; 
		case dogobj: 
			ob->temp2 = 1+US_RndT()/8; 
			break; 
 
		case bossobj: 
		case schabbobj: 
		case fakeobj: 
		case mechahitlerobj: 
		case realhitlerobj: 
		case gretelobj: 
		case giftobj: 
		case fatobj: 
		case spectreobj: 
		case angelobj: 
		case transobj: 
		case uberobj: 
		case willobj: 
		case deathobj: 
			ob->temp2 = 1; 
			break; 
		} 
		return false; 
	} 
 
	FirstSighting (ob); 
 
	return true; 
}