Files
source/Strategic/Map Screen Helicopter.cpp
T
Overhaul 7a27e7105d New attack busy system
Added missing #includes

Added LUA scripting and console

Changed the way tracers are visualized, so they work more like real tracers instead of a light fountain

Fixed signedness of many grid variables used in GetMouseMapPos calls

Fixed enemy weapon choosing:  Sometimes a mortar is chosen but later rejected, but the grenade class was not reset.  Caused assertion failure

Added checks so enemies don't try to chuck RPG grenades with their hands

Now possible to shoot a someone in the head if he's in water

Fixed InitSightArrays to also clear soldier interrupt duel points.  This was causing an assertion failure elsewhere in the code because the interrupt list still had soldiers on it sometimes when this function was called.

Soldiers are much less willing to forfeit their turn over an attempt to use more APs than they have

Removed early setting of muzzle flash.  This would allow enemies to get an interrupt before you even fired.

Fixed item dropping by AI.  If AI tried to drop something while standing it would cause deadlock

Change to greatly speed up closing the sector inventory window in an unloaded sector

Added conditional compile flag to always give robot weapon ready advantage, even for 360 degree sighting


Check builddefines.h for the conditional flags.  Of greatest interest might be LUA_CONSOLE.  This will cause the game to bring up a command console when run.  However this console is severely lacking in many areas.  If anybody knows of an open-source terminal/console that could be used instead, it would be appreciated.  The existing console does bad things when you try to close it, and since it counts as a separate app, it pauses the game while it has focus.

LUA scripting is very limited, basically just proof of concept.  There is one global variable, the Soldiers array.  An array index gives you the soldier in that slot in the currently loaded sector.  The soldier has a few things that can be accessed:  name (short name); fullname (long name); grid (current grid #, can be changed); walkto(grid) (function to walk to another grid); runto(grid) (function to run to another grid)


git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@924 3b4a5df2-a311-0410-b5c6-a8a6f20db521
2007-06-09 09:01:04 +00:00

2316 lines
64 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"
#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
13068, // 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;
memset ( &SoldierSkyRider, 0, sizeof( SoldierSkyRider ) );
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->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.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 );
memset( &SoldierSkyRider, 0, sizeof( SOLDIERTYPE ) );
SoldierSkyRider.ubProfile = SKYRIDER;
SoldierSkyRider.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;
BOOLEAN fInsertionCodeSet = FALSE;
UINT16 usInsertionData;
// 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;
}
}
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 usGridNo;
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++ )
{
usGridNo = MAPROWCOLTOPOS( sGridY, sGridX );
BumpAnyExistingMerc( usGridNo );
}
}
}
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 );
}