#include #include #include #include "Render Fun.h" #include "stdlib.h" #include "DEBUG.H" #include "MemMan.h" #include "Overhead Types.h" #include "Animation Cache.h" #include "Animation Data.h" #include "Animation Control.h" #include "PATHAI.H" #include "random.h" #include "worldman.h" #include "Isometric Utils.h" #include "renderworld.h" #include "video.h" #include "Points.h" // #include "Sound Control.h" #include "lighting.h" #include "Weapons.h" #include "vobject_blitters.h" #include "Handle UI.h" #include "Event Pump.h" #include "opplist.h" #include "ai.h" #include "Interface.h" #include "lighting.h" #include "faces.h" #include "Soldier Profile.h" #ifdef NETWORKED #include "Networking.h" #include "NetworkEvent.h" #endif #include "Items.h" #include "soundman.h" #include "soldier tile.h" #include "Soldier Add.h" #include "fov.h" #include "Font Control.h" #include "message.h" #include "Text.h" #include "NPC.h" #include "Soldier macros.h" extern UINT8 gubWaitingForAllMercsToExitCode; //forward declarations of common classes to eliminate includes class OBJECTTYPE; class SOLDIERTYPE; #define NEXT_TILE_CHECK_DELAY 700 #ifdef JA2BETAVERSION void OutputDebugInfoForTurnBasedNextTileWaiting( SOLDIERTYPE * pSoldier ) { if ( (gTacticalStatus.uiFlags & INCOMBAT) && (pSoldier->pathing.usPathDataSize > 0) ) { UINT32 uiLoop; INT32 usTemp = NOWHERE; INT32 usNewGridNo; usNewGridNo = NewGridNo( pSoldier->sGridNo, DirectionInc( (UINT8)pSoldier->pathing.usPathingData[ pSoldier->pathing.usPathIndex ] ) ); // provide more info!! DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String(" Soldier path size %d, index %d", pSoldier->pathing.usPathDataSize, pSoldier->pathing.usPathIndex ) ); DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String(" Who is at blocked gridno: %d", WhoIsThere2( usNewGridNo, pSoldier->pathing.bLevel ) ) ); for ( uiLoop = 0; uiLoop < pSoldier->pathing.usPathDataSize; uiLoop++ ) { if ( uiLoop > pSoldier->pathing.usPathIndex ) { usTemp = NewGridNo( usTemp, DirectionInc( (UINT8)pSoldier->pathing.usPathingData[ uiLoop ] ) ); DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String(" Soldier path[%d]: %d == gridno %d", uiLoop, pSoldier->pathing.usPathingData[uiLoop], usTemp ) ); } else if ( uiLoop == pSoldier->pathing.usPathIndex ) { usTemp = usNewGridNo; DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String(" Soldier path[%d]: %d == gridno %d", uiLoop, pSoldier->pathing.usPathingData[uiLoop], usTemp ) ); } else { DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String(" Soldier path[%d]: %d", uiLoop, pSoldier->pathing.usPathingData[uiLoop] ) ); } } } } #endif void SetDelayedTileWaiting( SOLDIERTYPE *pSoldier, INT32 sCauseGridNo, INT8 bValue ) { // Cancel AI Action // CancelAIAction( pSoldier, TRUE ); pSoldier->flags.fDelayedMovement = bValue; pSoldier->sDelayedMovementCauseGridNo = sCauseGridNo; RESETTIMECOUNTER( pSoldier->timeCounters.NextTileCounter, NEXT_TILE_CHECK_DELAY ); // ATE: Now update realtime movement speed.... // check if guy exists here... SoldierID ubPerson = WhoIsThere2( sCauseGridNo, pSoldier->pathing.bLevel ); // There may not be anybody there, but it's reserved by them! if ( ( gpWorldLevelData[ sCauseGridNo ].uiFlags & MAPELEMENT_MOVEMENT_RESERVED ) ) { ubPerson = gpWorldLevelData[ sCauseGridNo ].ubReservedSoldierID; } if ( ubPerson != NOBODY ) { // if they are our own team members ( both ) if ( ubPerson->bTeam == gbPlayerNum && pSoldier->bTeam == gbPlayerNum ) { // Here we have another guy.... save his stats so we can use them for // speed determinations.... pSoldier->bOverrideMoveSpeed = ubPerson; pSoldier->flags.fUseMoverrideMoveSpeed = TRUE; } } } void SetFinalTile( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fGivenUp ) { // OK, If we were waiting for stuff, do it here... // ATE: Disabled stuff below, made obsolete by timeout... //if ( pSoldier->ubWaitActionToDo ) //{ // pSoldier->ubWaitActionToDo = 0; // gbNumMercsUntilWaitingOver--; //} pSoldier->pathing.sFinalDestination = pSoldier->sGridNo; #ifdef JA2BETAVERSION if ( gTacticalStatus.uiFlags & INCOMBAT ) { OutputDebugInfoForTurnBasedNextTileWaiting( pSoldier ); } #endif if ( pSoldier->bTeam == gbPlayerNum && fGivenUp ) { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, TacticalStr[ NO_PATH_FOR_MERC ], pSoldier->name ); } pSoldier->EVENT_StopMerc( pSoldier->sGridNo, pSoldier->ubDirection ); } void MarkMovementReserved( SOLDIERTYPE *pSoldier, INT32 sGridNo ) { // Check if we have one reserrved already, and free it first! if (!TileIsOutOfBounds(pSoldier->sReservedMovementGridNo)) { UnMarkMovementReserved( pSoldier ); } // For single-tiled mercs, set this gridno gpWorldLevelData[ sGridNo ].uiFlags |= MAPELEMENT_MOVEMENT_RESERVED; // Save soldier's reserved ID # gpWorldLevelData[ sGridNo ].ubReservedSoldierID = pSoldier->ubID; pSoldier->sReservedMovementGridNo = sGridNo; } void UnMarkMovementReserved( SOLDIERTYPE *pSoldier ) { INT32 sNewGridNo; sNewGridNo = GETWORLDINDEXFROMWORLDCOORDS(pSoldier->dYPos, pSoldier->dXPos ); // OK, if NOT in fence anim.... if ( pSoldier->usAnimState == HOPFENCE && pSoldier->sReservedMovementGridNo != sNewGridNo ) { return; } if ( pSoldier->usAnimState == JUMPWINDOWS && pSoldier->sReservedMovementGridNo != sNewGridNo ) { return; } // For single-tiled mercs, unset this gridno // See if we have one reserved! if (!TileIsOutOfBounds(pSoldier->sReservedMovementGridNo)) { gpWorldLevelData[ pSoldier->sReservedMovementGridNo ].uiFlags &= (~MAPELEMENT_MOVEMENT_RESERVED); pSoldier->sReservedMovementGridNo = NOWHERE; } } INT8 TileIsClear( SOLDIERTYPE *pSoldier, INT8 bDirection, INT32 sGridNo, INT8 bLevel ) { INT32 sTempDestGridNo; INT32 sNewGridNo; BOOLEAN fSwapInDoor = FALSE; if (TileIsOutOfBounds(sGridNo)) { return( MOVE_TILE_CLEAR ); } // anv: vehicles can ram people if ( !ARMED_VEHICLE( pSoldier ) && gGameExternalOptions.fAllowCarsDrivingOverPeople && pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE && pSoldier->usSoldierFlagMask2 & SOLDIER_RAM_THROUGH_OBSTACLES ) { return( MOVE_TILE_CLEAR ); } else if ( ARMED_VEHICLE( pSoldier ) && gGameExternalOptions.fAllowTanksDrivingOverPeople && pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) { return( MOVE_TILE_CLEAR ); } SoldierID ubPerson = WhoIsThere2( sGridNo, bLevel ); if ( ubPerson != NOBODY ) { // If this us? if ( ubPerson != pSoldier->ubID ) { // OK, set flag indicating we are blocked by a merc.... if ( pSoldier->bTeam != gbPlayerNum ) // CJC: shouldn't this be in all cases??? //if ( 0 ) { pSoldier->flags.fBlockedByAnotherMerc = TRUE; // Set direction we were trying to goto pSoldier->bBlockedByAnotherMercDirection = bDirection; // Are we only temporarily blocked? // Check if our final destination is = our gridno if ( ( ubPerson->pathing.sFinalDestination == ubPerson->sGridNo ) ) { return( MOVE_TILE_STATIONARY_BLOCKED ); } else { // OK, if buddy who is blocking us is trying to move too... // And we are in opposite directions... if ( ubPerson->flags.fBlockedByAnotherMerc && ubPerson->bBlockedByAnotherMercDirection == gOppositeDirection[ bDirection ] ) { // OK, try and get a path around buddy.... // We have to temporarily make buddy stopped... sTempDestGridNo = ubPerson->pathing.sFinalDestination; ubPerson->pathing.sFinalDestination = ubPerson->sGridNo; if ( PlotPath( pSoldier, pSoldier->pathing.sFinalDestination, NO_COPYROUTE, NO_PLOT, TEMPORARY, pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints ) ) { pSoldier->pathing.bPathStored = FALSE; // OK, make guy go here... pSoldier->EVENT_GetNewSoldierPath( pSoldier->pathing.sFinalDestination, pSoldier->usUIMovementMode ); // Restore final dest.... ubPerson->pathing.sFinalDestination = sTempDestGridNo; pSoldier->flags.fBlockedByAnotherMerc = FALSE; // Is the next tile blocked too? sNewGridNo = NewGridNo( pSoldier->sGridNo, DirectionInc( (UINT8)guiPathingData[ 0 ] ) ); return( TileIsClear( pSoldier, (UINT8)guiPathingData[ 0 ], sNewGridNo, pSoldier->pathing.bLevel ) ); } else { // Not for multi-tiled things... if ( !( pSoldier->flags.uiStatusFlags & SOLDIER_MULTITILE ) ) { // Is the next movement cost for a door? if ( DoorTravelCost( pSoldier, sGridNo, gubWorldMovementCosts[ sGridNo ][ bDirection ][ pSoldier->pathing.bLevel ], (BOOLEAN)( pSoldier->bTeam == gbPlayerNum ), NULL ) == TRAVELCOST_DOOR ) { fSwapInDoor = TRUE; } // If we are to swap and we're near a door, open door first and then close it...? // Swap now! ubPerson->flags.fBlockedByAnotherMerc = FALSE; // Restore final dest.... ubPerson->pathing.sFinalDestination = sTempDestGridNo; // Swap merc positions..... SwapMercPositions( pSoldier, ubPerson ); // With these two guys swapped, they should try and continue on their way.... // Start them both again along their way... pSoldier->EVENT_GetNewSoldierPath( pSoldier->pathing.sFinalDestination, pSoldier->usUIMovementMode ); ubPerson->EVENT_GetNewSoldierPath( ubPerson->pathing.sFinalDestination, ubPerson->usUIMovementMode ); } } } return( MOVE_TILE_TEMP_BLOCKED ); } } else { //return( MOVE_TILE_STATIONARY_BLOCKED ); // ATE: OK, put some smartshere... // If we are waiting for more than a few times, change to stationary... if ( ubPerson->flags.fDelayedMovement >= 105 ) { // Set to special 'I want to walk through people' value pSoldier->flags.fDelayedMovement = 150; return( MOVE_TILE_STATIONARY_BLOCKED ); } if ( ubPerson->sGridNo == ubPerson->pathing.sFinalDestination ) { return( MOVE_TILE_STATIONARY_BLOCKED ); } return( MOVE_TILE_TEMP_BLOCKED ); } } } if ( ( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_MOVEMENT_RESERVED ) ) { if ( gpWorldLevelData[ sGridNo ].ubReservedSoldierID != pSoldier->ubID ) { return( MOVE_TILE_TEMP_BLOCKED ); } } // Are we clear of structs? if ( !NewOKDestination( pSoldier, sGridNo, FALSE, pSoldier->pathing.bLevel ) ) { // ATE: Fence cost is an exclusiuon here.... if ( gubWorldMovementCosts[ sGridNo ][ bDirection ][ pSoldier->pathing.bLevel ] != TRAVELCOST_FENCE ) { // ATE: HIdden structs - we do something here... reveal it! if ( gubWorldMovementCosts[ sGridNo ][ bDirection ][ pSoldier->pathing.bLevel ] == TRAVELCOST_HIDDENOBSTACLE ) { gpWorldLevelData[ sGridNo ].uiFlags|=MAPELEMENT_REVEALED; gpWorldLevelData[ sGridNo ].uiFlags|=MAPELEMENT_REDRAW; SetRenderFlags(RENDER_FLAG_MARKED); RecompileLocalMovementCosts( sGridNo ); } // Unset flag for blocked by soldier... pSoldier->flags.fBlockedByAnotherMerc = FALSE; return( MOVE_TILE_STATIONARY_BLOCKED ); } } // Unset flag for blocked by soldier... pSoldier->flags.fBlockedByAnotherMerc = FALSE; return( MOVE_TILE_CLEAR ); } BOOLEAN HandleNextTile( SOLDIERTYPE *pSoldier, INT8 bDirection, INT32 sGridNo, INT32 sFinalDestTile )//dnl ch53 111009 { INT8 bBlocked; INT16 bOverTerrainType; // Check for blocking if in realtime ///if ( ( gTacticalStatus.uiFlags & REALTIME ) || !( gTacticalStatus.uiFlags & INCOMBAT ) ) // ATE: If not on visible tile, return clear ( for path out of map ) if ( !GridNoOnVisibleWorldTile( sGridNo ) ) { return( TRUE ); } // If animation state is crow, iall is clear if ( pSoldier->usAnimState == CROW_FLY ) { return( TRUE ); } { bBlocked = TileIsClear( pSoldier, bDirection, sGridNo, pSoldier->pathing.bLevel ); // Check if we are blocked... if ( bBlocked != MOVE_TILE_CLEAR ) { // Is the next gridno our destination? // OK: Let's check if we are NOT walking off screen if ( sGridNo == sFinalDestTile && pSoldier->ubWaitActionToDo == 0 && (pSoldier->bTeam == gbPlayerNum || TileIsOutOfBounds(pSoldier->sAbsoluteFinalDestination)) ) { // Yah, well too bad, stop here. SetFinalTile( pSoldier, pSoldier->sGridNo, FALSE ); return( FALSE ); } // CHECK IF they are stationary else if ( bBlocked == MOVE_TILE_STATIONARY_BLOCKED ) { // Stationary, { INT32 sOldFinalDest;//dnl ch53 111009 // Maintain sFinalDest.... sOldFinalDest = pSoldier->pathing.sFinalDestination; #ifdef JA2BETAVERSION if ( gTacticalStatus.uiFlags & INCOMBAT ) { OutputDebugInfoForTurnBasedNextTileWaiting( pSoldier ); } #endif pSoldier->EVENT_StopMerc( pSoldier->sGridNo, pSoldier->ubDirection ); // Restore... pSoldier->pathing.sFinalDestination = sOldFinalDest; SetDelayedTileWaiting( pSoldier, sGridNo, 1 ); return( FALSE ); } } else { { INT32 sOldFinalDest;//dnl ch53 111009 // Maintain sFinalDest.... sOldFinalDest = pSoldier->pathing.sFinalDestination; #ifdef JA2BETAVERSION if ( gTacticalStatus.uiFlags & INCOMBAT ) { OutputDebugInfoForTurnBasedNextTileWaiting( pSoldier ); } #endif pSoldier->EVENT_StopMerc( pSoldier->sGridNo, pSoldier->ubDirection ); // Restore... pSoldier->pathing.sFinalDestination = sOldFinalDest; // Setting to two means: try and wait until this tile becomes free.... SetDelayedTileWaiting( pSoldier, sGridNo, 100 ); } return( FALSE ); } } else { // Mark this tile as reserverd ( until we get there! ) if ( !( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT) ) ) { MarkMovementReserved( pSoldier, sGridNo ); } bOverTerrainType = GetTerrainType( sGridNo ); // WANNE.WATER: If our soldier is not on the ground level and the tile is a "water" tile, then simply set the tile to "FLAT_GROUND" // This should fix "problems" for special modified maps if ( TERRAIN_IS_WATER( bOverTerrainType) && pSoldier->pathing.bLevel > 0 ) bOverTerrainType = FLAT_GROUND; // Check if we are going into water! if ( TERRAIN_IS_WATER( bOverTerrainType) ) { // Check if we are of prone or crawl height and change stance accordingly.... switch( gAnimControl[ pSoldier->usAnimState ].ubHeight ) { case ANIM_PRONE: case ANIM_CROUCH: // Change height to stand pSoldier->flags.fContinueMoveAfterStanceChange = TRUE; SendChangeSoldierStanceEvent( pSoldier, ANIM_STAND ); break; } // Check animation // Change to walking if ( pSoldier->usAnimState == RUNNING ) { pSoldier->ChangeSoldierState( WALKING, 0 , FALSE ); } } } } return( TRUE ); } BOOLEAN HandleNextTileWaiting( SOLDIERTYPE *pSoldier ) { // Buddy is waiting to continue his path INT8 bBlocked, bPathBlocked; INT32 sCost; INT32 sNewGridNo, sCheckGridNo; UINT8 ubDirection, bCauseDirection; SoldierID ubPerson; UINT8 fFlags = 0; if ( pSoldier->flags.fDelayedMovement ) { if ( TIMECOUNTERDONE( pSoldier->timeCounters.NextTileCounter, NEXT_TILE_CHECK_DELAY ) ) { RESETTIMECOUNTER( pSoldier->timeCounters.NextTileCounter, NEXT_TILE_CHECK_DELAY ); // ATE: Allow path to exit grid! if ( pSoldier->ubWaitActionToDo == 1 && gubWaitingForAllMercsToExitCode == WAIT_FOR_MERCS_TO_WALK_TO_GRIDNO ) { gfPlotPathToExitGrid = TRUE; } // Get direction from gridno... bCauseDirection = (INT8)GetDirectionToGridNoFromGridNo( pSoldier->sGridNo, pSoldier->sDelayedMovementCauseGridNo ); bBlocked = TileIsClear( pSoldier, bCauseDirection, pSoldier->sDelayedMovementCauseGridNo, pSoldier->pathing.bLevel ); // If we are waiting for a temp blockage.... continue to wait if ( pSoldier->flags.fDelayedMovement >= 100 && bBlocked == MOVE_TILE_TEMP_BLOCKED ) { // ATE: Increment 1 pSoldier->flags.fDelayedMovement++; // Are we close enough to give up? ( and are a pc ) if ( pSoldier->flags.fDelayedMovement > 120 ) { // Quit... SetFinalTile( pSoldier, pSoldier->sGridNo, TRUE ); pSoldier->flags.fDelayedMovement = FALSE; } gfPlotPathToExitGrid = FALSE; return( TRUE ); } // Try new path if anything but temp blockage! if ( bBlocked != MOVE_TILE_TEMP_BLOCKED ) { // Set to normal delay if ( pSoldier->flags.fDelayedMovement >= 100 && pSoldier->flags.fDelayedMovement != 150 ) { pSoldier->flags.fDelayedMovement = 1; } // Default to pathing through people fFlags = PATH_THROUGH_PEOPLE; // Now, if we are in the state where we are desparently trying to get out... // Use other flag // CJC: path-through-people includes ignoring person at dest /* if ( pSoldier->flags.fDelayedMovement >= 150 ) { fFlags = PATH_IGNORE_PERSON_AT_DEST; } */ // Check destination first! if ( pSoldier->sAbsoluteFinalDestination == pSoldier->pathing.sFinalDestination ) { // on last lap of scripted move, make sure we get to final dest sCheckGridNo = pSoldier->sAbsoluteFinalDestination; } else if (!NewOKDestination( pSoldier, pSoldier->pathing.sFinalDestination, TRUE, pSoldier->pathing.bLevel )) { if ( pSoldier->flags.fDelayedMovement >= 150 ) { // OK, look around dest for the first one! sCheckGridNo = FindGridNoFromSweetSpot( pSoldier, pSoldier->pathing.sFinalDestination, 6, &ubDirection ); if (TileIsOutOfBounds(sCheckGridNo)) { // If this is nowhere, try harder! sCheckGridNo = FindGridNoFromSweetSpot( pSoldier, pSoldier->pathing.sFinalDestination, 16, &ubDirection ); } } else { // OK, look around dest for the first one! sCheckGridNo = FindGridNoFromSweetSpotThroughPeople( pSoldier, pSoldier->pathing.sFinalDestination, 6, &ubDirection ); if (TileIsOutOfBounds(sCheckGridNo)) { // If this is nowhere, try harder! sCheckGridNo = FindGridNoFromSweetSpotThroughPeople( pSoldier, pSoldier->pathing.sFinalDestination, 16, &ubDirection ); } } } else { sCheckGridNo = pSoldier->pathing.sFinalDestination; } sCost = FindBestPath( pSoldier, sCheckGridNo, pSoldier->pathing.bLevel, pSoldier->usUIMovementMode, NO_COPYROUTE, fFlags ); // Can we get there if ( sCost > 0 ) { // Is the next tile blocked too? sNewGridNo = NewGridNo( pSoldier->sGridNo, DirectionInc( (UINT8)guiPathingData[ 0 ] ) ); bPathBlocked = TileIsClear( pSoldier, (UINT8)guiPathingData[ 0 ], sNewGridNo, pSoldier->pathing.bLevel ); if ( bPathBlocked == MOVE_TILE_STATIONARY_BLOCKED ) { // Try to path around everyone except dest person sCost = FindBestPath( pSoldier, sCheckGridNo, pSoldier->pathing.bLevel, pSoldier->usUIMovementMode, NO_COPYROUTE, PATH_IGNORE_PERSON_AT_DEST ); // Is the next tile in this new path blocked too? sNewGridNo = NewGridNo( pSoldier->sGridNo, DirectionInc( (UINT8)guiPathingData[ 0 ] ) ); bPathBlocked = TileIsClear( pSoldier, (UINT8)guiPathingData[ 0 ], sNewGridNo, pSoldier->pathing.bLevel ); // now working with a path which does not go through people pSoldier->ubDelayedMovementFlags &= (~DELAYED_MOVEMENT_FLAG_PATH_THROUGH_PEOPLE); } else { // path through people worked fine if ( pSoldier->flags.fDelayedMovement < 150 ) { pSoldier->ubDelayedMovementFlags |= DELAYED_MOVEMENT_FLAG_PATH_THROUGH_PEOPLE; } } // Are we clear? if ( bPathBlocked == MOVE_TILE_CLEAR ) { //pSoldier->flags.fDelayedMovement = FALSE; // ATE: THis will get set in EENT_GetNewSoldierPath.... pSoldier->aiData.usActionData = sCheckGridNo; pSoldier->pathing.bPathStored = FALSE; pSoldier->EVENT_GetNewSoldierPath( sCheckGridNo, pSoldier->usUIMovementMode ); gfPlotPathToExitGrid = FALSE; return( TRUE ); } } pSoldier->flags.fDelayedMovement++; if ( pSoldier->flags.fDelayedMovement == 99 ) { // Cap at 99 pSoldier->flags.fDelayedMovement = 99; } // Do we want to force a swap? if (pSoldier->flags.fDelayedMovement == 3 && (!TileIsOutOfBounds(pSoldier->sAbsoluteFinalDestination) || gTacticalStatus.fAutoBandageMode) ) { // with person who is in the way? ubPerson = WhoIsThere2( pSoldier->sDelayedMovementCauseGridNo, pSoldier->pathing.bLevel ); // if either on a mission from god, or two AI guys not on stationary... if ( ubPerson != NOBODY && ( pSoldier->ubQuoteRecord != 0 || ( pSoldier->bTeam != gbPlayerNum && pSoldier->aiData.bOrders != STATIONARY && ubPerson->bTeam != gbPlayerNum && ubPerson->aiData.bOrders != STATIONARY ) || (pSoldier->bTeam == gbPlayerNum && gTacticalStatus.fAutoBandageMode && !(ubPerson->bTeam == CIV_TEAM && ubPerson->aiData.bOrders == STATIONARY ) ) ) ) { // Swap now! //ubPerson->flags.fBlockedByAnotherMerc = FALSE; // Restore final dest.... //ubPerson->pathing.sFinalDestination = sTempDestGridNo; // Swap merc positions..... SwapMercPositions( pSoldier, ubPerson ); // With these two guys swapped, we should try to continue on our way.... pSoldier->flags.fDelayedMovement = FALSE; // We must calculate the path here so that we can give it the "through people" parameter if ( gTacticalStatus.fAutoBandageMode && TileIsOutOfBounds(pSoldier->sAbsoluteFinalDestination)) { FindBestPath( pSoldier, pSoldier->pathing.sFinalDestination, pSoldier->pathing.bLevel, pSoldier->usUIMovementMode, COPYROUTE, PATH_THROUGH_PEOPLE ); } else if (!TileIsOutOfBounds(pSoldier->sAbsoluteFinalDestination) && !FindBestPath( pSoldier, pSoldier->sAbsoluteFinalDestination, pSoldier->pathing.bLevel, pSoldier->usUIMovementMode, COPYROUTE, PATH_THROUGH_PEOPLE ) ) { // check to see if we're there now! if ( pSoldier->sGridNo == pSoldier->sAbsoluteFinalDestination ) { NPCReachedDestination( pSoldier, FALSE ); pSoldier->aiData.bNextAction = AI_ACTION_WAIT; pSoldier->aiData.usNextActionData = 500; gfPlotPathToExitGrid = FALSE; return( TRUE ); } } pSoldier->pathing.bPathStored = TRUE; pSoldier->EVENT_GetNewSoldierPath( pSoldier->sAbsoluteFinalDestination, pSoldier->usUIMovementMode ); //EVENT_GetNewSoldierPath( ubPerson, ubPerson->pathing.sFinalDestination, ubPerson->usUIMovementMode ); } } // Are we close enough to give up? ( and are a pc ) if ( pSoldier->flags.fDelayedMovement > 20 && pSoldier->flags.fDelayedMovement != 150) { if ( PythSpacesAway( pSoldier->sGridNo, pSoldier->pathing.sFinalDestination ) < 5 && pSoldier->bTeam == gbPlayerNum ) { // Quit... SetFinalTile( pSoldier, pSoldier->sGridNo, FALSE ); pSoldier->flags.fDelayedMovement = FALSE; } } // Are we close enough to give up? ( and are a pc ) if ( pSoldier->flags.fDelayedMovement > 170 ) { if ( PythSpacesAway( pSoldier->sGridNo, pSoldier->pathing.sFinalDestination ) < 5 && pSoldier->bTeam == gbPlayerNum ) { // Quit... SetFinalTile( pSoldier, pSoldier->sGridNo, FALSE ); pSoldier->flags.fDelayedMovement = FALSE; } } } } } gfPlotPathToExitGrid = FALSE; return( TRUE ); } BOOLEAN TeleportSoldier( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fForce ) { INT16 sX, sY; // Check dest... if ( NewOKDestination( pSoldier, sGridNo, TRUE, 0 ) || fForce ) { // TELEPORT TO THIS LOCATION! ConvertGridNoToCenterCellXY(sGridNo, &sX, &sY); pSoldier->EVENT_SetSoldierPosition( (FLOAT) sX, (FLOAT) sY ); pSoldier->pathing.sFinalDestination = sGridNo; // Make call to FOV to update items... RevealRoofsAndItems(pSoldier, TRUE, TRUE, pSoldier->pathing.bLevel, TRUE ); // Handle sight! HandleSight(pSoldier,SIGHT_LOOK | SIGHT_RADIO); // Cancel services... pSoldier->GivingSoldierCancelServices( ); // Change light.... if ( pSoldier->pathing.bLevel == 0 ) { if(pSoldier->iLight!=(-1)) LightSpriteRoofStatus(pSoldier->iLight, FALSE ); } else { if(pSoldier->iLight!=(-1)) LightSpriteRoofStatus(pSoldier->iLight, TRUE ); } return( TRUE ); } return( FALSE ); } // Swaps 2 soldier positions... void SwapMercPositions( SOLDIERTYPE *pSoldier1, SOLDIERTYPE *pSoldier2 ) { INT32 sGridNo1, sGridNo2; // OK, save positions... sGridNo1 = pSoldier1->sGridNo; sGridNo2 = pSoldier2->sGridNo; // OK, remove each..... pSoldier1->RemoveSoldierFromGridNo( ); pSoldier2->RemoveSoldierFromGridNo( ); // OK, test OK destination for each....... if ( NewOKDestination( pSoldier1, sGridNo2, TRUE, 0 ) && NewOKDestination( pSoldier2, sGridNo1, TRUE, 0 ) ) { // OK, call teleport function for each....... TeleportSoldier( pSoldier1, sGridNo2, FALSE ); TeleportSoldier( pSoldier2, sGridNo1, FALSE ); } else { // Place back... TeleportSoldier( pSoldier1, sGridNo1, TRUE ); TeleportSoldier( pSoldier2, sGridNo2, TRUE ); } } BOOLEAN CanExchangePlaces( SOLDIERTYPE *pSoldier1, SOLDIERTYPE *pSoldier2, BOOLEAN fShow ) { // NB checks outside of this function if ( EnoughPoints( pSoldier1, APBPConstants[AP_EXCHANGE_PLACES], 0, fShow ) ){ if ( EnoughPoints( pSoldier2, APBPConstants[AP_EXCHANGE_PLACES], 0, fShow ) ){ if ( ( gAnimControl[ pSoldier2->usAnimState ].uiFlags & ANIM_MOVING ) ) return( FALSE ); if ( ( gAnimControl[ pSoldier1->usAnimState ].uiFlags & ANIM_MOVING ) && !(gTacticalStatus.uiFlags & INCOMBAT) ) return( FALSE ); if ( pSoldier2->bSide == 0 ) return( TRUE ); // hehe - don't allow animals to exchange places if ( pSoldier2->flags.uiStatusFlags & ( SOLDIER_ANIMAL ) ) return( FALSE ); // must NOT be hostile, must NOT have stationary orders OR militia team, must be >= OKLIFE if( pSoldier2->aiData.bNeutral && pSoldier2->stats.bLife >= OKLIFE && pSoldier2->ubCivilianGroup != HICKS_CIV_GROUP && ( ( pSoldier2->aiData.bOrders != STATIONARY || pSoldier2->bTeam == MILITIA_TEAM ) || ( !TileIsOutOfBounds(pSoldier2->sAbsoluteFinalDestination) && pSoldier2->sAbsoluteFinalDestination != pSoldier2->sGridNo ) ) ) return( TRUE ); if ( fShow ){ if ( pSoldier2->ubProfile == NO_PROFILE ) ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, TacticalStr[ REFUSE_EXCHANGE_PLACES ] ); else ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, gzLateLocalizedString[3], pSoldier2->name ); } // ATE: OK, reduce this guy's next ai counter.... pSoldier2->uiAIDelay = 100; return( FALSE ); }else{ return( FALSE ); } } // if SirTech wouldn't setup your nested if statements so messily then perhaps wierd code like this // could be avoided... cleaned up the ifs too (jonathanl) /* else { return( FALSE ); } return( TRUE ); */ return FALSE; }