Files
source/Tactical/Soldier Find.cpp
T

1073 lines
27 KiB
C++

#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"
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;
UINT16 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;
UINT16 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 )
{
UINT16 ubID;
ubID = WhoIsThere2( sGridNo, bLevel );
if ( ubID == NOBODY )
{
return( NULL );
}
else
{
return( MercPtrs[ ubID ] );
}
}
BOOLEAN IsValidTargetMerc( UINT16 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;
ConvertGridNoToCenterCellXY(sGridNo, &sX, &sY);
// 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, sX, sY;
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
ConvertGridNoToCenterCellXY(pSoldier->sGridNo, &sX, &sY);
FLOAT bodycenterX = (FLOAT) sX;
FLOAT bodycenterY = (FLOAT) sY;
FLOAT difftobodycenter = sqrt( (bodycenterX - sWorldX) * (bodycenterX - sWorldX) + (bodycenterY - sWorldY) * (bodycenterY - sWorldY) );
INT32 viewdirectiongridno = NewGridNo( pSoldier->sGridNo, DirectionInc( pSoldier->ubDirection ) );
ConvertGridNoToCenterCellXY(viewdirectiongridno, &sX, &sY);
FLOAT nextgridnocenterX = (FLOAT) sX;
FLOAT nextgridnocenterY = (FLOAT) sY;
FLOAT difftonextgridno = sqrt( (nextgridnocenterX - sWorldX) * (nextgridnocenterX - sWorldX) + (nextgridnocenterY - sWorldY) * (nextgridnocenterY - sWorldY) );
INT32 oppositeviewdirectiongridno = NewGridNo( pSoldier->sGridNo, DirectionInc( gOppositeDirection[pSoldier->ubDirection] ) );
ConvertGridNoToCenterCellXY(oppositeviewdirectiongridno, &sX, &sY);
FLOAT oppositenextgridnocenterX = (FLOAT) sX;
FLOAT oppositenextgridnocenterY = (FLOAT) sY;
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 )
UINT16 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( (UINT16)cnt );
}
}
}
return( NOBODY );
}
void GetGridNoScreenPos( INT32 sGridNo, UINT8 ubLevel, INT16 *psScreenX, INT16 *psScreenY )
{
INT16 sScreenX, sScreenY, sX, sY;
FLOAT dOffsetX, dOffsetY;
FLOAT dTempX_S, dTempY_S;
ConvertGridNoToCenterCellXY(sGridNo, &sX, &sY);
// Get 'TRUE' merc position
dOffsetX = (FLOAT)( sX - gsRenderCenterX );
dOffsetY = (FLOAT)( sY - 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;
}