//- 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 "" 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 "" 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 "" 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