#include "sgp.h" #include "Overhead.h" #include "Overhead Types.h" #include "Isometric Utils.h" #include "Interface Panels.h" #include "Soldier macros.h" #include "strategicmap.h" #include "strategic.h" #include "Animation Control.h" #include "Soldier Create.h" #include "Soldier Init List.h" #include "Soldier Add.h" #include "Map Information.h" #include "fov.h" #include "PATHAI.H" #include "random.h" #include "Render Fun.h" #include "Meanwhile.h" #include "Exit Grids.h" #include "Interface.h" // added by Flugente for zBackground #include "renderworld.h" // added by Flugente #include "Vehicles.h" // added by Flugente #include "CampaignStats.h" // added by Flugente #include "worldman.h" // added by Flugente for Water(...) #ifdef JA2UB #include "Ja25 Strategic Ai.h" #include "Ja25_Tactical.h" #include "ub_config.h" #endif #include "GameSettings.h" // ary-05/05/2009 : add forced turn mode #include "Text.h" // : add forced turn mode #include "Font Control.h" // : add forced turn mode #include "message.h" // : add forced turn mode #include "connect.h" //forward declarations of common classes to eliminate includes class OBJECTTYPE; class SOLDIERTYPE; extern BOOLEAN gfHandleHeli; // Adds a soldier to a world gridno and set's direction void AddSoldierToSectorGridNo( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubDirection, BOOLEAN fUseAnimation, UINT16 usAnimState, UINT16 usAnimCode ); INT32 FindGridNoFromSweetSpotWithStructData( SOLDIERTYPE *pSoldier, UINT16 usAnimState, INT32 sSweetGridNo, INT8 ubRadius, UINT8 *pubDirection, BOOLEAN fClosestToMerc ); // SO, STEPS IN CREATING A MERC! // 1 ) Setup the SOLDIERCREATE_STRUCT // Among other things, this struct needs a sSectorX, sSectorY, and a valid InsertionDirection // and InsertionGridNo. // This GridNo will be determined by a prevouis function that will examine the sector // Infomration regarding placement positions and pick one // 2 ) Call TacticalCreateSoldier() which will create our soldier // What it does is: Creates a soldier in the MercPtrs[] array. // Allocates the Animation cache for this merc // Loads up the intial aniamtion file // Creates initial palettes, etc // And other cool things. // Now we have an allocated soldier, we just need to set him in the world! // 3) When we want them in the world, call AddSoldierToSector(). // This function sets the graphic in the world, lighting effects, etc // It also formally adds it to the TacticalSoldier slot and interface panel slot. //Kris: modified to actually path from sweetspot to gridno. Previously, it only checked if the //destination was sittable (though it was possible that that location would be trapped. INT32 FindGridNoFromSweetSpot( SOLDIERTYPE *pSoldier, INT32 sSweetGridNo, INT8 ubRadius, UINT8 *pubDirection ) { INT32 sTop, sBottom; INT32 sLeft, sRight; INT32 cnt1, cnt2; INT32 sGridNo = NOWHERE; INT32 uiRange, uiLowestRange = 999999; INT32 sLowestGridNo = NOWHERE; INT32 leftmost; BOOLEAN fFound = FALSE; SOLDIERTYPE soldier; INT16 ubSaveNPCAPBudget; UINT8 ubSaveNPCDistLimit; //Save AI pathing vars. changing the distlimit restricts how //far away the pathing will consider. ubSaveNPCAPBudget = gubNPCAPBudget; ubSaveNPCDistLimit = gubNPCDistLimit; gubNPCAPBudget = 0; gubNPCDistLimit = ubRadius; //create dummy soldier, and use the pathing to determine which nearby slots are //reachable. soldier.pathing.bLevel = 0; soldier.bTeam = 1; soldier.sGridNo = sSweetGridNo; soldier.sDragCorpseID = pSoldier->sDragCorpseID; soldier.sDragGridNo = pSoldier->sDragGridNo; soldier.usDragPersonID = pSoldier->usDragPersonID; sTop = ubRadius; sBottom = -ubRadius; sLeft = - ubRadius; sRight = ubRadius; //clear the mapelements of potential residue MAPELEMENT_REACHABLE flags //in the square region. // ATE: CHECK FOR BOUNDARIES!!!!!! for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ ) { leftmost = ( ( sSweetGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS; for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ ) { sGridNo = sSweetGridNo + ( WORLD_COLS * cnt1 ) + cnt2; if( sGridNo >=0 && sGridNo < WORLD_MAX && sGridNo >= leftmost && sGridNo < ( leftmost + WORLD_COLS ) ) { gpWorldLevelData[ sGridNo ].uiFlags &= (~MAPELEMENT_REACHABLE); } } } //Now, find out which of these gridnos are reachable //(use the fake soldier and the pathing settings) FindBestPath( &soldier, GRIDSIZE, 0, WALKING, COPYREACHABLE, ( PATH_IGNORE_PERSON_AT_DEST | PATH_THROUGH_PEOPLE ) );//dnl ch50 071009 uiLowestRange = 999999; for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ ) { leftmost = ( ( sSweetGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS; for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ ) { sGridNo = sSweetGridNo + ( 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 if ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->pathing.bLevel ) ) { // ATE: INstead of using absolute range, use the path cost! //uiRange = PlotPath( &soldier, sGridNo, NO_COPYROUTE, NO_PLOT, TEMPORARY, WALKING, NOT_STEALTH, FORWARD, 50 ); uiRange = CardinalSpacesAway( sSweetGridNo, sGridNo ); // if ( uiRange == 0 ) // { // uiRange = 999999; // } if ( uiRange < uiLowestRange ) { sLowestGridNo = sGridNo; uiLowestRange = uiRange; fFound = TRUE; } } } } } 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 ); } } INT32 FindGridNoFromSweetSpotThroughPeople( SOLDIERTYPE *pSoldier, INT32 sSweetGridNo, INT8 ubRadius, UINT8 *pubDirection ) { INT32 sTop, sBottom; INT32 sLeft, sRight; INT32 cnt1, cnt2; INT32 sGridNo = NOWHERE; INT32 uiRange, uiLowestRange = 999999; INT32 sLowestGridNo = NOWHERE; INT32 leftmost; BOOLEAN fFound = FALSE; SOLDIERTYPE soldier; INT16 ubSaveNPCAPBudget; UINT8 ubSaveNPCDistLimit; //Save AI pathing vars. changing the distlimit restricts how //far away the pathing will consider. ubSaveNPCAPBudget = gubNPCAPBudget; ubSaveNPCDistLimit = gubNPCDistLimit; gubNPCAPBudget = 0; gubNPCDistLimit = ubRadius; //create dummy soldier, and use the pathing to determine which nearby slots are //reachable. soldier.pathing.bLevel = 0; soldier.bTeam = pSoldier->bTeam; soldier.sGridNo = sSweetGridNo; sTop = ubRadius; sBottom = -ubRadius; sLeft = - ubRadius; sRight = ubRadius; //clear the mapelements of potential residue MAPELEMENT_REACHABLE flags //in the square region. // ATE: CHECK FOR BOUNDARIES!!!!!! for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ ) { leftmost = ( ( sSweetGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS; for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ ) { sGridNo = sSweetGridNo + ( WORLD_COLS * cnt1 ) + cnt2; if( sGridNo >=0 && sGridNo < WORLD_MAX && sGridNo >= leftmost && sGridNo < ( leftmost + WORLD_COLS ) ) { gpWorldLevelData[ sGridNo ].uiFlags &= (~MAPELEMENT_REACHABLE); } } } //Now, find out which of these gridnos are reachable //(use the fake soldier and the pathing settings) FindBestPath( &soldier, GRIDSIZE, 0, WALKING, COPYREACHABLE, ( PATH_IGNORE_PERSON_AT_DEST | PATH_THROUGH_PEOPLE ) );//dnl ch50 071009 uiLowestRange = 999999; for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ ) { leftmost = ( ( sSweetGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS; for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ ) { sGridNo = sSweetGridNo + ( 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 if ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->pathing.bLevel ) ) { uiRange = GetRangeInCellCoordsFromGridNoDiff( sSweetGridNo, sGridNo ); { if ( uiRange < uiLowestRange ) { sLowestGridNo = sGridNo; uiLowestRange = uiRange; fFound = TRUE; } } } } } } 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 ); } } //Kris: modified to actually path from sweetspot to gridno. Previously, it only checked if the //destination was sittable (though it was possible that that location would be trapped. INT32 FindGridNoFromSweetSpotWithStructData( SOLDIERTYPE *pSoldier, UINT16 usAnimState, INT32 sSweetGridNo, INT8 ubRadius, UINT8 *pubDirection, BOOLEAN fClosestToMerc ) { INT32 sTop, sBottom; INT32 sLeft, sRight; INT32 cnt1, cnt2, cnt3; INT32 sGridNo = NOWHERE; INT32 uiRange, uiLowestRange = 999999; INT32 sLowestGridNo = NOWHERE; INT32 leftmost; BOOLEAN fFound = FALSE; SOLDIERTYPE soldier; INT16 ubSaveNPCAPBudget; UINT8 ubSaveNPCDistLimit; UINT8 ubBestDirection=0; //Save AI pathing vars. changing the distlimit restricts how //far away the pathing will consider. ubSaveNPCAPBudget = gubNPCAPBudget; ubSaveNPCDistLimit = gubNPCDistLimit; gubNPCAPBudget = 0; gubNPCDistLimit = ubRadius; //create dummy soldier, and use the pathing to determine which nearby slots are //reachable. soldier.pathing.bLevel = 0; soldier.bTeam = 1; soldier.sGridNo = sSweetGridNo; sTop = ubRadius; sBottom = -ubRadius; sLeft = - ubRadius; sRight = ubRadius; // If we are already at this gridno.... if ( pSoldier->sGridNo == sSweetGridNo && !( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) ) { *pubDirection = pSoldier->ubDirection; return( sSweetGridNo ); } //clear the mapelements of potential residue MAPELEMENT_REACHABLE flags //in the square region. // ATE: CHECK FOR BOUNDARIES!!!!!! for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ ) { leftmost = ( ( sSweetGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS; for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ ) { sGridNo = sSweetGridNo + ( WORLD_COLS * cnt1 ) + cnt2; if( sGridNo >=0 && sGridNo < WORLD_MAX && sGridNo >= leftmost && sGridNo < ( leftmost + WORLD_COLS ) ) { gpWorldLevelData[ sGridNo ].uiFlags &= (~MAPELEMENT_REACHABLE); } } } //Now, find out which of these gridnos are reachable //(use the fake soldier and the pathing settings) FindBestPath( &soldier, GRIDSIZE, 0, WALKING, COPYREACHABLE, ( PATH_IGNORE_PERSON_AT_DEST | PATH_THROUGH_PEOPLE ) );//dnl ch50 071009 uiLowestRange = 999999; for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ ) { leftmost = ( ( sSweetGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS; for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ ) { sGridNo = sSweetGridNo + ( 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 if ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->pathing.bLevel ) ) { BOOLEAN fDirectionFound = FALSE; UINT16 usOKToAddStructID; STRUCTURE_FILE_REF * pStructureFileRef; UINT16 usAnimSurface; if ( pSoldier->pLevelNode != NULL ) { if ( pSoldier->pLevelNode->pStructureData != NULL ) { usOKToAddStructID = pSoldier->pLevelNode->pStructureData->usStructureID; } else { usOKToAddStructID = INVALID_STRUCTURE_ID; } } else { usOKToAddStructID = INVALID_STRUCTURE_ID; } // Get animation surface... usAnimSurface = DetermineSoldierAnimationSurface( pSoldier, usAnimState ); // Get structure ref... pStructureFileRef = GetAnimationStructureRef( pSoldier->ubID, usAnimSurface, usAnimState ); if( !pStructureFileRef ) { Assert( 0 ); } // Check each struct in each direction for ( cnt3 = 0; cnt3 < NUM_WORLD_DIRECTIONS; ++cnt3 ) { if (OkayToAddStructureToWorld( sGridNo, pSoldier->pathing.bLevel, &(pStructureFileRef->pDBStructureRef[gOneCDirection[ cnt3 ]]), usOKToAddStructID ) ) { fDirectionFound = TRUE; break; } } if ( fDirectionFound ) { if ( fClosestToMerc ) { uiRange = FindBestPath( pSoldier, sGridNo, pSoldier->pathing.bLevel, pSoldier->usUIMovementMode, NO_COPYROUTE, 0 ); if (uiRange == 0 ) { uiRange = 999; } } else { uiRange = GetRangeInCellCoordsFromGridNoDiff( sSweetGridNo, sGridNo ); } if ( uiRange < uiLowestRange ) { ubBestDirection = (UINT8)cnt3; sLowestGridNo = sGridNo; uiLowestRange = uiRange; fFound = TRUE; } } } } } } gubNPCAPBudget = ubSaveNPCAPBudget; gubNPCDistLimit = ubSaveNPCDistLimit; if ( fFound ) { // Set direction we chose... *pubDirection = ubBestDirection; return( sLowestGridNo ); } else { return( NOWHERE ); } } INT32 FindGridNoFromSweetSpotWithStructDataUsingGivenDirectionFirst( SOLDIERTYPE *pSoldier, UINT16 usAnimState, INT32 sSweetGridNo, INT8 ubRadius, UINT8 *pubDirection, BOOLEAN fClosestToMerc, INT8 bGivenDirection ) { INT32 sTop, sBottom; INT32 sLeft, sRight; INT32 cnt1, cnt2, cnt3; INT32 sGridNo = NOWHERE; INT32 uiRange, uiLowestRange = 999999; INT32 sLowestGridNo = NOWHERE; INT32 leftmost; BOOLEAN fFound = FALSE; SOLDIERTYPE soldier; INT16 ubSaveNPCAPBudget; UINT8 ubSaveNPCDistLimit; UINT8 ubBestDirection=0; //Save AI pathing vars. changing the distlimit restricts how //far away the pathing will consider. ubSaveNPCAPBudget = gubNPCAPBudget; ubSaveNPCDistLimit = gubNPCDistLimit; gubNPCAPBudget = 0; gubNPCDistLimit = ubRadius; //create dummy soldier, and use the pathing to determine which nearby slots are //reachable. soldier.pathing.bLevel = 0; soldier.bTeam = 1; soldier.sGridNo = sSweetGridNo; sTop = ubRadius; sBottom = -ubRadius; sLeft = - ubRadius; sRight = ubRadius; // If we are already at this gridno.... if ( pSoldier->sGridNo == sSweetGridNo && !( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) ) { *pubDirection = pSoldier->ubDirection; return( sSweetGridNo ); } //clear the mapelements of potential residue MAPELEMENT_REACHABLE flags //in the square region. // ATE: CHECK FOR BOUNDARIES!!!!!! for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ ) { leftmost = ( ( sSweetGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS; for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ ) { sGridNo = sSweetGridNo + ( WORLD_COLS * cnt1 ) + cnt2; if( sGridNo >=0 && sGridNo < WORLD_MAX && sGridNo >= leftmost && sGridNo < ( leftmost + WORLD_COLS ) ) { gpWorldLevelData[ sGridNo ].uiFlags &= (~MAPELEMENT_REACHABLE); } } } //Now, find out which of these gridnos are reachable //(use the fake soldier and the pathing settings) FindBestPath( &soldier, GRIDSIZE, 0, WALKING, COPYREACHABLE, ( PATH_IGNORE_PERSON_AT_DEST | PATH_THROUGH_PEOPLE ) );//dnl ch50 071009 uiLowestRange = 999999; for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ ) { leftmost = ( ( sSweetGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS; for( cnt2 = sLeft; cnt2 <= sRight; ++cnt2 ) { sGridNo = sSweetGridNo + ( 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 if ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->pathing.bLevel ) ) { BOOLEAN fDirectionFound = FALSE; UINT16 usOKToAddStructID; STRUCTURE_FILE_REF * pStructureFileRef; UINT16 usAnimSurface; if ( pSoldier->pLevelNode != NULL ) { if ( pSoldier->pLevelNode->pStructureData != NULL ) { usOKToAddStructID = pSoldier->pLevelNode->pStructureData->usStructureID; } else { usOKToAddStructID = INVALID_STRUCTURE_ID; } } else { usOKToAddStructID = INVALID_STRUCTURE_ID; } // Get animation surface... usAnimSurface = DetermineSoldierAnimationSurface( pSoldier, usAnimState ); // Get structure ref... pStructureFileRef = GetAnimationStructureRef( pSoldier->ubID, usAnimSurface, usAnimState ); if( !pStructureFileRef ) { Assert( 0 ); } // OK, check the perfered given direction first if (OkayToAddStructureToWorld( sGridNo, pSoldier->pathing.bLevel, &(pStructureFileRef->pDBStructureRef[gOneCDirection[ bGivenDirection ]]), usOKToAddStructID ) ) { fDirectionFound = TRUE; cnt3 = bGivenDirection; } else { // Check each struct in each direction for( cnt3 = 0; cnt3 pathing.bLevel, &(pStructureFileRef->pDBStructureRef[gOneCDirection[ cnt3 ]]), usOKToAddStructID ) ) { fDirectionFound = TRUE; break; } } } } if ( fDirectionFound ) { if ( fClosestToMerc ) { uiRange = FindBestPath( pSoldier, sGridNo, pSoldier->pathing.bLevel, pSoldier->usUIMovementMode, NO_COPYROUTE, 0 ); if (uiRange == 0 ) { uiRange = 999; } } else { uiRange = GetRangeInCellCoordsFromGridNoDiff( sSweetGridNo, sGridNo ); } if ( uiRange < uiLowestRange ) { ubBestDirection = (UINT8)cnt3; sLowestGridNo = sGridNo; uiLowestRange = uiRange; fFound = TRUE; } } } } } } gubNPCAPBudget = ubSaveNPCAPBudget; gubNPCDistLimit = ubSaveNPCDistLimit; if ( fFound ) { // Set direction we chose... *pubDirection = ubBestDirection; return( sLowestGridNo ); } else { return( NOWHERE ); } } INT32 FindGridNoFromSweetSpotWithStructDataFromSoldier( SOLDIERTYPE *pSoldier, UINT16 usAnimState, INT8 ubRadius, UINT8 *pubDirection, BOOLEAN fClosestToMerc, SOLDIERTYPE *pSrcSoldier, BOOLEAN fAllowSoldierCurrentGrid ) { INT16 sTop, sBottom; INT16 sLeft, sRight; INT16 cnt1, cnt2, cnt3; INT32 sGridNo = NOWHERE; INT32 uiRange, uiLowestRange = 999999; INT32 sLowestGridNo = NOWHERE; INT32 leftmost; BOOLEAN fFound = FALSE; INT16 ubSaveNPCAPBudget; UINT8 ubSaveNPCDistLimit; UINT8 ubBestDirection=0; INT32 sSweetGridNo = NOWHERE; SOLDIERTYPE soldier; sSweetGridNo = pSrcSoldier->sGridNo; //Save AI pathing vars. changing the distlimit restricts how //far away the pathing will consider. ubSaveNPCAPBudget = gubNPCAPBudget; ubSaveNPCDistLimit = gubNPCDistLimit; gubNPCAPBudget = 0; gubNPCDistLimit = ubRadius; //create dummy soldier, and use the pathing to determine which nearby slots are //reachable. soldier.pathing.bLevel = 0; soldier.bTeam = 1; soldier.sGridNo = sSweetGridNo; sTop = ubRadius; sBottom = -ubRadius; sLeft = - ubRadius; sRight = ubRadius; //clear the mapelements of potential residue MAPELEMENT_REACHABLE flags //in the square region. // ATE: CHECK FOR BOUNDARIES!!!!!! for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ ) { leftmost = ( ( sSweetGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS; for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ ) { sGridNo = sSweetGridNo + ( WORLD_COLS * cnt1 ) + cnt2; if( sGridNo >=0 && sGridNo < WORLD_MAX && sGridNo >= leftmost && sGridNo < ( leftmost + WORLD_COLS ) ) { gpWorldLevelData[ sGridNo ].uiFlags &= (~MAPELEMENT_REACHABLE); } } } //Now, find out which of these gridnos are reachable FindBestPath( &soldier, GRIDSIZE, 0, WALKING, COPYREACHABLE, ( PATH_IGNORE_PERSON_AT_DEST | PATH_THROUGH_PEOPLE ) );//dnl ch50 071009 uiLowestRange = 999999; for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ ) { leftmost = ( ( sSweetGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS; for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ ) { sGridNo = sSweetGridNo + ( 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 // anv: sometimes it's possible soldier already is on the best grid, NewOKDestination alone would skip it if ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->pathing.bLevel ) || ( fAllowSoldierCurrentGrid && pSoldier->sGridNo == sGridNo ) ) { BOOLEAN fDirectionFound = FALSE; UINT16 usOKToAddStructID; STRUCTURE_FILE_REF * pStructureFileRef; UINT16 usAnimSurface; if ( fClosestToMerc != 3 ) { if ( pSoldier->pLevelNode != NULL && pSoldier->pLevelNode->pStructureData != NULL ) { usOKToAddStructID = pSoldier->pLevelNode->pStructureData->usStructureID; } else { usOKToAddStructID = INVALID_STRUCTURE_ID; } // Get animation surface... usAnimSurface = DetermineSoldierAnimationSurface( pSoldier, usAnimState ); // Get structure ref... pStructureFileRef = GetAnimationStructureRef( pSoldier->ubID, usAnimSurface, usAnimState ); // Check each struct in each direction for ( cnt3 = 0; cnt3 < NUM_WORLD_DIRECTIONS; ++cnt3 ) { if (OkayToAddStructureToWorld( sGridNo, pSoldier->pathing.bLevel, &(pStructureFileRef->pDBStructureRef[gOneCDirection[ cnt3 ]]), usOKToAddStructID ) ) { fDirectionFound = TRUE; break; } } } else { fDirectionFound = TRUE; cnt3 = (UINT8)Random( 8 ); } if ( fDirectionFound ) { if ( fClosestToMerc == 1 ) { uiRange = GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo, sGridNo ); } else if ( fClosestToMerc == 2 ) { uiRange = GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo, sGridNo ) + GetRangeInCellCoordsFromGridNoDiff( sSweetGridNo, sGridNo ); } else { //uiRange = GetRangeInCellCoordsFromGridNoDiff( sSweetGridNo, sGridNo ); uiRange = abs((sSweetGridNo / MAXCOL) - (sGridNo / MAXCOL)) + abs((sSweetGridNo % MAXROW) - (sGridNo % MAXROW)); } if ( uiRange < uiLowestRange || (uiRange == uiLowestRange && PythSpacesAway( pSoldier->sGridNo, sGridNo ) < PythSpacesAway( pSoldier->sGridNo, sLowestGridNo ) ) ) { ubBestDirection = (UINT8)cnt3; sLowestGridNo = sGridNo; uiLowestRange = uiRange; fFound = TRUE; } } } } } } gubNPCAPBudget = ubSaveNPCAPBudget; gubNPCDistLimit = ubSaveNPCDistLimit; if ( fFound ) { // Set direction we chose... *pubDirection = ubBestDirection; return( sLowestGridNo ); } else { return( NOWHERE ); } } INT32 FindGridNoFromSweetSpotExcludingSweetSpot( SOLDIERTYPE *pSoldier, INT32 sSweetGridNo, INT8 ubRadius, UINT8 *pubDirection ) { INT16 sTop, sBottom; INT16 sLeft, sRight; INT16 cnt1, cnt2; INT32 sGridNo = NOWHERE; INT32 uiRange, uiLowestRange = 999999; INT32 sLowestGridNo = NOWHERE; INT32 leftmost; BOOLEAN fFound = FALSE; sTop = ubRadius; sBottom = -ubRadius; sLeft = - ubRadius; sRight = ubRadius; uiLowestRange = 999999; for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ ) { leftmost = ( ( sSweetGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS; for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ ) { sGridNo = sSweetGridNo + ( WORLD_COLS * cnt1 ) + cnt2; if ( sSweetGridNo == sGridNo ) { continue; } if ( sGridNo >=0 && sGridNo < WORLD_MAX && sGridNo >= leftmost && sGridNo < ( leftmost + WORLD_COLS ) ) { // Go on sweet stop if ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->pathing.bLevel ) ) { uiRange = GetRangeInCellCoordsFromGridNoDiff( sSweetGridNo, sGridNo ); if ( uiRange < uiLowestRange ) { sLowestGridNo = sGridNo; uiLowestRange = uiRange; fFound = TRUE; } } } } } 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 ); } } INT32 FindGridNoFromSweetSpotExcludingSweetSpotInQuardent( SOLDIERTYPE *pSoldier, INT32 sSweetGridNo, INT8 ubRadius, UINT8 *pubDirection, INT8 ubQuardentDir ) { INT16 sTop, sBottom; INT16 sLeft, sRight; INT16 cnt1, cnt2; INT32 sGridNo = NOWHERE; INT32 uiRange, uiLowestRange = 999999; INT32 sLowestGridNo = NOWHERE; INT32 leftmost; BOOLEAN fFound = FALSE; sTop = ubRadius; sBottom = -ubRadius; sLeft = - ubRadius; sRight = ubRadius; // Switch on quadrent if ( ubQuardentDir == SOUTHEAST ) { sBottom = 0; sLeft = 0; } uiLowestRange = 999999; for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ ) { leftmost = ( ( sSweetGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS; for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ ) { sGridNo = sSweetGridNo + ( WORLD_COLS * cnt1 ) + cnt2; if ( sSweetGridNo == sGridNo ) { continue; } if ( sGridNo >=0 && sGridNo < WORLD_MAX && sGridNo >= leftmost && sGridNo < ( leftmost + WORLD_COLS ) ) { // Go on sweet stop if ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->pathing.bLevel ) ) { uiRange = GetRangeInCellCoordsFromGridNoDiff( sSweetGridNo, sGridNo ); if ( uiRange < uiLowestRange ) { sLowestGridNo = sGridNo; uiLowestRange = uiRange; fFound = TRUE; } } } } } 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 ); } } BOOLEAN CanSoldierReachGridNoInGivenTileLimit( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 sMaxTiles, INT8 bLevel ) { INT32 iNumTiles; INT32 sActionGridNo = NOWHERE; UINT8 ubDirection; if ( pSoldier->pathing.bLevel != bLevel ) { return( FALSE ); } sActionGridNo = FindAdjacentGridEx( pSoldier, sGridNo, &ubDirection, NULL, FALSE, FALSE ); if ( sActionGridNo == -1 ) { sActionGridNo = sGridNo; } if ( sActionGridNo == pSoldier->sGridNo ) { return( TRUE ); } iNumTiles = FindBestPath( pSoldier, sActionGridNo, pSoldier->pathing.bLevel, WALKING, NO_COPYROUTE, PATH_IGNORE_PERSON_AT_DEST ); if ( iNumTiles <= sMaxTiles && iNumTiles != 0 ) { return( TRUE ); } else { return( FALSE ); } } INT32 FindRandomGridNoFromSweetSpot( SOLDIERTYPE *pSoldier, INT32 sSweetGridNo, INT8 ubRadius, UINT8 *pubDirection ) { INT16 sX, sY; INT32 sGridNo = NOWHERE; INT32 leftmost; BOOLEAN fFound = FALSE; UINT32 cnt = 0; SOLDIERTYPE soldier; INT16 ubSaveNPCAPBudget; UINT8 ubSaveNPCDistLimit; INT32 sTop, sBottom; INT32 sLeft, sRight; INT32 cnt1, cnt2; //DBrot: More Rooms //UINT8 ubRoomNum; UINT16 usRoomNum; //Save AI pathing vars. changing the distlimit restricts how //far away the pathing will consider. ubSaveNPCAPBudget = gubNPCAPBudget; ubSaveNPCDistLimit = gubNPCDistLimit; gubNPCAPBudget = 0; gubNPCDistLimit = ubRadius; //create dummy soldier, and use the pathing to determine which nearby slots are //reachable. soldier.pathing.bLevel = 0; soldier.bTeam = 1; soldier.sGridNo = sSweetGridNo; sTop = ubRadius; sBottom = -ubRadius; sLeft = - ubRadius; sRight = ubRadius; // ATE: CHECK FOR BOUNDARIES!!!!!! for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ ) { leftmost = ( ( sSweetGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS; for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ ) { sGridNo = sSweetGridNo + ( WORLD_COLS * cnt1 ) + cnt2; if( sGridNo >=0 && sGridNo < WORLD_MAX && sGridNo >= leftmost && sGridNo < ( leftmost + WORLD_COLS ) ) { gpWorldLevelData[ sGridNo ].uiFlags &= (~MAPELEMENT_REACHABLE); } } } //Now, find out which of these gridnos are reachable //(use the fake soldier and the pathing settings) FindBestPath( &soldier, GRIDSIZE, 0, WALKING, COPYREACHABLE, ( PATH_IGNORE_PERSON_AT_DEST | PATH_THROUGH_PEOPLE ) );//dnl ch50 071009 do { sX = (UINT16)Random( ubRadius ); sY = (UINT16)Random( ubRadius ); leftmost = ( ( sSweetGridNo + ( WORLD_COLS * sY ) )/ WORLD_COLS ) * WORLD_COLS; sGridNo = sSweetGridNo + ( WORLD_COLS * sY ) + sX; if ( sGridNo >=0 && sGridNo < WORLD_MAX && sGridNo >= leftmost && sGridNo < ( leftmost + WORLD_COLS ) && gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE ) { // Go on sweet stop if ( NewOKDestination( pSoldier, sGridNo, TRUE , pSoldier->pathing.bLevel) ) { // If we are a crow, we need this additional check if ( pSoldier->ubBodyType == CROW ) { if ( !InARoom( sGridNo, &usRoomNum ) ) { fFound = TRUE; } } else { fFound = TRUE; } } } cnt++; if ( cnt > 2000 ) { return( NOWHERE ); } } while( !fFound ); // Set direction to center of map! *pubDirection = (UINT8)GetDirectionToGridNoFromGridNo( sGridNo, ( ( ( WORLD_ROWS / 2 ) * WORLD_COLS ) + ( WORLD_COLS / 2 ) ) ); gubNPCAPBudget = ubSaveNPCAPBudget; gubNPCDistLimit = ubSaveNPCDistLimit; return( sGridNo ); } // Flugente: I've altered this function in two ways: // 1. The gridno is now drawn from the entirety of the circle - not just for sX and sY being positive // 2. The direction now points to sSweetGridNo, center of the circle, instead of the map center INT32 FindRandomGridNoFromSweetSpotExcludingSweetSpot( SOLDIERTYPE *pSoldier, INT32 sSweetGridNo, INT8 ubRadius, UINT8 *pubDirection ) { INT16 sX, sY; INT32 sGridNo = NOWHERE; BOOLEAN fFound = FALSE; UINT32 cnt = 0; do { sX = (UINT16)Random( 2 * ubRadius ) - ubRadius; sY = (UINT16)Random( 2 * ubRadius ) - ubRadius; sGridNo = sSweetGridNo + (WORLD_COLS * sY ) + sX; if ( sGridNo == sSweetGridNo || TileIsOutOfBounds( sGridNo ) || PythSpacesAway( sGridNo, sSweetGridNo ) >= ubRadius || !IsLocationSittable( sGridNo, 0 ) ) sGridNo = NOWHERE; else { // Go on sweet stop if ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->pathing.bLevel ) ) { fFound = TRUE; } } ++cnt; if ( cnt > 2000 ) { return( NOWHERE ); } } while( !fFound ); // Set direction to center of map! *pubDirection = (UINT8)GetDirectionToGridNoFromGridNo( sGridNo, sSweetGridNo ); return( sGridNo ); } // Flugente: returns random gridno in a circle around sCenterGridNo with radius uOuterRadius that is not inside the circle with radius uInnerRadius INT32 FindRandomGridNoBetweenCircles( INT32 sCenterGridNo, UINT8 uInnerRadius, UINT8 uOuterRadius, UINT8& urDirection ) { INT16 sX, sY; INT32 sGridNo = NOWHERE; BOOLEAN fFound = FALSE; UINT32 cnt = 0; if ( uInnerRadius >= uOuterRadius ) return NOWHERE; do { /*sX = (UINT16)Random( 2 * (uOuterRadius - uInnerRadius) ) - (uOuterRadius - uInnerRadius); sY = (UINT16)Random( 2 * (uOuterRadius - uInnerRadius) ) - (uOuterRadius - uInnerRadius); if ( sX > 0 ) sX += uInnerRadius; else sX -= uInnerRadius; if ( sY > 0 ) sY += uInnerRadius; else sY -= uInnerRadius;*/ sX = (UINT16)Random( 2 * uOuterRadius ) - uOuterRadius; sY = (UINT16)Random( 2 * uOuterRadius ) - uOuterRadius; sGridNo = sCenterGridNo + (WORLD_COLS * sY) + sX; if ( TileIsOutOfBounds( sGridNo ) || Water( sGridNo, 0 ) || !IsLocationSittable( sGridNo, 0 ) || PythSpacesAway( sGridNo, sCenterGridNo ) <= uInnerRadius || PythSpacesAway( sGridNo, sCenterGridNo ) > uOuterRadius ) sGridNo = NOWHERE; else fFound = TRUE; ++cnt; if ( cnt > 2000 ) { return(NOWHERE); } } while ( !fFound ); // Set direction to center of map! urDirection = (UINT8)GetDirectionToGridNoFromGridNo( sGridNo, sCenterGridNo ); return(sGridNo); } BOOLEAN InternalAddSoldierToSector(SoldierID ubID, BOOLEAN fCalculateDirection, BOOLEAN fUseAnimation, UINT16 usAnimState, UINT16 usAnimCode ) { UINT8 ubDirection = 0; UINT8 ubCalculatedDirection = 0; INT32 sGridNo = NOWHERE; INT32 sExitGridNo = NOWHERE; DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("InternalAddSoldierToSector")); SOLDIERTYPE *pSoldier = ubID; if ( pSoldier->bActive ) { #ifdef JA2UB //Ja25 No meanwhiles in exp #else // ATE: Make sure life of elliot is OK if from a meanwhile if ( AreInMeanwhile() && pSoldier->ubProfile == ELLIOT ) { if ( pSoldier->stats.bLife < OKLIFE ) { pSoldier->stats.bLife = 25; } } #endif // ADD SOLDIER TO SLOT! if (pSoldier->flags.uiStatusFlags & SOLDIER_OFF_MAP) { AddAwaySlot( pSoldier ); // Guy is NOT "in sector" pSoldier->bInSector = FALSE; } else { AddMercSlot( pSoldier ); // Add guy to sector flag pSoldier->bInSector = TRUE; } // If a driver or passenger - stop here! if ( pSoldier->flags.uiStatusFlags & SOLDIER_DRIVER || pSoldier->flags.uiStatusFlags & SOLDIER_PASSENGER ) { return( FALSE ); } // Add to panel CheckForAndAddMercToTeamPanel( pSoldier ); pSoldier->usQuoteSaidFlags &= (~SOLDIER_QUOTE_SAID_SPOTTING_CREATURE_ATTACK); pSoldier->usQuoteSaidFlags &= (~SOLDIER_QUOTE_SAID_SMELLED_CREATURE); pSoldier->usQuoteSaidFlags &= (~SOLDIER_QUOTE_SAID_WORRIED_ABOUT_CREATURES); BOOLEAN gfEnteredFromTacticalPlacement = FALSE; // Add to interface if the are ours if ( pSoldier->bTeam == CREATURE_TEAM ) { sGridNo = FindGridNoFromSweetSpotWithStructData( pSoldier, STANDING, pSoldier->sInsertionGridNo, 7, &ubCalculatedDirection, FALSE ); if( fCalculateDirection ) ubDirection = ubCalculatedDirection; else { // Override calculated direction if we were told to.... if ( pSoldier->ubInsertionDirection >= 100 ) { pSoldier->ubInsertionDirection -= 100; } ubDirection = pSoldier->ubInsertionDirection; } } else { if(TileIsOutOfBounds(pSoldier->sInsertionGridNo)) { //Add the soldier to the respective entrypoint. This is an error condition. // So treat it like an error already then // WANNE: ASSERTION: Removed the assertion until we find the bug! //Assert(0); } if( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) { sGridNo = FindGridNoFromSweetSpotWithStructDataUsingGivenDirectionFirst( pSoldier, STANDING, pSoldier->sInsertionGridNo, 12, &ubCalculatedDirection, FALSE, pSoldier->ubInsertionDirection ); // ATE: Override insertion direction if (TileIsOutOfBounds(sGridNo)) { // Well, we gotta place this soldier/vehicle somewhere. Just use the first position for now sGridNo = pSoldier->sGridNo = pSoldier->sInsertionGridNo; } else { pSoldier->ubInsertionDirection = ubCalculatedDirection; } } else { if ( ( is_client && (pSoldier->ubStrategicInsertionCode == INSERTION_CODE_GRIDNO) ) || ( pSoldier->usSoldierFlagMask2 & SOLDIER_CONCEALINSERTION ) ) { sGridNo = pSoldier->sInsertionGridNo; ubCalculatedDirection = pSoldier->ubDirection; } else sGridNo = FindGridNoFromSweetSpot( pSoldier, pSoldier->sInsertionGridNo, 7, &ubCalculatedDirection ); //hayden // ATE: Error condition - if nowhere use insertion gridno! if (TileIsOutOfBounds(sGridNo)) { sGridNo = pSoldier->sInsertionGridNo; } } // Flugente: campaign stats if ( pSoldier->usSoldierFlagMask & SOLDIER_AIRDROP ) gCurrentIncident.usIncidentFlags |= INCIDENT_AIRDROP; // problem: soldiers already present in a sector have ubStrategicInsertionCode = 0, which is INSERTION_CODE_NORTH - but they don't actually come from north, as they are already present // we thus count them as coming from north if they have valid usStrategicInsertionData if ( pSoldier->ubStrategicInsertionCode == INSERTION_CODE_NORTH && pSoldier->usStrategicInsertionData )//|| pSoldier->sGridNo != NOWHERE ) { if ( pSoldier->bSide == gbPlayerNum ) gCurrentIncident.usIncidentFlags |= INCIDENT_ATTACKDIR_NORTH; else if ( !pSoldier->aiData.bNeutral ) gCurrentIncident.usIncidentFlags |= INCIDENT_ATTACKDIR_NORTH_ENEMY; } else if ( pSoldier->ubStrategicInsertionCode == INSERTION_CODE_SOUTH ) { if ( pSoldier->bSide == gbPlayerNum ) gCurrentIncident.usIncidentFlags |= INCIDENT_ATTACKDIR_SOUTH; else if ( !pSoldier->aiData.bNeutral ) gCurrentIncident.usIncidentFlags |= INCIDENT_ATTACKDIR_SOUTH_ENEMY; } else if ( pSoldier->ubStrategicInsertionCode == INSERTION_CODE_EAST ) { if ( pSoldier->bSide == gbPlayerNum ) gCurrentIncident.usIncidentFlags |= INCIDENT_ATTACKDIR_EAST; else if ( !pSoldier->aiData.bNeutral ) gCurrentIncident.usIncidentFlags |= INCIDENT_ATTACKDIR_EAST_ENEMY; } else if ( pSoldier->ubStrategicInsertionCode == INSERTION_CODE_WEST ) { if ( pSoldier->bSide == gbPlayerNum ) gCurrentIncident.usIncidentFlags |= INCIDENT_ATTACKDIR_WEST; else if ( !pSoldier->aiData.bNeutral ) gCurrentIncident.usIncidentFlags |= INCIDENT_ATTACKDIR_WEST_ENEMY; } // add this flag whenever we enter strategically enter a sector (= we attack a sector) pSoldier->usSoldierFlagMask |= SOLDIER_ASSAULT_BONUS; // Override calculated direction if we were told to.... if ( pSoldier->ubInsertionDirection >= 100 ) { pSoldier->ubInsertionDirection = pSoldier->ubInsertionDirection - 100; fCalculateDirection = FALSE; gfEnteredFromTacticalPlacement = TRUE; } if ( fCalculateDirection ) { ubDirection = ubCalculatedDirection; // Check if we need to get direction from exit grid... if ( pSoldier->bUseExitGridForReentryDirection ) { pSoldier->bUseExitGridForReentryDirection = FALSE; // OK, we know there must be an exit gridno SOMEWHERE close... sExitGridNo = FindClosestExitGrid( pSoldier, sGridNo, 10 ); if (!TileIsOutOfBounds(sExitGridNo)) { // We found one // Calculate direction... ubDirection = (UINT8)GetDirectionToGridNoFromGridNo( sExitGridNo, sGridNo ); } } } else { ubDirection = pSoldier->ubInsertionDirection; } } //Add if(gTacticalStatus.uiFlags & LOADING_SAVED_GAME || (pSoldier->ubStrategicInsertionCode == INSERTION_CODE_GRIDNO && !gfHandleHeli && !gfEnteredFromTacticalPlacement)) AddSoldierToSectorGridNo( pSoldier, sGridNo, pSoldier->ubDirection, TRUE, -1, 0);//shadooow: hack to make sure animations aren't changed else AddSoldierToSectorGridNo( pSoldier, sGridNo, ubDirection, fUseAnimation, usAnimState, usAnimCode ); // SANDRO - only do this if we are not loading the game (we keep this value saved) if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) ) { CheckForPotentialAddToBattleIncrement( pSoldier ); } return( TRUE ); } return( FALSE ); } BOOLEAN AddSoldierToSector( SoldierID ubID ) { DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("AddSoldierToSector")); return( InternalAddSoldierToSector( ubID, TRUE, FALSE, 0 , 0) ); } BOOLEAN AddSoldierToSectorNoCalculateDirection( UINT16 ubID ) { DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("AddSoldierToSectorNoCalculateDirection")); return( InternalAddSoldierToSector( ubID, FALSE, FALSE, 0, 0 ) ); } BOOLEAN AddSoldierToSectorNoCalculateDirectionUseAnimation( UINT16 ubID, UINT16 usAnimState, UINT16 usAnimCode ) { DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("AddSoldierToSectorNoCalculateDirectionUseAnimation")); return( InternalAddSoldierToSector( ubID, FALSE, TRUE, usAnimState, usAnimCode ) ); } void InternalSoldierInSectorSleep( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fDoTransition ) { INT16 sWorldX, sWorldY; UINT8 ubNewDirection; INT32 sGoodGridNo = NOWHERE; UINT16 usAnim = SLEEPING; if ( !pSoldier->bInSector ) { return; } if ( AM_AN_EPC( pSoldier ) ) { usAnim = STANDING; } // OK, look for suitable placement.... sGoodGridNo = FindGridNoFromSweetSpotWithStructData( pSoldier, usAnim, sGridNo, 5, &ubNewDirection, FALSE ); ConvertGridNoToCenterCellXY(sGoodGridNo, &sWorldX, &sWorldY); pSoldier->EVENT_SetSoldierPosition( sWorldX, sWorldY ); pSoldier->EVENT_SetSoldierDirection( ubNewDirection ); pSoldier->EVENT_SetSoldierDesiredDirection( ubNewDirection ); //pSoldier->pathing.bDesiredDirection = pSoldier->ubDirection; if ( AM_AN_EPC( pSoldier ) ) { pSoldier->EVENT_InitNewSoldierAnim( STANDING, 1, TRUE ); } else { if ( fDoTransition ) { pSoldier->EVENT_InitNewSoldierAnim( GOTO_SLEEP, 1, TRUE ); } else { pSoldier->EVENT_InitNewSoldierAnim( SLEEPING, 1, TRUE ); } } } void SoldierInSectorIncompaciated( SOLDIERTYPE *pSoldier, INT32 sGridNo ) { INT16 sWorldX, sWorldY; UINT8 ubNewDirection; INT32 sGoodGridNo = NOWHERE; if ( !pSoldier->bInSector ) { return; } // OK, look for suitable placement.... sGoodGridNo = FindGridNoFromSweetSpotWithStructData( pSoldier, STAND_FALLFORWARD_STOP, sGridNo, 5, &ubNewDirection, FALSE ); ConvertGridNoToCenterCellXY(sGoodGridNo, &sWorldX, &sWorldY); pSoldier->EVENT_SetSoldierPosition( sWorldX, sWorldY ); pSoldier->EVENT_SetSoldierDirection( ubNewDirection ); pSoldier->EVENT_SetSoldierDesiredDirection( ubNewDirection ); //pSoldier->pathing.bDesiredDirection = pSoldier->ubDirection; pSoldier->EVENT_InitNewSoldierAnim( STAND_FALLFORWARD_STOP, 1, TRUE ); } /* void SoldierInSectorSleep( SOLDIERTYPE *pSoldier, INT16 sGridNo ) { InternalSoldierInSectorSleep( pSoldier, sGridNo, TRUE ); } */ void SoldierInSectorPatient( SOLDIERTYPE *pSoldier, INT32 sGridNo ) { INT16 sWorldX, sWorldY; UINT8 ubNewDirection; INT32 sGoodGridNo = NOWHERE; if ( !pSoldier->bInSector ) { return; } // OK, look for suitable placement.... sGoodGridNo = FindGridNoFromSweetSpotWithStructData( pSoldier, BEING_PATIENT, sGridNo, 5, &ubNewDirection, FALSE ); ConvertGridNoToCenterCellXY(sGoodGridNo, &sWorldX, &sWorldY); pSoldier->EVENT_SetSoldierPosition( sWorldX, sWorldY ); pSoldier->EVENT_SetSoldierDirection( ubNewDirection ); pSoldier->EVENT_SetSoldierDesiredDirection( ubNewDirection ); //pSoldier->pathing.bDesiredDirection = pSoldier->ubDirection; if ( !IS_MERC_BODY_TYPE( pSoldier ) ) { pSoldier->EVENT_InitNewSoldierAnim( STANDING, 1, TRUE ); } else { pSoldier->EVENT_InitNewSoldierAnim( BEING_PATIENT, 1, TRUE ); } } void SoldierInSectorDoctor( SOLDIERTYPE *pSoldier, INT32 sGridNo ) { INT16 sWorldX, sWorldY; UINT8 ubNewDirection; INT32 sGoodGridNo = NOWHERE; if ( !pSoldier->bInSector ) { return; } // OK, look for suitable placement.... sGoodGridNo = FindGridNoFromSweetSpotWithStructData( pSoldier, BEING_DOCTOR, sGridNo, 5, &ubNewDirection, FALSE ); ConvertGridNoToCenterCellXY(sGoodGridNo, &sWorldX, &sWorldY); pSoldier->EVENT_SetSoldierPosition( sWorldX, sWorldY ); pSoldier->EVENT_SetSoldierDirection( ubNewDirection ); pSoldier->EVENT_SetSoldierDesiredDirection( ubNewDirection ); //pSoldier->pathing.bDesiredDirection = pSoldier->ubDirection; if ( !IS_MERC_BODY_TYPE( pSoldier ) ) { pSoldier->EVENT_InitNewSoldierAnim( STANDING, 1, TRUE ); } else { pSoldier->EVENT_InitNewSoldierAnim( BEING_DOCTOR, 1, TRUE ); } } void SoldierInSectorRepair( SOLDIERTYPE *pSoldier, INT32 sGridNo ) { INT16 sWorldX, sWorldY; UINT8 ubNewDirection; INT32 sGoodGridNo = NOWHERE; if ( !pSoldier->bInSector ) { return; } // OK, look for suitable placement.... sGoodGridNo = FindGridNoFromSweetSpotWithStructData( pSoldier, BEING_REPAIRMAN, sGridNo, 5, &ubNewDirection, FALSE ); ConvertGridNoToCenterCellXY(sGoodGridNo, &sWorldX, &sWorldY); pSoldier->EVENT_SetSoldierPosition( sWorldX, sWorldY ); pSoldier->EVENT_SetSoldierDirection( ubNewDirection ); pSoldier->EVENT_SetSoldierDesiredDirection( ubNewDirection ); //pSoldier->pathing.bDesiredDirection = pSoldier->ubDirection; if ( !IS_MERC_BODY_TYPE( pSoldier ) ) { pSoldier->EVENT_InitNewSoldierAnim( STANDING, 1, TRUE ); } else { pSoldier->EVENT_InitNewSoldierAnim( BEING_REPAIRMAN, 1, TRUE ); } } extern void EVENT_SetSoldierPositionAndMaybeFinalDestAndMaybeNotDestination( SOLDIERTYPE *pSoldier, FLOAT dNewXPos, FLOAT dNewYPos, BOOLEAN fUpdateDest, BOOLEAN fUpdateFinalDest ); void AddSoldierToSectorGridNo( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubDirection, BOOLEAN fUseAnimation, UINT16 usAnimState, UINT16 usAnimCode ) { INT16 sWorldX, sWorldY; INT32 sNewGridNo = NOWHERE; UINT8 ubNewDirection; UINT8 ubInsertionCode; BOOLEAN fUpdateFinalPosition = TRUE; DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("AddSoldierToSectorGridNo")); // Add merc to gridno ConvertGridNoToCenterCellXY(sGridNo, &sWorldX, &sWorldY); // Set reserved location! pSoldier->sReservedMovementGridNo = NOWHERE; // Save OLD insertion code.. as this can change... ubInsertionCode = pSoldier->ubStrategicInsertionCode; // Remove any pending animations pSoldier->usPendingAnimation = NO_PENDING_ANIMATION; pSoldier->usPendingAnimation2 = NO_PENDING_ANIMATION; pSoldier->ubPendingDirection = NO_PENDING_DIRECTION; pSoldier->aiData.ubPendingAction = NO_PENDING_ACTION; //If we are not loading a saved game if( (gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) ) { // Set final dest to be the same... fUpdateFinalPosition = FALSE; } // Flugente: if we are airdropping, center screen on the action and remove soldier initially. He will be dropped into the sector by the helicopter if ( pSoldier->usSoldierFlagMask & SOLDIER_AIRDROP ) { pSoldier->usSoldierFlagMask &= ~SOLDIER_AIRDROP; if ((gGameExternalOptions.ubSkyriderHotLZ == 1 || gGameExternalOptions.ubSkyriderHotLZ == 3)) { gfIgnoreScrolling = FALSE; INT16 sNewCenterWorldX, sNewCenterWorldY; ConvertGridNoToCenterCellXY( sGridNo, &sNewCenterWorldX, &sNewCenterWorldY ); SetRenderCenter( sNewCenterWorldX, sNewCenterWorldY ); gfIgnoreScrolling = TRUE; pSoldier->RemoveSoldierFromGridNo( ); pSoldier->bInSector = FALSE; return; } } // If this is a special insertion location, get path! if ( ubInsertionCode == INSERTION_CODE_ARRIVING_GAME ) { EVENT_SetSoldierPositionAndMaybeFinalDestAndMaybeNotDestination( pSoldier, sWorldX, sWorldY, fUpdateFinalPosition, fUpdateFinalPosition ); pSoldier->EVENT_SetSoldierDirection( ubDirection ); pSoldier->EVENT_SetSoldierDesiredDirection( ubDirection ); } else if ( ubInsertionCode == INSERTION_CODE_CHOPPER ) { } else { EVENT_SetSoldierPositionAndMaybeFinalDestAndMaybeNotDestination( pSoldier, sWorldX, sWorldY, fUpdateFinalPosition, fUpdateFinalPosition ); //if we are loading, dont set the direction ( they are already set ) if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) ) { pSoldier->EVENT_SetSoldierDirection( ubDirection ); pSoldier->EVENT_SetSoldierDesiredDirection( ubDirection ); } } if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) ) { if ( !( pSoldier->flags.uiStatusFlags & SOLDIER_DEAD ) ) { if ( pSoldier->bTeam == gbPlayerNum ) { if ( !( pSoldier->usSoldierFlagMask2 & SOLDIER_CONCEALINSERTION ) ) RevealRoofsAndItems( pSoldier, TRUE, FALSE, pSoldier->pathing.bLevel, TRUE ); // ATE: Patch fix: If we are in an non-interruptable animation, stop! if ( pSoldier->usAnimState == HOPFENCE ) { pSoldier->flags.fInNonintAnim = FALSE; pSoldier->SoldierGotoStationaryStance( ); } pSoldier->EVENT_StopMerc( sGridNo, ubDirection ); } } // If just arriving, set a destination to walk into from! if ( ubInsertionCode == INSERTION_CODE_ARRIVING_GAME ) { // Find a sweetspot near... sNewGridNo = FindGridNoFromSweetSpot( pSoldier, gMapInformation.sNorthGridNo, 4, &ubNewDirection ); pSoldier->EVENT_GetNewSoldierPath( sNewGridNo, WALKING ); } // If he's an enemy... set presence if ( !pSoldier->aiData.bNeutral && (pSoldier->bSide != gbPlayerNum ) ) { // ATE: Added if not bloodcats // only do this once they are seen..... if ( pSoldier->ubBodyType != BLOODCAT ) { SetEnemyPresence( ); // ary-05/05/2009 : add forced turn mode : note : not for bloodcats.. // : note : no forced turn mode option for Multi Player if ( is_networked ) gGameSettings.fOptions[ TOPTION_TOGGLE_TURN_MODE ] = FALSE; if ( gGameSettings.fOptions[ TOPTION_TOGGLE_TURN_MODE ]) { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, pMessageStrings[ MSG_FTM_ENTER_COMBAT ] ); // Flugente 2018-03-11: this part is problematic. If the enemy enters combat first, it is possible for enemie groups that are already present to be counted as pending // reinfocements, causing them to spawn at the edges of a sector instead. // I am thus changing this so that the player team always gets the first turn if combat has not yet been started if ( !(gTacticalStatus.uiFlags & INCOMBAT) || Random( 100 ) >= Random( 100 ) ) EnterCombatMode( OUR_TEAM ); else EnterCombatMode( ENEMY_TEAM ); } } } } if ( !( pSoldier->flags.uiStatusFlags & SOLDIER_DEAD ) ) { //if we are loading a 'pristine' map ( ie, not loading a saved game ) if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) ) { // ATE: Double check if we are on the roof that there is a roof there! if ( pSoldier->pathing.bLevel == 1 ) { if ( !FindStructure( pSoldier->sGridNo, STRUCTURE_ROOF ) ) { pSoldier->SetSoldierHeight( (FLOAT)( 0 ) ); } } if ( ubInsertionCode != INSERTION_CODE_ARRIVING_GAME ) { // default to standing on arrival if ( pSoldier->usAnimState != HELIDROP ) { #ifdef JA2UB // DAVE!!!! if ( gfFirstTimeInGameHeliCrash && gGameUBOptions.InGameHeli == FALSE ) { //should we be on our back or tummy if( Random( 100 ) < 50 ) pSoldier->EVENT_InitNewSoldierAnim( STAND_FALLFORWARD_STOP, 1, TRUE ); else pSoldier->EVENT_InitNewSoldierAnim( FALLBACKHIT_STOP, 1, TRUE ); pSoldier->bCollapsed = TRUE; } else if ( fUseAnimation ) { pSoldier->EVENT_InitNewSoldierAnim( usAnimState, usAnimCode, TRUE ); } else if ( pSoldier->ubBodyType != CROW && ubInsertionCode != INSERTION_CODE_GRIDNO) { pSoldier->EVENT_InitNewSoldierAnim( STANDING, 1, TRUE ); } #else if ( fUseAnimation ) { pSoldier->EVENT_InitNewSoldierAnim( usAnimState, usAnimCode, TRUE ); } else if ( pSoldier->ubBodyType != CROW) { pSoldier->EVENT_InitNewSoldierAnim( STANDING, 1, TRUE ); } #endif } // ATE: if we are below OK life, make them lie down! if ( pSoldier->stats.bLife < OKLIFE ) { SoldierInSectorIncompaciated( pSoldier, pSoldier->sInsertionGridNo ); } else if ( pSoldier->flags.fMercAsleep == TRUE ) { InternalSoldierInSectorSleep( pSoldier, pSoldier->sInsertionGridNo, FALSE ); } else if ( IS_DOCTOR(pSoldier->bAssignment) || pSoldier->bAssignment == DOCTOR_MILITIA ) { SoldierInSectorDoctor( pSoldier, pSoldier->sInsertionGridNo ); } else if ( IS_PATIENT(pSoldier->bAssignment) ) { SoldierInSectorPatient( pSoldier, pSoldier->sInsertionGridNo ); } else if ( IS_REPAIR(pSoldier->bAssignment) ) { SoldierInSectorRepair( pSoldier, pSoldier->sInsertionGridNo ); } // ATE: Make sure movement mode is up to date! pSoldier->usUIMovementMode = pSoldier->GetMoveStateBasedOnStance( gAnimControl[ pSoldier->usAnimState ].ubEndHeight ); } } else { // THIS ALL SHOULD HAVE BEEN HANDLED BY THE FACT THAT A GAME WAS LOADED //pSoldier->EVENT_InitNewSoldierAnim( pSoldier->usAnimState, pSoldier->usAniCode, TRUE ); // if the merc had a final destination, get the merc walking there //if( pSoldier->pathing.sFinalDestination != pSoldier->sGridNo ) //{ // pSoldier->EVENT_GetNewSoldierPath( pSoldier->pathing.sFinalDestination, pSoldier->usUIMovementMode ); //} } } } // IsMercOnTeam() checks to see if the passed in Merc Profile ID is currently on the player's team BOOLEAN IsMercOnTeam(UINT8 ubMercID, BOOLEAN aAlreadyInCountry, BOOLEAN aAlive) { SOLDIERTYPE *pTeamSoldier; SoldierID cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; SoldierID ubLastTeamID = gTacticalStatus.Team[ OUR_TEAM ].bLastID; // look for all mercs on the same team, for ( ; cnt <= ubLastTeamID; ++cnt ) { pTeamSoldier = cnt; if ( pTeamSoldier->ubProfile == ubMercID && pTeamSoldier->bActive ) { if ( aAlreadyInCountry && pTeamSoldier->bAssignment == IN_TRANSIT ) continue; if ( aAlive && pTeamSoldier->stats.bLife <= 0 ) continue; return( TRUE ); } } return(FALSE); } // GetSoldierIDFromMercID() Gets the Soldier ID from the Merc Profile ID, else returns NOBODY SoldierID GetSoldierIDFromMercID(UINT8 ubMercID) { SOLDIERTYPE *pTeamSoldier = NULL; SoldierID cnt = gTacticalStatus.Team[OUR_TEAM].bFirstID; // look for all mercs on the same team, for ( ; cnt <= gTacticalStatus.Team[OUR_TEAM].bLastID; ++cnt ) { pTeamSoldier = cnt; if ( pTeamSoldier->ubProfile == ubMercID ) { if( pTeamSoldier->bActive ) return( cnt ); } } return NOBODY; }