Files
source/Strategic/Map Screen Helicopter.cpp
T
Wanne d23a94eefc - New Feature: Now it is possible to compile a JA2 1.13 Demo executable with the actual 1.13 source code (by Jazz)
o This JA2 demo executable is as close as possible to the original JA2 demo, but with the 1.13 features!
--> No laptop, disabled strategy actions, auto merc hired, only P1 maps, ...
o To build the 1.13 executable, just enable the "JA113DEMO" define in builddefines.h
o The 1.13 demo executable will read from ja2_Demo.ini instead of ja2.ini
o You also need the "Data-JA2Demo" folder from the official 1.13 SVN GameDir: https://ja2svn.dyndns.org/source/ja2_v1.13_data/GameDir
o Once you start the game with the demo executable, you will notice the "START DEMO" instead of "START NEW GAME" in the main menu
o Multiplayer is disabled in the demo executable

- There is another new define called "JA113NODEMO" (by Jazz)
o See builddefines.h
o If this define is enabled, a special version of 1.13 is build
-> Disable all meanwhiles, emails, blocked quest with queen, alien queen


git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@5287 3b4a5df2-a311-0410-b5c6-a8a6f20db521
2012-05-18 11:20:11 +00:00

2344 lines
65 KiB
C++

#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"
// HEADROCK HAM 3.5: Added facility-based skyrider costs modifier
#include "Facilities.h"
#endif
#include "Vehicles.h"
// 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
};
INT32 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( ) && gGameSettings.fOptions[ TOPTION_SILENT_SKYRIDER ] == FALSE )
{
// 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();
// WANNE: Fix by Headrock: Always stop time!
StopTimeCompression();
}
else
{
// Say quote: "Gonna have to abort. Enemies below"
//CHRISL: Whether skyrider is set to silent or not, we should still say this if we aren't allowing landing in a hot lz.
//if(gGameSettings.fOptions[ TOPTION_SILENT_SKYRIDER ] == FALSE)
if(gGameExternalOptions.ubSkyriderHotLZ == 0)
HeliCharacterDialogue( pSkyRider, ARRIVED_IN_HOSTILE_SECTOR );
else
HeliCharacterDialogue( pSkyRider, HELI_HOT_DROP );
PaySkyriderBill();
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;
// HEADROCK HAM 3.5: Costs externalized
INT32 iBaseCostPerGreenTile = gGameExternalOptions.usHelicopterBaseCostPerGreenTile;
INT32 iBaseCostPerRedTile = gGameExternalOptions.usHelicopterBaseCostPerRedTile;
// if they don't control it
if( StrategicMap[ CALCULATE_STRATEGIC_INDEX( sX, sY ) ].fEnemyAirControlled == FALSE )
{
iCost = iBaseCostPerGreenTile;
}
else
{
iCost = iBaseCostPerRedTile;
}
// HEADROCK HAM 3.5: Apply facility-based modifiers from global integer recalculated hourly
iCost = __max(0, iCost + gsSkyriderCostModifier);
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
{
#if (defined JA2UB || defined JA113NODEMO)
Assert( 0 );
//No meanwhiles
#else
// play meanwhile scene if it hasn't been used yet
HandleKillChopperMeanwhileScene();
#endif
}
}
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 < gNewVehicle[ pVehicleList[ iHelicopterVehicleId ].ubVehicleType ].iNewSeatingCapacities; 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 , UINT8 SkyDrive, UINT8 VehicleID )
{
if( fSkyRiderSetUp == FALSE )
{
fHelicopterAvailable = TRUE;
fSkyRiderAvailable = TRUE;
iHelicopterVehicleId = AddVehicleToList( sX, sY, 0, VehicleID ); //HELICOPTER
Assert( iHelicopterVehicleId != -1 );
SoldierSkyRider.initialize();
SoldierSkyRider.ubProfile = SkyDrive; //SKYRIDER;
SoldierSkyRider.stats.bLife = 80;
pSkyRider = &( SoldierSkyRider );
// set up for movement
SetUpHelicopterForMovement( );
UpdateRefuelSiteAvailability( );
fSkyRiderSetUp = TRUE;
gMercProfiles[ SkyDrive ].fUseProfileInsertionInfo = FALSE; //SKYRIDER
}
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 < gNewVehicle[ pVehicleList[ iHelicopterVehicleId ].ubVehicleType ].iNewSeatingCapacities; 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( INT32 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 )
{
INT32 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( ( sGridNo - 800 ), ( UINT16 )( sOStruct + 2 ));
AddHeliPeice( sGridNo, ( UINT16 )(sOStruct + 3 ));
AddHeliPeice( sGridNo, ( UINT16 )(sOStruct + 4));
AddHeliPeice( ( 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)
//CHRISL: This may no longer be the case but I'm not sure of a better way to handle things.
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 );
}