mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
Prevent merc in slot 0 from disappearing due to errant schedule. Still need to find and fix schedule though Prevent attempts to turn or climb roof when soldier collapsed Fix problems when mortar or RPG randomly assigned to a soldier but cannot be added for whatever reason Pathing favors beginning path on orthogonal and using as few turns as possible (mostly old behavior). Prevent interruptions when soldier climbs up, which can cause fall into building just below the target roof position Fix pauses due to soldiers hurt in previous turn Fix soldier path not reaching destination when thrown by explosion damage or heavy gunfire Restore pathing to cave and underground exits Fix too many AI ending up on the roofs git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@1588 3b4a5df2-a311-0410-b5c6-a8a6f20db521
412 lines
11 KiB
C++
412 lines
11 KiB
C++
#ifdef PRECOMPILEDHEADERS
|
|
#include "TileEngine All.h"
|
|
#else
|
|
#include <stdio.h>
|
|
#include "FileMan.h"
|
|
#include "debug.h"
|
|
#include "types.h"
|
|
#include "worlddef.h"
|
|
#include "worldman.h"
|
|
#include "smooth.h"
|
|
#include "Exit Grids.h"
|
|
#include "Editor Undo.h"
|
|
#include "StrategicMap.h"
|
|
#include "Strategic Movement.h"
|
|
#include "message.h"
|
|
#include "Font Control.h"
|
|
#include "pathai.h"
|
|
#include "overhead.h"
|
|
#include "Animation Control.h"
|
|
#include "Sys Globals.h"
|
|
#include "quests.h"
|
|
#include "SaveLoadMap.h"
|
|
#include "Text.h"
|
|
#endif
|
|
|
|
BOOLEAN gfLoadingExitGrids = FALSE;
|
|
|
|
//used by editor.
|
|
EXITGRID gExitGrid = {0,1,1,0};
|
|
|
|
BOOLEAN gfOverrideInsertionWithExitGrid = FALSE;
|
|
|
|
INT32 ConvertExitGridToINT32( EXITGRID *pExitGrid )
|
|
{
|
|
INT32 iExitGridInfo;
|
|
iExitGridInfo = (pExitGrid->ubGotoSectorX-1)<< 28;
|
|
iExitGridInfo += (pExitGrid->ubGotoSectorY-1)<< 24;
|
|
iExitGridInfo += pExitGrid->ubGotoSectorZ << 20;
|
|
iExitGridInfo += pExitGrid->usGridNo & 0x0000ffff;
|
|
return iExitGridInfo;
|
|
}
|
|
|
|
void ConvertINT32ToExitGrid( INT32 iExitGridInfo, EXITGRID *pExitGrid )
|
|
{
|
|
//convert the int into 4 unsigned bytes.
|
|
pExitGrid->ubGotoSectorX = (UINT8)(((iExitGridInfo & 0xf0000000)>>28)+1);
|
|
pExitGrid->ubGotoSectorY = (UINT8)(((iExitGridInfo & 0x0f000000)>>24)+1);
|
|
pExitGrid->ubGotoSectorZ = (UINT8)((iExitGridInfo & 0x00f00000)>>20);
|
|
pExitGrid->usGridNo = (UINT16)(iExitGridInfo & 0x0000ffff);
|
|
}
|
|
|
|
BOOLEAN GetExitGrid( UINT16 usMapIndex, EXITGRID *pExitGrid )
|
|
{
|
|
LEVELNODE *pShadow;
|
|
pShadow = gpWorldLevelData[ usMapIndex ].pShadowHead;
|
|
//Search through object layer for an exitgrid
|
|
while( pShadow )
|
|
{
|
|
if ( pShadow->uiFlags & LEVELNODE_EXITGRID )
|
|
{
|
|
ConvertINT32ToExitGrid( pShadow->iExitGridInfo, pExitGrid );
|
|
return TRUE;
|
|
}
|
|
pShadow = pShadow->pNext;
|
|
}
|
|
pExitGrid->ubGotoSectorX = 0;
|
|
pExitGrid->ubGotoSectorY = 0;
|
|
pExitGrid->ubGotoSectorZ = 0;
|
|
pExitGrid->usGridNo = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN ExitGridAtGridNo( UINT16 usMapIndex )
|
|
{
|
|
LEVELNODE *pShadow;
|
|
pShadow = gpWorldLevelData[ usMapIndex ].pShadowHead;
|
|
//Search through object layer for an exitgrid
|
|
while( pShadow )
|
|
{
|
|
if ( pShadow->uiFlags & LEVELNODE_EXITGRID )
|
|
{
|
|
return TRUE;
|
|
}
|
|
pShadow = pShadow->pNext;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN GetExitGridLevelNode( UINT16 usMapIndex, LEVELNODE **ppLevelNode )
|
|
{
|
|
LEVELNODE *pShadow;
|
|
pShadow = gpWorldLevelData[ usMapIndex ].pShadowHead;
|
|
//Search through object layer for an exitgrid
|
|
while( pShadow )
|
|
{
|
|
if ( pShadow->uiFlags & LEVELNODE_EXITGRID )
|
|
{
|
|
*ppLevelNode = pShadow;
|
|
return TRUE;
|
|
}
|
|
pShadow = pShadow->pNext;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void AddExitGridToWorld( INT32 iMapIndex, EXITGRID *pExitGrid )
|
|
{
|
|
LEVELNODE *pShadow, *tail;
|
|
pShadow = gpWorldLevelData[ iMapIndex ].pShadowHead;
|
|
|
|
//Search through object layer for an exitgrid
|
|
while( pShadow )
|
|
{
|
|
tail = pShadow;
|
|
if( pShadow->uiFlags & LEVELNODE_EXITGRID )
|
|
{ //we have found an existing exitgrid in this node, so replace it with the new information.
|
|
pShadow->iExitGridInfo = ConvertExitGridToINT32( pExitGrid );
|
|
//SmoothExitGridRadius( (UINT16)iMapIndex, 0 );
|
|
return;
|
|
}
|
|
pShadow = pShadow->pNext;
|
|
}
|
|
|
|
// Add levelnode
|
|
AddShadowToHead( iMapIndex, MOCKFLOOR1 );
|
|
// Get new node
|
|
pShadow = gpWorldLevelData[ iMapIndex ].pShadowHead;
|
|
|
|
//fill in the information for the new exitgrid levelnode.
|
|
pShadow->iExitGridInfo = ConvertExitGridToINT32( pExitGrid );
|
|
pShadow->uiFlags |= ( LEVELNODE_EXITGRID | LEVELNODE_HIDDEN );
|
|
|
|
//Add the exit grid to the sector, only if we call ApplyMapChangesToMapTempFile() first.
|
|
if( !gfEditMode && !gfLoadingExitGrids )
|
|
{
|
|
AddExitGridToMapTempFile( (UINT16)iMapIndex, pExitGrid, gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
|
|
}
|
|
}
|
|
|
|
void RemoveExitGridFromWorld( INT32 iMapIndex )
|
|
{
|
|
UINT16 usDummy;
|
|
if( TypeExistsInShadowLayer( iMapIndex, MOCKFLOOR, &usDummy ) )
|
|
{
|
|
RemoveAllShadowsOfTypeRange( iMapIndex, MOCKFLOOR, MOCKFLOOR );
|
|
}
|
|
}
|
|
|
|
void SaveExitGrids( HWFILE fp, UINT16 usNumExitGrids )
|
|
{
|
|
EXITGRID exitGrid;
|
|
UINT16 usNumSaved = 0;
|
|
UINT16 x;
|
|
UINT32 uiBytesWritten;
|
|
FileWrite( fp, &usNumExitGrids, 2, &uiBytesWritten );
|
|
for( x = 0; x < WORLD_MAX; x++ )
|
|
{
|
|
if( GetExitGrid( x, &exitGrid ) )
|
|
{
|
|
FileWrite( fp, &x, 2, &uiBytesWritten );
|
|
FileWrite( fp, &exitGrid, 5, &uiBytesWritten );
|
|
usNumSaved++;
|
|
}
|
|
}
|
|
//If these numbers aren't equal, something is wrong!
|
|
Assert( usNumExitGrids == usNumSaved );
|
|
}
|
|
|
|
void LoadExitGrids( INT8 **hBuffer )
|
|
{
|
|
EXITGRID exitGrid;
|
|
UINT16 x;
|
|
UINT16 usNumSaved;
|
|
UINT16 usMapIndex;
|
|
gfLoadingExitGrids = TRUE;
|
|
LOADDATA( &usNumSaved, *hBuffer, 2 );
|
|
//FileRead( hfile, &usNumSaved, 2, NULL);
|
|
for( x = 0; x < usNumSaved; x++ )
|
|
{
|
|
LOADDATA( &usMapIndex, *hBuffer, 2 );
|
|
//FileRead( hfile, &usMapIndex, 2, NULL);
|
|
LOADDATA( &exitGrid, *hBuffer, 5 );
|
|
//FileRead( hfile, &exitGrid, 5, NULL);
|
|
AddExitGridToWorld( usMapIndex, &exitGrid );
|
|
}
|
|
gfLoadingExitGrids = FALSE;
|
|
}
|
|
|
|
void AttemptToChangeFloorLevel( INT8 bRelativeZLevel )
|
|
{
|
|
UINT8 ubLookForLevel=0;
|
|
UINT16 i;
|
|
if( bRelativeZLevel != 1 && bRelativeZLevel != -1 )
|
|
return;
|
|
//Check if on ground level -- if so, can't go up!
|
|
if( bRelativeZLevel == -1 && !gbWorldSectorZ )
|
|
{
|
|
ScreenMsg( FONT_DKYELLOW, MSG_INTERFACE, pMessageStrings[ MSG_CANT_GO_UP ], ubLookForLevel );
|
|
return;
|
|
}
|
|
//Check if on bottom level -- if so, can't go down!
|
|
if( bRelativeZLevel == 1 && gbWorldSectorZ == 3 )
|
|
{
|
|
|
|
ScreenMsg( FONT_DKYELLOW, MSG_INTERFACE, pMessageStrings[ MSG_CANT_GO_DOWN ], ubLookForLevel );
|
|
return;
|
|
}
|
|
ubLookForLevel = (UINT8)(gbWorldSectorZ + bRelativeZLevel);
|
|
for( i = 0; i < WORLD_MAX; i++ )
|
|
{
|
|
if( GetExitGrid( i, &gExitGrid ) )
|
|
{
|
|
if( gExitGrid.ubGotoSectorZ == ubLookForLevel )
|
|
{ //found an exit grid leading to the goal sector!
|
|
gfOverrideInsertionWithExitGrid = TRUE;
|
|
//change all current mercs in the loaded sector, and move them
|
|
//to the new sector.
|
|
MoveAllGroupsInCurrentSectorToSector( (UINT8)gWorldSectorX, (UINT8)gWorldSectorY, ubLookForLevel );
|
|
if( ubLookForLevel )
|
|
ScreenMsg( FONT_YELLOW, MSG_INTERFACE, pMessageStrings[ MSG_ENTERING_LEVEL ], ubLookForLevel );
|
|
else
|
|
ScreenMsg( FONT_YELLOW, MSG_INTERFACE, pMessageStrings[ MSG_LEAVING_BASEMENT ] );
|
|
|
|
SetCurrentWorldSector( gWorldSectorX, gWorldSectorY, ubLookForLevel );
|
|
gfOverrideInsertionWithExitGrid = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
UINT16 FindGridNoFromSweetSpotCloseToExitGrid( SOLDIERTYPE *pSoldier, INT16 sSweetGridNo, INT8 ubRadius, UINT8 *pubDirection )
|
|
{
|
|
INT16 sTop, sBottom;
|
|
INT16 sLeft, sRight;
|
|
INT16 cnt1, cnt2;
|
|
INT16 sGridNo;
|
|
INT32 uiRange, uiLowestRange = 999999;
|
|
INT16 sLowestGridNo=0;
|
|
INT32 leftmost;
|
|
BOOLEAN fFound = FALSE;
|
|
SOLDIERTYPE soldier;
|
|
UINT8 ubSaveNPCAPBudget;
|
|
UINT8 ubSaveNPCDistLimit;
|
|
EXITGRID ExitGrid;
|
|
UINT8 ubGotoSectorX, ubGotoSectorY, ubGotoSectorZ;
|
|
|
|
//Save AI pathing vars. changing the distlimit restricts how
|
|
//far away the pathing will consider.
|
|
ubSaveNPCAPBudget = gubNPCAPBudget;
|
|
ubSaveNPCDistLimit = gubNPCDistLimit;
|
|
gubNPCAPBudget = 0;
|
|
gubNPCDistLimit = ubRadius;
|
|
|
|
// WDS - Clean up inventory handling
|
|
//create dummy soldier, and use the pathing to determine which nearby slots are
|
|
//reachable.
|
|
// memset( &soldier, 0, SIZEOF_SOLDIERTYPE );
|
|
soldier.initialize();
|
|
soldier.bLevel = 0;
|
|
soldier.bTeam = 1;
|
|
soldier.sGridNo = pSoldier->sGridNo;
|
|
|
|
// OK, Get an exit grid ( if possible )
|
|
if ( !GetExitGrid( sSweetGridNo, &ExitGrid ) )
|
|
{
|
|
return( NOWHERE );
|
|
}
|
|
|
|
// Copy our dest values.....
|
|
ubGotoSectorX = ExitGrid.ubGotoSectorX;
|
|
ubGotoSectorY = ExitGrid.ubGotoSectorY;
|
|
ubGotoSectorZ = ExitGrid.ubGotoSectorZ;
|
|
|
|
|
|
sTop = ubRadius;
|
|
sBottom = -ubRadius;
|
|
sLeft = - ubRadius;
|
|
sRight = ubRadius;
|
|
|
|
//clear the mapelements of potential residue MAPELEMENT_REACHABLE flags
|
|
//in the square region.
|
|
for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ )
|
|
{
|
|
for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ )
|
|
{
|
|
sGridNo = pSoldier->sGridNo + (WORLD_COLS * cnt1) + cnt2;
|
|
if ( sGridNo >= 0 && sGridNo < WORLD_MAX )
|
|
{
|
|
gpWorldLevelData[ sGridNo ].uiFlags &= (~MAPELEMENT_REACHABLE);
|
|
}
|
|
}
|
|
}
|
|
|
|
gfPlotPathToExitGrid = TRUE;
|
|
|
|
//Now, find out which of these gridnos are reachable
|
|
//(use the fake soldier and the pathing settings)
|
|
FindBestPath( &soldier, NOWHERE, 0, WALKING, COPYREACHABLE, PATH_THROUGH_PEOPLE );
|
|
|
|
uiLowestRange = 999999;
|
|
|
|
for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ )
|
|
{
|
|
leftmost = ( ( pSoldier->sGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS;
|
|
|
|
for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ )
|
|
{
|
|
sGridNo = pSoldier->sGridNo + ( WORLD_COLS * cnt1 ) + cnt2;
|
|
if ( sGridNo >=0 && sGridNo < WORLD_MAX && sGridNo >= leftmost && sGridNo < ( leftmost + WORLD_COLS ) &&
|
|
gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE )
|
|
{
|
|
// Go on sweet stop
|
|
// ATE: Added this check because for all intensive purposes, cavewalls will be not an OKDEST
|
|
// but we want thenm too...
|
|
if ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->bLevel ) )
|
|
{
|
|
if ( GetExitGrid( sGridNo, &ExitGrid ) )
|
|
{
|
|
// Is it the same exitgrid?
|
|
if ( ExitGrid.ubGotoSectorX == ubGotoSectorX && ExitGrid.ubGotoSectorY == ubGotoSectorY && ExitGrid.ubGotoSectorZ == ubGotoSectorZ )
|
|
{
|
|
uiRange = GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo, sGridNo );
|
|
|
|
if ( uiRange < uiLowestRange )
|
|
{
|
|
sLowestGridNo = sGridNo;
|
|
uiLowestRange = uiRange;
|
|
fFound = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
gfPlotPathToExitGrid = FALSE;
|
|
gubNPCAPBudget = ubSaveNPCAPBudget;
|
|
gubNPCDistLimit = ubSaveNPCDistLimit;
|
|
|
|
if ( fFound )
|
|
{
|
|
// Set direction to center of map!
|
|
*pubDirection = (UINT8)GetDirectionToGridNoFromGridNo( sLowestGridNo, ( ( ( WORLD_ROWS / 2 ) * WORLD_COLS ) + ( WORLD_COLS / 2 ) ) );
|
|
return( sLowestGridNo );
|
|
}
|
|
else
|
|
{
|
|
return( NOWHERE );
|
|
}
|
|
}
|
|
|
|
|
|
UINT16 FindClosestExitGrid( SOLDIERTYPE *pSoldier, INT16 sSrcGridNo, INT8 ubRadius )
|
|
{
|
|
INT16 sTop, sBottom;
|
|
INT16 sLeft, sRight;
|
|
INT16 cnt1, cnt2;
|
|
INT16 sGridNo;
|
|
INT32 uiRange, uiLowestRange = 999999;
|
|
INT16 sLowestGridNo=0;
|
|
INT32 leftmost;
|
|
BOOLEAN fFound = FALSE;
|
|
EXITGRID ExitGrid;
|
|
|
|
|
|
sTop = ubRadius;
|
|
sBottom = -ubRadius;
|
|
sLeft = - ubRadius;
|
|
sRight = ubRadius;
|
|
|
|
//clear the mapelements of potential residue MAPELEMENT_REACHABLE flags
|
|
uiLowestRange = 999999;
|
|
|
|
for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ )
|
|
{
|
|
leftmost = ( ( sSrcGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS;
|
|
|
|
for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ )
|
|
{
|
|
sGridNo = sSrcGridNo + ( WORLD_COLS * cnt1 ) + cnt2;
|
|
if( sGridNo >=0 && sGridNo < WORLD_MAX && sGridNo >= leftmost && sGridNo < ( leftmost + WORLD_COLS ) )
|
|
{
|
|
if ( GetExitGrid( sGridNo, &ExitGrid ) )
|
|
{
|
|
uiRange = GetRangeInCellCoordsFromGridNoDiff( sSrcGridNo, sGridNo );
|
|
|
|
if ( uiRange < uiLowestRange )
|
|
{
|
|
sLowestGridNo = sGridNo;
|
|
uiLowestRange = uiRange;
|
|
fFound = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( fFound )
|
|
{
|
|
return( sLowestGridNo );
|
|
}
|
|
else
|
|
{
|
|
return( NOWHERE );
|
|
}
|
|
}
|
|
|
|
|