#include "builddefines.h" #include "Types.h" #include "Merc Contract.h" #include "Soldier Profile.h" #include "History.h" #include "finances.h" #include "Game Clock.h" #include "Soldier Add.h" #include "Dialogue Control.h" #include "Soldier Create.h" #include "Message.h" #include "Font Control.h" #include "personnel.h" #include "LaptopSave.h" #include "Map Screen Interface.h" #include "strategic.h" #include "strategicmap.h" #include "Quests.h" #include "worlddef.h" #include "Animation Control.h" #include "Tactical Save.h" #include "Interface Control.h" #include "gamescreen.h" #include "jascreens.h" #include "Random.h" #include "Assignments.h" #include "Strategic Movement.h" #include "Squads.h" #include "gameloop.h" #include "Text.h" #include "Strategic Status.h" #include "mercs.h" #include "insurance contract.h" #include "Vehicles.h" #include "email.h" #include "Map Screen Helicopter.h" #include "GameSettings.h" #include "connect.h" #ifdef JA2UB #include "ub_config.h" #endif void CalculateMedicalDepositRefund( SOLDIERTYPE *pSoldier ); void NotifyPlayerOfMercDepartureAndPromptEquipmentPlacement( SOLDIERTYPE *pSoldier, BOOLEAN fAddRehireButton ); void MercDepartEquipmentBoxCallBack( UINT8 bExitValue ); BOOLEAN HandleFiredDeadMerc( SOLDIERTYPE *pSoldier ); void HandleExtendMercsContract( SOLDIERTYPE *pSoldier ); void HandleSoldierLeavingWithLowMorale( SOLDIERTYPE *pSoldier ); void HandleBuddiesReactionToFiringMerc(SOLDIERTYPE *pFiredSoldier, INT8 bMoraleEvent ); void HandleSoldierLeavingForAnotherContract( SOLDIERTYPE *pSoldier ); //BOOLEAN SoldierWantsToDelayRenewalOfContract( SOLDIERTYPE *pSoldier ); void HandleNotifyPlayerCantAffordInsurance( void ); void HandleNotifyPlayerCanAffordInsurance( SOLDIERTYPE *pSoldier, UINT8 ubLength, INT32 iCost ); void ExtendMercInsuranceContractCallBack( UINT8 bExitValue ); void HandleUniqueEventWhenPlayerLeavesTeam( SOLDIERTYPE *pSoldier ); UINT32 uiContractTimeMode = 0; SOLDIERTYPE *pLeaveSoldier = NULL; BOOLEAN fEnterMapDueToContract = FALSE; extern BOOLEAN fOneFrame; extern BOOLEAN fPausedTimeDuringQuote; UINT8 ubQuitType = 0; BOOLEAN gfFirstMercSayQuote = FALSE; extern BOOLEAN gfFirstMercSayingQuoteWillLeaveNoMatterWhat; extern CHAR16 gzUserDefinedButton1[ 128 ]; extern CHAR16 gzUserDefinedButton2[ 128 ]; SOLDIERTYPE *pContractReHireSoldier = NULL; UINT8 gubContractLength = 0; //used when extending a mercs insurance contract SOLDIERTYPE *gpInsuranceSoldier=NULL; // WDS - make number of mercenaries, etc. be configurable // The values need to be saved! std::vector ContractRenewalList (CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS); UINT8 ubNumContractRenewals = 0; // end UINT8 ubCurrentContractRenewal = 0; UINT8 ubCurrentContractRenewalInProgress = FALSE; BOOLEAN gfContractRenewalSquenceOn = FALSE; BOOLEAN gfInContractMenuFromRenewSequence = FALSE; BOOLEAN SaveContractRenewalDataToSaveGameFile( HWFILE hFile ) { UINT32 uiNumBytesWritten; for (int idx=0; idx < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; idx++) { FileWrite( hFile, &ContractRenewalList[idx], sizeof( CONTRACT_NEWAL_LIST_NODE ), &uiNumBytesWritten ); if( uiNumBytesWritten != sizeof( CONTRACT_NEWAL_LIST_NODE ) ) { return( FALSE ); } } //FileWrite( hFile, ContractRenewalList, sizeof( ContractRenewalList ), &uiNumBytesWritten ); //if( uiNumBytesWritten != sizeof( ContractRenewalList ) ) //{ // return( FALSE ); //} FileWrite( hFile, &ubNumContractRenewals, sizeof( ubNumContractRenewals ), &uiNumBytesWritten ); if( uiNumBytesWritten != sizeof( ubNumContractRenewals ) ) { return( FALSE ); } return( TRUE ); } BOOLEAN LoadContractRenewalDataFromSaveGameFile( HWFILE hFile ) { UINT32 uiNumBytesRead; for (int idx=0; idx < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; idx++) { FileRead( hFile, &ContractRenewalList[idx], sizeof( CONTRACT_NEWAL_LIST_NODE ), &uiNumBytesRead ); if( uiNumBytesRead != sizeof( CONTRACT_NEWAL_LIST_NODE ) ) { return( FALSE ); } } //FileRead( hFile, ContractRenewalList, sizeof( ContractRenewalList ), &uiNumBytesRead ); //if( uiNumBytesRead != sizeof( ContractRenewalList ) ) //{ // return( FALSE ); //} FileRead( hFile, &ubNumContractRenewals, sizeof( ubNumContractRenewals ), &uiNumBytesRead ); if( uiNumBytesRead != sizeof( ubNumContractRenewals ) ) { return( FALSE ); } return( TRUE ); } void BeginContractRenewalSequence( ) { INT32 cnt; SOLDIERTYPE *pSoldier; BOOLEAN fFoundAtLeastOne = FALSE; if ( ubNumContractRenewals > 0) { for ( cnt = 0; cnt < ubNumContractRenewals; cnt++ ) { // Get soldier - if there is none, adavance to next pSoldier = FindSoldierByProfileID( ContractRenewalList[ cnt ].ubProfileID, FALSE ); // Steve Willis, 80 if ( pSoldier ) { if( ( pSoldier->bActive == FALSE ) || ( pSoldier->stats.bLife == 0 ) || ( pSoldier->bAssignment == IN_TRANSIT ) ||( pSoldier->bAssignment == ASSIGNMENT_POW ) ) { // no continue; } // Double check there are valid people here that still want to renew... //if the user hasnt renewed yet, and is still leaving today if ( ContractIsExpiring( pSoldier ) ) { fFoundAtLeastOne = TRUE; } } } if ( fFoundAtLeastOne ) { // Set sequence on... gfContractRenewalSquenceOn = TRUE; // Start at first one.... ubCurrentContractRenewal = 0; ubCurrentContractRenewalInProgress = 0; PauseGame( ); LockPauseState( 7 ); InterruptTime( ); // Go into mapscreen if not already... SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_ENTER_MAPSCREEN,0,0,0,0,0 ); } } } void HandleContractRenewalSequence( ) { SOLDIERTYPE *pSoldier; if ( gfContractRenewalSquenceOn ) { // Should we stop now? if ( ubCurrentContractRenewal == ubNumContractRenewals ) { // Stop and clear any on list... ubNumContractRenewals = 0; gfContractRenewalSquenceOn = FALSE; } // Get soldier - if there is none, adavance to next pSoldier = FindSoldierByProfileID( ContractRenewalList[ ubCurrentContractRenewal ].ubProfileID, FALSE ); // Steve Willis, 80 if ( pSoldier == NULL ) { // Advance to next guy! EndCurrentContractRenewal( ); return; } // OK, check if it's in progress... if ( !ubCurrentContractRenewalInProgress ) { // Double check contract situation.... if ( ContractIsExpiring( pSoldier ) ) { // Set this one in motion! ubCurrentContractRenewalInProgress = 1; // Handle start here... // Determine what quote to use.... if( !WillMercRenew( pSoldier, FALSE ) ) { // OK, he does not want to renew....... HandleImportantMercQuote( pSoldier, QUOTE_MERC_LEAVING_ALSUCO_SOON ); // Do special dialogue event... SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_CONTRACT_NOGO_TO_RENEW , pSoldier->ubID,0 ,0 ,0 ,0 ); } else { // OK check what dialogue to play // If we have not used this one before.... if ( pSoldier->ubContractRenewalQuoteCode == SOLDIER_CONTRACT_RENEW_QUOTE_NOT_USED ) { SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,1 ,MAP_SCREEN ,0 ,0 ,0 ); HandleImportantMercQuote( pSoldier, QUOTE_CONTRACTS_OVER ); SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,0 ,MAP_SCREEN ,0 ,0 ,0 ); } // Else if we have said 89 already...... else if ( pSoldier->ubContractRenewalQuoteCode == SOLDIER_CONTRACT_RENEW_QUOTE_89_USED ) { SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,1 ,MAP_SCREEN ,0 ,0 ,0 ); HandleImportantMercQuote( pSoldier, QUOTE_MERC_LEAVING_ALSUCO_SOON ); SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,0 ,MAP_SCREEN ,0 ,0 ,0 ); } // Do special dialogue event... SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_CONTRACT_WANTS_TO_RENEW , pSoldier->ubID,0 ,0 ,0 ,0 ); } } else { // Skip to next guy! EndCurrentContractRenewal( ); } } } } void EndCurrentContractRenewal( ) { // Are we in the requence? if ( gfContractRenewalSquenceOn ) { // OK stop this one and increment current one ubCurrentContractRenewalInProgress = FALSE; gfInContractMenuFromRenewSequence = FALSE; ubCurrentContractRenewal++; } } void HandleMercIsWillingToRenew( SoldierID ubID ) { SOLDIERTYPE *pSoldier = ubID; // We wish to lock interface SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,1,MAP_SCREEN,0,0,0 ); CheckIfSalaryIncreasedAndSayQuote( pSoldier, FALSE ); // Setup variable for this.... gfInContractMenuFromRenewSequence = TRUE; // Show contract menu TacticalCharacterDialogueWithSpecialEvent( pSoldier, 0, DIALOGUE_SPECIAL_EVENT_SHOW_CONTRACT_MENU, 0,0 ); // Unlock now SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,0 ,MAP_SCREEN ,0 ,0 ,0 ); } void HandleMercIsNotWillingToRenew( SoldierID ubID ) { SOLDIERTYPE *pSoldier = ubID; // We wish to lock interface SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,1,MAP_SCREEN,0,0,0 ); // Setup variable for this.... gfInContractMenuFromRenewSequence = TRUE; // Show contract menu TacticalCharacterDialogueWithSpecialEvent( pSoldier, 0, DIALOGUE_SPECIAL_EVENT_SHOW_CONTRACT_MENU, 0,0 ); // Unlock now SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,0 ,MAP_SCREEN ,0 ,0 ,0 ); } // This is used only to EXTEND the contract of an AIM merc already on the team BOOLEAN MercContractHandling( SOLDIERTYPE *pSoldier, UINT8 ubDesiredAction ) { INT32 iContractCharge=0; INT32 iContractLength=0; UINT8 ubHistoryContractType=0; UINT8 ubFinancesContractType=0; INT32 iCostOfInsurance = 0; //determins what kind of merc the contract is being extended for (only aim mercs can extend contract) if( pSoldier->ubWhatKindOfMercAmI != MERC_TYPE__AIM_MERC ) return(FALSE); switch( ubDesiredAction ) { case CONTRACT_EXTEND_1_DAY: //check to see if the merc has enough money iContractCharge = gMercProfiles[ pSoldier->ubProfile ].sSalary; //set the contract length and the charge iContractLength = 1; ubHistoryContractType = HISTORY_EXTENDED_CONTRACT_1_DAY; ubFinancesContractType = EXTENDED_CONTRACT_BY_1_DAY; break; case CONTRACT_EXTEND_1_WEEK: iContractCharge = gMercProfiles[ pSoldier->ubProfile ].uiWeeklySalary; //set the contract length and the charge iContractLength = 7; ubHistoryContractType = HISTORY_EXTENDED_CONTRACT_1_WEEK; ubFinancesContractType = EXTENDED_CONTRACT_BY_1_WEEK; break; case CONTRACT_EXTEND_2_WEEK: iContractCharge = gMercProfiles[ pSoldier->ubProfile ].uiBiWeeklySalary; //set the contract length and the charge iContractLength = 14; ubHistoryContractType = HISTORY_EXTENDED_CONTRACT_2_WEEK; ubFinancesContractType = EXTENDED_CONTRACT_BY_2_WEEKS; break; default: return(FALSE); break; } //check to see if the merc has enough money if( LaptopSaveInfo.iCurrentBalance < iContractCharge ) return(FALSE); //Check to see if merc will renew if( !WillMercRenew( pSoldier, TRUE ) ) { // Remove soldier.... ( if this is setup because normal contract ending dequence... ) if ( ContractIsExpiring( pSoldier ) ) { TacticalCharacterDialogueWithSpecialEvent( pSoldier, 0, DIALOGUE_SPECIAL_EVENT_CONTRACT_ENDING, 1,0 ); } return(FALSE); } fPausedTimeDuringQuote = TRUE; SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,1 ,MAP_SCREEN ,0 ,0 ,0 ); // // These calcs need to be done before Getting/Calculating the insurance costs // //set the contract length and the charge pSoldier->iTotalContractLength += iContractLength; // pSoldier->iTotalContractCharge = iContractCharge; pSoldier->bTypeOfLastContract = ubDesiredAction; //determine the end of the contract pSoldier->iEndofContractTime += ( iContractLength * 1440 ); if( ( pSoldier->usLifeInsurance ) && ( pSoldier->bAssignment != ASSIGNMENT_POW ) ) // DEF: Removed cause they can extend a 1 day contract && ( iContractLength > 1 ) { // check if player can afford insurance, if not, tell them iCostOfInsurance = CalculateInsuranceContractCost( iContractLength, pSoldier->ubProfile ); HandleImportantMercQuote( pSoldier, QUOTE_ACCEPT_CONTRACT_RENEWAL ); if( iCostOfInsurance > LaptopSaveInfo.iCurrentBalance ) { // no can afford HandleNotifyPlayerCantAffordInsurance( ); // OK, handle ending of renew session if ( gfInContractMenuFromRenewSequence ) { EndCurrentContractRenewal( ); } } else { // can afford ask if they want it HandleNotifyPlayerCanAffordInsurance( pSoldier, ( UINT8 )( iContractLength ), iCostOfInsurance ); } } else { // no need to query for life insurance HandleImportantMercQuote( pSoldier, QUOTE_ACCEPT_CONTRACT_RENEWAL ); // OK, handle ending of renew session if ( gfInContractMenuFromRenewSequence ) { EndCurrentContractRenewal( ); } } SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,0 ,MAP_SCREEN ,0 ,0 ,0 ); // ATE: Setup when they can be signed again! // If they are 2-weeks this can be extended // otherwise don't change from current if ( pSoldier->bTypeOfLastContract == CONTRACT_EXTEND_2_WEEK ) { pSoldier->iTimeCanSignElsewhere = pSoldier->iEndofContractTime; } // ARM: Do not reset because of renewal! The quote is for early dismissal from *initial* time of hiring // pSoldier->uiTimeOfLastContractUpdate = GetWorldTotalMin(); // ARM: Do not reset because of renewal! The deposit in the profile goes up when merc levels, but the one in the soldier // structure must always reflect the deposit actually paid (which does NOT change when a merc levels). // pSoldier->usMedicalDeposit = gMercProfiles[ pSoldier->ubProfile ].sMedicalDepositAmount; //add an entry in the finacial page for the extending of the mercs contract AddTransactionToPlayersBook( ubFinancesContractType, pSoldier->ubProfile, GetWorldTotalMin(), -iContractCharge ); //add an entry in the history page for the extending of the merc contract AddHistoryToPlayersLog( ubHistoryContractType, pSoldier->ubProfile, GetWorldTotalMin(), pSoldier->sSectorX, pSoldier->sSectorY ); return( TRUE ); } BOOLEAN WillMercRenew( SOLDIERTYPE *pSoldier, BOOLEAN fSayQuote ) { UINT8 i; UINT8 bMercID; BOOLEAN fBuddyAround = FALSE; BOOLEAN fUnhappy = FALSE; UINT16 usBuddyQuote=0; UINT16 usReasonQuote=0; BOOLEAN fSayPrecedent = FALSE; SOLDIERTYPE * pHated; if( pSoldier->ubWhatKindOfMercAmI != MERC_TYPE__AIM_MERC ) return( FALSE ); // Flugente: an unconscious merc can't say 'yes' to a contract renewal if ( pSoldier->stats.bLife < CONSCIOUSNESS ) return( FALSE ); // does the merc have another contract already lined up? if( pSoldier->flags.fSignedAnotherContract ) { // NOTE: Having a buddy around will NOT stop a merc from leaving on another contract (IC's call) if( fSayQuote == TRUE ) { SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,1 ,MAP_SCREEN ,0 ,0 ,0 ); HandleImportantMercQuote( pSoldier, QUOTE_WONT_RENEW_CONTRACT_LAME_REFUSAL ); SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,0 ,MAP_SCREEN ,0 ,0 ,0 ); } return( FALSE ); } // find out if the merc has a buddy working for the player // loop through the list of people the merc considers buddies for(i=0; i<6; i++) { if( i<5 ) { bMercID = gMercProfiles[ pSoldier->ubProfile ].bBuddy[i]; } else { bMercID = gMercProfiles[ pSoldier->ubProfile ].bLearnToLike; // ignore learn to like, if he's not a buddy yet if( gMercProfiles[ pSoldier->ubProfile ].bLearnToLikeCount > 0 ) continue; } if( bMercID < 0 ) continue; // is this buddy on the team? if( IsMercOnTeam( (UINT8) bMercID, FALSE, FALSE ) ) { fBuddyAround = TRUE; if( i == 0 ) usBuddyQuote = QUOTE_RENEWING_CAUSE_BUDDY_1_ON_TEAM; else if( i == 1 ) usBuddyQuote = QUOTE_RENEWING_CAUSE_BUDDY_2_ON_TEAM; else if( i == 2 ) usBuddyQuote = QUOTE_RENEWING_CAUSE_BUDDY_3_ON_TEAM; else if( i == 3 ) usBuddyQuote = QUOTE_RENEWING_CAUSE_BUDDY_4_ON_TEAM; else if( i == 4 ) usBuddyQuote = QUOTE_RENEWING_CAUSE_BUDDY_5_ON_TEAM; else usBuddyQuote = QUOTE_RENEWING_CAUSE_LEARNED_TO_LIKE_BUDDY_ON_TEAM; // use first buddy in case there are multiple break; } } // WE CHECK FOR SOURCES OF UNHAPPINESS IN ORDER OF IMPORTANCE, which is: // 1) Hated Mercs (Highest), 2) Death Rate, 3) Morale (lowest) // see if someone the merc hates is on the team // loop through the list of people the merc hates for(i=0; i<5; i++) { bMercID = gMercProfiles[ pSoldier->ubProfile ].bHated[ i ]; if( bMercID < 0 ) continue; if( IsMercOnTeam( (UINT8) bMercID, TRUE, TRUE ) ) { if ( gMercProfiles[ pSoldier->ubProfile ].bHatedCount[ i ] == 0 ) { // our tolerance has run out! fUnhappy = TRUE; } else // else tolerance is > 0, only gripe if in same sector { pHated = FindSoldierByProfileID( bMercID, TRUE ); if ( pHated && pHated->sSectorX == pSoldier->sSectorX && pHated->sSectorY == pSoldier->sSectorY && pHated->bSectorZ == pSoldier->bSectorZ ) { fUnhappy = TRUE; } } if ( fUnhappy ) { if( i == 0 ) usReasonQuote = QUOTE_HATED_1_ON_TEAM_WONT_RENEW; else if( i == 1 ) usReasonQuote = QUOTE_HATED_2_ON_TEAM_WONT_RENEW; else if( i == 2 ) usReasonQuote = QUOTE_HATED_3_ON_TEAM_WONT_RENEW; else if( i == 3 ) usReasonQuote = QUOTE_HATED_4_ON_TEAM_WONT_RENEW; else if( i == 4 ) usReasonQuote = QUOTE_HATED_5_ON_TEAM_WONT_RENEW; // use first hated in case there are multiple break; } } } if ( !fUnhappy ) { // now check for learn to hate bMercID = gMercProfiles[ pSoldier->ubProfile ].bLearnToHate; if ( bMercID >= 0 ) { if ( IsMercOnTeam( (UINT8) bMercID, TRUE, TRUE ) ) { if ( gMercProfiles[ pSoldier->ubProfile ].bLearnToHateCount == 0 ) { // our tolerance has run out! fUnhappy = TRUE; usReasonQuote = QUOTE_LEARNED_TO_HATE_ON_TEAM_WONT_RENEW; } else if ( gMercProfiles[ pSoldier->ubProfile ].bLearnToHateCount <= gMercProfiles[ pSoldier->ubProfile ].bLearnToHateTime / 2 ) { pHated = FindSoldierByProfileID( bMercID, TRUE ); if ( pHated && pHated->sSectorX == pSoldier->sSectorX && pHated->sSectorY == pSoldier->sSectorY && pHated->bSectorZ == pSoldier->bSectorZ ) { fUnhappy = TRUE; usReasonQuote = QUOTE_LEARNED_TO_HATE_ON_TEAM_WONT_RENEW; } } } } } // happy so far? if (!fUnhappy) { // check if death rate is too high if( MercThinksDeathRateTooHigh( pSoldier->ubProfile ) ) { fUnhappy = TRUE; usReasonQuote = QUOTE_DEATH_RATE_RENEWAL; } } // happy so far? if (!fUnhappy) { // check if morale is too low if( MercThinksHisMoraleIsTooLow( pSoldier ) ) { fUnhappy = TRUE; usReasonQuote = QUOTE_REFUSAL_RENEW_DUE_TO_MORALE; } } // say the precedent? fSayPrecedent = FALSE; // check if we say the precdent for merc if( fSayQuote ) { if (fUnhappy) { if (fBuddyAround) { if( GetMercPrecedentQuoteBitStatus( pSoldier->ubProfile , GetQuoteBitNumberFromQuoteID( ( UINT32 ) ( usBuddyQuote ) ) ) == TRUE ) { fSayPrecedent = TRUE; } else { SetMercPrecedentQuoteBitStatus( pSoldier->ubProfile , GetQuoteBitNumberFromQuoteID( ( UINT32 ) ( usBuddyQuote ) ) ); } } else { if( GetMercPrecedentQuoteBitStatus( pSoldier->ubProfile , GetQuoteBitNumberFromQuoteID( ( UINT32 ) ( usReasonQuote ) ) ) == TRUE ) { fSayPrecedent = TRUE; } else { SetMercPrecedentQuoteBitStatus( pSoldier->ubProfile , GetQuoteBitNumberFromQuoteID( ( UINT32 ) ( usReasonQuote ) ) ); } } } } SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,1 ,MAP_SCREEN ,0 ,0 ,0 ); if( fSayPrecedent ) { HandleImportantMercQuote( pSoldier, QUOTE_PRECEDENT_TO_REPEATING_ONESELF_RENEW ); } SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,0 ,MAP_SCREEN ,0 ,0 ,0 ); // OK, we got all our info, let's make some decisions! if (fUnhappy) { if (fBuddyAround) { // unhappy, but buddy's around, so will agree to renew, but tell us why we're doing it if( fSayQuote == TRUE ) { SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,1,MAP_SCREEN,0,0,0 ); HandleImportantMercQuote( pSoldier, usBuddyQuote); SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,0,MAP_SCREEN,0,0,0 ); } return( TRUE ); } else { // unhappy, no buddies, will refuse to renew if( fSayQuote == TRUE ) { SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,1 ,MAP_SCREEN ,0 ,0 ,0 ); /* ARM: Delay quote too vague, no longer to be used if( ( SoldierWantsToDelayRenewalOfContract( pSoldier ) ) ) { // has a new job lined up HandleImportantMercQuote( pSoldier, QUOTE_DELAY_CONTRACT_RENEWAL ); } else */ { Assert( usReasonQuote != 0 ); HandleImportantMercQuote( pSoldier, usReasonQuote ); } SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,0 ,MAP_SCREEN ,0 ,0 ,0 ); } return(FALSE); } } else { // happy, no problem return( TRUE ); } } void HandleBuddiesReactionToFiringMerc(SOLDIERTYPE *pFiredSoldier, INT8 bMoraleEvent ) { UINT16 bMercID; UINT16 bLastTeamID; SOLDIERTYPE * pSoldier; bMercID = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; bLastTeamID = gTacticalStatus.Team[ gbPlayerNum ].bLastID; // loop through all mercs to find buddies for ( pSoldier = MercPtrs[ bMercID ]; bMercID <= bLastTeamID; bMercID++,pSoldier++) { //if the merc is active, in Arulco, not POW and is a buddy if ( WhichBuddy(pSoldier->ubProfile,pFiredSoldier->ubProfile) != (-1) && pSoldier->bActive && pSoldier->ubProfile != NO_PROFILE && !(pSoldier->bAssignment == IN_TRANSIT || pSoldier->bAssignment == ASSIGNMENT_DEAD || pSoldier->bAssignment == ASSIGNMENT_POW) ) { HandleMoraleEvent(pSoldier, bMoraleEvent, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ); } } } void HandleSoldierLeavingWithLowMorale( SOLDIERTYPE *pSoldier ) { if( MercThinksHisMoraleIsTooLow( pSoldier ) ) { // this will cause him give us lame excuses for a while until he gets over it // 3-6 days (but the first 1-2 days of that are spent "returning" home) gMercProfiles[ pSoldier->ubProfile ].ubDaysOfMoraleHangover = (UINT8) (3 + Random(4)); // piss off his buddies too HandleBuddiesReactionToFiringMerc(pSoldier, MORALE_BUDDY_FIRED_ON_BAD_TERMS); } } void HandleSoldierLeavingForAnotherContract( SOLDIERTYPE *pSoldier ) { if (pSoldier->flags.fSignedAnotherContract) { // merc goes to work elsewhere gMercProfiles[ pSoldier->ubProfile ].bMercStatus = MERC_WORKING_ELSEWHERE; gMercProfiles[ pSoldier->ubProfile ].uiDayBecomesAvailable += 1 + Random(6 + (pSoldier->stats.bExpLevel / 2) ); // 1-(6 to 11) days } } /* BOOLEAN SoldierWantsToDelayRenewalOfContract( SOLDIERTYPE *pSoldier ) { INT8 bTypeOfCurrentContract = 0; // what kind of contract the merc has..1 day, week or 2 week INT32 iLeftTimeOnContract = 0; // how much time til contract expires..in minutes INT32 iToleranceLevelForContract = 0; // how much time before contract ends before merc actually speaks thier mind // does the soldier want to delay renew of contract, possibly due to poor performance by player if( pSoldier->ubWhatKindOfMercAmI != MERC_TYPE__AIM_MERC ) return( FALSE ); // type of contract the merc had bTypeOfCurrentContract = pSoldier->bTypeOfLastContract; iLeftTimeOnContract = pSoldier->iEndofContractTime - GetWorldTotalMin(); // grab tolerance switch( bTypeOfCurrentContract ) { case( CONTRACT_EXTEND_1_DAY ): // 20 hour tolerance on 24 hour contract iToleranceLevelForContract = 20 * 60; break; case( CONTRACT_EXTEND_1_WEEK ): // two day tolerance for 1 week iToleranceLevelForContract = 2 * 24 * 60; break; case( CONTRACT_EXTEND_2_WEEK ): // three day on 2 week contract iToleranceLevelForContract = 3 * 24 * 60; break; } if( iLeftTimeOnContract > iToleranceLevelForContract ) { return( TRUE ); } else { return( FALSE ); } } */ // this is called once a day (daily update) for every merc working for the player void CheckIfMercGetsAnotherContract( SOLDIERTYPE *pSoldier ) { UINT32 uiFullDaysRemaining = 0; INT32 iChance = 0; // aim merc? if( pSoldier->ubWhatKindOfMercAmI != MERC_TYPE__AIM_MERC ) return; // ATE: check time we have and see if we can accept new contracts.... if ( GetWorldTotalMin() <= (UINT32) pSoldier->iTimeCanSignElsewhere ) { return; } // if he doesn't already have another contract if (!pSoldier->flags.fSignedAnotherContract) { // chance depends on how much time he has left in his contract, and his experience level (determines demand) uiFullDaysRemaining = (pSoldier->iEndofContractTime - GetWorldTotalMin()) / (24 * 60); if (uiFullDaysRemaining == 0) { // less than a full day left on contract // calc the chance merc will get another contract while working for ya (this is rolled once/day) iChance = 3; } else if (uiFullDaysRemaining == 1) { // < 2 days left iChance = 2; } else if (uiFullDaysRemaining == 2) { // < 3 days left iChance = 1; } else { // 3+ days iChance = 0; } // multiply by experience level iChance *= pSoldier->stats.bExpLevel; //tais: disabled mercs going on other assignment after being fired.. if( (INT32) Random( 100 ) < iChance && gGameExternalOptions.fMercsOnAssignment < 2 ) { // B'bye! pSoldier->flags.fSignedAnotherContract = TRUE; } } } //for ubRemoveType pass in the enum from the .h, ( MERC_QUIT, MERC_FIRED ) BOOLEAN BeginStrategicRemoveMerc( SOLDIERTYPE *pSoldier, BOOLEAN fAddRehireButton ) { InterruptTime( ); PauseGame(); LockPauseState( 8 ); //if the soldier may have some special action when he/she leaves the party, handle it HandleUniqueEventWhenPlayerLeavesTeam( pSoldier ); // IF the soldier is an EPC, don't ask about equipment if ( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__EPC ) { UnEscortEPC( pSoldier ); } else { if (!is_networked) NotifyPlayerOfMercDepartureAndPromptEquipmentPlacement( pSoldier, fAddRehireButton ); else { // WANNE - MP: Skip all the dialog boxes that appear. Just dismiss the merc StrategicRemoveMerc( pSoldier ); pLeaveSoldier = NULL; } } return( TRUE ); } BOOLEAN StrategicRemoveMerc( SOLDIERTYPE *pSoldier ) { UINT8 ubHistoryCode=0; if ( gfInContractMenuFromRenewSequence ) { EndCurrentContractRenewal( ); } // Flugente: if this is Buns alternate personality, change her back. // This way we don't have to deal with her transformation while she is absent (and we don't need additional laptop soundfiles). if ( pSoldier->ubProfile == BUNS_CHAOTIC ) { gMercProfiles[BUNS].bNPCData = 0; SwapToProfile( pSoldier, BUNS ); } // anv: let buddies react HandleBuddiesReactionToFiringMerc(pSoldier, MORALE_BUDDY_FIRED); // ATE: Determine which HISTORY ENTRY to use... if ( pSoldier->ubLeaveHistoryCode == 0 ) { // Default use contract expired reason... pSoldier->ubLeaveHistoryCode = HISTORY_MERC_CONTRACT_EXPIRED; } ubHistoryCode = pSoldier->ubLeaveHistoryCode; //if the soldier is DEAD if( pSoldier->stats.bLife <= 0 ) { AddCharacterToDeadList( pSoldier ); } //else if the merc was fired else if( ubHistoryCode == HISTORY_MERC_FIRED || pSoldier->bAssignment == ASSIGNMENT_POW ) { AddCharacterToFiredList( pSoldier ); } //The merc is leaving for some other reason else { AddCharacterToOtherList( pSoldier ); } if( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__NPC ) { SetupProfileInsertionDataForSoldier( pSoldier ); } if (IsVehicle(pSoldier)) { // for some reason, vehicles have their own idea of handling group ids RemovePlayerFromGroup(pVehicleList[ pSoldier->bVehicleID ].ubMovementGroup, pSoldier); } //remove him from the soldier structure if( pSoldier->bAssignment >= ON_DUTY ) { // is he/she in a mvt group, if so, remove and destroy the group if( pSoldier->ubGroupID ) { // anv: dead people are on "dead" assignment even if they were in vehicle pre-mortem, check flags too if( ( pSoldier->bAssignment != VEHICLE ) && !( pSoldier->flags.uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) ) ) //if ( pSoldier->bAssignment != VEHICLE ) { //Can only remove groups if they aren't persistant (not in a squad or vehicle) RemovePlayerFromGroup(pSoldier->ubGroupID, pSoldier); } else { // remove him from any existing merc slot he could be in RemoveMercSlot( pSoldier ); TakeSoldierOutOfVehicle( pSoldier ); } } } else { RemoveCharacterFromSquads( pSoldier ); } // if the merc is not dead if( gMercProfiles[ pSoldier->ubProfile ].bMercStatus != MERC_IS_DEAD ) { //shadooow: this allows RPCs to re-appear in the areas we recruited them initially after being fired if (pSoldier->ubProfile != SLAY || gGameExternalOptions.fEnableSlayForever) { gMercProfiles[pSoldier->ubProfile].ubMiscFlags &= (~PROFILE_MISC_FLAG_RECRUITED); } //Set the status to returning home ( delay the merc for rehire ) gMercProfiles[ pSoldier->ubProfile ].bMercStatus = MERC_RETURNING_HOME; // specify how long the merc will continue to be unavailable gMercProfiles[ pSoldier->ubProfile ].uiDayBecomesAvailable = 1 + Random(2); // 1-2 days HandleSoldierLeavingWithLowMorale( pSoldier ); HandleSoldierLeavingForAnotherContract( pSoldier ); } //add an entry in the history page for the firing/quiting of the merc // ATE: Don't do this if they are already dead! if ( !( pSoldier->flags.uiStatusFlags & SOLDIER_DEAD ) ) { AddHistoryToPlayersLog( ubHistoryCode, pSoldier->ubProfile, GetWorldTotalMin(), pSoldier->sSectorX, pSoldier->sSectorY ); } //if the merc was a POW, remember it becuase the merc cant show up in AIM or MERC anymore if( pSoldier->bAssignment == ASSIGNMENT_POW ) { gMercProfiles[ pSoldier->ubProfile ].bMercStatus = MERC_FIRED_AS_A_POW; } //else the merc CAN get his medical deposit back else { //Determine how much of a Medical deposit is going to be refunded to the player CalculateMedicalDepositRefund( pSoldier ); } //remove the merc from the tactical TacticalRemoveSoldier( pSoldier->ubID ); // Check if we should remove loaded world... CheckAndHandleUnloadingOfCurrentWorld(); if ( guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) { ReBuildCharactersList( ); } fMapPanelDirty = TRUE; fTeamPanelDirty = TRUE; fCharacterInfoPanelDirty = TRUE; // stop time compression so player can react to the departure StopTimeCompression(); // WDS: This allows for replacing dead IMP mercs. See "BtnIMPBeginScreenDoneCallback" in "IMP Begin Screen.cpp" if( ( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__PLAYER_CHARACTER ) && ( gMercProfiles[ pSoldier->ubProfile ].bMercStatus == MERC_IS_DEAD ) ) { // Replace the name with an empty string wcsncpy( gMercProfiles[ pSoldier->ubProfile ].zName, L"", 1 ); } // ATE: update team panels.... UpdateTeamPanelAssignments( ); // And unpause the @#$@#$ interface UnLockPauseState(); UnPauseGame(); if (is_client) send_dismiss(pSoldier->ubID); return( TRUE ); } void CalculateMedicalDepositRefund( SOLDIERTYPE *pSoldier ) { INT32 iRefundAmount=0; //if the merc didnt have any medical deposit, exit if( !gMercProfiles[ pSoldier->ubProfile ].bMedicalDeposit ) return; //if the merc is at full health, refund the full medical deposit if( pSoldier->stats.bLife == pSoldier->stats.bLifeMax ) { //add an entry in the finacial page for the FULL refund of the medical deposit // use the medical deposit in pSoldier, not in profile, which goes up with leveling AddTransactionToPlayersBook(FULL_MEDICAL_REFUND, pSoldier->ubProfile, GetWorldTotalMin(), pSoldier->usMedicalDeposit ); //add an email #ifdef JA2UB //no UB if( gubQuest[ QUEST_FIX_LAPTOP ] == QUESTDONE || gGameUBOptions.LaptopQuestEnabled == FALSE ) { if ( gGameUBOptions.fDeadMerc == TRUE ) AddEmailWithSpecialData( 27, AIM_MEDICAL_DEPOSIT_REFUND_LENGTH, AIM_SITE, GetWorldTotalMin(), pSoldier->usMedicalDeposit, pSoldier->ubProfile, TYPE_EMAIL_DEAD_MERC_AIM_SITE_EMAIL_JA2_EDT, TYPE_E_AIM_L2 ); } #else AddEmailWithSpecialData( AIM_MEDICAL_DEPOSIT_REFUND, AIM_MEDICAL_DEPOSIT_REFUND_LENGTH, AIM_SITE, GetWorldTotalMin(), pSoldier->usMedicalDeposit, pSoldier->ubProfile, TYPE_EMAIL_EMAIL_EDT, TYPE_E_NONE ); #endif } //else if the merc is a dead, refund NOTHING!! else if( pSoldier->stats.bLife <= 0 ) { //add an entry in the finacial page for NO refund of the medical deposit //AddTransactionToPlayersBook( NO_MEDICAL_REFUND, pSoldier->ubProfile, GetWorldTotalMin(), 0 ); //add an email #ifdef JA2UB //no UB if( gubQuest[ QUEST_FIX_LAPTOP ] == QUESTDONE || gGameUBOptions.LaptopQuestEnabled == FALSE ) { if ( gGameUBOptions.fDeadMerc == TRUE ) AddEmailWithSpecialData( 217, AIM_MEDICAL_DEPOSIT_NO_REFUND_LENGTH, AIM_SITE, GetWorldTotalMin(), pSoldier->usMedicalDeposit, pSoldier->ubProfile, TYPE_EMAIL_DEAD_MERC_AIM_SITE_EMAIL_JA2_EDT, TYPE_E_AIM_L3 ); } #else AddEmailWithSpecialData( AIM_MEDICAL_DEPOSIT_NO_REFUND, AIM_MEDICAL_DEPOSIT_NO_REFUND_LENGTH, AIM_SITE, GetWorldTotalMin(), pSoldier->usMedicalDeposit, pSoldier->ubProfile, TYPE_EMAIL_EMAIL_EDT, TYPE_E_NONE ); #endif } //else the player is injured, refund a partial amount else { // use the medical deposit in pSoldier, not in profile, which goes up with leveling iRefundAmount = (INT32) ( ( pSoldier->stats.bLife / ( FLOAT ) pSoldier->stats.bLifeMax ) * pSoldier->usMedicalDeposit + 0.5 ); //add an entry in the finacial page for a PARTIAL refund of the medical deposit AddTransactionToPlayersBook( PARTIAL_MEDICAL_REFUND, pSoldier->ubProfile, GetWorldTotalMin(), iRefundAmount ); //add an email #ifdef JA2UB // no UB if( gubQuest[ QUEST_FIX_LAPTOP ] == QUESTDONE || gGameUBOptions.LaptopQuestEnabled == FALSE ) { if ( gGameUBOptions.fDeadMerc == TRUE ) AddEmailWithSpecialData( 214, AIM_MEDICAL_DEPOSIT_PARTIAL_REFUND_LENGTH, AIM_SITE, GetWorldTotalMin(), iRefundAmount, pSoldier->ubProfile, TYPE_EMAIL_DEAD_MERC_AIM_SITE_EMAIL_JA2_EDT, TYPE_E_AIM_L4 ); } #else AddEmailWithSpecialData( AIM_MEDICAL_DEPOSIT_PARTIAL_REFUND, AIM_MEDICAL_DEPOSIT_PARTIAL_REFUND_LENGTH, AIM_SITE, GetWorldTotalMin(), iRefundAmount, pSoldier->ubProfile, TYPE_EMAIL_EMAIL_EDT, TYPE_E_NONE ); #endif } } void NotifyPlayerOfMercDepartureAndPromptEquipmentPlacement( SOLDIERTYPE *pSoldier, BOOLEAN fAddRehireButton ) { // will tell player this character is leaving and ask where they want the equipment left CHAR16 sString[ 1024 ]; BOOLEAN fInSector = FALSE; // INT16 zTownIDString[50]; CHAR16 zShortTownIDString[ 50 ], zShortDropOffString[ 50 ], zDropOffString[ 50 ]; // use YES/NO Pop up box, settup for particular screen SGPRect pCenteringRect= {0 + xResOffset, 0 + yResOffset, SCREEN_WIDTH - xResOffset, SCREEN_HEIGHT - yResOffset}; //GetSectorIDString( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ, zTownIDString, TRUE ); GetShortSectorString( pSoldier->sSectorX ,pSoldier->sSectorY, zShortTownIDString ); // Set string for generic button swprintf( gzUserDefinedButton1, L"%s", zShortTownIDString ); pLeaveSoldier = pSoldier; if( pSoldier->flags.fSignedAnotherContract == TRUE ) { fAddRehireButton = FALSE; } if( pSoldier->flags.fSignedAnotherContract == TRUE ) { fAddRehireButton = FALSE; } if( pSoldier->ubWhatKindOfMercAmI != MERC_TYPE__AIM_MERC ) { fAddRehireButton = FALSE; } //if the character is an RPC if ( gMercProfiles[pSoldier->ubProfile].Type == PROFILETYPE_RPC ) { if( gMercProfiles[ pSoldier->ubProfile ].bSex == MALE ) { swprintf( sString, pMercHeLeaveString[ 1 ], pSoldier->GetName(), zShortTownIDString ); } else { swprintf( sString, pMercSheLeaveString[ 1 ], pSoldier->GetName(), zShortTownIDString ); } fInSector = TRUE; } // check if drassen controlled else if( StrategicMap[CALCULATE_STRATEGIC_INDEX( AIRPORT_X, AIRPORT_Y )].fEnemyControlled == FALSE ) { GetSectorIDString( AIRPORT_X, AIRPORT_Y, 0, zDropOffString, FALSE ); if( ( pSoldier->sSectorX == AIRPORT_X ) && ( pSoldier->sSectorY == AIRPORT_Y ) && ( pSoldier->bSectorZ == 0 ) ) { if( gMercProfiles[ pSoldier->ubProfile ].bSex == MALE ) { swprintf( sString, pMercHeLeaveString[ 1 ], pSoldier->GetName(), zDropOffString ); } else { swprintf( sString, pMercSheLeaveString[ 1 ], pSoldier->GetName(), zDropOffString ); } fInSector = TRUE; } else { // Set string for generic button GetShortSectorString( AIRPORT_X, AIRPORT_Y, zShortDropOffString ); swprintf( gzUserDefinedButton2, L"%s", zShortDropOffString ); //B13 if( gMercProfiles[ pSoldier->ubProfile ].bSex == MALE ) { swprintf( sString, pMercHeLeaveString[ 0 ], pSoldier->GetName(), zShortTownIDString, zDropOffString ); } else { swprintf( sString, pMercSheLeaveString[ 0 ], pSoldier->GetName(), zShortTownIDString, zDropOffString ); } } } else { GetSectorIDString( OMERTA_LEAVE_EQUIP_SECTOR_X, OMERTA_LEAVE_EQUIP_SECTOR_Y, 0, zDropOffString, FALSE ); if( ( pSoldier->sSectorX == OMERTA_LEAVE_EQUIP_SECTOR_X ) && ( pSoldier->sSectorY == OMERTA_LEAVE_EQUIP_SECTOR_Y ) && ( pSoldier->bSectorZ == 0 ) ) { if( gMercProfiles[ pSoldier->ubProfile ].bSex == MALE ) { swprintf( sString, pMercHeLeaveString[ 1 ], pSoldier->GetName(), zDropOffString ); } else { swprintf( sString, pMercSheLeaveString[ 1 ], pSoldier->GetName() , zDropOffString ); } fInSector = TRUE; } else { // Set string for generic button GetShortSectorString( OMERTA_LEAVE_EQUIP_SECTOR_X, OMERTA_LEAVE_EQUIP_SECTOR_Y, zShortDropOffString ); swprintf( gzUserDefinedButton2, L"%s", zShortDropOffString ); //A9 if( gMercProfiles[ pSoldier->ubProfile ].bSex == MALE ) { swprintf( sString, pMercHeLeaveString[ 0 ], pSoldier->GetName(), zShortTownIDString, zDropOffString ); } else { swprintf( sString, pMercSheLeaveString[ 0 ], pSoldier->GetName(), zShortTownIDString, zDropOffString ); } } } /// which screen are we in? if ( (guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) ) { if( fInSector == FALSE ) { // set up for mapscreen DoMapMessageBox( MSG_BOX_BASIC_STYLE, sString, MAP_SCREEN, ( UINT16 )( ( fAddRehireButton ? MSG_BOX_FLAG_GENERICCONTRACT : MSG_BOX_FLAG_GENERIC_TWO_BUTTONS ) ), MercDepartEquipmentBoxCallBack ); } else { DoMapMessageBox( MSG_BOX_BASIC_STYLE, sString, MAP_SCREEN, ( UINT16 )( ( fAddRehireButton ? MSG_BOX_FLAG_OKCONTRACT : MSG_BOX_FLAG_OK ) ), MercDepartEquipmentBoxCallBack ); } } else { if( fInSector == FALSE ) { // set up for all otherscreens DoMessageBox( MSG_BOX_BASIC_STYLE, sString, guiCurrentScreen, ( UINT16 ) ( MSG_BOX_FLAG_USE_CENTERING_RECT | ( fAddRehireButton ? MSG_BOX_FLAG_GENERICCONTRACT : MSG_BOX_FLAG_GENERIC_TWO_BUTTONS ) ), MercDepartEquipmentBoxCallBack, &pCenteringRect ); } else { DoMessageBox( MSG_BOX_BASIC_STYLE, sString, guiCurrentScreen, ( UINT16 ) ( MSG_BOX_FLAG_USE_CENTERING_RECT | ( fAddRehireButton ? MSG_BOX_FLAG_OKCONTRACT : MSG_BOX_FLAG_OK ) ) , MercDepartEquipmentBoxCallBack, &pCenteringRect ); } } if( pSoldier->flags.fSignedAnotherContract == TRUE ) { //fCurrentMercFired = FALSE; } } void MercDepartEquipmentBoxCallBack( UINT8 bExitValue ) { // gear left in current sector? if( pLeaveSoldier == NULL ) { return; } if( bExitValue == MSG_BOX_RETURN_OK ) { // yep (NOTE that this passes the SOLDIER index, not the PROFILE index as the others do) HandleLeavingOfEquipmentInCurrentSector( pLeaveSoldier->ubID ); // aim merc will say goodbye when leaving if( ( pLeaveSoldier->ubWhatKindOfMercAmI == MERC_TYPE__AIM_MERC ) && ( ubQuitType != HISTORY_MERC_FIRED ) ) { // TacticalCharacterDialogue( pLeaveSoldier, QUOTE_MERC_LEAVING_ALSUCO_SOON ); } } else if( bExitValue == MSG_BOX_RETURN_CONTRACT ) { HandleExtendMercsContract( pLeaveSoldier ); UnLockPauseState(); return; } else if( bExitValue == MSG_BOX_RETURN_YES ) { // yep (NOTE that this passes the SOLDIER index, not the PROFILE index as the others do) HandleLeavingOfEquipmentInCurrentSector( pLeaveSoldier->ubID ); // aim merc will say goodbye when leaving if( ( pLeaveSoldier->ubWhatKindOfMercAmI == MERC_TYPE__AIM_MERC ) && ( ubQuitType != HISTORY_MERC_FIRED ) ) { // TacticalCharacterDialogue( pLeaveSoldier, QUOTE_MERC_LEAVING_ALSUCO_SOON ); } } else { // no if( StrategicMap[CALCULATE_STRATEGIC_INDEX( AIRPORT_X, AIRPORT_Y )].fEnemyControlled == FALSE ) { HandleMercLeavingEquipmentInDrassen( pLeaveSoldier->ubID ); } else { HandleMercLeavingEquipmentInOmerta( pLeaveSoldier->ubID ); } } StrategicRemoveMerc( pLeaveSoldier ); pLeaveSoldier = NULL; return; } BOOLEAN HandleFiredDeadMerc( SOLDIERTYPE *pSoldier ) { AddCharacterToDeadList( pSoldier ); return( TRUE ); } void HandleExtendMercsContract( SOLDIERTYPE *pSoldier ) { if ( !(guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) ) { gfEnteringMapScreen = TRUE; fEnterMapDueToContract = TRUE; pContractReHireSoldier = pSoldier; LeaveTacticalScreen( MAP_SCREEN ); uiContractTimeMode = TIME_COMPRESS_5MINS; } else { FindAndSetThisContractSoldier( pSoldier ); pContractReHireSoldier = pSoldier; uiContractTimeMode = giTimeCompressMode; } fTeamPanelDirty = TRUE; fCharacterInfoPanelDirty = TRUE; SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE, 1, MAP_SCREEN, 0, 0, 0 ); CheckIfSalaryIncreasedAndSayQuote( pSoldier, TRUE ); SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE, 0 ,MAP_SCREEN, 0, 0, 0 ); return; } void FindOutIfAnyMercAboutToLeaveIsGonnaRenew( void ) { // find out is something was said SOLDIERTYPE *pSoldier = NULL, *pSoldierWhoWillQuit = NULL; INT32 iCounter= 0, iNumberOnTeam = 0; // WDS - make number of mercenaries, etc. be configurable SoldierID ubPotentialMercs[ CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ] = { 0 }; UINT16 ubNumMercs = 0; UINT16 ubChosenMerc; gfFirstMercSayQuote = FALSE; pSoldier = &Menptr[ 0 ]; iNumberOnTeam =gTacticalStatus.Team[ OUR_TEAM ].bLastID; // run through list of grunts whoose contract are up in the next 2 hours // ATE: AND - build list THEN choose one! // What we will do here is make a list of mercs that will want // to stay if offered. Durning that process, also check if there // is any merc that does not want to stay and only display that quote // if they are the only one here.... for( iCounter = 0; iCounter < iNumberOnTeam; iCounter++ ) { pSoldier = &Menptr[ iCounter ]; // valid soldier? if( ( pSoldier->bActive == FALSE ) || ( pSoldier->stats.bLife == 0 ) || ( pSoldier->bAssignment == IN_TRANSIT ) ||( pSoldier->bAssignment == ASSIGNMENT_POW ) ) { // no continue; } if( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__AIM_MERC ) { //if the user hasnt renewed yet, and is still leaving today if ( ContractIsGoingToExpireSoon( pSoldier ) ) { // OK, default value for quote said pSoldier->ubContractRenewalQuoteCode = SOLDIER_CONTRACT_RENEW_QUOTE_NOT_USED; // Add this guy to the renewal list ContractRenewalList[ ubNumContractRenewals ].ubProfileID = pSoldier->ubProfile; ubNumContractRenewals++; if( WillMercRenew( pSoldier, FALSE ) ) { ubPotentialMercs[ ubNumMercs ] = pSoldier->ubID; ubNumMercs++; } else { pSoldierWhoWillQuit = pSoldier; } // Add to list! AddSoldierToWaitingListQueue( pSoldier ); } } else { if( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__MERC ) { // Do nothing here for now... } } } // OK, check if we should display line for the guy who does not want // to stay if ( ubNumMercs == 0 && pSoldierWhoWillQuit != NULL ) { // OK, he does not want to renew....... HandleImportantMercQuote( pSoldierWhoWillQuit, QUOTE_MERC_LEAVING_ALSUCO_SOON ); AddReasonToWaitingListQueue( CONTRACT_EXPIRE_WARNING_REASON ); TacticalCharacterDialogueWithSpecialEvent( pSoldierWhoWillQuit, 0, DIALOGUE_SPECIAL_EVENT_SHOW_UPDATE_MENU, 0,0 ); pSoldierWhoWillQuit->ubContractRenewalQuoteCode = SOLDIER_CONTRACT_RENEW_QUOTE_115_USED; } else { // OK, pick one.... if ( ubNumMercs > 0 ) { ubChosenMerc = (UINT16)Random( ubNumMercs ); SOLDIERTYPE *pChosenSoldier = ubPotentialMercs[ubChosenMerc]; SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,1 ,MAP_SCREEN ,0 ,0 ,0 ); HandleImportantMercQuote( pChosenSoldier, QUOTE_CONTRACTS_OVER ); SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,0 ,MAP_SCREEN ,0 ,0 ,0 ); AddReasonToWaitingListQueue( CONTRACT_EXPIRE_WARNING_REASON ); TacticalCharacterDialogueWithSpecialEvent( pChosenSoldier, 0, DIALOGUE_SPECIAL_EVENT_SHOW_UPDATE_MENU, 0,0 ); pChosenSoldier->ubContractRenewalQuoteCode = SOLDIER_CONTRACT_RENEW_QUOTE_89_USED; } } } void HandleNotifyPlayerCantAffordInsurance( void ) { DoScreenIndependantMessageBox( zMarksMapScreenText[ 9 ], MSG_BOX_FLAG_OK, NULL ); } void HandleNotifyPlayerCanAffordInsurance( SOLDIERTYPE *pSoldier, UINT8 ubLength, INT32 iCost ) { CHAR16 sString[ 128 ]; CHAR16 sStringA[ 32 ]; //parse the cost swprintf( sStringA, L"%d",iCost ); // insert the commans and dollar sign InsertCommasForDollarFigure( sStringA ); InsertDollarSignInToString( sStringA ); swprintf( sString, zMarksMapScreenText[ 10 ], pSoldier->GetName(), sStringA, ubLength ); //Set the length to the global variable ( so we know how long the contract is in the callback ) gubContractLength = ubLength; gpInsuranceSoldier = pSoldier; //Remember the soldier aswell pContractReHireSoldier = pSoldier; // now pop up the message box DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_YESNO, ExtendMercInsuranceContractCallBack ); return; } void ExtendMercInsuranceContractCallBack( UINT8 bExitValue ) { if( bExitValue == MSG_BOX_RETURN_YES ) { PurchaseOrExtendInsuranceForSoldier( gpInsuranceSoldier, gubContractLength ); } // OK, handle ending of renew session if ( gfInContractMenuFromRenewSequence ) { EndCurrentContractRenewal( ); } gpInsuranceSoldier = NULL; } void HandleUniqueEventWhenPlayerLeavesTeam( SOLDIERTYPE *pSoldier ) { if (!is_networked) { switch( pSoldier->ubProfile ) { //When iggy leaves the players team, case IGGY: //if he is owed money ( ie the player didnt pay him ) if( gMercProfiles[ pSoldier->ubProfile ].iBalance < 0 ) { //iggy is now available to be handled by the enemy gubFact[ FACT_IGGY_AVAILABLE_TO_ARMY ] = TRUE; } break; } } } UINT32 GetHourWhenContractDone( SOLDIERTYPE *pSoldier ) { UINT32 uiArriveHour; // Get the arrival hour - that will give us when they arrived.... if (!is_networked) { uiArriveHour = ( ( pSoldier->uiTimeSoldierWillArrive ) - ( ( ( pSoldier->uiTimeSoldierWillArrive ) / 1440 ) * 1440 ) ) / 60; } else { // WANNE - MP: For multiplayer game, the merc arrive hour is always the game starting time which is now in multiplayer = GameStartingTime + FirstArrivalDelay // Default value: 07:00 uiArriveHour = (gGameExternalOptions.iGameStartingTime + gGameExternalOptions.iFirstArrivalDelay - NUM_SEC_IN_DAY) / 3600; } return( uiArriveHour ); } BOOLEAN ContractIsExpiring( SOLDIERTYPE *pSoldier ) { UINT32 uiCheckHour; // First at least make sure same day.... if( ( pSoldier->iEndofContractTime /1440 ) <= (INT32)GetWorldDay( ) ) { uiCheckHour = GetHourWhenContractDone( pSoldier ); // See if the hour we are on is the same.... if ( GetWorldHour( ) == uiCheckHour ) { // All's good for go! return( TRUE ); } } return( FALSE ); } BOOLEAN ContractIsGoingToExpireSoon( SOLDIERTYPE *pSoldier ) { // get hour contract is going to expire.... UINT32 uiCheckHour; // First at least make sure same day.... if( ( pSoldier->iEndofContractTime /1440 ) <= (INT32)GetWorldDay( ) ) { uiCheckHour = GetHourWhenContractDone( pSoldier ); // If we are <= 2 hours from expiry. if ( GetWorldHour( ) >= ( uiCheckHour - 2 ) ) { // All's good for go! return( TRUE ); } } return( FALSE ); }