Files
source/TileEngine/Isometric Utils.cpp
T

1442 lines
33 KiB
C++

#ifdef PRECOMPILEDHEADERS
#include "TileEngine All.h"
#else
#include "debug.h"
#include "renderworld.h"
#include "interface.h"
#include "worldman.h"
#include "Structure Wrap.h"
#include "sys globals.h"
#include "overhead.h"
#include "Random.h"
#include "Pathai.h"
#endif
#include "Map Information.h"
#include "meanwhile.h"
#include "strategicmap.h"
UINT32 guiForceRefreshMousePositionCalculation = 0;
// GLOBALS
INT16 DirIncrementer[8] =
{
-WORLD_COLS, //N
1-WORLD_COLS, //NE
1, //E
1+WORLD_COLS, //SE
WORLD_COLS, //S
WORLD_COLS-1, //SW
-1, //W
-WORLD_COLS-1 //NW
};
// Opposite directions
UINT8 gOppositeDirection[ NUM_WORLD_DIRECTIONS ] =
{
SOUTH,
SOUTHWEST,
WEST,
NORTHWEST,
NORTH,
NORTHEAST,
EAST,
SOUTHEAST
};
UINT8 gTwoCCDirection[ NUM_WORLD_DIRECTIONS ] =
{
WEST,
NORTHWEST,
NORTH,
NORTHEAST,
EAST,
SOUTHEAST,
SOUTH,
SOUTHWEST
};
UINT8 gTwoCDirection[ NUM_WORLD_DIRECTIONS ] =
{
EAST,
SOUTHEAST,
SOUTH,
SOUTHWEST,
WEST,
NORTHWEST,
NORTH,
NORTHEAST
};
UINT8 gOneCDirection[ NUM_WORLD_DIRECTIONS ] =
{
NORTHEAST,
EAST,
SOUTHEAST,
SOUTH,
SOUTHWEST,
WEST,
NORTHWEST,
NORTH
};
UINT8 gOneCCDirection[ NUM_WORLD_DIRECTIONS ] =
{
NORTHWEST,
NORTH,
NORTHEAST,
EAST,
SOUTHEAST,
SOUTH,
SOUTHWEST,
WEST
};
// DIRECTION FACING DIRECTION WE WANT TO GOTO
UINT8 gPurpendicularDirection[ NUM_WORLD_DIRECTIONS ][ NUM_WORLD_DIRECTIONS ] =
{
// NORTH
WEST, // EITHER
NORTHWEST,
NORTH,
NORTHEAST,
EAST, // EITHER
NORTHWEST,
NORTH,
NORTHEAST,
// NORTH EAST
NORTHWEST,
NORTHWEST, // EITHER
SOUTH,
NORTHEAST,
EAST,
SOUTHEAST, // EITHER
NORTH,
NORTHEAST,
// EAST
EAST,
SOUTHEAST,
NORTH, // EITHER
NORTHEAST,
EAST,
SOUTHEAST,
NORTH, // EITHER
NORTHEAST,
// SOUTHEAST
EAST,
SOUTHEAST,
SOUTH,
SOUTHWEST, // EITHER
SOUTHWEST,
SOUTHEAST,
SOUTH,
SOUTHWEST, // EITHER
// SOUTH
WEST, // EITHER
SOUTHEAST,
SOUTH,
SOUTHWEST,
EAST, // EITHER
SOUTHEAST,
SOUTH,
SOUTHWEST,
// SOUTHWEST
WEST,
NORTHWEST, // EITHER
SOUTH,
SOUTHWEST,
WEST,
SOUTHEAST, // EITHER
SOUTH,
SOUTHWEST,
// WEST
WEST,
NORTHWEST,
NORTH, // EITHER
SOUTHWEST,
WEST,
NORTHWEST,
SOUTH, // EITHER
SOUTHWEST,
// NORTHWEST
WEST,
NORTHWEST,
NORTH,
SOUTHWEST, // EITHER
SOUTHWEST,
NORTHWEST,
NORTH,
NORTHEAST, // EITHER
};
//----------------------legion by Jazz-----------------
BOOLEAN FindWindowJumpDirection( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bStartingDir, INT8 *pbDirection )
{
INT32 cnt;
INT32 sNewGridNo, sOtherSideOfFence;
BOOLEAN fFound = FALSE;
UINT8 bMinNumTurns = 100;
INT8 bNumTurns;
INT8 bMinDirection = 0;
INT8 direction2 = -1;
if ( pSoldier->ubDirection == NORTH )
direction2 = NORTH;
else if ( pSoldier->ubDirection == EAST )
direction2 = EAST;
else if ( pSoldier->ubDirection == SOUTH )
direction2 = SOUTH;
else if ( pSoldier->ubDirection == WEST )
direction2 = WEST;
// WANNE: No need to check on SOUTH and EAST tile, because it is the tile that has the fence we are standing on!
if (direction2 == NORTH || direction2 == WEST)
{
// IF there is a fence in this gridno, return false!
if ( IsJumpableWindowPresentAtGridNo( sGridNo, direction2, gGameExternalOptions.fCanJumpThroughClosedWindows ) )
{
return( FALSE );
}
}
// LOOP THROUGH ALL 8 DIRECTIONS
for ( cnt = 0; cnt < NUM_WORLD_DIRECTIONS; cnt += 2 )
{
if (cnt != direction2)
continue;
// get the fence tile
if (cnt == NORTH || cnt == WEST)
{
sNewGridNo = NewGridNo( sGridNo, (UINT16)DirectionInc( (UINT8)cnt ) );
sOtherSideOfFence = sNewGridNo;
}
// current tile we are standing is the fence tile
else
{
sNewGridNo = sGridNo;
sOtherSideOfFence = NewGridNo( sNewGridNo, (UINT16)DirectionInc( (UINT8)cnt ) );
}
//if ( NewOKDestination( pSoldier, sOtherSideOfFence, TRUE, 0 ) && validDestTile)
if ( NewOKDestination( pSoldier, sOtherSideOfFence, TRUE, 0 ) /*&& validDestTile*/)
{
// ATE: Check if there is somebody waiting here.....
// Check if we have a fence here
if ( IsJumpableWindowPresentAtGridNo( sNewGridNo , direction2, gGameExternalOptions.fCanJumpThroughClosedWindows) )
{
fFound = TRUE;
// FInd how many turns we should go to get here
bNumTurns = FindNumTurnsBetweenDirs( (INT8)cnt, bStartingDir );
if ( bNumTurns < bMinNumTurns )
{
bMinNumTurns = bNumTurns;
bMinDirection = (INT8)cnt;
}
break;
}
}
}
if ( fFound )
{
*pbDirection = bMinDirection;
return( TRUE );
}
return( FALSE );
}
BOOLEAN FindWallJumpDirection( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bStartingDir, INT8 *pbDirection )
{
INT32 cnt;
INT32 sNewGridNo, sOtherSideOfFence;
BOOLEAN fFound = FALSE;
UINT8 bMinNumTurns = 100;
INT8 bNumTurns;
INT8 bMinDirection = 0;
// IF there is a fence in this gridno, return false!
if ( IsLegionWallPresentAtGridno( sGridNo ) )
{
return( FALSE );
}
// LOOP THROUGH ALL 8 DIRECTIONS
for ( cnt = 0; cnt < NUM_WORLD_DIRECTIONS; cnt += 2 )
{
// go out *2* tiles
sNewGridNo = NewGridNo( sGridNo, (UINT16)DirectionInc( (UINT8)cnt ) );
sOtherSideOfFence = NewGridNo( sNewGridNo, (UINT16)DirectionInc( (UINT8)cnt ) );
if ( NewOKDestination( pSoldier, sOtherSideOfFence, TRUE, 0 ) )
{
// ATE: Check if there is somebody waiting here.....
// Check if we have a fence here
if ( IsLegionWallPresentAtGridno( sNewGridNo ) )
{
fFound = TRUE;
// FInd how many turns we should go to get here
bNumTurns = FindNumTurnsBetweenDirs( (INT8)cnt, bStartingDir );
if ( bNumTurns < bMinNumTurns )
{
bMinNumTurns = bNumTurns;
bMinDirection = (INT8)cnt;
}
}
}
}
if ( fFound )
{
*pbDirection = bMinDirection;
return( TRUE );
}
return( FALSE );
}
BOOLEAN FindHeigherLevelOkno( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bStartingDir, INT8 *pbDirection )
{
INT32 cnt;
INT32 sNewGridNo;
BOOLEAN fFound = FALSE;
UINT8 bMinNumTurns = 100;
INT8 bNumTurns;
INT8 bMinDirection = 0;
// LOOP THROUGH ALL 8 DIRECTIONS
for ( cnt = 0; cnt < NUM_WORLD_DIRECTIONS; cnt += 2 )
{
sNewGridNo = NewGridNo( sGridNo, (UINT16)DirectionInc( (UINT8)cnt ) );
// Make sure there is NOT a roof here...
// Check OK destination
if ( NewOKDestination( pSoldier, sNewGridNo, TRUE, 0 ) )
{
if ( FindStructure( sNewGridNo, STRUCTURE_WALLNWINDOW ) == NULL )
{
{
fFound = TRUE;
// FInd how many turns we should go to get here
bNumTurns = FindNumTurnsBetweenDirs( (INT8)cnt, bStartingDir );
if ( bNumTurns < bMinNumTurns )
{
bMinNumTurns = bNumTurns;
bMinDirection = (INT8)cnt;
}
}
}
}
}
if ( fFound )
{
*pbDirection = bMinDirection;
return( TRUE );
}
return( FALSE );
/* INT32 cnt;
INT32 sNewGridNo;
BOOLEAN fFound = FALSE;
UINT8 bMinNumTurns = 100;
INT8 bNumTurns;
INT8 bMinDirection = 0;
//STRUCTURE * pStructure;
// IF there is a roof over our heads, this is an ivalid....
// return ( FALSE );l
if ( FindStructure( sGridNo, STRUCTURE_WALLNWINDOW ) != NULL )
{
return( FALSE );
}
for ( cnt = 0; cnt < NUM_WORLD_DIRECTIONS; cnt+= 2 )
{
sNewGridNo = NewGridNo( sGridNo, (UINT16)DirectionInc( (UINT8)cnt ) );
if ( NewOKDestination( pSoldier, sNewGridNo, TRUE, 0 ) )
{
// Check if this tile has a higher level
if ( IsOknoLevel( sNewGridNo ) )
{
fFound = TRUE;
// FInd how many turns we should go to get here
bNumTurns = FindNumTurnsBetweenDirs( (INT8)cnt, bStartingDir );
if ( bNumTurns < bMinNumTurns )
{
bMinNumTurns = bNumTurns;
bMinDirection = (INT8)cnt;
}
}
}
}
if ( fFound )
{
*pbDirection = bMinDirection;
return( TRUE );
}
return( FALSE );
*/
}
BOOLEAN FindHeigherLevelFence( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bStartingDir, INT8 *pbDirection )
{
INT32 cnt;
INT32 sNewGridNo;
BOOLEAN fFound = FALSE;
UINT8 bMinNumTurns = 100;
INT8 bNumTurns;
INT8 bMinDirection = 0;
//STRUCTURE * pStructure;
// IF there is a roof over our heads, this is an ivalid....
// return ( FALSE );l
if ( FindStructure( sGridNo, STRUCTURE_FENCE ) != NULL )
{
return( FALSE );
}
for ( cnt = 0; cnt < NUM_WORLD_DIRECTIONS; cnt += 2 )
{
sNewGridNo = NewGridNo( sGridNo, (UINT16)DirectionInc( (UINT8)cnt ) );
if ( NewOKDestination( pSoldier, sNewGridNo, TRUE, 0 ) )
{
// Check if this tile has a higher level
if ( IsLegionLevel( sNewGridNo ) )
{
fFound = TRUE;
// FInd how many turns we should go to get here
bNumTurns = FindNumTurnsBetweenDirs( (INT8)cnt, bStartingDir );
if ( bNumTurns < bMinNumTurns )
{
bMinNumTurns = bNumTurns;
bMinDirection = (INT8)cnt;
}
}
}
}
if ( fFound )
{
*pbDirection = bMinDirection;
return( TRUE );
}
return( FALSE );
}
BOOLEAN FindLowerLevelWall( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bStartingDir, INT8 *pbDirection )
{
INT32 cnt;
INT32 sNewGridNo;
BOOLEAN fFound = FALSE;
UINT8 bMinNumTurns = 100;
INT8 bNumTurns;
INT8 bMinDirection = 0;
// LOOP THROUGH ALL 8 DIRECTIONS
for ( cnt = 0; cnt < NUM_WORLD_DIRECTIONS; cnt += 2 )
{
sNewGridNo = NewGridNo( sGridNo, (UINT16)DirectionInc( (UINT8)cnt ) );
// Make sure there is NOT a roof here...
// Check OK destination
if ( NewOKDestination( pSoldier, sNewGridNo, TRUE, 0 ) )
{
if ( FindStructure( sNewGridNo, STRUCTURE_FENCE ) == NULL )
{
{
fFound = TRUE;
// FInd how many turns we should go to get here
bNumTurns = FindNumTurnsBetweenDirs( (INT8)cnt, bStartingDir );
if ( bNumTurns < bMinNumTurns )
{
bMinNumTurns = bNumTurns;
bMinDirection = (INT8)cnt;
}
}
}
}
}
if ( fFound )
{
*pbDirection = bMinDirection;
return( TRUE );
}
return( FALSE );
}
//---------------------------------------------------------------------------------
GridNode::MapXY_t GridNode::MapXY;
static GridNode::MapXY_t *pMapXY = GridNode::initGridNodes(); // A hack to initialize the MapXY grid
void FromCellToScreenCoordinates( INT16 sCellX, INT16 sCellY, INT16 *psScreenX, INT16 *psScreenY )
{
*psScreenX = ( 2 * sCellX ) - ( 2 * sCellY );
*psScreenY = sCellX + sCellY;
}
void FromScreenToCellCoordinates( INT16 sScreenX, INT16 sScreenY, INT16 *psCellX, INT16 *psCellY )
{
*psCellX = ( ( sScreenX + ( 2 * sScreenY ) + 2 ) / 4 );
*psCellY = ( ( 2 * sScreenY ) - sScreenX + 2 ) / 4;
}
// These two functions take into account that our world is projected and attached
// to the screen (0,0) in a specific way, and we MUSt take that into account then
// determining screen coords
void FloatFromCellToScreenCoordinates( FLOAT dCellX, FLOAT dCellY, FLOAT *pdScreenX, FLOAT *pdScreenY )
{
FLOAT dScreenX, dScreenY;
dScreenX = ( 2 * dCellX ) - ( 2 * dCellY );
dScreenY = dCellX + dCellY;
*pdScreenX = dScreenX;
*pdScreenY = dScreenY;
}
void FloatFromScreenToCellCoordinates( FLOAT dScreenX, FLOAT dScreenY, FLOAT *pdCellX, FLOAT *pdCellY )
{
FLOAT dCellX, dCellY;
dCellX = ( ( dScreenX + ( 2 * dScreenY ) ) / 4 );
dCellY = ( ( 2 * dScreenY ) - dScreenX ) / 4;
*pdCellX = dCellX;
*pdCellY = dCellY;
}
BOOLEAN GetMouseXY( INT16 *psMouseX, INT16 *psMouseY )
{
INT16 sWorldX, sWorldY;
if ( !GetMouseWorldCoords( &sWorldX, &sWorldY ) )
{
(*psMouseX) = 0;
(*psMouseY) = 0;
return( FALSE );
}
// Find start block
(*psMouseX) = ( sWorldX / CELL_X_SIZE );
(*psMouseY) = ( sWorldY / CELL_Y_SIZE );
return( TRUE );
}
BOOLEAN GetMouseXYWithRemainder( INT16 *psMouseX, INT16 *psMouseY, INT16 *psCellX, INT16 *psCellY )
{
INT16 sWorldX, sWorldY;
if ( !GetMouseWorldCoords( &sWorldX, &sWorldY ) )
{
return( FALSE );
}
// Find start block
(*psMouseX) = ( sWorldX / CELL_X_SIZE );
(*psMouseY) = ( sWorldY / CELL_Y_SIZE );
(*psCellX ) = sWorldX - ( (*psMouseX) * CELL_X_SIZE );
(*psCellY ) = sWorldY - ( (*psMouseY) * CELL_Y_SIZE );
return( TRUE );
}
BOOLEAN GetMouseWorldCoords( INT16 *psMouseX, INT16 *psMouseY )
{
INT16 sOffsetX, sOffsetY;
INT16 sTempPosX_W, sTempPosY_W;
INT16 sStartPointX_W, sStartPointY_W;
// Convert mouse screen coords into offset from center
if ( ! ( gViewportRegion.uiFlags & MSYS_MOUSE_IN_AREA ) )
{
*psMouseX = 0;
*psMouseY = 0;
return( FALSE );
}
sOffsetX = gViewportRegion.MouseXPos - ( ( gsVIEWPORT_END_X - gsVIEWPORT_START_X ) /2 ); // + gsRenderWorldOffsetX;
sOffsetY = gViewportRegion.MouseYPos - ( ( gsVIEWPORT_END_Y - gsVIEWPORT_START_Y ) /2 ) + 10;// + gsRenderWorldOffsetY;
// OK, Let's offset by a value if our interfac level is changed!
if ( gsInterfaceLevel != 0 )
{
//sOffsetY -= 50;
}
FromScreenToCellCoordinates( sOffsetX, sOffsetY, &sTempPosX_W, &sTempPosY_W );
// World start point is Render center plus this distance
sStartPointX_W = gsRenderCenterX + sTempPosX_W;
sStartPointY_W = gsRenderCenterY + sTempPosY_W;
// check if we are out of bounds..
if ( sStartPointX_W < 0 || sStartPointX_W >= WORLD_COORD_ROWS || sStartPointY_W < 0 || sStartPointY_W >= WORLD_COORD_COLS )
{
*psMouseX = 0;
*psMouseY = 0;
return( FALSE );
}
// Determine Start block and render offsets
// Find start block
// Add adjustment for render origin as well
(*psMouseX) = sStartPointX_W;
(*psMouseY) = sStartPointY_W;
return( TRUE );
}
BOOLEAN GetMouseWorldCoordsInCenter( INT16 *psMouseX, INT16 *psMouseY )
{
INT16 sMouseX, sMouseY;
// Get grid position
if ( !GetMouseXY( &sMouseX, &sMouseY ) )
{
return( FALSE );
}
// Now adjust these cell coords into world coords
*psMouseX = ( ( sMouseX ) * CELL_X_SIZE ) + ( CELL_X_SIZE / 2 );
*psMouseY = ( ( sMouseY ) * CELL_Y_SIZE ) + ( CELL_Y_SIZE / 2 );
return( TRUE );
}
#if 0
// 0verhaul
// I did that (or actually uncasted a bunch of stuff and re-typed others to correct them), so
// no worries
// (jonathanl) to save me having to cast all the previous code
BOOLEAN GetMouseMapPos( UINT32 *psMapPos )
{
return GetMouseMapPos( (INT32 *)psMapPos );
}
#endif
BOOLEAN GetMouseMapPos( INT32 *psMapPos )
{
INT16 sWorldX, sWorldY;
static INT32 sSameCursorPos;
static UINT32 uiOldFrameNumber = 99999;
// Check if this is the same frame as before, return already calculated value if so!
if ( uiOldFrameNumber == guiGameCycleCounter && !guiForceRefreshMousePositionCalculation )
{
( *psMapPos ) = sSameCursorPos;
if ( sSameCursorPos == 0 )
{
return( FALSE );
}
return( TRUE );
}
uiOldFrameNumber = guiGameCycleCounter;
guiForceRefreshMousePositionCalculation = FALSE;
if ( GetMouseXY( &sWorldX, &sWorldY ) )
{
*psMapPos = MAPROWCOLTOPOS( sWorldY, sWorldX );
sSameCursorPos = (*psMapPos);
return( TRUE );
}
else
{
*psMapPos = 0;
sSameCursorPos = (*psMapPos);
return( FALSE );
}
}
BOOLEAN ConvertMapPosToWorldTileCenter( INT32 usMapPos, INT16 *psXPos, INT16 *psYPos )
{
INT16 sWorldX, sWorldY;
INT16 sCellX, sCellY;
// Get X, Y world GRID Coordinates
sWorldY = ( usMapPos / WORLD_COLS );
sWorldX = usMapPos - ( sWorldY * WORLD_COLS );
// Convert into cell coords
sCellY = sWorldY * CELL_Y_SIZE;
sCellX = sWorldX * CELL_X_SIZE;
// Add center tile positions
*psXPos = sCellX + ( CELL_X_SIZE / 2 );
*psYPos = sCellY + ( CELL_Y_SIZE / 2 );
return( TRUE );
}
void GetScreenXYWorldCoords( INT16 sScreenX, INT16 sScreenY, INT16 *psWorldX, INT16 *psWorldY )
{
INT16 sOffsetX, sOffsetY;
INT16 sTempPosX_W, sTempPosY_W;
INT16 sStartPointX_W, sStartPointY_W;
// Convert mouse screen coords into offset from center
sOffsetX = sScreenX - ( gsVIEWPORT_END_X - gsVIEWPORT_START_X ) /2;
sOffsetY = sScreenY - ( gsVIEWPORT_END_Y - gsVIEWPORT_START_Y ) /2;
FromScreenToCellCoordinates( sOffsetX, sOffsetY, &sTempPosX_W, &sTempPosY_W );
// World start point is Render center plus this distance
sStartPointX_W = gsRenderCenterX + sTempPosX_W;
sStartPointY_W = gsRenderCenterY + sTempPosY_W;
// Determine Start block and render offsets
// Find start block
// Add adjustment for render origin as well
(*psWorldX) = sStartPointX_W;
(*psWorldY) = sStartPointY_W;
}
void GetScreenXYWorldCell( INT16 sScreenX, INT16 sScreenY, INT16 *psWorldCellX, INT16 *psWorldCellY )
{
INT16 sWorldX, sWorldY;
GetScreenXYWorldCoords( sScreenX, sScreenY, &sWorldX, &sWorldY );
// Find start block
(*psWorldCellX) = ( sWorldX / CELL_X_SIZE );
(*psWorldCellY) = ( sWorldY / CELL_Y_SIZE );
}
void GetScreenXYGridNo( INT16 sScreenX, INT16 sScreenY, INT32 *psMapPos )
{
INT16 sWorldX, sWorldY;
GetScreenXYWorldCell( sScreenX, sScreenY, &sWorldX, &sWorldY );
*psMapPos = MAPROWCOLTOPOS( sWorldY, sWorldX );
}
void GetWorldXYAbsoluteScreenXY( INT32 sWorldCellX, INT32 sWorldCellY, INT16 *psWorldScreenX, INT16 *psWorldScreenY )
{
INT16 sScreenCenterX, sScreenCenterY;
INT16 sDistToCenterY, sDistToCenterX;
// Find the diustance from render center to true world center
sDistToCenterX = ( sWorldCellX * CELL_X_SIZE ) - gCenterWorldX;
sDistToCenterY = ( sWorldCellY * CELL_Y_SIZE ) - gCenterWorldY;
// From render center in world coords, convert to render center in "screen" coords
// ATE: We should call the fowllowing function but I'm putting it here verbatim for speed
//FromCellToScreenCoordinates( sDistToCenterX , sDistToCenterY, &sScreenCenterX, &sScreenCenterY );
sScreenCenterX = ( 2 * sDistToCenterX ) - ( 2 * sDistToCenterY );
sScreenCenterY = sDistToCenterX + sDistToCenterY;
// Subtract screen center
*psWorldScreenX = sScreenCenterX + gsCX - gsTLX;
*psWorldScreenY = sScreenCenterY + gsCY - gsTLY;
}
void GetFromAbsoluteScreenXYWorldXY( INT32 *psWorldCellX, INT32* psWorldCellY, INT16 sWorldScreenX, INT16 sWorldScreenY )
{
INT16 sWorldCenterX, sWorldCenterY;
INT16 sDistToCenterY, sDistToCenterX;
// Subtract screen center
sDistToCenterX = sWorldScreenX - gsCX + gsTLX;
sDistToCenterY = sWorldScreenY - gsCY + gsTLY;
// From render center in world coords, convert to render center in "screen" coords
// ATE: We should call the fowllowing function but I'm putting it here verbatim for speed
//FromCellToScreenCoordinates( sDistToCenterX , sDistToCenterY, &sScreenCenterX, &sScreenCenterY );
sWorldCenterX = ( ( sDistToCenterX + ( 2 * sDistToCenterY ) ) / 4 );
sWorldCenterY = ( ( 2 * sDistToCenterY ) - sDistToCenterX ) / 4;
// Goto center again
*psWorldCellX = sWorldCenterX + gCenterWorldX;
*psWorldCellY = sWorldCenterY + gCenterWorldY;
}
// UTILITY FUNTIONS
INT32 OutOfBounds(INT32 sGridNo, INT32 sProposedGridNo)
{
INT32 sMod,sPropMod;
// get modulas of our origin
sMod = sGridNo % MAXCOL;
if (sMod != 0) // if we're not on leftmost grid
if (sMod != RIGHTMOSTGRID) // if we're not on rightmost grid
if (sGridNo < LASTROWSTART) // if we're above bottom row
if (sGridNo > MAXCOL) // if we're below top row
// Everything's OK - we're not on the edge of the map
return(FALSE);
// if we've got this far, there's a potential problem - check it out!
if (sProposedGridNo < 0)
return(TRUE);
sPropMod = sProposedGridNo % MAXCOL;
if (sMod == 0 && sPropMod == RIGHTMOSTGRID)
return(TRUE);
else
if (sMod == RIGHTMOSTGRID && sPropMod == 0)
return(TRUE);
else
if (sGridNo >= LASTROWSTART && sProposedGridNo >= GRIDSIZE)
return(TRUE);
else
return(FALSE);
}
INT32 NewGridNo(INT32 sGridNo, INT16 sDirInc)
{
INT32 sProposedGridNo = sGridNo + sDirInc;
// now check for out-of-bounds
if (OutOfBounds(sGridNo,sProposedGridNo))
// return ORIGINAL gridno to user
sProposedGridNo = sGridNo;
return(sProposedGridNo);
}
INT16 DirectionInc(UINT8 ubDirection)
{
if ( ubDirection > NORTHWEST )
{
//#ifdef BETAVERSION
// NumMessage("DirectionInc: Invalid direction received, = ",direction);
//#endif
//direction = random(8); // replace garbage with random direction
ubDirection = NORTH;
}
return(DirIncrementer[ubDirection]);
}
BOOLEAN CellXYToScreenXY(INT16 sCellX, INT16 sCellY, INT16 *sScreenX, INT16 *sScreenY)
{
INT16 sDeltaCellX, sDeltaCellY;
INT16 sDeltaScreenX, sDeltaScreenY;
sDeltaCellX=sCellX-gsRenderCenterX;
sDeltaCellY=sCellY-gsRenderCenterY;
FromCellToScreenCoordinates(sDeltaCellX, sDeltaCellY, &sDeltaScreenX, &sDeltaScreenY);
*sScreenX=(((gsVIEWPORT_END_X-gsVIEWPORT_START_X)/2)+sDeltaScreenX);
*sScreenY=(((gsVIEWPORT_END_Y-gsVIEWPORT_START_Y)/2)+sDeltaScreenY);
return(TRUE);
}
void ConvertGridNoToXY( INT32 sGridNo, INT16 *sXPos, INT16 *sYPos )
{
*sYPos = sGridNo / WORLD_COLS;
*sXPos = ( sGridNo - ( *sYPos * WORLD_COLS ) );
}
void ConvertGridNoToCellXY( INT32 sGridNo, INT16 *sXPos, INT16 *sYPos )
{
*sYPos = ( sGridNo / WORLD_COLS );
*sXPos = sGridNo - ( *sYPos * WORLD_COLS );
*sYPos = ( *sYPos * CELL_Y_SIZE );
*sXPos = ( *sXPos * CELL_X_SIZE );
}
void ConvertGridNoToCenterCellXY( INT32 sGridNo, INT16 *sXPos, INT16 *sYPos )
{
*sYPos = ( sGridNo / WORLD_COLS );
*sXPos = ( sGridNo - ( *sYPos * WORLD_COLS ) );
*sYPos = ( *sYPos * CELL_Y_SIZE ) + ( CELL_Y_SIZE / 2 );
*sXPos = ( *sXPos * CELL_X_SIZE ) + ( CELL_X_SIZE / 2 );
}
INT32 GetRangeFromGridNoDiff( INT32 sGridNo1, INT32 sGridNo2 )
{
INT32 uiDist;
INT16 sXPos, sYPos, sXPos2, sYPos2;
// Convert our grid-not into an XY
ConvertGridNoToXY( sGridNo1, &sXPos, &sYPos );
// Convert our grid-not into an XY
ConvertGridNoToXY( sGridNo2, &sXPos2, &sYPos2 );
uiDist = (INT32)(sqrt((double) ( sXPos2 - sXPos )*( sXPos2 - sXPos ) + ( sYPos2 - sYPos ) * ( sYPos2 - sYPos ) ));
return( uiDist );
}
INT32 GetRangeInCellCoordsFromGridNoDiff( INT32 sGridNo1, INT32 sGridNo2 )
{
INT16 sXPos, sYPos, sXPos2, sYPos2;
// Convert our grid-not into an XY
ConvertGridNoToXY( sGridNo1, &sXPos, &sYPos );
// Convert our grid-not into an XY
ConvertGridNoToXY( sGridNo2, &sXPos2, &sYPos2 );
return( (INT32)( sqrt((double) ( sXPos2 - sXPos ) * ( sXPos2 - sXPos ) + ( sYPos2 - sYPos ) * ( sYPos2 - sYPos ) ) ) * CELL_X_SIZE );
}
BOOLEAN IsPointInScreenRect( INT16 sXPos, INT16 sYPos, SGPRect *pRect )
{
if ( (sXPos >= pRect->iLeft) && (sXPos <= pRect->iRight) && (sYPos >= pRect->iTop) && (sYPos <= pRect->iBottom) )
{
return( TRUE );
}
else
{
return( FALSE );
}
}
BOOLEAN IsPointInScreenRectWithRelative( INT16 sXPos, INT16 sYPos, SGPRect *pRect, INT16 *sXRel, INT16 *sYRel )
{
if ( (sXPos >= pRect->iLeft) && (sXPos <= pRect->iRight) && (sYPos >= pRect->iTop) && (sYPos <= pRect->iBottom) )
{
(*sXRel) = pRect->iLeft - sXPos;
(*sYRel) = sYPos - (INT16)pRect->iTop;
return( TRUE );
}
else
{
return( FALSE );
}
}
INT16 PythSpacesAway(INT32 sOrigin, INT32 sDest)
{
INT16 sRows,sCols,sResult;
sRows = abs((sOrigin / MAXCOL) - (sDest / MAXCOL));
sCols = abs((sOrigin % MAXROW) - (sDest % MAXROW));
// apply Pythagoras's theorem for right-handed triangle:
// dist^2 = rows^2 + cols^2, so use the square root to get the distance
sResult = (INT16)sqrt((double) (sRows * sRows) + (sCols * sCols));
return(sResult);
}
INT16 SpacesAway(INT32 sOrigin, INT32 sDest)
{
INT16 sRows,sCols;
sRows = abs((sOrigin / MAXCOL) - (sDest / MAXCOL));
sCols = abs((sOrigin % MAXROW) - (sDest % MAXROW));
return( __max( sRows, sCols ) );
}
INT16 CardinalSpacesAway(INT32 sOrigin, INT32 sDest)
// distance away, ignoring diagonals!
{
INT16 sRows,sCols;
sRows = abs((sOrigin / MAXCOL) - (sDest / MAXCOL));
sCols = abs((sOrigin % MAXROW) - (sDest % MAXROW));
return( (INT16)( sRows + sCols ) );
}
INT8 FindNumTurnsBetweenDirs( INT8 sDir1, INT8 sDir2 )
{
INT16 sDirection;
INT16 sNumTurns = 0;
sDirection = sDir1;
do
{
sDirection = sDirection + QuickestDirection( sDir1, sDir2 );
if ( sDirection > NORTHWEST )
{
sDirection = NORTH;
}
else
{
if ( sDirection < NORTH )
{
sDirection = NORTHWEST;
}
}
if ( sDirection == sDir2 )
{
break;
}
sNumTurns++;
// SAFEGUARD ! - if we (somehow) do not get to were we want!
if ( sNumTurns > 100 )
{
sNumTurns = 0;
break;
}
} while( TRUE );
return( (INT8)sNumTurns );
}
BOOLEAN FindHeigherLevel( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bStartingDir, INT8 *pbDirection )
{
INT32 cnt;
INT32 sNewGridNo;
BOOLEAN fFound = FALSE;
UINT8 bMinNumTurns = 100;
INT8 bNumTurns;
INT8 bMinDirection = 0;
// IF there is a roof over our heads, this is an ivalid....
// return ( FALSE );l
if ( FindStructure( sGridNo, STRUCTURE_ROOF ) != NULL )
{
return( FALSE );
}
// LOOP THROUGH ALL 8 DIRECTIONS
for ( cnt = 0; cnt < NUM_WORLD_DIRECTIONS; cnt += 2 )
{
sNewGridNo = NewGridNo( sGridNo, (UINT16)DirectionInc( (UINT8)cnt ) );
if ( NewOKDestination( pSoldier, sNewGridNo, TRUE, 1 ) )
{
// Check if this tile has a higher level
if ( IsHeigherLevel( sNewGridNo ) )
{
fFound = TRUE;
// FInd how many turns we should go to get here
bNumTurns = FindNumTurnsBetweenDirs( (INT8)cnt, bStartingDir );
if ( bNumTurns < bMinNumTurns )
{
bMinNumTurns = bNumTurns;
bMinDirection = (INT8)cnt;
}
}
}
}
if ( fFound )
{
*pbDirection = bMinDirection;
return( TRUE );
}
return( FALSE );
}
BOOLEAN FindLowerLevel( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bStartingDir, INT8 *pbDirection )
{
INT32 cnt;
INT32 sNewGridNo;
BOOLEAN fFound = FALSE;
UINT8 bMinNumTurns = 100;
INT8 bNumTurns;
INT8 bMinDirection = 0;
// LOOP THROUGH ALL 8 DIRECTIONS
for ( cnt = 0; cnt < NUM_WORLD_DIRECTIONS; cnt += 2 )
{
sNewGridNo = NewGridNo( sGridNo, (UINT16)DirectionInc( (UINT8)cnt ) );
// Make sure there is NOT a roof here...
// Check OK destination
if ( NewOKDestination( pSoldier, sNewGridNo, TRUE, 0 ) )
{
if ( FindStructure( sNewGridNo, STRUCTURE_ROOF ) == NULL )
{
{
fFound = TRUE;
// FInd how many turns we should go to get here
bNumTurns = FindNumTurnsBetweenDirs( (INT8)cnt, bStartingDir );
if ( bNumTurns < bMinNumTurns )
{
bMinNumTurns = bNumTurns;
bMinDirection = (INT8)cnt;
}
}
}
}
}
if ( fFound )
{
*pbDirection = bMinDirection;
return( TRUE );
}
return( FALSE );
}
INT16 QuickestDirection(INT16 origin, INT16 dest)
{
INT16 v1,v2;
if (origin==dest)
return(0);
if ((abs(origin - dest)) == 4)
return(1); // this could be made random
else
if (origin > dest)
{
v1 = (INT16) abs(origin - dest);
v2 = (8 - origin) + dest;
if (v1 > v2)
return(1);
else
return(-1);
}
else
{
v1 = (INT16) abs(origin - dest);
v2 = (8 - dest) + origin;
if (v1 > v2)
return(-1);
else
return(1);
}
}
INT16 ExtQuickestDirection(INT16 origin, INT16 dest)
{
INT16 v1,v2;
if (origin==dest)
return(0);
if ((abs(origin - dest)) == 16)
return(1); // this could be made random
else
if (origin > dest)
{
v1 = (INT16) abs(origin - dest);
v2 = (32 - origin) + dest;
if (v1 > v2)
return(1);
else
return(-1);
}
else
{
v1 = (INT16) abs(origin - dest);
v2 = (32 - dest) + origin;
if (v1 > v2)
return(-1);
else
return(1);
}
}
// Returns the (center ) cell coordinates in X
INT16 CenterX( INT32 sGridNo )
{
INT32 sYPos, sXPos;
sYPos = sGridNo / WORLD_COLS;
sXPos = ( sGridNo - ( sYPos * WORLD_COLS ) );
return( ( sXPos * CELL_X_SIZE ) + ( CELL_X_SIZE / 2 ) );
}
// Returns the (center ) cell coordinates in Y
INT16 CenterY( INT32 sGridNo )
{
INT32 sYPos, sXPos;
sYPos = sGridNo / WORLD_COLS;
sXPos = ( sGridNo - ( sYPos * WORLD_COLS ) );
return( ( sYPos * CELL_Y_SIZE ) + ( CELL_Y_SIZE / 2 ) );
}
INT16 MapX( INT32 sGridNo )
{
INT32 sYPos, sXPos;
sYPos = sGridNo / WORLD_COLS;
sXPos = ( sGridNo - ( sYPos * WORLD_COLS ) );
return( sXPos );
}
INT16 MapY( INT32 sGridNo )
{
INT32 sYPos, sXPos;
sYPos = sGridNo / WORLD_COLS;
sXPos = ( sGridNo - ( sYPos * WORLD_COLS ) );
return( sYPos );
}
bool GridNoOnWalkableWorldTile(INT32 sGridNo)
{
if (AreInMeanwhile() || gbWorldSectorZ != 0) return true;
//Shadooow: this will compare sGridNo height with height of the center grid of the map, as long as the center of the map is on a walkable height it will work properly
MAP_ELEMENT *pMapElement = &(gpWorldLevelData[sGridNo]);
MAP_ELEMENT *pMapElementCenter = &(gpWorldLevelData[gMapInformation.sCenterGridNo]);
return pMapElement->sHeight == pMapElementCenter->sHeight;
}
BOOLEAN GridNoOnVisibleWorldTile( INT32 sGridNo )
{
INT16 sWorldX;
INT16 sWorldY;
INT16 sXMapPos, sYMapPos;
// Check for valid gridno...
ConvertGridNoToXY( sGridNo, &sXMapPos, &sYMapPos );
// Get screen coordinates for current position of soldier
GetWorldXYAbsoluteScreenXY( sXMapPos, sYMapPos, &sWorldX, &sWorldY);
#if 0//dnl ch53 151009
if ( sWorldX > 0 && sWorldX < ( gsTRX - gsTLX - 20 ) && sWorldY > 20 && sWorldY < ( gsBLY - gsTLY - 20 ) )
#else
if ( sWorldX >= 30 && sWorldX <= (gsTRX - gsTLX - 30) && sWorldY >= 20 && sWorldY <= (gsBLY - gsTLY - 10) )
#endif
{
return GridNoOnWalkableWorldTile(sGridNo);
}
return( FALSE );
}
#if 0//dnl ch53 101009
// This function is used when we care about astetics with the top Y portion of the
// gma eplay area
// mostly due to UI bar that comes down....
BOOLEAN GridNoOnVisibleWorldTileGivenYLimits( INT32 sGridNo )
{
INT16 sWorldX;
INT16 sWorldY;
INT16 sXMapPos, sYMapPos;
// Check for valid gridno...
ConvertGridNoToXY( sGridNo, &sXMapPos, &sYMapPos );
// Get screen coordinates for current position of soldier
GetWorldXYAbsoluteScreenXY( sXMapPos, sYMapPos, &sWorldX, &sWorldY);
if ( sWorldX > 0 && sWorldX < ( gsTRX - gsTLX - 20 ) &&
sWorldY > 40 && sWorldY < ( gsBLY - gsTLY - 20 ) )
{
return( TRUE );
}
return( FALSE );
}
#endif
BOOLEAN GridNoOnEdgeOfMap( INT32 sGridNo, INT8 * pbDirection )
{
INT8 bDir;
// check NE, SE, SW, NW because of tilt of isometric display
for (bDir = NORTHEAST; bDir < NUM_WORLD_DIRECTIONS; bDir += 2 )
{
if (gubWorldMovementCosts[ (sGridNo + DirectionInc( bDir ) ) ][ bDir ][ 0 ] == TRAVELCOST_OFF_MAP)
//if ( !GridNoOnVisibleWorldTile( (INT16) (sGridNo + DirectionInc( bDir ) ) ) )
{
*pbDirection = bDir;
return( TRUE );
}
}
return( FALSE );
}
BOOLEAN FindFenceJumpDirection( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bStartingDir, INT8 *pbDirection )
{
INT32 cnt;
INT32 sNewGridNo, sOtherSideOfFence;
BOOLEAN fFound = FALSE;
UINT8 bMinNumTurns = 100;
INT8 bNumTurns;
INT8 bMinDirection = 0;
// IF there is a fence in this gridno, return false!
if ( IsJumpableFencePresentAtGridNo( sGridNo ) )
{
return( FALSE );
}
// LOOP THROUGH ALL 8 DIRECTIONS
for ( cnt = 0; cnt < NUM_WORLD_DIRECTIONS; cnt += 2 )
{
// go out *2* tiles
sNewGridNo = NewGridNo( sGridNo, (UINT16)DirectionInc( (UINT8)cnt ) );
sOtherSideOfFence = NewGridNo( sNewGridNo, (UINT16)DirectionInc( (UINT8)cnt ) );
if ( NewOKDestination( pSoldier, sOtherSideOfFence, TRUE, 0 ) )
{
// ATE: Check if there is somebody waiting here.....
// Check if we have a fence here
if ( IsJumpableFencePresentAtGridNo( sNewGridNo ) )
{
fFound = TRUE;
// FInd how many turns we should go to get here
bNumTurns = FindNumTurnsBetweenDirs( (INT8)cnt, bStartingDir );
if ( bNumTurns < bMinNumTurns )
{
bMinNumTurns = bNumTurns;
bMinDirection = (INT8)cnt;
}
}
}
}
if ( fFound )
{
*pbDirection = bMinDirection;
return( TRUE );
}
return( FALSE );
}
//Simply chooses a random gridno within valid boundaries (for dropping things in unloaded sectors)
INT32 RandomGridNo()
{
INT32 iMapXPos, iMapYPos, iMapIndex;
do
{
iMapXPos = Random( WORLD_COLS );
iMapYPos = Random( WORLD_ROWS );
iMapIndex = iMapYPos * WORLD_COLS + iMapXPos;
}while( !GridNoOnVisibleWorldTile( iMapIndex ) );
return iMapIndex;
}
// Flugente: is this gridno near a player merc?
BOOLEAN GridNoNearPlayerMercs( INT32 sGridNo, INT16 sRadius )
{
SOLDIERTYPE* pTeamSoldier = NULL;
INT32 cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID;
INT32 lastid = gTacticalStatus.Team[ OUR_TEAM ].bLastID;
for ( pTeamSoldier = MercPtrs[ cnt ]; cnt < lastid; ++cnt, ++pTeamSoldier)
{
if ( pTeamSoldier && pTeamSoldier->bActive && pTeamSoldier->bInSector )
{
if ( PythSpacesAway(sGridNo, pTeamSoldier->sGridNo) < sRadius )
return( TRUE );
}
}
return( FALSE );
}