#ifdef PRECOMPILEDHEADERS
	#include "Tactical All.h"
#else
	#include <stdio.h>
	#include <string.h>
	#include "stdlib.h"
	#include "debug.h"
	#include "math.h"
	#include "worlddef.h"
	#include "worldman.h"
	#include "renderworld.h"
	#include "Assignments.h"
	#include "Soldier Control.h"
	#include "Animation Control.h"
	#include "Animation Data.h"
	#include "Isometric Utils.h"
	#include "Event Pump.h"
	#include "Render Fun.h"
	#include "mousesystem.h"
	#include "interface.h"
	#include "sysutil.h"
	#include "FileMan.h"
	#include "Random.h"
	#include "ai.h"
	#include "Interactive Tiles.h"
	#include "english.h"
	#include "overhead.h"
	//#include "Sound Control.h"
	#include "lighting.h"
	#include "pathai.h"
	#include "screenids.h"
	#include "weapons.h"
	#include "lighting.h"
	#include "Handle UI Plan.h"
	#include "structure.h"
	#include "message.h"
	#include "items.h"
	#include "Soldier Profile.h"
	#include "fov.h"
	#include "Soldier macros.h"
	#include "soldier tile.h"
	#include "soldier find.h"
	#include "Vehicles.h"
	#include "GameSettings.h"
	#include "ui cursors.h"
#endif


BOOLEAN IsGridNoInScreenRect( INT32 sGridNo, SGPRect *pRect );
BOOLEAN IsPointInScreenRect( INT16 sXPos, INT16 sYPos, SGPRect *pRect );
void GetSoldierScreenRect( SOLDIERTYPE *pSoldier, SGPRect *pRect );

// This value is used to keep a small static array of uBID's which are stacked
#define				MAX_STACKED_MERCS		10


UINT32							gScrollSlideInertiaDirection[ NUM_WORLD_DIRECTIONS ] =
{
	3,
	0,
	0,
	0,
	0,
	0,
	3,
	3,
};

// Struct used for cycling through multiple mercs per mouse position
typedef struct
{
	INT8			bNum;
	UINT8			ubIDs[ MAX_STACKED_MERCS ];
	INT8			bCur;
	BOOLEAN		fUseGridNo;
	INT32		sUseGridNoGridNo;

} SOLDIER_STACK_TYPE;

SOLDIER_STACK_TYPE		gSoldierStack;
BOOLEAN								gfHandleStack = FALSE;

extern								BOOLEAN	gUIActionModeChangeDueToMouseOver;
extern								UINT32	guiUITargetSoldierId;


BOOLEAN FindSoldierFromMouse( UINT16 *pusSoldierIndex, UINT32 *pMercFlags )
{
	INT32							usMapPos;

	*pMercFlags = 0;

	 if ( GetMouseMapPos( &usMapPos ) )
	{
			 if ( FindSoldier( usMapPos, pusSoldierIndex, pMercFlags ,FINDSOLDIERSAMELEVEL( gsInterfaceLevel ) ) )
			{
				return( TRUE );
			}
	}

	return( FALSE );
}

BOOLEAN SelectiveFindSoldierFromMouse( UINT16 *pusSoldierIndex, UINT32 *pMercFlags )
{
	INT32							usMapPos;

	*pMercFlags = 0;

	 if ( GetMouseMapPos( &usMapPos ) )
	{
			 if ( FindSoldier( usMapPos, pusSoldierIndex, pMercFlags , FINDSOLDIERSAMELEVEL( gsInterfaceLevel ) ) )
			{
				return( TRUE );
			}
	}

	return( FALSE );
}


UINT32 GetSoldierFindFlags( UINT16 ubID )
{
	UINT32 MercFlags = 0;
	SOLDIERTYPE *pSoldier;

	// Get pSoldier!
	pSoldier = MercPtrs[ ubID ];

	// FInd out and set flags
	if ( ubID == gusSelectedSoldier )
	{
		MercFlags |= SELECTED_MERC;
	}
	if ( ubID >= gTacticalStatus.Team[ gbPlayerNum ].bFirstID && ubID <= gTacticalStatus.Team[ gbPlayerNum ].bLastID )
	{
		if ( ( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) && !GetNumberInVehicle( pSoldier->bVehicleID ) )
		{
			// Don't do anything!
		}
		else
		{
				// It's our own merc
			MercFlags	|= OWNED_MERC;

			if ( pSoldier->bAssignment < ON_DUTY )
			{
					MercFlags |= ONDUTY_MERC;
			}
		}
	}
	else
	{
		// Check the side, etc
		if ( !pSoldier->aiData.bNeutral && (pSoldier->bSide != gbPlayerNum ) )
		{
				// It's an enemy merc
			MercFlags	|= ENEMY_MERC;
		}
		else
		{
				// It's not an enemy merc
			MercFlags	|= NEUTRAL_MERC;
		}
	}

	// Check for a guy who does not have an iterrupt ( when applicable! )
	if ( !OK_INTERRUPT_MERC( pSoldier ) )
	{
		MercFlags	|=	NOINTERRUPT_MERC;
	}

	if ( pSoldier->stats.bLife < OKLIFE )
	{
		MercFlags	|=	UNCONSCIOUS_MERC;
	}

	if ( pSoldier->usSkillCooldown[SOLDIER_COOLDOWN_CRYO] )
		MercFlags |= UNCONSCIOUS_MERC;

	if ( pSoldier->stats.bLife == 0 )
	{
		MercFlags	|=	DEAD_MERC;
	}

	if ( pSoldier->bVisible != -1 || (gTacticalStatus.uiFlags&SHOW_ALL_MERCS) )
	{
		MercFlags	|=	VISIBLE_MERC;
	}

	return( MercFlags );
}

extern BOOLEAN CheckVideoObjectScreenCoordinateInData( HVOBJECT hSrcVObject, UINT16 usIndex, INT32 iTextX, INT32 iTestY );


// THIS FUNCTION IS CALLED FAIRLY REGULARLY
BOOLEAN FindSoldier( INT32 sGridNo, UINT16 *pusSoldierIndex, UINT32 *pMercFlags, UINT32 uiFlags )
{
	UINT32				cnt;
	SOLDIERTYPE		*pSoldier;
	SGPRect				aRect;
	BOOLEAN				fSoldierFound = FALSE;
	INT16					sXMapPos, sYMapPos, sScreenX, sScreenY;
	INT16					sMaxScreenMercY, sHeighestMercScreenY = -32000;
	BOOLEAN				fDoFull;
	UINT8					ubBestMerc = NOBODY;
	UINT16				usAnimSurface;
	INT32					iMercScreenX, iMercScreenY;
	BOOLEAN				fInScreenRect = FALSE;
	BOOLEAN				fInGridNo			= FALSE;


	*pusSoldierIndex = NOBODY;
	*pMercFlags			= 0;

	if ( _KeyDown( SHIFT ) )
	{
		uiFlags = FIND_SOLDIER_GRIDNO;
	}

	// Set some values
	if ( uiFlags & FIND_SOLDIER_BEGINSTACK )
	{
		gSoldierStack.bNum = 0;
		gSoldierStack.fUseGridNo = FALSE;
	}


	// Loop through all mercs and make go
	for ( cnt = 0; cnt < guiNumMercSlots; cnt++ )
	{
		pSoldier			= MercSlots[ cnt ];
		fInScreenRect = FALSE;
		fInGridNo			= FALSE;

		if ( pSoldier != NULL )
		{

			if ( pSoldier->bActive && !( pSoldier->flags.uiStatusFlags & SOLDIER_DEAD ) && ( pSoldier->bVisible != -1 || (gTacticalStatus.uiFlags&SHOW_ALL_MERCS) ) )
			{
				// OK, ignore if we are a passenger...
				if ( pSoldier->flags.uiStatusFlags & ( SOLDIER_PASSENGER | SOLDIER_DRIVER ) )
				{
					continue;
				}

				// If we want same level, skip if buggy's not on the same level!
				if ( uiFlags & FIND_SOLDIER_SAMELEVEL )
				{
					if ( pSoldier->pathing.bLevel != (UINT8)( uiFlags >> 16 ) )
					{
						continue;
					}
				}


				// If we are selective.... do our own guys FULL and other with gridno!
				// First look for owned soldiers, by way of the full method
				if ( uiFlags & FIND_SOLDIER_GRIDNO )
				{
					fDoFull = FALSE;
				}
				else if ( uiFlags & FIND_SOLDIER_SELECTIVE )
				{
					if ( pSoldier->ubID >= gTacticalStatus.Team[ gbPlayerNum ].bFirstID && pSoldier->ubID <= gTacticalStatus.Team[ gbPlayerNum ].bLastID )
					{
						fDoFull = TRUE;
					}
					else
					{
						fDoFull = FALSE;
					}
				}
				else
				{
					fDoFull = TRUE;
				}

				if ( fDoFull )
				{
					// Get Rect contained in the soldier
					GetSoldierScreenRect( pSoldier, &aRect );

					// Get XY From gridno
					ConvertGridNoToXY( sGridNo, &sXMapPos, &sYMapPos );

					// Get screen XY pos from map XY
					// Be carefull to convert to cell cords
					//CellXYToScreenXY( (INT16)((sXMapPos*CELL_X_SIZE)), (INT16)((sYMapPos*CELL_Y_SIZE)), &sScreenX, &sScreenY);

					// Set mouse stuff
					sScreenX = gusMouseXPos;
					sScreenY = gusMouseYPos;

					if ( IsPointInScreenRect( sScreenX, sScreenY, &aRect ) )
					{
						fInScreenRect = TRUE;
					}

					if ( pSoldier->sGridNo == sGridNo )
					{
						fInGridNo			= TRUE;
					}

					// ATE: If we are an enemy....
					if ( !gGameSettings.fOptions[ TOPTION_SMART_CURSOR ] )
					{
						if ( pSoldier->ubID >= gTacticalStatus.Team[ gbPlayerNum ].bFirstID && pSoldier->ubID <= gTacticalStatus.Team[ gbPlayerNum ].bLastID )
						{
							// ATE: NOT if we are in action or comfirm action mode
							if ( gCurrentUIMode != ACTION_MODE && gCurrentUIMode != CONFIRM_ACTION_MODE || gUIActionModeChangeDueToMouseOver )
							{
								fInScreenRect = FALSE;
							}
						}
					}

					// ATE: Refine this further....
					// Check if this is the selected guy....
					if ( pSoldier->ubID == gusSelectedSoldier )
					{
						// Are we in action mode...
						if ( gCurrentUIMode == ACTION_MODE || gCurrentUIMode == CONFIRM_ACTION_MODE )
						{
							// Are we in medic mode?
							if ( GetActionModeCursor( pSoldier ) != AIDCURS )
							{
								fInScreenRect = FALSE;
								fInGridNo			= FALSE;
							}
						}
					}

					// Make sure we are always on guy if we are on same gridno
					if ( fInScreenRect || fInGridNo )
					{
						// Check if we are a vehicle and refine if so....
						if ( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE )
						{
							usAnimSurface = GetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState );

							if ( usAnimSurface != INVALID_ANIMATION_SURFACE )
							{
								iMercScreenX = (INT32)( sScreenX - aRect.iLeft );
				iMercScreenY = (INT32)( -1 * ( sScreenY - aRect.iBottom ) );

								if ( !CheckVideoObjectScreenCoordinateInData( gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject, pSoldier->usAniFrame, iMercScreenX, iMercScreenY ) )
								{
									continue;
								}
							}
						}

						// If thgis is from a gridno, use mouse pos!
						if ( pSoldier->sGridNo == sGridNo )
						{

						}

						// Only break here if we're not creating a stack of these fellas
						if ( uiFlags & FIND_SOLDIER_BEGINSTACK )
						{
							gfHandleStack = TRUE;

								// Add this one!
							gSoldierStack.ubIDs[ gSoldierStack.bNum ] = pSoldier->ubID;
							gSoldierStack.bNum++;

							// Determine if it's the current
							if ( aRect.iBottom > sHeighestMercScreenY )
							{
									sMaxScreenMercY = (UINT16)aRect.iBottom;
									sHeighestMercScreenY = sMaxScreenMercY;

									gSoldierStack.bCur = gSoldierStack.bNum - 1;
							}
						}
						//Are we handling a stack right now?
						else if ( gfHandleStack )
						{
							// Are we the selected stack?
								if ( gSoldierStack.fUseGridNo )
								{
									fSoldierFound = FALSE;
									break;
								}
								else if ( gSoldierStack.ubIDs[ gSoldierStack.bCur ] == pSoldier->ubID )
								{
									// Set it!
									ubBestMerc = pSoldier->ubID;

									fSoldierFound = TRUE;
									break;
								}
						}
						else
						{
							// Determine if it's the best one
							if ( aRect.iBottom > sHeighestMercScreenY )
							{
									sMaxScreenMercY = (UINT16)aRect.iBottom;
									sHeighestMercScreenY = sMaxScreenMercY;

									// Set it!
									ubBestMerc = pSoldier->ubID;
							}

							fSoldierFound = TRUE;
							// Don't break here, find the rest!

						}
					}

				}
				else
				{
					//Otherwise, look for a bad guy by way of gridno]
					// Selective means don't give out enemy mercs if they are not visible

					///&& !NewOKDestination( pSoldier, sGridNo, TRUE, (INT8)gsInterfaceLevel )
					if ( pSoldier->sGridNo == sGridNo && !NewOKDestination( pSoldier, sGridNo, TRUE, (INT8)gsInterfaceLevel ) )
					{
						// Set it!
						ubBestMerc = pSoldier->ubID;

						fSoldierFound = TRUE;
						break;
					}
				}
			}
		}
	}

	if ( fSoldierFound && ubBestMerc != NOBODY )
	{
		*pusSoldierIndex = (UINT16)ubBestMerc;

		(*pMercFlags) = GetSoldierFindFlags( ubBestMerc );

 		return( TRUE );

	}
	else
	{
		// If we were handling a stack, and we have not found anybody, end
		if ( gfHandleStack && !( uiFlags & ( FIND_SOLDIER_BEGINSTACK | FIND_SOLDIER_SELECTIVE ) ) )
		{
			if ( gSoldierStack.fUseGridNo )
			{
				if ( gSoldierStack.sUseGridNoGridNo != sGridNo )
				{
					gfHandleStack = FALSE;
				}
			}
			else
			{
				gfHandleStack = FALSE;
			}
		}
	}
	return( FALSE );
}

BOOLEAN CycleSoldierFindStack( INT32 usMapPos )
{
	UINT16	usSoldierIndex;
	UINT32	uiMercFlags;

	// Have we initalized for this yet?
	if ( !gfHandleStack )
	{
		if ( FindSoldier( usMapPos, &usSoldierIndex, &uiMercFlags , FINDSOLDIERSAMELEVEL( gsInterfaceLevel ) | FIND_SOLDIER_BEGINSTACK ) )
		{
			gfHandleStack = TRUE;
		}

	}

	if ( gfHandleStack )
	{
		// we are cycling now?
		if ( !gSoldierStack.fUseGridNo )
		{
			gSoldierStack.bCur++;
		}

		gfUIForceReExamineCursorData = TRUE;

		if ( gSoldierStack.bCur == gSoldierStack.bNum )
		{
			if ( !gSoldierStack.fUseGridNo )
			{
				gSoldierStack.fUseGridNo = TRUE;
				gUIActionModeChangeDueToMouseOver = FALSE;
			  gSoldierStack.sUseGridNoGridNo = usMapPos;
			}
			else
			{
				gSoldierStack.bCur = 0;
				gSoldierStack.fUseGridNo = FALSE;
			}
		}


		if ( !gSoldierStack.fUseGridNo )
		{
			gusUIFullTargetID = gSoldierStack.ubIDs[ gSoldierStack.bCur ];
			guiUIFullTargetFlags = GetSoldierFindFlags( gusUIFullTargetID );
			guiUITargetSoldierId = gusUIFullTargetID;
			gfUIFullTargetFound			= TRUE;
		}
		else
		{
			gfUIFullTargetFound			= FALSE;
		}


	}

	// Return if we are in the cycle mode now...
	return( gfHandleStack );
}


SOLDIERTYPE * SimpleFindSoldier( INT32 sGridNo, INT8 bLevel )
{
	UINT8 ubID;

	ubID = WhoIsThere2( sGridNo, bLevel );
	if ( ubID == NOBODY )
	{
		return( NULL );
	}
	else
	{
		return( MercPtrs[ ubID ] );
	}
}

BOOLEAN IsValidTargetMerc( UINT8 ubSoldierID )
{
	SOLDIERTYPE *pSoldier = MercPtrs[ ubSoldierID ];


	// CHECK IF ACTIVE!
	if ( !pSoldier->bActive )
	{
		return( FALSE );
	}

	// CHECK IF DEAD
	if( pSoldier->stats.bLife == 0 )
	{
		//return( FALSE );
	}

	// IF BAD GUY - CHECK VISIVILITY
	if ( pSoldier->bTeam != gbPlayerNum )
	{
		if ( pSoldier->bVisible == -1 && !(gTacticalStatus.uiFlags&SHOW_ALL_MERCS) )
		{
			return( FALSE	);
		}
	}

	return( TRUE );
}


BOOLEAN IsGridNoInScreenRect( INT32 sGridNo, SGPRect *pRect )
{
	INT32 iXTrav, iYTrav;
	INT32	sMapPos;

	// Start with top left corner
	iXTrav = pRect->iLeft;
	iYTrav = pRect->iTop;

	do
	{
		do
		{
			GetScreenXYGridNo( (INT16)iXTrav, (INT16)iYTrav, &sMapPos );

			if ( sMapPos == sGridNo )
			{
				return( TRUE );
			}

			iXTrav += WORLD_TILE_X;

		} while( iXTrav < pRect->iRight );

		iYTrav += WORLD_TILE_Y;
		iXTrav = pRect->iLeft;

	} while( iYTrav < pRect->iBottom );

	return( FALSE );
}


void GetSoldierScreenRect( SOLDIERTYPE *pSoldier, SGPRect *pRect )
{
		INT16 sMercScreenX, sMercScreenY;
		UINT16	usAnimSurface;
//		ETRLEObject *pTrav;
//		UINT32 usHeight, usWidth;

		GetSoldierScreenPos( pSoldier, &sMercScreenX, &sMercScreenY );

		usAnimSurface = GetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState );
		if ( usAnimSurface == INVALID_ANIMATION_SURFACE )
		{
			pRect->iLeft		= sMercScreenX;
			pRect->iTop			= sMercScreenY;
			pRect->iBottom	= sMercScreenY + 5;
			pRect->iRight		= sMercScreenX + 5;

			return;
		}

		//pTrav = &(gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject->pETRLEObject[ pSoldier->usAniFrame ] );
		//usHeight				= (UINT32)pTrav->usHeight;
		//usWidth					= (UINT32)pTrav->usWidth;

		pRect->iLeft		= sMercScreenX;
		pRect->iTop			= sMercScreenY;
		pRect->iBottom	= sMercScreenY + pSoldier->sBoundingBoxHeight;
		pRect->iRight		= sMercScreenX + pSoldier->sBoundingBoxWidth;
}

void GetSoldierAnimDims( SOLDIERTYPE *pSoldier, INT16 *psHeight, INT16 *psWidth )
{
	UINT16											usAnimSurface;

	usAnimSurface = GetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState );

	if ( usAnimSurface == INVALID_ANIMATION_SURFACE )
	{
		*psHeight					= (INT16)5;
		*psWidth					= (INT16)5;

		return;
	}

	// OK, noodle here on what we should do... If we take each frame, it will be different slightly
	// depending on the frame and the value returned here will vary thusly. However, for the
	// uses of this function, we should be able to use just the first frame...

	*psHeight					= (INT16)pSoldier->sBoundingBoxHeight;
	*psWidth					= (INT16)pSoldier->sBoundingBoxWidth;
}

void GetSoldierAnimOffsets( SOLDIERTYPE *pSoldier, INT16 *sOffsetX, INT16 *sOffsetY )
{
	UINT16											usAnimSurface;

	usAnimSurface = GetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState );

	if ( usAnimSurface == INVALID_ANIMATION_SURFACE )
	{
		*sOffsetX					= (INT16)0;
		*sOffsetY					= (INT16)0;

		return;
	}

	*sOffsetX					= (INT16)pSoldier->sBoundingBoxOffsetX;
	*sOffsetY					= (INT16)pSoldier->sBoundingBoxOffsetY;
}

void GetSoldierScreenPos( SOLDIERTYPE *pSoldier, INT16 *psScreenX, INT16 *psScreenY )
{
		INT16 sMercScreenX, sMercScreenY;
		FLOAT dOffsetX, dOffsetY;
		FLOAT dTempX_S, dTempY_S;
		UINT16											usAnimSurface;
//		ETRLEObject *pTrav;

		usAnimSurface = GetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState );

		if ( usAnimSurface == INVALID_ANIMATION_SURFACE )
		{
			*psScreenX = 0;
			*psScreenY = 0;
			return;
		}

		// Get 'TRUE' merc position
		dOffsetX = pSoldier->dXPos - gsRenderCenterX;
		dOffsetY = pSoldier->dYPos - gsRenderCenterY;

		FloatFromCellToScreenCoordinates( dOffsetX, dOffsetY, &dTempX_S, &dTempY_S );

		//pTrav = &(gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject->pETRLEObject[ pSoldier->usAniFrame ] );

		sMercScreenX = ( ( gsVIEWPORT_END_X - gsVIEWPORT_START_X ) /2 ) + (INT16)dTempX_S;
		sMercScreenY = ( ( gsVIEWPORT_END_Y - gsVIEWPORT_START_Y ) /2 ) + (INT16)dTempY_S;

		// Adjust starting screen coordinates
		sMercScreenX	-= gsRenderWorldOffsetX;
		sMercScreenY	-= gsRenderWorldOffsetY;
		sMercScreenY	-= gpWorldLevelData[ pSoldier->sGridNo ].sHeight;

		// Adjust for render height
		sMercScreenY += gsRenderHeight;

		// Add to start position of dest buffer
		//sMercScreenX += pTrav->sOffsetX;
		//sMercScreenY += pTrav->sOffsetY;
		sMercScreenX += pSoldier->sBoundingBoxOffsetX;
		sMercScreenY += pSoldier->sBoundingBoxOffsetY;


		sMercScreenY -= pSoldier->sHeightAdjustment;

		*psScreenX = sMercScreenX;
		*psScreenY = sMercScreenY;
}

// THE TRUE SCREN RECT DOES NOT TAKE THE OFFSETS OF BUDDY INTO ACCOUNT!
void GetSoldierTRUEScreenPos( SOLDIERTYPE *pSoldier, INT16 *psScreenX, INT16 *psScreenY )
{
		INT16 sMercScreenX, sMercScreenY;
		FLOAT dOffsetX, dOffsetY;
		FLOAT dTempX_S, dTempY_S;
		UINT16											usAnimSurface;

		usAnimSurface = GetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState );

		if ( usAnimSurface == INVALID_ANIMATION_SURFACE )
		{
			*psScreenX = 0;
			*psScreenY = 0;
			return;
		}

		// Get 'TRUE' merc position
		dOffsetX = pSoldier->dXPos - gsRenderCenterX;
		dOffsetY = pSoldier->dYPos - gsRenderCenterY;

		FloatFromCellToScreenCoordinates( dOffsetX, dOffsetY, &dTempX_S, &dTempY_S );

		sMercScreenX = ( ( gsVIEWPORT_END_X - gsVIEWPORT_START_X ) /2 ) + (INT16)dTempX_S;
		sMercScreenY = ( ( gsVIEWPORT_END_Y - gsVIEWPORT_START_Y ) /2 ) + (INT16)dTempY_S;

		// Adjust starting screen coordinates
		sMercScreenX	-= gsRenderWorldOffsetX;
		sMercScreenY	-= gsRenderWorldOffsetY;

		// Adjust for render height
		sMercScreenY += gsRenderHeight;
		sMercScreenY	-= gpWorldLevelData[ pSoldier->sGridNo ].sHeight;

		sMercScreenY -= pSoldier->sHeightAdjustment;


		*psScreenX = sMercScreenX;
		*psScreenY = sMercScreenY;
}


BOOLEAN GridNoOnScreen( INT32 sGridNo )
{
	INT16 sNewCenterWorldX, sNewCenterWorldY;
	INT16 sWorldX;
	INT16 sWorldY;
	INT16 sAllowance = 20;

	if ( gsVIEWPORT_WINDOW_START_Y == 20 )
	{
	sAllowance = 40;
	}

	ConvertGridNoToXY( sGridNo, &sNewCenterWorldX, &sNewCenterWorldY );

	// Get screen coordinates for current position of soldier
	GetWorldXYAbsoluteScreenXY( (INT16)(sNewCenterWorldX), (INT16)(sNewCenterWorldY), &sWorldX, &sWorldY);

	// ATE: OK, here, adjust the top value so that it's a tile and a bit over, because of our mercs!
	if ( sWorldX >= gsTopLeftWorldX && sWorldX <= gsBottomRightWorldX &&
			sWorldY >= ( gsTopLeftWorldY + sAllowance )	&& sWorldY <= ( gsBottomRightWorldY + 20 ) )
	{
		return( TRUE );
	}
	return( FALSE );
}


BOOLEAN SoldierOnScreen( UINT16 usID )
{
	SOLDIERTYPE *pSoldier;

	// Get pointer of soldier
	pSoldier = MercPtrs[ usID ];

	return( GridNoOnScreen( pSoldier->sGridNo ) );
}


BOOLEAN SoldierOnVisibleWorldTile( SOLDIERTYPE *pSoldier )
{
	return( GridNoOnVisibleWorldTile( pSoldier->sGridNo ) );
}



BOOLEAN SoldierLocationRelativeToScreen( INT32 sGridNo, UINT16 usReasonID, INT8 *pbDirection, UINT32 *puiScrollFlags )
{
	INT16 sWorldX;
	INT16 sWorldY;
	INT16 sY, sX;
	static BOOLEAN fCountdown = 0;
	INT16 sScreenCenterX, sScreenCenterY;
	INT16 sDistToCenterY, sDistToCenterX;

	*puiScrollFlags = 0;

	sX = CenterX( sGridNo );
	sY = CenterY( sGridNo );

	// Get screen coordinates for current position of soldier
	GetWorldXYAbsoluteScreenXY( (INT16)(sX/CELL_X_SIZE), (INT16)(sY/CELL_Y_SIZE), &sWorldX, &sWorldY);

	// Find the diustance from render center to true world center
	sDistToCenterX = gsRenderCenterX - gCenterWorldX;
	sDistToCenterY = gsRenderCenterY - gCenterWorldY;

	// From render center in world coords, convert to render center in "screen" coords
	FromCellToScreenCoordinates( sDistToCenterX , sDistToCenterY, &sScreenCenterX, &sScreenCenterY );

	// Subtract screen center
	sScreenCenterX += gsCX;
	sScreenCenterY += gsCY;

	// Adjust for offset origin!
	sScreenCenterX += 0;
	sScreenCenterY += 10;

	// Get direction
	//*pbDirection = atan8( sScreenCenterX, sScreenCenterY, sWorldX, sWorldY );
	*pbDirection = atan8( gsRenderCenterX, gsRenderCenterY, (INT16)(sX), (INT16)(sY) );

	// Check values!
	if ( sWorldX > ( sScreenCenterX + 20 ) )
	{
		(*puiScrollFlags) |= SCROLL_RIGHT;
	}
	if ( sWorldX < ( sScreenCenterX - 20 ) )
	{
		(*puiScrollFlags) |= SCROLL_LEFT;
	}
	if ( sWorldY > ( sScreenCenterY + 20 ) )
	{
		(*puiScrollFlags) |= SCROLL_DOWN;
	}
	if ( sWorldY < ( sScreenCenterY - 20 ) )
	{
		(*puiScrollFlags) |= SCROLL_UP;
	}


	// If we are on screen, stop
	if ( sWorldX >= gsTopLeftWorldX && sWorldX <= gsBottomRightWorldX &&
			sWorldY >= gsTopLeftWorldY	&& sWorldY <= ( gsBottomRightWorldY + 20 ) )
	{
		// CHECK IF WE ARE DONE...
		if ( fCountdown > gScrollSlideInertiaDirection[ *pbDirection ] )
		{
			fCountdown = 0;
			return( FALSE );
		}
		else
		{
			fCountdown++;
		}
	}

	return( TRUE );
}

BOOLEAN IsPointInSoldierBoundingBox( SOLDIERTYPE *pSoldier, INT16 sX, INT16 sY )
{
	SGPRect				aRect;

	// Get Rect contained in the soldier
	GetSoldierScreenRect( pSoldier, &aRect );

	if ( IsPointInScreenRect( sX, sY, &aRect ) )
	{
		return( TRUE );
	}

	return( FALSE );
}


BOOLEAN FindRelativeSoldierPosition( SOLDIERTYPE *pSoldier, UINT16 *usFlags, INT16 sX, INT16 sY )
{
	SGPRect				aRect;
	INT16					sRelX, sRelY;
	FLOAT					dRelPer;

	// Get Rect contained in the soldier
	GetSoldierScreenRect( pSoldier, &aRect );

	if ( IsPointInScreenRectWithRelative( sX, sY, &aRect, &sRelX, &sRelY ) )
	{
		dRelPer = (FLOAT)sRelY / ( aRect.iBottom - aRect.iTop );

		// Determine relative positions
		switch( gAnimControl[ pSoldier->usAnimState ].ubHeight )
		{
			case ANIM_STAND:

				if ( dRelPer < .2 )
				{
					(*usFlags )= TILE_FLAG_HEAD;
					return( TRUE );
				}
				else if ( dRelPer < .6 )
				{
					(*usFlags )= TILE_FLAG_MID;
					return( TRUE );
				}
				else
				{
					(*usFlags )= TILE_FLAG_FEET;
					return( TRUE );
				}
				break;

			case ANIM_CROUCH:

				if ( dRelPer < .2 )
				{
					(*usFlags )= TILE_FLAG_HEAD;
					return( TRUE );
				}
				else if ( dRelPer < .7 )
				{
					(*usFlags )= TILE_FLAG_MID;
					return( TRUE );
				}
				else
				{
					(*usFlags )= TILE_FLAG_FEET;
					return( TRUE );
				}
				break;

			case ANIM_PRONE:
				{
					// Flugente: if we are prone, determining what part of the body we are aiming for is trickier. 
					// Instead of focusing on relative height, we simply determine whether we are looking at the gridno the body itself is at, or an adjacent gridno.
					// Then, depending on whether this is the gridno we are looking at, this will be head or legs
					if ( gGameExternalOptions.fAllowTargetHeadAndLegIfProne )
					{
						INT16 sWorldX, sWorldY;
						GetMouseWorldCoords( &sWorldX, &sWorldY );

						// Flugente: we measure the distance of the bullet's location to the location of the soldier, and to the 2 gridnos his head and leg occupy
						// From this we can decide what body part was hit
						FLOAT bodycenterX = (FLOAT)CenterX( pSoldier->sGridNo );
						FLOAT bodycenterY = (FLOAT)CenterY( pSoldier->sGridNo );

						FLOAT difftobodycenter = sqrt( (bodycenterX - sWorldX) * (bodycenterX - sWorldX) + (bodycenterY - sWorldY) * (bodycenterY - sWorldY) );
						
						INT32 viewdirectiongridno = NewGridNo( pSoldier->sGridNo, DirectionInc( pSoldier->ubDirection ) );
						FLOAT nextgridnocenterX = (FLOAT)CenterX( viewdirectiongridno );
						FLOAT nextgridnocenterY = (FLOAT)CenterY( viewdirectiongridno );

						FLOAT difftonextgridno = sqrt( (nextgridnocenterX - sWorldX) * (nextgridnocenterX - sWorldX) + (nextgridnocenterY - sWorldY) * (nextgridnocenterY - sWorldY) );
						
						INT32 oppositeviewdirectiongridno = NewGridNo( pSoldier->sGridNo, DirectionInc( gOppositeDirection[pSoldier->ubDirection] ) );
						FLOAT oppositenextgridnocenterX = (FLOAT)CenterX( oppositeviewdirectiongridno );
						FLOAT oppositenextgridnocenterY = (FLOAT)CenterY( oppositeviewdirectiongridno );

						FLOAT difftooppositenextgridno = sqrt( (oppositenextgridnocenterX - sWorldX) * (oppositenextgridnocenterX - sWorldX) + (oppositenextgridnocenterY - sWorldY) * (oppositenextgridnocenterY - sWorldY) );

						if ( difftobodycenter < difftonextgridno )
						{
							if ( difftobodycenter < difftooppositenextgridno )
							{
								(*usFlags) = TILE_FLAG_MID;
								return(TRUE);
							}
							else
							{
								(*usFlags) = TILE_FLAG_FEET;
								return(TRUE);
							}
						}
						else
						{
							(*usFlags) = TILE_FLAG_HEAD;
							return(TRUE);
						}
					}
				}
				break;
		}
	}

	return( FALSE );
}

// VERY quickly finds a soldier at gridno , ( that is visible )
UINT8 QuickFindSoldier( INT32 sGridNo )
{
	UINT32 cnt;
	SOLDIERTYPE *pSoldier = NULL;

	// Loop through all mercs and make go
	for ( cnt = 0; cnt < guiNumMercSlots; cnt++ )
	{
		pSoldier = MercSlots[ cnt ];

		if ( pSoldier != NULL )
		{
			if ( pSoldier->sGridNo == sGridNo && pSoldier->bVisible != -1 )
			{
				return( (UINT8)cnt );
			}
		}

	}

	return( NOBODY );
}


void GetGridNoScreenPos( INT32 sGridNo, UINT8 ubLevel, INT16 *psScreenX, INT16 *psScreenY )
{
		INT16 sScreenX, sScreenY;
		FLOAT dOffsetX, dOffsetY;
		FLOAT dTempX_S, dTempY_S;

		// Get 'TRUE' merc position
		dOffsetX = (FLOAT)( CenterX( sGridNo ) - gsRenderCenterX );
		dOffsetY = (FLOAT)( CenterY( sGridNo ) - gsRenderCenterY );

		// OK, DONT'T ASK... CONVERSION TO PROPER Y NEEDS THIS...
		dOffsetX -= CELL_Y_SIZE;

		FloatFromCellToScreenCoordinates( dOffsetX, dOffsetY, &dTempX_S, &dTempY_S );

		sScreenX = ( ( gsVIEWPORT_END_X - gsVIEWPORT_START_X ) /2 ) + (INT16)dTempX_S;
		sScreenY = ( ( gsVIEWPORT_END_Y - gsVIEWPORT_START_Y ) /2 ) + (INT16)dTempY_S;

		// Adjust starting screen coordinates
		sScreenX	-= gsRenderWorldOffsetX;
		sScreenY	-= gsRenderWorldOffsetY;

		sScreenY += gsRenderHeight;

		// Adjust for world height
		sScreenY -= gpWorldLevelData[ sGridNo ].sHeight;

		// Adjust for level height
		if ( ubLevel )
		{
			sScreenY -= ROOF_LEVEL_HEIGHT;
		}

		*psScreenX = sScreenX;
		*psScreenY = sScreenY;
}


