//╒═════════════════════════════════════════════════════════════════════════════╕ //│ Communication Controller Main Script │ //╞═════════════════════════════════════════════════════════════════════════════╡ //│ │ //│ AUTHOR: Ben Rollinson │ //│ DATE: 12/10/10 │ //│ DESCRIPTION: A system used to control off-mission communications │ //│ between characters in the game. │ //│ │ //│ -Handles phone communications from characters to the player. │ //│ -Controls the frequency of communications with the player. │ //│ -Controls the frequency of communications from each character. │ //│ -Prioritises all incoming communications to the player. │ //│ -Intercepts calls from the player to characters. │ //│ │ //╘═════════════════════════════════════════════════════════════════════════════╛ USING "rage_builtins.sch" USING "globals.sch" USING "code_control_public.sch" USING "comms_control_private.sch" USING "comms_control_data_GTA5.sch" USING "code_control_data_GTA5.sch" USING "cellphone_public.sch" USING "vehicle_gen_public.sch" USING "ply_to_ply_calls.sch" //╒═════════════════════════════════════════════════════════════════════════════╕ //╞═══════════════════════════╡ System Variables ╞═════════════════════════════╡ //╘═════════════════════════════════════════════════════════════════════════════╛ BOOL bCommunicationMade // Has a communication been activated this frame? BOOL bFirstCallOngoingHelpQueued BOOL bDoCallBranch = FALSE BOOL bWaitForForceAwayFlagToClear = FALSE INT iTimeCallInitialisationStarted = -1 INT iDialBlimpState // Controls blimp delivery (preorder bonus unlocked after landing on the blimp) BOOL bCBActive = FALSE INT iCBState = -1 // Controls the state of the CB radio controller INT iCBCheckTimer // Stores the last time we checked the CB radio controller INT iCBIdentWaitTimer // Stores the last time we played an Ident type of CB conversation INT iCBMonConvWaitTimer // Stores the last time we played a Monologue or Conversation type of CB conversation INT iSoundID = -1 // Used to stop the background loop noise for the CB radio ENUM CommsControllerState CCS_LOOKING_FOR_COMM, CCS_INITIALISING_COMM, CCS_COMM_IN_PROGRESS ENDENUM #IF IS_DEBUG_BUILD WIDGET_GROUP_ID widgetID BOOL bDoMonologue BOOL bDoOneLine BOOL bDoConv BOOL bFireTestEmails = FALSE INT iForceDebugConv = -1 #ENDIF CommsControllerState eState = CCS_LOOKING_FOR_COMM structPedsForConversation convoStruct //╒═════════════════════════════════════════════════════════════════════════════╕ //╞═════════════════════════════╡ System Cleanup ╞═════════════════════════════╡ //╘═════════════════════════════════════════════════════════════════════════════╛ // PURPOSE: Ensures that the script gets a chance to cleanup under specific circumstances (ie: moving from SP to MP) PROC Script_Cleanup() CPRINTLN(DEBUG_COMMUNICATIONS, "Comms controller cleaning up.") g_iCallInProgress = -1 CC_CallData sBlankCallData INT index REPEAT g_savedGlobals.sCommsControlData.iNoMissedCalls index CPRINTLN(DEBUG_COMMUNICATIONS, "SCRIPT CLEANUP: Removing call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sMissedCalls[index].sCommData.eID), " from the missed call queue.") //Clear the missed caller status of the missed call contact. CLEAR_MISSED_CALLER_STATUS_FOR_CONTACT(g_savedGlobals.sCommsControlData.sMissedCalls[index].sCommData.eNPCCharacter) //Move all array positions up one place and overwrite the call we want to remove. INT index2 FOR index2 = index TO (g_savedGlobals.sCommsControlData.iNoMissedCalls-2) g_savedGlobals.sCommsControlData.sMissedCalls[index2] = g_savedGlobals.sCommsControlData.sMissedCalls[index2+1] ENDFOR //Set the old last missed call in the queue to be blank data to be safe. g_savedGlobals.sCommsControlData.sMissedCalls[g_savedGlobals.sCommsControlData.iNoMissedCalls-1] = sBlankCallData //Reduce the counter tracking the number of missed calls in the queue. g_savedGlobals.sCommsControlData.iNoMissedCalls-- ENDREPEAT //Currently nothing to clean up. TERMINATE_THIS_THREAD() ENDPROC //╒═════════════════════════════════════════════════════════════════════════════╕ //╞══════════════════════════╡ System Subfunctions ╞═══════════════════════════╡ //╘═════════════════════════════════════════════════════════════════════════════╛ PROC Pause_Timers() INT index INT iFrameTime = CEIL(GET_FRAME_TIME()*1000) //Extend global timer g_iGlobalWaitTime += iFrameTime //Extend queued call timers. REPEAT g_savedGlobals.sCommsControlData.iNoQueuedCalls index g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData.iQueueTime += iFrameTime ENDREPEAT //Extend queued text message timers. REPEAT g_savedGlobals.sCommsControlData.iNoQueuedTexts index g_savedGlobals.sCommsControlData.sQueuedTexts[index].sCommData.iQueueTime += iFrameTime ENDREPEAT //Extend queued email timers. REPEAT g_savedGlobals.sCommsControlData.iNoQueuedEmails index g_savedGlobals.sCommsControlData.sQueuedEmails[index].sCommData.iQueueTime += iFrameTime ENDREPEAT //Extend queued chat call timers. REPEAT g_savedGlobals.sCommsControlData.iNoChatCalls index g_savedGlobals.sCommsControlData.sChatCalls[index].sCommData.iQueueTime += iFrameTime ENDREPEAT //Extend character timers. INT iMaxCharacter = ENUM_TO_INT(GLOBAL_CHARACTER_SHEET_GET_MAX_CHARACTERS_FOR_GAMEMODE()) REPEAT iMaxCharacter index g_iCharWaitTime[index] += iFrameTime ENDREPEAT ENDPROC PROC Execute_Special_Code_For_Communication(CC_CommData &sCommData) CPRINTLN(DEBUG_COMMUNICATIONS, "Checking for CodeID to run for communication ", GET_COMM_ID_DEBUG_STRING(sCommData.eID), "...") //Double check we don't have an invalid CID set. IF sCommData.eExecuteOnCompleteID = CID_MAX SCRIPT_ASSERT("Execute_Special_Code_For_Communication: A communication had a CodeID of CID_MAX set. This is an invalid CodeID and should be guarded against!") EXIT ENDIF //Add contact to character phonebooks if requested. IF IS_BIT_SET(sCommData.iSettings, COMM_BIT_ADD_CONTACT) INT iPlayerIndex REPEAT 3 iPlayerIndex IF IS_BIT_SET(sCommData.iPlayerCharBitset, iPlayerIndex) CPRINTLN(DEBUG_COMMUNICATIONS, "Adding contact ", GET_CHARSHEET_DISPLAY_STRING_FROM_CHARSHEET(sCommData.eNPCCharacter), " to ", GET_CHARSHEET_DISPLAY_STRING_FROM_CHARSHEET(INT_TO_ENUM(enumCharacterList, iPlayerIndex)), "'s phonebook as communication ", GET_COMM_ID_DEBUG_STRING(sCommData.eID), " sends.") ADD_CONTACT_TO_PHONEBOOK(sCommData.eNPCCharacter, INT_TO_ENUM(enumPhoneBookPresence, iPlayerIndex), FALSE) ENDIF ENDREPEAT ENDIF //Check if the CID has been set to something of meaning. IF sCommData.eExecuteOnCompleteID <> CID_BLANK //Tell code controller to execute CID without a delay. CPRINTLN(DEBUG_COMMUNICATIONS, "Running ", Get_Debug_String_For_Communication_Code_ID(sCommData.eExecuteOnCompleteID), " with no delay.") Execute_Code_ID(sCommData.eExecuteOnCompleteID) EXIT ENDIF CPRINTLN(DEBUG_COMMUNICATIONS, "No CodeID found.") ENDPROC PROC Process_Cleanup_Of_Sent_Text(INT &gameTime, CC_TextMessageData &sentText) //Execute any special code assigned to this text. Execute_Special_Code_For_Communication(sentText.sCommData) //Register this as the last completed text message. g_savedGlobals.sCommsControlData.eLastCompletedText = sentText.sCommData.eID //Update the system's global wait time. g_iGlobalWaitTime = gameTime + CC_GLOBAL_DELAY_BETWEEN_COMMS //Update the sending character's wait time. g_iCharWaitTime[sentText.sCommData.eNPCCharacter] = gameTime + CC_CHARACTER_DELAY_BETWEEN_COMMS //Remove text from sent text queue. PRIVATE_Remove_Text_From_Sent_Queue(sentText.sCommData.eID) ENDPROC PROC Process_Cleanup_Of_Sent_Email(INT &gameTime, CC_EmailData &sentEmail) //Execute any special code assigned to this text. Execute_Special_Code_For_Communication(sentEmail.sCommData) //Register this as the last completed text message. g_savedGlobals.sCommsControlData.eLastCompletedEmail = sentEmail.sCommData.eID //Update the system's global wait time. g_iGlobalWaitTime = gameTime + CC_GLOBAL_DELAY_BETWEEN_COMMS //Update the sending character's wait time. IF sentEmail.sCommData.eNPCCharacter != CHAR_BLANK_ENTRY g_iCharWaitTime[sentEmail.sCommData.eNPCCharacter] = gameTime + CC_CHARACTER_DELAY_BETWEEN_COMMS ENDIF ENDPROC PROC Check_For_Queued_Phonecall_To_Player_To_Run(INT &gameTime) // Ensure we are a playable character IF NOT IS_PLAYER_PED_PLAYABLE(GET_CURRENT_PLAYER_PED_ENUM()) EXIT ENDIF INT index = 0 WHILE (index < g_savedGlobals.sCommsControlData.iNoQueuedCalls) AND NOT bCommunicationMade Run_Call_Timer_Safeguard(g_savedGlobals.sCommsControlData.sQueuedCalls[index]) //Is this call going to a playable character? IF NOT IS_BIT_SET(g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData.iSettings, COMM_BIT_FROM_CHAR_IS_PLAYER) //Check if the priority of this call is high enough to override character and global timers. BOOL bPriorityTimerOverride = FALSE IF ENUM_TO_INT(g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData.ePriority) >= ENUM_TO_INT(CPR_VERY_HIGH) bPriorityTimerOverride = TRUE ENDIF //Is the global wait timer at 0? IF gameTime >= g_iGlobalWaitTime OR bPriorityTimerOverride OR IS_BIT_SET(g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData.iSettings, COMM_BIT_INGORE_GLOBAL_DELAY) //Is this queued phonecall the correct priority? IF g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData.ePriority = g_savedGlobals.sCommsControlData.eCharacterPriorityLevel[GET_CURRENT_PLAYER_PED_ENUM()] OR NOT IS_BIT_SET(g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData.iPlayerCharBitset, GET_CURRENT_PLAYER_PED_INT()) //Are the characters involved in this call waiting for timers? IF gameTime >= g_iCharWaitTime[g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData.eNPCCharacter] OR bPriorityTimerOverride //Is this queued call ready to be triggered? IF gameTime >= g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData.iQueueTime //Attempt to make the call. IF PRIVATE_Attempt_Make_Queued_Call(g_savedGlobals.sCommsControlData.sQueuedCalls[index], bDoCallBranch) //Register a call in progress. g_iCallInProgress = index iTimeCallInitialisationStarted = -1 eState = CCS_INITIALISING_COMM g_savedGlobals.sCommsControlData.bLastCallHadResponse = FALSE bCommunicationMade = TRUE IF HAS_CELLPHONE_JUST_BEEN_FORCED_AWAY() bWaitForForceAwayFlagToClear = TRUE ENDIF ELSE //Call failed to start. Reschedule the call. CPRINTLN(DEBUG_COMMUNICATIONS, "Call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData.eID), " failed to start. Rescheduling.") PRIVATE_Set_New_Communication_Queue_Time(gameTime, g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData) ENDIF ENDIF ENDIF ENDIF ENDIF ENDIF index++ ENDWHILE ENDPROC PROC Check_For_Queued_Phonecall_From_Player_To_Run(INT &gameTime) // Ensure we are a playable character IF NOT IS_PLAYER_PED_PLAYABLE(GET_CURRENT_PLAYER_PED_ENUM()) EXIT ENDIF INT index = 0 WHILE (index < g_savedGlobals.sCommsControlData.iNoQueuedCalls) AND NOT bCommunicationMade Run_Call_Timer_Safeguard(g_savedGlobals.sCommsControlData.sQueuedCalls[index]) //Is this call coming from a playable character? IF IS_BIT_SET(g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData.iSettings, COMM_BIT_FROM_CHAR_IS_PLAYER) //Check if the priority of this call is high enough to override character and global timers. BOOL bPriorityTimerOverride = FALSE IF ENUM_TO_INT(g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData.ePriority) >= ENUM_TO_INT(CPR_VERY_HIGH) bPriorityTimerOverride = TRUE ENDIF //Is the global wait timer at 0? IF gameTime >= g_iGlobalWaitTime OR IS_BIT_SET(g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData.iSettings, COMM_BIT_INGORE_GLOBAL_DELAY) OR bPriorityTimerOverride //Is this queued phonecall the correct priority? IF g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData.ePriority = g_savedGlobals.sCommsControlData.eCharacterPriorityLevel[GET_CURRENT_PLAYER_PED_ENUM()] OR NOT IS_BIT_SET(g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData.iPlayerCharBitset, GET_CURRENT_PLAYER_PED_INT()) //Is this queued call ready to be triggered? IF gameTime >= g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData.iQueueTime //Attempt to make the call. IF PRIVATE_Attempt_Make_Queued_Call(g_savedGlobals.sCommsControlData.sQueuedCalls[index], bDoCallBranch) //Register a call in progress. g_iCallInProgress = index iTimeCallInitialisationStarted = -1 eState = CCS_INITIALISING_COMM g_savedGlobals.sCommsControlData.bLastCallHadResponse = FALSE bCommunicationMade = TRUE IF HAS_CELLPHONE_JUST_BEEN_FORCED_AWAY() bWaitForForceAwayFlagToClear = TRUE ENDIF ELSE //Call failed to start. Reschedule the call. CPRINTLN(DEBUG_COMMUNICATIONS, "Call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData.eID), " failed to start. Rescheduling.") PRIVATE_Set_New_Communication_Queue_Time(gameTime, g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData) ENDIF ENDIF ENDIF ENDIF ENDIF index++ ENDWHILE ENDPROC PROC Check_For_Queued_Text_Message_To_Run(INT &gameTime) // Ensure we are a playable character IF NOT IS_PLAYER_PED_PLAYABLE(GET_CURRENT_PLAYER_PED_ENUM()) EXIT ENDIF INT index = 0 WHILE (index < g_savedGlobals.sCommsControlData.iNoQueuedTexts) AND NOT bCommunicationMade Run_Text_Timer_Safeguard(g_savedGlobals.sCommsControlData.sQueuedTexts[index]) //Check if the priority of this text is high enough to override character and global timers. BOOL bPriorityTimerOverride = FALSE IF ENUM_TO_INT(g_savedGlobals.sCommsControlData.sQueuedTexts[index].sCommData.ePriority) >= ENUM_TO_INT(CPR_VERY_HIGH) bPriorityTimerOverride = TRUE ENDIF //Is the global wait timer at 0? IF gameTime >= g_iGlobalWaitTime OR IS_BIT_SET(g_savedGlobals.sCommsControlData.sQueuedTexts[index].sCommData.iSettings, COMM_BIT_INGORE_GLOBAL_DELAY) OR bPriorityTimerOverride //Are the characters involved in this text waiting for timers? IF gameTime >= g_iCharWaitTime[g_savedGlobals.sCommsControlData.sQueuedTexts[index].sCommData.eNPCCharacter] OR bPriorityTimerOverride //Is this queued text ready to be triggered? IF gameTime >= g_savedGlobals.sCommsControlData.sQueuedTexts[index].sCommData.iQueueTime //Attempt to send the text. IF PRIVATE_Attempt_Send_Queued_Text_Message(g_savedGlobals.sCommsControlData.sQueuedTexts[index]) //Add a copy of the text message that we've just sent to the sent text queue. PRIVATE_Add_Text_To_Sent_Text_Queue(g_savedGlobals.sCommsControlData.sQueuedTexts[index]) //Tell the communication controller that we're waiting for a response. g_savedGlobals.sCommsControlData.bLastTextHadResponse = FALSE //Remove text entry from queue. PRIVATE_Remove_Text_From_Queue(index) bCommunicationMade = TRUE ELSE //Text message failed. Reschedule the text message. CPRINTLN(DEBUG_COMMUNICATIONS, "Text ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedTexts[index].sCommData.eID), " failed to send. Rescheduling.") PRIVATE_Set_New_Communication_Queue_Time(gameTime, g_savedGlobals.sCommsControlData.sQueuedTexts[index].sCommData, TRUE) ENDIF ENDIF ENDIF ENDIF index++ ENDWHILE ENDPROC PROC Check_For_Quick_Calls_To_Run(INT &gameTime) INT index //Check if there are any quick calls to run. REPEAT g_savedGlobals.sCommsControlData.iNoQueuedCalls index IF IS_BIT_SET(g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData.iSettings, COMM_BIT_CALL_IS_QUICK) IF IS_CALLING_CONTACT(g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData.eNPCCharacter) IF gameTime >= g_iCharWaitTime[g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData.eNPCCharacter] //Attempt to make the quick call. CPRINTLN(DEBUG_COMMUNICATIONS, "Attempting to make outgoing quick call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData.eID), ".") IF PRIVATE_Attempt_Make_Outgoing_Call(g_savedGlobals.sCommsControlData.sQueuedCalls[index], bDoCallBranch) //Register a call in progress. g_iCallInProgress = index iTimeCallInitialisationStarted = -1 eState = CCS_INITIALISING_COMM g_savedGlobals.sCommsControlData.bLastCallHadResponse = FALSE bCommunicationMade = TRUE IF HAS_CELLPHONE_JUST_BEEN_FORCED_AWAY() bWaitForForceAwayFlagToClear = TRUE ENDIF ELSE //Call failed to start. Reschedule the call. CPRINTLN(DEBUG_COMMUNICATIONS, "Quick call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData.eID), " failed to start. Rescheduling.") PRIVATE_Set_New_Communication_Queue_Time(gameTime, g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData) ENDIF ENDIF ENDIF ENDIF ENDREPEAT ENDPROC PROC Check_For_Missed_Calls_To_Run(INT &gameTime) INT index //Check if there are any missed calls queued to run. REPEAT g_savedGlobals.sCommsControlData.iNoMissedCalls index IF IS_CALLING_CONTACT(g_savedGlobals.sCommsControlData.sMissedCalls[index].sCommData.eNPCCharacter) IF gameTime >= g_iCharWaitTime[g_savedGlobals.sCommsControlData.sMissedCalls[index].sCommData.eNPCCharacter] //Try and get the missed call queue index. INT iQueueIndex = PRIVATE_Get_Missed_Call_Queue_Index(g_savedGlobals.sCommsControlData.sMissedCalls[index].sCommData.eID) //Call not in the queue. Add it. IF iQueueIndex = -1 iQueueIndex = g_savedGlobals.sCommsControlData.iNoQueuedCalls g_savedGlobals.sCommsControlData.sQueuedCalls[iQueueIndex] = g_savedGlobals.sCommsControlData.sMissedCalls[index] g_savedGlobals.sCommsControlData.iNoQueuedCalls++ //Update character queue priority levels in case this new call is the new highest priority communication. INT iPlayerIndex REPEAT 3 iPlayerIndex IF IS_BIT_SET(g_savedGlobals.sCommsControlData.sMissedCalls[index].sCommData.iPlayerCharBitset, iPlayerIndex) PRIVATE_Update_Playable_Character_Priority_Level(INT_TO_ENUM(enumCharacterList, iPlayerIndex)) ENDIF ENDREPEAT ENDIF //Attempt to make the missed call. CPRINTLN(DEBUG_COMMUNICATIONS, "Attempting to make outgoing missed call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[iQueueIndex].sCommData.eID), ".") IF PRIVATE_Attempt_Make_Outgoing_Call(g_savedGlobals.sCommsControlData.sQueuedCalls[iQueueIndex], bDoCallBranch) //Register a call in progress. g_iCallInProgress = index iTimeCallInitialisationStarted = -1 eState = CCS_INITIALISING_COMM g_savedGlobals.sCommsControlData.bLastCallHadResponse = FALSE bCommunicationMade = TRUE IF HAS_CELLPHONE_JUST_BEEN_FORCED_AWAY() bWaitForForceAwayFlagToClear = TRUE ENDIF ELSE //Call failed to start. Reschedule the call. CPRINTLN(DEBUG_COMMUNICATIONS, "Missed call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[iQueueIndex].sCommData.eID), " failed to start. Rescheduling.") PRIVATE_Set_New_Communication_Queue_Time(gameTime, g_savedGlobals.sCommsControlData.sQueuedCalls[iQueueIndex].sCommData) ENDIF ENDIF ENDIF ENDREPEAT ENDPROC PROC Check_For_Chat_Calls_To_Run(INT &gameTime) INT index //Check if there are any chat calls to run. REPEAT g_savedGlobals.sCommsControlData.iNoChatCalls index enumCharacterList ePlayer = GET_CURRENT_PLAYER_PED_ENUM() IF IS_PLAYER_PED_PLAYABLE(ePlayer) IF IS_BIT_SET(g_savedGlobals.sCommsControlData.sChatCalls[index].sCommData.iPlayerCharBitset, ENUM_TO_INT(ePlayer)) IF IS_CALLING_CONTACT(g_savedGlobals.sCommsControlData.sChatCalls[index].sCommData.eNPCCharacter) IF gameTime >= g_iCharWaitTime[g_savedGlobals.sCommsControlData.sChatCalls[index].sCommData.eNPCCharacter] //Chat call should be able to run. Check if we have it in the main queue yet. BOOL bAlreadyQueued = FALSE INT iQueueIndex = 0 WHILE (iQueueIndex < g_savedGlobals.sCommsControlData.iNoQueuedCalls) AND NOT bAlreadyQueued IF g_savedGlobals.sCommsControlData.sQueuedCalls[iQueueIndex].sCommData.eID = g_savedGlobals.sCommsControlData.sChatCalls[index].sCommData.eID CPRINTLN(DEBUG_COMMUNICATIONS, "Chat call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sChatCalls[index].sCommData.eID), " already in main queue.") bAlreadyQueued = TRUE ENDIF iQueueIndex++ ENDWHILE //Add the chat call to the main communication queue. IF NOT bAlreadyQueued CPRINTLN(DEBUG_COMMUNICATIONS, "Player is calling contact for chat call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sChatCalls[index].sCommData.eID), ". Adding to main call queue.") iQueueIndex = g_savedGlobals.sCommsControlData.iNoQueuedCalls IF iQueueIndex < CC_MAX_QUEUED_CALLS g_savedGlobals.sCommsControlData.sQueuedCalls[iQueueIndex] = g_savedGlobals.sCommsControlData.sChatCalls[index] g_savedGlobals.sCommsControlData.iNoQueuedCalls++ //Update character queue priority levels in case this new call is the new highest priority communication. INT iPlayerIndex REPEAT 3 iPlayerIndex IF IS_BIT_SET(g_savedGlobals.sCommsControlData.sQueuedCalls[iQueueIndex].sCommData.iPlayerCharBitset, iPlayerIndex) PRIVATE_Update_Playable_Character_Priority_Level(INT_TO_ENUM(enumCharacterList, iPlayerIndex)) ENDIF ENDREPEAT ELSE SCRIPT_ASSERT("Check_For_Chat_Calls_To_Run: Tried to add a chat call to the main queue, but it was full. Bug BenR.") EXIT ENDIF ENDIF //Attempt to make the chat call. CPRINTLN(DEBUG_COMMUNICATIONS, "Attempting to make outgoing chat call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[iQueueIndex].sCommData.eID), ".") IF PRIVATE_Attempt_Make_Outgoing_Chat_Call(g_savedGlobals.sCommsControlData.sQueuedCalls[iQueueIndex]) //Register a call in progress. g_iCallInProgress = iQueueIndex iTimeCallInitialisationStarted = -1 eState = CCS_INITIALISING_COMM g_savedGlobals.sCommsControlData.bLastCallHadResponse = FALSE bCommunicationMade = TRUE IF HAS_CELLPHONE_JUST_BEEN_FORCED_AWAY() bWaitForForceAwayFlagToClear = TRUE ENDIF ELSE //Call failed to start. Remove chat call from the main queue. CPRINTLN(DEBUG_COMMUNICATIONS, "Chat call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sChatCalls[iQueueIndex].sCommData.eID), " failed to start.") PRIVATE_Remove_Call_From_Queue(iQueueIndex) ENDIF ENDIF ENDIF ENDIF ENDIF ENDREPEAT ENDPROC PROC Check_For_Queued_Email_To_Run(INT &gameTime) // Ensure we are a playable character IF NOT IS_PLAYER_PED_PLAYABLE(GET_CURRENT_PLAYER_PED_ENUM()) EXIT ENDIF INT index = 0 WHILE (index < g_savedGlobals.sCommsControlData.iNoQueuedEmails) AND NOT bCommunicationMade Run_Email_Timer_Safeguard(g_savedGlobals.sCommsControlData.sQueuedEmails[index]) //Check if the priority of this text is high enough to override character and global timers. BOOL bPriorityTimerOverride = FALSE IF ENUM_TO_INT(g_savedGlobals.sCommsControlData.sQueuedEmails[index].sCommData.ePriority) >= ENUM_TO_INT(CPR_VERY_HIGH) bPriorityTimerOverride = TRUE ENDIF //Is the global wait timer at 0? IF gameTime >= g_iGlobalWaitTime OR IS_BIT_SET(g_savedGlobals.sCommsControlData.sQueuedEmails[index].sCommData.iSettings, COMM_BIT_INGORE_GLOBAL_DELAY) OR bPriorityTimerOverride //Are the characters involved in this call waiting for timers? IF gameTime >= g_iCharWaitTime[g_savedGlobals.sCommsControlData.sQueuedEmails[index].sCommData.eNPCCharacter] OR bPriorityTimerOverride //Is this queued text ready to be triggered? IF gameTime >= g_savedGlobals.sCommsControlData.sQueuedEmails[index].sCommData.iQueueTime //Attempt to send the text. IF PRIVATE_Attempt_Send_Queued_Email(g_savedGlobals.sCommsControlData.sQueuedEmails[index]) //Remove text entry from queue. iTimeCallInitialisationStarted = -1 Process_Cleanup_Of_Sent_Email(gameTime, g_savedGlobals.sCommsControlData.sQueuedEmails[index]) PRIVATE_Remove_Email_From_Queue(index) bCommunicationMade = TRUE ELSE //Email failed. Reschedule the email. CPRINTLN(DEBUG_COMMUNICATIONS, "Email ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedEmails[index].sCommData.eID), " failed to send. Rescheduling.") PRIVATE_Set_New_Communication_Queue_Time(gameTime, g_savedGlobals.sCommsControlData.sQueuedEmails[index].sCommData) ENDIF ENDIF ENDIF ENDIF index++ ENDWHILE ENDPROC /// PURPOSE: /// Activate the closest blimp vehicle gen to the player FUNC BOOL Activate_Closest_Blimp_Location() IF IS_PED_INJURED(PLAYER_PED_ID()) RETURN FALSE ENDIF INT i FLOAT fShortestDist = 99999.99 INT iClosestGen = -1 VECTOR vGenCoords[2] VECTOR vPlayerCoords = GET_ENTITY_COORDS(PLAYER_PED_ID()) vGenCoords[0] = << 1133.21, 120.20, 80.9 >> vGenCoords[1] = << -806.31, -2679.65, 13.9 >> REPEAT 2 i IF GET_DISTANCE_BETWEEN_COORDS(vPlayerCoords, vGenCoords[i]) < fShortestDist fShortestDist = GET_DISTANCE_BETWEEN_COORDS(vPlayerCoords, vGenCoords[i]) iClosestGen = i ENDIF ENDREPEAT CLEANUP_VEHICLE_GEN_VEHICLE(VEHGEN_BLIMP_CASINO) CLEANUP_VEHICLE_GEN_VEHICLE(VEHGEN_BLIMP_DOCKS) // B*1558610 - Swap gen if the player is within 250m of the blimp spawn position IF IS_SPHERE_VISIBLE(vGenCoords[iClosestGen], 300) OR fShortestDist < 300 // Swap to Docks from Casino... IF iClosestGen = 0 iClosestGen = 1 // ...or vice versa ELSE iClosestGen = 0 ENDIF ENDIF // Casino IF iClosestGen = 0 SET_VEHICLE_GEN_AVAILABLE(VEHGEN_BLIMP_CASINO, TRUE) CLEAR_MUST_LEAVE_AREA_VEHICLE_GEN_FLAG(VEHGEN_BLIMP_CASINO) RETURN TRUE // Docks ELIF iClosestGen = 1 SET_VEHICLE_GEN_AVAILABLE(VEHGEN_BLIMP_DOCKS, TRUE) CLEAR_MUST_LEAVE_AREA_VEHICLE_GEN_FLAG(VEHGEN_BLIMP_DOCKS) RETURN TRUE ENDIF RETURN FALSE ENDFUNC FUNC BOOL Is_Blimp_Nearby() VEHICLE_INDEX vehArray[15] INT numberOfVehicles = GET_PED_NEARBY_VEHICLES(PLAYER_PED_ID(),vehArray) INT i FOR i = 0 TO (numberOfVehicles - 1) IF GET_ENTITY_MODEL(vehArray[i]) = BLIMP IF NOT IS_ENTITY_DEAD(vehArray[i]) AND IS_VEHICLE_DRIVEABLE(vehArray[i]) RETURN TRUE ENDIF ENDIF ENDFOR RETURN FALSE ENDFUNC FUNC BOOL Is_Player_In_Blimp() IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID(),TRUE) IF GET_ENTITY_MODEL(GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID(), TRUE)) = BLIMP RETURN TRUE ENDIF ENDIF RETURN FALSE ENDFUNC PROC Check_For_Ability_Calls_To_Run(INT &gameTime) STRING sRootLabel[5] // Dial a Blimp! SWITCH iDialBlimpState // Wait for player to call CASE 0 IF NOT IS_PED_INJURED(PLAYER_PED_ID()) IF IS_CALLING_CONTACT(CHAR_BLIMP) // Has preorder content IF IS_PREORDER_GAME() OR IS_COLLECTORS_EDITION_GAME() OR IS_SPECIAL_EDITION_GAME() OR IS_JAPANESE_SPECIAL_EDITION_GAME() OR IS_LAST_GEN_PLAYER() // Don't connect if a blimp already exists in the world IF NOT DOES_ENTITY_EXIST(GET_VEHICLE_GEN_VEHICLE_INDEX(VEHGEN_BLIMP_CASINO)) AND NOT DOES_ENTITY_EXIST(GET_VEHICLE_GEN_VEHICLE_INDEX(VEHGEN_BLIMP_DOCKS)) //If the blip is called in a mission trigger we get an assert B*1868387 AND g_iOffMissionCutsceneRequestAllowed = NULL_OFFMISSION_CUTSCENE_REQUEST AND NOT Is_Player_In_Blimp() AND NOT Is_Blimp_Nearby() // Michael IF GET_CURRENT_PLAYER_PED_ENUM() = CHAR_MICHAEL ADD_PED_FOR_DIALOGUE(convoStruct, 0, PLAYER_PED_ID(), "MICHAEL") ADD_PED_FOR_DIALOGUE(convoStruct, 3, NULL, "DBLIMPOperator") sRootLabel[0] = "DAB_HELLO" sRootLabel[1] = "DAB_MICHAEL" sRootLabel[2] = "DAB_SEND" sRootLabel[3] = "DAB_THANK_M" sRootLabel[4] = "DAB_BYE" // Franklin ELIF GET_CURRENT_PLAYER_PED_ENUM() = CHAR_FRANKLIN ADD_PED_FOR_DIALOGUE(convoStruct, 1, PLAYER_PED_ID(), "FRANKLIN") ADD_PED_FOR_DIALOGUE(convoStruct, 3, NULL, "DBLIMPOperator") sRootLabel[0] = "DAB_HELLO" sRootLabel[1] = "DAB_FRANKLIN" sRootLabel[2] = "DAB_SEND" sRootLabel[3] = "DAB_THANK_F" sRootLabel[4] = "DAB_BYE" // Trevor ELIF GET_CURRENT_PLAYER_PED_ENUM() = CHAR_TREVOR ADD_PED_FOR_DIALOGUE(convoStruct, 2, PLAYER_PED_ID(), "TREVOR") ADD_PED_FOR_DIALOGUE(convoStruct, 3, NULL, "DBLIMPOperator") sRootLabel[0] = "DAB_HELLO" sRootLabel[1] = "DAB_TREVOR" sRootLabel[2] = "DAB_SEND" sRootLabel[3] = "DAB_THANK_T" sRootLabel[4] = "DAB_BYE" ENDIF // Attempt phonecall CPRINTLN(DEBUG_COMMUNICATIONS, "Check_For_Ability_Calls_To_Run - Blimp - Initiating Call") IF PLAYER_CALL_CHAR_CELLPHONE_MULTIPART_WITH_N_LINES(5, convoStruct, CHAR_BLIMP, "BLIMPAU", sRootLabel, sRootLabel, CONV_PRIORITY_CELLPHONE) CPRINTLN(DEBUG_COMMUNICATIONS, "Check_For_Ability_Calls_To_Run - blimp - Call in progess") iDialBlimpState++ ENDIF ENDIF ENDIF ENDIF ENDIF BREAK // Wait for player to finish call CASE 1 IF NOT IS_PED_INJURED(PLAYER_PED_ID()) IF NOT IS_CALLING_CONTACT(CHAR_BLIMP) AND NOT IS_PHONE_ONSCREEN(TRUE) g_iGlobalWaitTime = gameTime + CC_GLOBAL_DELAY_BETWEEN_COMMS IF NOT IS_CELLPHONE_CONVERSATION_PLAYING() CPRINTLN(DEBUG_COMMUNICATIONS, "Check_For_Ability_Calls_To_Run - Blimp - Call Completed") /* CPRINTLN(DEBUG_COMMUNICATIONS, "Waiting 15 Seconds for delivery") iDialBlimpState++ */ // Bypass delay for now iDialBlimpState = 3 ENDIF ELSE //If the blip is called in a mission trigger we get an assert B*1868387 IF g_iOffMissionCutsceneRequestAllowed != NULL_OFFMISSION_CUTSCENE_REQUEST CPRINTLN(DEBUG_COMMUNICATIONS, "Check_For_Ability_Calls_To_Run - a cutscene is being requested while ringing for a blimp") HANG_UP_AND_PUT_AWAY_PHONE() ENDIF ENDIF ENDIF BREAK // Wait some time for delivery CASE 2 IF (GET_GAME_TIMER() > g_iGlobalWaitTime) CPRINTLN(DEBUG_COMMUNICATIONS, "Check_For_Ability_Calls_To_Run - Blimp - Delay before Delivery Complete") iDialBlimpState++ ENDIF BREAK // Activate suitable vehicle gen CASE 3 // Get the nearest blimp location to the player Activate_Closest_Blimp_Location() iDialBlimpState = 0 BREAK ENDSWITCH ENDPROC PROC Check_For_Any_Outgoing_Calls_To_Run(INT &gameTime) IF IS_CALLING_ANY_CONTACT() IF eState = CCS_LOOKING_FOR_COMM Check_For_Quick_Calls_To_Run(gameTime) ENDIF IF eState = CCS_LOOKING_FOR_COMM Check_For_Missed_Calls_To_Run(gameTime) ENDIF IF eState = CCS_LOOKING_FOR_COMM Check_For_Chat_Calls_To_Run(gameTime) ENDIF ENDIF // Keep this out the calling contact loop as we need // to run some script when the call ends. Check_For_Ability_Calls_To_Run(gameTime) ENDPROC PROC Update_Call_In_Progress_Responses() //Does the call in progress have a question that needs responding to? IF IS_BIT_SET(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.iSettings, COMM_BIT_HAS_QUESTION) //Check if we need to display help text for making a decision on the phone. IF NOT HAS_ONE_TIME_HELP_DISPLAYED(FHM_FIRST_CALL_DECISION) IF IS_CELLPHONE_CALL_WITH_REPLIES_WAITING_ON_USER_INPUT() SWITCH g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID CASE QCALL_ME_AMANDA CASE QCALL_ME_JIMMY CASE QCALL_ME_TRACEY PRINT_HELP("AM_H_FDEC") SET_ONE_TIME_HELP_MESSAGE_DISPLAYED(FHM_FIRST_CALL_DECISION) BREAK ENDSWITCH ENDIF ENDIF //Yes. Check for responses from the player. IF NOT g_savedGlobals.sCommsControlData.bLastCallHadResponse IF (CHECK_RESPONSE_TO_CELLPHONE_PROMPT() <> RESPONSE_STORE_EMPTY) IF IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("AM_H_FDEC") CLEAR_HELP(FALSE) ENDIF IF (CHECK_RESPONSE_TO_CELLPHONE_PROMPT() = RESPONDED_YES) CPRINTLN(DEBUG_COMMUNICATIONS, "Call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID), " received a YES response.") g_savedGlobals.sCommsControlData.bLastCallResponse = TRUE g_savedGlobals.sCommsControlData.bLastCallHadResponse = TRUE ELSE CPRINTLN(DEBUG_COMMUNICATIONS, "Call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID), " received a NO response.") IF g_iCommsCandidateID != NO_CANDIDATE_ID Mission_Over(g_iCommsCandidateID) Set_Leave_Area_Flag_For_All_Blipped_Missions() CPRINTLN(DEBUG_COMMUNICATIONS, "Mission triggering call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID), " was rejected so cleaned-up mission candidate.") CONST_FLOAT fCONST_FRIEND_TIME_RESET_FOR_MISSION_BACKOUT 300.0 enumFriendConnection friendConnID REPEAT MAX_FRIEND_CONNECTIONS friendConnID IF g_SavedGlobals.sFriendsData.g_FriendConnectData[friendConnID].lastContactType = FRIEND_CONTACT_PHONE #IF IS_DEBUG_BUILD CPRINTLN(DEBUG_FRIENDS, "RESTART_TIMER_AT(", GetLabel_enumFriendConnection(friendConnID), ", ", fCONST_FRIEND_TIME_RESET_FOR_MISSION_BACKOUT, ")") #ENDIF RESTART_TIMER_AT(g_savedGlobals.sFriendsData.g_FriendConnectData[friendConnID].lastContactTimer, fCONST_FRIEND_TIME_RESET_FOR_MISSION_BACKOUT) ENDIF ENDREPEAT ENDIF g_savedGlobals.sCommsControlData.bLastCallResponse = FALSE g_savedGlobals.sCommsControlData.bLastCallHadResponse = TRUE ENDIF ENDIF ENDIF ENDIF ENDPROC //If we've flagged to load dynamic dialogue for a branched call, load the correct branch here. PROC Update_Branched_Call_Dialogue() IF bDoCallBranch FLOW_CHECK_IDS eBranchCheck = g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eSendCheck IF eBranchCheck != FLOW_CHECK_NONE CPRINTLN(DEBUG_COMMUNICATIONS, "Running check ", GET_DEBUG_STRING_FOR_FLOW_CHECK_ID(eBranchCheck), " for ongoing branched call.") //Grab dialogue details based on the flow check outcome. CC_CommID eBranch IF DO_CUSTOM_FLOW_CHECK(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eSendCheck) CPRINTLN(DEBUG_COMMUNICATIONS, "Branched call loading the TRUE branch.") eBranch = g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].eCommExtra ELSE CPRINTLN(DEBUG_COMMUNICATIONS, "Branched call loading the FALSE branch.") eBranch = g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].eCommExtra2 ENDIF IF eBranch != COMM_NONE CC_CallStringData sCallStrings PRIVATE_Get_Call_String_Data(eBranch, sCallStrings) SET_DYNAMIC_BRANCH_FOR_ONGOING_CALL(sCallStrings.tBlock, sCallStrings.tConversation) CPRINTLN(DEBUG_COMMUNICATIONS, "Branched call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID), " adding dynamic conversation ending ", GET_COMM_ID_DEBUG_STRING(eBranch), " to the call.") ELSE CPRINTLN(DEBUG_COMMUNICATIONS, "Branch for call was blank. No dynamic dialogue being added.") ENDIF ELSE SCRIPT_ASSERT("Update_Branched_Call_Dialogue: Branched call did not have a valid branch check defined.") ENDIF bDoCallBranch = FALSE ENDIF ENDPROC PROC Update_Call_Help() IF NOT HAS_ONE_TIME_HELP_DISPLAYED(FHM_FIRST_CALL_RINGING) IF GET_MISSION_COMPLETE_STATE(SP_MISSION_ARMENIAN_1) IF NOT GET_MISSION_COMPLETE_STATE(SP_MISSION_ARMENIAN_2) IF IS_PHONE_ONSCREEN(TRUE) IF g_iCallInProgress != -1 IF g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID = CALL_ARM2_UNLOCK or g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID = CALL_CARDMG_ARM2_UNLOCK SWITCH GET_FLOW_HELP_MESSAGE_STATUS("AM_H_FCAL1") CASE FHS_EXPIRED ADD_HELP_TO_FLOW_QUEUE("AM_H_FCAL1", FHP_HIGH, 0, 1000, DEFAULT_GOD_TEXT_TIME) BREAK CASE FHS_DISPLAYED SET_ONE_TIME_HELP_MESSAGE_DISPLAYED(FHM_FIRST_CALL_RINGING) BREAK ENDSWITCH ENDIF ENDIF ENDIF ENDIF ENDIF ENDIF IF NOT HAS_ONE_TIME_HELP_DISPLAYED(FHM_FIRST_CALL_ONGOING) IF NOT bFirstCallOngoingHelpQueued IF GET_MISSION_COMPLETE_STATE(SP_MISSION_ARMENIAN_1) IF NOT GET_MISSION_COMPLETE_STATE(SP_MISSION_ARMENIAN_2) IF IS_PHONE_ONSCREEN(TRUE) IF g_iCallInProgress != -1 IF g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID = CALL_ARM2_UNLOCK or g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID = CALL_CARDMG_ARM2_UNLOCK IF IS_CELLPHONE_CONVERSATION_PLAYING() IF IS_FLOW_HELP_MESSAGE_QUEUED("AM_H_FCAL1") REMOVE_HELP_FROM_FLOW_QUEUE("AM_H_FCAL1") ENDIF IF IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("AM_H_FCAL1") CLEAR_HELP(FALSE) ENDIF SET_ONE_TIME_HELP_MESSAGE_DISPLAYED(FHM_FIRST_CALL_RINGING) ADD_HELP_TO_FLOW_QUEUE("AM_H_FCAL2", FHP_HIGH, 0, 1000, DEFAULT_GOD_TEXT_TIME) bFirstCallOngoingHelpQueued = TRUE ENDIF ENDIF ENDIF ENDIF ENDIF ENDIF ELSE IF GET_FLOW_HELP_MESSAGE_STATUS("AM_H_FCAL2") = FHS_EXPIRED bFirstCallOngoingHelpQueued = FALSE ELIF GET_FLOW_HELP_MESSAGE_STATUS("AM_H_FCAL2") = FHS_DISPLAYED SET_ONE_TIME_HELP_MESSAGE_DISPLAYED(FHM_FIRST_CALL_ONGOING) ENDIF IF NOT IS_CELLPHONE_CONVERSATION_PLAYING() REMOVE_HELP_FROM_FLOW_QUEUE("AM_H_FCAL2") IF IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("AM_H_FCAL2") CLEAR_HELP(FALSE) ENDIF SET_ONE_TIME_HELP_MESSAGE_DISPLAYED(FHM_FIRST_CALL_ONGOING) ENDIF ENDIF IF g_savedGlobals.sCommsControlData.eLastCompletedCall = CALL_ARM2_UNLOCK or g_savedGlobals.sCommsControlData.eLastCompletedCall = CALL_CARDMG_ARM2_UNLOCK SET_ONE_TIME_HELP_MESSAGE_DISPLAYED(FHM_FIRST_CALL_ONGOING) SET_ONE_TIME_HELP_MESSAGE_DISPLAYED(FHM_FIRST_CALL_RINGING) IF IS_FLOW_HELP_MESSAGE_QUEUED("AM_H_FCAL1") REMOVE_HELP_FROM_FLOW_QUEUE("AM_H_FCAL1") ENDIF IF IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("AM_H_FCAL1") CLEAR_HELP(FALSE) ENDIF IF IS_FLOW_HELP_MESSAGE_QUEUED("AM_H_FCAL2") REMOVE_HELP_FROM_FLOW_QUEUE("AM_H_FCAL2") ENDIF IF IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("AM_H_FCAL2") CLEAR_HELP(FALSE) ENDIF ENDIF ENDIF ENDPROC FUNC BOOL Update_Secure_Mission_Candidate_For_Mission_Triggering_Call() SWITCH Request_Mission_Launch(g_iCommsCandidateID, MCTID_PHONECALL_TO_PLAYER, MISSION_TYPE_STORY) CASE MCRET_ACCEPTED CPRINTLN(DEBUG_COMMUNICATIONS, "Call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID), " secured a candidate ID for mission that will trigger from this call.") RETURN TRUE BREAK CASE MCRET_DENIED CPRINTLN(DEBUG_COMMUNICATIONS, "Backing out of call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID), " because it couldn't secure a candidate ID.") HANG_UP_AND_PUT_AWAY_PHONE() BREAK ENDSWITCH CDEBUG1LN(DEBUG_COMMUNICATIONS, "Call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID), " waiting to secure a candidate ID.") RETURN FALSE ENDFUNC PROC Process_Early_Cancellation_Of_Call(INT &gameTime, BOOL bForceReqeue = FALSE) CPRINTLN(DEBUG_COMMUNICATIONS, "Processing early cancellation of call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID), ".") HANG_UP_AND_PUT_AWAY_PHONE() IF IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("AM_H_FCAL1") CLEAR_HELP(FALSE) ENDIF //Do we need to clean up a temporary contact? IF IS_BIT_SET(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.iSettings, COMM_BIT_CALL_REMOVE_TEMP_CONTACT) enumPhoneBookPresence ePhonebook SWITCH GET_CURRENT_PLAYER_PED_ENUM() CASE CHAR_MICHAEL ePhonebook = MICHAEL_BOOK BREAK CASE CHAR_FRANKLIN ePhonebook = FRANKLIN_BOOK BREAK CASE CHAR_TREVOR ePhonebook = TREVOR_BOOK BREAK ENDSWITCH REMOVE_CONTACT_FROM_INDIVIDUAL_PHONEBOOK(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eNPCCharacter, ePhonebook) CLEAR_BIT(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.iSettings, COMM_BIT_CALL_REMOVE_TEMP_CONTACT) CPRINTLN(DEBUG_COMMUNICATIONS, "Removing temporary contact for call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID), " was cancelled.") ENDIF //If the call has a question handle cleaning up its responses and adding unique character delays. IF IS_BIT_SET(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.iSettings, COMM_BIT_HAS_QUESTION) IF NOT g_savedGlobals.sCommsControlData.bLastCallHadResponse CPRINTLN(DEBUG_COMMUNICATIONS, "Backed out of call with question. Forcing requeue flag on and adding to missed call queue.") SET_BIT(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.iSettings, COMM_BIT_CALL_REQUEUE_ON_MISS) PRIVATE_Add_Call_To_Missed_Call_Queue(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress]) IF g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eNPCCharacter = CHAR_AMANDA Execute_Code_ID(CID_SET_LONG_AMANDA_TIMER) ELIF g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eNPCCharacter = CHAR_JIMMY Execute_Code_ID(CID_SET_LONG_JIMMY_TIMER) ELIF g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eNPCCharacter = CHAR_TRACEY Execute_Code_ID(CID_SET_LONG_TRACEY_TIMER) ENDIF ENDIF ENDIF IF g_iCommsCandidateID != NO_CANDIDATE_ID Set_Leave_Area_Flag_For_All_Blipped_Missions() Mission_Over(g_iCommsCandidateID) CPRINTLN(DEBUG_COMMUNICATIONS, "Backed out of mission triggering call so cleaned-up mission candidate.") CONST_FLOAT fCONST_FRIEND_TIME_RESET_FOR_MISSION_BACKOUT 300.0 enumFriendConnection friendConnID REPEAT MAX_FRIEND_CONNECTIONS friendConnID IF g_SavedGlobals.sFriendsData.g_FriendConnectData[friendConnID].lastContactType = FRIEND_CONTACT_PHONE #IF IS_DEBUG_BUILD CPRINTLN(DEBUG_FRIENDS, "RESTART_TIMER_AT(", GetLabel_enumFriendConnection(friendConnID), ", ", fCONST_FRIEND_TIME_RESET_FOR_MISSION_BACKOUT, ")") #ENDIF RESTART_TIMER_AT(g_savedGlobals.sFriendsData.g_FriendConnectData[friendConnID].lastContactTimer, fCONST_FRIEND_TIME_RESET_FOR_MISSION_BACKOUT) ENDIF ENDREPEAT ENDIF //Requeue if missed. IF IS_BIT_SET(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.iSettings, COMM_BIT_CALL_REQUEUE_ON_MISS) OR bForceReqeue CPRINTLN(DEBUG_COMMUNICATIONS, "Executing call missed response for call: Requeuing.") PRIVATE_Set_New_Communication_Queue_Time(gameTime, g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData) IF IS_BIT_SET(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.iSettings, COMM_BIT_TRIGGERS_MISSION) IF g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.iRequeueTime < 300000 g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.iRequeueTime += 60000 CPRINTLN(DEBUG_COMMUNICATIONS, "Added 1 min to next requeue time of mission trigger communication ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID), ".") ENDIF ENDIF //Check if we should add the call to the missed call queue. IF IS_BIT_SET(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.iSettings, COMM_BIT_CALL_IS_MISSED) PRIVATE_Add_Call_To_Missed_Call_Queue(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress]) ENDIF //Text if missed. ELIF IS_BIT_SET(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.iSettings, COMM_BIT_CALL_TEXT_ON_MISS) CPRINTLN(DEBUG_COMMUNICATIONS, "Executing call missed response for call: Sending text message [", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].eCommExtra), "].") REGISTER_TEXT_MESSAGE_FROM_CHARACTER_TO_PLAYER( g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].eCommExtra, CT_FLOW, g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.iPlayerCharBitset, g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eNPCCharacter, g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.iRequeueTime, g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.iRequeueTime, g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eRestrictedAreaID, g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eExecuteOnCompleteID, g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eSendCheck, g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.iSettings) //Flag this call as the last completed call. g_savedGlobals.sCommsControlData.eLastCompletedCall = g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID //Clear the last call answered flag. g_savedGlobals.sCommsControlData.bLastCallAnswered = FALSE PRIVATE_Remove_Call_From_Missed_Call_Queue(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID) PRIVATE_Remove_Call_From_Chat_Call_Queue(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID) PRIVATE_Remove_Call_From_Queue(g_iCallInProgress) //Remove from queue if missed. ELSE CPRINTLN(DEBUG_COMMUNICATIONS, "Executing call missed response for call: Removing from queue.") //Flag this call as the last completed call. g_savedGlobals.sCommsControlData.eLastCompletedCall = g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID //Clear the last call answered flag. g_savedGlobals.sCommsControlData.bLastCallAnswered = FALSE PRIVATE_Remove_Call_From_Missed_Call_Queue(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID) PRIVATE_Remove_Call_From_Chat_Call_Queue(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID) PRIVATE_Remove_Call_From_Queue(g_iCallInProgress) ENDIF //Clean up the PedsForConversation struct. PRIVATE_Clean_Up_Peds_For_Conversation_Struct() g_iCallInProgress = -1 ENDPROC PROC Update_Mission_Triggering_Calls_Safeguards(INT paramGameTime) IF IS_BIT_SET(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.iSettings, COMM_BIT_TRIGGERS_MISSION) VectorID eRestrictedArea = g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eRestrictedAreaID IF eRestrictedArea != VID_BLANK //If the player gets 2/3 of the way into the restricted area before the call finishes, end it early. IF IS_PED_AT_VECTOR_ID( PLAYER_PED_ID(), eRestrictedArea, g_sVectorIDData[eRestrictedArea].radius * 0.35) CPRINTLN(DEBUG_COMMUNICATIONS, "Call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID), " was cancelled early as the player got too close the a mission triggering restricted area.") Process_Early_Cancellation_Of_Call(paramGameTime) HANG_UP_AND_PUT_AWAY_PHONE() eState = CCS_LOOKING_FOR_COMM g_iGlobalWaitTime = paramGameTime + CC_GLOBAL_DELAY_BETWEEN_COMMS ENDIF ENDIF ENDIF ENDPROC PROC Process_Termination_Of_Last_Call(INT &gameTime) IF g_iCallInProgress != -1 //Was the call hung up before a required response was made? IF IS_BIT_SET(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.iSettings, COMM_BIT_HAS_QUESTION) AND NOT g_savedGlobals.sCommsControlData.bLastCallHadResponse CPRINTLN(DEBUG_COMMUNICATIONS, "Call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID), " ended before a response was made.") Process_Early_Cancellation_Of_Call(gameTime) //The call succeeded. ELSE CPRINTLN(DEBUG_COMMUNICATIONS, "Call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID), " sucessfully completed.") //Set the last call answered flag. g_savedGlobals.sCommsControlData.bLastCallAnswered = TRUE // Reset friend last-contact timer PRIVATE_Update_Friend_Timer_Resets_For_Call(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eNPCCharacter, GET_CURRENT_PLAYER_PED_ENUM()) //Clean up the PedsForConversation struct. PRIVATE_Clean_Up_Peds_For_Conversation_Struct() //Execute any special code assigned to this call. Execute_Special_Code_For_Communication(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData) //Register this as the last completed call. g_savedGlobals.sCommsControlData.eLastCompletedCall = g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID //Update the system's global wait time. g_iGlobalWaitTime = gameTime + CC_GLOBAL_DELAY_BETWEEN_COMMS //Update the sending character's wait time. g_iCharWaitTime[g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eNPCCharacter] = gameTime + CC_CHARACTER_DELAY_BETWEEN_COMMS //Remove call from missed call queue (if it's in it). PRIVATE_Remove_Call_From_Missed_Call_Queue(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID) //Remove call from chat call queue (if it's in it). PRIVATE_Remove_Call_From_Chat_Call_Queue(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID) //Remove call entry from queue. PRIVATE_Remove_Call_From_Queue(g_iCallInProgress) //Do we need to clean up a temporary contact? IF IS_BIT_SET(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.iSettings, COMM_BIT_CALL_REMOVE_TEMP_CONTACT) enumPhoneBookPresence ePhonebook SWITCH GET_CURRENT_PLAYER_PED_ENUM() CASE CHAR_MICHAEL ePhonebook = MICHAEL_BOOK BREAK CASE CHAR_FRANKLIN ePhonebook = FRANKLIN_BOOK BREAK CASE CHAR_TREVOR ePhonebook = TREVOR_BOOK BREAK ENDSWITCH REMOVE_CONTACT_FROM_INDIVIDUAL_PHONEBOOK(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eNPCCharacter, ePhonebook) CLEAR_BIT(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.iSettings, COMM_BIT_CALL_REMOVE_TEMP_CONTACT) CPRINTLN(DEBUG_COMMUNICATIONS, "Removing temporary contact for call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID), " from player phonebook as call has finished.") ENDIF //Special case. Clear help when hanging up the first flow phonecall. IF HAS_ONE_TIME_HELP_DISPLAYED(FHM_FIRST_CALL_ONGOING) IF NOT GET_MISSION_COMPLETE_STATE(SP_MISSION_ARMENIAN_2) IF IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("AM_H_FCAL2") CLEAR_HELP(FALSE) ENDIF ENDIF ENDIF ENDIF ENDIF ENDPROC PROC Update_Sent_Text_Responses(INT &gameTime, CC_TextMessageData &sentText) TEXT_LABEL tTextMessageLabel PRIVATE_Get_Text_Message_Message_Label(sentText.sCommData.eID, tTextMessageLabel) //Check the status of the text message that was sent. SWITCH GET_TEXT_MESSAGE_REPLY_STATUS(tTextMessageLabel) CASE NO_REPLY_REQUIRED CPRINTLN(DEBUG_COMMUNICATIONS, "A response is not required for sent text ", GET_COMM_ID_DEBUG_STRING(sentText.sCommData.eID), ".") g_savedGlobals.sCommsControlData.bLastTextHadResponse = FALSE Process_Cleanup_Of_Sent_Text(gameTime, sentText) BREAK CASE REPLIED_YES CPRINTLN(DEBUG_COMMUNICATIONS, "YES response received for sent text ", GET_COMM_ID_DEBUG_STRING(sentText.sCommData.eID), ".") g_savedGlobals.sCommsControlData.bLastTextHadResponse = TRUE g_savedGlobals.sCommsControlData.bLastTextResponse = TRUE Process_Cleanup_Of_Sent_Text(gameTime, sentText) BREAK CASE REPLIED_NO CPRINTLN(DEBUG_COMMUNICATIONS, "NO response received for sent text ", GET_COMM_ID_DEBUG_STRING(sentText.sCommData.eID), ".") g_savedGlobals.sCommsControlData.bLastTextHadResponse = TRUE g_savedGlobals.sCommsControlData.bLastTextResponse = FALSE BREAK //NB. If REPLY_REQUIRED then the text is not cleaned up and we will run this check again next frame. ENDSWITCH ENDPROC DEBUGONLY PROC Debug_Draw_All_Queued_Comms_Restricted_Areas() IF g_iCallInProgress = -1 //No call in progress. Render restricted area for queued communications. INT index REPEAT g_savedGlobals.sCommsControlData.iNoQueuedCalls index VectorID eRestrictedArea = g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData.eRestrictedAreaID IF eRestrictedArea != VID_BLANK DEBUG_DRAW_VECTOR_ID_AREA(eRestrictedArea, GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[index].sCommData.eID)) ENDIF ENDREPEAT REPEAT g_savedGlobals.sCommsControlData.iNoQueuedTexts index VectorID eRestrictedArea = g_savedGlobals.sCommsControlData.sQueuedTexts[index].sCommData.eRestrictedAreaID IF eRestrictedArea != VID_BLANK DEBUG_DRAW_VECTOR_ID_AREA(eRestrictedArea, GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedTexts[index].sCommData.eID)) ENDIF ENDREPEAT REPEAT g_savedGlobals.sCommsControlData.iNoQueuedEmails index VectorID eRestrictedArea = g_savedGlobals.sCommsControlData.sQueuedEmails[index].sCommData.eRestrictedAreaID IF eRestrictedArea != VID_BLANK DEBUG_DRAW_VECTOR_ID_AREA(eRestrictedArea, GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedEmails[index].sCommData.eID)) ENDIF ENDREPEAT ELSE //Call in progress. If this call triggers a mission, render its cancel area. VectorID eRestrictedArea = g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eRestrictedAreaID IF eRestrictedArea != VID_BLANK IF IS_BIT_SET(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.iSettings, COMM_BIT_TRIGGERS_MISSION) DEBUG_DRAW_VECTOR_ID_AREA(eRestrictedArea, "Call Cancel-Area", g_sVectorIDData[eRestrictedArea].radius*0.35) ENDIF ENDIF ENDIF ENDPROC PROC GET_CHARACTER_INDEXES_FOR_CB_CONV_INDEX(INT iConvIndex, INT &iChar1, INT &iChar2) SWITCH iConvIndex CASE 1 iChar1 = 1 iChar2 = 8 BREAK CASE 2 iChar1 = 5 iChar2 = 2 BREAK CASE 3 iChar1 = 3 iChar2 = 10 BREAK CASE 4 iChar1 = 4 iChar2 = 13 BREAK CASE 5 iChar1 = 7 iChar2 = 6 BREAK CASE 6 iChar1 = 12 iChar2 = 9 BREAK CASE 7 iChar1 = 11 iChar2 = 44 BREAK CASE 8 iChar1 = 34 iChar2 = 14 BREAK CASE 9 iChar1 = 15 iChar2 = 28 BREAK CASE 10 iChar1 = 43 iChar2 = 16 BREAK CASE 11 iChar1 = 17 iChar2 = 18 BREAK CASE 12 iChar1 = 19 iChar2 = 20 BREAK CASE 13 iChar1 = 26 iChar2 = 21 BREAK CASE 14 iChar1 = 22 iChar2 = 23 BREAK CASE 15 iChar1 = 24 iChar2 = 25 BREAK CASE 16 iChar1 = 27 iChar2 = 29 BREAK CASE 17 iChar1 = 30 iChar2 = 31 BREAK CASE 18 iChar1 = 33 iChar2 = 32 BREAK CASE 19 iChar1 = 35 iChar2 = 36 BREAK CASE 20 iChar1 = 38 iChar2 = 37 BREAK CASE 21 iChar1 = 40 iChar2 = 39 BREAK CASE 22 iChar1 = 41 iChar2 = 42 BREAK DEFAULT CPRINTLN(DEBUG_COMMUNICATIONS, "Tried to get speaker IDs for a CB conversation # that doesn't exist!") iChar1 = 1 iChar2 = 2 BREAK ENDSWITCH ENDPROC /// PURPOSE: /// Checks whether the current weather is OK for this monologue to play (in case it references weather like Mon 5) /// PARAMS: /// iMonID - the ID we're checking /// RETURNS: /// TRUE if the monologue makes sense if said with the current weather (most do), false if not FUNC BOOL IS_CURRENT_WEATHER_OK_FOR_MONOLOGUE(int iMonID) SWITCH iMonID CASE 5 IF IS_PREV_WEATHER_TYPE("SMOG") OR IS_PREV_WEATHER_TYPE("OVERCAST") OR IS_PREV_WEATHER_TYPE("RAIN") OR IS_PREV_WEATHER_TYPE("THUNDER") OR IS_PREV_WEATHER_TYPE("SNOW") OR IS_NEXT_WEATHER_TYPE("SMOG") OR IS_NEXT_WEATHER_TYPE("OVERCAST") OR IS_NEXT_WEATHER_TYPE("RAIN") OR IS_NEXT_WEATHER_TYPE("THUNDER") OR IS_NEXT_WEATHER_TYPE("SNOW") RETURN FALSE ENDIF BREAK DEFAULT // If it ain't in this list, it can be used RETURN TRUE BREAK ENDSWITCH RETURN TRUE ENDFUNC /// PURPOSE: /// Generates a random ID for a monologue (int between 1 and 43), checks to make sure it's okay to be played now, and regenerates it if not /// RETURNS: /// An int between 1 and 43 FUNC INT RETURN_APPROPRIATE_MONOLOGUE_ID() TIMEOFDAY newTOD = GET_CURRENT_TIMEOFDAY() INT iHour = GET_TIMEOFDAY_HOUR(newTOD) BOOL bCanUseID = FALSE INT iConvNum WHILE bCanUseID = FALSE iConvNum = GET_RANDOM_INT_IN_RANGE(1, 44) SWITCH iConvNum CASE 5 IF iHour > 19 // Too late in the day for this monologue to make sense bCanUseID = FALSE CPRINTLN(DEBUG_COMMUNICATIONS, "Too late for Monologue ", iConvNum, " to play, regenerating another one") ELIF iHour < 9 // Too early in the day for this monologue to make sense bCanUseID = FALSE CPRINTLN(DEBUG_COMMUNICATIONS, "Too early for Monologue ", iConvNum, " to play, regenerating another one") ELIF NOT IS_CURRENT_WEATHER_OK_FOR_MONOLOGUE(iConvNum) bCanUseID = FALSE CPRINTLN(DEBUG_COMMUNICATIONS, "Current weather makes Monologue ", iConvNum, " not make sense, regenerating another one") ELSE bCanUseID = TRUE // Yay, we can use it ENDIF BREAK DEFAULT // If it ain't in this list, it can be used bCanUseID = TRUE BREAK ENDSWITCH WAIT(0) ENDWHILE //Force debug conversation #IF IS_DEBUG_BUILD IF iForceDebugConv != -1 iConvNum = CLAMP_INT(iForceDebugConv,1,43) iForceDebugConv = -1 ENDIF #ENDIF RETURN iConvNum ENDFUNC /// PURPOSE: /// Try to do a monologue or conversation type CB convo /// RETURNS: /// TRUE if the conversation was successful FUNC BOOL Do_CB_MonConvo() TEXT_LABEL_15 tlConv INT iConvNum TEXT_LABEL_15 tlPedName BOOL bDecision = GET_RANDOM_BOOL() // If any of these RAG widget bools are true, override the random decision because one of us wants a specific conv type #IF IS_DEBUG_BUILD IF bDoMonologue bDecision = TRUE ELIF bDoConv bDecision = FALSE ENDIF #ENDIF IF bDecision // If true, do a monologue - if false, do a conversation tlConv = "CB_MON" iConvNum = RETURN_APPROPRIATE_MONOLOGUE_ID() tlConv += iConvNum // Creates the label we want, e.g. "CB_MON23" tlPedName = "CB_CHAR" tlPedName += iConvNum // Reuse the conv number we just generated to get the correct ped ID (they must match in D*!) ADD_PED_FOR_DIALOGUE(convoStruct, 3, NULL, tlPedName, FALSE, FALSE) IF CREATE_CONVERSATION(convoStruct, "CBRADAU", tlConv, CONV_PRIORITY_AMBIENT_LOW, DO_NOT_DISPLAY_SUBTITLES) #IF IS_DEBUG_BUILD IF bDoMonologue bDoMonologue = FALSE ENDIF #ENDIF PLAY_SOUND_FRONTEND(-1, "Start_Squelch", "CB_RADIO_SFX") PLAY_SOUND_FRONTEND(iSoundID, "Background_Loop", "CB_RADIO_SFX") RETURN TRUE ENDIF ELSE INT iChar1 INT iChar2 TEXT_LABEL_15 tlPedName2 // Need a second ped name for a conversation! tlConv = "CB_CONVO" iConvNum = GET_RANDOM_INT_IN_RANGE(1, 23) //Force debug conversation #IF IS_DEBUG_BUILD IF iForceDebugConv != -1 iConvNum = CLAMP_INT(iForceDebugConv,1,22) iForceDebugConv = -1 ENDIF #ENDIF tlConv += iConvNum // Creates the label we want, e.g. "CB_CONVO10" GET_CHARACTER_INDEXES_FOR_CB_CONV_INDEX(iConvNum, iChar1, iChar2) // Set up the two characters tlPedName = "CB_CHAR" tlPedName += iChar1 ADD_PED_FOR_DIALOGUE(convoStruct, 3, NULL, tlPedName, FALSE, FALSE) tlPedName2 = "CB_CHAR" tlPedName2 += iChar2 ADD_PED_FOR_DIALOGUE(convoStruct, 4, NULL, tlPedName2, FALSE, FALSE) IF CREATE_CONVERSATION(convoStruct, "CBRADAU", tlConv, CONV_PRIORITY_AMBIENT_LOW, DO_NOT_DISPLAY_SUBTITLES) #IF IS_DEBUG_BUILD IF bDoConv bDoConv = FALSE ENDIF #ENDIF PLAY_SOUND_FRONTEND(-1, "Start_Squelch", "CB_RADIO_SFX") PLAY_SOUND_FRONTEND(iSoundID, "Background_Loop", "CB_RADIO_SFX") RETURN TRUE ENDIF ENDIF // Something went wrong... RETURN FALSE ENDFUNC /// PURPOSE: /// Try to do an Ident type CB conversation /// RETURNS: /// TRUE if the conversation was successful FUNC BOOL Do_CB_Ident() TEXT_LABEL_15 tlConv = "CB_IDN" INT iConvNum = GET_RANDOM_INT_IN_RANGE(1, 44) #IF IS_DEBUG_BUILD IF iForceDebugConv != -1 iConvNum = CLAMP_INT(iForceDebugConv,1,43) iForceDebugConv = -1 ENDIF #ENDIF tlConv += iConvNum // Creates the label we want, e.g. "CB_IDN6" TEXT_LABEL_15 tlPedName = "CB_CHAR" tlPedName += iConvNum // Reuse the conv number we just generated to get the correct ped ID (they must match in D*!) ADD_PED_FOR_DIALOGUE(convoStruct, 3, NULL, tlPedName, FALSE, FALSE) IF CREATE_CONVERSATION(convoStruct, "CBRADAU", tlConv, CONV_PRIORITY_AMBIENT_LOW, DO_NOT_DISPLAY_SUBTITLES) // Reset the RAG debug bool if it's set #IF IS_DEBUG_BUILD IF bDoOneLine bDoOneLine = FALSE ENDIF #ENDIF PLAY_SOUND_FRONTEND(-1, "Start_Squelch", "CB_RADIO_SFX") PLAY_SOUND_FRONTEND(iSoundID, "Background_Loop", "CB_RADIO_SFX") RETURN TRUE ENDIF RETURN FALSE ENDFUNC /// PURPOSE: /// Handles CB radio conversations in vehicles. /// The player must be Trevor, and the player must in a vehicle that supports a CB radio and be within range of a transmitter. PROC Handle_CB_Radio_In_Vehicles() IF bCBActive = TRUE // Check the ongoing convo status to handle the player leaving the vehicle IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED() STOP_SOUND(iSoundID) PLAY_SOUND_FRONTEND(-1, "End_Squelch", "CB_RADIO_SFX") CPRINTLN(DEBUG_COMMUNICATIONS, "Stopped CB loop, played end squelch") CPRINTLN(DEBUG_COMMUNICATIONS, "CB conversation finished, now inactive") bCBActive = FALSE // If no conversation is ongoing, the CB conversation must have finished ELSE IF NOT IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID(), TRUE) KILL_FACE_TO_FACE_CONVERSATION_DO_NOT_FINISH_LAST_LINE() // If the player exits the vehicle, kill the conversation early STOP_SOUND(iSoundID) PLAY_SOUND_FRONTEND(-1, "End_Squelch", "CB_RADIO_SFX") CPRINTLN(DEBUG_COMMUNICATIONS, "Player exited vehicle during CB dialogue - loop stopped, end squelch played") bCBActive = FALSE // If no conversation is ongoing, the CB conversation must have finished ENDIF ENDIF ELSE // Only check this every 10 seconds, except if we're trying to do a CB radio conversation IF (GET_GAME_TIMER() - iCBCheckTimer) >= 10000 OR iCBState >= 1 IF GET_CURRENT_PLAYER_PED_ENUM() = CHAR_TREVOR IF IS_PLAYER_PLAYING(PLAYER_ID()) IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID()) IF CAN_VEHICLE_RECEIVE_CB_RADIO(GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID(), FALSE)) SWITCH iCBState // Initialise CASE -1 iCBIdentWaitTimer = GET_GAME_TIMER() iCBMonConvWaitTimer = GET_GAME_TIMER() iCBState++ // Done initialising iSoundID = GET_SOUND_ID() CPRINTLN(DEBUG_COMMUNICATIONS, "Initialised CB radio in Trevor's vehicle") BREAK // Check timers for either Ident or Monologue/Conversation time tiggers CASE 0 IF (GET_GAME_TIMER() - iCBIdentWaitTimer) >= 120000 // 2 MINUTES iCBState = 1 // Ident time ENDIF IF (GET_GAME_TIMER() - iCBMonConvWaitTimer) >= 300000 // 5 MINUTES iCBState = 2 // Monologue/Conversation time ENDIF // Check for the RAG debug bools overriding the timer #IF IS_DEBUG_BUILD IF bDoOneLine iCBState = 1 ELIF (bDoMonologue OR bDoConv) iCBState = 2 ENDIF #ENDIF BREAK // Do an Ident CB call CASE 1 IF Do_CB_Ident() bCBActive = TRUE // Set CB conversation as active iCBIdentWaitTimer = GET_GAME_TIMER() // Reset the timer for Idents iCBState = 0 // Back to waiting... CPRINTLN(DEBUG_COMMUNICATIONS, "Started an ident CB radio conversation! ") ENDIF BREAK // Do an Monologue/Conversation CB call CASE 2 IF Do_CB_MonConvo() bCBActive = TRUE // Set CB conversation as active iCBMonConvWaitTimer = GET_GAME_TIMER() // Reset the timer for Idents iCBState = 0 // Back to waiting... CPRINTLN(DEBUG_COMMUNICATIONS, "Started a monologue/conversation CB radio conversation!") ENDIF BREAK ENDSWITCH ENDIF ELSE IF iCBState != -1 iCBState = -1 // Reset the state back to the initialise stage ENDIF ENDIF ENDIF ENDIF iCBCheckTimer = GET_GAME_TIMER() // Reset the check timer now we've run this at least once ENDIF ENDIF ENDPROC /// PURPOSE: /// Handles updating calls to Cletus. Calls need to change based on what's gon on in hunting recently. PROC MAINTAIN_HUNTING_PHONECALL_STATUS() //Early out if no hunter call timer is set. IF ENUM_TO_INT(g_SavedGlobals.sAmbient.todHuntedWeekExp) = 0 EXIT ENDIF //Early out if the hunting minigame hasn't been unlocked. IF NOT GET_MISSION_FLOW_BITSET_BIT_STATE(FLOWBITSET_MINIGAME_ACTIVE, ENUM_TO_INT(MINIGAME_HUNTING)) EXIT ENDIF // If the conversations saying that the palyer has hunted in the last week are registered, and it's been more than a week // swap them. IF IS_COMMUNICATION_REGISTERED(CHAT_CLE1_1) OR IS_COMMUNICATION_REGISTERED(CHAT_CLE1_2) IF IS_NOW_AFTER_TIMEOFDAY(g_SavedGlobals.sAmbient.todHuntedWeekExp) // These calls have expired because the player hasn't hunted recently. Remove them, and register the conversations // where the hunter comments on the fact that Trevor hasn't hunted recently. CANCEL_COMMUNICATION(CHAT_CLE1_1) CANCEL_COMMUNICATION(CHAT_CLE1_2) // Register the "You haven't hunted in a while recently." conversations. REGISTER_CHAT_CALL_FROM_PLAYER_TO_CHARACTER(CHAT_CLE2_1, BIT_TREVOR, CHAR_HUNTER, 3, 7200000) REGISTER_CHAT_CALL_FROM_PLAYER_TO_CHARACTER(CHAT_CLE2_2, BIT_TREVOR, CHAR_HUNTER, 3, 7200000) EXIT ENDIF ENDIF // Cletus comments on the player's last awesome hunt is happening, remove it, and register something else. IF (GET_LAST_COMPLETED_CALL() = CHAT_CLE3_1) CANCEL_COMMUNICATION(CHAT_CLE3_1) CANCEL_COMMUNICATION(CHAT_CLE1_1) CANCEL_COMMUNICATION(CHAT_CLE1_2) CANCEL_COMMUNICATION(CHAT_CLE2_1) CANCEL_COMMUNICATION(CHAT_CLE2_2) // Wipe that call... g_savedGlobals.sCommsControlData.eLastCompletedCall = COMM_NONE IF IS_NOW_AFTER_TIMEOFDAY(g_SavedGlobals.sAmbient.todHuntedWeekExp) // Not hunted in a while, register that. REGISTER_CHAT_CALL_FROM_PLAYER_TO_CHARACTER(CHAT_CLE2_1, BIT_TREVOR, CHAR_HUNTER, 3, 7200000) REGISTER_CHAT_CALL_FROM_PLAYER_TO_CHARACTER(CHAT_CLE2_2, BIT_TREVOR, CHAR_HUNTER, 3, 7200000) ELSE REGISTER_CHAT_CALL_FROM_PLAYER_TO_CHARACTER(CHAT_CLE1_1, BIT_TREVOR, CHAR_HUNTER, 3, 7200000) REGISTER_CHAT_CALL_FROM_PLAYER_TO_CHARACTER(CHAT_CLE1_2, BIT_TREVOR, CHAR_HUNTER, 3, 7200000) ENDIF ENDIF ENDPROC //╒═════════════════════════════════════════════════════════════════════════════╕ //╞═════════════════════════╡ Controller Main Loop ╞═══════════════════════════╡ //╘═════════════════════════════════════════════════════════════════════════════╛ SCRIPT CPRINTLN(DEBUG_COMMUNICATIONS, "Starting communication controller.") // This script needs to cleanup only when the game moves from SP to MP IF HAS_FORCE_CLEANUP_OCCURRED(FORCE_CLEANUP_FLAG_SP_TO_MP|FORCE_CLEANUP_FLAG_DEBUG_MENU|FORCE_CLEANUP_FLAG_REPEAT_PLAY) CPRINTLN(DEBUG_COMMUNICATIONS, "The communication controller has been forced to cleanup.") Script_Cleanup() ENDIF #IF IS_DEBUG_BUILD IF NOT DOES_WIDGET_GROUP_EXIST(widgetID) widgetID = START_WIDGET_GROUP("Comms Controller") ADD_WIDGET_STRING("CB Radio debug") ADD_WIDGET_STRING("(Will only work if player is Trevor, is inside a CB-equipped vehicle, and within range of a transmitter!)") ADD_WIDGET_STRING("(Only checked every 10 seconds - there may be a delay between ticking the box and the conv starting)") ADD_WIDGET_BOOL("Do One Line CB Radio Conversation", bDoOneLine) ADD_WIDGET_BOOL("Do Monologue CB Radio Conversation", bDoMonologue) ADD_WIDGET_BOOL("Do Full Conv CB Radio Conversation", bDoConv) ADD_WIDGET_BOOL("Fire test emails into queue...", bFireTestEmails) ADD_WIDGET_INT_SLIDER("Force conversarion number, -1 for random",iForceDebugConv,-1,43,1) STOP_WIDGET_GROUP() ENDIF #ENDIF g_iGlobalWaitTime = GET_GAME_TIMER() + CC_GLOBAL_DELAY_BETWEEN_COMMS INT iCommsCheckStage = 0 //Used to cycle through a different queue check each frame. iCBCheckTimer = GET_GAME_TIMER() // Initialise the CB check timer WHILE TRUE #IF IS_DEBUG_BUILD IF NOT g_flowUnsaved.bUpdatingGameflow IF bFireTestEmails //INIT_AND_FIRE_TEST_EMAIL_QUEUE() // Commented out in comms_control_public.sch bFireTestEmails = FALSE ENDIF #ENDIF INT iGameTime = GET_GAME_TIMER() SWITCH (eState) CASE CCS_LOOKING_FOR_COMM //If we go on mission while idling looking for a call then clean up. IF IS_CURRENTLY_ON_MISSION_OF_ANY_TYPE() and not IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_EXILE) CPRINTLN(DEBUG_COMMUNICATIONS, "Cleaning up as we go on mission.") Script_Cleanup() ENDIF //No call in progress. Look for a communication to process. bCommunicationMade = FALSE IF iTimeCallInitialisationStarted != -1 iTimeCallInitialisationStarted = -1 ENDIF SWITCH(iCommsCheckStage) CASE 0 Check_For_Queued_Phonecall_To_Player_To_Run(iGameTime) BREAK CASE 1 Check_For_Queued_Phonecall_From_Player_To_Run(iGameTime) BREAK CASE 2 Check_For_Queued_Text_Message_To_Run(iGameTime) BREAK CASE 3 Check_For_Any_Outgoing_Calls_To_Run(iGameTime) BREAK CASE 4 Check_For_Queued_Email_To_Run(iGameTime) BREAK ENDSWITCH //Loop check stage counter. iCommsCheckStage++ IF iCommsCheckStage > 4 iCommsCheckStage = 0 ENDIF // Try and check the CB radio, if possible IF NOT IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_STORY) AND NOT IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_RANDOM_CHAR) AND NOT IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_RANDOM_EVENT) AND NOT IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_STORY_PREP) AND NOT IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_SPMC) AND NOT IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_STORY_FRIENDS) AND NOT IS_MISSION_LEADIN_ACTIVE() Handle_CB_Radio_In_Vehicles() ENDIF BREAK CASE CCS_INITIALISING_COMM //Has a mission launched before this call could go through? IF g_iCommsCandidateID = NO_CANDIDATE_ID IF NOT CAN_MISSION_TYPE_START_AGAINST_CURRENT_TYPE(MISSION_TYPE_STORY) AND NOT IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_EXILE) CPRINTLN(DEBUG_COMMUNICATIONS, "Call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID), " cancelled as mission started before it initialised.") Process_Early_Cancellation_Of_Call(iGameTime) eState = CCS_LOOKING_FOR_COMM ENDIF ENDIF //Has the phone been hung up before it could be answered? IF eState = CCS_INITIALISING_COMM IF bWaitForForceAwayFlagToClear IF NOT HAS_CELLPHONE_JUST_BEEN_FORCED_AWAY() bWaitForForceAwayFlagToClear = FALSE ENDIF ELSE IF CHECK_CELLPHONE_LAST_CALL_REJECTED() OR WAS_LAST_CELLPHONE_CALL_INTERRUPTED() OR HAS_CELLPHONE_JUST_BEEN_FORCED_AWAY() OR g_bScriptsSetSafeForCutscene #IF IS_DEBUG_BUILD CPRINTLN(DEBUG_COMMUNICATIONS, "Call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID), " rejected or forced away for the following reason/s:") IF CHECK_CELLPHONE_LAST_CALL_REJECTED() CPRINTLN(DEBUG_COMMUNICATIONS, "CHECK_CELLPHONE_LAST_CALL_REJECTED = TRUE") ENDIF IF WAS_LAST_CELLPHONE_CALL_INTERRUPTED() CPRINTLN(DEBUG_COMMUNICATIONS, "WAS_LAST_CELLPHONE_CALL_INTERRUPTED = TRUE") ENDIF IF HAS_CELLPHONE_JUST_BEEN_FORCED_AWAY() CPRINTLN(DEBUG_COMMUNICATIONS, "HAS_CELLPHONE_JUST_BEEN_FORCED_AWAY = TRUE") ENDIF IF g_bScriptsSetSafeForCutscene CPRINTLN(DEBUG_COMMUNICATIONS, "g_bScriptsSetSafeForCutscene = TRUE") ENDIF #ENDIF Process_Early_Cancellation_Of_Call(iGameTime) eState = CCS_LOOKING_FOR_COMM ENDIF ENDIF ENDIF //Has this call been answered yet? IF eState = CCS_INITIALISING_COMM IF IS_PHONE_ONSCREEN(FALSE) IF IS_CELLPHONE_CONVERSATION_PLAYING() IF g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.ePriority = CPR_VERY_HIGH DISABLE_HANGUP_FOR_THIS_CALL(FALSE) ENDIF //Does this call need to secure a candidate ID for a mission? IF IS_BIT_SET(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.iSettings, COMM_BIT_TRIGGERS_MISSION) IF Update_Secure_Mission_Candidate_For_Mission_Triggering_Call() CPRINTLN(DEBUG_COMMUNICATIONS, "Call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID), " finished initialising.") eState = CCS_COMM_IN_PROGRESS ENDIF ELSE CPRINTLN(DEBUG_COMMUNICATIONS, "Call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID), " finished initialising.") eState = CCS_COMM_IN_PROGRESS ENDIF ENDIF ENDIF ENDIF //Check for call initialisation taking too long. Timeout and requeue as a failsafe. IF eState = CCS_INITIALISING_COMM IF iTimeCallInitialisationStarted = -1 CPRINTLN(DEBUG_COMMUNICATIONS, "Setting new initialisation timer for call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID), ".") iTimeCallInitialisationStarted = GET_GAME_TIMER() ELIF (GET_GAME_TIMER() - iTimeCallInitialisationStarted) > 20000 CPRINTLN(DEBUG_COMMUNICATIONS, "Call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID), " failed to initialise in time. Killing call and requeuing.") KILL_PHONE_CONVERSATION() HANG_UP_AND_PUT_AWAY_PHONE(TRUE) Process_Early_Cancellation_Of_Call(iGameTime, TRUE) iTimeCallInitialisationStarted = -1 eState = CCS_LOOKING_FOR_COMM ENDIF ENDIF BREAK CASE CCS_COMM_IN_PROGRESS IF HAS_CELLPHONE_CALL_FINISHED() CPRINTLN(DEBUG_COMMUNICATIONS, "Call ", GET_COMM_ID_DEBUG_STRING(g_savedGlobals.sCommsControlData.sQueuedCalls[g_iCallInProgress].sCommData.eID), " has finished.") Process_Termination_Of_Last_Call(iGameTime) bDoCallBranch = FALSE g_iGlobalWaitTime = iGameTime + CC_GLOBAL_DELAY_BETWEEN_COMMS eState = CCS_LOOKING_FOR_COMM g_iCallInProgress = -1 ELIF g_iCallInProgress != -1 Update_Branched_Call_Dialogue() Update_Call_In_Progress_Responses() Update_Mission_Triggering_Calls_Safeguards(iGameTime) ENDIF BREAK ENDSWITCH //Process all texts that are sitting in the sent text queue. INT index REPEAT g_savedGlobals.sCommsControlData.iNoSentTexts index Update_Sent_Text_Responses(iGameTime, g_savedGlobals.sCommsControlData.sSentTexts[index]) ENDREPEAT Update_Call_Help() IF g_bPauseCommsQueues OR g_bPauseCommsQueuesThisFrame Pause_Timers() g_bPauseCommsQueuesThisFrame = FALSE ENDIF #IF IS_DEBUG_BUILD ENDIF IF g_bTriggerDebugOn Debug_Draw_All_Queued_Comms_Restricted_Areas() ENDIF #ENDIF MAINTAIN_HUNTING_PHONECALL_STATUS() WAIT(0) ENDWHILE ENDSCRIPT