#ifdef PRECOMPILEDHEADERS #include "Strategic All.h" #else #include "Map Screen Helicopter.h" #include "LaptopSave.h" #include "Vehicles.h" #include "Finances.h" #include "Quests.h" #include "Game Clock.h" #include "strategic.h" #include "Queen Command.h" #include "Strategic Pathing.h" #include "Random.h" #include "Game Event Hook.h" #include "String.h" #include "Dialogue Control.h" #include "Message.h" #include "Strategic Movement.h" #include "Soldier Profile.h" #include "Assignments.h" #include "PreBattle Interface.h" #include "strategicmap.h" #include "worlddef.h" #include "worldman.h" #include "tiledat.h" #include "Map Screen Interface.h" #include "Text.h" #include "Squads.h" #include "Player Command.h" #include "Sound Control.h" #include "GameSettings.h" #include "meanwhile.h" #include "Map Screen Interface Border.h" #include "Strategic Event Handler.h" #include "Overhead.h" #include "Soldier Create.h" #include "renderworld.h" #include "soundman.h" #include "Isometric Utils.h" #include "Scheduling.h" #endif // the amounts of time to wait for hover stuff #define TIME_DELAY_FOR_HOVER_WAIT 10 // minutes #define TIME_DELAY_FOR_HOVER_WAIT_TOO_LONG 20 // mintues #define MIN_DAYS_BETWEEN_SKYRIDER_MONOLOGUES 1 // refuel delay #define REFUEL_HELICOPTER_DELAY 30 // minutes // total number of sectors one can go //#define MAX_HELICOPTER_DISTANCE 25 // maximum chance out of a hundred per unsafe sector that a SAM site in decent working condition will hit Skyrider #define MAX_SAM_SITE_ACCURACY 33 extern FACETYPE *gpCurrentTalkingFace; extern UINT8 gubCurrentTalkingID; // current temp path for dest char extern PathStPtr pTempHelicopterPath; extern UINT8 ubSAMControlledSectors[ MAP_WORLD_Y ][ MAP_WORLD_X ]; // the seating capacities extern INT32 iSeatingCapacities[]; // the static NPC dialogue faces extern UINT32 uiExternalStaticNPCFaces[]; // the squad mvt groups extern INT8 SquadMovementGroups[ ]; // whether helicopted variables have been set up BOOLEAN fSkyRiderSetUp = FALSE; // plotting for a helicopter BOOLEAN fPlotForHelicopter = FALSE; // is the helicopter available to player? BOOLEAN fHelicopterAvailable = FALSE; // helicopter vehicle id INT32 iHelicopterVehicleId = -1; // helicopter icon UINT32 guiHelicopterIcon; // total distance travelled //INT32 iTotalHeliDistanceSinceRefuel = 0; // total owed to player INT32 iTotalAccumulatedCostByPlayer = 0; // whether or not skyrider is alive and well? and on our side yet? BOOLEAN fSkyRiderAvailable = FALSE; #ifdef JA2TESTVERSION BOOLEAN fSAMSitesDisabledFromAttackingPlayer = FALSE; #endif // helicopter destroyed BOOLEAN fHelicopterDestroyed = FALSE; // list of sector locations where SkyRider can be refueled UINT8 ubRefuelList[ NUMBER_OF_REFUEL_SITES ][ 2 ] = { { 13, 2 }, // Drassen airport { 6, 9 }, // Estoni }; INT16 sRefuelStartGridNo[ NUMBER_OF_REFUEL_SITES ] ={ 9001, // drassen 13067, // estoni }; // whether or not helicopter can refuel at this site BOOLEAN fRefuelingSiteAvailable[ NUMBER_OF_REFUEL_SITES ] = { FALSE, FALSE }; // is the heli in the air? BOOLEAN fHelicopterIsAirBorne = FALSE; // is the pilot returning straight to base? BOOLEAN fHeliReturnStraightToBase = FALSE; // heli hovering BOOLEAN fHoveringHelicopter = FALSE; // time started hovering UINT32 uiStartHoverTime = 0; // what state are skyrider's monologues in in? UINT32 guiHelicopterSkyriderTalkState = 0; // the flags for skyrider events BOOLEAN fShowEstoniRefuelHighLight = FALSE; BOOLEAN fShowOtherSAMHighLight = FALSE; BOOLEAN fShowDrassenSAMHighLight = FALSE; BOOLEAN fShowCambriaHospitalHighLight = FALSE; UINT32 guiTimeOfLastSkyriderMonologue = 0; UINT8 gubHelicopterHitsTaken = 0; BOOLEAN gfSkyriderSaidCongratsOnTakingSAM = FALSE; UINT8 gubPlayerProgressSkyriderLastCommentedOn = 0; // skyrider placeholder SOLDIERTYPE SoldierSkyRider; SOLDIERTYPE *pSkyRider; // helicopter char dialogue BOOLEAN HeliCharacterDialogue( SOLDIERTYPE *pSoldier, UINT16 usQuoteNum ); // does skyrider notice bad guys in sector? BOOLEAN DoesSkyriderNoticeEnemiesInSector( UINT8 ubNumEnemies ); // are we at the end of the path for the helicopter BOOLEAN EndOfHelicoptersPath( void ); // find the location sector of closest refuel point for heli..and the criteria if the sector must be under the players control INT32 FindLocationOfClosestRefuelSite( BOOLEAN fMustBeAvailable ); // add the tactical heli graphic void AddHelicopterToMaps( BOOLEAN fAdd, UINT8 ubSite ); void PaySkyriderBill( void ); void MakeHeliReturnToBase( void ); void HandleSkyRiderMonologueAboutDrassenSAMSite( UINT32 uiSpecialCode ); void HandleSkyRiderMonologueAboutCambriaHospital( UINT32 uiSpecialCode ); void HandleSkyRiderMonologueAboutOtherSAMSites( UINT32 uiSpecialCode ); void HandleSkyRiderMonologueAboutEstoniRefuel( UINT32 uiSpecialCode ); void InitializeHelicopter( void ) { // must be called whenever a new game starts up! fHelicopterAvailable = FALSE; iHelicopterVehicleId = -1; fSkyRiderAvailable = FALSE; fSkyRiderSetUp = FALSE; pSkyRider = NULL; SoldierSkyRider.initialize(); fHelicopterIsAirBorne = FALSE; fHeliReturnStraightToBase = FALSE; fHoveringHelicopter = FALSE; uiStartHoverTime = 0; fPlotForHelicopter = FALSE; pTempHelicopterPath = NULL; // iTotalHeliDistanceSinceRefuel = 0; iTotalAccumulatedCostByPlayer = 0; fHelicopterDestroyed = FALSE; guiHelicopterSkyriderTalkState = 0; guiTimeOfLastSkyriderMonologue = 0; fShowEstoniRefuelHighLight = FALSE; fShowOtherSAMHighLight = FALSE; fShowDrassenSAMHighLight = FALSE; fShowCambriaHospitalHighLight = FALSE; gfSkyriderEmptyHelpGiven = FALSE; gubHelicopterHitsTaken = 0; gfSkyriderSaidCongratsOnTakingSAM = FALSE; gubPlayerProgressSkyriderLastCommentedOn = 0; } BOOLEAN AddSoldierToHelicopter( SOLDIERTYPE *pSoldier ) { // attempt to add soldier to helicopter if( iHelicopterVehicleId == -1 ) { // no heli yet return( FALSE ); } // check if heli is in motion or if on the ground if( ( fHelicopterIsAirBorne == TRUE ) && (fHoveringHelicopter == FALSE ) ) { return( FALSE ); } // is the heli returning to base?..he ain't waiting if so if( fHeliReturnStraightToBase == TRUE ) { return( FALSE ); } // attempt to add to vehicle return( PutSoldierInVehicle( pSoldier, ( INT8 )iHelicopterVehicleId ) ); } BOOLEAN RemoveSoldierFromHelicopter( SOLDIERTYPE *pSoldier ) { // attempt to add soldier to helicopter if( iHelicopterVehicleId == -1 ) { // no heli yet return( FALSE ); } // check if heli is in motion or if on the ground if( ( fHelicopterIsAirBorne == TRUE ) && (fHoveringHelicopter == FALSE ) ) { return( FALSE ); } // is the heli returning to base?..he ain't waiting if so if( fHeliReturnStraightToBase == TRUE ) { return( FALSE ); } pSoldier->sSectorX = pVehicleList[ iHelicopterVehicleId ].sSectorX; pSoldier->sSectorY = pVehicleList[ iHelicopterVehicleId ].sSectorY; pSoldier->bSectorZ = 0; // reset between sectors pSoldier->flags.fBetweenSectors = FALSE; // remove from the vehicle return( TakeSoldierOutOfVehicle( pSoldier ) ); } BOOLEAN HandleHeliEnteringSector( INT16 sX, INT16 sY ) { UINT8 ubNumEnemies; // check for SAM attack upon the chopper. If it's destroyed by the attack, do nothing else here if( HandleSAMSiteAttackOfHelicopterInSector( sX, sY ) == TRUE ) { // destroyed return( TRUE ); } // count how many enemies are camped there or passing through ubNumEnemies = NumEnemiesInSector( sX, sY ); // any baddies? if( ubNumEnemies > 0 ) { // if the player didn't know about these prior to the chopper's arrival if( WhatPlayerKnowsAboutEnemiesInSector( sX, sY ) == KNOWS_NOTHING ) { // but Skyrider notices them if ( DoesSkyriderNoticeEnemiesInSector( ubNumEnemies ) == TRUE ) { // if just passing through (different quotes are used below if it's his final destination) if( !EndOfHelicoptersPath( ) ) { // stop time compression and inform player that there are enemies in the sector below StopTimeCompression(); if( Random( 2 ) ) { HeliCharacterDialogue( pSkyRider, ENEMIES_SPOTTED_EN_ROUTE_IN_FRIENDLY_SECTOR_A ); } else { HeliCharacterDialogue( pSkyRider, ENEMIES_SPOTTED_EN_ROUTE_IN_FRIENDLY_SECTOR_B ); } } // make their presence appear on the map while Skyrider remains in the sector SectorInfo[ SECTOR( sX, sY ) ].uiFlags |= SF_SKYRIDER_NOTICED_ENEMIES_HERE; } } } // player pays for travel if Skyrider is NOT returning to base (even if empty while scouting/going for pickup) if ( fHeliReturnStraightToBase == FALSE ) { // charge cost for flying another sector iTotalAccumulatedCostByPlayer += GetCostOfPassageForHelicopter( sX, sY ); } // accumulate distance travelled // AddSectorToHelicopterDistanceTravelled( ); // check if heli has any real path left if( EndOfHelicoptersPath( ) ) { // start hovering StartHoverTime( ); // if sector is safe, or Skyrider MUST land anyway (returning to base) if ( ( ubNumEnemies == 0 ) || fHeliReturnStraightToBase ) { // if he has passengers, or he's not going straight to base, tell player he's arrived // (i.e. don't say anything nor stop time compression if he's empty and just returning to base) if ( ( GetNumberOfPassengersInHelicopter() > 0 ) || !fHeliReturnStraightToBase ) { // arrived at destination if(gGameSettings.fOptions[ TOPTION_SILENT_SKYRIDER ] == FALSE) HeliCharacterDialogue( pSkyRider, ARRIVED_IN_NON_HOSTILE_SECTOR ); StopTimeCompression(); } // destination reached, payment due. If player can't pay, mercs get kicked off and heli flies to base! PaySkyriderBill(); } else { // Say quote: "Gonna have to abort. Enemies below" if(gGameSettings.fOptions[ TOPTION_SILENT_SKYRIDER ] == FALSE) HeliCharacterDialogue( pSkyRider, ARRIVED_IN_HOSTILE_SECTOR ); StopTimeCompression(); } if( CheckForArrivalAtRefuelPoint( ) ) { ReFuelHelicopter( ); } } return( FALSE ); } /* INT32 GetTotalDistanceHelicopterCanTravel( void ) { return( MAX_HELICOPTER_DISTANCE ); } INT32 HowFarHelicopterhasTravelledSinceRefueling( void ) { // return total distance return( iTotalHeliDistanceSinceRefuel ); } INT32 HowFurtherCanHelicopterTravel( void ) { // how many sectors further can we go on remaining fuel? return( MAX_HELICOPTER_DISTANCE - ( HowFarHelicopterhasTravelledSinceRefueling( ) + DistanceOfIntendedHelicopterPath( ) ) ); } void AddSectorToHelicopterDistanceTravelled( void ) { // up the distance iTotalHeliDistanceSinceRefuel++; //reset hover time uiStartHoverTime = 0; return; } */ INT32 LocationOfNearestRefuelPoint( BOOLEAN fNotifyPlayerIfNoSafeLZ ) { INT32 iClosestLocation = -1; // try to find one, any one under the players control iClosestLocation = FindLocationOfClosestRefuelSite( TRUE ); // no go?...then find if( iClosestLocation == -1 ) { if( fNotifyPlayerIfNoSafeLZ ) { // no refueling sites available, might wanna warn player about this ScreenMsg( FONT_MCOLOR_DKRED, MSG_INTERFACE, pHelicopterEtaStrings[ 5 ] ); } // find the closest location regardless iClosestLocation = FindLocationOfClosestRefuelSite( FALSE ); } // always returns a valid refuel point, picking a hostile one if unavoidable Assert( iClosestLocation != -1 ); return( iClosestLocation ); } INT32 FindLocationOfClosestRefuelSite( BOOLEAN fMustBeAvailable ) { INT32 iShortestDistance = 9999; INT32 iCounter = 0; INT32 iDistance = 9999; INT32 iClosestLocation = -1; // find shortest distance to refuel site for( iCounter = 0; iCounter < NUMBER_OF_REFUEL_SITES; iCounter++ ) { // if this refuelling site is available if( ( fRefuelingSiteAvailable[ iCounter ] ) || ( fMustBeAvailable == FALSE ) ) { // find if sector is under control, find distance from heli to it iDistance = ( INT32 )FindStratPath( ( INT16 )( CALCULATE_STRATEGIC_INDEX( pVehicleList[ iHelicopterVehicleId ].sSectorX , pVehicleList[ iHelicopterVehicleId ].sSectorY ) ), ( INT16 )( CALCULATE_STRATEGIC_INDEX( ubRefuelList[ iCounter ][ 0 ], ubRefuelList[ iCounter ][ 1 ] ) ) , pVehicleList[ iHelicopterVehicleId ].ubMovementGroup, FALSE ); if( iDistance < iShortestDistance ) { // shorter, copy over iShortestDistance = iDistance; iClosestLocation = iCounter; } } } // return the location return( iClosestLocation ); } INT32 DistanceToNearestRefuelPoint( INT16 sX, INT16 sY ) { INT32 iClosestLocation; INT32 iDistance; // don't notify player during these checks! iClosestLocation = LocationOfNearestRefuelPoint( FALSE ); iDistance = ( INT32 )FindStratPath( ( INT16 )( CALCULATE_STRATEGIC_INDEX( sX, sY ) ), ( INT16 )( CALCULATE_STRATEGIC_INDEX( ubRefuelList[ iClosestLocation ][ 0 ], ubRefuelList[ iClosestLocation ][ 1 ] ) ) , pVehicleList[ iHelicopterVehicleId ].ubMovementGroup, FALSE ); return( iDistance ); } /* BOOLEAN IsSectorOutOfTheWay( INT16 sX, INT16 sY ) { // check distance to nearest refuel point if( DistanceToNearestRefuelPoint( sX, sY ) > HowFurtherCanHelicopterTravel( ) ) { return( TRUE ); } return( FALSE ); } */ void ReFuelHelicopter( void ) { // land, pay the man, and refuel LandHelicopter( ); /* AddStrategicEvent( EVENT_HELICOPTER_DONE_REFUELING, GetWorldTotalMin() + REFUEL_HELICOPTER_DELAY, 0 ); // reset distance traveled iTotalHeliDistanceSinceRefuel = 0; */ return; } INT32 GetCostOfPassageForHelicopter( INT16 sX, INT16 sY ) { // check if sector is air controlled or not, if so, then normal cost, otherwise increase the cost INT32 iCost = 0; // if they don't control it if( StrategicMap[ CALCULATE_STRATEGIC_INDEX( sX, sY ) ].fEnemyAirControlled == FALSE ) { iCost = COST_AIRSPACE_SAFE; } else { iCost = COST_AIRSPACE_UNSAFE; } return( iCost ); } void SkyriderDestroyed( void ) { // remove any arrival events for the helicopter's group DeleteStrategicEvent( EVENT_GROUP_ARRIVAL, pVehicleList[ iHelicopterVehicleId ].ubMovementGroup ); // kill eveyone on board KillAllInVehicle( iHelicopterVehicleId ); // kill skyrider fSkyRiderAvailable = FALSE; SoldierSkyRider.stats.bLife = 0; gMercProfiles[ SKYRIDER ].bLife = 0; // heli no longer available fHelicopterAvailable = FALSE; // destroy helicopter fHelicopterDestroyed = TRUE; // zero out balance due gMercProfiles[ SKYRIDER ].iBalance = 0; // iTotalHeliDistanceSinceRefuel = 0; iTotalAccumulatedCostByPlayer = 0; // remove vehicle and reset RemoveVehicleFromList( iHelicopterVehicleId ); iHelicopterVehicleId = -1; return; } BOOLEAN CanHelicopterFly( void ) { // check if heli is available for flight? // is the heli available if( fHelicopterAvailable == FALSE ) { return( FALSE ); } if( VehicleIdIsValid( iHelicopterVehicleId ) == FALSE ) { return( FALSE ); } /* // travelled too far? if( iTotalHeliDistanceSinceRefuel > MAX_HELICOPTER_DISTANCE ) { return( FALSE ); } */ // is the pilot alive, well, and willing to help us? if( IsHelicopterPilotAvailable( ) == FALSE ) { return( FALSE ); } if( fHeliReturnStraightToBase == TRUE ) { return ( FALSE ); } // grounded by enemies in sector? if ( CanHelicopterTakeOff() == FALSE ) { return ( FALSE ); } // everything A-OK! return( TRUE ); } BOOLEAN IsHelicopterPilotAvailable( void ) { // what is state of skyrider? if( fSkyRiderAvailable == FALSE ) { return( FALSE ); } // owe any money to skyrider? if( gMercProfiles[ SKYRIDER ].iBalance < 0 ) { return( FALSE ); } // Drassen too disloyal to wanna help player? if ( CheckFact( FACT_LOYALTY_LOW, SKYRIDER ) ) { return ( FALSE ); } return( TRUE ); } void LandHelicopter( void ) { // set the helictoper down, call arrive callback for this mvt group fHelicopterIsAirBorne = FALSE; // no longer hovering fHoveringHelicopter = FALSE; // reset fact that we might have returned straight here fHeliReturnStraightToBase = FALSE; HandleHelicopterOnGroundGraphic( ); HandleHelicopterOnGroundSkyriderProfile( ); // if we'll be unable to take off again (because there are enemies in the sector, or we owe pilot money) if ( CanHelicopterFly() == FALSE ) { // kick everyone out! MoveAllInHelicopterToFootMovementGroup( ); } else { // play meanwhile scene if it hasn't been used yet HandleKillChopperMeanwhileScene(); } } void TakeOffHelicopter( void ) { // heli in the air fHelicopterIsAirBorne = TRUE; // no longer hovering fHoveringHelicopter = FALSE; HandleHelicopterOnGroundGraphic( ); HandleHelicopterOnGroundSkyriderProfile( ); } void StartHoverTime( void ) { // start hover in this sector fHoveringHelicopter = TRUE; // post event for x mins in future, save start time, if event time - delay = start time, then hover has gone on too long uiStartHoverTime = GetWorldTotalMin( ); // post event..to call handle hover AddStrategicEvent( EVENT_HELICOPTER_HOVER_TOO_LONG, GetWorldTotalMin() + TIME_DELAY_FOR_HOVER_WAIT, 0 ); return; } void HandleHeliHoverLong( void ) { // post message about hovering too long if( fHoveringHelicopter ) { // proper event, post next one AddStrategicEvent( EVENT_HELICOPTER_HOVER_WAY_TOO_LONG, uiStartHoverTime + TIME_DELAY_FOR_HOVER_WAIT_TOO_LONG, 0 ); // inform player HeliCharacterDialogue( pSkyRider, HOVERING_A_WHILE ); // stop time compression if it's on StopTimeCompression( ); } else { // reset uiStartHoverTime = 0; } } void HandleHeliHoverTooLong( void ) { // reset hover time uiStartHoverTime = 0; if( fHoveringHelicopter == FALSE ) { return; } // hovered too long, inform player heli is returning to base HeliCharacterDialogue( pSkyRider, RETURN_TO_BASE ); // If the sector is safe if ( NumEnemiesInSector( pVehicleList[ iHelicopterVehicleId ].sSectorX, pVehicleList[ iHelicopterVehicleId ].sSectorY ) == 0 ) { // kick everyone out! MoveAllInHelicopterToFootMovementGroup( ); } MakeHeliReturnToBase(); } // check if anyone in the chopper sees any baddies in sector BOOLEAN DoesSkyriderNoticeEnemiesInSector( UINT8 ubNumEnemies ) { UINT8 ubChance; // is the pilot and heli around? if( CanHelicopterFly( ) == FALSE ) { return( FALSE ); } // if there aren't any, he obviously won't see them if( ubNumEnemies == 0 ) { return( FALSE ); } // figure out what the chance is of seeing them // make this relatively accurate most of the time, to encourage helicopter scouting by making it useful ubChance = 60 + ubNumEnemies; if( PreRandom( 100 ) < ubChance ) { return( TRUE ); } return( FALSE ); } // if the heli is on the move, what is the distance it will move..the length of the merc path, less the first node INT32 DistanceOfIntendedHelicopterPath( void ) { PathStPtr pNode = NULL; INT32 iLength = 0; if( CanHelicopterFly( ) == FALSE ) { // big number, no go return( 9999 ); } pNode = pVehicleList[ iHelicopterVehicleId ].pMercPath; // any path yet? if( pNode != NULL ) { while( pNode->pNext ) { iLength++; pNode = pNode->pNext; } } pNode = MoveToBeginningOfPathList( pTempHelicopterPath ); // any path yet? if( pNode != NULL ) { while( pNode->pNext ) { iLength++; pNode = pNode->pNext; } } return( iLength ); } BOOLEAN CheckForArrivalAtRefuelPoint( void ) { // check if this is our final destination if ( GetLengthOfPath( pVehicleList[ iHelicopterVehicleId ].pMercPath ) > 0 ) { return( FALSE ); } // check if we're at a refuel site if( DistanceToNearestRefuelPoint( pVehicleList[ iHelicopterVehicleId ].sSectorX, pVehicleList[ iHelicopterVehicleId ].sSectorY ) > 0 ) { // not at a refuel point return( FALSE ); } // we are at a refuel site return( TRUE ); } void SetUpHelicopterForMovement( void ) { // check if helicopter vehicle has a mvt group, if not, assign one in this sector INT32 iCounter = 0; // if no group, create one for vehicle if( pVehicleList[ iHelicopterVehicleId ].ubMovementGroup == 0 ) { // get the vehicle a mvt group pVehicleList[ iHelicopterVehicleId ].ubMovementGroup = CreateNewVehicleGroupDepartingFromSector( ( UINT8 )( pVehicleList[ iHelicopterVehicleId ].sSectorX ), ( UINT8 )( pVehicleList[ iHelicopterVehicleId ].sSectorY ), iHelicopterVehicleId ); // add everyone in vehicle to this mvt group for( iCounter = 0; iCounter < iSeatingCapacities[ pVehicleList[ iHelicopterVehicleId ].ubVehicleType ]; iCounter++ ) { if( pVehicleList[ iHelicopterVehicleId ].pPassengers[ iCounter ] != NULL ) { // add character AddPlayerToGroup( pVehicleList[ iHelicopterVehicleId ].ubMovementGroup, pVehicleList[ iHelicopterVehicleId ].pPassengers[ iCounter ] ); } } } } BOOLEAN HeliCharacterDialogue( SOLDIERTYPE *pSoldier, UINT16 usQuoteNum ) { // ARM: we could just return, but since various flags are often being set it's safer to honk so it gets fixed right! Assert( fSkyRiderAvailable ); return( CharacterDialogue( SKYRIDER, usQuoteNum, uiExternalStaticNPCFaces[ SKYRIDER_EXTERNAL_FACE ], DIALOGUE_EXTERNAL_NPC_UI, FALSE, FALSE ) ); } INT32 GetNumberOfPassengersInHelicopter( void ) { INT32 iNumber = 0; if( iHelicopterVehicleId != -1 ) { iNumber = GetNumberInVehicle( iHelicopterVehicleId ); } return( iNumber ); } BOOLEAN IsRefuelSiteInSector( INT16 sMapX, INT16 sMapY ) { INT32 iCounter = 0; for( iCounter = 0; iCounter < NUMBER_OF_REFUEL_SITES; iCounter++ ) { if ( ( ubRefuelList[ iCounter ][ 0 ] == sMapX ) && ( ubRefuelList[ iCounter ][ 1 ] == sMapY ) ) { return(TRUE); } } return(FALSE); } void UpdateRefuelSiteAvailability( void ) { INT32 iCounter = 0; // Generally, only Drassen is initially available for refuelling // Estoni must first be captured (although player may already have it when he gets Skyrider!) for( iCounter = 0; iCounter < NUMBER_OF_REFUEL_SITES; iCounter++ ) { // if enemy controlled sector (ground OR air, don't want to fly into enemy air territory) if( ( StrategicMap[ CALCULATE_STRATEGIC_INDEX( ubRefuelList[ iCounter ][ 0 ], ubRefuelList[ iCounter ][ 1 ] ) ].fEnemyControlled == TRUE ) || ( StrategicMap[ CALCULATE_STRATEGIC_INDEX( ubRefuelList[ iCounter ][ 0 ], ubRefuelList[ iCounter ][ 1 ] ) ].fEnemyAirControlled == TRUE ) || ( ( iCounter == ESTONI_REFUELING_SITE ) && ( CheckFact( FACT_ESTONI_REFUELLING_POSSIBLE, 0 ) == FALSE ) ) ) { // mark refueling site as unavailable fRefuelingSiteAvailable[ iCounter ] = FALSE; } else { // mark refueling site as available fRefuelingSiteAvailable[ iCounter ] = TRUE; // reactivate a grounded helicopter, if here if ( !fHelicopterAvailable && !fHelicopterDestroyed && fSkyRiderAvailable && ( iHelicopterVehicleId != -1 ) ) { if( ( pVehicleList[ iHelicopterVehicleId ].sSectorX == ubRefuelList[ iCounter ][ 0 ]) && ( pVehicleList[ iHelicopterVehicleId ].sSectorY == ubRefuelList[ iCounter ][ 1 ]) ) { // no longer grounded DoScreenIndependantMessageBox( pSkyriderText[ 5 ], MSG_BOX_FLAG_OK, NULL ); } } } } } void SetUpHelicopterForPlayer( INT16 sX, INT16 sY ) { if( fSkyRiderSetUp == FALSE ) { fHelicopterAvailable = TRUE; fSkyRiderAvailable = TRUE; iHelicopterVehicleId = AddVehicleToList( sX, sY, 0, HELICOPTER ); Assert( iHelicopterVehicleId != -1 ); SoldierSkyRider.initialize(); SoldierSkyRider.ubProfile = SKYRIDER; SoldierSkyRider.stats.bLife = 80; pSkyRider = &( SoldierSkyRider ); // set up for movement SetUpHelicopterForMovement( ); UpdateRefuelSiteAvailability( ); fSkyRiderSetUp = TRUE; gMercProfiles[ SKYRIDER ].fUseProfileInsertionInfo = FALSE; } return; } UINT8 MoveAllInHelicopterToFootMovementGroup( void ) { // take everyone out of heli and add to movement group INT32 iCounter = 0; UINT8 ubGroupId = 0; SOLDIERTYPE *pSoldier; INT8 bNewSquad; BOOLEAN fAnyoneAboard = FALSE; BOOLEAN fSuccess; UINT8 ubInsertionCode = 0; BOOLEAN fInsertionCodeSet = FALSE; UINT16 usInsertionData = 0; // put these guys on their own squad (we need to return their group ID, and can only return one, so they need a unique one bNewSquad = GetFirstEmptySquad(); if ( bNewSquad == -1 ) { return( 0 ); } // go through list of everyone in helicopter for( iCounter = 0; iCounter < iSeatingCapacities[ pVehicleList[ iHelicopterVehicleId ].ubVehicleType ]; iCounter++ ) { // get passenger pSoldier = pVehicleList[ iHelicopterVehicleId ].pPassengers[ iCounter ]; if( pSoldier != NULL ) { // better really be in there! Assert ( pSoldier->bAssignment == VEHICLE ); Assert ( pSoldier->iVehicleId == iHelicopterVehicleId ); fAnyoneAboard = TRUE; fSuccess = RemoveSoldierFromHelicopter( pSoldier ); Assert( fSuccess ); AddCharacterToSquad( pSoldier, bNewSquad ); // ATE: OK - the ubStrategicInsertionCode is set 'cause groupArrivesInsector has been // called when buddy is added to a squad. However, the insertion code onlt sets set for // the first merc, so the rest are going to use whatever they had previously.... if ( !fInsertionCodeSet ) { ubInsertionCode = pSoldier->ubStrategicInsertionCode; usInsertionData = pSoldier->usStrategicInsertionData; fInsertionCodeSet = TRUE; } else { pSoldier->ubStrategicInsertionCode = ubInsertionCode; pSoldier->usStrategicInsertionData = usInsertionData; } } } if ( fAnyoneAboard ) { ubGroupId = SquadMovementGroups[ bNewSquad ]; } return( ubGroupId ); } void SkyRiderTalk( UINT16 usQuoteNum ) { // have skyrider talk to player HeliCharacterDialogue( pSkyRider, usQuoteNum ); fTeamPanelDirty = TRUE; return; } void HandleSkyRiderMonologueEvent( UINT32 uiEventCode, UINT32 uiSpecialCode ) { // will handle the skyrider monologue about where the SAM sites are and what not TurnOnAirSpaceMode(); switch( uiEventCode ) { case( SKYRIDER_MONOLOGUE_EVENT_DRASSEN_SAM_SITE ): SetExternMapscreenSpeechPanelXY( DEFAULT_EXTERN_PANEL_X_POS, 117 ); HandleSkyRiderMonologueAboutDrassenSAMSite( uiSpecialCode ); break; case SKYRIDER_MONOLOGUE_EVENT_CAMBRIA_HOSPITAL: SetExternMapscreenSpeechPanelXY( DEFAULT_EXTERN_PANEL_X_POS, 172 ); HandleSkyRiderMonologueAboutCambriaHospital( uiSpecialCode ); break; case( SKYRIDER_MONOLOGUE_EVENT_OTHER_SAM_SITES ): SetExternMapscreenSpeechPanelXY( 335, DEFAULT_EXTERN_PANEL_Y_POS ); HandleSkyRiderMonologueAboutOtherSAMSites( uiSpecialCode ); break; case( SKYRIDER_MONOLOGUE_EVENT_ESTONI_REFUEL ): SetExternMapscreenSpeechPanelXY( DEFAULT_EXTERN_PANEL_X_POS, DEFAULT_EXTERN_PANEL_Y_POS ); HandleSkyRiderMonologueAboutEstoniRefuel( uiSpecialCode ); break; } // update time guiTimeOfLastSkyriderMonologue = GetWorldTotalMin(); } void HandleSkyRiderMonologueAboutEstoniRefuel( UINT32 uiSpecialCode ) { // once estoni is free tell player about refueling switch( uiSpecialCode ) { case( 0 ): CharacterDialogueWithSpecialEvent( SKYRIDER, SPIEL_ABOUT_ESTONI_AIRSPACE, uiExternalStaticNPCFaces[ SKYRIDER_EXTERNAL_FACE ] , DIALOGUE_EXTERNAL_NPC_UI , FALSE , FALSE , DIALOGUE_SPECIAL_EVENT_SKYRIDERMAPSCREENEVENT ,SKYRIDER_MONOLOGUE_EVENT_ESTONI_REFUEL, 1 ); // if special event data 2 is true, then do dialogue, else this is just a trigger for an event CharacterDialogue( SKYRIDER, SPIEL_ABOUT_ESTONI_AIRSPACE, uiExternalStaticNPCFaces[ SKYRIDER_EXTERNAL_FACE ], DIALOGUE_EXTERNAL_NPC_UI, FALSE, FALSE ); CharacterDialogueWithSpecialEvent( SKYRIDER, SPIEL_ABOUT_ESTONI_AIRSPACE, uiExternalStaticNPCFaces[ SKYRIDER_EXTERNAL_FACE ] , DIALOGUE_EXTERNAL_NPC_UI , FALSE , FALSE , DIALOGUE_SPECIAL_EVENT_SKYRIDERMAPSCREENEVENT ,SKYRIDER_MONOLOGUE_EVENT_ESTONI_REFUEL, 2 ); break; case( 1 ): // highlight Estoni fShowEstoniRefuelHighLight = TRUE; break; case( 2 ): fShowEstoniRefuelHighLight = FALSE; break; } return; } void HandleSkyRiderMonologueAboutDrassenSAMSite( UINT32 uiSpecialCode ) { switch( uiSpecialCode ) { case( 0 ): //gpCurrentTalkingFace = &gFacesData[ uiExternalStaticNPCFaces[ SKYRIDER_EXTERNAL_FACE ] ]; //gubCurrentTalkingID = SKYRIDER; // if special event data 2 is true, then do dialogue, else this is just a trigger for an event CharacterDialogue( SKYRIDER, MENTION_DRASSEN_SAM_SITE, uiExternalStaticNPCFaces[ SKYRIDER_EXTERNAL_FACE ], DIALOGUE_EXTERNAL_NPC_UI, FALSE, FALSE ); CharacterDialogueWithSpecialEvent( SKYRIDER, MENTION_DRASSEN_SAM_SITE, uiExternalStaticNPCFaces[ SKYRIDER_EXTERNAL_FACE ], DIALOGUE_EXTERNAL_NPC_UI , FALSE , TRUE , DIALOGUE_SPECIAL_EVENT_SKYRIDERMAPSCREENEVENT ,SKYRIDER_MONOLOGUE_EVENT_DRASSEN_SAM_SITE, 1 ); if( SAMSitesUnderPlayerControl( SAM_2_X, SAM_2_Y ) == FALSE ) { CharacterDialogue( SKYRIDER, SECOND_HALF_OF_MENTION_DRASSEN_SAM_SITE, uiExternalStaticNPCFaces[ SKYRIDER_EXTERNAL_FACE ], DIALOGUE_EXTERNAL_NPC_UI, FALSE, FALSE ); } else { // Ian says don't use the SAM site quote unless player has tried flying already if ( CheckFact( FACT_SKYRIDER_USED_IN_MAPSCREEN, SKYRIDER ) ) { CharacterDialogue( SKYRIDER, SAM_SITE_TAKEN, uiExternalStaticNPCFaces[ SKYRIDER_EXTERNAL_FACE ], DIALOGUE_EXTERNAL_NPC_UI, FALSE, FALSE ); gfSkyriderSaidCongratsOnTakingSAM = TRUE; } } CharacterDialogueWithSpecialEvent( SKYRIDER, MENTION_DRASSEN_SAM_SITE, uiExternalStaticNPCFaces[ SKYRIDER_EXTERNAL_FACE ], DIALOGUE_EXTERNAL_NPC_UI , FALSE , TRUE , DIALOGUE_SPECIAL_EVENT_SKYRIDERMAPSCREENEVENT ,SKYRIDER_MONOLOGUE_EVENT_DRASSEN_SAM_SITE, 2 ); break; case( 1 ): // highlight Drassen SAM site sector fShowDrassenSAMHighLight = TRUE; SetSAMSiteAsFound( SAM_SITE_TWO ); break; case( 2 ): fShowDrassenSAMHighLight = FALSE; break; } return; } void HandleSkyRiderMonologueAboutCambriaHospital( UINT32 uiSpecialCode ) { switch( uiSpecialCode ) { case( 0 ): //gpCurrentTalkingFace = &gFacesData[ uiExternalStaticNPCFaces[ SKYRIDER_EXTERNAL_FACE ] ]; //gubCurrentTalkingID = SKYRIDER; // if special event data 2 is true, then do dialogue, else this is just a trigger for an event CharacterDialogue( SKYRIDER, MENTION_HOSPITAL_IN_CAMBRIA, uiExternalStaticNPCFaces[ SKYRIDER_EXTERNAL_FACE ], DIALOGUE_EXTERNAL_NPC_UI, FALSE, FALSE ); CharacterDialogueWithSpecialEvent( SKYRIDER, MENTION_HOSPITAL_IN_CAMBRIA, uiExternalStaticNPCFaces[ SKYRIDER_EXTERNAL_FACE ], DIALOGUE_EXTERNAL_NPC_UI , FALSE , TRUE , DIALOGUE_SPECIAL_EVENT_SKYRIDERMAPSCREENEVENT ,SKYRIDER_MONOLOGUE_EVENT_CAMBRIA_HOSPITAL, 1 ); // highlight Drassen hospital sector fShowCambriaHospitalHighLight = TRUE; break; case( 1 ): fShowCambriaHospitalHighLight = FALSE; break; } return; } void HandleSkyRiderMonologueAboutOtherSAMSites( UINT32 uiSpecialCode ) { // handle skyrider telling player about other sam sites..on fifth hiring or after one near drassen is taken out switch( uiSpecialCode ) { case( 0 ): // do quote 21 gpCurrentTalkingFace = &gFacesData[ uiExternalStaticNPCFaces[ SKYRIDER_EXTERNAL_FACE ] ]; gubCurrentTalkingID = SKYRIDER; // if special event data 2 is true, then do dialogue, else this is just a trigger for an event CharacterDialogue( SKYRIDER, SPIEL_ABOUT_OTHER_SAM_SITES, uiExternalStaticNPCFaces[ SKYRIDER_EXTERNAL_FACE ], DIALOGUE_EXTERNAL_NPC_UI, FALSE, FALSE ); CharacterDialogueWithSpecialEvent( SKYRIDER, SPIEL_ABOUT_OTHER_SAM_SITES, uiExternalStaticNPCFaces[ SKYRIDER_EXTERNAL_FACE ] , DIALOGUE_EXTERNAL_NPC_UI , FALSE , FALSE , DIALOGUE_SPECIAL_EVENT_SKYRIDERMAPSCREENEVENT ,SKYRIDER_MONOLOGUE_EVENT_OTHER_SAM_SITES, 1 ); CharacterDialogue( SKYRIDER, SECOND_HALF_OF_SPIEL_ABOUT_OTHER_SAM_SITES, uiExternalStaticNPCFaces[ SKYRIDER_EXTERNAL_FACE ], DIALOGUE_EXTERNAL_NPC_UI, FALSE, FALSE ); CharacterDialogueWithSpecialEvent( SKYRIDER, SPIEL_ABOUT_OTHER_SAM_SITES, uiExternalStaticNPCFaces[ SKYRIDER_EXTERNAL_FACE ] , DIALOGUE_EXTERNAL_NPC_UI , FALSE , FALSE , DIALOGUE_SPECIAL_EVENT_SKYRIDERMAPSCREENEVENT ,SKYRIDER_MONOLOGUE_EVENT_OTHER_SAM_SITES, 2 ); break; case( 1 ): // highlight other SAMs fShowOtherSAMHighLight = TRUE; // reveal other 3 SAM sites SetSAMSiteAsFound( SAM_SITE_ONE ); SetSAMSiteAsFound( SAM_SITE_THREE ); SetSAMSiteAsFound( SAM_SITE_FOUR ); break; case( 2 ): fShowOtherSAMHighLight = FALSE; break; } return; } void CheckAndHandleSkyriderMonologues( void ) { // wait at least this many days between Skyrider monologues if ( ( GetWorldTotalMin() - guiTimeOfLastSkyriderMonologue ) >= ( MIN_DAYS_BETWEEN_SKYRIDER_MONOLOGUES * 24 * 60 ) ) { if( guiHelicopterSkyriderTalkState == 0 ) { HandleSkyRiderMonologueEvent( SKYRIDER_MONOLOGUE_EVENT_DRASSEN_SAM_SITE, 0 ); guiHelicopterSkyriderTalkState = 1; } else if( guiHelicopterSkyriderTalkState == 1 ) { // if enemy still controls the Cambria hospital sector if( StrategicMap[ CALCULATE_STRATEGIC_INDEX( HOSPITAL_SECTOR_X, HOSPITAL_SECTOR_Y ) ].fEnemyControlled ) { HandleSkyRiderMonologueEvent( SKYRIDER_MONOLOGUE_EVENT_CAMBRIA_HOSPITAL, 0 ); } // advance state even if player already has Cambria's hospital sector!!! guiHelicopterSkyriderTalkState = 2; } else if( guiHelicopterSkyriderTalkState == 2 ) { // wait until player has taken over a SAM site before saying this and advancing state if ( gfSkyriderSaidCongratsOnTakingSAM ) { HandleSkyRiderMonologueEvent( SKYRIDER_MONOLOGUE_EVENT_OTHER_SAM_SITES, 0 ); guiHelicopterSkyriderTalkState = 3; } } else if( guiHelicopterSkyriderTalkState == 3 ) { // wait until Estoni refuelling site becomes available if ( fRefuelingSiteAvailable[ ESTONI_REFUELING_SITE ] ) { HandleSkyRiderMonologueEvent( SKYRIDER_MONOLOGUE_EVENT_ESTONI_REFUEL, 0 ); guiHelicopterSkyriderTalkState = 4; } } } } void HandleAnimationOfSectors( void ) { BOOLEAN fSkipSpeakersLocator = FALSE; // these don't need to be saved, they merely turn off the highlights after they stop flashing static BOOLEAN fOldShowDrassenSAMHighLight = FALSE; static BOOLEAN fOldShowCambriaHospitalHighLight = FALSE; static BOOLEAN fOldShowEstoniRefuelHighLight = FALSE; static BOOLEAN fOldShowOtherSAMHighLight = FALSE; // find out which mode we are in and animate for that mode // Drassen SAM site if( fShowDrassenSAMHighLight ) { fOldShowDrassenSAMHighLight = TRUE; // Drassen's SAM site is #3 HandleBlitOfSectorLocatorIcon( SAM_2_X, SAM_2_Y, 0, LOCATOR_COLOR_RED ); fSkipSpeakersLocator = TRUE; } else if( fOldShowDrassenSAMHighLight ) { fOldShowDrassenSAMHighLight = FALSE; fMapPanelDirty = TRUE; } // Cambria hospital if( fShowCambriaHospitalHighLight ) { fOldShowCambriaHospitalHighLight = TRUE; HandleBlitOfSectorLocatorIcon( HOSPITAL_SECTOR_X, HOSPITAL_SECTOR_Y, 0, LOCATOR_COLOR_RED ); fSkipSpeakersLocator = TRUE; } else if( fOldShowCambriaHospitalHighLight ) { fOldShowCambriaHospitalHighLight = FALSE; fMapPanelDirty = TRUE; } // show other SAM sites if( fShowOtherSAMHighLight ) { fOldShowOtherSAMHighLight = TRUE; HandleBlitOfSectorLocatorIcon( SAM_1_X, SAM_1_Y, 0, LOCATOR_COLOR_RED ); HandleBlitOfSectorLocatorIcon( SAM_3_X, SAM_3_Y, 0, LOCATOR_COLOR_RED ); HandleBlitOfSectorLocatorIcon( SAM_4_X, SAM_4_Y, 0, LOCATOR_COLOR_RED ); fSkipSpeakersLocator = TRUE; } else if( fOldShowOtherSAMHighLight ) { fOldShowOtherSAMHighLight = FALSE; fMapPanelDirty = TRUE; } // show Estoni site if( fShowEstoniRefuelHighLight ) { fOldShowEstoniRefuelHighLight = TRUE; HandleBlitOfSectorLocatorIcon( ubRefuelList[ ESTONI_REFUELING_SITE ][ 0 ], ubRefuelList[ ESTONI_REFUELING_SITE ][ 1 ], 0, LOCATOR_COLOR_RED ); fSkipSpeakersLocator = TRUE; } else if( fOldShowEstoniRefuelHighLight ) { fOldShowEstoniRefuelHighLight = FALSE; fMapPanelDirty = TRUE; } // don't show sector locator over the speaker's sector if he is talking about another sector - it's confusing if ( !fSkipSpeakersLocator ) { switch( gubBlitSectorLocatorCode ) { case LOCATOR_COLOR_RED: // normal one used for mines (will now be overriden with yellow) HandleBlitOfSectorLocatorIcon( gsSectorLocatorX, gsSectorLocatorY, 0, LOCATOR_COLOR_RED ); break; case LOCATOR_COLOR_YELLOW: // used for all other dialogues HandleBlitOfSectorLocatorIcon( gsSectorLocatorX, gsSectorLocatorY, 0, LOCATOR_COLOR_YELLOW ); break; } } return; } INT16 LastSectorInHelicoptersPath( void ) { // get the last sector value in the helictoper's path PathStPtr pNode = NULL; UINT32 uiLocation = 0; // if the heli is on the move, what is the distance it will move..the length of the merc path, less the first node if( CanHelicopterFly( ) == FALSE ) { // big number, no go return( 0 ); } uiLocation = pVehicleList[ iHelicopterVehicleId ].sSectorX + pVehicleList[ iHelicopterVehicleId ].sSectorY * MAP_WORLD_X; pNode = pVehicleList[ iHelicopterVehicleId ].pMercPath; // any path yet? if( pNode != NULL ) { while( pNode) { uiLocation = pNode->uiSectorId; pNode = pNode->pNext; } } pNode = MoveToBeginningOfPathList( pTempHelicopterPath ); // any path yet? if( pNode != NULL ) { while( pNode ) { uiLocation = pNode->uiSectorId; pNode = pNode->pNext; } } return( ( INT16 )uiLocation ); } /* INT32 GetTotalCostOfHelicopterTrip( void ) { // get cost of helicopter trip PathStPtr pNode = NULL, pTempNode = NULL; UINT32 uiCost = 0; UINT32 uiLastTempPathSectorId = 0; UINT32 iClosestRefuelPoint = 0; UINT32 uiStartSectorNum = 0; UINT32 uiLength = 0; // if the heli is on the move, what is the distance it will move..the length of the merc path, less the first node if( CanHelicopterFly( ) == FALSE ) { // big number, no go return( 0 ); } pNode = pVehicleList[ iHelicopterVehicleId ].pMercPath; // any path yet? uiLastTempPathSectorId = pVehicleList[ iHelicopterVehicleId ].sSectorX + pVehicleList[ iHelicopterVehicleId ].sSectorY * MAP_WORLD_X; uiStartSectorNum = uiLastTempPathSectorId; if( pNode ) { pNode = pNode->pNext; } if( pNode != NULL ) { while( pNode) { if( uiLength == 0 ) { if( pNode->pNext ) { if( uiLastTempPathSectorId == pNode->pNext->uiSectorId ) { // do nothing } else { uiCost += GetCostOfPassageForHelicopter( ( UINT16 )( pNode->uiSectorId % MAP_WORLD_X ), ( UINT16 ) ( pNode->uiSectorId / MAP_WORLD_X ) ); } } else { uiCost += GetCostOfPassageForHelicopter( ( UINT16 )( pNode->uiSectorId % MAP_WORLD_X ), ( UINT16 ) ( pNode->uiSectorId / MAP_WORLD_X ) ); } } else { uiCost += GetCostOfPassageForHelicopter( ( UINT16 )( pNode->uiSectorId % MAP_WORLD_X ), ( UINT16 ) ( pNode->uiSectorId / MAP_WORLD_X ) ); } uiLength++; uiLastTempPathSectorId = pNode->uiSectorId; pNode = pNode->pNext; } } pNode = NULL; if( pTempHelicopterPath ) { pNode = MoveToBeginningOfPathList( pTempHelicopterPath ); } if( pNode ) { pNode = pNode->pNext; } // any path yet? if( pNode != NULL ) { while( pNode ) { if( uiLength == 0 ) { if( pNode->pNext ) { if( uiLastTempPathSectorId == pNode->pNext->uiSectorId ) { // do nothing } else { uiCost += GetCostOfPassageForHelicopter( ( UINT16 )( pNode->uiSectorId % MAP_WORLD_X ), ( UINT16 ) ( pNode->uiSectorId / MAP_WORLD_X ) ); } } else { uiCost += GetCostOfPassageForHelicopter( ( UINT16 )( pNode->uiSectorId % MAP_WORLD_X ), ( UINT16 ) ( pNode->uiSectorId / MAP_WORLD_X ) ); } } else { uiCost += GetCostOfPassageForHelicopter( ( UINT16 )( pNode->uiSectorId % MAP_WORLD_X ), ( UINT16 ) ( pNode->uiSectorId / MAP_WORLD_X ) ); } uiLength++; //uiCost += GetCostOfPassageForHelicopter( ( UINT16 ) ( pNode->uiSectorId % MAP_WORLD_X ), ( UINT16 ) ( pNode->uiSectorId / MAP_WORLD_X ) ); uiLastTempPathSectorId = pNode->uiSectorId; pNode = pNode->pNext; } } iClosestRefuelPoint = ( INT16 )( CALCULATE_STRATEGIC_INDEX( ubRefuelList[ LocationOfNearestRefuelPoint( FALSE ) ][ 0 ], ubRefuelList[ LocationOfNearestRefuelPoint( FALSE ) ][ 1 ] ) ); pNode = NULL; if( uiLastTempPathSectorId != iClosestRefuelPoint ) { pNode = BuildAStrategicPath( pNode, ( INT16 )( uiLastTempPathSectorId ), ( INT16 )iClosestRefuelPoint, pVehicleList[ iHelicopterVehicleId ].ubMovementGroup, FALSE ); // pNode = BuildAStrategicPath( pNode, ( INT16 )( uiLastTempPathSectorId ), ( INT16 )iClosestRefuelPoint, pVehicleList[ iHelicopterVehicleId ].ubMovementGroup, FALSE, TRUE ); pNode = MoveToBeginningOfPathList( pNode ); } pTempNode = pNode; uiLength = 0; if( pTempNode ) { pTempNode = pTempNode->pNext; } while( pTempNode ) { if( uiLength == 0 ) { if( pTempNode->pNext ) { if( uiLastTempPathSectorId == pNode->pNext->uiSectorId ) { // do nothing } else { uiCost += GetCostOfPassageForHelicopter( ( UINT16 )( pTempNode->uiSectorId % MAP_WORLD_X ), ( UINT16 ) ( pTempNode->uiSectorId / MAP_WORLD_X ) ); } } else { uiCost += GetCostOfPassageForHelicopter( ( UINT16 )( pTempNode->uiSectorId % MAP_WORLD_X ), ( UINT16 ) ( pTempNode->uiSectorId / MAP_WORLD_X ) ); } } else { uiCost += GetCostOfPassageForHelicopter( ( UINT16 )( pTempNode->uiSectorId % MAP_WORLD_X ), ( UINT16 ) ( pTempNode->uiSectorId / MAP_WORLD_X ) ); } uiLength++; pTempNode = pTempNode->pNext; } return( ( INT32 )uiCost ); } */ void HandleHelicopterOnGroundGraphic( void ) { UINT8 ubSite = 0; SOLDIERTYPE *pSoldier; // no worries if underground if (gbWorldSectorZ != 0 ) { return; } for( ubSite = 0; ubSite < NUMBER_OF_REFUEL_SITES; ubSite++ ) { // is this refueling site sector the loaded sector ? if ( ( ubRefuelList[ ubSite ][ 0 ] == gWorldSectorX ) && ( ubRefuelList[ ubSite ][ 1 ] == gWorldSectorY ) ) { // YES, so find out if the chopper is landed here if ( IsHelicopterOnGroundAtRefuelingSite( ubSite ) ) { // Add.... AddHelicopterToMaps( TRUE, ubSite ); // ATE: Add skyridder too // ATE: only if hired...... if ( fHelicopterAvailable ) { gMercProfiles[ SKYRIDER ].sSectorX = gWorldSectorX; gMercProfiles[ SKYRIDER ].sSectorY = gWorldSectorY; gMercProfiles[ SKYRIDER ].ubStrategicInsertionCode = INSERTION_CODE_GRIDNO; gMercProfiles[ SKYRIDER ].usStrategicInsertionData = sRefuelStartGridNo[ubSite] - 639; // Stand near the heli gMercProfiles[ SKYRIDER ].fUseProfileInsertionInfo = TRUE; } } else { AddHelicopterToMaps( FALSE, ubSite ); // ATE: Remove skyridder.... if ( fHelicopterAvailable ) { gMercProfiles[ SKYRIDER ].sSectorX = 0; gMercProfiles[ SKYRIDER ].sSectorY = 0; // see if we can find him and remove him if so.... pSoldier = FindSoldierByProfileID( SKYRIDER, FALSE ); // ATE: Don't do this if buddy is on our team! if ( pSoldier != NULL && pSoldier->bTeam != gbPlayerNum ) { TacticalRemoveSoldier( pSoldier->ubID ); } } } // Invalidate rendering InvalidateWorldRedundency( ); // can't be 2 places at once! break; } } } void HandleHelicopterOnGroundSkyriderProfile( void ) { UINT8 ubSite = 0; SOLDIERTYPE *pSoldier; // no worries if underground if (gbWorldSectorZ != 0 ) { return; } for( ubSite = 0; ubSite < NUMBER_OF_REFUEL_SITES; ubSite++ ) { // is this refueling site sector the loaded sector ? if ( ( ubRefuelList[ ubSite ][ 0 ] == gWorldSectorX ) && ( ubRefuelList[ ubSite ][ 1 ] == gWorldSectorY ) ) { // YES, so find out if the chopper is landed here if ( IsHelicopterOnGroundAtRefuelingSite( ubSite ) ) { // ATE: Add skyridder too // ATE: only if hired...... if ( fHelicopterAvailable ) { gMercProfiles[ SKYRIDER ].sSectorX = gWorldSectorX; gMercProfiles[ SKYRIDER ].sSectorY = gWorldSectorY; } } else { // ATE: Remove skyridder.... if ( fHelicopterAvailable ) { gMercProfiles[ SKYRIDER ].sSectorX = 0; gMercProfiles[ SKYRIDER ].sSectorY = 0; // see if we can find him and remove him if so.... pSoldier = FindSoldierByProfileID( SKYRIDER, FALSE ); // ATE: Don't do this if buddy is on our team! if ( pSoldier != NULL && pSoldier->bTeam != gbPlayerNum ) { TacticalRemoveSoldier( pSoldier->ubID ); } } } // can't be 2 places at once! break; } } } BOOLEAN IsHelicopterOnGroundAtRefuelingSite( UINT8 ubRefuelingSite ) { if ( fHelicopterDestroyed ) { return(FALSE); } if ( fHelicopterIsAirBorne ) { return(FALSE); } // if we haven't even met SkyRider if ( !fSkyRiderSetUp ) { // then it's always at Drassen if ( ubRefuelingSite == DRASSEN_REFUELING_SITE ) { return(TRUE); } else { return(FALSE); } } // skyrider is setup, helicopter isn't destroyed, so this ought to be a valid vehicle id Assert( iHelicopterVehicleId != -1 ); // on the ground, but is it at this site or at another one? if ( ( ubRefuelList[ ubRefuelingSite ][ 0 ] == pVehicleList[ iHelicopterVehicleId ].sSectorX ) && ( ubRefuelList[ ubRefuelingSite ][ 1 ] == pVehicleList[ iHelicopterVehicleId ].sSectorY ) ) { return(TRUE); } // not here return(FALSE); } // drassen airport sector #define AIRPORT_X 13 #define AIRPORT_Y 2 // meduna airport sector #define AIRPORT2_X 3 #define AIRPORT2_Y 14 BOOLEAN WillAirRaidBeStopped( INT16 sSectorX, INT16 sSectorY ) { UINT8 ubSamNumber = 0; INT8 bSAMCondition; UINT8 ubChance; DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("WillAirRaidBeStopped: enemy air controlled = %d",StrategicMap[CALCULATE_STRATEGIC_INDEX( sSectorX, sSectorY ) ].fEnemyAirControlled)); // if enemy controls this SAM site, then it can't stop an air raid if( StrategicMap[CALCULATE_STRATEGIC_INDEX( sSectorX, sSectorY ) ].fEnemyAirControlled == TRUE ) { return( FALSE ); } if( !StrategicMap[ ( AIRPORT_X + ( MAP_WORLD_X * AIRPORT_Y ) ) ].fEnemyControlled && !StrategicMap[ ( AIRPORT2_X + ( MAP_WORLD_X * AIRPORT2_Y ) ) ].fEnemyControlled ) { DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("WillAirRaidBeStopped: enemy has no more airports")); return( TRUE ); } // which SAM controls this sector? ubSamNumber = ubSAMControlledSectors[ sSectorY ][ sSectorX ]; DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("WillAirRaidBeStopped: SAM number = %d",ubSamNumber)); // if none of them if (ubSamNumber == 0) { return( FALSE); } // get the condition of that SAM site (NOTE: SAM #s are 1-4, but indexes are 0-3!!!) Assert( ubSamNumber <= NUMBER_OF_SAMS ); bSAMCondition = StrategicMap[ SECTOR_INFO_TO_STRATEGIC_INDEX( pSamList[ ubSamNumber - 1 ] ) ].bSAMCondition; // if it's too busted to work, then it can't stop an air raid DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("WillAirRaidBeStopped: SAM condition = %d",bSAMCondition)); if( bSAMCondition < MIN_CONDITION_FOR_SAM_SITE_TO_WORK ) { // no problem, SAM site not working return( FALSE ); } // Friendly airspace controlled by a working SAM site, so SAM site fires a SAM at air raid bomber // calc chance that chopper will be shot down ubChance = bSAMCondition; // there's a fair chance of a miss even if the SAM site is in perfect working order if (ubChance > MAX_SAM_SITE_ACCURACY * 3) // Madd - since this is only used for enemy air raids, we'll say that good guy SAMs can have a max of 99% to hit instead of 33% { ubChance = MAX_SAM_SITE_ACCURACY * 3; } if( PreRandom( 100 ) < ubChance) { DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("WillAirRaidBeStopped: return true")); return( TRUE ); } DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("WillAirRaidBeStopped: return false")); return( FALSE ); } void HeliCrashSoundStopCallback( void *pData ) { SkyriderDestroyed( ); } BOOLEAN HandleSAMSiteAttackOfHelicopterInSector( INT16 sSectorX, INT16 sSectorY ) { UINT8 ubSamNumber = 0; INT8 bSAMCondition; UINT8 ubChance; // if this sector is in friendly airspace, we're safe if( StrategicMap[CALCULATE_STRATEGIC_INDEX( sSectorX, sSectorY ) ].fEnemyAirControlled == FALSE ) { // no problem, friendly airspace return( FALSE ); } // which SAM controls this sector? ubSamNumber = ubSAMControlledSectors[ sSectorY ][ sSectorX ]; // if none of them if (ubSamNumber == 0) { return( FALSE); } // get the condition of that SAM site (NOTE: SAM #s are 1-4, but indexes are 0-3!!!) Assert( ubSamNumber <= NUMBER_OF_SAMS ); bSAMCondition = StrategicMap[ SECTOR_INFO_TO_STRATEGIC_INDEX( pSamList[ ubSamNumber - 1 ] ) ].bSAMCondition; // if the SAM site is too damaged to be a threat if( bSAMCondition < MIN_CONDITION_FOR_SAM_SITE_TO_WORK ) { // no problem, SAM site not working return( FALSE ); } #ifdef JA2TESTVERSION if( fSAMSitesDisabledFromAttackingPlayer == TRUE ) { return( FALSE ); } #endif // Hostile airspace controlled by a working SAM site, so SAM site fires a SAM at Skyrider!!! // calc chance that chopper will be shot down ubChance = bSAMCondition; // there's a fair chance of a miss even if the SAM site is in perfect working order if (ubChance > MAX_SAM_SITE_ACCURACY) { ubChance = MAX_SAM_SITE_ACCURACY; } if( PreRandom( 100 ) < ubChance) { // another hit! gubHelicopterHitsTaken++; // Took a hit! Pause time so player can reconsider StopTimeCompression(); // first hit? if ( gubHelicopterHitsTaken == 1 ) { HeliCharacterDialogue( pSkyRider, HELI_TOOK_MINOR_DAMAGE ); } // second hit? else if ( gubHelicopterHitsTaken == 2 ) { // going back to base (no choice, dialogue says so) HeliCharacterDialogue( pSkyRider, HELI_TOOK_MAJOR_DAMAGE ); MakeHeliReturnToBase(); } // third hit! else { // Important: Skyrider must still be alive when he talks, so must do this before heli is destroyed! HeliCharacterDialogue( pSkyRider, HELI_GOING_DOWN ); // everyone die die die // play sound if ( PlayJA2StreamingSampleFromFile( "stsounds\\blah2.wav", RATE_11025, HIGHVOLUME, 1, MIDDLEPAN, HeliCrashSoundStopCallback ) == SOUND_ERROR ) { // Destroy here if we cannot play streamed sound sample.... SkyriderDestroyed( ); } else { // otherwise it's handled in the callback // remove any arrival events for the helicopter's group DeleteStrategicEvent( EVENT_GROUP_ARRIVAL, pVehicleList[ iHelicopterVehicleId ].ubMovementGroup ); } // special return code indicating heli was destroyed return( TRUE ); } } // still flying return( FALSE ); } // are we at the end of the path for the heli? BOOLEAN EndOfHelicoptersPath( void ) { if( pVehicleList[ iHelicopterVehicleId ].pMercPath == NULL ) { return( TRUE ); } if( pVehicleList[ iHelicopterVehicleId ].pMercPath->pNext == NULL ) { return( TRUE ); } return( FALSE ); } // check if helicopter can take off? BOOLEAN CanHelicopterTakeOff( void ) { INT16 sHelicopterSector = 0; // if it's already in the air if( fHelicopterIsAirBorne == TRUE ) { return( TRUE ); } // grab location sHelicopterSector = pVehicleList[ iHelicopterVehicleId ].sSectorX + pVehicleList[ iHelicopterVehicleId ].sSectorY * MAP_WORLD_X; // if it's not in enemy control, we can take off if( StrategicMap[ sHelicopterSector ].fEnemyControlled == FALSE ) { return( TRUE ); } return( FALSE ); } void AddHeliPeice( INT16 sGridNo, UINT16 sOStruct ) { UINT16 usDummy; // ATE: Check first if already exists.... if ( !TypeExistsInStructLayer( sGridNo, sOStruct, &usDummy ) ) { // place in the world AddStructToTail( sGridNo, sOStruct ); } } void AddHelicopterToMaps( BOOLEAN fAdd, UINT8 ubSite ) { INT16 sGridNo = sRefuelStartGridNo[ ubSite ]; INT16 sOStruct = 0; INT16 sGridX, sGridY; INT16 sCentreGridX, sCentreGridY; // find out what slot it is by which site if( ubSite == 0 ) { // drassen sOStruct = FIRSTOSTRUCT1; } else { // estoni sOStruct = FOURTHOSTRUCT1; } // are we adding or taking away if( fAdd ) { AddHeliPeice( sGridNo, sOStruct ); AddHeliPeice( sGridNo, ( UINT16 )( sOStruct + 1)); AddHeliPeice( (INT16)( sGridNo - 800 ), ( UINT16 )( sOStruct + 2 )); AddHeliPeice( sGridNo, ( UINT16 )(sOStruct + 3 )); AddHeliPeice( sGridNo, ( UINT16 )(sOStruct + 4)); AddHeliPeice( (INT16)( sGridNo - 800 ), ( UINT16 )(sOStruct + 5)); InvalidateWorldRedundency(); SetRenderFlags( RENDER_FLAG_FULL ); // ATE: If any mercs here, bump them off! ConvertGridNoToXY( sGridNo, &sCentreGridX, &sCentreGridY ); for( sGridY = sCentreGridY - 5; sGridY < sCentreGridY + 5; sGridY++ ) { for( sGridX = sCentreGridX - 5; sGridX < sCentreGridX + 5; sGridX++ ) { sGridNo = MAPROWCOLTOPOS( sGridY, sGridX ); BumpAnyExistingMerc( sGridNo ); } } } else { // remove from the world RemoveStruct( sRefuelStartGridNo[ ubSite ], ( UINT16 )(sOStruct )); RemoveStruct( sRefuelStartGridNo[ ubSite ], ( UINT16 )(sOStruct + 1 )); RemoveStruct( sRefuelStartGridNo[ ubSite ] - 800, ( UINT16 )(sOStruct + 2)); RemoveStruct( sRefuelStartGridNo[ ubSite ], ( UINT16 )(sOStruct + 3)); RemoveStruct( sRefuelStartGridNo[ ubSite ], ( UINT16 )(sOStruct + 4)); RemoveStruct( sRefuelStartGridNo[ ubSite ] - 800, ( UINT16 )(sOStruct +5)); InvalidateWorldRedundency(); SetRenderFlags( RENDER_FLAG_FULL ); } } BOOLEAN IsSkyriderIsFlyingInSector( INT16 sSectorX, INT16 sSectorY ) { GROUP *pGroup; // up and about? if ( fHelicopterAvailable && ( iHelicopterVehicleId != -1 ) && CanHelicopterFly() && fHelicopterIsAirBorne ) { pGroup = GetGroup( pVehicleList[ iHelicopterVehicleId ].ubMovementGroup ); // the right sector? if ( ( sSectorX == pGroup->ubSectorX ) && ( sSectorY == pGroup->ubSectorY ) ) { return(TRUE); } } return( FALSE ); } BOOLEAN IsGroupTheHelicopterGroup( GROUP *pGroup ) { if( ( iHelicopterVehicleId != -1 ) && VehicleIdIsValid( iHelicopterVehicleId ) && ( pVehicleList[ iHelicopterVehicleId ].ubMovementGroup != 0 ) && ( pVehicleList[ iHelicopterVehicleId ].ubMovementGroup == pGroup->ubGroupID ) ) { return( TRUE ); } return( FALSE ); } INT16 GetNumSafeSectorsInPath( void ) { // get the last sector value in the helictoper's path PathStPtr pNode = NULL; UINT32 uiLocation = 0; UINT32 uiCount = 0; INT32 iHeliSector = -1; GROUP *pGroup; // if the heli is on the move, what is the distance it will move..the length of the merc path, less the first node if( CanHelicopterFly( ) == FALSE ) { // big number, no go return( 0 ); } // may need to skip the sector the chopper is currently in iHeliSector = CALCULATE_STRATEGIC_INDEX( pVehicleList[ iHelicopterVehicleId ].sSectorX, pVehicleList[ iHelicopterVehicleId ].sSectorY ); // get chopper's group ptr pGroup = GetGroup( pVehicleList[ iHelicopterVehicleId ].ubMovementGroup ); pNode = pVehicleList[ iHelicopterVehicleId ].pMercPath; // any path yet? if( pNode != NULL ) { // first node: skip it if that's the sector the chopper is currently in, AND // we're NOT gonna be changing directions (not actually performed until waypoints are rebuilt AFTER plotting is done) if ( ( ( INT32 ) pNode->uiSectorId == iHeliSector ) && ( pNode->pNext != NULL ) && !GroupBetweenSectorsAndSectorXYIsInDifferentDirection( pGroup, ( UINT8 ) GET_X_FROM_STRATEGIC_INDEX( pNode->pNext->uiSectorId ), ( UINT8 ) GET_Y_FROM_STRATEGIC_INDEX( pNode->pNext->uiSectorId ) ) ) { pNode = pNode->pNext; } while( pNode) { uiLocation = pNode->uiSectorId; if ( !StrategicMap[ uiLocation ].fEnemyAirControlled ) { uiCount++; } pNode = pNode->pNext; } } pNode = MoveToBeginningOfPathList( pTempHelicopterPath ); // any path yet? if( pNode != NULL ) { // first node: skip it if that's the sector the chopper is currently in, AND // we're NOT gonna be changing directions (not actually performed until waypoints are rebuilt AFTER plotting is done) // OR if the chopper has a mercpath, in which case this a continuation of it that would count the sector twice if ( ( ( ( INT32 ) pNode->uiSectorId == iHeliSector ) && ( pNode->pNext != NULL ) && !GroupBetweenSectorsAndSectorXYIsInDifferentDirection( pGroup, ( UINT8 ) GET_X_FROM_STRATEGIC_INDEX( pNode->pNext->uiSectorId ), ( UINT8 ) GET_Y_FROM_STRATEGIC_INDEX( pNode->pNext->uiSectorId ) ) ) || ( GetLengthOfPath( pVehicleList[ iHelicopterVehicleId ].pMercPath ) > 0 ) ) { pNode = pNode->pNext; } while( pNode) { uiLocation = pNode->uiSectorId; if ( !StrategicMap[ uiLocation ].fEnemyAirControlled ) { uiCount++; } pNode = pNode->pNext; } } return( (INT16)uiCount ); } INT16 GetNumUnSafeSectorsInPath( void ) { // get the last sector value in the helictoper's path PathStPtr pNode = NULL; UINT32 uiLocation = 0; UINT32 uiCount = 0; INT32 iHeliSector = -1; GROUP *pGroup; // if the heli is on the move, what is the distance it will move..the length of the merc path, less the first node if( CanHelicopterFly( ) == FALSE ) { // big number, no go return( 0 ); } // may need to skip the sector the chopper is currently in iHeliSector = CALCULATE_STRATEGIC_INDEX( pVehicleList[ iHelicopterVehicleId ].sSectorX, pVehicleList[ iHelicopterVehicleId ].sSectorY ); // get chopper's group ptr pGroup = GetGroup( pVehicleList[ iHelicopterVehicleId ].ubMovementGroup ); pNode = pVehicleList[ iHelicopterVehicleId ].pMercPath; // any path yet? if( pNode != NULL ) { // first node: skip it if that's the sector the chopper is currently in, AND // we're NOT gonna be changing directions (not actually performed until waypoints are rebuilt AFTER plotting is done) if ( ( ( INT32 ) pNode->uiSectorId == iHeliSector ) && ( pNode->pNext != NULL ) && !GroupBetweenSectorsAndSectorXYIsInDifferentDirection( pGroup, ( UINT8 ) GET_X_FROM_STRATEGIC_INDEX( pNode->pNext->uiSectorId ), ( UINT8 ) GET_Y_FROM_STRATEGIC_INDEX( pNode->pNext->uiSectorId ) ) ) { pNode = pNode->pNext; } while( pNode) { uiLocation = pNode->uiSectorId; if ( StrategicMap[ uiLocation ].fEnemyAirControlled ) { uiCount++; } pNode = pNode->pNext; } } pNode = MoveToBeginningOfPathList( pTempHelicopterPath ); // any path yet? if( pNode != NULL ) { // first node: skip it if that's the sector the chopper is currently in, AND // we're NOT gonna be changing directions (not actually performed until waypoints are rebuilt AFTER plotting is done) // OR if the chopper has a mercpath, in which case this a continuation of it that would count the sector twice if ( ( ( ( INT32 ) pNode->uiSectorId == iHeliSector ) && ( pNode->pNext != NULL ) && !GroupBetweenSectorsAndSectorXYIsInDifferentDirection( pGroup, ( UINT8 ) GET_X_FROM_STRATEGIC_INDEX( pNode->pNext->uiSectorId ), ( UINT8 ) GET_Y_FROM_STRATEGIC_INDEX( pNode->pNext->uiSectorId ) ) ) || ( GetLengthOfPath( pVehicleList[ iHelicopterVehicleId ].pMercPath ) > 0 ) ) { pNode = pNode->pNext; } while( pNode) { uiLocation = pNode->uiSectorId; if ( StrategicMap[ uiLocation ].fEnemyAirControlled ) { uiCount++; } pNode = pNode->pNext; } } return( (INT16)uiCount ); } void PaySkyriderBill( void) { // if we owe anything for the trip if ( iTotalAccumulatedCostByPlayer > 0 ) { // if player can afford to pay the Skyrider bill if( LaptopSaveInfo.iCurrentBalance >= iTotalAccumulatedCostByPlayer ) { // no problem, pay the man // add the transaction AddTransactionToPlayersBook( PAYMENT_TO_NPC, SKYRIDER, GetWorldTotalMin( ), -iTotalAccumulatedCostByPlayer ); ScreenMsg( FONT_MCOLOR_DKRED, MSG_INTERFACE, pSkyriderText[ 0 ], iTotalAccumulatedCostByPlayer ); } else { // money owed if( LaptopSaveInfo.iCurrentBalance > 0 ) { ScreenMsg( FONT_MCOLOR_DKRED, MSG_INTERFACE, pSkyriderText[ 0 ], LaptopSaveInfo.iCurrentBalance ); gMercProfiles[ SKYRIDER ].iBalance = LaptopSaveInfo.iCurrentBalance - iTotalAccumulatedCostByPlayer; // add the transaction AddTransactionToPlayersBook( PAYMENT_TO_NPC, SKYRIDER, GetWorldTotalMin( ), -LaptopSaveInfo.iCurrentBalance ); } else { gMercProfiles[ SKYRIDER ].iBalance = - iTotalAccumulatedCostByPlayer; } HeliCharacterDialogue( pSkyRider, OWED_MONEY_TO_SKYRIDER ); ScreenMsg( FONT_MCOLOR_DKRED, MSG_INTERFACE, pSkyriderText[ 1 ], -gMercProfiles[ SKYRIDER ].iBalance ); // kick everyone out! (we know we're in a safe sector if we're paying) MoveAllInHelicopterToFootMovementGroup( ); MakeHeliReturnToBase(); } iTotalAccumulatedCostByPlayer = 0; } } void PayOffSkyriderDebtIfAny( ) { INT32 iAmountOwed; INT32 iPayAmount; iAmountOwed = - gMercProfiles[ SKYRIDER ].iBalance; // if we owe him anything, and have any money if ( ( iAmountOwed > 0 ) && ( LaptopSaveInfo.iCurrentBalance > 0 ) ) { iPayAmount = min( iAmountOwed, LaptopSaveInfo.iCurrentBalance ); // pay the man what we can gMercProfiles[ SKYRIDER ].iBalance += iPayAmount; // add the transaction AddTransactionToPlayersBook( PAYMENT_TO_NPC, SKYRIDER, GetWorldTotalMin( ), -iPayAmount ); // tell player ScreenMsg( FONT_MCOLOR_DKRED, MSG_INTERFACE, pSkyriderText[ 0 ], iPayAmount ); // now whaddawe owe? iAmountOwed = - gMercProfiles[ SKYRIDER ].iBalance; // if it wasn't enough if ( iAmountOwed > 0 ) { ScreenMsg( FONT_MCOLOR_DKRED, MSG_INTERFACE, pSkyriderText[ 1 ], iAmountOwed ); HeliCharacterDialogue( pSkyRider, OWED_MONEY_TO_SKYRIDER ); } } } void MakeHeliReturnToBase( void ) { INT32 iLocation = 0; // if already at a refueling point if ( CheckForArrivalAtRefuelPoint() ) { ReFuelHelicopter( ); } else { // choose destination (closest refueling sector) iLocation = LocationOfNearestRefuelPoint( TRUE ); // null out path pVehicleList[ iHelicopterVehicleId ].pMercPath = ClearStrategicPathList( pVehicleList[ iHelicopterVehicleId ].pMercPath, pVehicleList[ iHelicopterVehicleId ].ubMovementGroup ); // plot path to that sector pVehicleList[ iHelicopterVehicleId ].pMercPath = AppendStrategicPath( MoveToBeginningOfPathList( BuildAStrategicPath( NULL, GetLastSectorIdInVehiclePath( iHelicopterVehicleId ) , ( INT16 )( CALCULATE_STRATEGIC_INDEX( ubRefuelList[ iLocation ][ 0 ], ubRefuelList[ iLocation ][ 1 ] ) ) , pVehicleList[ iHelicopterVehicleId ].ubMovementGroup, FALSE /*, FALSE */ ) ), pVehicleList[ iHelicopterVehicleId ].pMercPath ); pVehicleList[ iHelicopterVehicleId ].pMercPath = MoveToBeginningOfPathList( pVehicleList[ iHelicopterVehicleId ].pMercPath ); // rebuild the movement waypoints RebuildWayPointsForGroupPath( pVehicleList[ iHelicopterVehicleId ].pMercPath, pVehicleList[ iHelicopterVehicleId ].ubMovementGroup ); fHeliReturnStraightToBase = TRUE; fHoveringHelicopter = FALSE; } // stop time compression if it's on so player can digest this StopTimeCompression( ); } BOOLEAN SoldierAboardAirborneHeli( SOLDIERTYPE *pSoldier ) { Assert( pSoldier ); // if not in a vehicle, or not aboard the helicopter if ( ( pSoldier->bAssignment != VEHICLE ) || ( pSoldier->iVehicleId != iHelicopterVehicleId ) ) { return( FALSE ); } // he's in the heli - is it airborne? if ( !fHelicopterIsAirBorne ) { // nope, it's currently on the ground return( FALSE ); } // yes, airborne return( TRUE ); }