Files
gtav-src/script/dev_ng/singleplayer/scripts/friends/friends_controller.sc
T
2025-09-29 00:52:08 +02:00

1001 lines
37 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// _________________________________________________________________________________________
// _________________________________________________________________________________________
// ___ ___
// ___ Author: Alwyn Roberts Date: 21/09/2010 ___
// _________________________________________________________________________________________
// ___ ___
// ___ (A1) - Friends_controller file for use Friends_controller.sc ___
// ___ ___
// _________________________________________________________________________________________
//Compile out Title Update changes to header functions.
//Must be before includes.
//CONST_INT USE_TU_CHANGES 0 // Removed by Kenneth R.
USING "rage_builtins.sch"
USING "globals.sch"
USING "model_enums.sch"
//- commands headers -//
USING "commands_streaming.sch"
//- script headers -//
//- public headers -//
USING "dialogue_public.sch"
USING "cellphone_public.sch"
USING "player_ped_public.sch"
USING "timer_public.sch"
USING "minigames_helpers.sch"
USING "comms_control_public.sch"
USING "selector_public.sch"
//- private headers -//
USING "friendController_private.sch"
#IF IS_DEBUG_BUILD
//- debug headers -//
USING "script_debug.sch"
USING "shared_debug.sch"
#ENDIF
// *******************************************************************************************
// CONSTANTS
// *******************************************************************************************
// *******************************************************************************************
// ENUMERATIONS
// *******************************************************************************************
// *******************************************************************************************
// VARIABLES
// *******************************************************************************************
//- Flags(INT and BOOL) -//
BOOL bFriends_controller_in_progress = TRUE
BOOL bDefault_player_char_has_been_initialized = FALSE
enumFriendConnection eMaintainedFriendCon
enumFriend eMaintainFriendTimer
//enumCharacterList ePhoneLoopChar // BBUDDIES REMOVED
structAmbGroup ambGroup
structPedsForConversation friendActivityCallConversation
#IF IS_DEBUG_BUILD
WIDGET_GROUP_ID hWidget
BOOL bDummyRagButton
PROC CreateDebugWidget()
hWidget = START_WIDGET_GROUP("Friends Controller")
ADD_WIDGET_BOOL("Dummy entry", bDummyRagButton)
STOP_WIDGET_GROUP()
ENDPROC
PROC CleanupDebugWidget()
IF DOES_WIDGET_GROUP_EXIST(hWidget)
DELETE_WIDGET_GROUP(hWidget)
ENDIF
ENDPROC
#ENDIF
// *******************************************************************************************
// GENERAL FUNCTIONS AND PROCEDURES
// *******************************************************************************************
//PURPOSE: Called on completion of the mission to release memory used for models etc
PROC Friends_Controller_Cleanup()
CPRINTLN(DEBUG_FRIENDS, "Friends_Controller_Cleanup() - Shutting down friend controller")
#IF IS_DEBUG_BUILD
CleanupDebugWidget()
#ENDIF
// Remove squad contacts
// IF IS_BIT_SET(g_bitfieldBattleBuddyPhoneContact, ENUM_TO_INT(CHAR_MICHAEL))
// CPRINTLN(DEBUG_FRIENDS, "UpdateFriendSquadContacts() - REMOVE_MICHAEL_SECONDARY_CONTACT_LIST_FUNCTION") // BBUDDIES REMOVED
// REMOVE_MICHAEL_SECONDARY_CONTACT_LIST_FUNCTION()
// ENDIF
// IF IS_BIT_SET(g_bitfieldBattleBuddyPhoneContact, ENUM_TO_INT(CHAR_FRANKLIN))
// CPRINTLN(DEBUG_FRIENDS, "UpdateFriendSquadContacts() - REMOVE_FRANKLIN_SECONDARY_CONTACT_LIST_FUNCTION")
// REMOVE_FRANKLIN_SECONDARY_CONTACT_LIST_FUNCTION()
// ENDIF
// IF IS_BIT_SET(g_bitfieldBattleBuddyPhoneContact, ENUM_TO_INT(CHAR_TREVOR))
// CPRINTLN(DEBUG_FRIENDS, "UpdateFriendSquadContacts() - REMOVE_TREVOR_SECONDARY_CONTACT_LIST_FUNCTION")
// REMOVE_TREVOR_SECONDARY_CONTACT_LIST_FUNCTION()
// ENDIF
// g_bitfieldBattleBuddyPhoneContact = 0
// Reset any initiating connections
enumFriendConnection eConnection
REPEAT MAX_FRIEND_CONNECTIONS eConnection
IF g_FriendConnectState[eConnection].state = FC_STATE_PhoneAccept
OR g_FriendConnectState[eConnection].state = FC_STATE_PhoneDecline
OR g_FriendConnectState[eConnection].state = FC_STATE_Init
SET_CONNECTION_STATE(eConnection, FC_STATE_ContactWait)
ENDIF
ENDREPEAT
// Cleanup ambient friends
Private_CleanupAmbGroup(ambGroup)
// Cleanup queued ambient friends
enumCharacterList eCharLoop
REPEAT NUM_OF_PLAYABLE_PEDS eCharLoop
IF NOT IS_PED_INJURED(g_pDismissPeds[eCharLoop])
CPRINTLN(DEBUG_FRIENDS, "Friends_Controller_Cleanup() - already queued ambient friend to global delete queue as part of friends_controller shutdown (", GetLabel_enumCharacterList(eCharLoop), ")")
Private_AddPedToDeleteQueue(g_pDismissPeds[eCharLoop])
g_pDismissPeds[eCharLoop] = NULL
ENDIF
ENDREPEAT
// Shut down is successful
CPRINTLN(DEBUG_FRIENDS, "/\\/\\ ...friends_controller.sc has shutdown (not running)")
SET_GAME_PAUSED(FALSE)
TERMINATE_THIS_THREAD()
ENDPROC
//PURPOSE: Initializes all the variables used within the scope of the script
PROC Initialise_Friends_Controller_Variables()
//- vectors -//
//- floats -//
//- ints -//
//-- structs : FRIENDS_STRUCT --//
PRIVATE_InitialiseFriendLocations(TRUE)
PRIVATE_InitialiseActivityLocations()
Private_InitAmbGroup(ambGroup)
#IF IS_DEBUG_BUILD
CreateDebugWidget()
#ENDIF
ENDPROC
// *******************************************************************************************
// SETUP FUNCTIONS AND PROCEDURES
// *******************************************************************************************
//PURPOSE: Load all the required models etc, and init peds, vehicles etc
PROC Setup_Friends_controller()
//- request models - peds -//
//- request models - vehicles -//
//- request models - objects -//
//- request models - weapons -//
//- request anims and ptfx --//
//- request vehicle recordings -//
//- request interior models -//
//- wait for assets to load -//
//- create any script vehicles -//
//- create any script peds -//
//- create the peds for hot-swap -//
//- set the players initial coords and heading -//
//- create any script objects -//
//- create any sequence tasks -//
//- setup any scripted coverpoints -//
//- add any initial taxi locations -//
//- load scene(if required) -//
//- load additional text and mission text link-//
//- setup audio malarky for player -//
ENDPROC
#IF IS_DEBUG_BUILD
//// *******************************************************************************************
//// DEBUGGING FUNCTIONS AND PROCEDURES
//// *******************************************************************************************
PROC DEBUG_DisplayMissionZoneInfo()
IF g_flowUnsaved.bShowMissionFlowDebugScreen
IF (g_iDebugSelectedFriendConnDisplay > 0)
INT iTopLine = 32
HUD_COLOURS textColour = HUD_COLOUR_YELLOW
// Draw background box
DrawFriendBox((0.77+1.0)/2.0, 0.85, (1.0-0.77), 0.1, 0, 0, 0, 175)
// Print current mission zone + accepted chars
SWITCH g_eFriendMissionZoneState
CASE FRIEND_MISSION_ZONE_LAUNCHING FALLTHRU
CASE FRIEND_MISSION_ZONE_LAUNCHED
textColour = HUD_COLOUR_YELLOW
BREAK
CASE FRIEND_MISSION_ZONE_REJECT
textColour = HUD_COLOUR_RED
BREAK
CASE FRIEND_MISSION_ZONE_CALL
CASE FRIEND_MISSION_ZONE_ON
textColour = HUD_COLOUR_WHITE
BREAK
CASE FRIEND_MISSION_ZONE_OFF FALLTHRU
DEFAULT
textColour = HUD_COLOUR_BLUEDARK
BREAK
ENDSWITCH
// Print mission zone state + accept chars
TEXT_LABEL_63 tZoneMission = "CurrentZone: "
tZoneMission += GetLabel_SP_MISSIONS(g_eFriendMissionZoneID)
tZoneMission += " ["
tZoneMission += GetLabel_FriendBits(g_iFriendMissionZoneAcceptBitset)
tZoneMission += "]"
DrawFriendLiteralString(tZoneMission, iTopLine, textColour)
iTopLine++
// Print mission ID
TEXT_LABEL_63 tZoneState = "CurrentZone: "
tZoneState += GetLabel_enumFriendMissionZoneState(g_eFriendMissionZoneState)
DrawFriendLiteralString(tZoneState, iTopLine, textColour)
iTopLine++
// Print mission ID
IF g_BattleBuddyMission <> SP_MISSION_NONE
textColour = HUD_COLOUR_GREYLIGHT
ELSE
textColour = HUD_COLOUR_BLUEDARK
ENDIF
TEXT_LABEL_63 tBuddyMission = "BBuddyMission: "
tBuddyMission += GetLabel_SP_MISSIONS(g_BattleBuddyMission)
tBuddyMission += " ["
tBuddyMission += GetLabel_FriendBits(g_BattleBuddyAllowedChars)
tBuddyMission += "]"
DrawFriendLiteralString(tBuddyMission, iTopLine, textColour)
iTopLine++
ENDIF
ENDIF
ENDPROC
#ENDIF
// *******************************************************************************************
// MISSION SPECIFIC FUNCTIONS AND PROCEDURES
// *******************************************************************************************
FUNC BOOL Private_StartFriendCall(enumCharacterList friendCharID, BOOL& bWasOutgoingCallSuccessful, BOOL& bWasCallAnswephone, BOOL& bWillFriendComeToPlayer)
// Setup the conversation participants
enumCharacterList playerCharID = g_eDefaultPlayerChar
enumFriend playerID = GET_FRIEND_FROM_CHAR(playerCharID)
enumFriend friendID = GET_FRIEND_FROM_CHAR(friendCharID)
ADD_FRIEND_CHAR_FOR_DIALOGUE(friendActivityCallConversation, playerCharID, PLAYER_PED_ID())
ADD_FRIEND_CHAR_FOR_DIALOGUE(friendActivityCallConversation, friendCharID, NULL)
TEXT_LABEL tBlock, tRootA, tRootB, tRootC, tRootC_spec, tRootD
BOOL ShouldPhoneBeForcedOnScreen = FALSE //TRUE
// Answerphone
IF NOT PRIVATE_FriendAcceptsCall(playerID, friendID)
IF IS_PLAYER_PED_PLAYABLE(friendCharID)
OR PRIVATE_Friend_DoAnswerPhone(friendActivityCallConversation, friendCharID)
CPRINTLN(DEBUG_FRIENDS, "DEBUG_PrintFriendCallInfo(player called friend ANSWERPHONE)")
bWasOutgoingCallSuccessful = FALSE
bWasCallAnswephone = TRUE
RETURN TRUE
ENDIF
// Accepts
ELIF PRIVATE_FriendAcceptsPlayer(playerID, friendID)
enumFriendPhonePhrase eGreetPhraseA, eGreetPhraseB
PRIVATE_Friend_GetPhoneConv_Greet(playerID, eGreetPhraseA, eGreetPhraseB)
enumFriendPhonePhrase eAcceptPhraseA, eAcceptPhraseB
PRIVATE_Friend_GetPhoneConv_Accept(playerID, friendID, eAcceptPhraseA, eAcceptPhraseB, TRUE)
Private_GetFriendPhonePhrase(playerCharID, friendCharID, eGreetPhraseA, tBlock, tRootA)
Private_GetFriendPhonePhrase(playerCharID, friendCharID, eGreetPhraseB, tBlock, tRootB)
Private_GetFriendPhonePhrase(playerCharID, friendCharID, eAcceptPhraseA, tBlock, tRootC)
Private_GetFriendPhonePhrase(playerCharID, friendCharID, eAcceptPhraseB, tBlock, tRootD)
tRootC_spec = tRootC
IF eAcceptPhraseA = FPP_OUTGOING_YES1_FIRSTTIME
tRootC_spec += "_1"
ENDIF
IF PLAYER_CALL_CHAR_CELLPHONE_MULTIPART_WITH_4_LINES(friendActivityCallConversation,
GET_CHAR_FROM_FRIEND(friendID),
tBlock,
tRootA, tRootA, // Greeting 1
tRootB, tRootB, // Greeting 2
tRootC, tRootC_spec, // Friend accepts
tRootD, tRootD, // Player says bye
CONV_PRIORITY_FLOW_ONLY_USE_AMBIENT_SLOT, ShouldPhoneBeForcedOnScreen)
#IF IS_DEBUG_BUILD
DEBUG_PrintFriendCallInfo("player called friend ACCEPT", tRootA, tRootB, tRootC, tRootD)
#ENDIF
// Check if friend will come to player
IF eAcceptPhraseA = FPP_OUTGOING_YES1_COMETOYOU
bWillFriendComeToPlayer = TRUE
ELSE
bWillFriendComeToPlayer = FALSE
ENDIF
bWasOutgoingCallSuccessful = TRUE
bWasCallAnswephone = FALSE
RETURN TRUE
ENDIF
// Declines - special case: because Lamar is laying low during the day after Michael/Trevor killed in finale
ELIF NOT PRIVATE_FriendAcceptsPlayer_laylow(friendID)
INT iRandomConv = GET_RANDOM_INT_IN_RANGE(0, 3)
IF iRandomConv = 0
Private_GetFriendPhonePhrase(playerCharID, friendCharID, FPP_LAYLOW1, tBlock, tRootA)
ELIF iRandomConv = 1
Private_GetFriendPhonePhrase(playerCharID, friendCharID, FPP_LAYLOW2, tBlock, tRootA)
ELSE
Private_GetFriendPhonePhrase(playerCharID, friendCharID, FPP_LAYLOW3, tBlock, tRootA)
ENDIF
IF PLAYER_CALL_CHAR_CELLPHONE(friendActivityCallConversation,
GET_CHAR_FROM_FRIEND(friendID),
tBlock, tRootA,
CONV_PRIORITY_FLOW_ONLY_USE_AMBIENT_SLOT, ShouldPhoneBeForcedOnScreen)
#IF IS_DEBUG_BUILD
DEBUG_PrintFriendCallInfo("player called friend DECLINE (LAYLOW)", tRootA, tRootA, tRootA, tRootA)
#ENDIF
bWasOutgoingCallSuccessful = FALSE
bWasCallAnswephone = FALSE
RETURN TRUE
ENDIF
// Declines - general
ELSE
enumFriendPhonePhrase eGreetPhraseA, eGreetPhraseB
PRIVATE_Friend_GetPhoneConv_Greet(playerID, eGreetPhraseA, eGreetPhraseB)
enumFriendPhonePhrase eDeclinePhraseA, eDeclinePhraseB
PRIVATE_Friend_GetPhoneConv_Reject(playerID, friendID, eDeclinePhraseA, eDeclinePhraseB)
Private_GetFriendPhonePhrase(playerCharID, friendCharID, eGreetPhraseA, tBlock, tRootA)
Private_GetFriendPhonePhrase(playerCharID, friendCharID, eGreetPhraseB, tBlock, tRootB)
Private_GetFriendPhonePhrase(playerCharID, friendCharID, eDeclinePhraseA, tBlock, tRootC)
Private_GetFriendPhonePhrase(playerCharID, friendCharID, eDeclinePhraseB, tBlock, tRootD)
IF PLAYER_CALL_CHAR_CELLPHONE_MULTIPART_WITH_4_LINES(friendActivityCallConversation,
GET_CHAR_FROM_FRIEND(friendID),
tBlock,
tRootA, tRootA, // Greeting 1
tRootB, tRootB, // Greeting 2
tRootC, tRootC, // Friend declines
tRootD, tRootD, // Player says bye
CONV_PRIORITY_FLOW_ONLY_USE_AMBIENT_SLOT, ShouldPhoneBeForcedOnScreen)
#IF IS_DEBUG_BUILD
DEBUG_PrintFriendCallInfo("player called friend DECLINE", tRootA, tRootB, tRootC, tRootD)
#ENDIF
bWasOutgoingCallSuccessful = FALSE
bWasCallAnswephone = FALSE
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
//FUNC BOOL Private_StartSquadCall(enumCharacterList friendCharID, BOOL& bWasOutgoingCallSuccessful) // BBUDDIES REMOVED
//
// enumCharacterList playerCharID = g_eDefaultPlayerChar
//
// enumFriend playerID = GET_FRIEND_FROM_CHAR(playerCharID)
// enumFriend friendID = GET_FRIEND_FROM_CHAR(friendCharID)
//
// // Setup the conversation participants
// ADD_FRIEND_CHAR_FOR_DIALOGUE(friendActivityCallConversation, playerCharID, PLAYER_PED_ID())
// ADD_FRIEND_CHAR_FOR_DIALOGUE(friendActivityCallConversation, friendCharID, NULL)
//
// BOOL ShouldPhoneBeForcedOnScreen = FALSE //TRUE
//
// // Answerphone
// IF NOT PRIVATE_SquadAcceptsCall(playerID, friendID)
//
// IF PRIVATE_Friend_DoAnswerPhone(friendActivityCallConversation, friendCharID)
//
// CPRINTLN(DEBUG_FRIENDS, "DEBUG_PrintFriendCallInfo(player called squad ANSWERPHONE)")
// bWasOutgoingCallSuccessful = FALSE
// RETURN TRUE
//
// ENDIF
//
// // Successful call
// ELSE
//
// enumFriendPhonePhrase eBackupPhrase
// PRIVATE_Friend_GetPhoneConv_Backup(eBackupPhrase)
//
// TEXT_LABEL tBlock
// TEXT_LABEL tBackupRoot
// Private_GetFriendPhonePhrase(playerCharID, friendCharID, eBackupPhrase, tBlock, tBackupRoot)
//
// IF PLAYER_CALL_CHAR_CELLPHONE(friendActivityCallConversation,
// friendCharID,
// tBlock,
// tBackupRoot,
// CONV_PRIORITY_FLOW_ONLY_USE_AMBIENT_SLOT, ShouldPhoneBeForcedOnScreen)
//
// #IF IS_DEBUG_BUILD
// TEXT_LABEL tEmpty = ""
// DEBUG_PrintFriendCallInfo("player called squad ACCEPT", tBackupRoot, tEmpty, tEmpty, tEmpty)
// #ENDIF
// bWasOutgoingCallSuccessful = TRUE
// RETURN TRUE
//
// ENDIF
//
// ENDIF
//
// RETURN FALSE
//ENDFUNC
// PURPOSE: Maintains the Drunk Request at the specified Drunk Request array position.
// Involves: error checks; requesting, loading, running 'drunk' script; setting/clearing details.
//
// INPUT PARAMS: paramArrayIndex Index into drunk requests array
PROC UpdateFriendConnection(enumFriendConnection eConnection, enumFriend eFriendID)
// enumCharacterList ePlayerChar = g_eDefaultPlayerChar
enumCharacterList eFriendChar = GET_CHAR_FROM_FRIEND(eFriendID)
BOOL bWasCallSuccessful
BOOL bWasCallAnswerphone
BOOL bWasCallComeToYou
enumFriendConnectionState eState = g_FriendConnectState[eConnection].state
// SWITCH g_FriendConnectState[eConnection].state
// ----------------------------------------------------------------------------------------------------- CONTACTWAIT
// Wait for friend activity to be arranged
// NB: In this state, FriendActivity.sc/BattleBuddy.sc can directly set the connection's state to Active - (if two other friend connections are active, and share the friends in this connection)
IF eState = FC_STATE_ContactWait
IF NOT IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_SWITCH)
// Monitor squad calls
// IF IS_CALLING_CONTACT_FOR_SECONDARY_FUNCTION(eFriendChar) // BBUDDIES REMOVED
// IF CAN_CHARS_ARRANGE_BATTLE_BUDDY(ePlayerChar, eFriendChar)
// IF Private_StartSquadCall(eFriendChar, bWasCallSuccessful)
// IF bWasCallSuccessful
// SET_CONNECTION_FLAG(eConnection, FC_FLAG_HasCallConnected, FALSE)
// SET_CONNECTION_FLAG(eConnection, FC_FLAG_IsCallAnswerphone, FALSE)
// SET_CONNECTION_STATE(eConnection, FC_STATE_PhoneAccept)
//
// SET_CONNECTION_MODE(eConnection, FC_MODE_Squad)
// SET_SQUAD_MISSION_TO_CURRENT_ZONE()
// ELSE
// SET_CONNECTION_FLAG(eConnection, FC_FLAG_HasCallConnected, FALSE)
// SET_CONNECTION_FLAG(eConnection, FC_FLAG_IsCallAnswerphone, TRUE)
// SET_CONNECTION_STATE(eConnection, FC_STATE_PhoneDecline)
// ENDIF
// EXIT
// ENDIF
// ENDIF
// ENDIF
// Monitor friend calls
IF IS_CALLING_FRIEND_FOR_ACTIVITY(eFriendChar)
IF NOT IS_CURRENTLY_ON_MISSION_TO_TYPE(MISSION_TYPE_FRIEND_ACTIVITY)
IF Private_StartFriendCall(eFriendChar, bWasCallSuccessful, bWasCallAnswerphone, bWasCallComeToYou)
IF bWasCallSuccessful
IF bWasCallComeToYou
SET_CONNECTION_MODE(eConnection, FC_MODE_Adhoc)
ELSE
SET_CONNECTION_MODE(eConnection, FC_MODE_Friend)
ENDIF
SET_CONNECTION_FLAG(eConnection, FC_FLAG_HasCallConnected, FALSE)
SET_CONNECTION_FLAG(eConnection, FC_FLAG_IsCallAnswerphone, FALSE)
SET_CONNECTION_STATE(eConnection, FC_STATE_PhoneAccept)
ELSE
g_SavedGlobals.sFriendsData.g_FriendConnectData[eConnection].wanted = eFriendID
SET_CONNECTION_FLAG(eConnection, FC_FLAG_HasCallConnected, FALSE)
SET_CONNECTION_FLAG(eConnection, FC_FLAG_IsCallAnswerphone, bWasCallAnswerphone)
SET_CONNECTION_STATE(eConnection, FC_STATE_PhoneDecline)
ENDIF
EXIT
ENDIF
ENDIF
ENDIF
ENDIF
// BREAK
// ----------------------------------------------------------------------------------------------------- FC_STATE_PhoneAccept
ELIF eState = FC_STATE_PhoneAccept
// Set bit if phone is answered
IF NOT IS_CONNECTION_FLAG_SET(eConnection, FC_FLAG_HasCallConnected)
IF IS_CELLPHONE_CONVERSATION_PLAYING()
SET_CONNECTION_FLAG(eConnection, FC_FLAG_HasCallConnected, TRUE)
ENDIF
ENDIF
TEXT_LABEL tFriendScriptName = "FriendActivity"
REQUEST_SCRIPT(tFriendScriptName)
// When call ends, set to init state (if not answered return to ContactWait state)
IF HAS_CELLPHONE_CALL_FINISHED()
OR WAS_LAST_CELLPHONE_CALL_INTERRUPTED()
IF IS_CONNECTION_FLAG_SET(eConnection, FC_FLAG_HasCallConnected)
RESET_FRIEND_LAST_CONTACT_TIMER(g_eDefaultPlayerChar, eFriendChar, FRIEND_CONTACT_PHONE)
g_SavedGlobals.sFriendsData.g_bHelpDoneCanCancel = TRUE
// IF GET_CONNECTION_MODE(eConnection) = FC_MODE_Squad // BBUDDIES REMOVED
// INC_BBUDDY_CALL_STAT(g_eDefaultPlayerChar, eFriendChar)
// ENDIF
SET_CONNECTION_STATE(eConnection, FC_STATE_Init)
ELSE
SET_SCRIPT_AS_NO_LONGER_NEEDED(tFriendScriptName)
SET_CONNECTION_STATE(eConnection, FC_STATE_ContactWait)
ENDIF
ENDIF
// BREAK
// ----------------------------------------------------------------------------------------------------- FC_STATE_PhoneDecline
// Wait for unsuccessful call to finish
ELIF eState = FC_STATE_PhoneDecline
IF IS_CONNECTION_FLAG_SET(eConnection, FC_FLAG_IsCallAnswerphone) AND IS_PLAYER_PED_PLAYABLE(eFriendChar)
IF NOT IS_CALLING_FRIEND_FOR_ACTIVITY(eFriendChar)
IF g_savedGlobals.sFriendsData.g_bHasPlayerBeenTurnedDown = FALSE
g_savedGlobals.sFriendsData.g_bHasPlayerBeenTurnedDown = TRUE
ENDIF
SET_CONNECTION_STATE(eConnection, FC_STATE_ContactWait)
ENDIF
ELSE
// Set bit if phone is answered
IF NOT IS_CONNECTION_FLAG_SET(eConnection, FC_FLAG_HasCallConnected)
IF IS_CELLPHONE_CONVERSATION_PLAYING()
SET_CONNECTION_FLAG(eConnection, FC_FLAG_HasCallConnected, TRUE)
ENDIF
ENDIF
// When call ends, return to ContactWait state
IF HAS_CELLPHONE_CALL_FINISHED()
OR WAS_LAST_CELLPHONE_CALL_INTERRUPTED()
// If call was answered by friend -> reset the timer
IF IS_CONNECTION_FLAG_SET(eConnection, FC_FLAG_HasCallConnected)
IF NOT IS_CONNECTION_FLAG_SET(eConnection, FC_FLAG_IsCallAnswerphone)
RESET_FRIEND_LAST_CONTACT_TIMER(g_eDefaultPlayerChar, eFriendChar, FRIEND_CONTACT_PHONE)
ENDIF
// If player has been turned down before -> decrease like stat
IF g_savedGlobals.sFriendsData.g_bHasPlayerBeenTurnedDown = FALSE
g_savedGlobals.sFriendsData.g_bHasPlayerBeenTurnedDown = TRUE
ELIF NOT IS_CONNECTION_FLAG_SET(eConnection, FC_FLAG_IsCallAnswerphone)
UPDATE_FRIEND_LIKE(g_eDefaultPlayerChar, eFriendChar, FriendLike_DECLINED_CALL)
ENDIF
ENDIF
//g_SavedGlobals.sFriendsData.g_FriendConnectData[eConnection].thread = 0
SET_CONNECTION_STATE(eConnection, FC_STATE_ContactWait)
ENDIF
ENDIF
// BREAK
// ----------------------------------------------------------------------------------------------------- FC_STATE_Init
// Wait for friend activity script to launch
// NB: In this state, FriendActivity.sc can directly set the connection's state to Friend/ContactWait
ELIF eState = FC_STATE_Init
TEXT_LABEL tFriendScriptName = "FriendActivity"
IF NOT IS_THREAD_ACTIVE(INT_TO_NATIVE(THREADID, g_SavedGlobals.sFriendsData.g_FriendScriptThread))
IF NOT HAS_SCRIPT_LOADED(tFriendScriptName)
CPRINTLN(DEBUG_FRIENDS, "FC_STATE_Init - Requesting script \"", tFriendScriptName, "\"")
REQUEST_SCRIPT(tFriendScriptName)
ELSE
CPRINTLN(DEBUG_FRIENDS, "FC_STATE_Init - STARTING SCRIPT **\"", tFriendScriptName, "\"**")
THREADID friendThread
friendThread = START_NEW_SCRIPT_WITH_ARGS(tFriendScriptName, eConnection, SIZE_OF(eConnection), FRIEND_STACK_SIZE)
g_SavedGlobals.sFriendsData.g_FriendScriptThread = NATIVE_TO_INT(friendThread)
SET_SCRIPT_AS_NO_LONGER_NEEDED(tFriendScriptName)
ENDIF
ENDIF
// BREAK
// ----------------------------------------------------------------------------------------------------- FC_STATE_Active
// Wait for friend activity script to finish
// NB: In this state, FriendActivity.sc can directly set the connection's state to ContactWait
ELIF eState = FC_STATE_Active
IF NOT IS_THREAD_ACTIVE(INT_TO_NATIVE(THREADID, g_SavedGlobals.sFriendsData.g_FriendScriptThread))
g_SavedGlobals.sFriendsData.g_FriendScriptThread = 0
SET_CONNECTION_STATE(eConnection, FC_STATE_ContactWait)
ENDIF
// BREAK
// -----------------------------------------------------------------------------------------------------
ELIF eState = FC_STATE_Invalid
// DEFAULT
// Empty
ENDIF
// ENDSWITCH
ENDPROC
// *******************************************************************************************
// MAIN SCRIPT
// *******************************************************************************************
#IF IS_DEBUG_BUILD
PED_INDEX hSelectorMirror[NUM_OF_PLAYABLE_PEDS]
PROC DEBUG_WatchSelector()
INT iGameTimer = GET_FRAME_COUNT()
enumCharacterList eChar
REPEAT NUM_OF_PLAYABLE_PEDS eChar
SELECTOR_SLOTS_ENUM eSlot = GET_SELECTOR_SLOT_FROM_PLAYER_PED_ENUM(eChar)
IF hSelectorMirror[eSlot] <> g_sPlayerPedRequest.sSelectorPeds.pedID[eSlot]
TEXT_LABEL tChar = GetLabel_enumCharacterList(eChar)
TEXT_LABEL tMirror
IF NOT IS_PED_INJURED(hSelectorMirror[eSlot]) tMirror = "a"
ELIF DOES_ENTITY_EXIST(hSelectorMirror[eSlot]) tMirror = "e"
ELSE tMirror = "x"
ENDIF
TEXT_LABEL tSelect
IF NOT IS_PED_INJURED(g_sPlayerPedRequest.sSelectorPeds.pedID[eSlot]) tSelect = "a"
ELIF DOES_ENTITY_EXIST(g_sPlayerPedRequest.sSelectorPeds.pedID[eSlot]) tSelect = "e"
ELSE tSelect = "x"
ENDIF
tMirror += NATIVE_TO_INT(hSelectorMirror[eSlot])
tSelect += NATIVE_TO_INT(g_sPlayerPedRequest.sSelectorPeds.pedID[eSlot])
PRINTSTRING("<friend_controller>:WatchSelector ")
PRINTINT(iGameTimer)
PRINTSTRING(": ")
PRINTSTRING(tChar)
PRINTSTRING(" SELECTOR SLOT CHANGE: ")
PRINTSTRING(tMirror)
PRINTSTRING(" to ")
PRINTSTRING(tSelect)
PRINTNL()
hSelectorMirror[eSlot] = g_sPlayerPedRequest.sSelectorPeds.pedID[eSlot]
ENDIF
ENDREPEAT
ENDPROC
#ENDIF
SCRIPT
// 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_MAGDEMO))
Friends_Controller_Cleanup()
ENDIF
CPRINTLN(DEBUG_FRIENDS, "/\\/\\ Starting friends_controller.sc (running)")
Initialise_Friends_Controller_Variables()
Setup_Friends_controller()
WAIT(0)
// Ensure all valid connections get upgraded if coming from a save game
enumFriendConnection eConnection
REPEAT MAX_FRIEND_CONNECTIONS eConnection
IF g_FriendConnectState[eConnection].state = FC_STATE_Invalid
IF IS_BIT_SET(g_SavedGlobals.sFriendsData.g_FriendConnectData[eConnection].flags, ENUM_TO_INT(FC_FLAG_HasInitiated))
SET_CONNECTION_STATE(eConnection, FC_STATE_ContactWait)
g_iNumberOfActiveFriends++
ENDIF
ENDIF
ENDREPEAT
enumFriend eNonPlayerFriend
eMaintainedFriendCon = FC_MICHAEL_FRANKLIN
// ePhoneLoopChar = CHAR_MICHAEL // BBUDDIES REMOVED
WHILE bFriends_controller_in_progress
WAIT(0)
// Maintain value for players current ped enum (instead of calling the accessor over and over again!)
g_eDefaultPlayerChar = GET_CURRENT_PLAYER_PED_ENUM()
IF g_eDefaultPlayerChar = NO_CHARACTER
IF bDefault_player_char_has_been_initialized
#IF IS_DEBUG_BUILD
IF (g_iDebugSelectedFriendConnDisplay > 0)
CPRINTLN(DEBUG_FRIENDS, "Dont maintain player requests - default player char = ", GET_PLAYER_PED_STRING(g_eDefaultPlayerChar), " [frame: ", GET_FRAME_COUNT(), "]")
ENDIF
#ENDIF
ENDIF
ELSE
// Update friend connections
bDefault_player_char_has_been_initialized = TRUE
IF g_flowUnsaved.bFlowControllerBusy
#IF IS_DEBUG_BUILD
DrawFriendLiteralString("g_bFlowControllerBusy!", 1, HUD_COLOUR_RED)
#ENDIF
ELIF IS_PLAYER_PLAYING(PLAYER_ID())
// Process friend connections (staggered, one per frame)
IF GET_OTHER_FRIEND_FROM_CONNECTION(eMaintainedFriendCon, eNonPlayerFriend)
UpdateFriendConnection(eMaintainedFriendCon, eNonPlayerFriend)
ENDIF
eMaintainedFriendCon = INT_TO_ENUM(enumFriendConnection, (ENUM_TO_INT(eMaintainedFriendCon) + 1) % ENUM_TO_INT(MAX_FRIEND_CONNECTIONS))
IF NOT g_savedGlobals.sFriendsData.g_bHelpDoneCanPhoneDecline
IF g_savedGlobals.sFriendsData.g_bHasPlayerBeenTurnedDown
SWITCH GET_FLOW_HELP_MESSAGE_STATUS("AM_H_NOFRND")
CASE FHS_EXPIRED
ADD_HELP_TO_FLOW_QUEUE("AM_H_NOFRND", FHP_HIGH, 0, 1000, DEFAULT_GOD_TEXT_TIME)
BREAK
CASE FHS_DISPLAYED
g_savedGlobals.sFriendsData.g_bHelpDoneCanPhoneDecline = TRUE
BREAK
ENDSWITCH
ENDIF
ENDIF
// Update squad phone contacts
// IF g_eDefaultPlayerChar <> ePhoneLoopChar // BBUDDIES REMOVED
// SWITCH ePhoneLoopChar
// CASE CHAR_MICHAEL
// IF CAN_CHARS_ARRANGE_BATTLE_BUDDY(g_eDefaultPlayerChar, CHAR_MICHAEL)
// IF NOT IS_BIT_SET(g_bitfieldBattleBuddyPhoneContact, ENUM_TO_INT(CHAR_MICHAEL))
// OR NOT IS_MICHAEL_SECONDARY_CONTACT_LIST_FUNCTION_AVAILABLE()
// SET_MICHAEL_SECONDARY_CONTACT_LIST_FUNCTION_AVAILABLE("CELL_223")
// SET_BIT(g_bitfieldBattleBuddyPhoneContact, ENUM_TO_INT(CHAR_MICHAEL))
// ENDIF
// ELSE
// IF IS_BIT_SET(g_bitfieldBattleBuddyPhoneContact, ENUM_TO_INT(CHAR_MICHAEL))
// CPRINTLN(DEBUG_FRIENDS, "UpdateFriendSquadContacts() - REMOVE_MICHAEL_SECONDARY_CONTACT_LIST_FUNCTION")
// CLEAR_BIT(g_bitfieldBattleBuddyPhoneContact, ENUM_TO_INT(CHAR_MICHAEL))
// REMOVE_MICHAEL_SECONDARY_CONTACT_LIST_FUNCTION()
// ENDIF
// ENDIF
// BREAK
//
// CASE CHAR_FRANKLIN
// IF CAN_CHARS_ARRANGE_BATTLE_BUDDY(g_eDefaultPlayerChar, CHAR_FRANKLIN)
// IF NOT IS_BIT_SET(g_bitfieldBattleBuddyPhoneContact, ENUM_TO_INT(CHAR_FRANKLIN))
// OR NOT IS_FRANKLIN_SECONDARY_CONTACT_LIST_FUNCTION_AVAILABLE()
// SET_FRANKLIN_SECONDARY_CONTACT_LIST_FUNCTION_AVAILABLE("CELL_223")
// SET_BIT(g_bitfieldBattleBuddyPhoneContact, ENUM_TO_INT(CHAR_FRANKLIN))
// ENDIF
// ELSE
// IF IS_BIT_SET(g_bitfieldBattleBuddyPhoneContact, ENUM_TO_INT(CHAR_FRANKLIN))
// CPRINTLN(DEBUG_FRIENDS, "UpdateFriendSquadContacts() - REMOVE_FRANKLIN_SECONDARY_CONTACT_LIST_FUNCTION")
// CLEAR_BIT(g_bitfieldBattleBuddyPhoneContact, ENUM_TO_INT(CHAR_FRANKLIN))
// REMOVE_FRANKLIN_SECONDARY_CONTACT_LIST_FUNCTION()
// ENDIF
// ENDIF
// BREAK
//
// CASE CHAR_TREVOR
// IF CAN_CHARS_ARRANGE_BATTLE_BUDDY(g_eDefaultPlayerChar, CHAR_TREVOR)
// IF NOT IS_BIT_SET(g_bitfieldBattleBuddyPhoneContact, ENUM_TO_INT(CHAR_TREVOR))
// OR NOT IS_TREVOR_SECONDARY_CONTACT_LIST_FUNCTION_AVAILABLE()
// SET_TREVOR_SECONDARY_CONTACT_LIST_FUNCTION_AVAILABLE("CELL_223")
// SET_BIT(g_bitfieldBattleBuddyPhoneContact, ENUM_TO_INT(CHAR_TREVOR))
// ENDIF
// ELSE
// IF IS_BIT_SET(g_bitfieldBattleBuddyPhoneContact, ENUM_TO_INT(CHAR_TREVOR))
// CPRINTLN(DEBUG_FRIENDS, "UpdateFriendSquadContacts() - REMOVE_TREVOR_SECONDARY_CONTACT_LIST_FUNCTION")
// CLEAR_BIT(g_bitfieldBattleBuddyPhoneContact, ENUM_TO_INT(CHAR_TREVOR))
// REMOVE_TREVOR_SECONDARY_CONTACT_LIST_FUNCTION()
// ENDIF
// ENDIF
// BREAK
// ENDSWITCH
// ENDIF
//
// ePhoneLoopChar = INT_TO_ENUM(enumCharacterList, (ENUM_TO_INT(ePhoneLoopChar) + 1) % ENUM_TO_INT(NUM_OF_PLAYABLE_PEDS))
// If mission has been set as launching, monitor it's progress until it ends
IF g_eFriendMissionZoneState = FRIEND_MISSION_ZONE_LAUNCHING
//IF MISSION_FLOW_GET_RUNNING_MISSION() = g_eFriendMissionZoneID
IF IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_STORY)
OR IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_STORY_FRIENDS)
OR IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_STORY_PREP)
SET_FRIEND_MISSION_ZONE(g_eFriendMissionZoneID, FRIEND_MISSION_ZONE_LAUNCHED, g_iFriendMissionZoneAcceptBitset)
ENDIF
ELIF g_eFriendMissionZoneState = FRIEND_MISSION_ZONE_LAUNCHED
// IF IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_STORY_PREP) // BBUDDIES REMOVED
//
// // If mission has failed and a replay has been accepted, don't class this as on-mission (otherwise pause fail timers)
// IF NOT (IS_REPLAY_BEING_PROCESSED() OR g_replay.replayStageID = RS_REJECTED)
//
// PAUSE_SOLDIER_FAIL_TIMERS()
//
// ENDIF
//
// ELIF NOT IS_CURRENTLY_ON_MISSION_OF_ANY_TYPE() OR MISSION_FLOW_GET_RUNNING_MISSION() <> g_eFriendMissionZoneID
//
// // Reset timers that were paused for existing during a non-replay-processing prep mission
// CANCEL_PAUSED_SOLDIER_FAIL_TIMERS()
// CLEAR_FRIEND_MISSION_ZONE(g_eFriendMissionZoneID)
//
// ENDIF
IF NOT IS_CURRENTLY_ON_MISSION_OF_ANY_TYPE() // BBUDDIES REPLACED
CLEAR_FRIEND_MISSION_ZONE(g_eFriendMissionZoneID)
ENDIF
ENDIF
// If player switches to failed soldier, clear the fail
// IF IS_TIMER_STARTED(g_savedGlobals.sFriendsData.g_SoldierFailTimers[g_eDefaultPlayerChar]) // BBUDDIES REMOVED
// CANCEL_TIMER(g_savedGlobals.sFriendsData.g_SoldierFailTimers[g_eDefaultPlayerChar])
// ENDIF
// If player switches to failed friend, clear the fail // BBUDDIES REPLACED
// enumFriend ePlayerID = GET_FRIEND_FROM_CHAR(g_eDefaultPlayerChar)
// IF ePlayerID < MAX_FRIENDS
// CANCEL_TIMER(g_savedGlobals.sFriendsData.g_FriendFailTimers[ePlayerID])
// ENDIF
// Unpause fail timer, if associated text message has been recieved
// enumFriend eFriendLoop
// REPEAT MAX_FRIENDS eFriendLoop
// IF g_savedGlobals.sFriendsData.g_FriendFailMessages[eFriendLoop] <> COMM_NONE
// IF IS_COMMUNICATION_REGISTERED(g_savedGlobals.sFriendsData.g_FriendFailMessages[eFriendLoop])
// CC_CommunicationStatus eStatus = GET_COMMUNICATION_STATUS(g_savedGlobals.sFriendsData.g_FriendFailMessages[eFriendLoop])
// IF eStatus = CS_INACTIVE
// OR eStatus = CS_ERROR
// UNPAUSE_FRIEND_FAIL_TIMER(eFriendLoop)
// ENDIF
// ENDIF
// ENDIF
// ENDREPEAT
// Process one friend fail timer each frame
IF GET_FRAME_COUNT() % 10 = 0
IF NOT IS_THREAD_ACTIVE(INT_TO_NATIVE(THREADID, g_SavedGlobals.sFriendsData.g_FriendScriptThread))
IF IS_TIMER_PAUSED(g_savedGlobals.sFriendsData.g_FriendFailTimers[eMaintainFriendTimer])
IF g_savedGlobals.sFriendsData.g_FriendFailMessages[eMaintainFriendTimer] = COMM_NONE
OR NOT IS_TEXT_MESSAGE_REGISTERED(g_savedGlobals.sFriendsData.g_FriendFailMessages[eMaintainFriendTimer])
OR GET_TEXT_MESSAGE_STATUS(g_savedGlobals.sFriendsData.g_FriendFailMessages[eMaintainFriendTimer]) = CS_ERROR
UNPAUSE_FRIEND_FAIL_TIMER(eMaintainFriendTimer)
ENDIF
ENDIF
eMaintainFriendTimer = INT_TO_ENUM(enumFriend, (ENUM_TO_INT(eMaintainFriendTimer) + 1) % ENUM_TO_INT(MAX_FRIENDS))
ENDIF
ENDIF
ENDIF
// Debug checks
#IF IS_DEBUG_BUILD
// DEBUG_WatchSelector()
DEBUG_DisplayMissionZoneInfo()
#ENDIF
// Update ambient friends
Private_UpdateAmbGroup(ambGroup, friendActivityCallConversation)
ENDIF
// VEHICLE_INDEX hVeh = ambGroup.mFriends[CHAR_MICHAEL].hVehicle //g_vPlayerVeh[CHAR_MICHAEL]
// TEXT_LABEL_63 tVeh = "None"
// IF DOES_ENTITY_EXIST(hVeh)
// tVeh = GET_MODEL_NAME_FOR_DEBUG(GET_ENTITY_MODEL(hVeh))
// IF IS_VEHICLE_DRIVEABLE(hVeh)
// tVeh += " driveable"
// ENDIF
// ENDIF
// DISPLAY_TEXT_WITH_LITERAL_STRING(0.1, 0.1, "STRING", tVeh)
//
// hVeh = ambGroup.mFriends[CHAR_FRANKLIN].hVehicle //g_vPlayerVeh[CHAR_MICHAEL]
// tVeh = "None"
// IF DOES_ENTITY_EXIST(hVeh)
// tVeh = GET_MODEL_NAME_FOR_DEBUG(GET_ENTITY_MODEL(hVeh))
// IF IS_VEHICLE_DRIVEABLE(hVeh)
// tVeh += " driveable"
// ENDIF
// ENDIF
// DISPLAY_TEXT_WITH_LITERAL_STRING(0.1, 0.2, "STRING", tVeh)
// IF Is_Savehouse_Respawn_Available(SAVEHOUSE_MICHAEL_BH)
// DISPLAY_TEXT_WITH_LITERAL_STRING(0.1, 0.1, "STRING", "SAVEHOUSE_MICHAEL_BH")
// ENDIF
// IF Is_Savehouse_Respawn_Available(SAVEHOUSE_MICHAEL_CS)
// DISPLAY_TEXT_WITH_LITERAL_STRING(0.1, 0.2, "STRING", "SAVEHOUSE_MICHAEL_CS")
// ENDIF
//
// IF Is_Savehouse_Respawn_Available(SAVEHOUSE_FRANKLIN_SC)
// DISPLAY_TEXT_WITH_LITERAL_STRING(0.1, 0.3, "STRING", "SAVEHOUSE_FRANKLIN_SC")
// ENDIF
// IF Is_Savehouse_Respawn_Available(SAVEHOUSE_FRANKLIN_VH)
// DISPLAY_TEXT_WITH_LITERAL_STRING(0.1, 0.4, "STRING", "SAVEHOUSE_FRANKLIN_VH")
// ENDIF
//
// IF Is_Savehouse_Respawn_Available(SAVEHOUSE_TREVOR_CS)
// DISPLAY_TEXT_WITH_LITERAL_STRING(0.1, 0.5, "STRING", "SAVEHOUSE_TREVOR_CS")
// ENDIF
// IF Is_Savehouse_Respawn_Available(SAVEHOUSE_TREVOR_VB)
// DISPLAY_TEXT_WITH_LITERAL_STRING(0.1, 0.6, "STRING", "SAVEHOUSE_TREVOR_VB")
// ENDIF
// IF Is_Savehouse_Respawn_Available(SAVEHOUSE_TREVOR_SC)
// DISPLAY_TEXT_WITH_LITERAL_STRING(0.1, 0.7, "STRING", "SAVEHOUSE_TREVOR_SC")
// ENDIF
// IF Private_IsPlayerAtOwnSafehouse()
// DISPLAY_TEXT_WITH_LITERAL_STRING(0.1, 0.1, "STRING", "AtSafehouse")
// ELSE
// DISPLAY_TEXT_WITH_LITERAL_STRING(0.1, 0.1, "STRING", "NOT AtSafehouse")
// ENDIF
// Shutdown if on disallowed mission type...
IF NOT Should_Friends_Controller_Be_Running()
#IF IS_DEBUG_BUILD
AND NOT g_bDebug_KeepFriendsControllerRunning
#ENDIF
CPRINTLN(DEBUG_FRIENDS, "...friends_controller.sc has been forced to cleanup...")
bFriends_controller_in_progress = FALSE
ENDIF
ENDWHILE
Friends_Controller_Cleanup()
ENDSCRIPT