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