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

4689 lines
161 KiB
Scheme
Executable File

//- commands headers -//
USING "rage_builtins.sch"
USING "globals.sch"
USING "commands_script.sch"
USING "commands_replay.sch"
//- script headers -//
USING "rc_helper_functions.sch"
USING "rgeneral_include.sch"
//- public headers -//
USING "player_ped_public.sch"
USING "timer_public.sch"
USING "dialogue_public.sch"
USING "blip_control_public.sch"
USING "flow_public_core_override.sch"
USING "organiser_public.sch"
USING "selector_public.sch"
USING "context_control_public.sch"
USING "chase_hint_cam.sch"
USING "friends_public.sch"
USING "friendActivity_public.sch"
USING "rc_threat_public.sch"
//- private headers -//
USING "friendUtil_private.sch"
#IF IS_DEBUG_BUILD
//- debug headers -//
USING "script_debug.sch"
USING "shared_debug.sch"
#ENDIF
///private header for friend controller scripts
/// sam.hackett@rockstarnorth.com
///
#IF IS_DEBUG_BUILD
PROC DEBUG_PrintFriendCallInfo(STRING sDescription, TEXT_LABEL tGreetingA, TEXT_LABEL tGreetingB, TEXT_LABEL tAnswerA, TEXT_LABEL tAnswerB)
TEXT_LABEL_63 tGreeting = ""
tGreeting += tGreetingA
tGreeting += ", "
tGreeting += tGreetingB
tGreeting += ", "
TEXT_LABEL_63 tAnswer = ""
tAnswer += tAnswerA
tAnswer += ", "
tAnswer += tAnswerB
CPRINTLN(DEBUG_FRIENDS, "DEBUG_PrintFriendCallInfo(", sDescription, ": ", tGreeting, tAnswer, ")")
ENDPROC
#ENDIF
// *******************************************************************************************
// Checks for if friend call will be successful
// *******************************************************************************************
FUNC BOOL PRIVATE_Get_Sleep_Times_from_enumFriend(enumFriend friendID, INT &iAwakeHour, INT &iSleepHour)
SWITCH friendID
CASE FR_MICHAEL iAwakeHour = 0 iSleepHour = 0 RETURN TRUE BREAK
CASE FR_FRANKLIN iAwakeHour = 0 iSleepHour = 0 RETURN TRUE BREAK
CASE FR_TREVOR iAwakeHour = 0 iSleepHour = 0 RETURN TRUE BREAK
CASE FR_LAMAR iAwakeHour = 0 iSleepHour = 0 RETURN TRUE BREAK
CASE FR_JIMMY iAwakeHour = 14 iSleepHour = 5 RETURN TRUE BREAK
CASE FR_AMANDA iAwakeHour = 9 iSleepHour = 0 RETURN TRUE BREAK
ENDSWITCH
SCRIPT_ASSERT("invalid friendID in PRIVATE_Get_Sleep_Times_from_enumFriend()")
iAwakeHour = -1
iSleepHour = 24
RETURN FALSE
ENDFUNC
FUNC BOOL PRIVATE_FriendAcceptsPlayer_exists(enumFriend eFriend)
enumCharacterList eFriendChar = GET_CHAR_FROM_FRIEND(eFriend)
IF NOT IS_PED_THE_CURRENT_PLAYER_PED(eFriendChar)
// Playable char
IF IS_PLAYER_PED_PLAYABLE(eFriendChar)
SELECTOR_SLOTS_ENUM eSelectorSlot = GET_SELECTOR_SLOT_FROM_PLAYER_PED_ENUM(eFriendChar)
IF eSelectorSlot < SELECTOR_PED_MULTIPLAYER
PED_INDEX playerLastKnownPedID = g_sPlayerPedRequest.sSelectorPeds.pedID[eSelectorSlot]
IF DOES_ENTITY_EXIST(playerLastKnownPedID)
RETURN FALSE
ENDIF
ENDIF
// NPC char
ELSE
INT iIndex = ENUM_TO_INT(eFriend) - NUM_OF_PLAYABLE_PEDS
IF iIndex < NUM_OF_NPC_FRIENDS
IF DOES_ENTITY_EXIST(g_pGlobalFriends[iIndex])
RETURN FALSE
ENDIF
ENDIF
ENDIF
ENDIF
RETURN TRUE
ENDFUNC
FUNC BOOL PRIVATE_FriendAcceptsPlayer_fail(enumFriend eFriendID) // BBUDDIES REMOVED
IF HAS_FRIEND_FAILED(eFriendID)
RETURN FALSE
ENDIF
RETURN TRUE
ENDFUNC
FUNC BOOL PRIVATE_FriendAcceptsPlayer_hiatusBlock(enumFriend playerID, enumFriend friendID)
RETURN NOT IS_FRIEND_BLOCK_FLAG_SET(GET_CHAR_FROM_FRIEND(playerID), GET_CHAR_FROM_FRIEND(friendID), FRIEND_BLOCK_FLAG_HIATUS)
ENDFUNC
FUNC BOOL PRIVATE_FriendAcceptsPlayer_clashBlock(enumFriend playerID, enumFriend friendID)
RETURN NOT IS_FRIEND_BLOCK_FLAG_SET(GET_CHAR_FROM_FRIEND(playerID), GET_CHAR_FROM_FRIEND(friendID), FRIEND_BLOCK_FLAG_CLASH)
ENDFUNC
FUNC BOOL PRIVATE_FriendAcceptsPlayer_missionBlock(enumFriend playerID, enumFriend friendID)
RETURN NOT IS_FRIEND_BLOCK_FLAG_SET(GET_CHAR_FROM_FRIEND(playerID), GET_CHAR_FROM_FRIEND(friendID), FRIEND_BLOCK_FLAG_MISSION)
ENDFUNC
FUNC BOOL PRIVATE_FriendAcceptsPlayer_missionBlock_AnswerPhone(enumFriend playerID, enumFriend friendID)
#if USE_CLF_DLC
playerID = playerID
friendID = friendID
#ENDIF
#if USE_NRM_DLC
playerID = playerID
friendID = friendID
#ENDIF
#if not USE_CLF_DLC
#if not USE_NRM_DLC
IF (playerID = FR_MICHAEL AND friendID = FR_TREVOR)
OR (friendID = FR_MICHAEL AND playerID = FR_TREVOR)
IF IS_FRIEND_BLOCK_FLAG_SET(GET_CHAR_FROM_FRIEND(playerID), GET_CHAR_FROM_FRIEND(friendID), FRIEND_BLOCK_FLAG_MISSION)
IF g_SavedGlobals.sFriendsData.g_FriendConnectData[FC_TREVOR_MICHAEL].blockMissionID = SP_MISSION_MICHAEL_1
CPRINTLN(DEBUG_FRIENDS, "PRIVATE_FriendAcceptsPlayer_missionBlock_AnswerPhone: SP_MISSION_MICHAEL_1")
RETURN FALSE
ENDIF
ENDIF
ENDIF
#ENDIF
#ENDIF
RETURN TRUE
ENDFUNC
FUNC BOOL PRIVATE_FriendAcceptsPlayer_wanted(enumFriend playerID, enumFriend friendID)
enumFriendConnection eConnID = GET_CONNECTION_FROM_FRIENDS(playerID, friendID)
enumFriend wantedFriend = g_SavedGlobals.sFriendsData.g_FriendConnectData[eConnID].wanted
IF playerID = wantedFriend
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL PRIVATE_FriendAcceptsPlayer_group(enumFriend ePlayerID, enumFriend eFriendID)
enumCharacterList ePlayerChar = GET_CHAR_FROM_FRIEND(ePlayerID)
enumCharacterList eFriendChar = GET_CHAR_FROM_FRIEND(eFriendID)
enumFriendConnection eDesiredConnection
enumFriendConnection eActiveConnection
enumFriendConnection eCommonConnection
INT iActiveOrPendingFriends = GET_PENDING_OR_ACTIVE_CONNECTIONS(eActiveConnection)
// Make sure the requested connection is ready
IF GET_CONNECTION(ePlayerChar, eFriendChar, eDesiredConnection)
AND GET_CONNECTION_STATE(eDesiredConnection) = FC_STATE_ContactWait
// If no connections running...
IF iActiveOrPendingFriends = 0
RETURN TRUE
// If one connection are already running...
ELIF iActiveOrPendingFriends = 1
// Make sure there's a common connection, and it's ready
IF GET_COMMON_CONNECTION(eActiveConnection, eDesiredConnection, eCommonConnection)
AND GET_CONNECTION_STATE(eCommonConnection) = FC_STATE_ContactWait
// Don't allow multiple hangouts involving Jimmmy
IF DOES_CONNECTION_INVOLVE_CHAR(eDesiredConnection, CHAR_JIMMY)
OR DOES_CONNECTION_INVOLVE_CHAR(eActiveConnection, CHAR_JIMMY)
RETURN FALSE
ELSE
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL PRIVATE_FriendAcceptsPlayer_common(enumFriend ePlayerID, enumFriend eFriendID)
enumCharacterList ePlayerChar = GET_CHAR_FROM_FRIEND(ePlayerID)
enumCharacterList eFriendChar = GET_CHAR_FROM_FRIEND(eFriendID)
enumFriendConnection eDesiredConnection
enumFriendConnection eActiveConnection
enumFriendConnection eCommonConnection
INT iActiveOrPendingFriends = GET_PENDING_OR_ACTIVE_CONNECTIONS(eActiveConnection)
// Make sure the requested connection is ready
IF GET_CONNECTION(ePlayerChar, eFriendChar, eDesiredConnection)
// If no connections running...
IF iActiveOrPendingFriends = 0
RETURN TRUE
// If one connection are already running...
ELIF iActiveOrPendingFriends = 1
// Make sure there's a common connection, and it's not blocked
IF GET_COMMON_CONNECTION(eActiveConnection, eDesiredConnection, eCommonConnection)
IF g_SavedGlobals.sFriendsData.g_FriendConnectData[eCommonConnection].blockBits = 0
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL PRIVATE_FriendAcceptsPlayer_time(enumFriend playerID, enumFriend friendID, FLOAT &fLastContactTime, enumFriendContactType &eLastContactType)
enumCharacterList playerCharID = GET_CHAR_FROM_FRIEND(playerID)
enumCharacterList friendCharID = GET_CHAR_FROM_FRIEND(friendID)
fLastContactTime = Private_GET_FRIEND_LAST_CONTACT_TIME(playerCharID, friendCharID)
eLastContactType = Private_GET_FRIEND_LAST_CONTACT_TYPE(playerCharID, friendCharID)
CONST_FLOAT CONST_fPlayerRealtimeMinuteWait 5.0
IF fLastContactTime <= (CONST_fPlayerRealtimeMinuteWait*60.0)
RETURN FALSE
ENDIF
RETURN TRUE
ENDFUNC
FUNC BOOL PRIVATE_FriendAcceptsPlayer_awake(enumFriend friendID, INT &iAwakeHour, INT &iSleepHour, INT &iCurrentHour)
PRIVATE_Get_Sleep_Times_from_enumFriend(friendID, iAwakeHour, iSleepHour)
iCurrentHour = GET_CLOCK_HOURS()
IF iAwakeHour < iSleepHour
IF iCurrentHour < iAwakeHour
OR iCurrentHour >= iSleepHour
RETURN FALSE
ELSE
RETURN TRUE
ENDIF
ELSE
IF iCurrentHour < iAwakeHour
AND iCurrentHour >= iSleepHour
RETURN FALSE
ELSE
RETURN TRUE
ENDIF
ENDIF
ENDFUNC
FUNC BOOL PRIVATE_FriendAcceptsPlayer_laylow(enumFriend friendID)
// After the finale, if Michael/Trevor killed, Lamar lays low and will only go out at night
IF friendID = FR_LAMAR
#if not USE_CLF_DLC
#if not USE_NRM_DLC
IF (GET_MISSION_FLOW_FLAG_STATE(FLOWFLAG_MICHAEL_KILLED)
OR GET_MISSION_FLOW_FLAG_STATE(FLOWFLAG_TREVOR_KILLED))
INT iCurrentHour = GET_CLOCK_HOURS()
IF iCurrentHour < 21
AND iCurrentHour >= 5
RETURN FALSE
ELSE
RETURN TRUE
ENDIF
ENDIF
#endif
#endif
ENDIF
RETURN TRUE
ENDFUNC
FUNC BOOL PRIVATE_FriendAcceptsPlayer(enumFriend playerID, enumFriend friendID)
// Can't accept call if laying low
IF NOT PRIVATE_FriendAcceptsPlayer_laylow(friendID)
CPRINTLN(DEBUG_FRIENDS, "friend ", GetLabel_enumFriend(friendID), "can't accept call: laying low")
RETURN FALSE
ENDIF
// Can't accept as blocked by mission
IF NOT PRIVATE_FriendAcceptsPlayer_missionBlock(playerID, friendID)
#IF IS_DEBUG_BUILD
enumFriendConnection eConn = GET_CONNECTION_FROM_FRIENDS(playerID, friendID)
TEXT_LABEL tFriend = GetLabel_enumFriend(friendID)
TEXT_LABEL tMission = GET_SP_MISSION_DISPLAY_STRING_FROM_ID(g_SavedGlobals.sFriendsData.g_FriendConnectData[eConn].blockMissionID)
CPRINTLN(DEBUG_FRIENDS, "friend ", tFriend, " can't accept hangout: will be appearing with player in mission '", tMission, "'")
#ENDIF
RETURN FALSE
ENDIF
// Can't accept as blocked by clash
IF NOT PRIVATE_FriendAcceptsPlayer_clashBlock(playerID, friendID)
#IF IS_DEBUG_BUILD
CPRINTLN(DEBUG_FRIENDS, "friend ", GetLabel_enumFriend(friendID), " can't accept call: blocked (clash type)")
#ENDIF
RETURN FALSE
ENDIF
// Can't accept as called recently (and isn't specifically wanted)
enumFriendContactType eLastContactType
FLOAT fLastContactTime
IF NOT PRIVATE_FriendAcceptsPlayer_wanted(playerID, friendID)
IF NOT PRIVATE_FriendAcceptsPlayer_time(playerID, friendID, fLastContactTime, eLastContactType)
CPRINTLN(DEBUG_FRIENDS, "friend ", GetLabel_enumFriend(friendID), "can't accept hangout: contacted too recently: ", GET_STRING_FROM_MILISECONDS(ROUND((fLastContactTime) * 1000.0)),
" <= (five minutes) //FCR_time_", ENUM_TO_INT(eLastContactType))
RETURN FALSE
ENDIF
ENDIF
// Can't accept as other activities are running, and the common connection is blocked
IF NOT PRIVATE_FriendAcceptsPlayer_common(playerID, friendID)
CPRINTLN(DEBUG_FRIENDS, "friend ", GetLabel_enumFriend(friendID), " can't accept multiple hangout: common connection blocked")
RETURN FALSE
ENDIF
RETURN TRUE
ENDFUNC
FUNC BOOL PRIVATE_FriendAcceptsCall(enumFriend playerID, enumFriend friendID)
INT iAwakeHour, iSleepHour, iCurrentHour
// Can't accept call if friend ped exists
IF NOT PRIVATE_FriendAcceptsPlayer_exists(friendID)
CPRINTLN(DEBUG_FRIENDS, "friend ", GetLabel_enumFriend(friendID), " can't accept call: Friend ped exists")
RETURN FALSE
// Can't accept call if friend failed recently
ELIF NOT PRIVATE_FriendAcceptsPlayer_fail(friendID)
CPRINTLN(DEBUG_FRIENDS, "friend ", GetLabel_enumFriend(friendID), "can't accept call: char failed recently")
RETURN FALSE
// Can't accept call if friend is blocked by mission where player character doesn't know about friend's participation in advance
ELIF NOT PRIVATE_FriendAcceptsPlayer_missionBlock_AnswerPhone(playerID, friendID)
CPRINTLN(DEBUG_FRIENDS, "friend ", GetLabel_enumFriend(friendID), " can't accept call: blocked (mission - divert to answerphone)")
RETURN FALSE
// Can't accept call if friend is blocked by hiatus
ELIF NOT PRIVATE_FriendAcceptsPlayer_hiatusBlock(playerID, friendID)
CPRINTLN(DEBUG_FRIENDS, "friend ", GetLabel_enumFriend(friendID), " can't accept call: blocked (hiatus type)")
RETURN FALSE
// Can't accept multiple activity call if group not allowed
ELIF NOT PRIVATE_FriendAcceptsPlayer_group(playerID, friendID)
CPRINTLN(DEBUG_FRIENDS, "friend ", GetLabel_enumFriend(friendID), "can't accept call: group not allowed")
RETURN FALSE
// Can't accept call if not awake
ELIF NOT PRIVATE_FriendAcceptsPlayer_awake(friendID, iAwakeHour, iSleepHour, iCurrentHour)
CPRINTLN(DEBUG_FRIENDS, "friend ", GetLabel_enumFriend(friendID), "can't accept call: asleep: ", iAwakeHour, " < ", iCurrentHour, " < ", iSleepHour)
RETURN FALSE
// If we know friend wants to play, but conditions mean he must decline - just go to answer phone so he doesn't have to give an excuse.
ELIF PRIVATE_FriendAcceptsPlayer_wanted(playerID, friendID)
IF NOT PRIVATE_FriendAcceptsPlayer(playerID, friendID)
CPRINTLN(DEBUG_FRIENDS, "friend ", GetLabel_enumFriend(friendID), "can't accept call: Can't accept player, but is known to want to see them")
RETURN FALSE
ENDIF
ENDIF
RETURN TRUE
ENDFUNC
// *******************************************************************************************
// Checks for if squad call will be successful
// *******************************************************************************************
//FUNC BOOL PRIVATE_SquadAcceptsPlayer_fail(enumFriend eFriendID) // BBUDDIES REMOVED
//
// IF HAS_SOLDIER_FAILED(GET_CHAR_FROM_FRIEND(eFriendID))
// RETURN FALSE
// ENDIF
//
// RETURN TRUE
//ENDFUNC
//
//FUNC BOOL PRIVATE_SquadAcceptsPlayer_group(enumFriend ePlayerID, enumFriend eFriendID)
//
// enumCharacterList ePlayerChar = GET_CHAR_FROM_FRIEND(ePlayerID)
// enumCharacterList eFriendChar = GET_CHAR_FROM_FRIEND(eFriendID)
//
// enumFriendConnection eDesiredConnection
// enumFriendConnection eActiveConnection
// enumFriendConnection eCommonConnection
//
// // Make sure the requested connection is ready
// IF GET_CONNECTION(ePlayerChar, eFriendChar, eDesiredConnection)
// AND GET_CONNECTION_STATE(eDesiredConnection) = FC_STATE_ContactWait
//
// INT iActiveOrPendingSoldiers = GET_PENDING_OR_ACTIVE_CONNECTIONS(eActiveConnection)
//
// // If no connections running...
// IF iActiveOrPendingSoldiers = 0
// RETURN TRUE
//
// // If one connection are already running...
// ELIF iActiveOrPendingSoldiers = 1
//
// // Make sure there's a common connection, and it's ready
// IF GET_COMMON_CONNECTION(eActiveConnection, eDesiredConnection, eCommonConnection)
// AND GET_CONNECTION_STATE(eCommonConnection) = FC_STATE_ContactWait
//
// RETURN TRUE
//
// ENDIF
// ENDIF
// ENDIF
//
// RETURN FALSE
//
//ENDFUNC
//
//FUNC BOOL PRIVATE_SquadAcceptsPlayer_count()
//
// // One buddy limit applies on big score prep B
// IF g_BattleBuddyMission = SP_HEIST_FINALE_PREP_B
// OR g_BattleBuddyMission = SP_MISSION_FBI_4_PREP_2
// IF ARE_ANY_SQUAD_CONNECTIONS_PENDING_OR_ACTIVE()
// RETURN FALSE
// ENDIF
// ENDIF
//
// RETURN TRUE
//
//ENDFUNC
//
//FUNC BOOL PRIVATE_SquadAcceptsCall(enumFriend playerID, enumFriend friendID)
//
// // Can't accept call if battle buddy exists
// IF NOT PRIVATE_FriendAcceptsPlayer_exists(friendID)
// CPRINTLN(DEBUG_FRIENDS, "buddy ", GetLabel_enumFriend(friendID), " can't accept call: Battle buddy exists")
// RETURN FALSE
//
// // Can't accept call if battle buddy came already and failed
// ELIF NOT PRIVATE_SquadAcceptsPlayer_fail(friendID)
// CPRINTLN(DEBUG_FRIENDS, "buddy ", GetLabel_enumFriend(friendID), " can't accept call: Battle buddy failed recently")
// RETURN FALSE
//
// // Can't accept call if friend is blocked by hiatus
//// ELIF NOT PRIVATE_FriendAcceptsPlayer_hiatusBlock(playerID, friendID)
//// CPRINTLN(DEBUG_FRIENDS, "friend ", GetLabel_enumFriend(friendID), " can't accept call: blocked (hiatus type)")
//// RETURN FALSE
//
// // Can't accept multiple activity call if group not allowed
// ELIF NOT PRIVATE_SquadAcceptsPlayer_group(playerID, friendID)
// CPRINTLN(DEBUG_FRIENDS, "buddy ", GetLabel_enumFriend(friendID), "can't accept call: group not allowed")
// RETURN FALSE
//
// // Can't accept call if only one battle buddy is allowed and has already been called
// ELIF NOT PRIVATE_SquadAcceptsPlayer_count()
// CPRINTLN(DEBUG_FRIENDS, "buddy ", GetLabel_enumFriend(friendID), " can't accept call: Only one battle buddy allowed")
// RETURN FALSE
// ENDIF
//
// RETURN TRUE
//ENDFUNC
// *******************************************************************************************
// GET RELEVANT CONVERSATION
// *******************************************************************************************
FUNC BOOL TIMER_DO_WHEN_VERYIRATE(structTimer &t)
CONST_FLOAT fCONST_MINUTES_IN_HOUR 60.0
CONST_FLOAT fCONST_VERY_IRATE_minToWait 300.0
// CONST_FLOAT fCONST_IRATE_minToWait 60.0
IF TIMER_DO_WHEN_READY(t, fCONST_VERY_IRATE_minToWait*fCONST_MINUTES_IN_HOUR)
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL TIMER_DO_WHEN_IRATE(structTimer &t)
CONST_FLOAT fCONST_MINUTES_IN_HOUR 60.0
// CONST_FLOAT fCONST_VERY_IRATE_minToWait 300.0
CONST_FLOAT fCONST_IRATE_minToWait 60.0
IF TIMER_DO_WHEN_READY(t, fCONST_IRATE_minToWait*fCONST_MINUTES_IN_HOUR)
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL PRIVATE_Friend_GetPhoneConv_Greet(enumFriend ePlayer, enumFriendPhonePhrase &greetConv_a, enumFriendPhonePhrase &greetConv_b)
enumFriend eAlreadyMeetingFriend = NO_FRIEND
// If a connection is active (and current player is one of the friends in the connection), must be travelling to meet the other friend in the connection
enumFriendConnection eActiveConnection
IF GET_PENDING_OR_ACTIVE_CONNECTIONS(eActiveConnection) > 0
enumFriend eActiveFriendA = g_SavedGlobals.sFriendsData.g_FriendConnectData[eActiveConnection].friendA
enumFriend eActiveFriendB = g_SavedGlobals.sFriendsData.g_FriendConnectData[eActiveConnection].friendB
// Friend will be the one who isn't current player
IF eActiveFriendA = ePlayer
eAlreadyMeetingFriend = eActiveFriendB
ENDIF
IF eActiveFriendB = ePlayer
eAlreadyMeetingFriend = eActiveFriendA
ENDIF
ENDIF
// Setup proper greeting pair
SWITCH (eAlreadyMeetingFriend)
CASE FR_MICHAEL
greetConv_a = FPP_OUTGOING_GREET1
greetConv_b = FPP_OUTGOING_GREET2_MEETINGMICHAEL
BREAK
CASE FR_FRANKLIN
greetConv_a = FPP_OUTGOING_GREET1
greetConv_b = FPP_OUTGOING_GREET2_MEETINGFRANKLIN
BREAK
CASE FR_TREVOR
greetConv_a = FPP_OUTGOING_GREET1
greetConv_b = FPP_OUTGOING_GREET2_MEETINGTREVOR
BREAK
CASE FR_LAMAR
greetConv_a = FPP_OUTGOING_GREET1
greetConv_b = FPP_OUTGOING_GREET2_MEETINGLAMAR
BREAK
DEFAULT
greetConv_a = FPP_OUTGOING_GREET1
greetConv_b = FPP_OUTGOING_GREET2
BREAK
ENDSWITCH
RETURN TRUE
ENDFUNC
FUNC BOOL PRIVATE_Friend_GetPhoneConv_Accept(enumFriend ePlayer, enumFriend eBuddy, enumFriendPhonePhrase &yesConv_a, enumFriendPhonePhrase &yesConv_b, BOOL bAttemptAdhoc=FALSE)
enumFriendConnection eConn = GET_CONNECTION_FROM_FRIENDS(ePlayer, eBuddy)
CONST_INT iCONST_LOW_LIKES 30
// select YES-ACCEPTED response
IF NOT g_SavedGlobals.sFriendsData.g_bHelpDoneCanCancel
yesConv_a = FPP_OUTGOING_YES1_FIRSTTIME
yesConv_b = FPP_OUTGOING_YES2_OK
RETURN TRUE
ELSE
// enumFriend eAlreadyMeetingFriend = NO_FRIEND
//
// // If a connection is active (and phone call being made to one of the friends in the connection), must be travelling to meet the other friend in the connection
// enumFriendConnection eActiveConnection
// IF GET_ONLY_ACTIVE_CONNECTION(eActiveConnection)
//
// enumFriend eActiveFriendA = g_SavedGlobals.sFriendsData.g_FriendConnectData[eActiveConnection].friendA
// enumFriend eActiveFriendB = g_SavedGlobals.sFriendsData.g_FriendConnectData[eActiveConnection].friendB
//
// // Friend will be the one who isn't current player
// IF eActiveFriendA = eBuddy
// eAlreadyMeetingFriend = eActiveFriendB
// ENDIF
// IF eActiveFriendB = eBuddy
// eAlreadyMeetingFriend = eActiveFriendA
// ENDIF
//
// ENDIF
//
// IF eAlreadyMeetingFriend <> NO_FRIEND
// SWITCH (eAlreadyMeetingFriend)
// CASE FR_MICHAEL
// yesConv_a = FPP_OUTGOING_YES1_MEETINGMICHAEL
// yesConv_b = FPP_OUTGOING_YES2_OK
// RETURN TRUE
// BREAK
// CASE FR_FRANKLIN
// yesConv_a = FPP_OUTGOING_YES1_MEETINGFRANKLIN
// yesConv_b = FPP_OUTGOING_YES2_OK
// RETURN TRUE
// BREAK
// CASE FR_TREVOR
// yesConv_a = FPP_OUTGOING_YES1_MEETINGTREVOR
// yesConv_b = FPP_OUTGOING_YES2_OK
// RETURN TRUE
// BREAK
// CASE FR_LAMAR
// yesConv_a = FPP_OUTGOING_YES1_MEETINGLAMAR
// yesConv_b = FPP_OUTGOING_YES2_OK
// RETURN TRUE
// BREAK
// ENDSWITCH
// IF ARE_ANY_EVENTS_PENDING_IN_ORGANISER() // TODO: Surely this needs to know which organiser the event is in?
// yesConv_a = FPP_OUTGOING_YES1_BUSY
// yesConv_b = FPP_OUTGOING_YES2_OK
// RETURN TRUE
// ELSE
IF (g_SavedGlobals.sFriendsData.g_FriendConnectData[eConn].likes < iCONST_LOW_LIKES)
yesConv_a = FPP_OUTGOING_YES1_LIKELOW
yesConv_b = FPP_OUTGOING_YES2_OK
RETURN TRUE
ELIF TIMER_DO_WHEN_VERYIRATE(g_SavedGlobals.sFriendsData.g_FriendConnectData[eConn].lastContactTimer)
yesConv_a = FPP_OUTGOING_YES1_VERYIRATE
yesConv_b = FPP_OUTGOING_YES2_OK
RETURN TRUE
ELIF TIMER_DO_WHEN_IRATE(g_SavedGlobals.sFriendsData.g_FriendConnectData[eConn].lastContactTimer)
yesConv_a = FPP_OUTGOING_YES1_IRATE
yesConv_b = FPP_OUTGOING_YES2_OK
RETURN TRUE
ELIF bAttemptAdhoc
AND GET_RANDOM_INT_IN_RANGE(0, 100) < CONST_iFriendAdhocChance
AND NOT (ePlayer = FR_TREVOR AND eBuddy = FR_MICHAEL)
AND NOT (ePlayer = FR_MICHAEL AND eBuddy = FR_AMANDA)
AND NOT IS_PED_INJURED(PLAYER_PED_ID())
AND Private_GetAvailableAdhocFriendLoc(GET_ENTITY_COORDS(PLAYER_PED_ID()))
yesConv_a = FPP_OUTGOING_YES1_COMETOYOU
yesConv_b = FPP_OUTGOING_YES2_COMETOYOU
RETURN TRUE
ELSE
yesConv_a = FPP_OUTGOING_YES1_OK
yesConv_b = FPP_OUTGOING_YES2_OK
RETURN TRUE
ENDIF
// ENDIF
ENDIF
SCRIPT_ASSERT("missing PRIVATE_Friend_GetPhoneConv_Accept()")
RETURN FALSE
ENDFUNC
FUNC BOOL PRIVATE_Friend_GetPhoneConv_Reject(enumFriend ePlayer, enumFriend eBuddy, enumFriendPhonePhrase &noConv_a, enumFriendPhonePhrase &noConv_b)
enumFriendContactType eLastContactType
FLOAT fLastContactTime
// select NO-REJECT response
IF NOT PRIVATE_FriendAcceptsPlayer_time(ePlayer, eBuddy, fLastContactTime, eLastContactType)
IF eLastContactType = FRIEND_CONTACT_FACE OR eLastContactType = FRIEND_CONTACT_DISMISSED
noConv_a = FPP_OUTGOING_NO1_RECENTFACE
noConv_b = FPP_OUTGOING_NO2
RETURN TRUE
ELSE
noConv_a = FPP_OUTGOING_NO1_RECENTPHONE
noConv_b = FPP_OUTGOING_NO2
RETURN TRUE
ENDIF
ELIF NOT PRIVATE_FriendAcceptsPlayer_missionBlock(ePlayer, eBuddy)
noConv_a = FPP_OUTGOING_NO1_BLOCKED
noConv_b = FPP_OUTGOING_NO2
RETURN TRUE
// ELIF NOT PRIVATE_FriendAcceptsPlayer_clashBlock(ePlayer, eBuddy)
// noConv_a = FPP_OUTGOING_NO1_OK
// noConv_b = FPP_OUTGOING_NO2
// RETURN TRUE
ELSE
noConv_a = FPP_OUTGOING_NO1_OK
noConv_b = FPP_OUTGOING_NO2
RETURN TRUE
ENDIF
SCRIPT_ASSERT("missing PRIVATE_Friend_GetPhoneConv_Reject()")
RETURN FALSE
ENDFUNC
FUNC BOOL PRIVATE_Friend_GetPhoneConv(enumFriend ePlayer, enumFriend eBuddy,
enumFriendPhonePhrase &greetConv_a, enumFriendPhonePhrase &greetConv_b,
enumFriendPhonePhrase &yesConv_a, enumFriendPhonePhrase &yesConv_b,
enumFriendPhonePhrase &noConv_a, enumFriendPhonePhrase &noConv_b)
// select GREET-HANGOUT response
PRIVATE_Friend_GetPhoneConv_Greet(ePlayer, greetConv_a, greetConv_b)
// select YES-ACCEPTED response
PRIVATE_Friend_GetPhoneConv_Accept(ePlayer, eBuddy, yesConv_a, yesConv_b)
// select NO-REJECT response
PRIVATE_Friend_GetPhoneConv_Reject(ePlayer, eBuddy, noConv_a, noConv_b)
RETURN FALSE
ENDFUNC
//FUNC BOOL PRIVATE_Friend_GetPhoneConv_Backup(enumFriendPhonePhrase &ePhrase) // BBUDDIES REMOVED
//
// // If on/near prep mission use mission-specific dialogue, otherwise use default
// IF g_eFriendMissionZoneID <> SP_MISSION_NONE
//
// SWITCH g_eFriendMissionZoneID
// CASE SP_HEIST_DOCKS_PREP_2B
// ePhrase = FPP_BACKUP_DOCKS_2
// BREAK
//
// CASE SP_HEIST_RURAL_PREP_1
// ePhrase = FPP_BACKUP_RURAL
// BREAK
//
// CASE SP_HEIST_AGENCY_PREP_1
// ePhrase = FPP_BACKUP_AGENCY
// BREAK
//
// CASE SP_HEIST_FINALE_PREP_A
// ePhrase = FPP_BACKUP_FINALE_A
// BREAK
//
// CASE SP_HEIST_FINALE_PREP_B
// ePhrase = FPP_BACKUP_FINALE_B
// BREAK
//
// CASE SP_MISSION_FBI_4_PREP_1
// ePhrase = FPP_BACKUP_FBI_1
// BREAK
//
// CASE SP_MISSION_FBI_4_PREP_2
// ePhrase = FPP_BACKUP_FBI_2
// BREAK
//
// CASE SP_MISSION_FBI_4_PREP_4
// ePhrase = FPP_BACKUP_FBI_4
// BREAK
//
// CASE SP_MISSION_FBI_4_PREP_5
// ePhrase = FPP_BACKUP_FBI_5
// BREAK
//
// DEFAULT
// ePhrase = FPP_BACKUP
// BREAK
// ENDSWITCH
//
// RETURN TRUE
//
// ENDIF
//
// SCRIPT_ASSERT("missing PRIVATE_Friend_GetPhoneConv_Backup()")
// ePhrase = FPP_BACKUP
// RETURN FALSE
//
//ENDFUNC
// *******************************************************************************************
// APPROACH MONITOR
// *******************************************************************************************
ENUM enumAmbFriendState
AMBFRIEND_NULL = 0,
AMBFRIEND_PLAYER,
AMBFRIEND_IDLE,
AMBFRIEND_GREET,
AMBFRIEND_GREET_SAFEHOUSE,
AMBFRIEND_GREET_OUTSIDE,
AMBFRIEND_DISMISSED,
AMBFRIEND_OUTRO
ENDENUM
ENUM enumAmbConvState
AMBCONV_NULL = 0,
AMBCONV_SAFEHOUSE_TRAILER1,
AMBCONV_SAFEHOUSE_TRAILER2,
AMBCONV_SAFEHOUSE_TRAILER3,
AMBCONV_MFSHOWROOM_HELLO,
AMBCONV_MFSHOWROOM_CHAT,
AMBCONV_MTFALLOUT_HELLO,
AMBCONV_MTFALLOUT_CHAT,
AMBCONV_KILLEDT_HELLO,
AMBCONV_KILLEDT_CHAT,
AMBCONV_KILLEDM_HELLO,
AMBCONV_KILLEDM_CHAT,
AMBCONV_ANGRY_HELLO,
AMBCONV_ANGRY_GOODBYE,
AMBCONV_OFFER_HELLO_HOME,
AMBCONV_OFFER_HELLO,
AMBCONV_OFFER_ASK,
AMBCONV_OFFER_INPUT,
AMBCONV_OFFER_NO1,
AMBCONV_OFFER_NO2,
AMBCONV_OFFER_YES1,
AMBCONV_OFFER_YES2,
AMBCONV_OFFER_LAUNCH1,
AMBCONV_OFFER_LAUNCH2,
AMBCONV_MISBLOCK_HELLO_HOME,
AMBCONV_MISBLOCK_HELLO,
AMBCONV_MISBLOCK_BUSY,
AMBCONV_MISBLOCK_GOODBYE,
AMBCONV_DEFBLOCK_HELLO_HOME,
AMBCONV_DEFBLOCK_HELLO,
AMBCONV_DEFBLOCK_BUSY,
AMBCONV_DEFBLOCK_GOODBYE
ENDENUM
STRUCT structAmbFriend
enumAmbFriendState eState
enumCharacterList eChar
PED_INDEX hPed
VEHICLE_INDEX hVehicle
INT iProgress
PED_INDEX hTargetPed
REL_GROUP_HASH hOriginalGroup
REL_GROUP_HASH hOriginalDefaultGroup
BOOL bIsVulnerable
SHAPETEST_INDEX hLosTest
INT iLosTestTime
enumAmbGrabMode eGrabMode
enumAmbDismissMode eDismissMode
BOOL bDismissAudioRequest
BOOL bInitDismissMode
#IF IS_DEBUG_BUILD
PED_INDEX hDebugGrabFail
#ENDIF
ENDSTRUCT
CONST_INT MAX_GRIEF_OUTRO_AREAS 3
STRUCT structAmbGroup
structAmbFriend mFriends[NUM_OF_PLAYABLE_PEDS]
INT iGreetCount
BOOL bHasSaidHello
BOOL bHasJoined
BOOL bIsSwitchActive
enumAmbConvState eConvState
enumCharacterList eConvChar
BOOL bInitConvState
INT hConvInput
structTimer mOfferTimer
REL_GROUP_HASH hEmptyGroup
BOOL bAttemptResumeFightAfterSwitch
BOOL bUpdateAfterSwitch
BOOL bOutroActive
SHAPETEST_INDEX hOutroTest[MAX_GRIEF_OUTRO_AREAS]
INT iOutroTestTime[MAX_GRIEF_OUTRO_AREAS]
VECTOR vOutroTestPos
FLOAT fOutroTestRot
INT iOutroSpeechVariation
enumCharacterList eOutroChar
INT hOutroCandidate
INT hOutroScene
CAMERA_INDEX hOutroCamera
SCENARIO_BLOCKING_INDEX hOutroScenarioBlock
VECTOR vOutroAnchor
ENDSTRUCT
//------------------------------------------------------------------------------------------------------------------------------
// Debug
//------------------------------------------------------------------------------------------------------------------------------
#IF IS_DEBUG_BUILD
FUNC STRING GetLabel_enumAmbFriendState(enumAmbFriendState eState)
SWITCH eState
CASE AMBFRIEND_NULL RETURN "AMBFRIEND_NULL" BREAK
CASE AMBFRIEND_PLAYER RETURN "AMBFRIEND_PLAYER" BREAK
CASE AMBFRIEND_IDLE RETURN "AMBFRIEND_IDLE" BREAK
CASE AMBFRIEND_GREET RETURN "AMBFRIEND_GREET" BREAK
CASE AMBFRIEND_GREET_SAFEHOUSE RETURN "AMBFRIEND_GREET_SAFEHOUSE" BREAK
CASE AMBFRIEND_GREET_OUTSIDE RETURN "AMBFRIEND_GREET_OUTSIDE" BREAK
CASE AMBFRIEND_DISMISSED RETURN "AMBFRIEND_DISMISSED" BREAK
CASE AMBFRIEND_OUTRO RETURN "AMBFRIEND_OUTRO" BREAK
ENDSWITCH
SCRIPT_ASSERT("GetLabel_enumAmbFriendState() - Unknown state")
RETURN "<unknown state>"
ENDFUNC
FUNC STRING GetLabel_enumAmbConvState(enumAmbConvState eState)
SWITCH eState
CASE AMBCONV_NULL RETURN "AMBCONV_NULL" BREAK
CASE AMBCONV_SAFEHOUSE_TRAILER1 RETURN "AMBCONV_SAFEHOUSE_TRAILER1" BREAK
CASE AMBCONV_SAFEHOUSE_TRAILER2 RETURN "AMBCONV_SAFEHOUSE_TRAILER2" BREAK
CASE AMBCONV_SAFEHOUSE_TRAILER3 RETURN "AMBCONV_SAFEHOUSE_TRAILER3" BREAK
CASE AMBCONV_MFSHOWROOM_HELLO RETURN "AMBCONV_MFSHOWROOM_HELLO" BREAK
CASE AMBCONV_MFSHOWROOM_CHAT RETURN "AMBCONV_MFSHOWROOM_CHAT" BREAK
CASE AMBCONV_MTFALLOUT_HELLO RETURN "AMBCONV_MTFALLOUT_HELLO" BREAK
CASE AMBCONV_MTFALLOUT_CHAT RETURN "AMBCONV_MTFALLOUT_CHAT" BREAK
CASE AMBCONV_KILLEDT_HELLO RETURN "AMBCONV_KILLEDT_HELLO" BREAK
CASE AMBCONV_KILLEDT_CHAT RETURN "AMBCONV_KILLEDT_CHAT" BREAK
CASE AMBCONV_KILLEDM_HELLO RETURN "AMBCONV_KILLEDM_HELLO" BREAK
CASE AMBCONV_KILLEDM_CHAT RETURN "AMBCONV_KILLEDM_CHAT" BREAK
CASE AMBCONV_ANGRY_HELLO RETURN "AMBCONV_ANGRY_HELLO" BREAK
CASE AMBCONV_ANGRY_GOODBYE RETURN "AMBCONV_ANGRY_GOODBYE" BREAK
CASE AMBCONV_OFFER_HELLO_HOME RETURN "AMBCONV_OFFER_HELLO_HOME" BREAK
CASE AMBCONV_OFFER_HELLO RETURN "AMBCONV_OFFER_HELLO" BREAK
CASE AMBCONV_OFFER_ASK RETURN "AMBCONV_OFFER_ASK" BREAK
CASE AMBCONV_OFFER_INPUT RETURN "AMBCONV_OFFER_INPUT" BREAK
CASE AMBCONV_OFFER_NO1 RETURN "AMBCONV_OFFER_NO1" BREAK
CASE AMBCONV_OFFER_NO2 RETURN "AMBCONV_OFFER_NO2" BREAK
CASE AMBCONV_OFFER_YES1 RETURN "AMBCONV_OFFER_YES1" BREAK
CASE AMBCONV_OFFER_YES2 RETURN "AMBCONV_OFFER_YES2" BREAK
CASE AMBCONV_OFFER_LAUNCH1 RETURN "AMBCONV_OFFER_LAUNCH1" BREAK
CASE AMBCONV_OFFER_LAUNCH2 RETURN "AMBCONV_OFFER_LAUNCH2" BREAK
CASE AMBCONV_MISBLOCK_HELLO_HOME RETURN "AMBCONV_MISBLOCK_HELLO_HOME" BREAK
CASE AMBCONV_MISBLOCK_HELLO RETURN "AMBCONV_MISBLOCK_HELLO" BREAK
CASE AMBCONV_MISBLOCK_BUSY RETURN "AMBCONV_MISBLOCK_BUSY" BREAK
CASE AMBCONV_MISBLOCK_GOODBYE RETURN "AMBCONV_MISBLOCK_GOODBYE" BREAK
CASE AMBCONV_DEFBLOCK_HELLO_HOME RETURN "AMBCONV_DEFBLOCK_HELLO_HOME" BREAK
CASE AMBCONV_DEFBLOCK_HELLO RETURN "AMBCONV_DEFBLOCK_HELLO" BREAK
CASE AMBCONV_DEFBLOCK_BUSY RETURN "AMBCONV_DEFBLOCK_BUSY" BREAK
CASE AMBCONV_DEFBLOCK_GOODBYE RETURN "AMBCONV_DEFBLOCK_GOODBYE" BREAK
ENDSWITCH
SCRIPT_ASSERT("GetLabel_enumAmbConvState() - Unknown state")
RETURN "<unknown state>"
ENDFUNC
FUNC STRING GetLabel_enumAmbDismissMode(enumAmbDismissMode eState)
SWITCH eState
CASE AMBMODE_WANDER RETURN "Wander" BREAK
CASE AMBMODE_FLEE RETURN "Flee" BREAK
CASE AMBMODE_REJECTED RETURN "Rejected" BREAK
CASE AMBMODE_JACKED RETURN "Jacked" BREAK
CASE AMBMODE_FIGHT RETURN "Fight" BREAK
ENDSWITCH
SCRIPT_ASSERT("GetLabel_enumAmbDismissMode() - Unknown state")
RETURN "<unknown state>"
ENDFUNC
PROC DEBUG_DisplayAmbFriendInfo(structAmbGroup& group, structAmbFriend& friend)
// Get line to print on
INT iRow = 4 + ENUM_TO_INT(friend.eChar)
// Build string
TEXT_LABEL_63 tName = GetLabel_enumFriend(GET_FRIEND_FROM_CHAR(friend.eChar))
IF friend.bIsVulnerable
tName+="-V"
ENDIF
tName += ": "
tName += GetLabel_enumAmbFriendState(friend.eState)
IF friend.eState = AMBFRIEND_DISMISSED
tName += " / "
tName += GetLabel_enumAmbDismissMode(friend.eDismissMode)
IF friend.bDismissAudioRequest
tName += "*"
ENDIF
ENDIF
IF group.eConvState <> AMBCONV_NULL
IF group.eConvChar = friend.eChar
tName += " [conv]"
ENDIF
ENDIF
DrawFriendLiteralString(tName, iRow, HUD_COLOUR_BLUELIGHT)
ENDPROC
PROC DEBUG_DisplayAmbGroupInfo(structAmbGroup& group)
IF g_flowUnsaved.bShowMissionFlowDebugScreen
IF (g_iDebugSelectedFriendConnDisplay > 0)
IF NOT IS_FRIEND_ACTIVITY_SCRIPT_RUNNING()
// Get line to print on
INT iRow = 4 + NUM_OF_PLAYABLE_PEDS + 1
TEXT_LABEL_63 tName = "Conversation: "
tName += GetLabel_enumAmbConvState(group.eConvState)
DrawFriendLiteralString(tName, iRow, HUD_COLOUR_BLUELIGHT)
iRow++
tName = ""
IF group.bHasSaidHello
tName+= "[HelloDone]"
ENDIF
IF group.bHasJoined
tName+= "[JoinDone]"
ENDIF
DrawFriendLiteralString(tName, iRow, HUD_COLOUR_BLUELIGHT)
// Print info for each ambfriend
enumCharacterList eChar
REPEAT NUM_OF_PLAYABLE_PEDS eChar
DEBUG_DisplayAmbFriendInfo(group, group.mFriends[eChar])
ENDREPEAT
ENDIF
ENDIF
ENDIF
ENDPROC
#ENDIF
//------------------------------------------------------------------------------------------------------------------------------
// AmbFriend - Idle state
//------------------------------------------------------------------------------------------------------------------------------
PROC Private_UpdateAmbFriend_Idle(structAmbGroup& group, structAmbFriend& friend)
IF NOT IS_PED_IN_ANY_VEHICLE(friend.hPed)
IF friend.iProgress = 0
// Is ped on road...
CLEAR_PED_TASKS(friend.hPed)
VECTOR vFriendPos = GET_ENTITY_COORDS(friend.hPed)
IF IS_POINT_ON_ROAD(vFriendPos, NULL)
friend.iProgress++
ELSE
friend.iProgress = 60
ENDIF
ELIF friend.iProgress < 60
// On road -> Try to find pavement pos to walk to
CPRINTLN(DEBUG_FRIENDS, "Search for pavement vector... (", friend.iProgress, ")")
friend.iProgress++
VECTOR vFriendPos = GET_ENTITY_COORDS(friend.hPed)
VECTOR vPavementPos
INT iFlags = ENUM_TO_INT(GSC_FLAG_ONLY_PAVEMENT)|ENUM_TO_INT(GSC_FLAG_NOT_ISOLATED)|ENUM_TO_INT(GSC_FLAG_NOT_INTERIOR)|ENUM_TO_INT(GSC_FLAG_NOT_WATER)
IF GET_SAFE_COORD_FOR_PED(vFriendPos, TRUE, vPavementPos, INT_TO_ENUM(GET_SAFE_COORD_FLAGS, iFlags))
BOOL bCloseToOtherFriend = FALSE
enumCharacterList eCharLoop
REPEAT NUM_OF_PLAYABLE_PEDS eCharLoop
IF eCharLoop <> friend.eChar
IF group.mFriends[eCharLoop].eState != AMBFRIEND_NULL
IF NOT IS_PED_INJURED(group.mFriends[eCharLoop].hPed)
IF Util_IsPedInsideRange(group.mFriends[eCharLoop].hPed, vPavementPos, 3.0)
bCloseToOtherFriend = TRUE
ENDIF
ENDIF
ENDIF
ENDIF
ENDREPEAT
IF bCloseToOtherFriend = FALSE
TASK_FOLLOW_NAV_MESH_TO_COORD(friend.hPed, vPavementPos, PEDMOVE_WALK, DEFAULT_TIME_NEVER_WARP)
CPRINTLN(DEBUG_FRIENDS, "Private_AmbFriendTasks_Idle() DEST = <<", vPavementPos.x, ", ", vPavementPos.y, ", ", vPavementPos.z, ">>")
friend.iProgress = 60
ENDIF
ENDIF
ELIF friend.iProgress = 60
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_FOLLOW_NAV_MESH_TO_COORD)
friend.iProgress++
ENDIF
ELIF friend.iProgress = 61
IF NOT IS_PED_USING_ANY_SCENARIO(friend.hPed)
SET_PED_CAN_PLAY_AMBIENT_ANIMS(friend.hPed, TRUE)
SET_PED_CAN_PLAY_AMBIENT_BASE_ANIMS(friend.hPed, TRUE)
SET_PED_SHOULD_PLAY_IMMEDIATE_SCENARIO_EXIT(friend.hPed)
CLEAR_PED_TASKS(friend.hPed)
INT iRandom = GET_RANDOM_INT_IN_RANGE(0, 3)
IF iRandom = 0
TASK_START_SCENARIO_IN_PLACE(friend.hPed, "WORLD_HUMAN_SMOKING", 0, TRUE)
ELSE
TASK_START_SCENARIO_IN_PLACE(friend.hPed, "WORLD_HUMAN_STAND_MOBILE", 0, TRUE)
ENDIF
// ELIF iRandom = 2
// TASK_START_SCENARIO_IN_PLACE(friend.hPed, "WORLD_HUMAN_STAND_IMPATIENT", 0, TRUE)
// ELIF iRandom = 3
// TASK_START_SCENARIO_IN_PLACE(friend.hPed, "WORLD_HUMAN_GUARD_STAND", 0, TRUE)
// ENDIF
ELSE//IF NOT IS_PED_ACTIVE_IN_SCENARIO(friend.hPed)
TRIGGER_IDLE_ANIMATION_ON_PED(friend.hPed)
ENDIF
ENDIF
ELSE
friend.iProgress = 0
VEHICLE_INDEX hVehicle = GET_VEHICLE_PED_IS_IN(friend.hPed)
IF GET_PED_IN_VEHICLE_SEAT(hVehicle, VS_DRIVER) = friend.hPed
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_VEHICLE_MISSION)
CLEAR_PED_TASKS(friend.hPed)
TASK_VEHICLE_DRIVE_WANDER(friend.hPed, hVehicle, 7.5, DRIVINGMODE_STOPFORCARS)
ENDIF
ELSE
IF ABSF(GET_ENTITY_SPEED(hVehicle)) < 10.0
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_LEAVE_ANY_VEHICLE)
CLEAR_PED_TASKS(friend.hPed)
TASK_LEAVE_ANY_VEHICLE(friend.hPed)
ENDIF
ELSE
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_PAUSE)
CLEAR_PED_TASKS(friend.hPed)
TASK_PAUSE(friend.hPed, -1)
ENDIF
ENDIF
ENDIF
ENDIF
ENDPROC
//------------------------------------------------------------------------------------------------------------------------------
// AmbFriend - Greet state
//------------------------------------------------------------------------------------------------------------------------------
PROC Private_UpdateAmbFriend_Greet(structAmbFriend& friend, BOOL bAllowMovement = TRUE)
IF bAllowMovement
// If in vehicle, leave it
IF IS_PED_IN_ANY_VEHICLE(friend.hPed)
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_LEAVE_ANY_VEHICLE)
CLEAR_PED_TASKS(friend.hPed)
TASK_LEAVE_ANY_VEHICLE(friend.hPed, GET_RANDOM_INT_IN_RANGE(0, 1000), ECF_RESUME_IF_INTERRUPTED)
ENDIF
// If on foot, approach player (run)
ELIF NOT IS_ENTITY_AT_ENTITY(friend.hPed, PLAYER_PED_ID(), <<15.0, 15.0, 15.0>>)
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_GO_TO_ENTITY)
CLEAR_PED_TASKS(friend.hPed)
TASK_GO_TO_ENTITY(friend.hPed, PLAYER_PED_ID(), DEFAULT_TIME_NEVER_WARP, 6.0, PEDMOVE_RUN)
ENDIF
// If on foot, approach player (walk)
ELIF NOT IS_ENTITY_AT_ENTITY(friend.hPed, PLAYER_PED_ID(), <<7.5, 7.5, 7.5>>)
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_GO_TO_ENTITY)
CLEAR_PED_TASKS(friend.hPed)
TASK_GO_TO_ENTITY(friend.hPed, PLAYER_PED_ID(), DEFAULT_TIME_NEVER_WARP, 6.0, PEDMOVE_WALK)
ENDIF
// If on foot, turn to player
ELSE
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_TURN_PED_TO_FACE_ENTITY)
CLEAR_PED_TASKS(friend.hPed)
TASK_TURN_PED_TO_FACE_ENTITY(friend.hPed, PLAYER_PED_ID(), -1)
ENDIF
ENDIF
ENDIF
// Peds look at each other
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_LOOK_AT_ENTITY)
TASK_LOOK_AT_ENTITY(friend.hPed, PLAYER_PED_ID(), -1, SLF_WHILE_NOT_IN_FOV, SLF_LOOKAT_HIGH)
ENDIF
IF NOT IsPedPerformingTask(PLAYER_PED_ID(), SCRIPT_TASK_LOOK_AT_ENTITY)
TASK_LOOK_AT_ENTITY(PLAYER_PED_ID(), friend.hPed, -1, SLF_DEFAULT, SLF_LOOKAT_HIGH)
ENDIF
ENDPROC
//------------------------------------------------------------------------------------------------------------------------------
// AmbFriend - Dismissed state
//------------------------------------------------------------------------------------------------------------------------------
FUNC BOOL Private_CanAmbFriendConverse(structAmbGroup& group)
// Check technical reasons
IF group.eConvState <> AMBCONV_NULL
OR IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
OR IS_MESSAGE_BEING_DISPLAYED()
RETURN FALSE
ENDIF
RETURN TRUE
ENDFUNC
PROC Private_GetAmbFriendOutroAreas(enumCharacterList eChar, VECTOR& vBoxOffsetA, VECTOR& vBoxSizeA, VECTOR& vBoxOffsetB, VECTOR& vBoxSizeB, VECTOR& vBoxOffsetC, VECTOR& vBoxSizeC)
SWITCH eChar
CASE CHAR_MICHAEL
vBoxOffsetA = << 0.0, 0.5, 0.0 >>
vBoxSizeA = << 1.0, 1.5, 2.0 >>
vBoxOffsetB = << 0.0, 1.5, 0.25 >>
vBoxSizeB = << 1.0, 1.0, 1.5 >>
vBoxOffsetC = << 0.0, -1.5, 0.0 >>
vBoxSizeC = << 1.5, 2.5, 1.25 >>
BREAK
CASE CHAR_FRANKLIN
vBoxOffsetA = << 0.1, 0.5, 0.0 >>
vBoxSizeA = << 1.75, 1.5, 2.0 >>
vBoxOffsetB = << 0.0, 1.5, 0.1 >>
vBoxSizeB = << 1.0, 1.8, 1.6 >>
vBoxOffsetC = << 0.75, -1.75, 0.0 >>
vBoxSizeC = << 2.5, 3.0, 1.25 >>
BREAK
CASE CHAR_TREVOR FALLTHRU
DEFAULT
vBoxOffsetA = << -0.25, 0.75, 0.0 >>
vBoxSizeA = << 1.25, 1.5, 2.0 >>
vBoxOffsetB = << -0.25, 1.75, 0.1 >>
vBoxSizeB = << 1.0, 2.0, 1.6 >>
vBoxOffsetC = << -1.0, -1.5, 0.0 >>
vBoxSizeC = << 3.0, 3.0, 1.25 >>
BREAK
ENDSWITCH
ENDPROC
FUNC BOOL Private_CheckAmbFriendOutroArea(structAmbGroup& group, structAmbFriend& friend)
// IF NOT IS_ENTITY_IN_AIR(PLAYER_PED_ID())
// AND NOT IS_PED_RAGDOLL(PLAYER_PED_ID())
// AND NOT IS_PED_RAGDOLL(friend.hPed)
// Calculate proposed scene position
VECTOR vPlayerPos = GET_ENTITY_COORDS(PLAYER_PED_ID())
VECTOR vPlayerToFriend = GET_ENTITY_COORDS(friend.hPed) - GET_ENTITY_COORDS(PLAYER_PED_ID())
FLOAT fSceneRot = GET_HEADING_FROM_VECTOR_2D(vPlayerToFriend.x, vPlayerToFriend.y)
VEHICLE_INDEX hPlayerCar = NULL
ENTITY_INDEX hHitEntity
VECTOR vHitPoint, vHitNormal
INT iHitCount
VECTOR vBoxOffset[MAX_GRIEF_OUTRO_AREAS]
VECTOR vBoxSize[MAX_GRIEF_OUTRO_AREAS]
Private_GetAmbFriendOutroAreas(friend.eChar, vBoxOffset[0], vBoxSize[0], vBoxOffset[1], vBoxSize[1], vBoxOffset[2], vBoxSize[2])
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
hPlayerCar = GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID())
ENDIF
INT iAreaLoop
REPEAT MAX_GRIEF_OUTRO_AREAS iAreaLoop
IF (group.hOutroTest[iAreaLoop] = NULL)
IF vBoxSize[iAreaLoop].x != 0.0 AND vBoxSize[iAreaLoop].y != 0.0 AND vBoxSize[iAreaLoop].z != 0.0
VECTOR vCentre = GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS(vPlayerPos, fSceneRot, vBoxOffset[iAreaLoop])
INT iFlags = /*SCRIPT_INCLUDE_MOVER|*/SCRIPT_INCLUDE_VEHICLE|SCRIPT_INCLUDE_OBJECT|SCRIPT_INCLUDE_GLASS|SCRIPT_INCLUDE_FOLIAGE
IF iAreaLoop = 0
vCentre += << 0.0, 0.0, 0.1 >>
ENDIF
group.hOutroTest[iAreaLoop] = START_SHAPE_TEST_BOX(vCentre, vBoxSize[iAreaLoop], <<0, 0, fSceneRot>>, EULER_YXZ, iFlags, hPlayerCar, SCRIPT_SHAPETEST_OPTION_DEFAULT)
ENDIF
ELSE
SHAPETEST_STATUS eStatus = GET_SHAPE_TEST_RESULT(group.hOutroTest[iAreaLoop], iHitCount, vHitPoint, vHitNormal, hHitEntity)
SWITCH (eStatus)
CASE SHAPETEST_STATUS_RESULTS_READY
IF iHitCount = 0
IF iAreaLoop = 0
group.vOutroTestPos = vPlayerPos
group.fOutroTestRot = fSceneRot
ENDIF
group.iOutroTestTime[iAreaLoop] = GET_GAME_TIMER()
// ELSE
// VECTOR vCentre
// vCentre = GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS(vPlayerPos, fSceneRot, vBoxOffset[iAreaLoop])
// IF iAreaLoop = 0
// DRAW_DEBUG_BOX(vCentre - (vBoxSize[iAreaLoop]*0.5), vCentre + (vBoxSize[iAreaLoop]*0.5), 255, 0, 0, 64)
// ELIF iAreaLoop = 1
// DRAW_DEBUG_BOX(vCentre - (vBoxSize[iAreaLoop]*0.5), vCentre + (vBoxSize[iAreaLoop]*0.5), 0, 255, 0, 64)
// ELIF iAreaLoop = 2
// DRAW_DEBUG_BOX(vCentre - (vBoxSize[iAreaLoop]*0.5), vCentre + (vBoxSize[iAreaLoop]*0.5), 0, 0, 255, 64)
// ENDIF
ENDIF
group.hOutroTest[iAreaLoop] = NULL
BREAK
CASE SHAPETEST_STATUS_RESULTS_NOTREADY
BREAK
CASE SHAPETEST_STATUS_NONEXISTENT
group.hOutroTest[iAreaLoop] = NULL
BREAK
ENDSWITCH
ENDIF
ENDREPEAT
IF GET_GAME_TIMER() < group.iOutroTestTime[0] + 500
AND GET_GAME_TIMER() < group.iOutroTestTime[1] + 500
AND GET_GAME_TIMER() < group.iOutroTestTime[2] + 500
// DRAW_DEBUG_BOX(group.vOutroTestPos + vBoxOffset[0] - (vBoxSize[0]*0.5), group.vOutroTestPos + vBoxOffset[0] + (vBoxSize[0]*0.5), 255, 255, 255, 64)
RETURN TRUE
ENDIF
// ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_CanAmbFriendBlank(structAmbFriend& friend, BOOL bDoDialogue = FALSE)
#if USE_CLF_DLC
bDoDialogue = bDoDialogue
unused_parameter(friend)
#ENDIF
#if USE_NRM_DLC
bDoDialogue = bDoDialogue
unused_parameter(friend)
#ENDIF
#if not USE_CLF_DLC
#if not USE_NRM_DLC
IF (friend.eChar = CHAR_FRANKLIN AND g_eDefaultPlayerChar = CHAR_TREVOR)
OR (friend.eChar = CHAR_TREVOR AND g_eDefaultPlayerChar = CHAR_FRANKLIN)
IF NOT GET_MISSION_COMPLETE_STATE(SP_MISSION_FBI_2)
IF bDoDialogue
IF NOT IS_ANY_SPEECH_PLAYING(friend.hPed)
IF g_eDefaultPlayerChar = CHAR_TREVOR
PLAY_PED_AMBIENT_SPEECH(friend.hPed, "GENERIC_FUCK_YOU", SPEECH_PARAMS_FORCE) // Treat Trevor as a homeless person
ELSE
PLAY_PED_AMBIENT_SPEECH(friend.hPed, "GENERIC_FUCK_YOU", SPEECH_PARAMS_FORCE) // Call Franklin a cunt
ENDIF
ENDIF
IF IS_ANY_SPEECH_PLAYING(friend.hPed)
RETURN TRUE
ENDIF
ELSE
RETURN TRUE
ENDIF
ENDIF
ENDIF
#ENDIF
#ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_HasPlayerAttackedAmbFriend(structAmbFriend& friend)
VEHICLE_INDEX hVehicle
IF IS_PED_IN_ANY_VEHICLE(friend.hPed)
hVehicle = GET_VEHICLE_PED_IS_IN(friend.hPed)
ENDIF
VECTOR vFriendPos = GET_ENTITY_COORDS(friend.hPed)
// Check if damage has occured
IF (NOT IS_PED_INJURED(PLAYER_PED_ID()) AND HAS_ENTITY_BEEN_DAMAGED_BY_ENTITY(friend.hPed, PLAYER_PED_ID()))
OR (NOT IS_PED_INJURED(PLAYER_PED_ID()) AND DOES_ENTITY_EXIST(hVehicle) AND HAS_ENTITY_BEEN_DAMAGED_BY_ENTITY(hVehicle, PLAYER_PED_ID()))
OR IS_PLAYER_SHOOTING_NEAR_PED(friend.hPed, FALSE, FALSE, FALSE)
// OR (GET_NUMBER_OF_FIRES_IN_RANGE(vFriendPos, 20.0) > 0)
OR IS_ENTITY_ON_FIRE(friend.hPed)
OR (IS_EXPLOSION_IN_SPHERE(EXP_TAG_DONTCARE, vFriendPos, 20.0) AND NOT ( IS_EXPLOSION_IN_SPHERE(EXP_TAG_DIR_STEAM, vFriendPos, 20.0)
OR IS_EXPLOSION_IN_SPHERE(EXP_TAG_DIR_WATER_HYDRANT, vFriendPos, 20.0)
OR IS_EXPLOSION_IN_SPHERE(EXP_TAG_DIR_GAS_CANISTER, vFriendPos, 20.0)))
// Clear damage
IF DOES_ENTITY_EXIST(hVehicle)
CLEAR_ENTITY_LAST_DAMAGE_ENTITY(hVehicle)
ENDIF
CLEAR_ENTITY_LAST_DAMAGE_ENTITY(friend.hPed)
CPRINTLN(DEBUG_FRIENDS, "Private_HasPlayerAttackedAmbFriend() - RETURNING TRUE ---------------------------------------")
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_HasAnyoneAttackedAmbFriend(structAmbFriend& friend, BOOL bCheckAngryPeds, PED_INDEX& hAngryPed)
// VEHICLE_INDEX hVehicle = NULL
// IF IS_PED_IN_ANY_VEHICLE(friend.hPed)
// hVehicle = GET_VEHICLE_PED_IS_IN(friend.hPed)
// ENDIF
// If anyone nearby is in combat with friend, or friend has been damaged by someone
IF (bCheckAngryPeds AND COUNT_PEDS_IN_COMBAT_WITH_TARGET_WITHIN_RADIUS(friend.hPed, GET_ENTITY_COORDS(friend.hPed), 75.0) > 0)
OR HAS_ENTITY_BEEN_DAMAGED_BY_ANY_PED(friend.hPed)
// Check 12 nearest peds
PED_INDEX nearbyPeds[12]
INT i, iNearbyCount = GET_PED_NEARBY_PEDS(friend.hPed, nearbyPeds)
REPEAT iNearbyCount i
hAngryPed = nearbyPeds[i]
IF NOT IS_PED_INJURED(hAngryPed)
// If ped is in combat with friend (and is near) or has damaged friend
IF IS_PED_IN_COMBAT(hAngryPed, friend.hPed)
IF HAS_ENTITY_BEEN_DAMAGED_BY_ENTITY(friend.hPed, hAngryPed)
// OR ( DOES_ENTITY_EXIST(hVehicle) AND HAS_ENTITY_BEEN_DAMAGED_BY_ENTITY(hVehicle, hAngryPed) )
OR (bCheckAngryPeds AND Util_IsPedInsideRangePed(hAngryPed, friend.hPed, 75.0))
CPRINTLN(DEBUG_FRIENDS, "Private_HasAnyoneAttackedAmbFriend() - RETURNING TRUE ---------------------------------------")
hAngryPed = nearbyPeds[i]
CLEAR_ENTITY_LAST_DAMAGE_ENTITY(friend.hPed)
// CLEAR_ENTITY_LAST_WEAPON_DAMAGE(friend.hPed)
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDREPEAT
hAngryPed = NULL
ENDIF
// CLEAR_ENTITY_LAST_DAMAGE_ENTITY(friend.hPed)
// CLEAR_ENTITY_LAST_WEAPON_DAMAGE(friend.hPed)
RETURN FALSE
ENDFUNC
FUNC BOOL Private_HasPlayerThreatenedAmbFriend(structAmbGroup& group, structAmbFriend& friend)
// Check if player is threatening ped
// IF IS_ENTITY_AT_ENTITY(friend.hPed, PLAYER_PED_ID(), <<15.0, 15.0, 15.0>>)//<<45.0, 45.0, 45.0>>)
IF IS_PLAYER_VISIBLY_TARGETTING_PED(friend.hPed, 170.0)
// Don't count as threaten if agreed to hangout with friend
IF friend.eState = AMBFRIEND_GREET
AND group.eConvChar = friend.eChar
IF group.eConvState = AMBCONV_OFFER_YES1
OR group.eConvState = AMBCONV_OFFER_YES2
OR group.eConvState = AMBCONV_OFFER_LAUNCH1
OR group.eConvState = AMBCONV_OFFER_LAUNCH2
RETURN FALSE
ENDIF
ENDIF
// Return true
CPRINTLN(DEBUG_FRIENDS, "Private_HasPlayerThreatenedAmbFriend() - RETURNING TRUE ---------------------------------------")
RETURN TRUE
ENDIF
// ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_HasPlayerStolenCarAmbFriend(structAmbGroup& group, structAmbFriend& friend)
// Check if player is threatening ped
IF IS_VEHICLE_DRIVEABLE(friend.hVehicle) AND IS_PED_IN_VEHICLE(PLAYER_PED_ID(), friend.hVehicle)
// Don't count as a jack if agreed to hangout with friend
IF friend.eState = AMBFRIEND_GREET
AND group.eConvChar = friend.eChar
IF group.eConvState = AMBCONV_OFFER_YES1
OR group.eConvState = AMBCONV_OFFER_YES2
OR group.eConvState = AMBCONV_OFFER_LAUNCH1
OR group.eConvState = AMBCONV_OFFER_LAUNCH2
RETURN FALSE
ENDIF
ENDIF
// Return true
CPRINTLN(DEBUG_FRIENDS, "Private_HasPlayerStolenCarAmbFriend() - RETURNING TRUE ---------------------------------------")
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_AmbFriendTasks_IsVehicleUsable(structAmbFriend& friend, VEHICLE_INDEX hVehicle, BOOL bMustBeDriver)
IF IS_VEHICLE_DRIVEABLE(hVehicle)
AND GET_ENTITY_HEALTH(hVehicle) > 600
AND NOT IS_ENTITY_UPSIDEDOWN(hVehicle)
AND GET_ENTITY_MODEL(hVehicle) <> CADDY
AND GET_ENTITY_MODEL(hVehicle) <> CADDY2
AND IS_ENTITY_AT_ENTITY(friend.hPed, hVehicle, <<150.0, 150.0, 150.0>>)
IF bMustBeDriver = FALSE OR (GET_PED_IN_VEHICLE_SEAT(GET_VEHICLE_PED_IS_IN(friend.hPed), VS_DRIVER) = friend.hPed)
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_IsPlayerJackable()
// Don't jack the player if player control is off
IF IS_PLAYER_CONTROL_ON(PLAYER_ID())
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
PROC Private_AmbFriendTasks_LeaveAnyVehicle(structAmbFriend& friend)
// Stop and get out
IF ABSF(GET_ENTITY_SPEED(friend.hPed)) > 5.0
IF GET_PED_IN_VEHICLE_SEAT(GET_VEHICLE_PED_IS_IN(friend.hPed), VS_DRIVER) = friend.hPed
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_VEHICLE_MISSION)
CLEAR_PED_TASKS(friend.hPed)
TASK_VEHICLE_MISSION(friend.hPed, GET_VEHICLE_PED_IS_IN(friend.hPed), NULL, MISSION_STOP, 0.0, DRIVINGMODE_STOPFORCARS, 0.0, 0.0)
ENDIF
ENDIF
ELSE
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_LEAVE_ANY_VEHICLE)
CLEAR_PED_TASKS(friend.hPed)
TASK_LEAVE_ANY_VEHICLE(friend.hPed)
ENDIF
ENDIF
ENDPROC
PROC Private_AmbFriendTasks_DriveWander(structAmbFriend& friend, FLOAT fSpeed, DRIVINGMODE mode=DRIVINGMODE_STOPFORCARS)
// Drive at given speed
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_VEHICLE_DRIVE_WANDER)
CLEAR_PED_TASKS(friend.hPed)
TASK_VEHICLE_DRIVE_WANDER(friend.hPed, GET_VEHICLE_PED_IS_IN(friend.hPed), fSpeed, mode)
ELSE
SET_DRIVE_TASK_CRUISE_SPEED(friend.hPed, fSpeed)
ENDIF
ENDPROC
PROC Private_AmbFriendTasks_JackVehicle(structAmbFriend& friend)
IF IS_ENTITY_AT_ENTITY(friend.hPed, friend.hVehicle, <<75.0, 75.0, 75.0>>)
VEHICLE_SEAT eSeat = GET_SEAT_PED_IS_IN(PLAYER_PED_ID())
IF eSeat = VS_DRIVER
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_ENTER_VEHICLE)
CLEAR_PED_TASKS(friend.hPed)
TASK_ENTER_VEHICLE(friend.hPed, friend.hVehicle, DEFAULT_TIME_NEVER_WARP, VS_DRIVER, PEDMOVE_RUN, ECF_RESUME_IF_INTERRUPTED|ECF_JACK_ANYONE|ECF_JUST_PULL_PED_OUT)
ENDIF
ELSE
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_TURN_PED_TO_FACE_ENTITY)
CLEAR_PED_TASKS(friend.hPed)
TASK_TURN_PED_TO_FACE_ENTITY(friend.hPed, PLAYER_PED_ID(), -1)
ENDIF
ENDIF
ELSE
friend.hVehicle = NULL
ENDIF
ENDPROC
PROC Private_AmbFriendTasks_EnterVehicle(structAmbFriend& friend)
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_ENTER_VEHICLE)
CLEAR_PED_TASKS(friend.hPed)
TASK_ENTER_VEHICLE(friend.hPed, friend.hVehicle, DEFAULT_TIME_NEVER_WARP, VS_DRIVER, PEDMOVE_WALK, ECF_RESUME_IF_INTERRUPTED|ECF_WARP_IF_DOOR_IS_BLOCKED|ECF_WARP_IF_SHUFFLE_LINK_IS_BLOCKED)
ENDIF
ENDPROC
PROC Private_AmbFriendTasks_WalkWander(structAmbFriend& friend)
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_WANDER_STANDARD)
CLEAR_PED_TASKS(friend.hPed)
TASK_WANDER_STANDARD(friend.hPed)
ENDIF
ENDPROC
PROC Private_AmbFriendTasks_Flee(structAmbFriend& friend, PED_INDEX hTargetPed)
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_SMART_FLEE_PED)
CLEAR_PED_TASKS(friend.hPed)
TASK_SMART_FLEE_PED(friend.hPed, hTargetPed, 1000.0, -1, FALSE)
ENDIF
ENDPROC
PROC Private_SetAmbFriendDismissMode(structAmbFriend& friend, enumAmbDismissMode eMode, BOOL bShout = FALSE, PED_INDEX hTargetPed = NULL)
#IF IS_DEBUG_BUILD
TEXT_LABEL_31 tChar = GetLabel_enumCharacterList(friend.eChar)
TEXT_LABEL_31 tMode = GetLabel_enumAmbDismissMode(eMode)
IF bShout
CPRINTLN(DEBUG_FRIENDS, "Private_SetAmbFriendDismissMode(", tChar, ", ", tMode, ", bShout=TRUE)")
ELSE
CPRINTLN(DEBUG_FRIENDS, "Private_SetAmbFriendDismissMode(", tChar, ", ", tMode, ", bShout=FALSE)")
ENDIF
#ENDIF
friend.bInitDismissMode = TRUE
friend.eDismissMode = eMode
friend.hTargetPed = hTargetPed
// If already dismissed, reset dismissed time
IF Private_GET_FRIEND_LAST_CONTACT_TYPE(g_eDefaultPlayerChar, friend.eChar) = FRIEND_CONTACT_DISMISSED
IF Private_GET_FRIEND_LAST_CONTACT_TIME(g_eDefaultPlayerChar, friend.eChar) < 3.0*60.0
RESET_FRIEND_LAST_CONTACT_TIMER(g_eDefaultPlayerChar, friend.eChar, FRIEND_CONTACT_DISMISSED)
ENDIF
ENDIF
IF bShout
friend.bDismissAudioRequest = TRUE
ENDIF
ENDPROC
PROC Private_SetAmbFriendThreatResponse(structAmbFriend& friend, BOOL bShout = FALSE, PED_INDEX hTargetPed = NULL)
IF friend.bIsVulnerable AND hTargetPed = PLAYER_PED_ID()
Private_SetAmbFriendDismissMode(friend, AMBMODE_FIGHT, bShout, hTargetPed)
ELSE
Private_SetAmbFriendDismissMode(friend, AMBMODE_FLEE, bShout, hTargetPed)
ENDIF
ENDPROC
FUNC BOOL Private_UpdateAmbFriend_Dismissed(structAmbGroup& group, structAmbFriend& friend, structPedsForConversation& convPeds, FLOAT fDist2)
PED_INDEX hAngryPed = NULL
//-- Tasks
//-------------------------------------------------------------------------------------------------------- AMBMODE_REJECTED
IF friend.eDismissMode = AMBMODE_REJECTED
// Attacked...
IF Private_HasPlayerAttackedAmbFriend(friend)
CLEAR_PED_TASKS(friend.hPed)
Private_SetAmbFriendThreatResponse(friend, TRUE, PLAYER_PED_ID())
ELIF Private_HasAnyoneAttackedAmbFriend(friend, TRUE, hAngryPed)
CLEAR_PED_TASKS(friend.hPed)
Private_SetAmbFriendDismissMode(friend, AMBMODE_FLEE, FALSE, hAngryPed)
// Finished rejection tasks...
ELIF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_PERFORM_SEQUENCE)
CLEAR_PED_TASKS(friend.hPed)
Private_SetAmbFriendDismissMode(friend, AMBMODE_WANDER)
ENDIF
//-------------------------------------------------------------------------------------------------------- AMBMODE_WANDER
ELIF friend.eDismissMode = AMBMODE_WANDER
IF friend.bDismissAudioRequest
IF NOT IS_ANY_SPEECH_PLAYING(friend.hPed) AND NOT IS_PED_RAGDOLL(friend.hPed) AND NOT IS_PED_RUNNING_RAGDOLL_TASK(friend.hPed)
PLAY_PED_AMBIENT_SPEECH(friend.hPed, "GENERIC_WHATEVER", SPEECH_PARAMS_FORCE)
IF IS_AMBIENT_SPEECH_PLAYING(friend.hPed)
CPRINTLN(DEBUG_FRIENDS, "AMBMODE_WANDER - PLAY_PED_AMBIENT_SPEECH(GENERIC_WHATEVER)")
friend.bDismissAudioRequest = FALSE
ENDIF
ENDIF
ENDIF
// Attacked...
IF Private_HasPlayerAttackedAmbFriend(friend)
CLEAR_PED_TASKS(friend.hPed)
Private_SetAmbFriendThreatResponse(friend, TRUE, PLAYER_PED_ID())
ELIF Private_HasAnyoneAttackedAmbFriend(friend, TRUE, hAngryPed)
CLEAR_PED_TASKS(friend.hPed)
Private_SetAmbFriendDismissMode(friend, AMBMODE_FLEE, FALSE, hAngryPed)
// In car...
ELIF IS_PED_IN_ANY_VEHICLE(friend.hPed)
// Car health ok, drive around
IF Private_AmbFriendTasks_IsVehicleUsable(friend, GET_VEHICLE_PED_IS_IN(friend.hPed), TRUE)
friend.hVehicle = GET_VEHICLE_PED_IS_IN(friend.hPed)
IF IS_POINT_ON_ROAD(GET_ENTITY_COORDS(friend.hPed), friend.hVehicle)
Private_AmbFriendTasks_DriveWander(friend, 17.0)
ELSE
Private_AmbFriendTasks_DriveWander(friend, 10.0)
ENDIF
// Car unusable, get out
ELSE
friend.hVehicle = NULL
Private_AmbFriendTasks_LeaveAnyVehicle(friend)
ENDIF
// On foot...
ELSE
// Car usable, get in
IF Private_AmbFriendTasks_IsVehicleUsable(friend, friend.hVehicle, FALSE)
// Go to vehicle and get in / jack player
IF NOT IS_PED_INJURED(PLAYER_PED_ID()) AND IS_PED_IN_VEHICLE(PLAYER_PED_ID(), friend.hVehicle)
IF GET_PLAYER_PED_PERSONAL_VEHICLE_BELONGS_TO(friend.hVehicle) = friend.eChar AND Private_IsPlayerJackable()
CLEAR_PED_TASKS(friend.hPed)
Private_SetAmbFriendDismissMode(friend, AMBMODE_JACKED, TRUE)
ELSE
friend.hVehicle = NULL
Private_AmbFriendTasks_WalkWander(friend)
ENDIF
ELSE
Private_AmbFriendTasks_EnterVehicle(friend)
ENDIF
// Car unusable, walk around
ELSE
friend.hVehicle = NULL
Private_AmbFriendTasks_WalkWander(friend)
ENDIF
ENDIF
//-------------------------------------------------------------------------------------------------------- AMBMODE_JACKED
ELIF friend.eDismissMode = AMBMODE_JACKED
IF friend.bDismissAudioRequest
IF NOT IS_ANY_SPEECH_PLAYING(friend.hPed) AND NOT IS_PED_RAGDOLL(friend.hPed) AND NOT IS_PED_RUNNING_RAGDOLL_TASK(friend.hPed)
PLAY_PED_AMBIENT_SPEECH(friend.hPed, "JACKED_GENERIC", SPEECH_PARAMS_FORCE)
IF IS_AMBIENT_SPEECH_PLAYING(friend.hPed)
CPRINTLN(DEBUG_FRIENDS, "AMBMODE_JACKED - PLAY_PED_AMBIENT_SPEECH(JACKED_GENERIC)")
friend.bDismissAudioRequest = FALSE
ENDIF
ENDIF
ENDIF
IF friend.iProgress < 1
friend.iProgress = 1
ENDIF
// Player control is off, so don't jack them...
IF NOT Private_IsPlayerJackable()
CPRINTLN(DEBUG_FRIENDS, "Aboted AMBMODE_JACKED ambfriend state, as cutscene started")
CLEAR_PED_TASKS(friend.hPed)
friend.hVehicle = NULL
Private_SetAmbFriendDismissMode(friend, AMBMODE_WANDER)
// Attacked...
ELIF Private_HasPlayerAttackedAmbFriend(friend)
CLEAR_PED_TASKS(friend.hPed)
Private_SetAmbFriendThreatResponse(friend, TRUE, PLAYER_PED_ID())
ELIF Private_HasAnyoneAttackedAmbFriend(friend, TRUE, hAngryPed)
CLEAR_PED_TASKS(friend.hPed)
Private_SetAmbFriendDismissMode(friend, AMBMODE_FLEE, FALSE, hAngryPed)
ELIF NOT Private_AmbFriendTasks_IsVehicleUsable(friend, friend.hVehicle, FALSE)
CLEAR_PED_TASKS(friend.hPed)
friend.hVehicle = NULL
Private_SetAmbFriendDismissMode(friend, AMBMODE_WANDER)
ELIF IS_PED_IN_ANY_VEHICLE(friend.hPed)
CLEAR_PED_TASKS(friend.hPed)
Private_SetAmbFriendDismissMode(friend, AMBMODE_WANDER)
ELIF IS_PED_INJURED(PLAYER_PED_ID()) OR NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), friend.hVehicle)
CLEAR_PED_TASKS(friend.hPed)
Private_SetAmbFriendDismissMode(friend, AMBMODE_WANDER)
ELSE
Private_AmbFriendTasks_JackVehicle(friend)
ENDIF
//-------------------------------------------------------------------------------------------------------- AMBMODE_FLEE
ELIF friend.eDismissMode = AMBMODE_FLEE
IF friend.bDismissAudioRequest
IF NOT IS_ANY_SPEECH_PLAYING(friend.hPed) AND NOT IS_PED_RAGDOLL(friend.hPed) AND NOT IS_PED_RUNNING_RAGDOLL_TASK(friend.hPed)
PLAY_PED_AMBIENT_SPEECH(friend.hPed, "EXTREME_GRIEFING" /*"GENERIC_INSULT_HIGH"*/, SPEECH_PARAMS_SHOUTED_CRITICAL)//SPEECH_PARAMS_FORCE_FRONTEND)//SPEECH_PARAMS_FORCE)
IF IS_AMBIENT_SPEECH_PLAYING(friend.hPed)
CPRINTLN(DEBUG_FRIENDS, "AMBMODE_FLEE - PLAY_PED_AMBIENT_SPEECH(EXTREME_GRIEFING)")
friend.bDismissAudioRequest = FALSE
ENDIF
ENDIF
ENDIF
IF friend.bInitDismissMode
CLEAR_PED_TASKS(friend.hPed)
friend.bInitDismissMode = FALSE
ENDIF
IF friend.hTargetPed = PLAYER_PED_ID()
IF friend.iProgress < 2
friend.iProgress = 2
ENDIF
ENDIF
// #IF IS_DEBUG_BUILD
// SET_DEBUG_LINES_AND_SPHERES_DRAWING_ACTIVE(TRUE)
// IF NOT IS_PED_INJURED(friend.hTargetPed)
// DRAW_DEBUG_LINE(GET_ENTITY_COORDS(friend.hPed), GET_ENTITY_COORDS(friend.hTargetPed), 0, 255, 175, 255)
// ELSE
// DRAW_DEBUG_SPHERE(GET_ENTITY_COORDS(friend.hPed), 1.0, 255, 0, 0, 255)
// ENDIF
// #ENDIF
// Target invalid...
IF IS_PED_INJURED(friend.hTargetPed)
Private_SetAmbFriendDismissMode(friend, AMBMODE_WANDER)
// In car...
ELIF IS_PED_IN_ANY_VEHICLE(friend.hPed)
IF Private_AmbFriendTasks_IsVehicleUsable(friend, GET_VEHICLE_PED_IS_IN(friend.hPed), TRUE)
friend.hVehicle = GET_VEHICLE_PED_IS_IN(friend.hPed)
IF Util_IsPedInsideRangePed(friend.hPed, friend.hTargetPed, 150.0)
INT iFleeMode = ENUM_TO_INT(DF_SwerveAroundAllCars)|ENUM_TO_INT(DF_SteerAroundStationaryCars)|ENUM_TO_INT(DF_SteerAroundPeds)|ENUM_TO_INT(DF_SteerAroundObjects)|ENUM_TO_INT(DF_GoOffRoadWhenAvoiding)|ENUM_TO_INT(DF_DriveIntoOncomingTraffic)|ENUM_TO_INT(DF_ChangeLanesAroundObstructions)
Private_AmbFriendTasks_DriveWander(friend, 55.0, INT_TO_ENUM(DRIVINGMODE, iFleeMode))//40
ELSE
CLEAR_PED_TASKS(friend.hPed)
Private_SetAmbFriendDismissMode(friend, AMBMODE_WANDER)
ENDIF
ELSE
friend.hVehicle = NULL
Private_AmbFriendTasks_LeaveAnyVehicle(friend)
ENDIF
// On foot...
ELSE
IF Util_IsPedInsideRangePed(friend.hPed, friend.hTargetPed, 150.0) OR IS_ENTITY_ON_SCREEN(friend.hPed)
Private_AmbFriendTasks_Flee(friend, friend.hTargetPed)
ELSE
CLEAR_PED_TASKS(friend.hPed)
Private_SetAmbFriendDismissMode(friend, AMBMODE_WANDER)
ENDIF
ENDIF
//-------------------------------------------------------------------------------------------------------- AMBMODE_FLEE
ELIF friend.eDismissMode = AMBMODE_FIGHT
IF friend.bDismissAudioRequest
IF NOT IS_ANY_SPEECH_PLAYING(friend.hPed) AND NOT IS_PED_RAGDOLL(friend.hPed) AND NOT IS_PED_RUNNING_RAGDOLL_TASK(friend.hPed)
PLAY_PED_AMBIENT_SPEECH(friend.hPed, "EXTREME_GRIEFING" /*"GENERIC_INSULT_HIGH"*/, SPEECH_PARAMS_SHOUTED_CRITICAL)//SPEECH_PARAMS_FORCE_FRONTEND)//SPEECH_PARAMS_FORCE)
IF IS_AMBIENT_SPEECH_PLAYING(friend.hPed)
CPRINTLN(DEBUG_FRIENDS, "AMBMODE_FIGHT - PLAY_PED_AMBIENT_SPEECH(EXTREME_GRIEFING)")
friend.bDismissAudioRequest = FALSE
ENDIF
ENDIF
ENDIF
IF friend.bInitDismissMode
CLEAR_PED_TASKS(friend.hPed)
// SET_CURRENT_PED_WEAPON(friend.hPed, WEAPONTYPE_UNARMED, TRUE)
friend.bInitDismissMode = FALSE
ENDIF
IF friend.hTargetPed = PLAYER_PED_ID()
IF friend.iProgress < 2
friend.iProgress = 2
ENDIF
ENDIF
// #IF IS_DEBUG_BUILD
// SET_DEBUG_LINES_AND_SPHERES_DRAWING_ACTIVE(TRUE)
// IF NOT IS_PED_INJURED(friend.hTargetPed)
// DRAW_DEBUG_LINE(GET_ENTITY_COORDS(friend.hPed), GET_ENTITY_COORDS(friend.hTargetPed), 255, 0, 70, 255)
// ELSE
// DRAW_DEBUG_SPHERE(GET_ENTITY_COORDS(friend.hPed), 1.0, 255, 0, 0, 255)
// ENDIF
// #ENDIF
// Target invalid...
IF IS_PED_INJURED(friend.hTargetPed)
Private_SetAmbFriendDismissMode(friend, AMBMODE_WANDER)
// In car...
ELIF IS_PED_IN_ANY_VEHICLE(friend.hPed)
Private_AmbFriendTasks_LeaveAnyVehicle(friend)
// On foot...
ELSE
IF NOT IS_PED_RAGDOLL(friend.hPed) AND NOT IS_PED_RUNNING_RAGDOLL_TASK(friend.hPed)
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_COMBAT)
CPRINTLN(DEBUG_FRIENDS, "--- AMBMODE_FIGHT - TASK_COMBAT_PED()")
CLEAR_PED_TASKS(friend.hPed)
TASK_COMBAT_PED(friend.hPed, friend.hTargetPed, COMBAT_PED_PREVENT_CHANGING_TARGET)
// ELSE
//
// IF GET_PEDS_CURRENT_WEAPON(friend.hPed) <> WEAPONTYPE_UNARMED
//
// IF GET_PEDS_CURRENT_WEAPON(friend.hTargetPed) = WEAPONTYPE_UNARMED
// GIVE_WEAPON_TO_PED(friend.hPed, WEAPONTYPE_UNARMED, 1, TRUE, TRUE)
// ENDIF
//
// ENDIF
ENDIF
ENDIF
// IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_COMBAT)
// IF NOT IS_PED_RAGDOLL(friend.hPed) AND NOT IS_PED_RUNNING_RAGDOLL_TASK(friend.hPed)
// CLEAR_PED_TASKS(friend.hPed)
// //IF GET_PEDS_CURRENT_WEAPON(friend.hTargetPed) = WEAPONTYPE_UNARMED
// GIVE_WEAPON_TO_PED(friend.hPed, WEAPONTYPE_UNARMED, 1, TRUE, TRUE)
// //ENDIF
// TASK_COMBAT_PED(friend.hPed, friend.hTargetPed, COMBAT_PED_PREVENT_CHANGING_TARGET)
// ENDIF
//// ELSE
//// IF GET_PEDS_CURRENT_WEAPON(friend.hTargetPed) <> WEAPONTYPE_UNARMED
//// IF GET_PEDS_CURRENT_WEAPON(friend.hPed) = WEAPONTYPE_UNARMED
//// WEAPON_TYPE eBestWeapon = GET_BEST_PED_WEAPON(friend.hPed)
//// IF eBestWeapon <> WEAPONTYPE_UNARMED
//// SET_CURRENT_PED_WEAPON(friend.hPed, eBestWeapon, TRUE)
//// ENDIF
//// ENDIF
//// ENDIF
// ENDIF
ENDIF
ENDIF
//-- Run dialogue sequence...
TEXT_LABEL tRoot, tBlock
// IF NOT IS_VEHICLE_DRIVEABLE(friend.hVehicle) OR NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), friend.hVehicle)
IF friend.eDismissMode <> AMBMODE_JACKED
AND friend.eDismissMode <> AMBMODE_FIGHT
AND (friend.eDismissMode <> AMBMODE_FLEE OR friend.hTargetPed = PLAYER_PED_ID())
IF GET_INTERIOR_FROM_ENTITY(PLAYER_PED_ID()) = NULL
AND GET_INTERIOR_FROM_ENTITY(friend.hPed) = NULL
IF NOT IS_PED_IN_ANY_VEHICLE(friend.hPed)
OR IS_PED_ON_ANY_BIKE(friend.hPed)
OR (IS_VEHICLE_A_CONVERTIBLE(GET_VEHICLE_PED_IS_IN(friend.hPed), TRUE) AND
(GET_CONVERTIBLE_ROOF_STATE(GET_VEHICLE_PED_IS_IN(friend.hPed)) = CRS_LOWERED OR GET_CONVERTIBLE_ROOF_STATE(GET_VEHICLE_PED_IS_IN(friend.hPed)) = CRS_ROOF_STUCK_LOWERED))
IF friend.iProgress = 0
// SET_DEBUG_LINES_AND_SPHERES_DRAWING_ACTIVE(TRUE)
friend.iProgress++
ELIF friend.iProgress = 1
IF Private_CanAmbFriendConverse(group)
IF Private_GET_FRIEND_LAST_CONTACT_TIME(g_eDefaultPlayerChar, friend.eChar) > 15.0
IF fDist2 < 8.0*8.0
IF Private_CanAmbFriendBlank(friend)
Private_GetFriendActivityPhrase(g_eDefaultPlayerChar, friend.eChar, FAP_AMBIENT_STALK_FT, tBlock, tRoot)
ELSE
Private_GetFriendActivityPhrase(g_eDefaultPlayerChar, friend.eChar, FAP_AMBIENT_STALK_1, tBlock, tRoot)
ENDIF
ADD_FRIEND_CHAR_FOR_DIALOGUE(convPeds, friend.eChar, friend.hPed, FALSE)
IF CREATE_CONVERSATION(convPeds, tBlock, tRoot, CONV_PRIORITY_AMBIENT_HIGH)
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_LOOK_AT_ENTITY)
TASK_LOOK_AT_ENTITY(friend.hPed, PLAYER_PED_ID(), 4000, SLF_WHILE_NOT_IN_FOV|SLF_USE_TORSO|SLF_WIDEST_PITCH_LIMIT, SLF_LOOKAT_VERY_HIGH)
ENDIF
RESET_FRIEND_LAST_CONTACT_TIMER(g_eDefaultPlayerChar, friend.eChar, FRIEND_CONTACT_DISMISSED)
friend.iProgress++
ENDIF
ENDIF
ENDIF
ENDIF
ELIF friend.iProgress = 2
IF Private_CanAmbFriendConverse(group)
IF Private_GET_FRIEND_LAST_CONTACT_TIME(g_eDefaultPlayerChar, friend.eChar) > 10.0
IF fDist2 < 8.0*8.0
IF Private_CanAmbFriendBlank(friend)
Private_GetFriendActivityPhrase(g_eDefaultPlayerChar, friend.eChar, FAP_AMBIENT_STALK_FT, tBlock, tRoot)
ELSE
Private_GetFriendActivityPhrase(g_eDefaultPlayerChar, friend.eChar, FAP_AMBIENT_STALK_2, tBlock, tRoot)
ENDIF
ADD_FRIEND_CHAR_FOR_DIALOGUE(convPeds, friend.eChar, friend.hPed, FALSE)
IF CREATE_CONVERSATION(convPeds, tBlock, tRoot, CONV_PRIORITY_AMBIENT_HIGH)
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_LOOK_AT_ENTITY)
TASK_LOOK_AT_ENTITY(friend.hPed, PLAYER_PED_ID(), 4000, SLF_WHILE_NOT_IN_FOV|SLF_USE_TORSO|SLF_WIDEST_PITCH_LIMIT, SLF_LOOKAT_VERY_HIGH)
ENDIF
RESET_FRIEND_LAST_CONTACT_TIMER(g_eDefaultPlayerChar, friend.eChar, FRIEND_CONTACT_DISMISSED)
friend.iProgress++
ENDIF
ENDIF
ENDIF
ENDIF
ELIF friend.iProgress = 3
IF Private_CanAmbFriendConverse(group)
IF Private_GET_FRIEND_LAST_CONTACT_TIME(g_eDefaultPlayerChar, friend.eChar) > 10.0
IF fDist2 < 8.0*8.0
IF Private_CanAmbFriendBlank(friend)
Private_GetFriendActivityPhrase(g_eDefaultPlayerChar, friend.eChar, FAP_AMBIENT_STALK_FT, tBlock, tRoot)
ELSE
Private_GetFriendActivityPhrase(g_eDefaultPlayerChar, friend.eChar, FAP_AMBIENT_STALK_3, tBlock, tRoot)
ENDIF
ADD_FRIEND_CHAR_FOR_DIALOGUE(convPeds, friend.eChar, friend.hPed, FALSE)
IF CREATE_CONVERSATION(convPeds, tBlock, tRoot, CONV_PRIORITY_AMBIENT_HIGH)
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_LOOK_AT_ENTITY)
TASK_LOOK_AT_ENTITY(friend.hPed, PLAYER_PED_ID(), 4000, SLF_WHILE_NOT_IN_FOV|SLF_USE_TORSO|SLF_WIDEST_PITCH_LIMIT, SLF_LOOKAT_VERY_HIGH)
ENDIF
RESET_FRIEND_LAST_CONTACT_TIMER(g_eDefaultPlayerChar, friend.eChar, FRIEND_CONTACT_DISMISSED)
friend.iProgress++
ENDIF
ENDIF
ENDIF
ENDIF
ELIF friend.iProgress = 4
IF fDist2 < 8.0*8.0
IF Private_GET_FRIEND_LAST_CONTACT_TIME(g_eDefaultPlayerChar, friend.eChar) > 10.0
CPRINTLN(DEBUG_FRIENDS, "Private_UpdateAmbFriend_Dismissed() wants to do knockout...")
IF GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH(HASH("appInternet")) = 0
IF g_bAllowAmbientFriendStalking
AND group.eConvState = AMBCONV_NULL
// AND NOT IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
AND (NOT IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID()) OR (IS_PED_ON_ANY_BIKE(PLAYER_PED_ID())) AND ABSF(GET_ENTITY_SPEED(PLAYER_PED_ID())) < 10.0)
AND NOT IS_PED_IN_ANY_VEHICLE(friend.hPed)
AND NOT IS_ENTITY_IN_AIR(PLAYER_PED_ID())
AND NOT IS_ENTITY_IN_AIR(friend.hPed)
AND NOT IS_PED_RAGDOLL(PLAYER_PED_ID())
AND NOT IS_PED_RAGDOLL(friend.hPed)
// AND GET_INTERIOR_FROM_ENTITY(PLAYER_PED_ID()) = NULL
// AND GET_INTERIOR_FROM_ENTITY(friend.hPed) = NULL
AND NOT IS_PLAYER_PED_SWITCH_IN_PROGRESS()
AND NOT IS_PLAYER_SWITCH_IN_PROGRESS()
AND Private_CheckAmbFriendOutroArea(group, friend)
RETURN TRUE
ENDIF
ENDIF
ENDIF
IF Private_GET_FRIEND_LAST_CONTACT_TIME(g_eDefaultPlayerChar, friend.eChar) > 15.0
IF Private_CanAmbFriendConverse(group)
IF Private_CanAmbFriendBlank(friend)
Private_GetFriendActivityPhrase(g_eDefaultPlayerChar, friend.eChar, FAP_AMBIENT_STALK_FT, tBlock, tRoot)
ELSE
Private_GetFriendActivityPhrase(g_eDefaultPlayerChar, friend.eChar, FAP_AMBIENT_STALK_3, tBlock, tRoot)
ENDIF
ADD_FRIEND_CHAR_FOR_DIALOGUE(convPeds, friend.eChar, friend.hPed, FALSE)
IF CREATE_CONVERSATION(convPeds, tBlock, tRoot, CONV_PRIORITY_AMBIENT_HIGH)
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_LOOK_AT_ENTITY)
TASK_LOOK_AT_ENTITY(friend.hPed, PLAYER_PED_ID(), 4000, SLF_WHILE_NOT_IN_FOV|SLF_USE_TORSO|SLF_WIDEST_PITCH_LIMIT, SLF_LOOKAT_VERY_HIGH)
ENDIF
RESET_FRIEND_LAST_CONTACT_TIMER(g_eDefaultPlayerChar, friend.eChar, FRIEND_CONTACT_DISMISSED)
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
//------------------------------------------------------------------------------------------------------------------------------
// AmbFriend - Outro state
//------------------------------------------------------------------------------------------------------------------------------
FUNC BOOL Private_TryStartAmbFriendOutro(structAmbGroup& group, structAmbFriend& friend)
CPRINTLN(DEBUG_FRIENDS, "Private_TryStartAmbFriendOutro()...")
m_enumMissionCandidateReturnValue allowLaunch = Request_Mission_Launch(group.hOutroCandidate, MCTID_MEET_CHARACTER, MISSION_TYPE_GRIEFING)
IF allowLaunch = MCRET_ACCEPTED
CPRINTLN(DEBUG_FRIENDS, "Private_TryStartAmbFriendOutro() - Started MISSION_TYPE_GRIEFING (candidate ", group.hOutroCandidate, ")")
// IF NOT GET_MISSION_FLAG()
// SET_MISSION_FLAG(TRUE)
// ENDIF
// Init vars
group.bOutroActive = TRUE
group.hOutroScenarioBlock = NULL
group.eOutroChar = friend.eChar
group.iOutroSpeechVariation = -1
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
PROC Private_GetAmbFriendOutroAnims(enumCharacterList eChar, FLOAT& fHitTime, TEXT_LABEL_63& tAnimDict, TEXT_LABEL_31& tPlayerAnim, TEXT_LABEL_31& tFriendAnim, TEXT_LABEL_31& tCameraAnim)
SWITCH eChar
CASE CHAR_MICHAEL
tAnimDict = "friends@frm@ig_2"
tPlayerAnim = "knockout_player"
tFriendAnim = "knockout_mic"
tCameraAnim = "knockout_cam"
fHitTime = 0.231//0.287
BREAK
CASE CHAR_FRANKLIN
tAnimDict = "friends@frf@ig_2"
tPlayerAnim = "knockout_plyr"
tFriendAnim = "knockout_fra"
tCameraAnim = "knockout_cam"
fHitTime = 0.224//0.255
BREAK
CASE CHAR_TREVOR FALLTHRU
DEFAULT
tAnimDict = "friends@frt@ig_2"
tPlayerAnim = "knockout_player"
tFriendAnim = "knockout_trevor"
tCameraAnim = "knockout_cam"
fHitTime = 0.388//0.386//0.4//0.415
BREAK
ENDSWITCH
ENDPROC
PROC Private_GetAmbFriendOutroFaceAnimsForPlayer(enumCharacterList ePlayerChar, enumCharacterList eFriendChar, TEXT_LABEL_63& tPlayerFaceDict, TEXT_LABEL_31& tPlayerFaceAnim)
IF ePlayerChar = CHAR_MICHAEL
IF eFriendChar = CHAR_FRANKLIN
tPlayerFaceDict = "FRIENDS@FRF@IG_2"
tPlayerFaceAnim = "KNOCKOUT_FRF_MIC_facial"
ELIF eFriendChar = CHAR_TREVOR
tPlayerFaceDict = "FRIENDS@FRT@IG_2"
tPlayerFaceAnim = "KNOCKOUT_FRT_MIC_facial"
ELSE
SCRIPT_ASSERT("Private_GetAmbFriendOutroFaceAnimsForPlayer() - Bad eFriendChar")
ENDIF
ELIF ePlayerChar = CHAR_FRANKLIN
IF eFriendChar = CHAR_MICHAEL
tPlayerFaceDict = "FRIENDS@FRM@IG_2"
tPlayerFaceAnim = "KNOCKOUT_FRM_FRA_facial"
ELIF eFriendChar = CHAR_TREVOR
tPlayerFaceDict = "FRIENDS@FRT@IG_2"
tPlayerFaceAnim = "KNOCKOUT_FRT_FRA_facial"
ELSE
SCRIPT_ASSERT("Private_GetAmbFriendOutroFaceAnimsForPlayer() - Bad eFriendChar")
ENDIF
ELIF ePlayerChar = CHAR_TREVOR
IF eFriendChar = CHAR_MICHAEL
tPlayerFaceDict = "FRIENDS@FRM@IG_2"
tPlayerFaceAnim = "KNOCKOUT_FRM_TRV_facial"
ELIF eFriendChar = CHAR_FRANKLIN
tPlayerFaceDict = "FRIENDS@FRF@IG_2"
tPlayerFaceAnim = "KNOCKOUT_FRF_TRV_facial"
ELSE
SCRIPT_ASSERT("Private_GetAmbFriendOutroFaceAnimsForPlayer() - Bad eFriendChar")
ENDIF
ELSE
SCRIPT_ASSERT("Private_GetAmbFriendOutroFaceAnimsForPlayer() - Bad ePlayerChar")
ENDIF
ENDPROC
PROC Private_GetAmbFriendOutroFaceAnimsForFriend(enumCharacterList eFriendChar, /*PED_INDEX hFriendPed,*/ INT& iVariation, TEXT_LABEL_63& tFriendFaceDict, TEXT_LABEL_31& tFriendFaceAnim, TEXT_LABEL_63& tFriendAudioStream, TEXT_LABEL_31& tSubtitleLabel, FLOAT& fSubtitleStart, INT& iSubtitleDuration)
BOOL bFirstTime = FALSE
IF iVariation <= 0
bFirstTime = TRUE
ENDIF
IF eFriendChar = CHAR_MICHAEL
IF iVariation <= 0
iVariation = GET_RANDOM_INT_IN_RANGE(1, 4)
ENDIF
tFriendFaceDict = "FRIENDS@FRM@IG_2"
tFriendFaceAnim = "MICHAEL_KNOCKS_OUT_ANYONE_0"
tFriendFaceAnim += iVariation
tFriendAudioStream = "MICHAEL_KNOCKOUT_FAFM_ANAA_0"
tFriendAudioStream += iVariation
tSubtitleLabel = "FrOUT_M_0"
tSubtitleLabel += iVariation
IF iVariation = 1
fSubtitleStart = 0.04
iSubtitleDuration = 1500
ELIF iVariation = 2
fSubtitleStart = 0.00
iSubtitleDuration = 1650
ELIF iVariation = 3
fSubtitleStart = 0.0
iSubtitleDuration = 1650
ELSE
fSubtitleStart = 0.0
iSubtitleDuration = 2000
ENDIF
ELIF eFriendChar = CHAR_FRANKLIN
IF iVariation <= 0
iVariation = GET_RANDOM_INT_IN_RANGE(1, 8)//5)
ENDIF
tFriendFaceDict = "FRIENDS@FRF@IG_2"
tFriendFaceAnim = "FRANKLIN_KNOCKS_OUT_ANYONE_0"
tFriendFaceAnim += iVariation
tFriendAudioStream = "FRANKLIN_KNOCKOUT_FAMF_APAA_0"
tFriendAudioStream += iVariation
tSubtitleLabel = "FrOUT_F_0"
tSubtitleLabel += iVariation
IF iVariation = 1
fSubtitleStart = 0.0
iSubtitleDuration = 2000
ELIF iVariation = 2
fSubtitleStart = 0.065
iSubtitleDuration = 2500
ELIF iVariation = 3
fSubtitleStart = 0.05
iSubtitleDuration = 1750
ELIF iVariation = 4
fSubtitleStart = 0.05
iSubtitleDuration = 2180
ELIF iVariation = 5
fSubtitleStart = 0.075
iSubtitleDuration = 1200
ELIF iVariation = 6
fSubtitleStart = 0.075
iSubtitleDuration = 1200
ELIF iVariation = 7
fSubtitleStart = 0.058
iSubtitleDuration = 1200
ELSE
fSubtitleStart = 0.0
iSubtitleDuration = 2000
ENDIF
ELIF eFriendChar = CHAR_TREVOR
IF iVariation <= 0
iVariation = GET_RANDOM_INT_IN_RANGE(1, 5)
ENDIF
tFriendFaceDict = "FRIENDS@FRT@IG_2"
tFriendFaceAnim = "TREVOR_KNOCKS_OUT_ANYONE_0"
tFriendFaceAnim += iVariation
tFriendAudioStream = "TREVOR_KNOCKOUT_FAMT_ANAA_0"
tFriendAudioStream += iVariation
tSubtitleLabel = "FrOUT_T_0"
tSubtitleLabel += iVariation
IF iVariation = 1
fSubtitleStart = 0.02
iSubtitleDuration = 2400
ELIF iVariation = 2
fSubtitleStart = 0.02
iSubtitleDuration = 2600
ELIF iVariation = 3
fSubtitleStart = 0.0
iSubtitleDuration = 2800
ELIF iVariation = 4
fSubtitleStart = 0.078
iSubtitleDuration = 1750
ELSE
fSubtitleStart = 0.0
iSubtitleDuration = 2000
ENDIF
ELSE
SCRIPT_ASSERT("Private_GetAmbFriendOutroFaceAnimsForFriend() - Bad eFriendChar")
tFriendFaceDict = ""
tFriendFaceAnim = ""
ENDIF
IF bFirstTime
#IF IS_DEBUG_BUILD
TEXT_LABEL_31 tChar = GetLabel_enumCharacterList(eFriendChar)
CPRINTLN(DEBUG_FRIENDS, "Private_GetAmbFriendOutroFaceAnimsForFriend(", tChar, ") - iVariation = ", iVariation, ", anim = ", tFriendFaceAnim)
#ENDIF
// INIT_SYNCH_SCENE_AUDIO_WITH_ENTITY(tFriendAudioStream, hFriendPed)
ENDIF
ENDPROC
FUNC BOOL Private_UpdateAmbFriend_Outro(structAmbGroup& group, structAmbFriend& friend)
// TEXT_LABEL tBlock, tRoot
TEXT_LABEL_63 tAnimDict
TEXT_LABEL_31 tPlayerAnim
TEXT_LABEL_31 tFriendAnim
TEXT_LABEL_31 tCameraAnim
TEXT_LABEL_63 tPlayerFaceDict
TEXT_LABEL_31 tPlayerFaceAnim
TEXT_LABEL_63 tFriendFaceDict
TEXT_LABEL_31 tFriendFaceAnim
TEXT_LABEL_63 tFriendAudioStream
TEXT_LABEL_31 tSubtitleLabel
FLOAT fSubtitleStart
INT iSubtitleDuration
FLOAT fHitTime
CPRINTLN(DEBUG_FRIENDS, "Private_UpdateAmbFriend_Outro() ", friend.iProgress, " - ", GET_FRAME_COUNT())
Private_GetAmbFriendOutroAnims(friend.eChar, fHitTime, tAnimDict, tPlayerAnim, tFriendAnim, tCameraAnim)
Private_GetAmbFriendOutroFaceAnimsForPlayer(g_eDefaultPlayerChar, friend.eChar, tPlayerFaceDict, tPlayerFaceAnim)
Private_GetAmbFriendOutroFaceAnimsForFriend(friend.eChar, /*friend.hPed,*/ group.iOutroSpeechVariation, tFriendFaceDict, tFriendFaceAnim, tFriendAudioStream, tSubtitleLabel, fSubtitleStart, iSubtitleDuration)
SET_THIS_IS_A_TRIGGER_SCRIPT(TRUE)
//-- Load anim dict and start scene...
IF friend.iProgress = 0
IF IS_SCREEN_FADED_OUT() OR IS_PED_INJURED(PLAYER_PED_ID()) OR IS_PED_INJURED(friend.hPed)
friend.iProgress++
ELSE
REQUEST_ANIM_DICT(tAnimDict)
REQUEST_ANIM_DICT(tPlayerFaceDict)
REQUEST_ANIM_DICT(tFriendFaceDict)
IF HAS_ANIM_DICT_LOADED(tAnimDict)
AND HAS_ANIM_DICT_LOADED(tPlayerFaceDict)
AND HAS_ANIM_DICT_LOADED(tFriendFaceDict)
IF PREPARE_SYNCHRONIZED_AUDIO_EVENT(tFriendAudioStream, 0)
// Calc scene position
VECTOR vPlayerToFriend = GET_ENTITY_COORDS(friend.hPed) - GET_ENTITY_COORDS(PLAYER_PED_ID())
VECTOR vRot = << 0.0, 0.0, GET_HEADING_FROM_VECTOR_2D(vPlayerToFriend.x, vPlayerToFriend.y) >>
VECTOR vAnchor = GET_ENTITY_COORDS(PLAYER_PED_ID())
GET_GROUND_Z_FOR_3D_COORD(vAnchor, vAnchor.z)
group.vOutroAnchor = vAnchor
// Start cutscene
RC_START_CUTSCENE_MODE(vAnchor, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE)
// Create cam
group.hOutroCamera = CREATE_CAM("DEFAULT_ANIMATED_CAMERA", TRUE)
// Remove bike if on one
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
VEHICLE_INDEX hBike = GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID())
IF DOES_ENTITY_EXIST(hBike)
SET_ENTITY_COORDS(PLAYER_PED_ID(), GET_ENTITY_COORDS(PLAYER_PED_ID()))
DELETE_VEHICLE(hBike)
ENDIF
ENDIF
// Clear area
CLEAR_AREA_OF_VEHICLES(vAnchor, 10.0, FALSE, FALSE, TRUE, TRUE)
CLEAR_AREA_OF_PEDS(vAnchor, 5.0)
group.hOutroScenarioBlock = ADD_SCENARIO_BLOCKING_AREA_FROM_POSITION_AND_RADIUS(vAnchor, 5.0)
CLEAR_PED_TASKS_IMMEDIATELY(PLAYER_PED_ID())
CLEAR_PED_TASKS_IMMEDIATELY(friend.hPed)
FORCE_PED_AI_AND_ANIMATION_UPDATE(PLAYER_PED_ID(), TRUE)
FORCE_PED_AI_AND_ANIMATION_UPDATE(friend.hPed, TRUE)
SET_CURRENT_PED_WEAPON(PLAYER_PED_ID(), WEAPONTYPE_UNARMED, TRUE)
SET_CURRENT_PED_WEAPON(friend.hPed, WEAPONTYPE_UNARMED, TRUE)
// Block paths
VECTOR vBlockMin = group.vOutroAnchor - <<10,10,10>>
VECTOR vBlockMax = group.vOutroAnchor + <<10,10,10>>
SET_ROADS_IN_AREA(vBlockMin, vBlockMax, FALSE)
SET_PED_PATHS_IN_AREA(vBlockMin, vBlockMax, FALSE)
// Create scene
group.hOutroScene = CREATE_SYNCHRONIZED_SCENE(vAnchor, vRot)
TASK_SYNCHRONIZED_SCENE(PLAYER_PED_ID(), group.hOutroScene, tAnimDict, tPlayerAnim, NORMAL_BLEND_IN, REALLY_SLOW_BLEND_OUT)
TASK_SYNCHRONIZED_SCENE(friend.hPed, group.hOutroScene, tAnimDict, tFriendAnim, NORMAL_BLEND_IN, REALLY_SLOW_BLEND_OUT)
PLAY_SYNCHRONIZED_CAM_ANIM(group.hOutroCamera, group.hOutroScene, tCameraAnim, tAnimDict)
PLAY_FACIAL_ANIM(PLAYER_PED_ID(), tPlayerFaceAnim, tPlayerFaceDict)
PLAY_FACIAL_ANIM(friend.hPed, tFriendFaceAnim, tFriendFaceDict)
PLAY_SYNCHRONIZED_AUDIO_EVENT(group.hOutroScene)
IF IS_SYNCHRONIZED_SCENE_HOLD_LAST_FRAME(group.hOutroScene)
SET_SYNCHRONIZED_SCENE_HOLD_LAST_FRAME(group.hOutroScene, FALSE)
ENDIF
// Enable camera
RENDER_SCRIPT_CAMS(TRUE, FALSE)
friend.iProgress++
ENDIF
ENDIF
ENDIF
ELIF friend.iProgress = 1
// Wait for impact time
IF NOT IS_SYNCHRONIZED_SCENE_RUNNING(group.hOutroScene) OR IS_SCREEN_FADED_OUT() OR IS_PED_INJURED(PLAYER_PED_ID()) OR IS_PED_INJURED(friend.hPed)
friend.iProgress++
ELIF GET_SYNCHRONIZED_SCENE_PHASE(group.hOutroScene) >= fSubtitleStart
IF IS_SUBTITLE_PREFERENCE_SWITCHED_ON()
PRINT_NOW(tSubtitleLabel, iSubtitleDuration, 0)
ENDIF
friend.iProgress++
ENDIF
ELIF friend.iProgress = 2
// Wait for impact time
IF NOT IS_SYNCHRONIZED_SCENE_RUNNING(group.hOutroScene) OR IS_SCREEN_FADED_OUT() OR IS_PED_INJURED(PLAYER_PED_ID()) OR IS_PED_INJURED(friend.hPed)
OR GET_SYNCHRONIZED_SCENE_PHASE(group.hOutroScene) >= fHitTime
friend.iProgress++
ENDIF
ELIF friend.iProgress = 3
// Kill player
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
SET_ENTITY_HEALTH(PLAYER_PED_ID(), 0)
ENDIF
// Cleanup when faded out
IF IS_SCREEN_FADED_OUT()
SET_THIS_IS_A_TRIGGER_SCRIPT(FALSE)
RETURN TRUE
ENDIF
ENDIF
SET_THIS_IS_A_TRIGGER_SCRIPT(FALSE)
RETURN FALSE
ENDFUNC
PROC Private_CleanupAmbFriendOutro(structAmbGroup& group)
CPRINTLN(DEBUG_FRIENDS, "Private_CleanupAmbFriendOutro()...")
TEXT_LABEL_63 tAnimDict
TEXT_LABEL_31 tPlayerAnim
TEXT_LABEL_31 tFriendAnim
TEXT_LABEL_31 tCameraAnim
FLOAT fHitTime
TEXT_LABEL_63 tPlayerFaceDict
TEXT_LABEL_31 tPlayerFaceAnim
Private_GetAmbFriendOutroAnims(group.eOutroChar, fHitTime, tAnimDict, tPlayerAnim, tFriendAnim, tCameraAnim)
Private_GetAmbFriendOutroFaceAnimsForPlayer(g_eDefaultPlayerChar, group.eOutroChar, tPlayerFaceDict, tPlayerFaceAnim)
// Clear tasks (in case aborting early)
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
CLEAR_PED_TASKS(PLAYER_PED_ID())
ENDIF
IF NOT IS_PED_INJURED(group.mFriends[group.eOutroChar].hPed)
CLEAR_PED_TASKS(group.mFriends[group.eOutroChar].hPed)
ENDIF
// Unblock scenario
IF group.hOutroScenarioBlock <> NULL
REMOVE_SCENARIO_BLOCKING_AREA(group.hOutroScenarioBlock)
group.hOutroScenarioBlock = NULL
ENDIF
// Unblock paths
VECTOR vBlockMin = group.vOutroAnchor - <<10,10,10>>
VECTOR vBlockMax = group.vOutroAnchor + <<10,10,10>>
SET_ROADS_BACK_TO_ORIGINAL(vBlockMin, vBlockMax)
SET_PED_PATHS_BACK_TO_ORIGINAL(vBlockMin, vBlockMax)
// Destroy cam
IF DOES_CAM_EXIST(group.hOutroCamera)
DESTROY_CAM(group.hOutroCamera)
ENDIF
RENDER_SCRIPT_CAMS(FALSE, FALSE)
// Unload resources
REMOVE_ANIM_DICT(tAnimDict)
// Restore control
RC_END_CUTSCENE_MODE(TRUE, FALSE)
IF (group.hOutroCandidate <> NO_CANDIDATE_ID)
CPRINTLN(DEBUG_FRIENDS, "Private_CleanupAmbFriendOutro() - Candidate ID: ", group.hOutroCandidate)
Mission_Over(group.hOutroCandidate)
ELSE
SCRIPT_ASSERT("Private_CleanupAmbFriendOutro() - No candidate ID")
ENDIF
group.hOutroCandidate = NO_CANDIDATE_ID
group.eOutroChar = NO_CHARACTER
group.bOutroActive = FALSE
ENDPROC
//-------------------------------------------------------------------------------------------------------------------------------------------
// AmbFriend - State utils
//-------------------------------------------------------------------------------------------------------------------------------------------
PROC Private_InitAmbFriend(structAmbFriend& friend, enumCharacterList eChar)
friend.eState = AMBFRIEND_NULL
friend.iProgress = 0
friend.eChar = eChar
friend.hPed = NULL
friend.hTargetPed = NULL
friend.eGrabMode = AMBGRAB_NORMAL
friend.eDismissMode = AMBMODE_WANDER
friend.bDismissAudioRequest = FALSE
friend.bInitDismissMode = FALSE
#IF IS_DEBUG_BUILD
friend.hDebugGrabFail = NULL
#ENDIF
friend.hOriginalGroup = RELGROUPHASH_NO_RELATIONSHIP
friend.hOriginalDefaultGroup = RELGROUPHASH_NO_RELATIONSHIP
friend.bIsVulnerable = FALSE
ENDPROC
PROC Private_SetAmbFriendState(structAmbGroup& group, structAmbFriend& friend, enumAmbFriendState eState, BOOL bVerbose = TRUE)
IF bVerbose
#IF IS_DEBUG_BUILD
TEXT_LABEL_63 tChar = GetLabel_enumCharacterList(friend.eChar)
TEXT_LABEL_63 tState = GetLabel_enumAmbFriendState(eState)
CPRINTLN(DEBUG_FRIENDS, "Private_SetAmbFriendState(", tChar, ", ", tState, ") ", GET_FRAME_COUNT())
IF group.bIsSwitchActive = FALSE
ENDIF
#ENDIF
ENDIF
// Exit old state
SWITCH friend.eState
CASE AMBFRIEND_GREET FALLTHRU
CASE AMBFRIEND_GREET_SAFEHOUSE FALLTHRU
CASE AMBFRIEND_GREET_OUTSIDE
IF NOT IS_PED_INJURED(friend.hPed)
TASK_CLEAR_LOOK_AT(friend.hPed)
ENDIF
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
TASK_CLEAR_LOOK_AT(PLAYER_PED_ID())
ENDIF
group.iGreetCount--
BREAK
CASE AMBFRIEND_OUTRO
Private_CleanupAmbFriendOutro(group)
BREAK
ENDSWITCH
// Set new state
friend.eState = eState
friend.iProgress = 0
IF friend.eState = AMBFRIEND_GREET
OR friend.eState = AMBFRIEND_GREET_SAFEHOUSE
OR friend.eState = AMBFRIEND_GREET_OUTSIDE
group.iGreetCount++
ELIF friend.eState = AMBFRIEND_DISMISSED
friend.eDismissMode = AMBMODE_WANDER
friend.bInitDismissMode = TRUE
friend.bDismissAudioRequest = FALSE
ENDIF
ENDPROC
//-------------------------------------------------------------------------------------------------------------------------------------------
// AmbFriend - Group utils
//-------------------------------------------------------------------------------------------------------------------------------------------
FUNC BOOL Private_IsAmbFriendDismissed(structAmbFriend& friend)
enumFriendContactType eContactType = Private_GET_FRIEND_LAST_CONTACT_TYPE(g_eDefaultPlayerChar, friend.eChar)
IF eContactType = FRIEND_CONTACT_DISMISSED
IF Private_GET_FRIEND_LAST_CONTACT_TIME(g_eDefaultPlayerChar, friend.eChar) < 3.0*60.0
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_IsAmbFriendContactRecent(structAmbFriend& friend)
enumFriendContactType eContactType = Private_GET_FRIEND_LAST_CONTACT_TYPE(g_eDefaultPlayerChar, friend.eChar)
IF eContactType = FRIEND_CONTACT_FACE
OR eContactType = FRIEND_CONTACT_DISMISSED
IF Private_GET_FRIEND_LAST_CONTACT_TIME(g_eDefaultPlayerChar, friend.eChar) < 6.0*60.0
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_IsAmbFriendDismissedByAnyone(structAmbFriend& friend)
enumCharacterList eLoopChar
REPEAT NUM_OF_PLAYABLE_PEDS eLoopChar
IF eLoopChar <> friend.eChar
IF Private_GET_FRIEND_LAST_CONTACT_TYPE(eLoopChar, friend.eChar) = FRIEND_CONTACT_DISMISSED
IF Private_GET_FRIEND_LAST_CONTACT_TIME(eLoopChar, friend.eChar) < 3.0*60.0
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDREPEAT
RETURN FALSE
ENDFUNC
FUNC BOOL Private_IsAmbGroupAllowed()
IF IS_PED_INJURED(PLAYER_PED_ID())
OR IS_PLAYER_BEING_ARRESTED(PLAYER_ID())
// OR IS_PED_FALLING(PLAYER_PED_ID())
OR g_bPlayerIsInTaxi
OR IS_TRANSITION_ACTIVE()
OR IS_SCRIPT_HUD_DISPLAYING(HUDPART_TRANSITIONHUD)
OR IS_RESULT_SCREEN_DISPLAYING()
OR IS_PLAYER_BROWSING_ITEMS_IN_ANY_SHOP()
OR NOT IS_PLAYER_CONTROL_ON(PLAYER_ID())
// OR NOT IS_SCREEN_FADED_IN()
// OR IS_PED_RAGDOLL(PLAYER_PED_ID())
// OR IS_PED_IN_COMBAT(PLAYER_PED_ID()) // TODO: Think about this one
RETURN FALSE
ENDIF
RETURN TRUE
ENDFUNC
//FUNC BOOL Private_IsAmbGroupOutOfRange(structAmbGroup& group)
// VECTOR vPlayerPos = GET_ENTITY_COORDS(PLAYER_PED_ID())
//
// BOOL bAreAnyInRange = FALSE
// enumCharacterList eLoopChar
// REPEAT NUM_OF_PLAYABLE_PEDS eLoopChar
// IF eLoopChar <> g_eDefaultPlayerChar
// IF NOT IS_PED_INJURED(group.mFriends[eLoopChar].hPed)
// FLOAT fDist2 = VDIST2(vPlayerPos, GET_ENTITY_COORDS(group.mFriends[eLoopChar].hPed))
// IF fDist2 < 30.0*30.0//20.0*20.0//50.0*50.0//
// bAreAnyInRange = TRUE
// ENDIF
// ENDIF
// ENDIF
// ENDREPEAT
//
// IF bAreAnyInRange = TRUE
// RETURN FALSE
// ENDIF
//
// RETURN TRUE
//ENDFUNC
FUNC BOOL Private_CanAmbFriendGreet(structAmbGroup& group, structAmbFriend& friend)
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
// Do LOS test to player
IF (friend.hLosTest = NULL)
INT iFlags = SCRIPT_INCLUDE_MOVER|SCRIPT_INCLUDE_VEHICLE|SCRIPT_INCLUDE_PED|SCRIPT_INCLUDE_OBJECT|SCRIPT_INCLUDE_FOLIAGE
VECTOR vPlayerHead = GET_PED_BONE_COORDS(PLAYER_PED_ID(), BONETAG_HEAD, <<0.0, 0.0, 0.0>>)
VECTOR vFriendHead = GET_PED_BONE_COORDS(friend.hPed, BONETAG_HEAD, <<0.0, 0.0, 0.0>>)
VEHICLE_INDEX hPlayerVehicle = NULL
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
hPlayerVehicle = GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID())
ENDIF
friend.hLosTest = START_SHAPE_TEST_LOS_PROBE(vPlayerHead, vFriendHead, iFlags, hPlayerVehicle, SCRIPT_SHAPETEST_OPTION_DEFAULT)
ELSE
INT iHitCount
VECTOR vHitPoint, vHitNormal
ENTITY_INDEX hHitEntity
SHAPETEST_STATUS eStatus = GET_SHAPE_TEST_RESULT(friend.hLosTest, iHitCount, vHitPoint, vHitNormal, hHitEntity)
IF eStatus = SHAPETEST_STATUS_RESULTS_READY
VEHICLE_INDEX hFriendVehicle = NULL
IF IS_PED_IN_ANY_VEHICLE(friend.hPed)
hFriendVehicle = GET_VEHICLE_PED_IS_IN(friend.hPed)
ENDIF
// TEXT_LABEL_63 tText = "HtCnt:"
// tText += iHitCount
// tText += ", HtExst:"
// IF DOES_ENTITY_EXIST(hHitEntity) tText += "T"
// ELSE tText += "F" ENDIF
// tText += ", HtEnt:"
// tText += NATIVE_TO_INT(hHitEntity)
// tText += ", CrEnt:"
// tText += NATIVE_TO_INT(hFriendVehicle)
// DISPLAY_TEXT_WITH_LITERAL_STRING(0.1, 0.1, "STRING", tText)
IF iHitCount = 0
OR (DOES_ENTITY_EXIST(hHitEntity) AND hHitEntity = GET_ENTITY_FROM_PED_OR_VEHICLE(friend.hPed))
OR (DOES_ENTITY_EXIST(hHitEntity) AND hHitEntity = GET_ENTITY_FROM_PED_OR_VEHICLE(hFriendVehicle))
friend.iLosTestTime = GET_GAME_TIMER()
ENDIF
friend.hLosTest = NULL
ELIF eStatus = SHAPETEST_STATUS_NONEXISTENT
friend.hLosTest = NULL
ENDIF
ENDIF
// // Debug - how long since last sighting was registered
// INT iTime = GET_GAME_TIMER() - friend.iLosTestTime
// TEXT_LABEL tTime
// IF iTime < 500
// tTime = "YES "
// ELSE
// tTime = "NO "
// ENDIF
// tTime += iTime
// DISPLAY_TEXT_WITH_LITERAL_STRING(0.1, 0.1, "STRING", tTime)
// If sighting has been registered in the last 0.5 seconds
IF GET_GAME_TIMER() - friend.iLosTestTime < 500
IF (NOT IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID()) OR ABSF(GET_ENTITY_SPEED(PLAYER_PED_ID())) < 10.0)
AND (NOT IS_PED_IN_ANY_VEHICLE(friend.hPed) OR ABSF(GET_ENTITY_SPEED(friend.hPed)) < 10.0)
IF NOT IS_PED_RAGDOLL(friend.hPed)
// Can pass in distance to another ped, but defaults to checking if near to player
FLOAT fDist2 = VDIST2(GET_ENTITY_COORDS(PLAYER_PED_ID()), GET_ENTITY_COORDS(friend.hPed))
FLOAT fRange = 13.0
IF Private_CanAmbFriendBlank(friend)
fRange = 5.0//6.0
ELIF group.iGreetCount > 0
fRange = 25.0
ENDIF
IF fDist2 < fRange*fRange
IF NOT Private_IsAmbFriendDismissed(friend)
IF friend.eState = AMBFRIEND_DISMISSED
IF friend.eDismissMode = AMBMODE_FIGHT
OR friend.eDismissMode = AMBMODE_FLEE
RETURN FALSE
ENDIF
ENDIF
IF IS_ENTITY_ON_SCREEN(friend.hPed) AND NOT IS_ENTITY_OCCLUDED(friend.hPed)
IF Private_IsAmbGroupAllowed()
AND g_flowUnsaved.bFlowControllerBusy = FALSE
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_CanAmbFriendSafehouseGreet(structAmbFriend& friend, BOOL bMustBeInTrailer, FLOAT fDist)
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
BOOL bIsInTrailer = FALSE
IF GET_INTERIOR_FROM_ENTITY(PLAYER_PED_ID()) <> NULL
bIsInTrailer = TRUE
ENDIF
IF bMustBeInTrailer = bIsInTrailer
// Can pass in distance to another ped, but defaults to checking if near to player
FLOAT fDist2 = VDIST2(GET_ENTITY_COORDS(PLAYER_PED_ID()), GET_ENTITY_COORDS(friend.hPed))
FLOAT fRange = fDist//30.0
IF fDist2 < fRange*fRange
IF Private_IsAmbGroupAllowed()
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_CanAmbFriendHangout(structAmbFriend& friend)
IF (g_eFriendMissionZoneState = FRIEND_MISSION_ZONE_OFF OR IS_BIT_SET(g_iFriendMissionZoneAcceptBitset, ENUM_TO_INT(friend.eChar)))
AND NOT IS_FRIEND_BLOCK_FLAG_SET(g_eDefaultPlayerChar, friend.eChar, FRIEND_BLOCK_FLAG_HIATUS)
AND NOT IS_FRIEND_BLOCK_FLAG_SET(g_eDefaultPlayerChar, friend.eChar, FRIEND_BLOCK_FLAG_CLASH)
AND g_savedGlobals.sCommsControlData.eCharacterPriorityLevel[g_eDefaultPlayerChar] <> CPR_VERY_HIGH // TODO: Think about this one
// AND NOT ARE_ANY_CONNECTIONS_PENDING_OR_PHONE() // TODO: Think about this one
// AND NOT IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
// AND NOT IS_PED_IN_ANY_VEHICLE(friend.hPed)
// AND NOT g_isPlayerDrunk
// Make sure connections are ok
enumFriendConnection eDesiredConnection
enumFriendConnection eActiveConnection
IF GET_CONNECTION(g_eDefaultPlayerChar, friend.eChar, eDesiredConnection)
INT iActiveConnections = GET_PENDING_OR_ACTIVE_CONNECTIONS(eActiveConnection)
IF eActiveConnection <> eDesiredConnection
IF iActiveConnections = 0
RETURN TRUE
ELIF iActiveConnections = 1
enumFriendConnection eCommonConnection
IF GET_COMMON_CONNECTION(eDesiredConnection, eActiveConnection, eCommonConnection)
AND GET_CONNECTION_STATE(eCommonConnection) = FC_STATE_ContactWait
IF g_SavedGlobals.sFriendsData.g_FriendConnectData[eCommonConnection].blockBits = 0
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
#if USE_CLF_DLC
FUNC BOOL Private_IsPlayerAtOwnSafehouseCLF()
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
enumCharacterList ePlayerChar = GET_CURRENT_PLAYER_PED_ENUM()
IF ePlayerChar = CHAR_MICHAEL
IF Is_Savehouse_Respawn_Available(SAVEHOUSEclf_MICHAEL_BH)
IF IS_ENTITY_AT_COORD(PLAYER_PED_ID(), <<-816.692261,169.942932,70.514229>>, <<115.0,115.0,115.0>>)//80.0)
RETURN TRUE
ENDIF
ENDIF
IF Is_Savehouse_Respawn_Available(SAVEHOUSEclf_MICHAEL_CS)
IF IS_ENTITY_AT_COORD(PLAYER_PED_ID(), <<1977.231934,3812.881348,31.357803>>, <<150.0,150.0,150.0>>)//80.0)
RETURN TRUE
ENDIF
ENDIF
ELIF ePlayerChar = CHAR_FRANKLIN
IF Is_Savehouse_Respawn_Available(SAVEHOUSEclf_FRANKLIN_SC)
VECTOR vMin = <<-31.631046,-1459.370483,35.043587>> - <<84.500000,50.250000,9.000000>>
VECTOR vMax = <<-31.631046,-1459.370483,35.043587>> + <<84.500000,50.250000,9.000000>>
IF IS_ENTITY_IN_AREA(PLAYER_PED_ID(), vMin, vMax)
RETURN TRUE
ENDIF
// IF IS_ENTITY_AT_COORD(PLAYER_PED_ID(), <<-31.631046,-1459.370483,35.043587>>, 150.0)//80.0)
// RETURN TRUE
// ENDIF
ENDIF
IF Is_Savehouse_Respawn_Available(SAVEHOUSEclf_FRANKLIN_VH)
VECTOR vMin = <<4.536090,528.395142,158.115036>> - <<152.500000,74.750000,28.500000>>
VECTOR vMax = <<4.536090,528.395142,158.115036>> + <<152.500000,74.750000,28.500000>>
IF IS_ENTITY_IN_AREA(PLAYER_PED_ID(), vMin, vMax)
RETURN TRUE
ENDIF
ENDIF
ELIF ePlayerChar = CHAR_TREVOR
IF Is_Savehouse_Respawn_Available(SAVEHOUSEclf_TREVOR_CS)
IF IS_ENTITY_AT_COORD(PLAYER_PED_ID(), <<1977.231934,3812.881348,31.357803>>, <<150.0,150.0,150.0>>)//80.0)
RETURN TRUE
ENDIF
ENDIF
IF Is_Savehouse_Respawn_Available(SAVEHOUSEclf_TREVOR_VB)
IF IS_ENTITY_AT_COORD(PLAYER_PED_ID(), <<-1155.240479,-1519.908447,3.350527>>, <<150.0,150.0,150.0>>)//80.0)
RETURN TRUE
ENDIF
ENDIF
IF Is_Savehouse_Respawn_Available(SAVEHOUSEclf_TREVOR_SC)
IF IS_ENTITY_AT_COORD(PLAYER_PED_ID(), <<131.592010,-1303.622437,28.226076>>, <<100.0,100.0,100.0>>)
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
#endif
#if USE_NRM_DLC
FUNC BOOL Private_IsPlayerAtOwnSafehouseNRM()
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
enumCharacterList ePlayerChar = GET_CURRENT_PLAYER_PED_ENUM()
IF ePlayerChar = CHAR_MICHAEL
IF Is_Savehouse_Respawn_Available(SAVEHOUSENRM_BH)
IF IS_ENTITY_AT_COORD(PLAYER_PED_ID(), <<-816.692261,169.942932,70.514229>>, <<115.0,115.0,115.0>>)//80.0)
RETURN TRUE
ENDIF
ENDIF
IF Is_Savehouse_Respawn_Available(SAVEHOUSENRM_CHATEAU)
IF IS_ENTITY_AT_COORD(PLAYER_PED_ID(), <<1977.231934,3812.881348,31.357803>>, <<150.0,150.0,150.0>>)//80.0)
RETURN TRUE
ENDIF
ENDIF
ELIF ePlayerChar = CHAR_FRANKLIN
ELIF ePlayerChar = CHAR_TREVOR
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
#endif
FUNC BOOL Private_IsPlayerAtOwnSafehouse()
#if USE_CLF_DLC
return Private_IsPlayerAtOwnSafehouseCLF()
#endif
#if USE_NRM_DLC
return Private_IsPlayerAtOwnSafehouseNRM()
#endif
#if not USE_CLF_DLC
#if not USE_NRM_DLC
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
enumCharacterList ePlayerChar = GET_CURRENT_PLAYER_PED_ENUM()
IF ePlayerChar = CHAR_MICHAEL
IF Is_Savehouse_Respawn_Available(SAVEHOUSE_MICHAEL_BH)
IF IS_ENTITY_AT_COORD(PLAYER_PED_ID(), <<-816.692261,169.942932,70.514229>>, <<115.0,115.0,115.0>>)//80.0)
RETURN TRUE
ENDIF
ENDIF
IF Is_Savehouse_Respawn_Available(SAVEHOUSE_MICHAEL_CS)
IF IS_ENTITY_AT_COORD(PLAYER_PED_ID(), <<1977.231934,3812.881348,31.357803>>, <<150.0,150.0,150.0>>)//80.0)
RETURN TRUE
ENDIF
ENDIF
ELIF ePlayerChar = CHAR_FRANKLIN
IF Is_Savehouse_Respawn_Available(SAVEHOUSE_FRANKLIN_SC)
VECTOR vMin = <<-31.631046,-1459.370483,35.043587>> - <<84.500000,50.250000,9.000000>>
VECTOR vMax = <<-31.631046,-1459.370483,35.043587>> + <<84.500000,50.250000,9.000000>>
IF IS_ENTITY_IN_AREA(PLAYER_PED_ID(), vMin, vMax)
RETURN TRUE
ENDIF
// IF IS_ENTITY_AT_COORD(PLAYER_PED_ID(), <<-31.631046,-1459.370483,35.043587>>, 150.0)//80.0)
// RETURN TRUE
// ENDIF
ENDIF
IF Is_Savehouse_Respawn_Available(SAVEHOUSE_FRANKLIN_VH)
VECTOR vMin = <<4.536090,528.395142,158.115036>> - <<152.500000,74.750000,28.500000>>
VECTOR vMax = <<4.536090,528.395142,158.115036>> + <<152.500000,74.750000,28.500000>>
IF IS_ENTITY_IN_AREA(PLAYER_PED_ID(), vMin, vMax)
RETURN TRUE
ENDIF
ENDIF
ELIF ePlayerChar = CHAR_TREVOR
IF Is_Savehouse_Respawn_Available(SAVEHOUSE_TREVOR_CS)
IF IS_ENTITY_AT_COORD(PLAYER_PED_ID(), <<1977.231934,3812.881348,31.357803>>, <<150.0,150.0,150.0>>)//80.0)
RETURN TRUE
ENDIF
ENDIF
IF Is_Savehouse_Respawn_Available(SAVEHOUSE_TREVOR_VB)
IF IS_ENTITY_AT_COORD(PLAYER_PED_ID(), <<-1155.240479,-1519.908447,3.350527>>, <<150.0,150.0,150.0>>)//80.0)
RETURN TRUE
ENDIF
ENDIF
IF Is_Savehouse_Respawn_Available(SAVEHOUSE_TREVOR_SC)
IF IS_ENTITY_AT_COORD(PLAYER_PED_ID(), <<131.592010,-1303.622437,28.226076>>, <<100.0,100.0,100.0>>)
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
#endif
#endif
ENDFUNC
FUNC enumAmbConvState Private_GetAmbFriendConvRequest(structAmbFriend& friend)
enumFriendConnection eConnection
IF GET_CONNECTION(g_eDefaultPlayerChar, friend.eChar, eConnection)
#if not USE_CLF_DLC
#if not USE_NRM_DLC
IF friend.eGrabMode = AMBGRAB_SAFEHOUSE_TRAILER // Safehouse: Trevor says "Oh, it's you again"
OR friend.eGrabMode = AMBGRAB_SAFEHOUSE_OUTSIDE // Safehouse: Trevor says "Oh, it's you again"
RETURN AMBCONV_SAFEHOUSE_TRAILER1
ELIF friend.eGrabMode = AMBGRAB_SAFEHOUSE_DRUNK // Safehouse: Says nothing (drunk)
RETURN AMBCONV_SAFEHOUSE_TRAILER3
ELIF eConnection = FC_MICHAEL_FRANKLIN // Set piece: Michael/Franklin after showroom crash
AND NOT GET_MISSION_COMPLETE_STATE(SP_MISSION_FAMILY_1)
IF NOT IS_BIT_SET(g_SavedGlobals.sFriendsData.g_iAmbChatBitfield, ENUM_TO_INT(AMBCHATBIT_MFSHOWROOM))
RETURN AMBCONV_MFSHOWROOM_HELLO
ELSE
RETURN AMBCONV_DEFBLOCK_HELLO
ENDIF
ELIF eConnection = FC_TREVOR_MICHAEL // Set piece: Michael/Trevor whilst fallen out
AND GET_MISSION_FLOW_FLAG_STATE(FLOWFLAG_MICHAEL_TREVOR_HAVE_FALLEN_OUT)
IF NOT IS_BIT_SET(g_SavedGlobals.sFriendsData.g_iAmbChatBitfield, ENUM_TO_INT(AMBCHATBIT_MTFALLOUT))
RETURN AMBCONV_MTFALLOUT_HELLO
ELSE
RETURN AMBCONV_ANGRY_HELLO
ENDIF
ELIF eConnection = FC_FRANKLIN_TREVOR // Set piece: Franklin/Trevor after killing Michael
AND GET_MISSION_FLOW_FLAG_STATE(FLOWFLAG_MICHAEL_KILLED)
IF NOT IS_BIT_SET(g_SavedGlobals.sFriendsData.g_iAmbChatBitfield, ENUM_TO_INT(AMBCHATBIT_KILLEDM))
RETURN AMBCONV_KILLEDM_HELLO
ELSE
RETURN AMBCONV_ANGRY_HELLO
ENDIF
ELIF eConnection = FC_MICHAEL_FRANKLIN // Set piece: Franklin/Michael after killing Trevor
AND GET_MISSION_FLOW_FLAG_STATE(FLOWFLAG_TREVOR_KILLED)
IF NOT IS_BIT_SET(g_SavedGlobals.sFriendsData.g_iAmbChatBitfield, ENUM_TO_INT(AMBCHATBIT_KILLEDT))
RETURN AMBCONV_KILLEDT_HELLO
ELSE
RETURN AMBCONV_ANGRY_HELLO
ENDIF
ELIF IS_FRIEND_BLOCK_FLAG_SET(g_eDefaultPlayerChar, friend.eChar, FRIEND_BLOCK_FLAG_MISSION) // Friend activity blocked by mission
IF PRIVATE_FriendAcceptsPlayer_missionBlock_AnswerPhone(GET_FRIEND_FROM_CHAR(g_eDefaultPlayerChar), GET_FRIEND_FROM_CHAR(friend.eChar))
IF Private_IsPlayerAtOwnSafehouse() RETURN AMBCONV_MISBLOCK_HELLO_HOME
ELSE RETURN AMBCONV_MISBLOCK_HELLO
ENDIF
ELSE
IF Private_IsPlayerAtOwnSafehouse() RETURN AMBCONV_DEFBLOCK_HELLO_HOME // If Michael/Trevor blocked for Michael1, don't talk as if they know there's a mission coming
ELSE RETURN AMBCONV_DEFBLOCK_HELLO
ENDIF
ENDIF
ELIF Private_CanAmbFriendHangout(friend) // Friend activity available
IF Private_IsPlayerAtOwnSafehouse() RETURN AMBCONV_OFFER_HELLO_HOME
ELSE RETURN AMBCONV_OFFER_HELLO
ENDIF
ELSE // Default: "I'm busy"
IF Private_IsPlayerAtOwnSafehouse() RETURN AMBCONV_DEFBLOCK_HELLO_HOME
ELSE RETURN AMBCONV_DEFBLOCK_HELLO
ENDIF
ENDIF
#endif
#endif
ENDIF
// TODO: Might not need to assert here, just means that conv won't be counted when sorting highest priority?
#IF IS_DEBUG_BUILD
TEXT_LABEL_63 tPlayer = GetLabel_enumCharacterList(g_eDefaultPlayerChar)
TEXT_LABEL_63 tFriend = GetLabel_enumCharacterList(friend.eChar)
CPRINTLN(DEBUG_FRIENDS, "Private_GetAmbFriendConvRequest() - No connection between chars: ", tPlayer, "/", tFriend)
SCRIPT_ASSERT("Private_GetAmbFriendConvRequest() - No connection between chars")
#ENDIF
RETURN AMBCONV_NULL
ENDFUNC
PROC Private_DismissAmbFriend(structAmbGroup& group, structAmbFriend& friend)
CPRINTLN(DEBUG_FRIENDS, "Private_DismissAmbFriend(", GetLabel_enumCharacterList(friend.eChar), ")")
DEBUG_PRINTCALLSTACK()
// Reset last contact timer (to player and friends in group)
IF friend.eState = AMBFRIEND_GREET OR friend.eState = AMBFRIEND_IDLE OR friend.eState = AMBFRIEND_DISMISSED OR friend.eState = AMBFRIEND_GREET_SAFEHOUSE OR friend.eState = AMBFRIEND_GREET_OUTSIDE
enumCharacterList eLoopChar
REPEAT NUM_OF_PLAYABLE_PEDS eLoopChar
IF friend.eChar <> eLoopChar
IF group.mFriends[eLoopChar].eState = AMBFRIEND_PLAYER
OR group.mFriends[eLoopChar].eState = AMBFRIEND_GREET
OR group.mFriends[eLoopChar].eState = AMBFRIEND_GREET_SAFEHOUSE
OR group.mFriends[eLoopChar].eState = AMBFRIEND_GREET_OUTSIDE
RESET_FRIEND_LAST_CONTACT_TIMER(friend.eChar, eLoopChar, FRIEND_CONTACT_DISMISSED)
ENDIF
ENDIF
ENDREPEAT
ENDIF
// Normal dismiss...
IF friend.eState = AMBFRIEND_GREET OR friend.eState = AMBFRIEND_IDLE OR friend.eState = AMBFRIEND_DISMISSED
// Set state to dismissed
IF friend.eState <> AMBFRIEND_DISMISSED
Private_SetAmbFriendState(group, friend, AMBFRIEND_DISMISSED)
ENDIF
// Dismiss safehouse ped...
ELIF friend.eState = AMBFRIEND_GREET_SAFEHOUSE
OR friend.eState = AMBFRIEND_GREET_OUTSIDE
// Mark for never picking up again
// IF NOT IS_PED_INJURED(friend.hPed)
// DECOR_SET_BOOL(friend.hPed, "BlockFriendGrab", TRUE)
// ENDIF
// Set state to released
Private_SetAmbFriendState(group, friend, AMBFRIEND_NULL)
Private_InitAmbFriend(friend, friend.eChar)
ENDIF
ENDPROC
PROC Private_DismissAmbGroup(structAmbGroup& group)
enumCharacterList eChar
REPEAT NUM_OF_PLAYABLE_PEDS eChar
IF group.mFriends[eChar].eState = AMBFRIEND_GREET
OR group.mFriends[eChar].eState = AMBFRIEND_GREET_SAFEHOUSE
OR group.mFriends[eChar].eState = AMBFRIEND_GREET_OUTSIDE
Private_DismissAmbFriend(group, group.mFriends[eChar])
ENDIF
ENDREPEAT
ENDPROC
PROC Private_DismissAmbGroup_IfBlocked(structAmbGroup& group)
enumCharacterList eChar
REPEAT NUM_OF_PLAYABLE_PEDS eChar
IF group.mFriends[eChar].eState = AMBFRIEND_GREET
OR group.mFriends[eChar].eState = AMBFRIEND_GREET_SAFEHOUSE
OR group.mFriends[eChar].eState = AMBFRIEND_GREET_OUTSIDE
enumAmbConvState eConv = Private_GetAmbFriendConvRequest(group.mFriends[eChar])
IF eConv = AMBCONV_MISBLOCK_HELLO
OR eConv = AMBCONV_MISBLOCK_HELLO_HOME
OR eConv = AMBCONV_DEFBLOCK_HELLO
OR eConv = AMBCONV_DEFBLOCK_HELLO_HOME
Private_DismissAmbFriend(group, group.mFriends[eChar])
ENDIF
ENDIF
ENDREPEAT
ENDPROC
PROC Private_DismissAmbGroup_IfAngry(structAmbGroup& group)
enumCharacterList eChar
REPEAT NUM_OF_PLAYABLE_PEDS eChar
IF group.mFriends[eChar].eState = AMBFRIEND_GREET
OR group.mFriends[eChar].eState = AMBFRIEND_GREET_SAFEHOUSE
OR group.mFriends[eChar].eState = AMBFRIEND_GREET_OUTSIDE
enumAmbConvState eConv = Private_GetAmbFriendConvRequest(group.mFriends[eChar])
IF eConv = AMBCONV_ANGRY_HELLO
OR eConv = AMBCONV_MTFALLOUT_HELLO
OR eConv = AMBCONV_KILLEDM_HELLO
OR eConv = AMBCONV_KILLEDT_HELLO
Private_DismissAmbFriend(group, group.mFriends[eChar])
ENDIF
ENDIF
ENDREPEAT
ENDPROC
PROC Private_DismissAmbGroup_IfSecondary(structAmbGroup& group)
enumCharacterList eChar
REPEAT NUM_OF_PLAYABLE_PEDS eChar
IF group.mFriends[eChar].eState = AMBFRIEND_GREET
OR group.mFriends[eChar].eState = AMBFRIEND_GREET_SAFEHOUSE
OR group.mFriends[eChar].eState = AMBFRIEND_GREET_OUTSIDE
IF eChar <> group.eConvChar
Private_DismissAmbFriend(group, group.mFriends[eChar])
ENDIF
ENDIF
ENDREPEAT
ENDPROC
PROC Private_DismissAmbGroup_IfOffer(structAmbGroup& group)
enumCharacterList eChar
REPEAT NUM_OF_PLAYABLE_PEDS eChar
IF group.mFriends[eChar].eState = AMBFRIEND_GREET
OR group.mFriends[eChar].eState = AMBFRIEND_GREET_SAFEHOUSE
OR group.mFriends[eChar].eState = AMBFRIEND_GREET_OUTSIDE
enumAmbConvState eConv = Private_GetAmbFriendConvRequest(group.mFriends[eChar])
IF eConv = AMBCONV_OFFER_HELLO
OR eConv = AMBCONV_OFFER_HELLO_HOME
Private_DismissAmbFriend(group, group.mFriends[eChar])
ENDIF
ENDIF
ENDREPEAT
ENDPROC
//-------------------------------------------------------------------------------------------------------------------------------------------
// AmbFriend - FSM
//-------------------------------------------------------------------------------------------------------------------------------------------
PROC Private_SetAmbFriendVulnerable(structAmbGroup& group, structAmbFriend& friend, BOOL bEnable)
IF bEnable
IF friend.bIsVulnerable = FALSE
AND friend.eChar <> g_eDefaultPlayerChar
CPRINTLN(DEBUG_FRIENDS, "Private_SetAmbFriendVulnerable(", GetLabel_enumCharacterList(friend.eChar), ", TRUE)")
friend.hOriginalGroup = GET_PED_RELATIONSHIP_GROUP_DEFAULT_HASH(friend.hPed)
friend.hOriginalDefaultGroup = GET_PED_RELATIONSHIP_GROUP_DEFAULT_HASH(friend.hPed)
SET_PED_RELATIONSHIP_GROUP_DEFAULT_HASH(friend.hPed, group.hEmptyGroup)
SET_PED_RELATIONSHIP_GROUP_HASH(friend.hPed, group.hEmptyGroup)
friend.bIsVulnerable = TRUE
ENDIF
ELSE
IF friend.bIsVulnerable = TRUE
CPRINTLN(DEBUG_FRIENDS, "Private_SetAmbFriendVulnerable(", GetLabel_enumCharacterList(friend.eChar), ", FALSE)")
IF friend.eChar = g_eDefaultPlayerChar
SCRIPT_ASSERT("Private_SetAmbFriendVulnerable(FALSE) - Can't call on player, skipping over (ignorable)")
ELSE
IF IsPedPerformingTask(friend.hPed, SCRIPT_TASK_COMBAT)
CLEAR_PED_TASKS(friend.hPed)
ENDIF
SET_PED_RELATIONSHIP_GROUP_HASH(friend.hPed, friend.hOriginalGroup)
SET_PED_RELATIONSHIP_GROUP_DEFAULT_HASH(friend.hPed, friend.hOriginalDefaultGroup)
ENDIF
friend.bIsVulnerable = FALSE
ENDIF
ENDIF
ENDPROC
PROC Private_SetAmbFriendAttribs(structAmbGroup& group, structAmbFriend& friend, structPedsForConversation& convPeds)
CPRINTLN(DEBUG_FRIENDS, "Private_SetAmbFriendAttribs(", GetLabel_enumCharacterList(friend.eChar), ")")
IF NOT IS_PED_INJURED(friend.hPed)
IF IS_PED_USING_ACTION_MODE(friend.hPed)
SET_PED_USING_ACTION_MODE(friend.hPed, FALSE)
ENDIF
IF IS_PED_IN_ANY_VEHICLE(friend.hPed)
VEHICLE_INDEX hVehicle = GET_VEHICLE_PED_IS_IN(friend.hPed)
IF IS_VEHICLE_DRIVEABLE(hVehicle) AND GET_PED_IN_VEHICLE_SEAT(hVehicle, VS_DRIVER) = friend.hPed
friend.hVehicle = hVehicle
ENDIF
ENDIF
CLEAR_ENTITY_LAST_DAMAGE_ENTITY(friend.hPed)
IF DOES_ENTITY_EXIST(friend.hVehicle) AND NOT IS_ENTITY_DEAD(friend.hVehicle)
CLEAR_ENTITY_LAST_DAMAGE_ENTITY(friend.hVehicle)
ENDIF
//ENUM_TO_INT(RBF_PLAYER_IMPACT)|ENUM_TO_INT(RBF_PLAYER_BUMP)|ENUM_TO_INT(RBF_PLAYER_RAGDOLL_BUMP)|ENUM_TO_INT(RBF_PED_RAGDOLL_BUMP)
SET_RAGDOLL_BLOCKING_FLAGS(friend.hPed, RBF_PLAYER_IMPACT)
// SET_RAGDOLL_BLOCKING_FLAGS(friend.hPed, RBF_MELEE)
ADD_FRIEND_CHAR_FOR_DIALOGUE(convPeds, g_eDefaultPlayerChar, PLAYER_PED_ID(), FALSE)
ADD_FRIEND_CHAR_FOR_DIALOGUE(convPeds, friend.eChar, friend.hPed, FALSE)
// SET_PED_CAN_PLAY_AMBIENT_ANIMS(friend.hPed, TRUE)
// SET_PED_CAN_PLAY_AMBIENT_BASE_ANIMS(friend.hPed, TRUE)
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(friend.hPed, TRUE)
STOP_PED_SPEAKING(friend.hPed, FALSE)
SET_CURRENT_PED_WEAPON(friend.hPed, WEAPONTYPE_UNARMED, TRUE)
//#1665158
FREEZE_ENTITY_POSITION(friend.hPed, FALSE)
PRINTLN("//#1665158 - FREEZE_ENTITY_POSITION(friend.hPed, FALSE)")
SET_ENTITY_SHOULD_FREEZE_WAITING_ON_COLLISION(friend.hPed, TRUE)
// If bringing Trevor/Franklin together before FBI2, allow them to fight...
IF Private_CanAmbFriendBlank(friend, FALSE)
Private_SetAmbFriendVulnerable(group, friend, TRUE)
ENDIF
ENDIF
ENDPROC
PROC Private_ClearAmbFriendAttribs(structAmbGroup& group, structAmbFriend& friend)
CPRINTLN(DEBUG_FRIENDS, "Private_ClearAmbFriendAttribs(", GetLabel_enumCharacterList(friend.eChar), ")")
IF NOT IS_PED_INJURED(friend.hPed)
SET_PED_SHOULD_PLAY_NORMAL_SCENARIO_EXIT(friend.hPed)
CLEAR_RAGDOLL_BLOCKING_FLAGS(friend.hPed, RBF_PLAYER_IMPACT)
// CLEAR_RAGDOLL_BLOCKING_FLAGS(friend.hPed, RBF_MELEE)
SET_PED_CAN_SWITCH_WEAPON(friend.hPed, TRUE)
Private_SetAmbFriendVulnerable(group, friend, FALSE)
ENDIF
ENDPROC
FUNC BOOL Private_GrabAmbFriendPed(structAmbGroup& group, structAmbFriend& friend, structPedsForConversation& convPeds)
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
// Get player ped
IF friend.eChar = g_eDefaultPlayerChar
friend.hPed = PLAYER_PED_ID()
ADD_FRIEND_CHAR_FOR_DIALOGUE(convPeds, friend.eChar, friend.hPed, FALSE)
friend.eGrabMode = AMBGRAB_NORMAL
RETURN TRUE
// Get queued ped
ELIF NOT IS_PED_INJURED(g_pDismissPeds[friend.eChar])
friend.hPed = g_pDismissPeds[friend.eChar]
friend.eGrabMode = g_pDismissMode[friend.eChar]
// Grab ped
SET_ENTITY_AS_MISSION_ENTITY(friend.hPed, TRUE, TRUE)
IF DOES_ENTITY_BELONG_TO_THIS_SCRIPT(friend.hPed)
CPRINTLN(DEBUG_FRIENDS, "Private_GrabAmbFriendPed(", GetLabel_enumCharacterList(friend.eChar), ") - Grabbed as queued char")
Private_SetAmbFriendAttribs(group, friend, convPeds)
g_pDismissPeds[friend.eChar] = NULL
friend.hVehicle = NULL
RETURN TRUE
ENDIF
// Get non-player ped
ELSE
// Grab ped if allowed
SELECTOR_SLOTS_ENUM eSelectorSlot = GET_SELECTOR_SLOT_FROM_PLAYER_PED_ENUM(friend.eChar)
friend.hPed = g_sPlayerPedRequest.sSelectorPeds.pedID[eSelectorSlot]
IF NOT IS_PED_INJURED(friend.hPed)
// Is this ped a non-lead in char
IF IS_BIT_SET(g_sPlayerPedRequest.iBitsetPedInLeadIn, ENUM_TO_INT(eSelectorSlot))
#IF IS_DEBUG_BUILD
IF friend.hDebugGrabFail <> friend.hPed
CPRINTLN(DEBUG_FRIENDS, "Private_GrabAmbFriendPed(", GetLabel_enumCharacterList(friend.eChar), ") - Cannot grab - Ped tagged as lead-in scene character")
friend.hDebugGrabFail = friend.hPed
ENDIF
#ENDIF
// Make sure ped hasn't already been added to delete queue
ELIF Private_IsPedInDeleteQueue(friend.hPed)
#IF IS_DEBUG_BUILD
IF friend.hDebugGrabFail <> friend.hPed
CPRINTLN(DEBUG_FRIENDS, "Private_GrabAmbFriendPed(", GetLabel_enumCharacterList(friend.eChar), ") - Cannot grab - Ped is in delete queue")
friend.hDebugGrabFail = friend.hPed
ENDIF
#ENDIF
ELSE
// Does a friend connection exist and is ready
enumFriendConnection eConnection
IF GET_CONNECTION(g_eDefaultPlayerChar, friend.eChar, eConnection)
AND GET_CONNECTION_STATE(eConnection) = FC_STATE_ContactWait
STRING sOwnerScript = ""
INT hOwnerScript = 0
sOwnerScript = GET_ENTITY_SCRIPT(friend.hPed, hOwnerScript)
IF NOT IS_STRING_NULL_OR_EMPTY(sOwnerScript)
VECTOR vPlayerPos = GET_ENTITY_COORDS(PLAYER_PED_ID())
VECTOR vFriendPos = GET_ENTITY_COORDS(friend.hPed)
FLOAT fDist2 = VDIST2(vPlayerPos, vFriendPos)
IF fDist2 < 200.0 * 200.0
// Grabbing from player controller...
IF (ARE_STRINGS_EQUAL(sOwnerScript, "player_controller") OR ARE_STRINGS_EQUAL(sOwnerScript, "player_controller_b"))
// Grab ped
SET_ENTITY_AS_MISSION_ENTITY(friend.hPed, TRUE, TRUE)
IF DOES_ENTITY_BELONG_TO_THIS_SCRIPT(friend.hPed)
CPRINTLN(DEBUG_FRIENDS, "Private_GrabAmbFriendPed(", GetLabel_enumCharacterList(friend.eChar), ") - Grabbed as normal open-world char")
friend.eGrabMode = AMBGRAB_NORMAL
Private_SetAmbFriendAttribs(group, friend, convPeds)
RETURN TRUE
ENDIF
// Grabbing from trevor family scene
ELIF friend.eChar = CHAR_TREVOR
AND ARE_STRINGS_EQUAL(sOwnerScript, "family_scene_t0")
SWITCH g_eCurrentFamilyEvent[FM_TREVOR_0_TREVOR]
CASE FE_T0_TREVOR_blowing_shit_up
CASE FE_T0_TREVOR_and_kidnapped_wife_walk
IF Private_CanAmbFriendSafehouseGreet(friend, FALSE, 12.0)
CPRINTLN(DEBUG_FRIENDS, "Private_GrabAmbFriendPed(", GetLabel_enumCharacterList(friend.eChar), ") - Grabbed as trevor family scene (outside)")
friend.eGrabMode = AMBGRAB_SAFEHOUSE_OUTSIDE
Private_SetAmbFriendAttribs(group, friend, convPeds)
RETURN TRUE
ENDIF
BREAK
CASE FE_T0_TREVOR_and_kidnapped_wife_stare
//CASE FE_T0_TREVOR_and_kidnapped_wife_laugh
#IF NOT IS_JAPANESE_BUILD
CASE FE_T0_TREVOR_doing_a_shit
#ENDIF
IF Private_CanAmbFriendSafehouseGreet(friend, TRUE, 30.0)
CPRINTLN(DEBUG_FRIENDS, "Private_GrabAmbFriendPed(", GetLabel_enumCharacterList(friend.eChar), ") - Grabbed as trevor family scene (inside)")
friend.eGrabMode = AMBGRAB_SAFEHOUSE_TRAILER
Private_SetAmbFriendAttribs(group, friend, convPeds)
RETURN TRUE
ENDIF
BREAK
CASE FE_T0_TREVOR_smoking_crystal
CASE FE_T0_TREVOR_passed_out_naked_drunk
IF Private_CanAmbFriendSafehouseGreet(friend, TRUE, 30.0)
CPRINTLN(DEBUG_FRIENDS, "Private_GrabAmbFriendPed(", GetLabel_enumCharacterList(friend.eChar), ") - Grabbed as trevor family scene (drunk)")
friend.eGrabMode = AMBGRAB_SAFEHOUSE_DRUNK
Private_SetAmbFriendAttribs(group, friend, convPeds)
RETURN TRUE
ENDIF
BREAK
ENDSWITCH
ELSE
#IF IS_DEBUG_BUILD
IF friend.hDebugGrabFail <> friend.hPed
CPRINTLN(DEBUG_FRIENDS, "Private_GrabAmbFriendPed(", GetLabel_enumCharacterList(friend.eChar), ") - Cannot grab - Not owned by player_controller or family_scene_t0 scripts")
friend.hDebugGrabFail = friend.hPed
ENDIF
#ENDIF
ENDIF
ENDIF
ELSE
#IF IS_DEBUG_BUILD
IF friend.hDebugGrabFail <> friend.hPed
CPRINTLN(DEBUG_FRIENDS, "Private_GrabAmbFriendPed(", GetLabel_enumCharacterList(friend.eChar), ") - Cannot grab - Not owned by any script")
friend.hDebugGrabFail = friend.hPed
ENDIF
#ENDIF
ENDIF
ELSE
#IF IS_DEBUG_BUILD
IF friend.hDebugGrabFail <> friend.hPed
IF GET_CONNECTION(g_eDefaultPlayerChar, friend.eChar, eConnection)
CPRINTLN(DEBUG_FRIENDS, "Private_GrabAmbFriendPed(", GetLabel_enumCharacterList(friend.eChar), ") - Cannot grab - Connection not in ContactWait")
CPRINTLN(DEBUG_FRIENDS, "Private_GrabAmbFriendPed() - [state is ", GetLabel_enumFriendConnectionState(GET_CONNECTION_STATE(eConnection)), "]")
ELSE
CPRINTLN(DEBUG_FRIENDS, "Private_GrabAmbFriendPed(", GetLabel_enumCharacterList(friend.eChar), ") - Cannot grab - Connection doesn't exist")
ENDIF
friend.hDebugGrabFail = friend.hPed
ENDIF
#ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
PROC Private_ReleaseAmbFriend(structAmbGroup& group, structAmbFriend& friend, BOOL bDelete = FALSE, BOOL bVerbose = TRUE, BOOL bStorePlayerPedInfoOnDelete = TRUE)
IF friend.eState <> AMBFRIEND_NULL
// Clean up current state
Private_SetAmbFriendState(group, friend, AMBFRIEND_NULL, bVerbose)
// If ped exists and I own it
IF DOES_ENTITY_EXIST(friend.hPed) AND DOES_ENTITY_BELONG_TO_THIS_SCRIPT(friend.hPed, FALSE) AND NOT Private_IsPedInDeleteQueue(friend.hPed)
// If dead, force to hospital switch scene and release ped
IF IS_PED_INJURED(friend.hPed)
g_SavedGlobals.sPlayerSceneData.g_ePlayerLastScene[friend.eChar] = PR_SCENE_DEAD
SAFE_AMBIENT_STORE_PLAYER_PED_INFO(friend.hPed) //STORE_PLAYER_PED_INFO(friend.hPed)
g_savedGlobals.sPlayerData.sInfo.sLastTimeActive[friend.eChar] = GET_CURRENT_TIMEOFDAY()
SET_PED_AS_NO_LONGER_NEEDED(friend.hPed)
//Private_AddPedToDeleteQueue(friend.hPed)
// Otherwise, store last known position and remove (delete now or add to queue)
ELSE
Private_ClearAmbFriendAttribs(group, friend)
IF bDelete
IF bStorePlayerPedInfoOnDelete
SAFE_AMBIENT_STORE_PLAYER_PED_INFO(friend.hPed) //STORE_PLAYER_PED_INFO(friend.hPed)
ENDIF
DELETE_PED(friend.hPed)
ELSE
Private_AddPedToDeleteQueue(friend.hPed)
ENDIF
ENDIF
ENDIF
ENDIF
// Reset all vars
Private_InitAmbFriend(friend, friend.eChar)
ENDPROC
PROC Private_UpdateAmbFriend(structAmbGroup& group, structAmbFriend& friend, structPedsForConversation& convPeds)
FLOAT fDist2 = 0.0
FLOAT fGround
PED_INDEX hAngryPed = NULL
//-- Handle instant shutdowns
IF REPLAY_SYSTEM_HAS_REQUESTED_A_SCRIPT_CLEANUP()
IF friend.eState <> AMBFRIEND_NULL
CPRINTLN(DEBUG_FRIENDS, "Private_UpdateAmbFriend(", GetLabel_enumCharacterList(friend.eChar), ") - Shutting down for playback replay")
Private_ReleaseAmbFriend(group, friend, TRUE, TRUE, FALSE)
ENDIF
EXIT
ELIF group.bOutroActive AND friend.eState <> AMBFRIEND_OUTRO
IF friend.eState <> AMBFRIEND_NULL
CPRINTLN(DEBUG_FRIENDS, "Private_UpdateAmbFriend(", GetLabel_enumCharacterList(friend.eChar), ") - Shutting down for outro")
Private_ReleaseAmbFriend(group, friend)
ENDIF
EXIT
// Alive checks
ELIF friend.eState <> AMBFRIEND_NULL AND friend.eState <> AMBFRIEND_PLAYER
IF IS_PED_INJURED(friend.hPed)
// Display fail reason
IF DOES_ENTITY_EXIST(friend.hPed) AND NOT IS_PED_INJURED(PLAYER_PED_ID())
VECTOR vPlayerPos = GET_ENTITY_COORDS(PLAYER_PED_ID())
VECTOR vFriendPos = GET_ENTITY_COORDS(friend.hPed, FALSE)
IF VDIST2(vPlayerPos, vFriendPos) < 150.0
// Get friend name
enumFriend eFriend = GET_FRIEND_FROM_CHAR(friend.eChar)
IF eFriend < MAX_FRIENDS
// Add fail reason to flow queue
ADD_HELP_WITH_SUBSTRING_TO_FLOW_QUEUE("FR_X_INJ_1m", g_sCharacterSheetAll[friend.eChar].label, FHP_VERY_HIGH, 0, 20000)
ENDIF
ENDIF
ENDIF
// Start failed timer
enumFriend eFriend = GET_FRIEND_FROM_CHAR(friend.eChar)
CC_CommID eCommID = GET_COMM_ID_FOR_FRIEND_FAIL(eFriend)
START_FRIEND_FAIL_TIMER(eFriend, eCommID)
// Should the player get an angry text message from the hospital
IF NOT Private_CanAmbFriendBlank(friend)
IF eCommID != COMM_NONE
CC_CodeID eHospitalCID = Private_GetHospitalChargeCID(friend.hPed)
// Send angry text message, if allowed, and not already an angry message queued
IF eHospitalCID != CID_BLANK
IF NOT IS_COMMUNICATION_REGISTERED(eCommID)
OR GET_COMMUNICATION_STATUS(eCommID) = CS_ERROR
REGISTERED_FRIEND_TEXT_MESSAGE_TO_PLAYER(g_eDefaultPlayerChar, friend.eChar, FTM_FRIEND_HOSPITAL, FALSE, eHospitalCID, eCommID)
ENDIF
ENDIF
ENDIF
ENDIF
Private_ReleaseAmbFriend(group, friend, FALSE)
EXIT
ELIF friend.hPed = PLAYER_PED_ID()
Private_ReleaseAmbFriend(group, friend, FALSE, FALSE)
IF friend.eChar = g_eDefaultPlayerChar AND Private_GrabAmbFriendPed(group, friend, convPeds)
Private_SetAmbFriendState(group, friend, AMBFRIEND_PLAYER)
EXIT
ELSE
Private_SetAmbFriendState(group, friend, AMBFRIEND_NULL)
EXIT
ENDIF
ELIF Private_IsPedInDeleteQueue(friend.hPed)
CPRINTLN(DEBUG_FRIENDS, "Private_UpdateAmbFriend(", GetLabel_enumCharacterList(friend.eChar), ") - Ped (", NATIVE_TO_INT(friend.hPed), ") is in delete queue")
Private_ReleaseAmbFriend(group, friend, FALSE)
EXIT
ELIF NOT DOES_ENTITY_BELONG_TO_THIS_SCRIPT(friend.hPed) AND friend.eState <> AMBFRIEND_GREET_SAFEHOUSE AND friend.eState <> AMBFRIEND_GREET_OUTSIDE
CPRINTLN(DEBUG_FRIENDS, "Private_UpdateAmbFriend(", GetLabel_enumCharacterList(friend.eChar), ") - Ped (", NATIVE_TO_INT(friend.hPed), ") doesn't belong to script")
#IF IS_DEBUG_BUILD
IF NOT Private_IsEntityOwnedBy(friend.hPed, "friendActivity")
SCRIPT_ASSERT("Private_UpdateAmbFriend() - Ped doesn't belong to script")
ENDIF
#ENDIF
Private_ReleaseAmbFriend(group, friend, FALSE)
EXIT
ELIF friend.hPed <> g_sPlayerPedRequest.sSelectorPeds.pedID[GET_SELECTOR_SLOT_FROM_PLAYER_PED_ENUM(friend.eChar)]
CPRINTLN(DEBUG_FRIENDS, "Private_UpdateAmbFriend(", GetLabel_enumCharacterList(friend.eChar), ") - Ped (", NATIVE_TO_INT(friend.hPed), ") out of sync with selector (", NATIVE_TO_INT(g_sPlayerPedRequest.sSelectorPeds.pedID[GET_SELECTOR_SLOT_FROM_PLAYER_PED_ENUM(friend.eChar)]), ")")
SCRIPT_ASSERT("Private_UpdateAmbFriend() - Ped out of sync with selector")
Private_ReleaseAmbFriend(group, friend, FALSE)
EXIT
ELIF (IS_PED_FALLING(friend.hPed) OR IS_PED_IN_PARACHUTE_FREE_FALL(friend.hPed))
AND ((NOT HAS_COLLISION_LOADED_AROUND_ENTITY(friend.hPed)and NOT IS_PED_INJURED(friend.hPed)) OR NOT GET_GROUND_Z_FOR_3D_COORD(GET_ENTITY_COORDS(friend.hPed), fGround) OR fGround < -200.0)
CPRINTLN(DEBUG_FRIENDS, "Private_UpdateAmbFriend(", GetLabel_enumCharacterList(friend.eChar), ") - Ped (", NATIVE_TO_INT(friend.hPed), ") fallen through world?")
// SCRIPT_ASSERT("Private_UpdateAmbFriend() - Ped fallen through world?")
DELETE_PED(friend.hPed)
Private_ReleaseAmbFriend(group, friend, TRUE)
EXIT
ELIF NOT IS_PED_INJURED(PLAYER_PED_ID())
fDist2 = VDIST2(GET_ENTITY_COORDS(PLAYER_PED_ID()), GET_ENTITY_COORDS(friend.hPed))
IF fDist2 > 300.0*300.0
Private_ReleaseAmbFriend(group, friend, TRUE)
EXIT
ENDIF
ENDIF
IF group.bUpdateAfterSwitch
IF Private_CanAmbFriendBlank(friend)
Private_SetAmbFriendVulnerable(group, friend, TRUE)
ELSE
Private_SetAmbFriendVulnerable(group, friend, FALSE)
ENDIF
ENDIF
ENDIF
// Handle state
SWITCH friend.eState
//------------------------------------------------------------------------------------------------- AMBFRIEND_NULL
CASE AMBFRIEND_NULL
IF g_bAllowAmbientFriendLaunching
// AND (NOT IS_CURRENTLY_ON_MISSION_OF_ANY_TYPE() OR IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_FRIEND_ACTIVITY))
IF Private_GrabAmbFriendPed(group, friend, convPeds)
IF friend.hPed = PLAYER_PED_ID()
Private_SetAmbFriendState(group, friend, AMBFRIEND_PLAYER)
ELIF friend.eGrabMode = AMBGRAB_SAFEHOUSE_TRAILER
Private_SetAmbFriendState(group, friend, AMBFRIEND_GREET_SAFEHOUSE)
ELIF friend.eGrabMode = AMBGRAB_SAFEHOUSE_DRUNK
Private_SetAmbFriendState(group, friend, AMBFRIEND_GREET_SAFEHOUSE)
ELIF friend.eGrabMode = AMBGRAB_SAFEHOUSE_OUTSIDE
Private_SetAmbFriendState(group, friend, AMBFRIEND_GREET_OUTSIDE)
ELIF friend.eGrabMode = AMBGRAB_TRANSFER_REJECTED
Private_SetAmbFriendState(group, friend, AMBFRIEND_DISMISSED)
Private_SetAmbFriendDismissMode(friend, AMBMODE_REJECTED, TRUE)
friend.eGrabMode = AMBGRAB_NORMAL
ELIF friend.eGrabMode = AMBGRAB_TRANSFER_WANDER
Private_SetAmbFriendState(group, friend, AMBFRIEND_DISMISSED)
Private_SetAmbFriendDismissMode(friend, AMBMODE_WANDER, TRUE)
friend.eGrabMode = AMBGRAB_NORMAL
ELIF friend.eGrabMode = AMBGRAB_TRANSFER_FLEE
Private_SetAmbFriendState(group, friend, AMBFRIEND_DISMISSED)
Private_SetAmbFriendDismissMode(friend, AMBMODE_FLEE, TRUE, PLAYER_PED_ID())
friend.eGrabMode = AMBGRAB_NORMAL
ELIF Private_CanAmbFriendGreet(group, friend)
IF Private_CanAmbFriendBlank(friend)
Private_DismissAmbFriend(group, friend)
Private_SetAmbFriendState(group, friend, AMBFRIEND_DISMISSED)
ELSE
Private_SetAmbFriendState(group, friend, AMBFRIEND_GREET)
ENDIF
ELIF Private_IsAmbFriendDismissedByAnyone(friend)
Private_SetAmbFriendState(group, friend, AMBFRIEND_DISMISSED)
ELSE
Private_SetAmbFriendState(group, friend, AMBFRIEND_IDLE)
ENDIF
ENDIF
ENDIF
BREAK
//------------------------------------------------------------------------------------------------- AMBFRIEND_PLAYER
CASE AMBFRIEND_PLAYER
IF IS_PED_INJURED(friend.hPed)
Private_SetAmbFriendState(group, friend, AMBFRIEND_NULL)
ELIF friend.hPed <> PLAYER_PED_ID()
IF NOT Private_GrabAmbFriendPed(group, friend, convPeds)
Private_SetAmbFriendState(group, friend, AMBFRIEND_NULL)
ELIF friend.eGrabMode = AMBGRAB_TRANSFER_REJECTED
Private_SetAmbFriendState(group, friend, AMBFRIEND_DISMISSED)
Private_SetAmbFriendDismissMode(friend, AMBMODE_REJECTED, TRUE)
friend.eGrabMode = AMBGRAB_NORMAL
ELIF friend.eGrabMode = AMBGRAB_TRANSFER_WANDER
Private_SetAmbFriendState(group, friend, AMBFRIEND_DISMISSED)
Private_SetAmbFriendDismissMode(friend, AMBMODE_WANDER, TRUE)
friend.eGrabMode = AMBGRAB_NORMAL
ELIF friend.eGrabMode = AMBGRAB_TRANSFER_FLEE
Private_SetAmbFriendState(group, friend, AMBFRIEND_DISMISSED)
Private_SetAmbFriendDismissMode(friend, AMBMODE_FLEE, TRUE, PLAYER_PED_ID())
friend.eGrabMode = AMBGRAB_NORMAL
ELIF Private_CanAmbFriendGreet(group, friend)
IF Private_CanAmbFriendBlank(friend)
Private_DismissAmbFriend(group, friend)
Private_SetAmbFriendState(group, friend, AMBFRIEND_DISMISSED)
ELSE
Private_SetAmbFriendState(group, friend, AMBFRIEND_GREET)
ENDIF
ELIF Private_IsAmbFriendDismissed(friend)
Private_SetAmbFriendState(group, friend, AMBFRIEND_DISMISSED)
ELSE
Private_SetAmbFriendState(group, friend, AMBFRIEND_IDLE)
ENDIF
ELIF GET_FRAME_COUNT() % 30 = 0
IF IS_PED_SITTING_IN_ANY_VEHICLE(friend.hPed)
VEHICLE_INDEX hCurVehicle
hCurVehicle = GET_VEHICLE_PED_IS_IN(friend.hPed)
IF IS_VEHICLE_DRIVEABLE(hCurVehicle) AND GET_PED_IN_VEHICLE_SEAT(hCurVehicle, VS_DRIVER) = friend.hPed
friend.hVehicle = hCurVehicle
ENDIF
ENDIF
ENDIF
BREAK
//------------------------------------------------------------------------------------------------- AMBFRIEND_IDLE
CASE AMBFRIEND_IDLE
IF Private_IsAmbFriendDismissed(friend)
Private_SetAmbFriendState(group, friend, AMBFRIEND_DISMISSED)
ELIF Private_CanAmbFriendGreet(group, friend)
IF Private_CanAmbFriendBlank(friend, TRUE)
Private_DismissAmbFriend(group, friend)
Private_SetAmbFriendState(group, friend, AMBFRIEND_DISMISSED)
ELSE
Private_SetAmbFriendState(group, friend, AMBFRIEND_GREET)
ENDIF
ELIF Private_HasPlayerAttackedAmbFriend(friend)
Private_DismissAmbFriend(group, friend)
Private_SetAmbFriendThreatResponse(friend, TRUE, PLAYER_PED_ID())
ELIF Private_HasAnyoneAttackedAmbFriend(friend, FALSE, hAngryPed)
Private_SetAmbFriendState(group, friend, AMBFRIEND_DISMISSED) // Only set state to dimiss, don't update timer (want dimiss to end if player meets them when flee is done)
Private_SetAmbFriendDismissMode(friend, AMBMODE_FLEE, FALSE, hAngryPed)
// ELIF Private_HasPlayerThreatenedAmbFriend(friend)
// Private_DismissAmbFriend(group, friend)
ELSE
Private_UpdateAmbFriend_Idle(group, friend)
ENDIF
BREAK
//------------------------------------------------------------------------------------------------- AMBFRIEND_GREET
CASE AMBFRIEND_GREET
IF Private_IsAmbFriendDismissed(friend)
Private_SetAmbFriendState(group, friend, AMBFRIEND_DISMISSED)
ELIF group.iGreetCount > 1 AND fDist2 > 35.0*35.0
Private_DismissAmbFriend(group, friend)
ELIF group.iGreetCount = 1 AND fDist2 > 25.0*25.0
Private_DismissAmbFriend(group, friend)
ELIF Private_HasPlayerAttackedAmbFriend(friend)
Private_DismissAmbFriend(group, friend)
Private_SetAmbFriendThreatResponse(friend, TRUE, PLAYER_PED_ID())
ELIF Private_HasAnyoneAttackedAmbFriend(friend, FALSE, hAngryPed)
Private_DismissAmbFriend(group, friend)
Private_SetAmbFriendDismissMode(friend, AMBMODE_FLEE, FALSE, hAngryPed)
ELIF Private_HasPlayerThreatenedAmbFriend(group, friend)
Private_DismissAmbFriend(group, friend)
Private_SetAmbFriendDismissMode(friend, AMBMODE_WANDER, TRUE)
ELIF Private_HasPlayerStolenCarAmbFriend(group, friend)
Private_DismissAmbFriend(group, friend)
Private_SetAmbFriendDismissMode(friend, AMBMODE_JACKED, TRUE)
ELIF IS_PED_RAGDOLL(friend.hPed)
Private_DismissAmbFriend(group, friend)
ELSE
Private_UpdateAmbFriend_Greet(friend)
ENDIF
BREAK
//------------------------------------------------------------------------------------------------- AMBFRIEND_GREET_SAFEHOUSE
CASE AMBFRIEND_GREET_SAFEHOUSE
IF GET_INTERIOR_FROM_ENTITY(PLAYER_PED_ID()) = NULL
Private_DismissAmbFriend(group, friend)
ELSE
Private_UpdateAmbFriend_Greet(friend, FALSE)
ENDIF
BREAK
//------------------------------------------------------------------------------------------------- AMBFRIEND_GREET_OUTSIDE
CASE AMBFRIEND_GREET_OUTSIDE
IF GET_INTERIOR_FROM_ENTITY(PLAYER_PED_ID()) <> NULL
OR fDist2 > 20.0*20.0
Private_DismissAmbFriend(group, friend)
ELSE
Private_UpdateAmbFriend_Greet(friend, FALSE)
ENDIF
BREAK
//------------------------------------------------------------------------------------------------- AMBFRIEND_DISMISSED
CASE AMBFRIEND_DISMISSED
IF Private_CanAmbFriendGreet(group, friend)
Private_SetAmbFriendState(group, friend, AMBFRIEND_GREET)
ELSE
IF Private_UpdateAmbFriend_Dismissed(group, friend, convPeds, fDist2)
IF Private_TryStartAmbFriendOutro(group, friend)
Private_SetAmbFriendState(group, friend, AMBFRIEND_OUTRO)
ENDIF
ENDIF
ENDIF
BREAK
//------------------------------------------------------------------------------------------------- AMBFRIEND_OUTRO
CASE AMBFRIEND_OUTRO
IF Private_UpdateAmbFriend_Outro(group, friend)
Private_ReleaseAmbFriend(group, friend, TRUE)
ENDIF
BREAK
ENDSWITCH
ENDPROC
//-------------------------------------------------------------------------------------------------------------------------------------------
// AmbConv
//-------------------------------------------------------------------------------------------------------------------------------------------
PROC Private_SetAmbConvState(structAmbGroup& group, enumAmbConvState eState)
IF group.eConvState <> eState
CPRINTLN(DEBUG_FRIENDS, "Private_SetAmbConvState(", GetLabel_enumAmbConvState(eState), ")")
// Clear context button help
IF group.hConvInput <> NEW_CONTEXT_INTENTION
RELEASE_CONTEXT_INTENTION(group.hConvInput)
group.hConvInput = NEW_CONTEXT_INTENTION
ENDIF
// Set new state
group.eConvState = eState
group.bInitConvState = TRUE
ENDIF
ENDPROC
PROC Private_StopAmbConv(structAmbGroup& group, BOOL bFinishCurrentLine = FALSE)
// Stop conversation
IF group.eConvState <> AMBCONV_NULL
STOP_SCRIPTED_CONVERSATION(bFinishCurrentLine)
Private_SetAmbConvState(group, AMBCONV_NULL)
ENDIF
ENDPROC
FUNC BOOL Private_IsMichaelTrevorSafehouse(structAmbFriend& friend)
#if USE_CLF_DLC
unused_parameter(friend)
#endif
#if USE_NRM_DLC
unused_parameter(friend)
#endif
#if not USE_CLF_DLC
#if not USE_NRM_DLC
IF Is_Savehouse_Respawn_Available(SAVEHOUSE_MICHAEL_CS)
IF (g_eDefaultPlayerChar = CHAR_MICHAEL AND friend.eChar = CHAR_TREVOR)
RETURN TRUE
ENDIF
IF (g_eDefaultPlayerChar = CHAR_TREVOR AND friend.eChar = CHAR_MICHAEL)
RETURN TRUE
ENDIF
ENDIF
#endif
#endif
RETURN FALSE
ENDFUNC
FUNC enumFriendActivityPhrase Private_GetAmbConvGreetPhrase(structAmbGroup& group, BOOL bIsSafehouse = FALSE)
IF bIsSafeHouse
RETURN FAP_AMBIENT_TRAILER_1r
// IF Private_IsAmbFriendContactRecent(group.mFriends[group.eConvChar])
// RETURN FAP_AMBIENT_TRAILER_1r
// ELSE
// RETURN FAP_AMBIENT_TRAILER_1
// ENDIF
ELSE
IF Private_IsAmbFriendContactRecent(group.mFriends[group.eConvChar])
RETURN FAP_AMBIENT_HELLO_RECENT
ELSE
RETURN FAP_AMBIENT_HELLO_OK
ENDIF
ENDIF
ENDFUNC
FUNC enumFriendActivityPhrase Private_GetAmbConvPooPhrase()
INT iRandom = GET_RANDOM_INT_IN_RANGE(0, 3)
IF iRandom = 0 RETURN FAP_AMBIENT_POO_1a
ELIF iRandom = 1 RETURN FAP_AMBIENT_POO_1b
ELSE RETURN FAP_AMBIENT_POO_1c
ENDIF
ENDFUNC
FUNC BOOL Private_PlayAmbConvState(structAmbGroup& group, structPedsForConversation& convPeds, enumCharacterList ePlayer, enumCharacterList eFriend, enumFriendActivityPhrase ePhrase, BOOL bInterruptDialogue = TRUE)
TEXT_LABEL tBlock, tRoot
IF group.bInitConvState
IF bInterruptDialogue OR NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
IF NOT Private_GetFriendActivityPhrase(ePlayer, eFriend, ePhrase, tBlock, tRoot)
OR CREATE_CONVERSATION(convPeds, tBlock, tRoot, CONV_PRIORITY_AMBIENT_HIGH)
group.bInitConvState = FALSE
ENDIF
ENDIF
ELIF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_PlayAmbConvChat(structAmbGroup& group, structPedsForConversation& convPeds, enumCharacterList ePlayer, enumCharacterList eFriend, INT iIndex, enumAmbChatBitIndex eBitIndex)
TEXT_LABEL tBlock, tRoot
IF group.bInitConvState
IF NOT Private_GetAmbFriendChat(ePlayer, eFriend, iIndex, tBlock, tRoot)
OR CREATE_CONVERSATION(convPeds, tBlock, tRoot, CONV_PRIORITY_AMBIENT_HIGH)
CPRINTLN(DEBUG_FRIENDS, "Private_PlayAmbConvChat() - Set g_SavedGlobals.sFriendsData.g_iAmbChatBitfield / bit ", ENUM_TO_INT(eBitIndex))
SET_BIT(g_SavedGlobals.sFriendsData.g_iAmbChatBitfield, ENUM_TO_INT(eBitIndex))
group.bInitConvState = FALSE
ENDIF
ELIF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
PROC Private_StartAmbConv(structAmbGroup& group)
// Ask each friend in greet state to request a conversation...
enumAmbConvState eRequestState = AMBCONV_NULL
enumCharacterList eRequestChar = NO_CHARACTER
enumCharacterList eLoopChar
REPEAT NUM_OF_PLAYABLE_PEDS eLoopChar
IF group.mFriends[eLoopChar].eState = AMBFRIEND_GREET
OR group.mFriends[eLoopChar].eState = AMBFRIEND_GREET_SAFEHOUSE
OR group.mFriends[eLoopChar].eState = AMBFRIEND_GREET_OUTSIDE
enumAmbConvState eThisRequest = Private_GetAmbFriendConvRequest(group.mFriends[eLoopChar])
IF eRequestState = AMBCONV_NULL
OR eRequestState > eThisRequest
eRequestState = eThisRequest
eRequestChar = group.mFriends[eLoopChar].eChar
ENDIF
ENDIF
ENDREPEAT
// If request is valid...
IF eRequestState <> AMBCONV_NULL
AND IS_PLAYER_PED_PLAYABLE(eRequestChar)
// Start highest priority conversation
Private_SetAmbConvState(group, eRequestState)
group.eConvChar = eRequestChar
ENDIF
ENDPROC
PROC Private_UpdateAmbConv(structAmbGroup& group, structPedsForConversation& convPeds)
enumFriendConnection eConnection
// Process conversation state
SWITCH group.eConvState
//----------------------------------------------------------------------------------------------------------------------------- AMBCONV_SAFEHOUSE_TRAILER1
CASE AMBCONV_SAFEHOUSE_TRAILER1
IF (Private_GET_FRIEND_LAST_CONTACT_TIME(g_eDefaultPlayerChar, group.eConvChar) > 2.0*60.0 OR Private_GET_FRIEND_LAST_CONTACT_TYPE(g_eDefaultPlayerChar, group.eConvChar) <> FRIEND_CONTACT_DISMISSED)
Private_SetAmbConvState(group, AMBCONV_SAFEHOUSE_TRAILER2)
ELSE
Private_SetAmbConvState(group, AMBCONV_SAFEHOUSE_TRAILER3)
ENDIF
BREAK
CASE AMBCONV_SAFEHOUSE_TRAILER2
IF Private_PlayAmbConvState(group, convPeds, g_eDefaultPlayerChar, group.eConvChar, FAP_AMBIENT_TRAILER_1r, FALSE) // "Oh, it's you again"
Private_SetAmbConvState(group, AMBCONV_SAFEHOUSE_TRAILER3)
group.bHasSaidHello = TRUE
ENDIF
BREAK
CASE AMBCONV_SAFEHOUSE_TRAILER3
// Empty
BREAK
// NB: Trailer conv works differently to all the others, it holds on at the end, and relies on the state (AMBFRIEND_GREET_SAFEHOUSE) to end everything when the player leaves the trailer/area
//----------------------------------------------------------------------------------------------------------------------------- AMBCONV_MFSHOWROOM_HELLO
CASE AMBCONV_MFSHOWROOM_HELLO
IF group.bHasSaidHello
OR Private_PlayAmbConvState(group, convPeds, g_eDefaultPlayerChar, group.eConvChar, FAP_AMBIENT_HELLO_OK) // "Hello!"
Private_SetAmbConvState(group, AMBCONV_MFSHOWROOM_CHAT)
group.bHasSaidHello = TRUE
ENDIF
BREAK
CASE AMBCONV_MFSHOWROOM_CHAT
IF Private_PlayAmbConvChat(group, convPeds, g_eDefaultPlayerChar, group.eConvChar, 0, AMBCHATBIT_MFSHOWROOM) // [showroom chat]
Private_DismissAmbFriend(group, group.mFriends[group.eConvChar])
ENDIF
BREAK
//----------------------------------------------------------------------------------------------------------------------------- AMBCONV_MTFALLOUT_HELLO
CASE AMBCONV_MTFALLOUT_HELLO
IF group.bHasSaidHello
OR Private_PlayAmbConvState(group, convPeds, g_eDefaultPlayerChar, group.eConvChar, FAP_AMBIENT_HELLO_IRATE) // "Hello asshole"
Private_SetAmbConvState(group, AMBCONV_MTFALLOUT_CHAT)
group.bHasSaidHello = TRUE
ENDIF
BREAK
CASE AMBCONV_MTFALLOUT_CHAT
Private_DismissAmbGroup_IfSecondary(group)
IF Private_PlayAmbConvChat(group, convPeds, g_eDefaultPlayerChar, group.eConvChar, 0, AMBCHATBIT_MTFALLOUT) // [fallout chat]
Private_DismissAmbFriend(group, group.mFriends[group.eConvChar])
ENDIF
BREAK
//----------------------------------------------------------------------------------------------------------------------------- AMBCONV_KILLEDM_HELLO
CASE AMBCONV_KILLEDM_HELLO
IF group.bHasSaidHello
OR Private_PlayAmbConvState(group, convPeds, g_eDefaultPlayerChar, group.eConvChar, FAP_AMBIENT_HELLO_IRATE) // "Hello asshole"
Private_SetAmbConvState(group, AMBCONV_KILLEDM_CHAT)
group.bHasSaidHello = TRUE
ENDIF
BREAK
CASE AMBCONV_KILLEDM_CHAT
IF Private_PlayAmbConvChat(group, convPeds, g_eDefaultPlayerChar, group.eConvChar, 0, AMBCHATBIT_KILLEDM) // [killed M chat]
Private_DismissAmbFriend(group, group.mFriends[group.eConvChar])
ENDIF
BREAK
//----------------------------------------------------------------------------------------------------------------------------- AMBCONV_KILLEDT_HELLO
CASE AMBCONV_KILLEDT_HELLO
IF group.bHasSaidHello
OR Private_PlayAmbConvState(group, convPeds, g_eDefaultPlayerChar, group.eConvChar, FAP_AMBIENT_HELLO_IRATE) // "Hello asshole"
Private_SetAmbConvState(group, AMBCONV_KILLEDT_CHAT)
group.bHasSaidHello = TRUE
ENDIF
BREAK
CASE AMBCONV_KILLEDT_CHAT
IF Private_PlayAmbConvChat(group, convPeds, g_eDefaultPlayerChar, group.eConvChar, 1, AMBCHATBIT_KILLEDT) // [killed T chat]
Private_DismissAmbFriend(group, group.mFriends[group.eConvChar])
ENDIF
BREAK
//----------------------------------------------------------------------------------------------------------------------------- AMBCONV_ANGRY_HELLO
CASE AMBCONV_ANGRY_HELLO
IF group.bHasSaidHello
OR Private_PlayAmbConvState(group, convPeds, g_eDefaultPlayerChar, group.eConvChar, FAP_AMBIENT_HELLO_IRATE) // "Hello asshole"
Private_SetAmbConvState(group, AMBCONV_ANGRY_GOODBYE)
group.bHasSaidHello = TRUE
ENDIF
BREAK
CASE AMBCONV_ANGRY_GOODBYE
IF Private_PlayAmbConvState(group, convPeds, group.eConvChar, g_eDefaultPlayerChar, FAP_AMBIENT_GOODBYE_IRATE) // "Fuck you"
Private_DismissAmbFriend(group, group.mFriends[group.eConvChar])
ENDIF
BREAK
//----------------------------------------------------------------------------------------------------------------------------- AMBCONV_OFFER_HELLO
CASE AMBCONV_OFFER_HELLO
Private_DismissAmbGroup_IfAngry(group)
IF group.bHasSaidHello
OR group.bHasJoined
OR Private_PlayAmbConvState(group, convPeds, g_eDefaultPlayerChar, group.eConvChar, Private_GetAmbConvGreetPhrase(group)) // "Hello" / "Hello again"
Private_SetAmbConvState(group, AMBCONV_OFFER_ASK)
group.bHasSaidHello = TRUE
ENDIF
BREAK
CASE AMBCONV_OFFER_HELLO_HOME
Private_DismissAmbGroup_IfAngry(group)
IF NOT Private_IsMichaelTrevorSafehouse(group.mFriends[group.eConvChar])
IF group.bHasSaidHello
OR group.bHasJoined
OR Private_PlayAmbConvState(group, convPeds, group.eConvChar, g_eDefaultPlayerChar, Private_GetAmbConvGreetPhrase(group)) // "Hello" / "Hello again" (reversed speaker/listener)
Private_SetAmbConvState(group, AMBCONV_OFFER_ASK)
group.bHasSaidHello = TRUE
ENDIF
ELSE
IF group.bHasSaidHello
OR group.bHasJoined
OR Private_PlayAmbConvState(group, convPeds, g_eDefaultPlayerChar, group.eConvChar, Private_GetAmbConvGreetPhrase(group, TRUE)) // "Hello" / "Hello again" (house version)
Private_SetAmbConvState(group, AMBCONV_OFFER_ASK)
group.bHasSaidHello = TRUE
ENDIF
ENDIF
BREAK
CASE AMBCONV_OFFER_ASK
Private_DismissAmbGroup_IfAngry(group)
IF group.bHasJoined
OR Private_PlayAmbConvState(group, convPeds, g_eDefaultPlayerChar, group.eConvChar, FAP_AMBIENT_OFFER_QUESTION) // "Want to get a drink?"
RESTART_TIMER_NOW(group.mOfferTimer)
IF GET_ACTIVE_CONNECTIONS(eConnection) = 0
Private_SetAmbConvState(group, AMBCONV_OFFER_INPUT)
ELSE
Private_SetAmbConvState(group, AMBCONV_OFFER_LAUNCH1)
ENDIF
ENDIF
BREAK
CASE AMBCONV_OFFER_INPUT
Private_DismissAmbGroup_IfAngry(group)
IF NOT TIMER_DO_WHEN_READY(group.mOfferTimer, 30.0)
IF group.hConvInput = NEW_CONTEXT_INTENTION // [wait for input]
IF group.eConvChar = CHAR_MICHAEL
REGISTER_CONTEXT_INTENTION(group.hConvInput, CP_MAXIMUM_PRIORITY, "FR_PK_AMB_M")
ELIF group.eConvChar = CHAR_FRANKLIN
REGISTER_CONTEXT_INTENTION(group.hConvInput, CP_MAXIMUM_PRIORITY, "FR_PK_AMB_F")
ELSE
REGISTER_CONTEXT_INTENTION(group.hConvInput, CP_MAXIMUM_PRIORITY, "FR_PK_AMB_T")
ENDIF
ELIF HAS_CONTEXT_BUTTON_TRIGGERED(group.hConvInput, FALSE)
Private_SetAmbConvState(group, AMBCONV_OFFER_YES1)
ELIF IS_CONTEXT_INTENTION_HELP_DISPLAYING(group.hConvInput) AND IS_CONTROL_PRESSED(PLAYER_CONTROL, INPUT_CONTEXT_SECONDARY)
Private_SetAmbConvState(group, AMBCONV_OFFER_NO1)
ENDIF
SET_INPUT_EXCLUSIVE(PLAYER_CONTROL, INPUT_CONTEXT_SECONDARY)
ELSE
Private_SetAmbConvState(group, AMBCONV_OFFER_NO2)
ENDIF
BREAK
CASE AMBCONV_OFFER_NO1
IF Private_PlayAmbConvState(group, convPeds, g_eDefaultPlayerChar, group.eConvChar, FAP_AMBIENT_OFFER_NO1) // "No"
Private_SetAmbConvState(group, AMBCONV_OFFER_NO2)
ENDIF
Private_DismissAmbGroup_IfAngry(group)
BREAK
CASE AMBCONV_OFFER_NO2
IF Private_PlayAmbConvState(group, convPeds, g_eDefaultPlayerChar, group.eConvChar, FAP_AMBIENT_GOODBYE_OK) // "Ok, see you later."
Private_DismissAmbFriend(group, group.mFriends[group.eConvChar])
Private_DismissAmbGroup_IfOffer(group)
ENDIF
Private_DismissAmbGroup_IfAngry(group)
BREAK
CASE AMBCONV_OFFER_YES1
Private_DismissAmbGroup_IfAngry(group)
IF Private_PlayAmbConvState(group, convPeds, g_eDefaultPlayerChar, group.eConvChar, FAP_AMBIENT_OFFER_YES1) // "Yes"
Private_SetAmbConvState(group, AMBCONV_OFFER_YES2)
ENDIF
BREAK
CASE AMBCONV_OFFER_YES2
Private_DismissAmbGroup_IfAngry(group)
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
OR Private_PlayAmbConvState(group, convPeds, g_eDefaultPlayerChar, group.eConvChar, FAP_AMBIENT_OFFER_YES2) // "Get a car"
Private_SetAmbConvState(group, AMBCONV_OFFER_LAUNCH1)
ENDIF
BREAK
CASE AMBCONV_OFFER_LAUNCH1
Private_DismissAmbGroup_IfAngry(group)
IF GET_CONNECTION(g_eDefaultPlayerChar, group.eConvChar, eConnection) // [request launch]
AND GET_CONNECTION_STATE(eConnection) = FC_STATE_ContactWait
SET_CONNECTION_STATE(eConnection, FC_STATE_Init)
SET_CONNECTION_MODE(eConnection, FC_MODE_Ambient)
group.bHasJoined = TRUE
ENDIF
Private_SetAmbConvState(group, AMBCONV_OFFER_LAUNCH2)
BREAK
CASE AMBCONV_OFFER_LAUNCH2
Private_DismissAmbGroup_IfAngry(group)
IF NOT GET_CONNECTION(g_eDefaultPlayerChar, group.eConvChar, eConnection) // [wait for launch, dismiss on fail]
OR (GET_CONNECTION_STATE(eConnection) <> FC_STATE_Init AND GET_CONNECTION_STATE(eConnection) <> FC_STATE_Active)
Private_DismissAmbFriend(group, group.mFriends[group.eConvChar])
ENDIF
BREAK
//----------------------------------------------------------------------------------------------------------------------------- AMBCONV_MISBLOCK_HELLO
CASE AMBCONV_MISBLOCK_HELLO
IF group.bHasSaidHello
OR Private_PlayAmbConvState(group, convPeds, g_eDefaultPlayerChar, group.eConvChar, Private_GetAmbConvGreetPhrase(group)) // "Hello" / "Hello again"
Private_SetAmbConvState(group, AMBCONV_MISBLOCK_BUSY)
group.bHasSaidHello = TRUE
ENDIF
BREAK
CASE AMBCONV_MISBLOCK_HELLO_HOME
IF NOT Private_IsMichaelTrevorSafehouse(group.mFriends[group.eConvChar])
IF group.bHasSaidHello
OR Private_PlayAmbConvState(group, convPeds, group.eConvChar, g_eDefaultPlayerChar, Private_GetAmbConvGreetPhrase(group)) // "Hello" / "Hello again" (reversed speaker/listener)
Private_SetAmbConvState(group, AMBCONV_MISBLOCK_BUSY)
group.bHasSaidHello = TRUE
ENDIF
ELSE
IF group.bHasSaidHello
OR Private_PlayAmbConvState(group, convPeds, g_eDefaultPlayerChar, group.eConvChar, Private_GetAmbConvGreetPhrase(group, TRUE)) // "Hello" / "Hello again" (house version)
Private_SetAmbConvState(group, AMBCONV_MISBLOCK_BUSY)
group.bHasSaidHello = TRUE
ENDIF
ENDIF
BREAK
CASE AMBCONV_MISBLOCK_BUSY
IF Private_PlayAmbConvState(group, convPeds, g_eDefaultPlayerChar, group.eConvChar, FAP_AMBIENT_BLOCKED_MISSION) // "We got stuff to do."
Private_SetAmbConvState(group, AMBCONV_MISBLOCK_GOODBYE)
ENDIF
BREAK
CASE AMBCONV_MISBLOCK_GOODBYE
IF Private_PlayAmbConvState(group, convPeds, group.eConvChar, g_eDefaultPlayerChar, FAP_AMBIENT_GOODBYE_OK) // "Ok, see you later."
Private_DismissAmbGroup_IfBlocked(group)
ENDIF
BREAK
//----------------------------------------------------------------------------------------------------------------------------- AMBCONV_DEFBLOCK_HELLO
CASE AMBCONV_DEFBLOCK_HELLO
IF group.bHasSaidHello
OR Private_PlayAmbConvState(group, convPeds, g_eDefaultPlayerChar, group.eConvChar, Private_GetAmbConvGreetPhrase(group)) // "Hello" / "Hello again"
Private_SetAmbConvState(group, AMBCONV_DEFBLOCK_BUSY)
group.bHasSaidHello = TRUE
ENDIF
BREAK
CASE AMBCONV_DEFBLOCK_HELLO_HOME
IF NOT Private_IsMichaelTrevorSafehouse(group.mFriends[group.eConvChar])
IF group.bHasSaidHello
OR Private_PlayAmbConvState(group, convPeds, group.eConvChar, g_eDefaultPlayerChar, Private_GetAmbConvGreetPhrase(group)) // "Hello" / "Hello again" (reversed speaker/listener)
Private_SetAmbConvState(group, AMBCONV_DEFBLOCK_BUSY)
group.bHasSaidHello = TRUE
ENDIF
ELSE
IF group.bHasSaidHello
OR Private_PlayAmbConvState(group, convPeds, g_eDefaultPlayerChar, group.eConvChar, Private_GetAmbConvGreetPhrase(group, TRUE)) // "Hello" / "Hello again" (house version)
Private_SetAmbConvState(group, AMBCONV_DEFBLOCK_BUSY)
group.bHasSaidHello = TRUE
ENDIF
ENDIF
BREAK
CASE AMBCONV_DEFBLOCK_BUSY
IF Private_PlayAmbConvState(group, convPeds, g_eDefaultPlayerChar, group.eConvChar, FAP_AMBIENT_BLOCKED_OK) // "I'm busy."
Private_SetAmbConvState(group, AMBCONV_DEFBLOCK_GOODBYE)
ENDIF
BREAK
CASE AMBCONV_DEFBLOCK_GOODBYE
IF Private_PlayAmbConvState(group, convPeds, group.eConvChar, g_eDefaultPlayerChar, FAP_AMBIENT_GOODBYE_OK) // "Ok, see you later."
Private_DismissAmbGroup_IfBlocked(group)
ENDIF
BREAK
//----------------------------------------------------------------------------------------------------------------------------- END
DEFAULT
CPRINTLN(DEBUG_FRIENDS, "Private_UpdateAmbConv() - Unhandled state: ", GetLabel_enumAmbConvState(group.eConvState))
SCRIPT_ASSERT("Private_UpdateAmbConv() - Unhandled state")
Private_DismissAmbGroup(group)
BREAK
ENDSWITCH
// Disable selector during conversations
IF group.eConvState <> AMBCONV_NULL AND group.eConvState <> AMBCONV_SAFEHOUSE_TRAILER3
DISABLE_SELECTOR_THIS_FRAME()
DISABLE_CELLPHONE_THIS_FRAME_ONLY()
ENDIF
ENDPROC
//-------------------------------------------------------------------------------------------------------------------------------------------
// AmbGroup
//-------------------------------------------------------------------------------------------------------------------------------------------
PROC Private_InitAmbGroup(structAmbGroup& group)
group.bIsSwitchActive = FALSE
group.iGreetCount = 0
group.bHasSaidHello = FALSE
group.bHasJoined = FALSE
group.eConvState = AMBCONV_NULL
group.eConvChar = NO_CHARACTER
group.hConvInput = NEW_CONTEXT_INTENTION
group.bOutroActive = FALSE
group.eOutroChar = NO_CHARACTER
group.hOutroCandidate = NO_CANDIDATE_ID
group.hOutroScenarioBlock = NULL
group.hOutroCamera = NULL
IF ADD_RELATIONSHIP_GROUP("AmbFriendEmpty", group.hEmptyGroup)
// SET_RELATIONSHIP_BETWEEN_GROUPS(ACQUAINTANCE_TYPE_PED_DISLIKE, RELGROUPHASH_PLAYER, group.hEmptyGroup)
// SET_RELATIONSHIP_BETWEEN_GROUPS(ACQUAINTANCE_TYPE_PED_DISLIKE, group.hEmptyGroup, RELGROUPHASH_PLAYER)
ENDIF
group.bAttemptResumeFightAfterSwitch = FALSE
group.bUpdateAfterSwitch = FALSE
enumCharacterList eChar
REPEAT NUM_OF_PLAYABLE_PEDS eChar
Private_InitAmbFriend(group.mFriends[eChar], eChar)
ENDREPEAT
ENDPROC
PROC Private_CleanupAmbGroup(structAmbGroup& group, BOOL bDelete = FALSE)
Private_StopAmbConv(group)
IF group.iGreetCount > 0
Private_DismissAmbGroup(group)
ENDIF
enumCharacterList eChar
REPEAT NUM_OF_PLAYABLE_PEDS eChar
Private_ReleaseAmbFriend(group, group.mFriends[eChar], bDelete)
ENDREPEAT
REMOVE_RELATIONSHIP_GROUP(group.hEmptyGroup)
Private_InitAmbGroup(group)
ENDPROC
PROC Private_UpdateAmbGroup(structAmbGroup& group, structPedsForConversation& convPeds)
//-- Handle switch
IF IS_PLAYER_PED_SWITCH_IN_PROGRESS() OR IS_PLAYER_SWITCH_IN_PROGRESS()
IF group.bIsSwitchActive = FALSE
CPRINTLN(DEBUG_FRIENDS, "Private_UpdateAmbGroup() - Switch started")
// Clear tasks on dest player ped
IF IS_PLAYER_PED_PLAYABLE(g_sPlayerPedRequest.ePed)
AND NOT IS_PED_INJURED(group.mFriends[g_sPlayerPedRequest.ePed].hPed)
CLEAR_PED_TASKS(group.mFriends[g_sPlayerPedRequest.ePed].hPed)
ENDIF
// Dismiss any group
Private_DismissAmbGroup(group)
Private_StopAmbConv(group)
// Store if any members are in fight mode
enumCharacterList eCharLoop
REPEAT NUM_OF_PLAYABLE_PEDS eCharLoop
IF group.mFriends[eCharLoop].eState = AMBFRIEND_DISMISSED
AND group.mFriends[eCharLoop].eDismissMode = AMBMODE_FIGHT
group.bAttemptResumeFightAfterSwitch = TRUE
ENDIF
ENDREPEAT
group.bUpdateAfterSwitch = TRUE
group.bIsSwitchActive = TRUE
ENDIF
// Remove phone for current player ped
// Private_DeleteAmbFriendPhone(group.mFriends[g_eDefaultPlayerChar])
ELIF group.bIsSwitchActive
group.bIsSwitchActive = FALSE
//-- Handle early outs
ELIF g_OnMissionState = MISSION_TYPE_STORY_PREP // IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_STORY_PREP)
// DR: Commenting out these debug outputs as it spams the TTY
// Sam - should this function be getting called repeatedly or should be it be a one time thing?
// CPRINTLN(DEBUG_FRIENDS, "Private_UpdateAmbFriend() - Shutting down for mission")
Private_CleanupAmbGroup(group)
ELIF (g_OnMissionState = MISSION_TYPE_FRIEND_ACTIVITY OR g_OnMissionState = MISSION_TYPE_FRIEND_ACTIVITY_WITH_MG)
AND g_eCurrentActivityLoc < MAX_ACTIVITY_LOCATIONS
// CPRINTLN(DEBUG_FRIENDS, "Private_UpdateAmbFriend() - Shutting down for friend minigame")
Private_CleanupAmbGroup(group)
ELIF g_bInMultiplayer
// CPRINTLN(DEBUG_FRIENDS, "Private_UpdateAmbFriend() - Shutting down for multiplayer")
Private_CleanupAmbGroup(group)
ELSE
// #IF IS_DEBUG_BUILD
// IF IS_KEYBOARD_KEY_JUST_PRESSED(KEY_K)
// enumCharacterList eTempChar
// REPEAT NUM_OF_PLAYABLE_PEDS eTempChar
// IF eTempChar <> g_eDefaultPlayerChar
// AND group.mFriends[eTempChar].eState <> AMBFRIEND_NULL
// Private_SetAmbFriendVulnerable(group, group.mFriends[eTempChar], FALSE)
// ENDIF
// ENDREPEAT
// ENDIF
// #ENDIF
//-- Update friends
enumCharacterList eChar
REPEAT NUM_OF_PLAYABLE_PEDS eChar
Private_UpdateAmbFriend(group, group.mFriends[eChar], convPeds)
ENDREPEAT
// If Trevor/Franklin were fighting, resume fight
IF group.bAttemptResumeFightAfterSwitch
IF group.mFriends[CHAR_FRANKLIN].eState = AMBFRIEND_DISMISSED
IF group.mFriends[CHAR_TREVOR].eState = AMBFRIEND_DISMISSED OR group.mFriends[CHAR_TREVOR].eState = AMBFRIEND_PLAYER
IF NOT IS_PED_INJURED(group.mFriends[CHAR_TREVOR].hPed)
Private_SetAmbFriendThreatResponse(group.mFriends[CHAR_FRANKLIN], FALSE, group.mFriends[CHAR_TREVOR].hPed)
ENDIF
ENDIF
ENDIF
IF group.mFriends[CHAR_TREVOR].eState = AMBFRIEND_DISMISSED
IF group.mFriends[CHAR_FRANKLIN].eState = AMBFRIEND_DISMISSED OR group.mFriends[CHAR_FRANKLIN].eState = AMBFRIEND_PLAYER
IF NOT IS_PED_INJURED(group.mFriends[CHAR_FRANKLIN].hPed)
Private_SetAmbFriendThreatResponse(group.mFriends[CHAR_TREVOR], FALSE, group.mFriends[CHAR_FRANKLIN].hPed)
ENDIF
ENDIF
ENDIF
group.bAttemptResumeFightAfterSwitch = FALSE
ENDIF
group.bUpdateAfterSwitch = FALSE
// Manage greet behaviour
IF group.bOutroActive = FALSE
IF group.iGreetCount > 0
// If greet exists, but isn't allowed, dismiss group members
IF NOT Private_IsAmbGroupAllowed()
Private_DismissAmbGroup(group)
Private_StopAmbConv(group)
ENDIF
// If greet still exists, start conversation
IF group.eConvState = AMBCONV_NULL
IF group.iGreetCount > 0
Private_StartAmbConv(group)
ENDIF
ENDIF
ENDIF
// If greet has ended, reset everything
IF group.iGreetCount = 0
group.bHasSaidHello = FALSE
group.bHasJoined = FALSE
ENDIF
// Update conversation
IF group.eConvState <> AMBCONV_NULL
IF IS_PLAYER_PED_PLAYABLE(group.eConvChar)
AND (group.mFriends[group.eConvChar].eState = AMBFRIEND_GREET OR group.mFriends[group.eConvChar].eState = AMBFRIEND_GREET_SAFEHOUSE OR group.mFriends[group.eConvChar].eState = AMBFRIEND_GREET_OUTSIDE)
Private_UpdateAmbConv(group, convPeds)
ELSE
IF IS_PLAYER_PED_PLAYABLE(group.eConvChar)
Private_DismissAmbFriend(group, group.mFriends[group.eConvChar])
ENDIF
Private_StopAmbConv(group)
ENDIF
ENDIF
ENDIF
ENDIF
// Print debug output
#IF IS_DEBUG_BUILD
DEBUG_DisplayAmbGroupInfo(group)
#ENDIF
ENDPROC