Files
gtav-src/script/dev_ng/singleplayer/include/public/friends_public.sch
T
2025-09-29 00:52:08 +02:00

975 lines
35 KiB
Scheme
Executable File

USING "rage_builtins.sch"
USING "globals.sch"
USING "timer_public.sch"
USING "friends_private.sch"
#IF IS_DEBUG_BUILD
USING "script_debug.sch"
USING "shared_debug.sch"
USING "flow_debug_GAME.sch"
#ENDIF
///public interface for friends scripts
/// sam.hackett@rockstarleeds.com
///
// *******************************************************************************************
// ENUMS
// *******************************************************************************************
ENUM enumFriendLikeType
FriendLike_DECLINED_CALL = 0,
FriendLike_BUDDY_INJURED,
FriendLike_BUDDY_LOST,
FriendLike_PLAYER_LATE_ARRIVAL,
FriendLike_PLAYER_NO_ARRIVAL,
FriendLike_PLAYER_CANCELLED,
FriendLike_DID_SUGGESTED_ACTIVITY,
FriendLike_DID_UNSUGGESTED_ACTIVITY,
// FriendLike_PASSED_ACTIVITY,
MAX_FRIEND_LIKE_EVENTS
ENDENUM
// *******************************************************************************************
// CONSTANTS
// *******************************************************************************************
CONST_INT CONST_iLikeStatInitial 50
CONST_INT CONST_iLikeStatMin 0
CONST_INT CONST_iLikeStatMax 100
// *******************************************************************************************
// FRIEND PED ACCESSORS
// *******************************************************************************************
FUNC PED_INDEX FRIEND_A_PED_ID()
RETURN g_pActivityFriendA
ENDFUNC
FUNC PED_INDEX FRIEND_B_PED_ID()
RETURN g_pActivityFriendB
ENDFUNC
// *******************************************************************************************
// FRIEND LIKE STAT
// *******************************************************************************************
#IF IS_DEBUG_BUILD
FUNC STRING GetLabel_enumFriendLikeType(enumFriendLikeType event)
SWITCH event
CASE FriendLike_DECLINED_CALL RETURN "FriendLike_DECLINED_CALL"
CASE FriendLike_PLAYER_CANCELLED RETURN "FriendLike_PLAYER_CANCELLED"
CASE FriendLike_PLAYER_LATE_ARRIVAL RETURN "FriendLike_PLAYER_LATE_ARRIVAL"
CASE FriendLike_PLAYER_NO_ARRIVAL RETURN "FriendLike_PLAYER_NO_ARRIVAL"
CASE FriendLike_BUDDY_INJURED RETURN "FriendLike_BUDDY_INJURED"
CASE FriendLike_BUDDY_LOST RETURN "FriendLike_BUDDY_LOST"
CASE FriendLike_DID_SUGGESTED_ACTIVITY RETURN "FriendLike_DID_SUGGESTED_ACTIVITY"
CASE FriendLike_DID_UNSUGGESTED_ACTIVITY RETURN "FriendLike_DID_UNSUGGESTED_ACTIVITY"
// CASE FriendLike_PASSED_ACTIVITY RETURN "FriendLike_PASSED_ACTIVITY"
DEFAULT
SCRIPT_ASSERT("invalid enumFriendLikeType in GetLabel_enumFriendLikeType()")
BREAK
ENDSWITCH
RETURN "<unknown FriendLikeType>"
ENDFUNC
#ENDIF
FUNC INT GET_FRIEND_LIKE(enumCharacterList playerCharID, enumCharacterList friendCharID)
enumFriend playerID = GET_FRIEND_FROM_CHAR(playerCharID)
enumFriend friendID = GET_FRIEND_FROM_CHAR(friendCharID)
enumFriendConnection friendConnID = GET_CONNECTION_FROM_FRIENDS(playerID, friendID)
RETURN g_SavedGlobals.sFriendsData.g_FriendConnectData[friendConnID].likes
ENDFUNC
PROC SET_FRIEND_LIKE(enumCharacterList playerCharID, enumCharacterList friendCharID, INT iNewLikes)
enumFriend playerID = GET_FRIEND_FROM_CHAR(playerCharID)
enumFriend friendID = GET_FRIEND_FROM_CHAR(friendCharID)
enumFriendConnection friendConnID = GET_CONNECTION_FROM_FRIENDS(playerID, friendID)
g_SavedGlobals.sFriendsData.g_FriendConnectData[friendConnID].likes = iNewLikes
IF g_SavedGlobals.sFriendsData.g_FriendConnectData[friendConnID].likes < CONST_iLikeStatMin
g_SavedGlobals.sFriendsData.g_FriendConnectData[friendConnID].likes = CONST_iLikeStatMin
ENDIF
IF g_SavedGlobals.sFriendsData.g_FriendConnectData[friendConnID].likes > CONST_iLikeStatMax
g_SavedGlobals.sFriendsData.g_FriendConnectData[friendConnID].likes = CONST_iLikeStatMax
ENDIF
ENDPROC
PROC ADD_TO_FRIEND_LIKE(enumCharacterList playerCharID, enumCharacterList friendCharID, INT iLikesToAdd)
SET_FRIEND_LIKE(playerCharID, friendCharID, GET_FRIEND_LIKE(playerCharID, friendCharID) + iLikesToAdd)
ENDPROC
PROC UPDATE_FRIEND_LIKE(enumCharacterList player, enumCharacterList friend, enumFriendLikeType event)
CPRINTLN(DEBUG_FRIENDS, "UPDATE_FRIEND_LIKE(", GetLabel_enumFriendLikeType(event), ")")
SWITCH event
CASE FriendLike_DECLINED_CALL ADD_TO_FRIEND_LIKE(player, friend, -1) BREAK
CASE FriendLike_PLAYER_CANCELLED ADD_TO_FRIEND_LIKE(player, friend, -1) BREAK
CASE FriendLike_PLAYER_LATE_ARRIVAL ADD_TO_FRIEND_LIKE(player, friend, -2) BREAK
CASE FriendLike_PLAYER_NO_ARRIVAL ADD_TO_FRIEND_LIKE(player, friend, -5) BREAK
CASE FriendLike_BUDDY_INJURED ADD_TO_FRIEND_LIKE(player, friend, -5) BREAK
CASE FriendLike_BUDDY_LOST ADD_TO_FRIEND_LIKE(player, friend, -5) BREAK
CASE FriendLike_DID_SUGGESTED_ACTIVITY ADD_TO_FRIEND_LIKE(player, friend, 5) BREAK
CASE FriendLike_DID_UNSUGGESTED_ACTIVITY ADD_TO_FRIEND_LIKE(player, friend, 3) BREAK
// CASE FriendLike_PASSED_ACTIVITY ADD_TO_FRIEND_LIKE(player, friend, 2) BREAK
DEFAULT
SCRIPT_ASSERT("invalid enumFriendLikeType in UPDATE_FRIEND_LIKE()")
BREAK
ENDSWITCH
ENDPROC
// *******************************************************************************************
// LAST CONTACT CONTACT TIMER
// *******************************************************************************************
/// PURPOSE:
/// Pause the time since last contact (usually because face/phone contact between the friends has just started)
/// PARAMS:
/// playerCharID - Friend A
/// friendCharID - Friend B
/// contactType - Is the current contact phone or face-to-face?
PROC PAUSE_FRIEND_LAST_CONTACT_TIMER(enumCharacterList playerCharID, enumCharacterList friendCharID)
enumFriend playerID = GET_FRIEND_FROM_CHAR(playerCharID)
enumFriend friendID = GET_FRIEND_FROM_CHAR(friendCharID)
enumFriendConnection friendConnID = GET_CONNECTION_FROM_FRIENDS(playerID, friendID)
#IF IS_DEBUG_BUILD
TEXT_LABEL_63 tConn = GetLabel_enumFriendConnection(friendConnID)
CPRINTLN(DEBUG_FRIENDS, "PAUSE_FRIEND_LAST_CONTACT_TIMER(", tConn, ")")
#ENDIF
IF friendConnID <> NO_FRIEND_CONNECTION
PAUSE_TIMER(g_SavedGlobals.sFriendsData.g_FriendConnectData[friendConnID].lastContactTimer)
ENDIF
ENDPROC
/// PURPOSE:
/// Reset the time since last contact, and set the recent contact type (usually because face/phone contact between the friends has just ended)
/// PARAMS:
/// playerCharID - Friend A
/// friendCharID - Friend B
/// contactType - Is the current ongoing contact phone or face-to-face?
PROC RESET_FRIEND_LAST_CONTACT_TIMER(enumCharacterList playerCharID, enumCharacterList friendCharID, enumFriendContactType contactType)
enumFriend playerID = GET_FRIEND_FROM_CHAR(playerCharID)
enumFriend friendID = GET_FRIEND_FROM_CHAR(friendCharID)
enumFriendConnection friendConnID = GET_CONNECTION_FROM_FRIENDS(playerID, friendID)
#IF IS_DEBUG_BUILD
TEXT_LABEL_63 tConn = GetLabel_enumFriendConnection(friendConnID)
IF contactType = FRIEND_CONTACT_PHONE
CPRINTLN(DEBUG_FRIENDS, "RESET_FRIEND_LAST_CONTACT_TIMER(", tConn, ", FRIEND_CONTACT_PHONE)")
ELIF contactType = FRIEND_CONTACT_FACE
CPRINTLN(DEBUG_FRIENDS, "RESET_FRIEND_LAST_CONTACT_TIMER(", tConn, ", FRIEND_CONTACT_FACE)")
ELIF contactType = FRIEND_CONTACT_DISMISSED
CPRINTLN(DEBUG_FRIENDS, "RESET_FRIEND_LAST_CONTACT_TIMER(", tConn, ", FRIEND_CONTACT_DISMISSED)")
ELSE
CPRINTLN(DEBUG_FRIENDS, "RESET_FRIEND_LAST_CONTACT_TIMER(", tConn, ", FRIEND_CONTACT_unknown)")
ENDIF
#ENDIF
IF friendConnID <> NO_FRIEND_CONNECTION
RESTART_TIMER_NOW(g_savedGlobals.sFriendsData.g_FriendConnectData[friendConnID].lastContactTimer)
DEBUG_PRINTCALLSTACK()
g_SavedGlobals.sFriendsData.g_FriendConnectData[friendConnID].lastContactType = contactType
ENDIF
ENDPROC
/// PURPOSE:
/// Reset the time since last contact, and set the recent contact type (usually because face/phone contact between the friends has just ended)
/// PARAMS:
/// playerCharID - Friend A
/// friendCharID - Friend B
/// contactType - Is the current ongoing contact phone or face-to-face?
FUNC BOOL IS_FRIEND_LAST_CONTACT_TIMER_PAUSED(enumCharacterList playerCharID, enumCharacterList friendCharID)
enumFriend playerID = GET_FRIEND_FROM_CHAR(playerCharID)
enumFriend friendID = GET_FRIEND_FROM_CHAR(friendCharID)
enumFriendConnection friendConnID = GET_CONNECTION_FROM_FRIENDS(playerID, friendID)
IF friendConnID <> NO_FRIEND_CONNECTION
RETURN IS_TIMER_PAUSED(g_savedGlobals.sFriendsData.g_FriendConnectData[friendConnID].lastContactTimer)
ENDIF
RETURN FALSE
ENDFUNC
// *******************************************************************************************
// SOLDIER FAIL TIMER
// *******************************************************************************************
FUNC CC_CommID GET_COMM_ID_FOR_FRIEND_FAIL(enumFriend eFriend)
IF eFriend = FR_MICHAEL RETURN TEXT_FRIEND_GRIEF_MICHAEL
ELIF eFriend = FR_FRANKLIN RETURN TEXT_FRIEND_GRIEF_FRANKLIN
ELIF eFriend = FR_TREVOR RETURN TEXT_FRIEND_GRIEF_TREVOR
ELIF eFriend = FR_LAMAR RETURN TEXT_FRIEND_GRIEF_LAMAR
ELIF eFriend = FR_JIMMY RETURN TEXT_FRIEND_GRIEF_JIMMY
ELIF eFriend = FR_AMANDA RETURN TEXT_FRIEND_GRIEF_AMANDA
ENDIF
RETURN COMM_NONE
ENDFUNC
/// PURPOSE:
/// Reset time since battle buddy failed, will not be able to call them again for 5 mins (timer pauses while on prep missions, and resets on pass/fail)
/// PARAMS:
/// eFriendChar - The playable char to reset the timer for
PROC START_FRIEND_FAIL_TIMER(enumFriend eFriend, CC_CommID eMessage = COMM_NONE)
CPRINTLN(DEBUG_FRIENDS, "START_FRIEND_FAIL_TIMER(", GetLabel_enumFriend(eFriend), ")")
IF eFriend < MAX_FRIENDS
// Restart timer
RESTART_TIMER_NOW(g_savedGlobals.sFriendsData.g_FriendFailTimers[eFriend])
// If a message has been sent, store ID and pause timer
g_savedGlobals.sFriendsData.g_FriendFailMessages[eFriend] = eMessage
IF eMessage <> COMM_NONE
PAUSE_TIMER(g_savedGlobals.sFriendsData.g_FriendFailTimers[eFriend])
ENDIF
ELSE
SCRIPT_ASSERT("START_FRIEND_FAIL_TIMER() - Invalid eFriendChar ID")
ENDIF
ENDPROC
/// PURPOSE:
/// Reset time since battle buddy failed, will not be able to call them again for 5 mins (timer pauses while on prep missions, and resets on pass/fail)
/// PARAMS:
/// eFriendChar - The playable char to reset the timer for
PROC UNPAUSE_FRIEND_FAIL_TIMER(enumFriend eFriend)
CPRINTLN(DEBUG_FRIENDS, "UNPAUSE_FRIEND_FAIL_TIMER(", GetLabel_enumFriend(eFriend), ")")
IF eFriend < MAX_FRIENDS
// Clear text message ID and unpause timer
g_savedGlobals.sFriendsData.g_FriendFailMessages[eFriend] = COMM_NONE
IF IS_TIMER_STARTED(g_savedGlobals.sFriendsData.g_FriendFailTimers[eFriend])
AND IS_TIMER_PAUSED(g_savedGlobals.sFriendsData.g_FriendFailTimers[eFriend])
UNPAUSE_TIMER(g_savedGlobals.sFriendsData.g_FriendFailTimers[eFriend])
ENDIF
ELSE
SCRIPT_ASSERT("UNPAUSE_FRIEND_FAIL_TIMER() - Invalid eFriendChar ID")
ENDIF
ENDPROC
/// PURPOSE:
/// Has friend failed in the last 3 minutes (blocks friend calls) - pauses timer if waiting on text to be sent
FUNC BOOL HAS_FRIEND_FAILED(enumFriend eFriend)
IF eFriend < MAX_FRIENDS
IF IS_TIMER_STARTED(g_SavedGlobals.sFriendsData.g_FriendFailTimers[eFriend])
IF GET_TIMER_IN_SECONDS(g_SavedGlobals.sFriendsData.g_FriendFailTimers[eFriend]) < 3.0*60.0
RETURN TRUE
ELSE
CANCEL_TIMER(g_SavedGlobals.sFriendsData.g_FriendFailTimers[eFriend])
RETURN FALSE
ENDIF
ENDIF
ELSE
CPRINTLN(DEBUG_FRIENDS, "HAS_FRIEND_FAILED(", GetLabel_enumFriend(eFriend), ") - Invalid eFriend ID")
SCRIPT_ASSERT("HAS_FRIEND_FAILED() - Invalid eFriend ID")
ENDIF
RETURN FALSE
ENDFUNC
///// PURPOSE:
///// Pauses all running friend fail timers (gets called when prep misison starts)
//PROC PAUSE_SOLDIER_FAIL_TIMERS()
// enumCharacterList eLoopChar
// REPEAT NUM_OF_PLAYABLE_PEDS eLoopChar
// IF IS_TIMER_STARTED(g_SavedGlobals.sFriendsData.g_SoldierFailTimers[eLoopChar])
// AND NOT IS_TIMER_PAUSED(g_SavedGlobals.sFriendsData.g_SoldierFailTimers[eLoopChar])
// CPRINTLN(DEBUG_FRIENDS, "PAUSE_SOLDIER_FAIL_TIMERS(", GetLabel_enumCharacterList(eLoopChar), ") - paused")
// PAUSE_TIMER(g_SavedGlobals.sFriendsData.g_SoldierFailTimers[eLoopChar])
// ENDIF
// ENDREPEAT
//ENDPROC
//
///// PURPOSE:
///// Stops all paused friend fail timers (gets called when any misison ends)
//PROC CANCEL_PAUSED_SOLDIER_FAIL_TIMERS()
// enumCharacterList eLoopChar
// REPEAT NUM_OF_PLAYABLE_PEDS eLoopChar
// IF IS_TIMER_STARTED(g_SavedGlobals.sFriendsData.g_SoldierFailTimers[eLoopChar])
// AND IS_TIMER_PAUSED(g_SavedGlobals.sFriendsData.g_SoldierFailTimers[eLoopChar])
// CPRINTLN(DEBUG_FRIENDS, "CANCEL_PAUSED_SOLDIER_FAIL_TIMERS(", GetLabel_enumCharacterList(eLoopChar), ") - cancelled")
// CANCEL_TIMER(g_SavedGlobals.sFriendsData.g_SoldierFailTimers[eLoopChar])
// ENDIF
// ENDREPEAT
//ENDPROC
// *******************************************************************************************
// FRIEND BLOCKING ACCESSORS
// *******************************************************************************************
FUNC BOOL SET_FRIEND_BLOCK_FLAG(enumCharacterList playerCharID, enumCharacterList friendCharID, enumFriendBlockFlag blockFlag, SP_MISSIONS missionID = SP_MISSION_NONE)
enumFriend playerID = GET_FRIEND_FROM_CHAR(playerCharID)
enumFriend friendID = GET_FRIEND_FROM_CHAR(friendCharID)
enumFriendConnection friendConnID = GET_CONNECTION_FROM_FRIENDS(playerID, friendID)
BOOL bHasUpdated = FALSE
IF blockFlag <> FRIEND_BLOCK_FLAG_MISSION AND missionID <> SP_MISSION_NONE
SCRIPT_ASSERT("SET_FRIEND_BLOCK_FLAG() - Mission ID was passed in params, but block type is not FRIEND_BLOCK_FLAG_MISSION")
RETURN FALSE
ENDIF
IF blockFlag = FRIEND_BLOCK_FLAG_MISSION AND missionID = SP_MISSION_MAX
SCRIPT_ASSERT("SET_FRIEND_BLOCK_FLAG() - Type is FRIEND_BLOCK_FLAG_MISSION, but mission ID is SP_MISSION_MAX")
RETURN FALSE
ENDIF
IF blockFlag = FRIEND_BLOCK_FLAG_MISSION AND missionID = SP_MISSION_NONE
SCRIPT_ASSERT("SET_FRIEND_BLOCK_FLAG() - Type is FRIEND_BLOCK_FLAG_MISSION, but mission ID is SP_MISSION_NONE")
RETURN FALSE
ENDIF
IF friendConnID <> NO_FRIEND_CONNECTION
// Debug print
#IF IS_DEBUG_BUILD
INT iOldBits = g_SavedGlobals.sFriendsData.g_FriendConnectData[friendConnID].blockBits
#ENDIF
// Set the flag (+ mission ref)
IF NOT IS_BIT_SET(g_SavedGlobals.sFriendsData.g_FriendConnectData[friendConnID].blockBits, ENUM_TO_INT(blockFlag))
SET_BIT(g_SavedGlobals.sFriendsData.g_FriendConnectData[friendConnID].blockBits, ENUM_TO_INT(blockFlag))
bHasUpdated = TRUE
ENDIF
IF blockFlag = FRIEND_BLOCK_FLAG_MISSION
g_SavedGlobals.sFriendsData.g_FriendConnectData[friendConnID].blockMissionID = missionID
ENDIF
// Debug print the changes
#IF IS_DEBUG_BUILD
TEXT_LABEL_63 strFlag = GetLabel_enumFriendBlockFlag(blockFlag)
TEXT_LABEL_63 strConn = GetLabel_enumFriendConnection(friendConnID)
TEXT_LABEL_63 strOldBits = GetLabel_FriendBlockBits(iOldBits)
TEXT_LABEL_63 strNewBits = GetLabel_FriendBlockBits(g_SavedGlobals.sFriendsData.g_FriendConnectData[friendConnID].blockBits)
IF blockFlag = FRIEND_BLOCK_FLAG_MISSION
TEXT_LABEL_63 strMission = GET_SP_MISSION_DISPLAY_STRING_FROM_ID(missionID)
CPRINTLN(DEBUG_FRIENDS, "SET_FRIEND_BLOCK_FLAG(", strFlag, ", ", strMission, ") - ", strConn, " ", strOldBits, " -> ", strNewBits)
ELSE
CPRINTLN(DEBUG_FRIENDS, "SET_FRIEND_BLOCK_FLAG(", strFlag, ") - ", strConn, " ", strOldBits, " -> ", strNewBits)
ENDIF
#ENDIF
//PAUSE_friend_lastCellphone(playerCharID, friendCharID) //don't block just because he's busy
//PAUSE_friend_lastFaceToFace(playerCharID, friendCharID)
ENDIF
RETURN bHasUpdated
ENDFUNC
FUNC BOOL CLEAR_FRIEND_BLOCK_FLAG(enumCharacterList playerCharID, enumCharacterList friendCharID, enumFriendBlockFlag blockFlag)
enumFriend playerID = GET_FRIEND_FROM_CHAR(playerCharID)
enumFriend friendID = GET_FRIEND_FROM_CHAR(friendCharID)
enumFriendConnection friendConnID = GET_CONNECTION_FROM_FRIENDS(playerID, friendID)
BOOL bHasUpdated = FALSE
IF friendConnID <> NO_FRIEND_CONNECTION
// Debug print
#IF IS_DEBUG_BUILD
INT iOldBits = g_SavedGlobals.sFriendsData.g_FriendConnectData[friendConnID].blockBits
#ENDIF
// Clear the flag (+ mission ref)
IF IS_BIT_SET(g_SavedGlobals.sFriendsData.g_FriendConnectData[friendConnID].blockBits, ENUM_TO_INT(blockFlag))
CLEAR_BIT(g_SavedGlobals.sFriendsData.g_FriendConnectData[friendConnID].blockBits, ENUM_TO_INT(blockFlag))
bHasUpdated = TRUE
ENDIF
IF NOT IS_BIT_SET(g_SavedGlobals.sFriendsData.g_FriendConnectData[friendConnID].blockBits, ENUM_TO_INT(FRIEND_BLOCK_FLAG_MISSION))
g_SavedGlobals.sFriendsData.g_FriendConnectData[friendConnID].blockMissionID = SP_MISSION_NONE
ENDIF
// Debug print
#IF IS_DEBUG_BUILD
TEXT_LABEL_63 strFlag = GetLabel_enumFriendBlockFlag(blockFlag)
TEXT_LABEL_63 strConn = GetLabel_enumFriendConnection(friendConnID)
TEXT_LABEL_63 strOldBits = GetLabel_FriendBlockBits(iOldBits)
TEXT_LABEL_63 strNewBits = GetLabel_FriendBlockBits(g_SavedGlobals.sFriendsData.g_FriendConnectData[friendConnID].blockBits)
IF blockFlag = FRIEND_BLOCK_FLAG_MISSION AND g_SavedGlobals.sFriendsData.g_FriendConnectData[friendConnID].blockMissionID <> SP_MISSION_NONE
TEXT_LABEL_63 strMission = GET_SP_MISSION_DISPLAY_STRING_FROM_ID(g_SavedGlobals.sFriendsData.g_FriendConnectData[friendConnID].blockMissionID)
CPRINTLN(DEBUG_FRIENDS, "CLEAR_FRIEND_BLOCK_FLAG(", strFlag, ", ", strMission, ") - ", strConn, " ", strOldBits, " -> ", strNewBits)
ELSE
CPRINTLN(DEBUG_FRIENDS, "CLEAR_FRIEND_BLOCK_FLAG(", strFlag, ") - ", strConn, " ", strOldBits, " -> ", strNewBits)
ENDIF
#ENDIF
ENDIF
RETURN bHasUpdated
ENDFUNC
FUNC BOOL IS_FRIEND_BLOCK_FLAG_SET(enumCharacterList playerCharID, enumCharacterList friendCharID, enumFriendBlockFlag blockFlag)
enumFriend playerID = GET_FRIEND_FROM_CHAR(playerCharID)
enumFriend friendID = GET_FRIEND_FROM_CHAR(friendCharID)
enumFriendConnection friendConnID = GET_CONNECTION_FROM_FRIENDS(playerID, friendID)
IF friendConnID = NO_FRIEND_CONNECTION
#IF IS_DEBUG_BUILD
TEXT_LABEL tPlayer = GetLabel_enumCharacterList(playerCharID)
TEXT_LABEL tFriend = GetLabel_enumCharacterList(friendCharID)
CPRINTLN(DEBUG_FRIENDS, "IS_FRIEND_BLOCK_FLAG_SET(", tPlayer, ", ", tFriend, ") - Can't find friend connection.")
#ENDIF
SCRIPT_ASSERT("IS_FRIEND_BLOCK_FLAG_SET() - Invalid friend connection")
RETURN FALSE
ENDIF
RETURN IS_BIT_SET(g_SavedGlobals.sFriendsData.g_FriendConnectData[friendConnID].blockBits, ENUM_TO_INT(blockFlag))
ENDFUNC
// *******************************************************************************************
// FRIEND BLOCKING/FLOW MAINTENANCE
// *******************************************************************************************
FUNC BOOL ARE_BOTH_FRIENDS_INVOLVED_IN_MISSION(SP_MISSIONS eMissionID, enumFriendConnection eConn)
IF eMissionID = SP_MISSION_NONE OR eMissionID = SP_MISSION_MAX
SCRIPT_ASSERT("ARE_BOTH_FRIENDS_INVOLVED_IN_MISSION: SP_MISSION_NONE and SP_MISSION_MAX are invalid IDs to pass.")
RETURN FALSE
ENDIF
INT friendBits = g_sMissionStaticData[eMissionID].friendCharBitset
enumCharacterList friendCharA, friendCharB
GET_FRIEND_CHARS_FROM_CONNECTION(eConn, friendCharA, friendCharB)
IF DOES_CHARACTER_BITSET_CONTAIN_FRIEND_CHARACTER(friendBits, friendCharA)
AND DOES_CHARACTER_BITSET_CONTAIN_FRIEND_CHARACTER(friendBits, friendCharB)
#IF IS_DEBUG_BUILD
IF (DOES_CHARACTER_BITSET_CONTAIN_FRIEND_CHARACTER(friendBits, CHAR_FRANKLIN))
CPRINTLN(DEBUG_FRIENDS, "ARE_BOTH_FRIENDS_INVOLVED_IN_MISSION() friendBits: ", friendBits, " [FRANKLIN] //", GET_SP_MISSION_DISPLAY_STRING_FROM_ID(eMissionID))
ENDIF
IF (DOES_CHARACTER_BITSET_CONTAIN_FRIEND_CHARACTER(friendBits, CHAR_MICHAEL))
CPRINTLN(DEBUG_FRIENDS, "ARE_BOTH_FRIENDS_INVOLVED_IN_MISSION() friendBits: ", friendBits, " [MICHAEL] //", GET_SP_MISSION_DISPLAY_STRING_FROM_ID(eMissionID))
ENDIF
IF (DOES_CHARACTER_BITSET_CONTAIN_FRIEND_CHARACTER(friendBits, CHAR_TREVOR))
CPRINTLN(DEBUG_FRIENDS, "ARE_BOTH_FRIENDS_INVOLVED_IN_MISSION() friendBits: ", friendBits, " [TREVOR] //", GET_SP_MISSION_DISPLAY_STRING_FROM_ID(eMissionID))
ENDIF
IF (DOES_CHARACTER_BITSET_CONTAIN_FRIEND_CHARACTER(friendBits, CHAR_LAMAR))
CPRINTLN(DEBUG_FRIENDS, "ARE_BOTH_FRIENDS_INVOLVED_IN_MISSION() friendBits: ", friendBits, " [LAMAR] //", GET_SP_MISSION_DISPLAY_STRING_FROM_ID(eMissionID))
ENDIF
IF (DOES_CHARACTER_BITSET_CONTAIN_FRIEND_CHARACTER(friendBits, CHAR_JIMMY))
CPRINTLN(DEBUG_FRIENDS, "ARE_BOTH_FRIENDS_INVOLVED_IN_MISSION() friendBits: ", friendBits, " [JIMMY] //", GET_SP_MISSION_DISPLAY_STRING_FROM_ID(eMissionID))
ENDIF
IF (DOES_CHARACTER_BITSET_CONTAIN_FRIEND_CHARACTER(friendBits, CHAR_AMANDA))
CPRINTLN(DEBUG_FRIENDS, "ARE_BOTH_FRIENDS_INVOLVED_IN_MISSION() friendBits: ", friendBits, " [AMANDA] //", GET_SP_MISSION_DISPLAY_STRING_FROM_ID(eMissionID))
ENDIF
#ENDIF
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
PROC SET_ALL_FRIEND_BLOCKS_FOR_MISSION(SP_MISSIONS eMissionID)
enumFriendConnection eConn
enumCharacterList friendCharA
enumCharacterList friendCharB
#IF IS_DEBUG_BUILD
BOOL bWasFriendRegisteredForMission = FALSE
TEXT_LABEL_63 tMission = GET_SP_MISSION_DISPLAY_STRING_FROM_ID(eMissionID)
TEXT_LABEL_63 tInvolved = GetLabel_InvolvedFriends(eMissionID)
TEXT_LABEL_63 tIsPrep = ""
IF IS_BIT_SET(g_sMissionStaticData[eMissionID].settingsBitset, MF_INDEX_IS_PREP)
tIsPrep = " (is prep)"
ENDIF
CPRINTLN(DEBUG_FRIENDS, "SET_ALL_FRIEND_BLOCKS_FOR_MISSION(", tMission, ") - Involved friend bits = ", tInvolved, tIsPrep)
#ENDIF
// IF eMissionID = SP_MISSION_FINALE_C1
// CPRINTLN(DEBUG_FRIENDS, "SET_ALL_FRIEND_BLOCKS_FOR_MISSION(", tMission, ") callstack >>>>>>>>>>")
// DEBUG_PRINTCALLSTACK()
// ENDIF
// Don't set mission blocks for prep
// IF NOT IS_BIT_SET(g_sMissionStaticData[eMissionID].settingsBitset, MF_INDEX_IS_PREP) // BBUDDIES REMOVED
// For each connection...
REPEAT MAX_FRIEND_CONNECTIONS eConn
// If both friends are involved in mission, block connection
IF ARE_BOTH_FRIENDS_INVOLVED_IN_MISSION(eMissionID, eConn)
IF GET_FRIEND_CHARS_FROM_CONNECTION(eConn, friendCharA, friendCharB)
IF GET_CONNECTION_STATE(eConn) <> FC_STATE_Invalid
SET_FRIEND_BLOCK_FLAG(friendCharA, friendCharB, FRIEND_BLOCK_FLAG_MISSION, eMissionID)
ENDIF
ENDIF
#IF IS_DEBUG_BUILD
bWasFriendRegisteredForMission = TRUE
#ENDIF
ENDIF
ENDREPEAT
// ENDIF
#IF IS_DEBUG_BUILD
IF bWasFriendRegisteredForMission = FALSE
CPRINTLN(DEBUG_FRIENDS, "SET_ALL_FRIEND_BLOCKS_FOR_MISSION(", GET_SP_MISSION_DISPLAY_STRING_FROM_ID(eMissionID), ") - None")
ENDIF
#ENDIF
ENDPROC
// NOTE: CLEAR_ALL_FRIEND_BLOCKS_FOR_MISSION() is defined in friend_flow_public.sch
FUNC BOOL MISSION_CANNOT_BE_REGISTERED_BECAUSE_OF_ACTIVE_FRIEND(SP_MISSIONS paramMissionID)
INT iConnectionsBlocked = 0
// Don't block prep missions
// IF NOT IS_BIT_SET(g_sMissionStaticData[paramMissionID].settingsBitset, MF_INDEX_IS_PREP)
// For each connection...
enumFriendConnection eConn
REPEAT MAX_FRIEND_CONNECTIONS eConn
// Is this connection active or initiating
enumFriendConnectionState state = GET_CONNECTION_STATE(eConn)
enumFriendConnectionMode mode = GET_CONNECTION_MODE(eConn)
IF (state = FC_STATE_Init OR state = FC_STATE_Active)
AND (mode = FC_MODE_Friend OR mode = FC_MODE_Adhoc OR mode = FC_MODE_Ambient)
// Activity is ongoing for this friend
IF ARE_BOTH_FRIENDS_INVOLVED_IN_MISSION(paramMissionID, eConn)
CPRINTLN(DEBUG_FRIENDS, "Mission_Blocked_Because_Of_Friend(", GetLabel_enumFriendConnection(eConn), ")")
iConnectionsBlocked++
ELSE
CPRINTLN(DEBUG_FRIENDS, "No_Mission_Blocked_Because_Of_Friend(", GetLabel_enumFriendConnection(eConn), ")")
ENDIF
ELSE
// No activity currently ongoing for this friend
ENDIF
ENDREPEAT
// ENDIF
RETURN (iConnectionsBlocked <> 0)
ENDFUNC
// *******************************************************************************************
// ADD FRIEND
// *******************************************************************************************
PROC ADD_CHAR_AS_CHAR_FRIEND(enumCharacterList paramCharacterContact, enumCharacterList paramCharacterPhoneOwner, BOOL bSkipContactDelay = FALSE)
#IF IS_DEBUG_BUILD
TEXT_LABEL_63 tFriendChar = GetLabel_enumCharacterList(paramCharacterContact)
TEXT_LABEL_63 tPlayerChar = GetLabel_enumCharacterList(paramCharacterPhoneOwner)
#ENDIF
enumFriendConnection eConnection
IF GET_CONNECTION(paramCharacterPhoneOwner, paramCharacterContact, eConnection)
CPRINTLN(DEBUG_FRIENDS, "ADD_CHAR_AS_CHAR_FRIEND(", tFriendChar, ", ", tPlayerChar, ")")
// UPDATE_FRIEND_CONVERSATION_STAGE(paramCharacterPhoneOwner, paramCharacterContact, FGS_stageEarly)
RESET_FRIEND_LAST_CONTACT_TIMER(paramCharacterPhoneOwner, paramCharacterContact, FRIEND_CONTACT_FACE)
SET_FRIEND_LIKE(paramCharacterPhoneOwner, paramCharacterContact, CONST_iLikeStatInitial)
IF bSkipContactDelay
Private_ALTER_FRIEND_LAST_CONTACT_TIME(paramCharacterContact, paramCharacterPhoneOwner, 5.0 * 60.0)
ENDIF
SET_CONNECTION_STATE(eConnection, FC_STATE_ContactWait)
SET_BIT(g_SavedGlobals.sFriendsData.g_FriendConnectData[eConnection].flags, ENUM_TO_INT(FC_FLAG_HasInitiated))
g_iNumberOfActiveFriends++
ELSE
CPRINTLN(DEBUG_FRIENDS, "ADD_CHAR_AS_CHAR_FRIEND(", tFriendChar, ", ", tPlayerChar, ") - UNSUCCESSFUL")
ENDIF
ENDPROC
PROC Add_Char_As_DefaultPlayer_Friend(enumCharacterList friendCharID)
#IF IS_DEBUG_BUILD
SWITCH g_eDefaultPlayerChar
CASE CHAR_MICHAEL
ADD_CONTACT_TO_PHONEBOOK(friendCharID, MICHAEL_BOOK)
BREAK
CASE CHAR_TREVOR
ADD_CONTACT_TO_PHONEBOOK(friendCharID, TREVOR_BOOK)
BREAK
CASE CHAR_FRANKLIN
ADD_CONTACT_TO_PHONEBOOK(friendCharID, FRANKLIN_BOOK)
BREAK
DEFAULT
SCRIPT_ASSERT("invalid g_eDefaultPlayerChar in Add_Char_As_DefaultPlayer_Friend???")
EXIT
BREAK
ENDSWITCH
SWITCH friendCharID
CASE CHAR_MICHAEL
ADD_CONTACT_TO_PHONEBOOK(g_eDefaultPlayerChar, MICHAEL_BOOK)
BREAK
CASE CHAR_TREVOR
ADD_CONTACT_TO_PHONEBOOK(g_eDefaultPlayerChar, TREVOR_BOOK)
BREAK
CASE CHAR_FRANKLIN
ADD_CONTACT_TO_PHONEBOOK(g_eDefaultPlayerChar, FRANKLIN_BOOK)
BREAK
CASE CHAR_LAMAR
//
BREAK
CASE CHAR_JIMMY
//
BREAK
CASE CHAR_AMANDA
//
BREAK
DEFAULT
SCRIPT_ASSERT("invalid friendCharID in Add_Char_As_DefaultPlayer_Friend???")
EXIT
BREAK
ENDSWITCH
MAKE_CONTACT_ENTRY_PRIORITY(friendCharID)
#ENDIF
Add_Char_As_Char_Friend(g_eDefaultPlayerChar, friendCharID)
ENDPROC
// *******************************************************************************************
// RELATIONSHIP QUERY
// *******************************************************************************************
FUNC BOOL ARE_CHARS_FRIENDS(enumCharacterList eCharA, enumCharacterList eCharB)
enumCharacterList eMaxCharacter = GLOBAL_CHARACTER_SHEET_GET_MAX_CHARACTERS_FOR_GAMEMODE()
IF eCharA >= eMaxCharacter
OR eCharB >= eMaxCharacter
OR eCharA = eCharB
RETURN FALSE
ENDIF
enumFriend eFriendA = GET_FRIEND_FROM_CHAR(eCharA)
enumFriend eFriendB = GET_FRIEND_FROM_CHAR(eCharB)
// If both chars are valid friend chars, see if they have a friend connection
IF eFriendA <> NO_FRIEND
AND eFriendB <> NO_FRIEND
enumFriendConnection eConnection = GET_CONNECTION_FROM_FRIENDS(eFriendA, eFriendB)
// If they have a valid friend connection, reset the friend-last-contact timer
IF eConnection <> NO_FRIEND_CONNECTION
IF GET_CONNECTION_STATE(eConnection) <> FC_STATE_Invalid
RETURN TRUE
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL CAN_CHARS_ARRANGE_BATTLE_BUDDY(enumCharacterList ePlayerChar, enumCharacterList eFriendChar)
RETURN FALSE // BBUDDIES REMOVED
enumCharacterList eMaxCharacter = GLOBAL_CHARACTER_SHEET_GET_MAX_CHARACTERS_FOR_GAMEMODE()
IF ePlayerChar >= eMaxCharacter
OR eFriendChar >= eMaxCharacter
OR ePlayerChar = eFriendChar
RETURN FALSE
ENDIF
// If char is allowed on mission
IF g_eFriendMissionZoneID <> SP_MISSION_NONE
AND IS_BIT_SET(g_sMissionStaticData[g_eFriendMissionZoneID].settingsBitset, MF_INDEX_IS_PREP)
AND IS_BIT_SET(g_iFriendMissionZoneAcceptBitset, ENUM_TO_INT(eFriendChar))
// If mission is stored as launched, make sure it is (friend controller might have been switched off and not updated it)
IF g_eFriendMissionZoneState <> FRIEND_MISSION_ZONE_LAUNCHED
OR MISSION_FLOW_GET_RUNNING_MISSION() = g_eFriendMissionZoneID
// Don't allow if mission has dynamically disabled battle buddies
IF NOT IS_BATTLEBUDDY_BEHAVIOUR_REQUESTED(BBF_RejectAllBattleBuddies)
// Get desired connection, is it in ContactWait state?
enumFriendConnection eDesiredConnection
IF GET_CONNECTION(ePlayerChar, eFriendChar, eDesiredConnection)
AND GET_CONNECTION_STATE(eDesiredConnection) = FC_STATE_ContactWait
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL CAN_MISSION_ARRANGE_MULTIPLE_BATTLE_BUDDIES(SP_MISSIONS eMission)
#if USE_CLF_DLC
eMission = eMission
RETURN FALSE // BBUDDIES REMOVED
#ENDIF
#if USE_NRM_DLC
eMission = eMission
RETURN FALSE // BBUDDIES REMOVED
#ENDIF
#if not USE_SP_DLC
RETURN FALSE // BBUDDIES REMOVED
IF eMission = SP_HEIST_RURAL_PREP_1
OR eMission = SP_HEIST_AGENCY_PREP_1
OR eMission = SP_HEIST_FINALE_PREP_B
OR eMission = SP_MISSION_FBI_4_PREP_2
RETURN FALSE
ENDIF
RETURN TRUE
#ENDIF
ENDFUNC
FUNC BOOL HAVE_CHARS_ARRANGED_FRIEND_ACTIVITYCLF(enumCharacterList eCharA, enumCharacterList eCharB)
IF eCharA >= MAX_CLF_CHARACTERS
OR eCharB >= MAX_CLF_CHARACTERS
OR eCharA = eCharB
RETURN FALSE
ENDIF
IF NOT IS_CURRENTLY_ON_MISSION_TO_TYPE(MISSION_TYPE_FRIEND_ACTIVITY)
enumFriend eFriendA = GET_FRIEND_FROM_CHARCLF(eCharA)
enumFriend eFriendB = GET_FRIEND_FROM_CHARCLF(eCharB)
// If both chars are valid friend chars, see if they have a friend connection
IF eFriendA <> NO_FRIEND
AND eFriendB <> NO_FRIEND
enumFriendConnection eConnection = GET_CONNECTION_FROM_FRIENDS(eFriendA, eFriendB)
// If they have a valid friend connection, reset the friend-last-contact timer
IF eConnection <> NO_FRIEND_CONNECTION
enumFriendConnectionState state = GET_CONNECTION_STATE(eConnection)
IF state = FC_STATE_Init
OR state = FC_STATE_Active
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL HAVE_CHARS_ARRANGED_FRIEND_ACTIVITYNRM(enumCharacterList eCharA, enumCharacterList eCharB)
IF eCharA >= MAX_NRM_CHARACTERS
OR eCharB >= MAX_NRM_CHARACTERS
OR eCharA = eCharB
RETURN FALSE
ENDIF
IF NOT IS_CURRENTLY_ON_MISSION_TO_TYPE(MISSION_TYPE_FRIEND_ACTIVITY)
enumFriend eFriendA = GET_FRIEND_FROM_CHARNRM(eCharA)
enumFriend eFriendB = GET_FRIEND_FROM_CHARNRM(eCharB)
// If both chars are valid friend chars, see if they have a friend connection
IF eFriendA <> NO_FRIEND
AND eFriendB <> NO_FRIEND
enumFriendConnection eConnection = GET_CONNECTION_FROM_FRIENDS(eFriendA, eFriendB)
// If they have a valid friend connection, reset the friend-last-contact timer
IF eConnection <> NO_FRIEND_CONNECTION
enumFriendConnectionState state = GET_CONNECTION_STATE(eConnection)
IF state = FC_STATE_Init
OR state = FC_STATE_Active
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL HAVE_CHARS_ARRANGED_FRIEND_ACTIVITY(enumCharacterList eCharA, enumCharacterList eCharB)
#IF USE_CLF_DLC
IF g_bLoadedClifford
RETURN HAVE_CHARS_ARRANGED_FRIEND_ACTIVITYCLF(eCharA,eCharB)
ENDIF
#ENDIF
#IF USE_NRM_DLC
IF g_bLoadedNorman
RETURN HAVE_CHARS_ARRANGED_FRIEND_ACTIVITYNRM(eCharA,eCharB)
ENDIF
#ENDIF
enumCharacterList eMaxCharacter = GLOBAL_CHARACTER_SHEET_GET_MAX_CHARACTERS_FOR_GAMEMODE()
IF eCharA >= eMaxCharacter
OR eCharB >= eMaxCharacter
OR eCharA = eCharB
RETURN FALSE
ENDIF
IF NOT IS_CURRENTLY_ON_MISSION_TO_TYPE(MISSION_TYPE_FRIEND_ACTIVITY)
enumFriend eFriendA = GET_FRIEND_FROM_CHAR(eCharA)
enumFriend eFriendB = GET_FRIEND_FROM_CHAR(eCharB)
// If both chars are valid friend chars, see if they have a friend connection
IF eFriendA <> NO_FRIEND
AND eFriendB <> NO_FRIEND
enumFriendConnection eConnection = GET_CONNECTION_FROM_FRIENDS(eFriendA, eFriendB)
// If they have a valid friend connection, reset the friend-last-contact timer
IF eConnection <> NO_FRIEND_CONNECTION
enumFriendConnectionState state = GET_CONNECTION_STATE(eConnection)
IF state = FC_STATE_Init
OR state = FC_STATE_Active
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL CAN_CHARS_ARRANGE_FRIEND_ACTIVITY(enumCharacterList ePlayerChar, enumCharacterList eFriendChar)
enumCharacterList eMaxCharacter = GLOBAL_CHARACTER_SHEET_GET_MAX_CHARACTERS_FOR_GAMEMODE()
IF ePlayerChar >= eMaxCharacter
OR eFriendChar >= eMaxCharacter
OR ePlayerChar = eFriendChar
RETURN FALSE
ENDIF
#IF NOT USE_CLF_DLC
#IF NOT USE_NRM_DLC
//B* 1929691: Disable friend activity if before Fame or Shame but after Jewel Heist
IF (ePlayerChar = CHAR_FRANKLIN AND eFriendChar = CHAR_MICHAEL)
OR (ePlayerChar = CHAR_MICHAEL AND eFriendChar = CHAR_FRANKLIN)
IF GET_MISSION_COMPLETE_STATE(SP_HEIST_JEWELRY_2) AND (NOT GET_MISSION_COMPLETE_STATE(SP_MISSION_FAMILY_4))
CPRINTLN(debug_friends, "Franklin can't call Michael to arrange Friend Activity, Fame or shame not completed")
RETURN FALSE
ENDIF
ENDIF
#ENDIF
#ENDIF
// IF NOT CAN_CHARS_ARRANGE_BATTLE_BUDDY(ePlayerChar, eFriendChar) // BBUDDIES REMOVED
IF NOT IS_CURRENTLY_ON_MISSION_TO_TYPE(MISSION_TYPE_FRIEND_ACTIVITY)
// Get desired connection, is it in ContactWait state?
enumFriendConnection eDesiredConnection
IF GET_CONNECTION(ePlayerChar, eFriendChar, eDesiredConnection)
AND GET_CONNECTION_STATE(eDesiredConnection) = FC_STATE_ContactWait
RETURN TRUE
ENDIF
ENDIF
// ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL CAN_CHARS_CANCEL_FRIEND_ACTIVITY(enumCharacterList eCharA, enumCharacterList eCharB)
enumCharacterList eMaxCharacter = GLOBAL_CHARACTER_SHEET_GET_MAX_CHARACTERS_FOR_GAMEMODE()
IF eCharA >= eMaxCharacter
OR eCharB >= eMaxCharacter
OR eCharA = eCharB
RETURN FALSE
ENDIF
// Can't cancel if already picked up the friend
IF NOT IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_FRIEND_ACTIVITY)
AND NOT IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_FRIEND_ACTIVITY_WITH_MG)
IF HAVE_CHARS_ARRANGED_FRIEND_ACTIVITY(eCharA, eCharB)
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
// *******************************************************************************************