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

2868 lines
100 KiB
Scheme
Executable File

// *****************************************************************************************
// *****************************************************************************************
//
// MISSION NAME : friendActivity_memberFriend_private.sch
// AUTHOR : Sam Hackett
// DESCRIPTION : State machine for individual friend activity members
//
// *****************************************************************************************
// *****************************************************************************************
//- commands headers -//
//- script headers -//
USING "ambient_common.sch"
//- public headers -//
USING "shop_public.sch"
//- private headers -//
USING "friendActivity_private.sch"
USING "player_scene_private.sch"
#IF IS_DEBUG_BUILD
//- debug headers -//
#ENDIF
//---------------------------------------------------------------------------------------------------
//-- Consts
//---------------------------------------------------------------------------------------------------
CONST_FLOAT CONST_fFriendCreateDist 300.0 //150.0
CONST_FLOAT CONST_fFriendCreateTooCloseDist 225.0 //65.0
CONST_FLOAT CONST_fFriendRemoveDist 400.0 //250.0//300.0
CONST_FLOAT CONST_fFriendRemoveDistOnFoot 175.0
CONST_FLOAT CONST_fFriendCreateAdhocMin 200.0
CONST_FLOAT CONST_fFriendCreateAdhocMax 250.0
CONST_FLOAT CONST_fFriendArriveSpeed 18.0//20.0
CONST_FLOAT CONST_fDrivewayParkRange 20.0
CONST_FLOAT CONST_fDoorstepWaitRange 1.5
CONST_FLOAT CONST_fRunToPlayerRange 15.0
CONST_FLOAT CONST_fScenarioBlockingBoxSize 10.0
CONST_INT c_iPickupRunBlockDuration 10000
//---------------------------------------------------------------------------------------------------
//-- Enums
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
//-- Friend Debug
//---------------------------------------------------------------------------------------------------
#IF IS_DEBUG_BUILD
PROC DEBUG_DisplayFriendInfo(structFriend& friend, INT iSlot)
TEXT_LABEL_63 tName = GetLabel_enumFriend(GET_FRIEND_FROM_CHAR(friend.eChar))
INT iRow = CONST_iActivityDebugPrintLineTop + iSlot
IF friend.eState = FRIEND_PICKUP
// Buddy name (+ mWaitTimer)
tName += ": FRIEND_PICKUP: "
tName += GET_STRING_FROM_MILISECONDS(ROUND(GET_TIMER_IN_SECONDS(friend.mWaitTimer) * 1000.0))
FLOAT fTimeLimit = CONST_fLateFailInRealMinutes
HUD_COLOURS eTimeColour = HUD_COLOUR_BLUELIGHT
IF gActivity.bIsRural
fTimeLimit = CONST_fLateFailInRealMinutes_rural
eTimeColour = HUD_COLOUR_GREENLIGHT
ENDIF
DrawFriendTimerBar(friend.mWaitTimer, fTimeLimit/60.0, iRow, eTimeColour)
DrawFriendLiteralString(tName, iRow, eTimeColour)
iRow++
// Display dist to friend
TEXT_LABEL_63 tDist = ""
tDist += " loc dist: "
IF DOES_ENTITY_EXIST(friend.hPed) AND NOT IS_PED_INJURED(friend.hPed)
DrawFriendLiteralStringFloat(tDist, VDIST(GET_ENTITY_COORDS(PLAYER_PED_ID()), friend.vDoorstep), iRow, HUD_COLOUR_WHITE)
ELSE
DrawFriendLiteralStringFloat(tDist, VDIST(GET_ENTITY_COORDS(PLAYER_PED_ID()), friend.vDoorstep), iRow, HUD_COLOUR_GREY)
ENDIF
ELIF friend.eState >= FRIEND_FAIL_INJURED
// Buddy name (+ state + mFailTimer)
tName += ": "
tName += GetLabel_enumFriendState(friend.eState)
tName += ": "
IF IS_TIMER_STARTED(gActivity.mFailTimer)
tName += GET_STRING_FROM_MILISECONDS(ROUND(GET_TIMER_IN_SECONDS(gActivity.mFailTimer) * 1000.0))
ELSE
tName += GET_STRING_FROM_MILISECONDS(0)
ENDIF
DrawFriendLiteralString(tName, iRow, HUD_COLOUR_BLUELIGHT)
ELIF friend.eState <> FRIEND_NULL
// Buddy name (+ state)
tName += ": "
tName += GetLabel_enumFriendState(friend.eState)
DrawFriendLiteralString(tName, iRow, HUD_COLOUR_BLUELIGHT)
ENDIF
ENDPROC
#ENDIF
//---------------------------------------------------------------------------------------------------
//-- Member init
//---------------------------------------------------------------------------------------------------
PROC Private_InitFriend(structFriend& friend)
friend.eState = FRIEND_NULL
friend.eChar = NO_CHARACTER
friend.ePickup = NO_FRIEND_LOCATION
friend.tName = ""
friend.hPed = null
friend.hCar = null
friend.hPhone = null
friend.hPedBlip = null
friend.hLocBlip = null
RESTART_TIMER_NOW(friend.mWaitTimer)
friend.vDoorstep = << 0.0, 0.0, 0.0 >>
friend.vDriveway = << 0.0, 0.0, 0.0 >>
friend.iOffsetIndex = 0
friend.hScenarioBlock = NULL
CANCEL_TIMER(friend.mArrivalStoppedTimer)
friend.iArrivalDrivingMode = 0
friend.ePedModel = DUMMY_MODEL_FOR_SCRIPT
friend.eCarModel = DUMMY_MODEL_FOR_SCRIPT
friend.bDoneTrappedDialogue = FALSE
friend.iBlockRunningUntilTime = 0
friend.iCarHealth = 0
friend.iCarShotTime = 0
friend.bWereInVehicleTogether = FALSE
friend.iHealth = 0
friend.bSwitchOverride = FALSE
friend.bIsBeingCalledToCancel = FALSE
friend.bIsShowingPickupCam = FALSE
friend.bForceCreateAsArriving = FALSE
friend.bUseStoredVehicleModel = FALSE
friend.bWasPickedUp = FALSE
friend.bWasMetAmbiently = FALSE
friend.eFailReason = FAF_NoFail
ENDPROC
//---------------------------------------------------------------------------------------------------
//-- Member general utils
//---------------------------------------------------------------------------------------------------
FUNC VEHICLE_SEAT Private_GetMemberSeat(structFriend& friend)
IF gActivity.mFriendA.eChar = friend.eChar
RETURN VS_FRONT_RIGHT
ELSE
RETURN VS_BACK_RIGHT|VS_BACK_LEFT
ENDIF
ENDFUNC
//---------------------------------------------------------------------------------------------------
//-- Member ped utils
//---------------------------------------------------------------------------------------------------
PROC Private_SetFriendAttribs(structFriend& friend, structPedsForConversation& convStruct, BOOL bIsPlayer = FALSE)
IF friend.eState <> FRIEND_NULL AND friend.eState < FRIEND_CANCEL
IF NOT IS_PED_INJURED(friend.hPed)
IF bIsPlayer
// Player controlled
CLEAR_ENTITY_LAST_DAMAGE_ENTITY(friend.hPed)
CLEAR_ENTITY_LAST_WEAPON_DAMAGE(friend.hPed)
IF NOT IS_PED_REGISTERED_TO_ACTIVATE_AUTOMATIC_DOOR(AUTODOOR_MICHAEL_MANSION_GATE, friend.hPed)
REGISTER_PED_TO_ACTIVATE_AUTOMATIC_DOOR(AUTODOOR_MICHAEL_MANSION_GATE, friend.hPed) // Don't know what this is all about (and should it be buddy ped instead of player?)
ENDIF
ADD_FRIEND_CHAR_FOR_DIALOGUE(convStruct, friend.eChar, friend.hPed)
SET_PED_CONFIG_FLAG(friend.hPed, PCF_WillFlyThroughWindscreen, FALSE)
ELSE
// AI controlled
CLEAR_ENTITY_LAST_DAMAGE_ENTITY(friend.hPed)
CLEAR_ENTITY_LAST_WEAPON_DAMAGE(friend.hPed)
friend.iHealth = GET_ENTITY_HEALTH(friend.hPed)
IF NOT IS_PED_REGISTERED_TO_ACTIVATE_AUTOMATIC_DOOR(AUTODOOR_MICHAEL_MANSION_GATE, friend.hPed)
REGISTER_PED_TO_ACTIVATE_AUTOMATIC_DOOR(AUTODOOR_MICHAEL_MANSION_GATE, friend.hPed) // Don't know what this is all about (and should it be buddy ped instead of player?)
ENDIF
ADD_FRIEND_CHAR_FOR_DIALOGUE(convStruct, friend.eChar, friend.hPed)
SET_PED_CAN_BE_TARGETTED(friend.hPed, FALSE)
SET_PED_RELATIONSHIP_GROUP_HASH(friend.hPed, RELGROUPHASH_PLAYER)
// SET_ENTITY_ONLY_DAMAGED_BY_PLAYER(friend.hPed, TRUE)
SET_PED_HELMET(friend.hPed, TRUE)
SET_ENTITY_SHOULD_FREEZE_WAITING_ON_COLLISION(friend.hPed, TRUE)
SET_PED_PATH_CAN_USE_CLIMBOVERS(friend.hPed, TRUE)
SET_PED_PATH_CAN_USE_LADDERS(friend.hPed, TRUE)
SET_PED_PATH_CAN_DROP_FROM_HEIGHT(friend.hPed, TRUE)
SET_PED_PATH_MAY_ENTER_WATER(friend.hPed, TRUE)
SET_PED_PATH_PREFER_TO_AVOID_WATER(friend.hPed, TRUE)
SET_PED_SUFFERS_CRITICAL_HITS(friend.hPed, FALSE)
SET_PED_CAN_BE_KNOCKED_OFF_VEHICLE(friend.hPed, KNOCKOFFVEHICLE_HARD)
SET_PED_CONFIG_FLAG(friend.hPed, PCF_DontActivateRagdollFromBulletImpact, TRUE)
SET_PED_CONFIG_FLAG(friend.hPed, PCF_WillFlyThroughWindscreen, FALSE)
// SET_PED_CONFIG_FLAG(friend.hPed, PCF_TeleportToLeaderVehicle, TRUE)
SET_PED_STAY_IN_VEHICLE_WHEN_JACKED(friend.hPed, TRUE)
SET_PED_CONFIG_FLAG(friend.hPed, PCF_OnlyAttackLawIfPlayerIsWanted, TRUE)
SET_PED_CONFIG_FLAG(friend.hPed, PCF_LawWillOnlyAttackIfPlayerIsWanted, TRUE)
SET_PED_DIES_INSTANTLY_IN_WATER(friend.hPed, FALSE)
SET_PED_DIES_IN_WATER(friend.hPed, FALSE)
IF IS_PED_USING_ACTION_MODE(friend.hPed)
SET_PED_USING_ACTION_MODE(friend.hPed, FALSE)
ENDIF
// Fighting chars
IF friend.eChar <> CHAR_JIMMY AND friend.eChar <> CHAR_AMANDA
IF NOT friend.bWasPickedUp
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(friend.hPed, TRUE)
ELSE
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(friend.hPed, FALSE)
ENDIF
SET_PED_CONFIG_FLAG(friend.hPed, PCF_RunFromFiresAndExplosions, FALSE)
SET_PED_COMBAT_ATTRIBUTES(friend.hPed, CA_ALWAYS_FIGHT, TRUE)
SET_PED_COMBAT_ATTRIBUTES(friend.hPed, CA_ALWAYS_FLEE, FALSE)
// SET_PED_FLEE_ATTRIBUTES(friend.hPed, FA_NEVER_FLEE, TRUE)
SET_PED_COMBAT_ATTRIBUTES(friend.hPed, CA_REQUIRES_LOS_TO_SHOOT, TRUE)
SET_PED_COMBAT_ATTRIBUTES(friend.hPed, CA_CAN_SHOOT_WITHOUT_LOS, FALSE)
SET_PED_COMBAT_ATTRIBUTES(friend.hPed, CA_CAN_FIGHT_ARMED_PEDS_WHEN_NOT_ARMED, TRUE)
SET_PED_COMBAT_ATTRIBUTES(friend.hPed, CA_CAN_USE_DYNAMIC_STRAFE_DECISIONS, TRUE)
SET_PED_COMBAT_ATTRIBUTES(friend.hPed, CA_DO_DRIVEBYS, TRUE)
SET_PED_COMBAT_ATTRIBUTES(friend.hPed, CA_USE_COVER, TRUE)
SET_PED_COMBAT_ATTRIBUTES(friend.hPed, CA_USE_VEHICLE, TRUE)
SET_PED_COMBAT_ATTRIBUTES(friend.hPed, CA_LEAVE_VEHICLES, FALSE)
SET_PED_FLEE_ATTRIBUTES(friend.hPed, FA_USE_VEHICLE, FALSE)
SET_PED_FLEE_ATTRIBUTES(friend.hPed, FA_NEVER_FLEE, FALSE)
SET_PED_FLEE_ATTRIBUTES(friend.hPed, FA_DISABLE_COWER, TRUE)
SET_PED_FLEE_ATTRIBUTES(friend.hPed, FA_DISABLE_EXIT_VEHICLE, FALSE)
SET_PED_FLEE_ATTRIBUTES(friend.hPed, FA_DISABLE_FLEE_FROM_INDIRECT_THREATS, FALSE)
SET_PED_FLEE_ATTRIBUTES(friend.hPed, FA_FORCE_EXIT_VEHICLE, TRUE)
SET_PED_FLEE_ATTRIBUTES(friend.hPed, FA_DISABLE_HESITATE_IN_VEHICLE, TRUE)
SET_PED_COMBAT_ABILITY(friend.hPed, CAL_PROFESSIONAL)
SET_PED_COMBAT_MOVEMENT(friend.hPed, CM_WILLADVANCE)
// Fleeing chars
ELSE
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(friend.hPed, TRUE)
SET_PED_COMBAT_ATTRIBUTES(friend.hPed, CA_ALWAYS_FIGHT, FALSE)
SET_PED_COMBAT_ATTRIBUTES(friend.hPed, CA_USE_COVER, FALSE)
SET_PED_FLEE_ATTRIBUTES(friend.hPed, FA_NEVER_FLEE, TRUE)
SET_PED_FLEE_ATTRIBUTES(friend.hPed, FA_USE_COVER, FALSE)
SET_PED_FLEE_ATTRIBUTES(friend.hPed, FA_CAN_SCREAM, TRUE)
//// SET_PED_COMBAT_ATTRIBUTES(friend.hPed, CA_ALWAYS_FIGHT, TRUE)
// SET_PED_COMBAT_ATTRIBUTES(friend.hPed, CA_ALWAYS_FLEE, TRUE)
//// SET_PED_COMBAT_ATTRIBUTES(friend.hPed, CA_JUST_SEEK_COVER, TRUE)
// SET_PED_COMBAT_ATTRIBUTES(friend.hPed, CA_USE_COVER, TRUE)
//// SET_PED_COMBAT_ATTRIBUTES(friend.hPed, CA_FLEE_WHILST_IN_VEHICLE, FALSE)
////
//// SET_PED_COMBAT_ATTRIBUTES(friend.hPed, CA_USE_VEHICLE, TRUE)
////
//// SET_PED_FLEE_ATTRIBUTES(friend.hPed, FA_NEVER_FLEE, FALSE)
// SET_PED_FLEE_ATTRIBUTES(friend.hPed, FA_CAN_SCREAM, TRUE)
//
// SET_PED_FLEE_ATTRIBUTES(friend.hPed, FA_USE_COVER, TRUE)
// SET_PED_FLEE_ATTRIBUTES(friend.hPed, FA_DISABLE_COWER, FALSE)
//// SET_PED_FLEE_ATTRIBUTES(friend.hPEd, FA_COWER_INSTEAD_OF_FLEE, TRUE)
//
//// SET_PED_FLEE_ATTRIBUTES(friend.hPed, FA_PREFER_PAVEMENTS, TRUE)
//// SET_PED_FLEE_ATTRIBUTES(friend.hPed, FA_RETURN_TO_ORIGNAL_POSITION_AFTER_FLEE, TRUE)
//// SET_PED_FLEE_ATTRIBUTES(friend.hPed, FA_USE_COVER, TRUE)
ENDIF
ENDIF
ENDIF
ENDIF
ENDPROC
PROC Private_ClearFriendAttribs(structFriend& friend)
IF friend.eState <> FRIEND_NULL
IF NOT IS_PED_INJURED(friend.hPed)
SET_PED_SUFFERS_CRITICAL_HITS(friend.hPed, TRUE)
SET_PED_CAN_BE_KNOCKED_OFF_VEHICLE(friend.hPed, KNOCKOFFVEHICLE_DEFAULT)
SET_PED_CONFIG_FLAG(friend.hPed, PCF_WillFlyThroughWindscreen, TRUE)
SET_PED_CONFIG_FLAG(friend.hPed, PCF_DontActivateRagdollFromBulletImpact, FALSE)
IF DOES_GROUP_EXIST(PLAYER_GROUP_ID()) AND IS_PED_GROUP_MEMBER(friend.hPed, PLAYER_GROUP_ID())
SET_PED_CAN_TELEPORT_TO_GROUP_LEADER(friend.hPed, PLAYER_GROUP_ID(), FALSE)
ENDIF
SET_ENTITY_ONLY_DAMAGED_BY_PLAYER(friend.hPed, FALSE)
IF friend.eChar <> CHAR_MICHAEL
IF IS_PED_REGISTERED_TO_ACTIVATE_AUTOMATIC_DOOR(AUTODOOR_MICHAEL_MANSION_GATE, friend.hPed)
UNREGISTER_PED_TO_ACTIVATE_AUTOMATIC_DOOR(AUTODOOR_MICHAEL_MANSION_GATE, friend.hPed)
ENDIF
ENDIF
IF friend.hPed <> PLAYER_PED_ID()
IF IS_PED_IN_GROUP(friend.hPed)
REMOVE_PED_FROM_GROUP(friend.hPed)
ENDIF
ENDIF
ENDIF
ENDIF
ENDPROC
PROC Private_SyncFriendPed(structFriend& friend)
IF friend.eState <> FRIEND_NULL
// Current player
IF friend.eChar = GET_CURRENT_PLAYER_PED_ENUM()
IF friend.hPed <> PLAYER_PED_ID()
CPRINTLN(DEBUG_FRIENDS, "Private_SyncFriendPed(", GetLabel_enumCharacterList(friend.eChar), "):- Grabbed ped from current player")
friend.hPed = PLAYER_PED_ID()
Private_SetFriendAttribs(friend, gActivity.convPedsDefault, TRUE)
ENDIF
EXIT
ELIF friend.eChar <> NO_CHARACTER
// Playable selector ped
IF IS_PLAYER_PED_PLAYABLE( friend.eChar )
SELECTOR_SLOTS_ENUM eSelectorSlot = GET_SELECTOR_SLOT_FROM_PLAYER_PED_ENUM(friend.eChar)
PED_INDEX hSelectorPed = g_sPlayerPedRequest.sSelectorPeds.pedID[eSelectorSlot]
IF NOT DOES_ENTITY_EXIST(friend.hPed)
friend.hPed = hSelectorPed
ELSE
IF NOT DOES_ENTITY_EXIST(hSelectorPed)
CPRINTLN(DEBUG_FRIENDS, "Private_SyncFriendPed(", GetLabel_enumCharacterList(friend.eChar), ") - Member has ped, but selector doesn't")
SCRIPT_ASSERT("Private_SyncFriendPed() - Member has ped, but selector doesn't (playable)")
DELETE_PED(friend.hPed)
ENDIF
ENDIF
// NPC cached ped
ELSE
enumFriend eFriend = GET_FRIEND_FROM_CHAR(friend.eChar)
INT iIndex = ENUM_TO_INT(eFriend) - NUM_OF_PLAYABLE_PEDS
IF eFriend = NO_FRIEND
SCRIPT_ASSERT("Private_SyncFriendPed() - Can't get friend handle for char")
CPRINTLN(DEBUG_FRIENDS, "Private_SyncFriendPed(", GetLabel_enumCharacterList(friend.eChar), ") - Can't get friend handle for char")
ELSE
IF NOT DOES_ENTITY_EXIST(friend.hPed)
friend.hPed = g_pGlobalFriends[iIndex]
ELSE
IF NOT DOES_ENTITY_EXIST(g_pGlobalFriends[iIndex])
CPRINTLN(DEBUG_FRIENDS, "Private_SyncFriendPed(", GetLabel_enumCharacterList(friend.eChar), ") - Member has ped, but selector doesn't")
SCRIPT_ASSERT("Private_SyncFriendPed() - Member has ped, but selector doesn't (npc)")
DELETE_PED(friend.hPed)
ENDIF
ENDIF
ENDIF
ENDIF
// If got a ped and I don't own it, take control of it
IF NOT IS_PED_INJURED(friend.hPed)
IF NOT DOES_ENTITY_BELONG_TO_THIS_SCRIPT(friend.hPed)
CPRINTLN(DEBUG_FRIENDS, "Private_SyncFriendPed(", GetLabel_enumCharacterList(friend.eChar), "):- Grabbed ped from selector")
IF friend.bWasPickedUp
SCRIPT_ASSERT("Private_SyncFriendPed() - Grabbed ped from selector, even though friend has already been picked up")
ENDIF
SET_ENTITY_AS_MISSION_ENTITY(friend.hPed, TRUE, TRUE)
Private_SetFriendAttribs(friend, gActivity.convPedsDefault)
ENDIF
ENDIF
ENDIF
ENDIF
ENDPROC
//PROC Private_StorePedInfo(PED_INDEX hPed, enumCharacterList eChar)
// STORE_PLAYER_PED_INFO(hPed)
//
// IF IS_PLAYER_PED_PLAYABLE(eChar)
// STORE_VEH_DATA_FROM_PED(hPed, g_sPlayerLastVeh[eChar], g_vPlayerLastVehCoordOff[eChar], g_fPlayerLastVehHeadOff[eChar], g_ePlayerLastVehState[eChar])
// ENDIF
//ENDPROC
PROC Private_SetPedAmandaOutfit(PED_INDEX PedIndex)
FAMILY_COMP_NAME_ENUM eFamilyOutfitName = FC_AMANDA_OUTFIT_leavingNoGlasses
FAMILY_COMP_NAME_ENUM ePC_head, ePC_hair, ePC_torso, ePC_leg, ePC_feet, ePC_hand, ePC_special, ePC_special2, ePC_decl, ePC_berd, ePC_teeth, ePC_jbib
FAMILY_PROP_BIT_ENUM ePC_prop
IF GetOutfitForFamilyMember(eFamilyOutfitName,
ePC_head, ePC_hair, ePC_torso, ePC_leg, ePC_feet, ePC_hand, ePC_special, ePC_special2, ePC_decl, ePC_berd, ePC_teeth, ePC_jbib, ePC_prop)
// SetComponentForFamilyMember(PedIndex, ePC_head)
SetComponentForFamilyMember(PedIndex, ePC_hair)
SetComponentForFamilyMember(PedIndex, ePC_torso)
SetComponentForFamilyMember(PedIndex, ePC_leg)
SetComponentForFamilyMember(PedIndex, ePC_feet)
SetComponentForFamilyMember(PedIndex, ePC_hand)
SetComponentForFamilyMember(PedIndex, ePC_special)
SetComponentForFamilyMember(PedIndex, ePC_special2)
SetComponentForFamilyMember(PedIndex, ePC_decl)
SetComponentForFamilyMember(PedIndex, ePC_berd)
SetComponentForFamilyMember(PedIndex, ePC_teeth)
SetComponentForFamilyMember(PedIndex, ePC_jbib)
IF IS_BITMASK_ENUM_AS_ENUM_SET(ePC_prop, FAMILY_PROP_NULL)
CLEAR_ALL_PED_PROPS(pedIndex)
ENDIF
IF IS_BITMASK_ENUM_AS_ENUM_SET(ePC_prop, FPB_AMANDA_GLASSES)
CLEAR_ALL_PED_PROPS(pedIndex)
SET_PED_PROP_INDEX(pedIndex, ANCHOR_EYES, 0) //amanda glasses
ENDIF
IF IS_BITMASK_ENUM_AS_ENUM_SET(ePC_prop, FPB_AMANDA_CUCUMBERS)
CLEAR_ALL_PED_PROPS(pedIndex)
SET_PED_PROP_INDEX(pedIndex, ANCHOR_EYES, 1) //amanda cucumbers
ENDIF
ENDIF
ENDPROC
//---------------------------------------------------------------------------------------------------
//-- Member loc utils
//---------------------------------------------------------------------------------------------------
PROC Private_SetFriendSwitchOverride(structFriend& friend)
IF friend.eState <> FRIEND_NULL
#IF IS_DEBUG_BUILD
TEXT_LABEL_63 tFriend = GetLabel_enumCharacterList(friend.eChar)
TEXT_LABEL_63 tLocation = GetLabel_enumFriendLocation(friend.ePickup)
CPRINTLN(DEBUG_FRIENDS, "Private_SetFriendSwitchOverride(", tFriend, ") At location ", tLocation)
#ENDIF
IF IS_PLAYER_PED_PLAYABLE(friend.eChar)
IF friend.ePickup < MAX_FRIEND_LOCATIONS
VECTOR vFacing = friend.vDriveway - friend.vDoorstep
SET_PED_SWITCH_OVERRIDE(friend.eChar, friend.vDoorstep, GET_HEADING_FROM_VECTOR_2D(vFacing.x, vFacing.y))
friend.bSwitchOverride = TRUE
ENDIF
ENDIF
ENDIF
ENDPROC
PROC Private_ClearFriendSwitchOverride(structFriend& friend)
IF friend.eState <> FRIEND_NULL
IF IS_PLAYER_PED_PLAYABLE(friend.eChar)
IF friend.bSwitchOverride
IF (g_SavedGlobals.sPlayerSceneData.g_ePlayerLastScene[friend.eChar] = PR_SCENE_M_OVERRIDE)
OR (g_SavedGlobals.sPlayerSceneData.g_ePlayerLastScene[friend.eChar] = PR_SCENE_F_OVERRIDE)
OR (g_SavedGlobals.sPlayerSceneData.g_ePlayerLastScene[friend.eChar] = PR_SCENE_T_OVERRIDE)
CLEAR_PED_SWITCH_OVERRIDE(friend.eChar)
ELSE
#IF IS_DEBUG_BUILD
TEXT_LABEL_63 tFriend = GetLabel_enumCharacterList(friend.eChar)
TEXT_LABEL_63 tLocation = GetLabel_enumFriendLocation(friend.ePickup)
TEXT_LABEL_63 tScene = Get_String_From_Ped_Request_Scene_Enum(g_SavedGlobals.sPlayerSceneData.g_ePlayerLastScene[friend.eChar])
CPRINTLN(DEBUG_FRIENDS, "Private_ClearFriendSwitchOverride(", tFriend, ") Set for location ", tLocation, " but switch scene is not overriden (it's ", tScene, ")")
#ENDIF
ENDIF
friend.bSwitchOverride = FALSE
ENDIF
ENDIF
ENDIF
ENDPROC
PROC Private_SetFriendScenarioBlocking(structFriend& friend)
IF friend.eState <> FRIEND_NULL
IF friend.ePickup < MAX_FRIEND_LOCATIONS
IF friend.hScenarioBlock = NULL
FLOAT fHalfSize = CONST_fScenarioBlockingBoxSize/2.0
VECTOR vHalfSize = <<fHalfSize, fHalfSize, fHalfSize>>
VECTOR vMin = FriendLoc_GetCoord(friend.ePickup) - vHalfSize
VECTOR vMax = FriendLoc_GetCoord(friend.ePickup) + vHalfSize
friend.hScenarioBlock = ADD_SCENARIO_BLOCKING_AREA(vMin, vMax)
ELSE
CPRINTLN(DEBUG_FRIENDS, "Private_SetFriendScenarioBlocking(", GetLabel_enumCharacterList(friend.eChar), ") - Trying to set scenario blocking, when blocking has already been set")
SCRIPT_ASSERT("Private_SetFriendScenarioBlocking() - Trying to set scenario blocking, when blocking has already been set")
ENDIF
ENDIF
ENDIF
ENDPROC
PROC Private_ClearFriendScenarioBlocking(structFriend& friend)
IF friend.eState <> FRIEND_NULL
IF friend.ePickup < MAX_FRIEND_LOCATIONS
IF friend.hScenarioBlock <> NULL
REMOVE_SCENARIO_BLOCKING_AREA(friend.hScenarioBlock)
friend.hScenarioBlock = NULL
// ELSE
// CPRINTLN(DEBUG_FRIENDS, "Private_ClearFriendScenarioBlocking(", GetLabel_enumCharacterList(friend.eChar), ") - Trying to clear scenario blocking, but not block is set")
// SCRIPT_ASSERT("Private_ClearFriendScenarioBlocking() - Trying to clear scenario blocking, but not block is set")
ENDIF
ENDIF
ENDIF
ENDPROC
PROC Private_SetFriendPickupLocOffset(structFriend& friend, INT iOffsetIndex)
IF friend.ePickup < MAX_FRIEND_LOCATIONS
// Get offset for this particular friend
VECTOR vOffset
IF iOffsetIndex = 0
vOffset = g_FriendLocations[friend.ePickup].vPedOffsetA
ELIF iOffsetIndex = 1
vOffset = g_FriendLocations[friend.ePickup].vPedOffsetB
ELSE
SCRIPT_ASSERT("Private_SetMemberDestOffset() - iOffsetIndex must be 0 or 1")
vOffset = g_FriendLocations[friend.ePickup].vPedOffsetB
iOffsetIndex = 1
ENDIF
// Setup shorthand vectors
friend.vDriveway = g_FriendLocations[friend.ePickup].vPickupCoord
friend.vDoorstep = g_FriendLocations[friend.ePickup].vPickupCoord + vOffset
friend.iOffsetIndex = iOffsetIndex
ENDIF
ENDPROC
PROC Private_UpdateFriendPickupLocToNearest(structFriend& friend)
IF friend.eState = FRIEND_NULL
SCRIPT_ASSERT("Private_UpdateFriendPickupLocToNearest() - passed friend is not valid")
ENDIF
// Store locations for each friend
enumFriendLocation arrayLocations[3]
arrayLocations[0] = gActivity.mPlayer.ePickup
arrayLocations[1] = gActivity.mFriendA.ePickup
arrayLocations[2] = gActivity.mFriendB.ePickup
// Find closest location
IF NOT IS_PED_INJURED(friend.hPed)
VECTOR vFriend = GET_ENTITY_COORDS(friend.hPed)
FLOAT fClosest = 999999.0*999999.0
INT iClosest = -1
// (Don't need to do this, as all three locations are now included in loop)
// IF friend.ePickup <> NO_FRIEND_LOCATION
// fClosest = VDIST2(vFriend, g_FriendLocations[friend.ePickup].vPickupCoord)
// ENDIF
INT i
REPEAT COUNT_OF(arrayLocations) i
IF arrayLocations[i] < MAX_FRIEND_LOCATIONS
FLOAT fDist = VDIST2(vFriend, g_FriendLocations[arrayLocations[i]].vPickupCoord)
IF fClosest > fDist
fClosest = fDist
iClosest = i
ENDIF
ENDIF
ENDREPEAT
// If have a better loc than members current one, set it as home destination
IF iClosest <> -1
friend.ePickup = arrayLocations[iClosest]
ENDIF
ENDIF
// Update for location
IF friend.ePickup < MAX_FRIEND_LOCATIONS
#IF IS_DEBUG_BUILD
TEXT_LABEL_63 tFriend = GetLabel_enumCharacterList(friend.eChar)
TEXT_LABEL_63 tPickup = GetLabel_enumFriendLocation(friend.ePickup)
CPRINTLN(DEBUG_FRIENDS, "Private_UpdateFriendPickupLocToNearest(", tFriend, "):- ", tPickup)
#ENDIF
Private_SetFriendPickupLocOffset(friend, 0)
ELSE
#IF IS_DEBUG_BUILD
TEXT_LABEL_63 tFriend = GetLabel_enumCharacterList(friend.eChar)
CPRINTLN(DEBUG_FRIENDS, "Private_UpdateFriendPickupLocToNearest(", tFriend, ") <NO_FRIEND_LOCATION>")
#ENDIF
SCRIPT_ASSERT("Private_UpdateFriendPickupLocToNearest() - No location")
ENDIF
ENDPROC
//---------------------------------------------------------------------------------------------------
//-- Member creation / deletion
//---------------------------------------------------------------------------------------------------
FUNC BOOL Private_TryCreateFriend_AtDest(structFriend& friend, BOOL bIgnoreTooCloseCheck = FALSE)
IF friend.ePickup <> NO_FRIEND_LOCATION
IF friend.bForceCreateAsArriving = FALSE
VECTOR vCreatePos = friend.vDoorstep
VECTOR vLookAt = friend.vDriveway - friend.vDoorstep
FLOAT fCreateRot = GET_HEADING_FROM_VECTOR_2D(vLookAt.x, vLookAt.y)
FLOAT fTooCloseDist = CONST_fFriendCreateTooCloseDist
IF Util_IsPedInsideRange(PLAYER_PED_ID(), vCreatePos, CONST_fFriendCreateDist)
AND (Util_IsPedOutsideRange(PLAYER_PED_ID(), vCreatePos, fTooCloseDist) OR bIgnoreTooCloseCheck)
// Create ped
BOOL bSuccess
IF IS_PLAYER_PED_PLAYABLE(friend.eChar)
bSuccess = CREATE_PLAYER_PED_ON_FOOT(friend.hPed, friend.eChar, vCreatePos, fCreateRot, TRUE)
ELSE
bSuccess = CREATE_NPC_PED_ON_FOOT(friend.hPed, friend.eChar, vCreatePos, fCreateRot, TRUE)
ENDIF
// If it's Amanda, change from default outfit
IF NOT IS_PED_INJURED(friend.hPed) AND friend.eChar = CHAR_AMANDA
// SetOutfitForFamilyMember(friend.hPed, FC_AMANDA_OUTFIT_leavingGlasses)
Private_SetPedAmandaOutfit(friend.hPed) // Fix for 1976287: Special variation of SetOutfitForFamilyMember() that doesn't set the head (because Amanda's main outdoors outift has her makeup running from crying)
ENDIF
IF bSuccess
CPRINTLN(DEBUG_FRIENDS, "ActivityPickup: Created buddy (AtDest) ", GetLabel_enumCharacterList(friend.eChar))
Private_SetFriendAttribs(friend, gActivity.convPedsDefault)
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_TryCreateFriend_ArrivingAtDest(structFriend& friend)
IF friend.ePickup <> NO_FRIEND_LOCATION
// If player is waiting at doorstep
IF Util_IsPedInsideRange(PLAYER_PED_ID(), friend.vDoorstep, CONST_fIsPlayerAtDoorstep)
VECTOR vCreatePos = g_FriendLocations[friend.ePickup].vSpawnPos
FLOAT fCreateRot = g_FriendLocations[friend.ePickup].fSpawnRot
// If there is no set spawn point, try to generate one
IF vCreatePos.x = 0.0
AND vCreatePos.y = 0.0
AND vCreatePos.z = 0.0
VECTOR vPlayerPos = GET_ENTITY_COORDS(PLAYER_PED_ID())
VECTOR vResultPos
VECTOR vResultDir
IF GENERATE_VEHICLE_CREATION_POS_FROM_PATHS(vPlayerPos, vResultPos, vResultDir, 0, 180, CONST_fFriendCreateAdhocMin, FALSE, TRUE, FALSE)
AND GET_DISTANCE_BETWEEN_COORDS(vPlayerPos, vResultPos) < CONST_fFriendCreateAdhocMax
vResultDir = vPlayerPos - vResultPos
vCreatePos = vResultPos
fCreateRot = GET_HEADING_FROM_VECTOR_2D(vResultDir.x, vResultDir.y)
ENDIF
ENDIF
// If managed to get spawn point, try to create...
IF vCreatePos.x <> 0.0
OR vCreatePos.y <> 0.0
OR vCreatePos.z <> 0.0
// Set required models
MODEL_NAMES pedModel
MODEL_NAMES vehModel = ASEA
BOOL bSuccess = FALSE
PED_VEH_DATA_STRUCT sVehData
IF friend.bUseStoredVehicleModel
IF NOT IS_PLAYER_PED_PLAYABLE(friend.eChar)
OR g_ePlayerLastVehState[friend.eChar] <> PTVS_2_playerInVehicle
OR g_sPlayerLastVeh[friend.eChar].model = DUMMY_MODEL_FOR_SCRIPT
OR IS_VEHICLE_MODEL_ON_BLACKLIST(g_sPlayerLastVeh[friend.eChar].model)
friend.bUseStoredVehicleModel = FALSE
ENDIF
ENDIF
IF friend.bUseStoredVehicleModel // Set required models (playable char in stored car)
vehModel = g_sPlayerLastVeh[friend.eChar].model
pedModel = GET_PLAYER_PED_MODEL(friend.eChar)
ELIF IS_PLAYER_PED_PLAYABLE(friend.eChar) // Set required models (playable char)
GET_PLAYER_VEH_DATA(friend.eChar, sVehData, VEHICLE_TYPE_CAR)
vehModel = sVehData.model
pedModel = GET_PLAYER_PED_MODEL(friend.eChar)
ELIF friend.eChar = CHAR_JIMMY // Set required models (Jimmy rides a bike)
GET_NPC_VEH_DATA(friend.eChar, sVehData, VEHICLE_TYPE_BIKE)
vehModel = sVehData.model
pedModel = GET_NPC_PED_MODEL(friend.eChar)
ELSE // Set required models (npc char)
GET_NPC_VEH_DATA(friend.eChar, sVehData, VEHICLE_TYPE_CAR)
vehModel = sVehData.model
pedModel = GET_NPC_PED_MODEL(friend.eChar)
ENDIF
IF vehModel = DUMMY_MODEL_FOR_SCRIPT
vehModel = ASEA
ENDIF
// Load models + create
REQUEST_MODEL(pedModel)
REQUEST_MODEL(vehModel)
IF HAS_MODEL_LOADED(vehModel)
AND HAS_MODEL_LOADED(pedModel)
IF IS_PLAYER_PED_PLAYABLE(friend.eChar) // Create vehicle and ped (playable)
BOOL bCarSuccess = FALSE
IF friend.bUseStoredVehicleModel
bCarSuccess = CREATE_STORED_VEHICLE(friend.hCar, friend.eChar, vCreatePos, fCreateRot, TRUE, g_sPlayerLastVeh[friend.eChar])
ELSE
bCarSuccess = CREATE_PLAYER_VEHICLE(friend.hCar, friend.eChar, vCreatePos, fCreateRot, TRUE, VEHICLE_TYPE_CAR)
ENDIF
IF bCarSuccess
bSuccess = CREATE_PLAYER_PED_INSIDE_VEHICLE(friend.hPed, friend.eChar, friend.hCar, VS_DRIVER, TRUE)
ENDIF
ELSE // Create vehicle and ped (npc)
IF CREATE_NPC_VEHICLE(friend.hCar, friend.eChar, vCreatePos, fCreateRot, TRUE, VEHICLE_TYPE_CAR)
bSuccess = CREATE_NPC_PED_INSIDE_VEHICLE(friend.hPed, friend.eChar, friend.hCar, VS_DRIVER, TRUE)
// If it's Amanda, change from default outfit
IF NOT IS_PED_INJURED(friend.hPed) AND friend.eChar = CHAR_AMANDA
SetOutfitForFamilyMember(friend.hPed, FC_AMANDA_OUTFIT_leavingNoGlasses)
ENDIF
ENDIF
ENDIF
ENDIF
IF bSuccess
IF friend.bUseStoredVehicleModel
CPRINTLN(DEBUG_FRIENDS, "ActivityPickup: Created buddy (ArrivingAtDest) ", GetLabel_enumCharacterList(friend.eChar), " - using backed up vehicle model")
ELSE
CPRINTLN(DEBUG_FRIENDS, "ActivityPickup: Created buddy (ArrivingAtDest) ", GetLabel_enumCharacterList(friend.eChar), " - using standard vehicle model")
ENDIF
IF DOES_ENTITY_EXIST(friend.hCar) AND NOT IS_ENTITY_DEAD(friend.hCar)
SET_ENTITY_COORDS(friend.hCar, vCreatePos)
SET_ENTITY_HEADING(friend.hCar, fCreateRot)
SET_VEHICLE_ON_GROUND_PROPERLY(friend.hCar)
ENDIF
Private_SetFriendAttribs(friend, gActivity.convPedsDefault)
friend.bForceCreateAsArriving = FALSE
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_TryRemoveFriend(structFriend& friend, BOOL bForceDelete = FALSE, BOOL bAllowVehicleBackup = FALSE)
BOOL bRemove = FALSE
BOOL bFallen = FALSE
FLOAT fGround
IF bForceDelete
OR Util_IsPedOutsideRangePed(PLAYER_PED_ID(), friend.hPed, CONST_fFriendRemoveDist)
bRemove = TRUE
ELIF (IS_PED_FALLING(friend.hPed) OR IS_PED_IN_PARACHUTE_FREE_FALL(friend.hPed))
AND (NOT HAS_COLLISION_LOADED_AROUND_ENTITY(friend.hPed) OR NOT GET_GROUND_Z_FOR_3D_COORD(GET_ENTITY_COORDS(friend.hPed), fGround) OR fGround < -200.0)
CPRINTLN(DEBUG_FRIENDS, "Private_TryRemoveFriend(): fallen through world??...")
bRemove = TRUE
bFallen = TRUE
ELSE
IF IS_PED_ON_FOOT(friend.hPed)
AND Util_IsPedInsideRange(PLAYER_PED_ID(), friend.vDoorstep, CONST_fIsPlayerAtDoorstep)
AND Util_IsPedOutsideRangePed(PLAYER_PED_ID(), friend.hPed, CONST_fFriendRemoveDistOnFoot)
bRemove = TRUE
ENDIF
ENDIF
IF bRemove
CPRINTLN(DEBUG_FRIENDS, "Private_TryRemoveFriend(): Removed buddy ", GetLabel_enumCharacterList(friend.eChar))
VEHICLE_INDEX hVehicle = null
IF IS_PED_IN_ANY_VEHICLE(friend.hPed)
hVehicle = GET_VEHICLE_PED_IS_IN(friend.hPed)
ENDIF
IF IS_PLAYER_PED_PLAYABLE(friend.eChar) AND bFallen = FALSE
SAFE_AMBIENT_STORE_PLAYER_PED_INFO(friend.hPed) // STORE_PLAYER_PED_INFO(friend.hPed)
IF bAllowVehicleBackup
CPRINTLN(DEBUG_FRIENDS, "Private_TryRemoveFriend() - Backup vehicle data for next creation")
STORE_VEH_DATA_FROM_VEH(friend.hPed, hVehicle, g_sPlayerLastVeh[friend.eChar], g_vPlayerLastVehCoord[friend.eChar], g_fPlayerLastVehHead[friend.eChar], g_ePlayerLastVehState[friend.eChar], g_ePlayerLastVehGen[friend.eChar])
friend.bUseStoredVehicleModel = TRUE
ENDIF
ENDIF
DELETE_PED(friend.hPed)
friend.hPed = NULL
// TODO: Delete phone?
IF DOES_ENTITY_EXIST(hVehicle) AND DOES_ENTITY_BELONG_TO_THIS_SCRIPT(hVehicle)
DELETE_VEHICLE(hVehicle)
// ELIF DOES_ENTITY_EXIST(g_vPlayerVeh[friend.eChar])
// SET_VEHICLE_AS_NO_LONGER_NEEDED(g_vPlayerVeh[friend.eChar])
ENDIF
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_GetFriendAnimDict(structFriend& friend, TEXT_LABEL_63& tAnimDict)
IF friend.eChar = CHAR_MICHAEL tAnimDict = "friends@frm@ig_1" RETURN TRUE
ELIF friend.eChar = CHAR_FRANKLIN tAnimDict = "friends@frf@ig_1" RETURN TRUE
ELIF friend.eChar = CHAR_TREVOR tAnimDict = "friends@frt@ig_1" RETURN TRUE
ELIF friend.eChar = CHAR_LAMAR tAnimDict = "friends@frl@ig_1" RETURN TRUE
ELIF friend.eChar = CHAR_JIMMY tAnimDict = "friends@frj@ig_1" RETURN TRUE
ELIF friend.eChar = CHAR_AMANDA tAnimDict = "friends@fra@ig_1" RETURN TRUE
ENDIF
SCRIPT_ASSERT("Private_GetFriendAnimDict() - passed friend is not a valid friend char")
RETURN FALSE
ENDFUNC
FUNC BOOL Private_GetFriendAnim_Impatient(structFriend& friend, TEXT_LABEL_63& tAnim)
INT iVariation
IF friend.eChar = CHAR_MICHAEL
iVariation = GET_RANDOM_INT_IN_RANGE(0, 3)
IF iVariation = 0 tAnim = "impatient_idle_a"
ELIF iVariation = 1 tAnim = "impatient_idle_b"
ELSE tAnim = "impatient_idle_c"
ENDIF
RETURN TRUE
ELIF friend.eChar = CHAR_FRANKLIN
iVariation = GET_RANDOM_INT_IN_RANGE(0, 4)
IF iVariation = 0 tAnim = "impatient_idle_a"
ELIF iVariation = 1 tAnim = "impatient_idle_b"
ELIF iVariation = 2 tAnim = "impatient_idle_c"
ELSE tAnim = "impatient_idle_d"
ENDIF
RETURN TRUE
ELIF friend.eChar = CHAR_TREVOR
iVariation = GET_RANDOM_INT_IN_RANGE(0, 3)
IF iVariation = 0 tAnim = "trevor_impatient_wait_1"
ELIF iVariation = 1 tAnim = "trevor_impatient_wait_2"
ELIF iVariation = 2 tAnim = "trevor_impatient_wait_3"
ELSE tAnim = "trevor_impatient_wait_4"
ENDIF
RETURN TRUE
ELIF friend.eChar = CHAR_LAMAR
iVariation = GET_RANDOM_INT_IN_RANGE(0, 3)
IF iVariation = 0 tAnim = "idle_a_lamar"
ELIF iVariation = 1 tAnim = "idle_b_lamar"
ELSE tAnim = "idle_c_lamar"
ENDIF
RETURN TRUE
ELIF friend.eChar = CHAR_JIMMY
iVariation = GET_RANDOM_INT_IN_RANGE(0, 4)
IF iVariation = 0 tAnim = "idle_a"
ELIF iVariation = 1 tAnim = "idle_b"
ELIF iVariation = 2 tAnim = "idle_c"
ELSE tAnim = "idle_d"
ENDIF
RETURN TRUE
ELIF friend.eChar = CHAR_AMANDA
iVariation = GET_RANDOM_INT_IN_RANGE(0, 2)
IF iVariation = 0 tAnim = "impatient_idle_a"
ELSE tAnim = "impatient_idle_b"
ENDIF
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_LoadFriendPickupAnims(structFriend& friend)
TEXT_LABEL_63 tAnimDict
IF Private_GetFriendAnimDict(friend, tAnimDict)
REQUEST_ANIM_DICT(tAnimDict)
IF NOT HAS_ANIM_DICT_LOADED(tAnimDict)
CPRINTLN(DEBUG_FRIENDS, "Private_LoadFriendPickupAnims(): ", tAnimDict)
RETURN FALSE
ENDIF
ENDIF
RETURN TRUE
ENDFUNC
PROC Private_ReleaseFriendPickupAnims(structFriend& friend)
TEXT_LABEL_63 tAnimDict
IF Private_GetFriendAnimDict(friend, tAnimDict)
REMOVE_ANIM_DICT(tAnimDict)
ENDIF
ENDPROC
//---------------------------------------------------------------------------------------------------
//-- Member behaviour tasks
//---------------------------------------------------------------------------------------------------
//FUNC BOOL Private_IsFriendCarUsableForArrival(structFriend& friend, FLOAT fMaxDist)
//
// IF IS_VEHICLE_DRIVEABLE(friend.hCar)
// AND GET_ENTITY_HEALTH(friend.hCar) > 600
// AND NOT IS_ENTITY_UPSIDEDOWN(friend.hCar)
// AND IS_ENTITY_AT_ENTITY(friend.hPed, friend.hCar, <<fMaxDist, fMaxDist, fMaxDist>>)
// AND ABSF(GET_ENTITY_SPEED(friend.hCar)) < 5.0
//
//// VECTOR vFriendPos = GET_ENTITY_COORDS(friend.hPed)
//// VECTOR vCarPos = GET_ENTITY_COORDS(friend.hCar)
////
//// FLOAT fCarDist = VDIST(vFriendPos, vCarPos)
//// FLOAT fLocDist = VDIST(vFriendPos, friend.vDoorstep)
////
//// IF fCarDist < fLocDist - 50.0
// PED_INDEX hDriver = GET_PED_IN_VEHICLE_SEAT(friend.hCar, VS_DRIVER)
// IF NOT DOES_ENTITY_EXIST(hDriver)
// OR hDriver = friend.hPed
// RETURN TRUE
// ENDIF
//// ENDIF
//
// ENDIF
//
// IF NOT IS_VEHICLE_DRIVEABLE(friend.hCar)
// CPRINTLN(DEBUG_FRIENDS, "Private_IsFriendCarUsableForArrival(", GetLabel_enumCharacterList(friend.eChar), ") - NOT IS_VEHICLE_DRIVEABLE(friend.hCar)")
// ENDIF
// IF GET_ENTITY_HEALTH(friend.hCar) <= 600
// CPRINTLN(DEBUG_FRIENDS, "Private_IsFriendCarUsableForArrival(", GetLabel_enumCharacterList(friend.eChar), ") - GET_ENTITY_HEALTH(friend.hCar) <= 600")
// ENDIF
// IF IS_ENTITY_UPSIDEDOWN(friend.hCar)
// CPRINTLN(DEBUG_FRIENDS, "Private_IsFriendCarUsableForArrival(", GetLabel_enumCharacterList(friend.eChar), ") - IS_ENTITY_UPSIDEDOWN(friend.hCar)")
// ENDIF
// IF NOT IS_ENTITY_AT_ENTITY(friend.hPed, friend.hCar, <<fMaxDist, fMaxDist, fMaxDist>>)
// CPRINTLN(DEBUG_FRIENDS, "Private_IsFriendCarUsableForArrival(", GetLabel_enumCharacterList(friend.eChar), ") - NOT IS_ENTITY_AT_ENTITY(friend.hPed, friend.hCar, <<fMaxDist, fMaxDist, fMaxDist>>)")
// ENDIF
// IF ABSF(GET_ENTITY_SPEED(friend.hCar)) >= 5.0
// CPRINTLN(DEBUG_FRIENDS, "Private_IsFriendCarUsableForArrival(", GetLabel_enumCharacterList(friend.eChar), ") - ABSF(GET_ENTITY_SPEED(friend.hCar)) >= 5.0")
// ENDIF
//
// RETURN FALSE
//
//ENDFUNC
PROC Private_FriendTask_RunToCar( structFriend& friend )
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_ENTER_VEHICLE)
CLEAR_PED_TASKS(friend.hPed)
TASK_ENTER_VEHICLE(friend.hPed, friend.hCar, 30000, VS_DRIVER, PEDMOVE_RUN, ECF_RESUME_IF_INTERRUPTED|ECF_WARP_IF_DOOR_IS_BLOCKED|ECF_WARP_IF_SHUFFLE_LINK_IS_BLOCKED)
ENDIF
ENDPROC
PROC Private_FriendTask_DriveAndPark(structFriend& friend)
RESTART_TIMER_NOW(friend.mParkedTimer)
VEHICLE_INDEX hVehicle = GET_VEHICLE_PED_IS_IN(friend.hPed)
VECTOR vParkPos = g_FriendLocations[friend.ePickup].vParkPos
FLOAT fDestDist = GET_DISTANCE_BETWEEN_ENTITY_AND_COORD(friend.hPed, vParkPos)
INT iBasicMode = ENUM_TO_INT(DF_DriveIntoOncomingTraffic)|ENUM_TO_INT(DF_GoOffRoadWhenAvoiding)|ENUM_TO_INT(DF_SteerAroundObjects)|ENUM_TO_INT(DF_StopForPeds)//|ENUM_TO_INT(DF_SteerAroundStationaryCars)//|ENUM_TO_INT(DF_UseSwitchedOffNodes)
INT iDriveAvoidMode = iBasicMode|ENUM_TO_INT(DF_ChangeLanesAroundObstructions)|ENUM_TO_INT(DF_SteerAroundStationaryCars)|ENUM_TO_INT(DF_SwerveAroundAllCars)|ENUM_TO_INT(DF_SteerAroundPeds)
INT iParkStopMode = iBasicMode|/*ENUM_TO_INT(DF_SteerAroundStationaryCars)|*/ENUM_TO_INT(DF_StopForCars)
// IF GET_ENTITY_SPEED(friend.hPed) > 1.0
// friend.iArrivalStoppedTimer = GET_GAME_TIMER()
// ENDIF
FLOAT fStoppedTimer = 0.0
IF GET_ENTITY_SPEED(friend.hPed) <= 1.0
IF NOT IS_TIMER_STARTED(friend.mArrivalStoppedTimer)
RESTART_TIMER_NOW(friend.mArrivalStoppedTimer)
ELSE
fStoppedTimer = GET_TIMER_IN_SECONDS(friend.mArrivalStoppedTimer)
ENDIF
ELSE
CANCEL_TIMER(friend.mArrivalStoppedTimer)
ENDIF
// At dest / Near dest and stopped for blockage ... Get out
IF fDestDist < 2.0
// OR (fDestDist < 15.0 AND (friend.iArrivalStoppedTimer+1000) < GET_GAME_TIMER())
// OR (fDestDist < 30.0 AND (friend.iArrivalStoppedTimer+3000) < GET_GAME_TIMER())
OR (fDestDist < 15.0 AND fStoppedTimer > 1.0)
// OR (fDestDist < 30.0 AND fStoppedTimer > 3.0)
OR (fDestDist < 20.0 AND fStoppedTimer > 6.0)
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_LEAVE_ANY_VEHICLE)
CPRINTLN(DEBUG_FRIENDS, "Exit vehicle")
CLEAR_PED_TASKS(friend.hPed)
// B*1480465 - Friend will turn off headlights when exiting vehicle
IF DOES_ENTITY_EXIST(hVehicle)
SET_VEHICLE_ENGINE_ON(hVehicle, FALSE, TRUE)
ENDIF
TASK_LEAVE_ANY_VEHICLE(friend.hPed)
// TASK_LEAVE_ANY_VEHICLE(friend.hPed, 0, ECF_RESUME_IF_INTERRUPTED|ECF_WARP_IF_DOOR_IS_BLOCKED)
ENDIF
// Very close to dest... Drive slow, stop for blockages // MAKE SURE ALL THIS WORKS WHEN SWITCHING TO PED AND AWAY AGAIN
ELIF fDestDist < 10.0//12.0//7.0
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_VEHICLE_MISSION) OR friend.iArrivalDrivingMode <> 0
CPRINTLN(DEBUG_FRIENDS, "Driving mode 0: Drive slow, stop for blockages")
CLEAR_PED_TASKS(friend.hPed)
TASK_VEHICLE_MISSION_COORS_TARGET(friend.hPed, hVehicle, vParkPos, MISSION_GOTO, 10.0, INT_TO_ENUM(DRIVINGMODE, iParkStopMode), 2.0, 10.0/*20.0*//*15.0*/, TRUE)
friend.iArrivalDrivingMode = 0
ENDIF
ELSE
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_VEHICLE_MISSION) OR friend.iArrivalDrivingMode <> 1
CPRINTLN(DEBUG_FRIENDS, "Driving mode 1: Drive slow, go around blockages")
CLEAR_PED_TASKS(friend.hPed)
TASK_VEHICLE_MISSION_COORS_TARGET(friend.hPed, hVehicle, vParkPos, MISSION_GOTO, 10.0, INT_TO_ENUM(DRIVINGMODE, iDriveAvoidMode), 2.0, 20.0/*15.0*/, TRUE)
friend.iArrivalDrivingMode = 1
ENDIF
IF fDestDist < 30.0
SET_DRIVE_TASK_CRUISE_SPEED(friend.hPed, 10.0)
ELSE
SET_DRIVE_TASK_CRUISE_SPEED(friend.hPed, CONST_fFriendArriveSpeed)
ENDIF
ENDIF
// VEHICLE_INDEX hVehicle = GET_VEHICLE_PED_IS_IN(friend.hPed)
// VECTOR vParkPos = g_FriendLocations[friend.ePickup].vParkPos
// INT iDrivingMode = ENUM_TO_INT(DF_StopForPeds)|ENUM_TO_INT(DF_SteerAroundPeds)|ENUM_TO_INT(DF_SteerAroundObjects)|ENUM_TO_INT(DF_SteerAroundStationaryCars)|ENUM_TO_INT(DF_SwerveAroundAllCars)|ENUM_TO_INT(DF_GoOffRoadWhenAvoiding)
// INT iParkingMode = ENUM_TO_INT(DF_StopForCars)|ENUM_TO_INT(DF_StopForPeds)|ENUM_TO_INT(DF_SteerAroundObjects)|ENUM_TO_INT(DF_GoOffRoadWhenAvoiding)|ENUM_TO_INT(DF_DriveIntoOncomingTraffic)
//
// RESTART_TIMER_NOW(friend.mParkedTimer)
//
// //-- Drive --
// IF Util_IsPedOutsideRange(friend.hPed, vParkPos, 30.0)
//
// IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_VEHICLE_MISSION)
// CLEAR_PED_TASKS(friend.hPed)
// TASK_VEHICLE_MISSION_COORS_TARGET(friend.hPed, hVehicle, friend.vDriveway, MISSION_GOTO, CONST_fFriendArriveSpeed, INT_TO_ENUM(DRIVINGMODE, iDrivingMode), 1, 10, TRUE)//FALSE)
// ENDIF
//
// //-- Park --
// ELSE
//
// IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_PERFORM_SEQUENCE)
// IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_LEAVE_ANY_VEHICLE)
// CLEAR_PED_TASKS(friend.hPed)
// SEQUENCE_INDEX seq
// OPEN_SEQUENCE_TASK(seq)
// TASK_VEHICLE_GOTO_NAVMESH(null, hVehicle, vParkPos, 6.0, INT_TO_ENUM(DRIVINGMODE, iParkingMode), 1)
// TASK_LEAVE_ANY_VEHICLE(null, 0, ECF_RESUME_IF_INTERRUPTED|ECF_WARP_IF_DOOR_IS_BLOCKED)
// CLOSE_SEQUENCE_TASK(seq)
// TASK_PERFORM_SEQUENCE(friend.hPed, seq)
//
// friend.iParkTimer = GET_GAME_TIMER() + 10000
// ENDIF
// ELSE
// IF friend.iParkTimer < GET_GAME_TIMER()
// TASK_LEAVE_ANY_VEHICLE(friend.hPed, 0, ECF_RESUME_IF_INTERRUPTED|ECF_WARP_IF_DOOR_IS_BLOCKED)
// ENDIF
// ENDIF
//
// ENDIF
ENDPROC
PROC Private_FriendTask_RunToPlayer( structFriend& friend )
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_GO_TO_ENTITY)
IF NOT IS_PED_INJURED(gActivity.mPlayer.hPed)
TASK_GO_TO_ENTITY(friend.hPed, gActivity.mPlayer.hPed, DEFAULT_TIME_NEVER_WARP, 5.0)
ENDIF
ENDIF
ENDPROC
PROC Private_FriendTask_RunToDoorstep( structFriend& friend )
//-- Run to doorstep --
IF Util_IsPedOutsideRange(friend.hPed, friend.vDoorstep, 1.5)
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_FOLLOW_NAV_MESH_TO_COORD)
TASK_FOLLOW_NAV_MESH_TO_COORD(friend.hPed, friend.vDoorstep, PEDMOVE_RUN, DEFAULT_TIME_NEVER_WARP, DEFAULT_NAVMESH_RADIUS, ENAV_STOP_EXACTLY)
ENDIF
ELSE
VECTOR vPedForward = GET_ENTITY_FORWARD_VECTOR(friend.hPed)
VECTOR vTargetForward = NORMALISE_VECTOR(friend.vDriveway - friend.vDoorstep)
FLOAT fAngle = 0.0
IF (vPedForward.x <> 0.0 OR vPedForward.y <> 0.0) AND (vTargetForward.x <> 0.0 OR vTargetForward.y <> 0.0)
fAngle = GET_ANGLE_BETWEEN_2D_VECTORS(vPedForward.x, vPedForward.y, vTargetForward.x, vTargetForward.y)
ENDIF
//-- Turn to face loc --
IF (fAngle > 20 OR NOT Private_LoadFriendPickupAnims(friend))
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_TURN_PED_TO_FACE_COORD)
AND NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_PLAY_ANIM)
CPRINTLN(DEBUG_FRIENDS, "------------- Turn friend to player ------------- ", GetLabel_enumCharacterList(friend.eChar))
TASK_TURN_PED_TO_FACE_COORD(friend.hPed, friend.vDriveway, 6000)
ENDIF
//-- Play wait anim --
ELSE
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_PLAY_ANIM)
TEXT_LABEL_63 tAnimDict, tAnim
Private_GetFriendAnimDict(friend, tAnimDict)
Private_GetFriendAnim_Impatient(friend, tAnim)
CPRINTLN(DEBUG_FRIENDS, "------------- Starting friend anim ------------- ", GetLabel_enumCharacterList(friend.eChar))
IF friend.eChar = CHAR_AMANDA
TASK_PLAY_ANIM(friend.hPed, tAnimDict, tAnim, WALK_BLEND_IN, WALK_BLEND_OUT, -1, AF_LOOPING)
ELSE
TASK_PLAY_ANIM(friend.hPed, tAnimDict, tAnim, WALK_BLEND_IN, WALK_BLEND_OUT, -1)
ENDIF
ENDIF
ENDIF
ENDIF
ENDPROC
PROC Private_FriendArrivalTasks(structFriend& friend)
IF NOT IS_PED_RAGDOLL(friend.hPed) AND NOT IS_PED_RUNNING_RAGDOLL_TASK(friend.hPed)
IF IS_PED_IN_ANY_VEHICLE(friend.hPed)
Private_FriendTask_DriveAndPark(friend)
ELSE
IF IS_PED_UNINJURED(PLAYER_PED_ID())
AND NOT IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
AND Util_IsPedInsideRangePed(PLAYER_PED_ID(), friend.hPed, CONST_fRunToPlayerRange)
Private_FriendTask_RunToPlayer(friend)
// ELIF Util_IsPedOutsideRange(friend.hPed, friend.vDoorstep, 50.0)
// ELIF Private_IsFriendCarUsableForArrival(friend, 100.0)
//
// Private_FriendTask_RunToCar(friend)
ELSE
Private_FriendTask_RunToDoorstep(friend)
ENDIF
ENDIF
ENDIF
ENDPROC
//---------------------------------------------------------------------------------------------------
//-- Member objectives
//---------------------------------------------------------------------------------------------------
PROC Private_ClearFriendObjectives(structFriend& friend)
IF DOES_BLIP_EXIST(friend.hLocBlip)
REMOVE_BLIP(friend.hLocBlip)
ENDIF
IF DOES_BLIP_EXIST(friend.hPedBlip)
REMOVE_BLIP(friend.hPedBlip)
ENDIF
IF friend.bIsShowingPickupCam = TRUE
IF IS_GAMEPLAY_HINT_ACTIVE()
STOP_GAMEPLAY_HINT()
ENDIF
SET_CINEMATIC_BUTTON_ACTIVE(TRUE)
friend.bIsShowingPickupCam = FALSE
ENDIF
Private_ClearHelpWithString("FR_HV_HORN", friend.tName)
Private_ClearHelpWithString("FR_H_PKUP", friend.tName)
Private_ClearHelpWithString("FR_H_WAIT", friend.tName)
Private_ClearHelpWithString("FR_H_DROPOFF0", friend.tName)
Private_ClearHelpWithString("FR_H_DROPOFF1", friend.tName)
Private_ClearHelpWithString("FR_H_DROPOFF2", friend.tName)
Private_ClearPrintWithString("FR_GETBACK", friend.tName)
Private_ClearPrint("FR_GETBACK2")
ENDPROC
FUNC BLIP_INDEX Private_CreateFriendLocBlip(structFriend& friend)
IF friend.eState = FRIEND_NULL
SCRIPT_ASSERT("Private_CreateFriendLocBlip() - friend is null")
ELIF friend.ePickup <> NO_FRIEND_LOCATION
CPRINTLN(DEBUG_FRIENDS, "Private_CreateFriendLocBlip(", GetLabel_enumCharacterList(friend.eChar), ")")
friend.hLocBlip = CREATE_BLIP_FOR_COORD(friend.vDriveway)
IF DOES_BLIP_EXIST(friend.hLocBlip)
SET_BLIP_AS_FRIENDLY(friend.hLocBlip, TRUE)
SET_BLIP_SPRITE(friend.hLocBlip, RADAR_TRACE_FRIEND)
SET_BLIP_NAME_FROM_TEXT_FILE(friend.hLocBlip, "FR_PKUPBLIP")
ENDIF
RETURN friend.hLocBlip
ENDIF
RETURN null
ENDFUNC
FUNC BLIP_INDEX Private_CreateFriendPedBlip(structFriend& friend)
IF friend.eState = FRIEND_NULL
SCRIPT_ASSERT("Private_CreateFriendPedBlip() - friend is null")
ELSE
CPRINTLN(DEBUG_FRIENDS, "Private_CreateFriendPedBlip(", GetLabel_enumCharacterList(friend.eChar), ")")
IF NOT IS_PED_INJURED(friend.hPed)
friend.hPedBlip = CREATE_BLIP_FOR_PED(friend.hPed)
IF DOES_BLIP_EXIST(friend.hPedBlip)
SET_BLIP_AS_FRIENDLY(friend.hPedBlip, TRUE)
SET_BLIP_NAME_FROM_TEXT_FILE(friend.hPedBlip, friend.tName)
ENDIF
RETURN friend.hPedBlip
ENDIF
ENDIF
RETURN null
ENDFUNC
PROC Private_RemoveFriendLocBlip(structFriend& friend)
IF DOES_BLIP_EXIST(friend.hLocBlip)
REMOVE_BLIP(friend.hLocBlip)
ENDIF
ENDPROC
PROC Private_RemoveFriendPedBlip(structFriend& friend)
IF DOES_BLIP_EXIST(friend.hPedBlip)
REMOVE_BLIP(friend.hPedBlip)
ENDIF
ENDPROC
PROC Private_UpdateFriendPedBlip(structFriend& friend, BOOL bForceOn = FALSE)
BOOL bDisplay = FALSE
IF friend.eState <> FRIEND_NULL
IF NOT IS_PED_INJURED(friend.hPed)
IF bForceOn = TRUE
OR friend.eState = FRIEND_PARACHUTE
OR friend.eState = FRIEND_PLUMMET
OR friend.eState = FRIEND_LOST
OR friend.eState = FRIEND_FAIL_LOST
bDisplay = TRUE
ELSE
INTERIOR_INSTANCE_INDEX hStripClubInterior = GET_INTERIOR_AT_COORDS(<<121.9946, -1292.5457, 29.2792>>)
IF hStripClubInterior <> NULL
IF GET_INTERIOR_FROM_ENTITY(friend.hPed) = hStripClubInterior
bDisplay = TRUE
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
IF bDisplay
IF NOT DOES_BLIP_EXIST(friend.hPedBlip) AND (GET_BLIP_FROM_ENTITY(friend.hPed) = NULL)
Private_CreateFriendPedBlip(friend)
ENDIF
ELSE
IF DOES_BLIP_EXIST(friend.hPedBlip)
Private_RemoveFriendPedBlip(friend)
ENDIF
ENDIF
ENDPROC
//---------------------------------------------------------------------------------------------------
//-- Member state utils
//---------------------------------------------------------------------------------------------------
PROC Private_SetFriendState(structFriend& friend, enumFriendState eState)
#IF IS_DEBUG_BUILD
TEXT_LABEL_63 tChar = GetLabel_enumCharacterList(friend.eChar)
TEXT_LABEL_63 tState = GetLabel_enumFriendState(eState)
CPRINTLN(DEBUG_FRIENDS, "Private_SetFriendState(", tChar, ", ", tState, ")")
#ENDIF
friend.eState = eState
ENDPROC
FUNC BOOL Private_IsFriendArriving(structFriend& friend)
// Set objective
IF friend.eState = FRIEND_PICKUP
AND DOES_ENTITY_EXIST(friend.hPed)
IF Util_IsPedOutsideRange(friend.hPed, friend.vDriveway, CONST_fIsBuddyAtDriveway)
AND Util_IsPedInsideRange(PLAYER_PED_ID(), friend.vDoorstep, CONST_fIsPlayerAtDoorstep)
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_CanPickupFriend(structFriend& friend)
IF friend.eState = FRIEND_PICKUP
IF IS_PLAYER_CONTROL_ON(PLAYER_ID())
// Friend must be on foot to trigger
IF NOT IS_PED_IN_ANY_VEHICLE(friend.hPed)
// Player on foot
IF NOT IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
// Is friend near + on screen?
IF Util_IsPedInsideRangePed(friend.hPed, PLAYER_PED_ID(), CONST_fPickupHornDist)
IF IS_ENTITY_ON_SCREEN(friend.hPed)
RETURN TRUE
ENDIF
ENDIF
// Player in car
ELSE
// Is friend near?
IF Util_IsPedInsideRangePed(friend.hPed, PLAYER_PED_ID(), CONST_fPickupHornDist)
RETURN TRUE
// Are friend and player at driveway?
ELIF friend.ePickup <> NO_FRIEND_LOCATION
IF Util_IsPedInsideRange(friend.hPed, friend.vDoorstep, CONST_fIsBuddyAtDriveway)
IF Util_IsPedInsideRange(PLAYER_PED_ID(), friend.vDriveway, CONST_fPickupHornDist)
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_IsPlayerTakingTaxi()
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
IF IS_VEHICLE_MODEL(GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID()), TAXI)
IF GET_PED_IN_VEHICLE_SEAT(GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID())) <> PLAYER_PED_ID()
RETURN TRUE
ENDIF
ENDIF
ENDIF
// IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
// VEHICLE_INDEX hVehicle = GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID())
// IF DOES_ENTITY_EXIST(hVehicle)
// IF GET_ENTITY_MODEL(hVehicle) = TAXI
// PED_INDEX hDriver = GET_PED_IN_VEHICLE_SEAT(hVehicle, VS_DRIVER)
// IF DOES_ENTITY_EXIST(hDriver) AND hDriver <> PLAYER_PED_ID()
// RETURN TRUE
// ENDIF
// ENDIF
// ENDIF
// ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_TryPickupFriend(structFriend& friend)
TEXT_LABEL sUseCarHorn_help = "FR_HV_HORN" //Click ~INPUT_HORN~ to use the vehicle horn and attract ~a~'s attention.
BOOL bCanPickup = Private_CanPickupFriend(friend)
// Display help text / Update hint cam
IF bCanPickup AND IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID()) AND NOT Private_IsPlayerTakingTaxi() //AND NOT IS_PLAYER_PRESSING_HORN(PLAYER_ID())
// Try to start camera
IF friend.bIsShowingPickupCam = FALSE
IF NOT IS_CINEMATIC_CAM_RENDERING()
IF IS_GAMEPLAY_HINT_ACTIVE()
STOP_GAMEPLAY_HINT()
ELSE
SET_GAMEPLAY_ENTITY_HINT(friend.hPed, <<0,0,0>>, TRUE, -1, 3000, 3000, HINTTYPE_DEFAULT)
SET_CINEMATIC_BUTTON_ACTIVE(FALSE)
friend.bIsShowingPickupCam = TRUE
ENDIF
ENDIF
ENDIF
// Show hown help text
IF NOT IS_HELP_MESSAGE_BEING_DISPLAYED()
IF NOT IS_PED_IN_ANY_HELI(PLAYER_PED_ID())
AND NOT IS_PED_IN_ANY_BOAT(PLAYER_PED_ID())
AND NOT IS_PED_IN_ANY_SUB(PLAYER_PED_ID())
AND NOT IS_PED_IN_ANY_TRAIN(PLAYER_PED_ID())
AND NOT IS_PED_IN_MODEL(PLAYER_PED_ID(), BMX)
PRINT_HELP_FOREVER_WITH_STRING_NO_SOUND(sUseCarHorn_help, friend.tName)
ENDIF
ENDIF
ELSE
// Clear camera
IF friend.bIsShowingPickupCam = TRUE
IF IS_GAMEPLAY_HINT_ACTIVE()
STOP_GAMEPLAY_HINT()
ENDIF
SET_CINEMATIC_BUTTON_ACTIVE(TRUE)
friend.bIsShowingPickupCam = FALSE
ENDIF
// Clear hown help text
IF IS_THIS_HELP_MESSAGE_WITH_STRING_BEING_DISPLAYED(sUseCarHorn_help, friend.tName)
CLEAR_HELP()
ENDIF
ENDIF
IF NOT IS_PED_RAGDOLL(friend.hPed) AND NOT IS_PED_RUNNING_RAGDOLL_TASK(friend.hPed) AND NOT Private_IsPlayerTakingTaxi()
// Pickup if in zone and onfoot / stops car / not on doorstep
IF bCanPickup
IF NOT IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID()) // On foot - pickup automatically
RETURN TRUE
ELIF IS_PLAYER_PRESSING_HORN(PLAYER_ID()) // In car - pickup if pressing horn
RETURN TRUE
ELIF Util_IsPedOutsideRange(friend.hPed, friend.vDoorstep, 2.0) // In car - pickup automatically if friend is not on doorstep
RETURN TRUE
ENDIF
ENDIF
// Car-pickup, if in normal range, and both stopped
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID()) // In car - pickup automatically if very close and both stopped
IF IS_CHAR_ALMOST_STOPPED(PLAYER_PED_ID())
IF IS_CHAR_ALMOST_STOPPED(friend.hPed)
IF Util_IsPedInsideRangePed(friend.hPed, PLAYER_PED_ID(), CONST_fPickupHornDist)
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
// Car-pickup, if in extended range, and beeping horn - Fix b*1987632 - Cars must be almost stopped when going into group state, otherwise friend won't be able to join group, and will be set to lost state.
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
IF IS_PLAYER_PRESSING_HORN(PLAYER_ID())
IF IS_CHAR_ALMOST_STOPPED(PLAYER_PED_ID())
IF IS_CHAR_ALMOST_STOPPED(friend.hPed)
IF Util_IsPedInsideRangePed(friend.hPed, PLAYER_PED_ID(), CONST_fPickupHornExtraDist)
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
PROC Private_ClearFriendPickupHelp(structFriend& friend, BOOL bForce = FALSE)
IF friend.bIsShowingPickupCam OR bForce
IF IS_GAMEPLAY_HINT_ACTIVE()
STOP_GAMEPLAY_HINT()
ENDIF
SET_CINEMATIC_BUTTON_ACTIVE(TRUE)
friend.bIsShowingPickupCam = FALSE
ENDIF
Private_ClearHelpWithString("FR_HV_HORN", friend.tName)
ENDPROC
PROC Private_SetFriendPickedUp(structFriend& friend)
Private_SetFriendState(friend, FRIEND_GROUP)
friend.bWasPickedUp = TRUE
Private_ReleaseFriendPickupAnims(friend)
IF gActivity.mPlayer.eChar <> friend.eChar
PAUSE_TIMER(friend.mWaitTimer) // TODO: This needs looking at
Private_SetFriendsForcedSeats()
Private_ClearFriendSwitchOverride(friend)
Private_ClearFriendPickupHelp(friend, TRUE)
// SET_ENTITY_ONLY_DAMAGED_BY_PLAYER(friend.hPed, FALSE)
// CLEAR_ENTITY_LAST_DAMAGE_ENTITY(friend.hPed)
// CLEAR_ENTITY_LAST_WEAPON_DAMAGE(friend.hPed)
CLEAR_PED_TASKS(friend.hPed)
// IF DOES_ENTITY_EXIST(friend.hCar)
// friend.iCarHealth = GET_ENTITY_HEALTH(friend.hCar)
// ENDIF
friend.iBlockRunningUntilTime = GET_GAME_TIMER() + c_iPickupRunBlockDuration
// Add to group
SET_PED_AS_GROUP_MEMBER(friend.hPed, PLAYER_GROUP_ID())
SET_PED_GROUP_MEMBER_PASSENGER_INDEX(friend.hPed, VS_FRONT_RIGHT)
TASK_LOOK_AT_ENTITY(friend.hPed, PLAYER_PED_ID(), 7000)//, SLF_FAST_TURN_RATE)
#if not USE_CLF_DLC
#if not USE_NRM_DLC
IF friend.eChar = CHAR_LAMAR
IF gActivity.mPlayer.eChar = CHAR_FRANKLIN
SET_MISSION_FLOW_FLAG_STATE(FLOWFLAG_FRAN_DONE_ACTIVITY_WITH_LAMAR, TRUE)
ENDIF
IF gActivity.mPlayer.eChar = CHAR_TREVOR
SET_MISSION_FLOW_FLAG_STATE(FLOWFLAG_FRAN_DONE_ACTIVITY_WITH_LAMAR, TRUE)
ENDIF
ENDIF
#endif
#endif
IF friend.eChar = CHAR_JIMMY OR friend.eChar = CHAR_AMANDA
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(friend.hPed, TRUE)
ELSE
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(friend.hPed, FALSE)
ENDIF
// Start pickup dialogue (if not already running for another member)
IF NOT Private_IsDialogueDoingPickup(gActivity.mDialogue)
IF friend.bWasMetAmbiently
Private_SetDialogueState(gActivity.mDialogue, FDIALOGUE_AMBIENT, friend.eChar)
ELSE
Private_SetDialogueState(gActivity.mDialogue, FDIALOGUE_PICKUP, friend.eChar)
ENDIF
ENDIF
ENDIF
ENDPROC
FUNC BOOL Private_IsFriendValid(structFriend& friend)
IF friend.eState <> FRIEND_NULL AND friend.eState < FRIEND_CANCEL
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_IsFriendOutside(structFriend& friend)
IF NOT IS_PED_INJURED(friend.hPed)
IF GET_INTERIOR_FROM_ENTITY(friend.hPed) = NULL
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_CanFriendConverse(structFriend& friend)
IF IS_PED_UNINJURED(PLAYER_PED_ID()) AND IS_PED_UNINJURED(friend.hPed)
IF friend.eChar = GET_CURRENT_PLAYER_PED_ENUM()
RETURN TRUE
ENDIF
IF friend.eState = FRIEND_GROUP
OR friend.eState = FRIEND_TRAPPED
IF IS_ENTITY_AT_ENTITY(PLAYER_PED_ID(), friend.hPed, <<CONST_fFriendConverseRadius, CONST_fFriendConverseRadius, CONST_fFriendConverseRadius>>)
RETURN TRUE
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_CanFriendLeave(structFriend& friend)
IF IS_PED_IN_ANY_VEHICLE(friend.hPed)
VEHICLE_INDEX hVehicle = GET_VEHICLE_PED_IS_IN(friend.hPed)
IF DOES_ENTITY_EXIST(hVehicle)
IF NOT IS_VEHICLE_STOPPED(hVehicle) //ABSF(GET_ENTITY_SPEED(friend.hPed)) > 8.0//0.5
IF NOT IS_PED_INJURED(PLAYER_PED_ID()) AND IS_PED_IN_VEHICLE(PLAYER_PED_ID(), hVehicle)
RETURN FALSE
ENDIF
ENDIF
ENDIF
ENDIF
RETURN TRUE
ENDFUNC
FUNC BOOL Private_IsPlayerOrFriendPersued(structFriend& friend, BOOL bCheckIfTarget = FALSE)
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
IF bCheckIfTarget
IF COUNT_PEDS_IN_COMBAT_WITH_TARGET_WITHIN_RADIUS(PLAYER_PED_ID(), GET_ENTITY_COORDS(PLAYER_PED_ID()), 50.0) > 0
CPRINTLN(DEBUG_FRIENDS, "Private_IsPlayerOrFriendPersued(", GetLabel_enumCharacterList(friend.eChar), ") = TRUE - Peds in combat with player")
RETURN TRUE
ENDIF
IF COUNT_PEDS_IN_COMBAT_WITH_TARGET_WITHIN_RADIUS(friend.hPed, GET_ENTITY_COORDS(friend.hPed), 50.0) > 0
CPRINTLN(DEBUG_FRIENDS, "Private_IsPlayerOrFriendPersued(", GetLabel_enumCharacterList(friend.eChar), ") = TRUE - Peds in combat with friend")
RETURN TRUE
ENDIF
ENDIF
IF HAS_ENTITY_BEEN_DAMAGED_BY_WEAPON(PLAYER_PED_ID(), WEAPONTYPE_INVALID, GENERALWEAPON_TYPE_ANYWEAPON)
AND NOT HAS_ENTITY_BEEN_DAMAGED_BY_WEAPON(PLAYER_PED_ID(), WEAPONTYPE_INVALID, GENERALWEAPON_TYPE_ANYMELEE)
CPRINTLN(DEBUG_FRIENDS, "Private_IsPlayerOrFriendPersued(", GetLabel_enumCharacterList(friend.eChar), ") = TRUE - Player attacked with weapons")
RETURN TRUE
ENDIF
IF HAS_ENTITY_BEEN_DAMAGED_BY_WEAPON(friend.hPed, WEAPONTYPE_INVALID, GENERALWEAPON_TYPE_ANYWEAPON)
AND NOT HAS_ENTITY_BEEN_DAMAGED_BY_WEAPON(friend.hPed, WEAPONTYPE_INVALID, GENERALWEAPON_TYPE_ANYMELEE)
CPRINTLN(DEBUG_FRIENDS, "Private_IsPlayerOrFriendPersued(", GetLabel_enumCharacterList(friend.eChar), ") = TRUE - Friend attacked with weapons")
RETURN TRUE
ENDIF
IF IS_PLAYER_WANTED_LEVEL_GREATER(PLAYER_ID(), 0)
VECTOR vPlayerPos = GET_ENTITY_COORDS(PLAYER_PED_ID())
VECTOR vMin = vPlayerPos - << 50.0,50.0,50.0 >>
VECTOR vMax = vPlayerPos + << 50.0,50.0,50.0 >>
IF IS_COP_PED_IN_AREA_3D(vMin, vMax)
CPRINTLN(DEBUG_FRIENDS, "Private_IsPlayerOrFriendPersued(", GetLabel_enumCharacterList(friend.eChar), ") = TRUE - Player persued by cops")
RETURN TRUE
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_IsFriendAirbourne(structFriend& friend)
IF IS_PED_IN_ANY_VEHICLE(friend.hPed)
VEHICLE_INDEX hVehicle = GET_VEHICLE_PED_IS_IN(friend.hPed)
IF DOES_ENTITY_EXIST(hVehicle)
IF GET_ENTITY_HEIGHT_ABOVE_GROUND(hVehicle) > 5.0
RETURN TRUE
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_ShouldFriendFlee(structFriend& friend)
IF friend.eState <> FRIEND_NULL AND NOT IS_PED_INJURED(friend.hPed)
IF NOT Private_IsFriendAirbourne(friend)
IF IS_PED_FLEEING(friend.hPed)
CPRINTLN(DEBUG_FRIENDS, "Private_ShouldFriendFlee(", GetLabel_enumCharacterList(friend.eChar), ") = TRUE - Already fleeing")
RETURN TRUE
ELIF friend.eChar = CHAR_JIMMY OR friend.eChar = CHAR_AMANDA
IF IS_BIT_SET(g_bitfieldFriendFlags, ENUM_TO_INT(FRIENDFLAG_IS_ROBBERY_UNDERWAY))
RETURN TRUE
ENDIF
IF HAS_ENTITY_BEEN_DAMAGED_BY_WEAPON(PLAYER_PED_ID(), WEAPONTYPE_INVALID, GENERALWEAPON_TYPE_ANYWEAPON)
AND NOT HAS_ENTITY_BEEN_DAMAGED_BY_WEAPON(PLAYER_PED_ID(), WEAPONTYPE_INVALID, GENERALWEAPON_TYPE_ANYMELEE)
CPRINTLN(DEBUG_FRIENDS, "Private_IsPlayerOrFriendPersued(", GetLabel_enumCharacterList(friend.eChar), ") = TRUE - Player attacked with weapons")
RETURN TRUE
ENDIF
IF HAS_ENTITY_BEEN_DAMAGED_BY_WEAPON(friend.hPed, WEAPONTYPE_INVALID, GENERALWEAPON_TYPE_ANYWEAPON)
AND NOT HAS_ENTITY_BEEN_DAMAGED_BY_WEAPON(friend.hPed, WEAPONTYPE_INVALID, GENERALWEAPON_TYPE_ANYMELEE)
CPRINTLN(DEBUG_FRIENDS, "Private_IsPlayerOrFriendPersued(", GetLabel_enumCharacterList(friend.eChar), ") = TRUE - Friend attacked with weapons")
RETURN TRUE
ENDIF
IF NOT IS_PED_IN_ANY_VEHICLE(friend.hPed)
IF IS_PLAYER_WANTED_LEVEL_GREATER(PLAYER_ID(), 0)
VECTOR vPlayerPos = GET_ENTITY_COORDS(PLAYER_PED_ID())
VECTOR vMin = vPlayerPos - << 50.0,50.0,50.0 >>
VECTOR vMax = vPlayerPos + << 50.0,50.0,50.0 >>
IF IS_COP_PED_IN_AREA_3D(vMin, vMax)
CPRINTLN(DEBUG_FRIENDS, "Private_IsPlayerOrFriendPersued(", GetLabel_enumCharacterList(friend.eChar), ") = TRUE - Player persued by cops")
RETURN TRUE
ENDIF
ENDIF
ENDIF
IF IS_PED_IN_ANY_VEHICLE(friend.hPed)
VEHICLE_INDEX hCar = GET_VEHICLE_PED_IS_IN(friend.hPed)
IF DOES_ENTITY_EXIST(hCar)
IF IS_ENTITY_DEAD(hCar)
OR IS_ENTITY_ON_FIRE(hCar)
CPRINTLN(DEBUG_FRIENDS, "Private_ShouldFriendFlee(", GetLabel_enumCharacterList(friend.eChar), ") = TRUE - Vehicle dead/on fire")
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_CanFriendEnterParachute(structFriend& friend)
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
PED_PARACHUTE_STATE ePlayerState = GET_PED_PARACHUTE_STATE(PLAYER_PED_ID())
IF ePlayerState = PPS_DEPLOYING
OR ePlayerState = PPS_PARACHUTING
OR ePlayerState = PPS_LANDING
IF IS_ENTITY_IN_AIR(friend.hPed)
IF NOT IS_PED_IN_ANY_VEHICLE(friend.hPed)
IF GET_ENTITY_HEIGHT_ABOVE_GROUND(friend.hPed) > 50.0
IF HAS_PED_GOT_WEAPON(friend.hPed, GADGETTYPE_PARACHUTE)
// Make sure not too close to any other parachuters
// PED_INDEX nearbyPeds[5]
// INT iNearbyPedCount = GET_PED_NEARBY_PEDS(friend.hPed, nearbyPeds)
//
// INT iPedID
// REPEAT iNearbyPedCount iPedID
// IF NOT IS_PED_INJURED(nearbyPeds[iPedID])
// SWITCH GET_PED_PARACHUTE_STATE(nearbyPeds[iPedID])
// CASE PPS_DEPLOYING
// CASE PPS_PARACHUTING
// CASE PPS_LANDING
// IF IS_ENTITY_AT_ENTITY(friend.hPed, nearbyPeds[iPedID], <<20.0, 20.0, 20.0>>)
// RETURN FALSE
// ENDIF
// BREAK
// ENDSWITCH
// ENDIF
// ENDREPEAT
//
// IF NOT IS_ENTITY_AT_ENTITY(friend.hPed, PLAYER_PED_ID(), <<25.0, 25.0, 25.0>>)
// RETURN TRUE
// ENDIF
RETURN TRUE
ELSE
CPRINTLN(DEBUG_FRIENDS, "Player parachuting and falling high in air, but ", GetLabel_enumCharacterList(friend.eChar), " doesn't have parachute")
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_CanFriendExitParachute(structFriend& friend)
IF NOT IS_ENTITY_IN_AIR(friend.hPed)
CPRINTLN(DEBUG_FRIENDS, "Private_CanFriendExitParachute() - Not in air")
IF GET_PED_PARACHUTE_STATE(friend.hPed) = PPS_INVALID //<> PPS_LANDING
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_CanFriendEnterPlummet(structFriend& friend)
IF IS_PED_IN_ANY_VEHICLE(friend.hPed)
VEHICLE_INDEX hVehicle = GET_VEHICLE_PED_IS_IN(friend.hPed)
IF DOES_ENTITY_EXIST(hVehicle)
IF IS_PED_INJURED(PLAYER_PED_ID()) OR NOT IS_PED_SITTING_IN_VEHICLE(PLAYER_PED_ID(), hVehicle)
IF GET_ENTITY_HEIGHT_ABOVE_GROUND(hVehicle) > 50.0
IF NOT HAS_PED_GOT_WEAPON(friend.hPed, GADGETTYPE_PARACHUTE)
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_CanFriendExitPlummet(structFriend& friend)
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
IF NOT IS_PED_IN_ANY_VEHICLE(friend.hPed)
CPRINTLN(DEBUG_FRIENDS, "Private_CanFriendExitPlummet(", GetLabel_enumCharacterList(friend.eChar), ") - Friend not in vehicle")
RETURN TRUE
ELSE
VEHICLE_INDEX hVehicle = GET_VEHICLE_PED_IS_IN(friend.hPed)
IF DOES_ENTITY_EXIST(hVehicle)
IF NOT IS_ENTITY_IN_AIR(hVehicle)
CPRINTLN(DEBUG_FRIENDS, "Private_CanFriendExitPlummet(", GetLabel_enumCharacterList(friend.eChar), ") - Vehicle not in air")
RETURN TRUE
ELSE
IF GET_ENTITY_SPEED(hVehicle) < 5.0
AND GET_ENTITY_HEIGHT_ABOVE_GROUND(hVehicle) < 30.0
CPRINTLN(DEBUG_FRIENDS, "Private_CanFriendExitPlummet(", GetLabel_enumCharacterList(friend.eChar), ") - Vehicle stopped near ground")
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
PROC Private_SetFriendTrapped(structFriend& friend)
IF NOT IS_PED_INJURED(friend.hPed)
CPRINTLN(DEBUG_FRIENDS, "Private_SetFriendTrapped(", GetLabel_enumCharacterList(friend.eChar), ")")
TASK_PAUSE(friend.hPed, -1)
friend.bDoneTrappedDialogue = FALSE
Private_SetFriendState(friend, FRIEND_TRAPPED)
ENDIF
ENDPROC
PROC Private_SetFriendFleeing(structFriend& friend, BOOL bAllowDeathShockDialogue = FALSE)
IF friend.eState <> FRIEND_NULL AND friend.eState < FRIEND_CANCEL
IF NOT IS_PED_INJURED(friend.hPed)
IF bAllowDeathShockDialogue AND Private_CanFriendConverse(friend)
IF Private_GetDialogueState(gActivity.mDialogue) <> FDIALOGUE_REJECTED
Private_SetDialogueState(gActivity.mDialogue, FDIALOGUE_REJECTED, friend.eChar, FAP_DEATH)
ENDIF
ENDIF
Private_SetFriendState(friend, FRIEND_FAIL_FLEE)
ENDIF
ENDIF
ENDPROC
PROC Private_SetFriendAttacked(structFriend& friend, BOOL bForceResetContactTime = FALSE)
IF friend.eState <> FRIEND_NULL AND friend.eState < FRIEND_CANCEL
IF NOT IS_PED_INJURED(friend.hPed)
Private_ResetFriendGroupTimers(friend, FRIEND_CONTACT_DISMISSED, bForceResetContactTime)
Private_SetFriendState(friend, FRIEND_FAIL_ATTACKED)
ENDIF
ENDIF
ENDPROC
PROC Private_SetFriendWander(structFriend& friend, BOOL bForceResetContactTime = FALSE)
IF friend.eState <> FRIEND_NULL AND friend.eState < FRIEND_CANCEL
IF NOT IS_PED_INJURED(friend.hPed)
Private_ResetFriendGroupTimers(friend, FRIEND_CONTACT_DISMISSED, bForceResetContactTime)
Private_SetFriendState(friend, FRIEND_FAIL_WANDER)
ENDIF
ENDIF
ENDPROC
PROC Private_SetFriendsFleeing(BOOL bDueToDeath)
IF gActivity.mFriendA.eState = FRIEND_GROUP
OR gActivity.mFriendA.eState = FRIEND_LOST
Private_SetFriendFleeing(gActivity.mFriendA, bDueToDeath)
ENDIF
IF gActivity.mFriendB.eState = FRIEND_GROUP
OR gActivity.mFriendB.eState = FRIEND_LOST
Private_SetFriendFleeing(gActivity.mFriendB, bDueToDeath)
ENDIF
ENDPROC
PROC Private_SetFriendsWander()
IF gActivity.mFriendA.eState = FRIEND_GROUP
OR gActivity.mFriendA.eState = FRIEND_LOST
IF NOT IS_PED_INJURED(gActivity.mFriendA.hPed)
Private_SetFriendState(gActivity.mFriendA, FRIEND_FAIL_WANDER)
ENDIF
ENDIF
IF gActivity.mFriendB.eState = FRIEND_GROUP
OR gActivity.mFriendB.eState = FRIEND_LOST
IF NOT IS_PED_INJURED(gActivity.mFriendB.hPed)
Private_SetFriendState(gActivity.mFriendB, FRIEND_FAIL_WANDER)
ENDIF
ENDIF
ENDPROC
FUNC BOOL Private_SetFriendRejected(structFriend& friend, BOOL bAllowSubtitles, enumFriendActivityPhrase ePhrase = FAP_REJECTION_OK)
IF friend.eState <> FRIEND_NULL AND friend.eState < FRIEND_CANCEL
#IF IS_DEBUG_BUILD
TEXT_LABEL_63 tChar = GetLabel_enumCharacterList(friend.eChar)
TEXT_LABEL_63 tSubtitles = "bAllowSubtitles"
TEXT_LABEL_63 tPhrase = GetLabel_enumFriendActivityPhrase(ePhrase)
IF bAllowSubtitles tSubtitles += "=TRUE"
ELSE tSubtitles += "=FALSE"
ENDIF
CPRINTLN(DEBUG_FRIENDS, "Private_SetFriendRejected(", tChar, ", ", tSubtitles, ", ", tPhrase, ")")
#ENDIF
enumCharacterList ePlayerChar = GET_CURRENT_PLAYER_PED_ENUM()
// Play audio comment
IF IS_PED_UNINJURED(friend.hPed)
AND friend.eChar <> ePlayerChar
IF Private_CanFriendConverse(friend)
IF (ePhrase <> NO_FRIEND_ACTIVITY_PHRASE)
TEXT_LABEL tRoot
IF bAllowSubtitles = FALSE
IF PRIVATE_FriendDialogue_GetRawAudio(friend.eChar, ePhrase, tRoot)
PLAY_PED_AMBIENT_SPEECH_WITH_VOICE(friend.hPed, tRoot, PRIVATE_Get_SpeakerLabel_From_Char(friend.eChar), SPEECH_PARAMS_FORCE_FRONTEND)
ENDIF
ELSE
IF Private_GetDialogueState(gActivity.mDialogue) <> FDIALOGUE_REJECTED
Private_SetDialogueState(gActivity.mDialogue, FDIALOGUE_REJECTED, friend.eChar, ePhrase)
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
Private_SetFriendState(friend, FRIEND_FAIL_REJECTED)
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
//---------------------------------------------------------------------------------------------------
//-- Member main
//---------------------------------------------------------------------------------------------------
FUNC BOOL Private_AddFriend(structFriend& friend, enumCharacterList eChar, enumFriendLocation ePickupLocation, INT iOffsetIndex, BOOL bWasMetAmbiently = FALSE)
CPRINTLN(DEBUG_FRIENDS, "Private_AddFriend(", GetLabel_enumCharacterList(eChar), ")")
IF eChar = NO_CHARACTER
SCRIPT_ASSERT("Private_AddFriend() - Character passed is null")
ELSE
enumFriend eFriend = GET_FRIEND_FROM_CHAR(eChar)
IF eFriend >= MAX_FRIENDS
SCRIPT_ASSERT("Private_AddFriend() - Character passed is not valid friend")
ELSE
Private_SetFriendState(friend, FRIEND_PICKUP)
friend.eChar = eChar
friend.ePickup = ePickupLocation
friend.tName = GLOBAL_CHARACTER_SHEET_GET_LABEL(eChar)// g_SavedGlobals.sFriendsData.g_FriendData[eFriend].charSheet.label - had bug where label was incorrect, inits in wrong order? Just use charsheet
RESTART_TIMER_NOW(friend.mWaitTimer)
// Set friend home location
IF ePickupLocation < MAX_FRIEND_LOCATIONS
Private_SetFriendPickupLocOffset(friend, iOffsetIndex)
Private_SetFriendSwitchOverride(friend)
// If picking up from a rural location, allow more time for pickup
IF ePickupLocation = FLOC_michael_CS
OR ePickupLocation = FLOC_trevor_CS
OR ePickupLocation = FLOC_paletoMainSt_PA
OR ePickupLocation = FLOC_minimartCarpark_SS
gActivity.bIsRural = TRUE
ENDIF
ENDIF
IF ePickupLocation = FLOC_adhoc
friend.bForceCreateAsArriving = TRUE
ENDIF
Private_SyncFriendPed(friend)
// Mark as ambient launched
IF bWasMetAmbiently
friend.bWasMetAmbiently = TRUE
Private_SetFriendPickedUp(friend)
Private_SetFriendPickedUp(gActivity.mPlayer)
ELSE
friend.bWasMetAmbiently = FALSE
Private_SetFriendScenarioBlocking(friend)
ENDIF
// Setup models for friend ped and car
PED_VEH_DATA_STRUCT sVehData
IF IS_PLAYER_PED_PLAYABLE(friend.eChar) // Set required models (playable char)
GET_PLAYER_VEH_DATA(friend.eChar, sVehData, VEHICLE_TYPE_CAR)
friend.eCarModel = sVehData.model
friend.ePedModel = GET_PLAYER_PED_MODEL(friend.eChar)
ELIF friend.eChar = CHAR_JIMMY // Set required models (Jimmy rides a bike)
GET_NPC_VEH_DATA(friend.eChar, sVehData, VEHICLE_TYPE_BIKE)
friend.eCarModel = sVehData.model
friend.ePedModel = GET_NPC_PED_MODEL(friend.eChar)
ELSE // Set required models (npc char)
GET_NPC_VEH_DATA(friend.eChar, sVehData, VEHICLE_TYPE_CAR)
friend.eCarModel = sVehData.model
friend.ePedModel = GET_NPC_PED_MODEL(friend.eChar)
ENDIF
IF friend.eCarModel = DUMMY_MODEL_FOR_SCRIPT
friend.eCarModel = ASEA
ENDIF
// request models
REQUEST_MODEL(friend.eCarModel)
REQUEST_MODEL(friend.ePedModel)
// Disable family scenes
Private_EnableFamilyScenes(eChar, FALSE)
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
PROC Private_CleanupFriend(structFriend& friend, enumMemberCleanupStyle eCleanupStyle = MC_Release, BOOL bForceTimerDismissed = FALSE)
IF friend.eState <> FRIEND_NULL
#IF IS_DEBUG_BUILD
TEXT_LABEL_31 tChar = GetLabel_enumCharacterList(friend.eChar)
TEXT_LABEL_31 tStyle = GetLabel_enumMemberCleanupStyle(eCleanupStyle)
CPRINTLN(DEBUG_FRIENDS, "Private_CleanupFriend(", tChar, ", ", tStyle, ")")
#ENDIF
// Clear friend available flag
IF IS_PLAYER_PED_PLAYABLE(friend.eChar)
CLEAR_BIT(g_bitfieldFriendFlags, ENUM_TO_INT(friend.eChar))
ENDIF
// Clear switch override
Private_ClearFriendSwitchOverride(friend)
// Clear pickup help text and camera
Private_ClearFriendPickupHelp(friend) // Probably not needed (keeping just to avoid breaking anything by taking it out)
Private_ClearFriendObjectives(friend)
// Remove blips
IF DOES_BLIP_EXIST(friend.hPedBlip)
REMOVE_BLIP(friend.hPedBlip)
ENDIF
IF DOES_BLIP_EXIST(friend.hLocBlip)
REMOVE_BLIP(friend.hLocBlip)
ENDIF
// Cleanup phone
IF DOES_ENTITY_EXIST(friend.hPhone)
IF IS_ENTITY_ATTACHED_TO_ANY_PED(friend.hPhone)
DETACH_ENTITY(friend.hPhone)
ENDIF
DELETE_OBJECT(friend.hPhone)
ENDIF
// Cleanup anims
Private_ReleaseFriendPickupAnims(friend)
// Cleanup attribs
Private_ClearFriendAttribs(friend)
// Cleanup model
IF (friend.eCarModel <> DUMMY_MODEL_FOR_SCRIPT)
SET_MODEL_AS_NO_LONGER_NEEDED(friend.eCarModel)
ENDIF
IF (friend.ePedModel <> DUMMY_MODEL_FOR_SCRIPT)
SET_MODEL_AS_NO_LONGER_NEEDED(friend.ePedModel)
ENDIF
// Add communication delay
IF friend.bWasPickedUp
ADD_COMMUNICATION_DELAY_FOR_CHARACTER(friend.eChar)
ENDIF
// Reset contact timers
IF IS_PLAYER_PED_PLAYABLE(friend.eChar) AND (eCleanupStyle = MC_AmbientFlee OR eCleanupStyle = MC_AmbientRejected OR eCleanupStyle = MC_AmbientWander OR bForceTimerDismissed)
Private_ResetFriendGroupTimers(friend, FRIEND_CONTACT_DISMISSED)
ELSE
Private_ResetFriendGroupTimers(friend, FRIEND_CONTACT_FACE)
ENDIF
// Unblock family scenes
Private_EnableFamilyScenes(friend.eChar, TRUE)
// Unblock scenarios
Private_ClearFriendScenarioBlocking(friend)
// Deactive friend connections
DEACTIVATE_CHAR_CONNECTIONS(friend.eChar)
// Cleanup car (if requested)
IF eCleanupStyle = MC_DeletePedAndCar
IF DOES_ENTITY_EXIST(friend.hCar) AND DOES_ENTITY_BELONG_TO_THIS_SCRIPT(friend.hCar, FALSE)
DELETE_VEHICLE(friend.hCar)
ENDIF
ENDIF
// Cleanup ped
IF DOES_ENTITY_EXIST(friend.hPed) AND friend.hPed <> PLAYER_PED_ID()
// TODO: Store player ped info?
IF eCleanupStyle = MC_Delete
OR eCleanupStyle = MC_DeletePedAndCar
DELETE_PED(friend.hPed)
ELIF eCleanupStyle = MC_Release
SET_PED_AS_NO_LONGER_NEEDED(friend.hPed)
ELIF eCleanupStyle = MC_AmbientFlee
SET_PED_AS_AMBIENT_FRIEND_FLEE(friend.hPed, friend.eChar)
ELIF eCleanupStyle = MC_AmbientRejected
SET_PED_AS_AMBIENT_FRIEND_REJECTED(friend.hPed, friend.eChar, g_eFriendMissionZoneID)
ELIF eCleanupStyle = MC_AmbientWander
SET_PED_AS_AMBIENT_FRIEND_WANDER(friend.hPed, friend.eChar)
ELIF eCleanupStyle = MC_LeavePedIntact
//
ENDIF
ENDIF
// Reset vars
Private_InitFriend(friend)
ENDIF
ENDPROC
PROC Private_UpdateFriend(structFriend& friend)
// Check that switch override hasn't been overwritten
// #IF IS_DEBUG_BUILD
// IF friend.bSwitchOverride
// IF (g_SavedGlobals.sPlayerSceneData.g_ePlayerLastScene[friend.eChar] <> PR_SCENE_M_OVERRIDE)
// AND (g_SavedGlobals.sPlayerSceneData.g_ePlayerLastScene[friend.eChar] <> PR_SCENE_F_OVERRIDE)
// AND (g_SavedGlobals.sPlayerSceneData.g_ePlayerLastScene[friend.eChar] <> PR_SCENE_T_OVERRIDE)
// TEXT_LABEL tChar = GetLabel_enumCharacterList(friend.eChar)
// TEXT_LABEL_63 tScene = Get_String_From_Ped_Request_Scene_Enum(g_SavedGlobals.sPlayerSceneData.g_ePlayerLastScene[friend.eChar])
// CPRINTLN(DEBUG_FRIENDS, "Private_UpdateFriend(", tChar, ") - Switch scene should be overridden but is \"", tScene, "\"")
// SCRIPT_ASSERT("Private_UpdateFriend() - switch override is set, but g_ePlayerLastScene is incorrect")
// ENDIF
// ENDIF
// #ENDIF
// Exit if null or failed
IF friend.eState = FRIEND_NULL
OR friend.eState >= FRIEND_CANCEL
EXIT
ENDIF
// Check car for damage
BOOL bCarDamaged = FALSE
IF DOES_ENTITY_EXIST(friend.hCar)
IF NOT IS_ENTITY_DEAD(friend.hCar)
INT iCarHealth = GET_ENTITY_HEALTH(friend.hCar)
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), friend.hCar)
IF iCarHealth < friend.iCarHealth - 50
bCarDamaged = TRUE
ENDIF
ENDIF
friend.iCarHealth = iCarHealth
CLEAR_ENTITY_LAST_DAMAGE_ENTITY(friend.hCar)
CLEAR_ENTITY_LAST_WEAPON_DAMAGE(friend.hCar)
ELSE
IF friend.iCarHealth <> -1
bCarDamaged = TRUE
ENDIF
friend.iCarHealth = -1
ENDIF
ENDIF
// Clear friend available flag
IF IS_PLAYER_PED_PLAYABLE(friend.eChar)
CLEAR_BIT(g_bitfieldFriendFlags, ENUM_TO_INT(friend.eChar))
ENDIF
// Exit if dead
IF DOES_ENTITY_EXIST(friend.hPed) AND IS_PED_INJURED(friend.hPed)
CPRINTLN(DEBUG_FRIENDS, "Private_UpdateFriend(", GetLabel_enumCharacterList(friend.eChar), ") - Ped is dead")
IF friend.bWasPickedUp
UPDATE_FRIEND_LIKE(gActivity.mPlayer.eChar, friend.eChar, FriendLike_BUDDY_INJURED)
ENDIF
IF IS_PLAYER_PED_PLAYABLE(friend.eChar)
Private_ClearFriendSwitchOverride(friend)
g_SavedGlobals.sPlayerSceneData.g_ePlayerLastScene[friend.eChar] = PR_SCENE_DEAD
ENDIF
// Make other friends run away (if in group)
Private_SetFriendsFleeing(TRUE)
Private_SetFriendState(friend, FRIEND_FAIL_INJURED)
EXIT
ENDIF
// Exit if picked friend up, but their ped doesn't exist
IF NOT DOES_ENTITY_EXIST(friend.hPed)
IF friend.eState = FRIEND_GROUP
OR friend.eState = FRIEND_LOST
OR friend.eState = FRIEND_TRAPPED
#IF IS_DEBUG_BUILD
TEXT_LABEL tChar = GetLabel_enumCharacterList(friend.eChar)
TEXT_LABEL tState = GetLabel_enumFriendState(friend.eState)
CPRINTLN(DEBUG_FRIENDS, "Private_UpdateFriend(", tChar, ") state = ", tState, ", ped doesn't exist")
SCRIPT_ASSERT("Private_UpdateFriend() state = FRIEND_GROUP/LOST/SHOP, ped doesn't exist")
#ENDIF
Private_SetFriendState(friend, FRIEND_CANCEL)
EXIT
ENDIF
ENDIF
// Check for ped damage
IF DOES_ENTITY_EXIST(friend.hPed) AND friend.hPed <> PLAYER_PED_ID()
// IF IS_PED_SITTING_IN_ANY_VEHICLE(PLAYER_PED_ID())
// AND NOT IS_PED_ON_ANY_BIKE(PLAYER_PED_ID())
// AND NOT IS_PED_IN_VEHICLE(friend.hPed, GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID()))
// IF HAS_ENTITY_BEEN_DAMAGED_BY_ENTITY(friend.hPed, PLAYER_PED_ID(), TRUE)
// Private_SetFriendAttacked(friend, TRUE)
// ENDIF
// ELSE
// ENDIF
BOOL bAreInVehicleTogether = FALSE
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
VEHICLE_INDEX hPlayerVehicle = GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID())
IF DOES_ENTITY_EXIST(hPlayerVehicle) AND IS_PED_IN_VEHICLE(friend.hPed, hPlayerVehicle)
bAreInVehicleTogether = TRUE
ENDIF
ENDIF
IF bAreInVehicleTogether = TRUE
friend.bWereInVehicleTogether = TRUE
ELSE
IF NOT IS_PED_RAGDOLL(PLAYER_PED_ID())
AND NOT IS_PED_RAGDOLL(friend.hPed)
AND NOT IS_PED_RUNNING_RAGDOLL_TASK(PLAYER_PED_ID())
AND NOT IS_PED_RUNNING_RAGDOLL_TASK(friend.hPed)
friend.bWereInVehicleTogether = FALSE
ENDIF
ENDIF
INT iNewHealth = GET_ENTITY_HEALTH(friend.hPed)
// TEXT_LABEL_31 tText = "Hlth:"
// tText += iNewHealth
// DISPLAY_TEXT_WITH_LITERAL_STRING(0.1, 0.1, "STRING", tText)
IF friend.bWereInVehicleTogether = FALSE
// DISPLAY_TEXT_WITH_LITERAL_STRING(0.1, 0.2, "STRING", "CanDamage")
IF HAS_ENTITY_BEEN_DAMAGED_BY_ENTITY(friend.hPed, PLAYER_PED_ID(), TRUE)
IF iNewHealth < friend.iHealth - 10
Private_SetFriendAttacked(friend, TRUE)
ENDIF
ENDIF
ENDIF
friend.iHealth = iNewHealth
ENDIF
// Exit if player is third party (delete ped too)
IF friend.eState <> FRIEND_TRAPPED
IF Private_IsPlayerThirdParty()
IF friend.eChar <> GET_CURRENT_PLAYER_PED_ENUM()
IF DOES_ENTITY_EXIST(friend.hPed)
Private_TryRemoveFriend(friend, TRUE)
ENDIF
ENDIF
EXIT
ENDIF
ENDIF
// Reset wait timer if not at doorstep
IF friend.eState = FRIEND_PICKUP
IF friend.ePickup = NO_FRIEND_LOCATION
OR (DOES_ENTITY_EXIST(friend.hPed) AND Util_IsPedOutsideRange(friend.hPed, friend.vDriveway, CONST_fIsBuddyAtDriveway))
RESTART_TIMER_NOW(friend.mWaitTimer)
ENDIF
ENDIF
// Delay calls from buddy till after mission
g_iCharWaitTime[friend.eChar] = GET_GAME_TIMER() + CC_GLOBAL_DELAY_BETWEEN_COMMS
// Prevent player/friends attacking each other
IF NOT IS_PED_INJURED(friend.hPed)
SET_PED_RESET_FLAG(friend.hPed, PRF_DisablePlayerMeleeFriendlyAttacks, TRUE)
ENDIF
// Debug print if collision is loaded around the friend ped
// IF DOES_ENTITY_EXIST(friend.hPed) AND friend.eChar <> GET_CURRENT_PLAYER_PED_ENUM()
// TEXT_LABEL_31 tColl
// IF friend.eChar = CHAR_MICHAEL tColl = "M-"
// ELIF friend.eChar = CHAR_FRANKLIN tColl = "F-"
// ELIF friend.eChar = CHAR_TREVOR tColl = "T-"
// ENDIF
// IF HAS_COLLISION_LOADED_AROUND_ENTITY(friend.hPed)
// tColl += "COLL"
// ELSE
// tColl += "none"
// ENDIF
// DISPLAY_TEXT_WITH_LITERAL_STRING(0.7, 0.6, "STRING", tColl)
//// SET_ENTITY_SHOULD_FREEZE_WAITING_ON_COLLISION
//
// TEXT_LABEL_31 tHeight = "H "
// VECTOR vPos = GET_ENTITY_COORDS(friend.hPed, FALSE)
// tHeight += ROUND(vPos.z * 1000.0)
// DISPLAY_TEXT_WITH_LITERAL_STRING(0.7, 0.7, "STRING", tHeight)
// ENDIF
//-- For non-player members, manage state...
IF friend.eChar <> GET_CURRENT_PLAYER_PED_ENUM()
// Pickup ----------------------------------------------------------------------------
IF friend.eState = FRIEND_PICKUP
Private_LoadFriendPickupAnims(friend)
//--- If not switching: Check state conditions ---
IF NOT Private_IsPlayerSwitching()
// Fail if late (and ped doesn't exist, and not on phone to cancel)
IF (gActivity.bIsRural = FALSE AND TIMER_DO_WHEN_READY(friend.mWaitTimer, CONST_fLateFailInRealMinutes*60.0))
OR (gActivity.bIsRural = TRUE AND TIMER_DO_WHEN_READY(friend.mWaitTimer, CONST_fLateFailInRealMinutes_rural*60.0))
IF NOT DOES_ENTITY_EXIST(friend.hPed)
IF NOT friend.bIsBeingCalledToCancel
RESET_FRIEND_LAST_CONTACT_TIMER(gActivity.mPlayer.eChar, friend.eChar, FRIEND_CONTACT_PHONE)
UPDATE_FRIEND_LIKE(gActivity.mPlayer.eChar, friend.eChar, FriendLike_PLAYER_NO_ARRIVAL)
Private_SetFriendState(friend, FRIEND_FAIL_LATE)
EXIT
ENDIF
ENDIF
ENDIF
// Cancel if calls back
IF NOT friend.bIsBeingCalledToCancel
IF IS_CALLING_FRIEND_FOR_ACTIVITY_CANCELLATION(friend.eChar)
IF DOES_ENTITY_EXIST(friend.hPed) // Calling+ped -> Start answerphone conversation
PRIVATE_Friend_DoAnswerPhone(gActivity.convPedsVoicemail, friend.eChar)
ELIF PRIVATE_Friend_DoCancelConv(gActivity.convPedsDefault, gActivity.mPlayer.eChar, friend.eChar) // Calling+no ped -> Start cancel conversation
friend.bIsBeingCalledToCancel = TRUE
EXIT
ENDIF
ENDIF
ELSE
IF WAS_LAST_CELLPHONE_CALL_INTERRUPTED() // Call is aborted before answer -> Reset bool
friend.bIsBeingCalledToCancel = FALSE
EXIT
ELIF IS_CELLPHONE_CONVERSATION_PLAYING() // Call connects -> Remove friend
RESET_FRIEND_LAST_CONTACT_TIMER(gActivity.mPlayer.eChar, friend.eChar, FRIEND_CONTACT_PHONE)
Private_SetFriendState(friend, FRIEND_CANCEL)
EXIT
ENDIF
ENDIF
// Pickup if able
IF NOT Private_IsPlayerSwitching()
IF NOT IS_PED_INJURED(friend.hPed)
IF friend.bWasMetAmbiently
OR Private_TryPickupFriend(friend)
Private_SetFriendPickedUp(friend)
Private_SetFriendPickedUp(gActivity.mPlayer)
EXIT
ELIF IS_ENTITY_AT_ENTITY(PLAYER_PED_ID(), friend.hPed, <<25.0,25.0,25.0>>)
IF (NOT IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID()) OR (ABSF(GET_ENTITY_SPEED(PLAYER_PED_ID())) < 10.0))
IF (friend.eChar = CHAR_JIMMY OR friend.eChar = CHAR_AMANDA)
IF Private_IsPlayerOrFriendPersued(friend, FALSE)
Private_SetFriendFleeing(friend)
EXIT
ENDIF
ELSE
IF Private_IsPlayerOrFriendPersued(friend, TRUE)
Private_SetFriendPickedUp(friend)
Private_SetFriendPickedUp(gActivity.mPlayer)
EXIT
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
// Display corona
IF Util_IsPedOutsideRange(PLAYER_PED_ID(), friend.vDriveway, 10.0)
IS_ENTITY_AT_COORD(PLAYER_PED_ID(), friend.vDriveway, g_vAnyMeansLocate, TRUE)
ENDIF
//--- If is switching: Freeze ped position ---
ELSE
IF NOT IS_PED_INJURED(friend.hPed)
IF Private_IsSwitchCamDescending()
IF IS_PED_IN_ANY_VEHICLE(friend.hPed)
FREEZE_ENTITY_POSITION(GET_VEHICLE_PED_IS_IN(friend.hPed), TRUE)
ELSE
FREEZE_ENTITY_POSITION(friend.hPed, TRUE)
ENDIF
//CLEAR_PED_TASKS(friend.hPed)
ELSE
IF IS_PED_IN_ANY_VEHICLE(friend.hPed)
FREEZE_ENTITY_POSITION(GET_VEHICLE_PED_IS_IN(friend.hPed), FALSE)
ELSE
FREEZE_ENTITY_POSITION(friend.hPed, FALSE)
ENDIF
ENDIF
ENDIF
CANCEL_TIMER(friend.mArrivalStoppedTimer)
// friend.iArrivalDrivingMode = -1
friend.bIsBeingCalledToCancel = FALSE
ENDIF
//--- Process ped ---
IF DOES_ENTITY_EXIST(friend.hPed)
IF NOT Private_IsPlayerSwitching()
Private_TryRemoveFriend(friend, FALSE, TRUE)
ENDIF
ELSE
IF NOT Private_TryCreateFriend_AtDest(friend, Private_IsSwitchCamDescending())
Private_TryCreateFriend_ArrivingAtDest(friend)
ENDIF
ENDIF
IF NOT IS_PED_INJURED(friend.hPed)
Private_FriendArrivalTasks(friend)
ENDIF
// In group --------------------------------------------------------------------------
ELIF friend.eState = FRIEND_GROUP
IF DOES_ENTITY_EXIST(friend.hPhone)
IF IS_ENTITY_ATTACHED_TO_ANY_PED(friend.hPhone)
DETACH_ENTITY(friend.hPhone)
ENDIF
DELETE_OBJECT(friend.hPhone)
ENDIF
// Check if buddy is in group...
IF Private_ShouldFriendFlee(friend)
Private_SetFriendFleeing(friend)
ELIF NOT IS_PED_GROUP_MEMBER(friend.hPed, PLAYER_GROUP_ID())
Private_SetFriendState(friend, FRIEND_LOST)
ELIF Private_CanFriendEnterPlummet(friend)
Private_SetFriendState(friend, FRIEND_PLUMMET)
ELIF Private_CanFriendEnterParachute(friend)
Private_SetFriendState(friend, FRIEND_PARACHUTE)
REMOVE_PED_FROM_GROUP(friend.hPed)
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(friend.hPed, TRUE)
CLEAR_PED_TASKS(friend.hPed)
ELSE
// Set friend available flag
IF IS_PLAYER_PED_PLAYABLE(friend.eChar)
SET_BIT(g_bitfieldFriendFlags, ENUM_TO_INT(friend.eChar))
ENDIF
// Set can teleport to player
SET_PED_CAN_TELEPORT_TO_GROUP_LEADER(friend.hPed, PLAYER_GROUP_ID(), TRUE)
// Get out of sinking car
IF IS_PED_IN_ANY_VEHICLE(friend.hPed)
VEHICLE_INDEX hCar = GET_VEHICLE_PED_IS_IN(friend.hPed)
IF DOES_ENTITY_EXIST(hCar) AND IS_VEHICLE_IN_WATER(hCar) AND NOT IS_VEHICLE_DRIVEABLE(hCar)
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_LEAVE_ANY_VEHICLE)
CLEAR_PED_TASKS(friend.hPed)
TASK_LEAVE_ANY_VEHICLE(friend.hPed)
ENDIF
ENDIF
ENDIF
// Block running (after pickup)
IF GET_GAME_TIMER() < friend.iBlockRunningUntilTime
IF NOT Private_IsPlayerOrFriendPersued(friend, TRUE) //IS_PLAYER_WANTED_LEVEL_GREATER(PLAYER_ID(), 0)
IF NOT IS_PED_INJURED(PLAYER_PED_ID()) AND IS_ENTITY_AT_ENTITY(PLAYER_PED_ID(), friend.hPed, <<20.0, 20.0, 20.0>>)
SET_PED_MAX_MOVE_BLEND_RATIO(friend.hPed, PEDMOVE_WALK)
ENDIF
ENDIF
ENDIF
// Head tracking
IF IS_ENTITY_AT_ENTITY(friend.hPed, PLAYER_PED_ID(), << 30.0, 30.0, 30.0 >>)
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_LOOK_AT_ENTITY)
IF NOT IS_PED_IN_ANY_VEHICLE(friend.hPed) OR NOT IS_PED_IN_VEHICLE(PLAYER_PED_ID(), GET_VEHICLE_PED_IS_IN(friend.hPed))
TASK_LOOK_AT_ENTITY(friend.hPed, PLAYER_PED_ID(), 3000)
ENDIF
ENDIF
IF NOT IsPedPerformingTask(PLAYER_PED_ID(), SCRIPT_TASK_LOOK_AT_ENTITY)
IF NOT IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID()) OR NOT IS_PED_IN_VEHICLE(friend.hPed, GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID()))
TASK_LOOK_AT_ENTITY(PLAYER_PED_ID(), friend.hPed, 3000)
ENDIF
ENDIF
ENDIF
// Respond to vehicle damage with dialogue
IF bCarDamaged
IF IS_PLAYER_PED_PLAYABLE(friend.eChar)
IF gActivity.mDialogue.eState != FDIALOGUE_DAMAGE
Private_SetDialogueState(gActivity.mDialogue, FDIALOGUE_DAMAGE, friend.eChar, FAP_COMMENT_CARDAMAGE)
friend.iCarShotTime = GET_GAME_TIMER() + 18000//12000
ENDIF
ELSE
CPRINTLN(DEBUG_FRIENDS, "Private_UpdateFriend() - Not playing car damage dialogue - only have lines for playable peds")
ENDIF
ENDIF
ENDIF
// Lost ------------------------------------------------------------------------------
ELIF friend.eState = FRIEND_LOST
IF DOES_ENTITY_EXIST(friend.hPhone)
IF IS_ENTITY_ATTACHED_TO_ANY_PED(friend.hPhone)
DETACH_ENTITY(friend.hPhone)
ENDIF
DELETE_OBJECT(friend.hPhone)
ENDIF
IF NOT IS_PED_GROUP_MEMBER(friend.hPed, PLAYER_GROUP_ID())
IF NOT IS_PED_RAGDOLL(friend.hPed)
IF IS_BUDDY_GOOD_TO_JOIN_PLAYERS_GROUP(gActivity.locateData, friend.hPed, CONST_fBuddyJoinDist)
SET_PED_AS_GROUP_MEMBER(friend.hPed, PLAYER_GROUP_ID())
ENDIF
ENDIF
ENDIF
IF Private_ShouldFriendFlee(friend)
Private_SetFriendFleeing(friend)
ELIF Util_IsPedOutsideRangePed(friend.hPed, PLAYER_PED_ID(), CONST_fBuddyLostDist)
Private_SetFriendState(friend, FRIEND_FAIL_LOST)
ELIF Private_CanFriendEnterPlummet(friend)
Private_SetFriendState(friend, FRIEND_PLUMMET)
ELIF Private_CanFriendEnterParachute(friend)
Private_SetFriendState(friend, FRIEND_PARACHUTE)
REMOVE_PED_FROM_GROUP(friend.hPed)
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(friend.hPed, TRUE)
CLEAR_PED_TASKS(friend.hPed)
ELIF IS_PED_GROUP_MEMBER(friend.hPed, PLAYER_GROUP_ID())
IF NOT IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
OR IS_PED_IN_VEHICLE(friend.hPed, GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID()))
Private_SetFriendState(friend, FRIEND_GROUP)
ELSE
// Get out of sinking car
IF IS_PED_IN_ANY_VEHICLE(friend.hPed)
VEHICLE_INDEX hCar = GET_VEHICLE_PED_IS_IN(friend.hPed)
IF DOES_ENTITY_EXIST(hCar) AND IS_VEHICLE_IN_WATER(hCar) AND NOT IS_VEHICLE_DRIVEABLE(hCar)
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_LEAVE_ANY_VEHICLE)
CLEAR_PED_TASKS(friend.hPed)
TASK_LEAVE_ANY_VEHICLE(friend.hPed)
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
// Trapped ---------------------------------------------------------------------------
ELIF friend.eState = FRIEND_TRAPPED
IF DOES_ENTITY_EXIST(friend.hPhone)
IF IS_ENTITY_ATTACHED_TO_ANY_PED(friend.hPhone)
DETACH_ENTITY(friend.hPhone)
ENDIF
DELETE_OBJECT(friend.hPhone)
ENDIF
// Get out of sinking car
IF IS_PED_IN_ANY_VEHICLE(friend.hPed)
VEHICLE_INDEX hCar = GET_VEHICLE_PED_IS_IN(friend.hPed)
IF DOES_ENTITY_EXIST(hCar) AND IS_VEHICLE_IN_WATER(hCar) AND NOT IS_VEHICLE_DRIVEABLE(hCar)
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_LEAVE_ANY_VEHICLE)
CLEAR_PED_TASKS(friend.hPed)
TASK_LEAVE_ANY_VEHICLE(friend.hPed)
ENDIF
ENDIF
ENDIF
// Complaining about being stuck in the car...
IF friend.bDoneTrappedDialogue = FALSE
TEXT_LABEL tBlock, tRoot
IF Private_CanPlayAudio(gActivity.mAudio)
AND Private_GetFriendActivityPhrase(GET_CURRENT_PLAYER_PED_ENUM(), friend.eChar, FAP_REJECTION_STUCK, tBlock, tRoot)
IF CREATE_CONVERSATION(gActivity.convPedsDefault, tBlock, tRoot, CONV_PRIORITY_AMBIENT_HIGH, DO_NOT_DISPLAY_SUBTITLES)
friend.bDoneTrappedDialogue = TRUE
ENDIF
ENDIF
ENDIF
// // Keep complaining about being stuck in the car...
// IF Private_CanPlayAudio(gActivity.mAudio)
// IF Private_CanFriendConverse(friend)
// IF TIMER_DO_WHEN_READY(friend.mTrappedTimer, 0.0)
//
// PLAY_PED_AMBIENT_SPEECH(friend.hPed, "LET_ME_OUT", SPEECH_PARAMS_FORCE_FRONTEND)
// RESTART_TIMER_AT(friend.mTrappedTimer, -15.0)
//
// ENDIF
// ENDIF
// ENDIF
// Plummet ----------------------------------------------------------------------------
ELIF friend.eState = FRIEND_PLUMMET
IF DOES_ENTITY_EXIST(friend.hPhone)
IF IS_ENTITY_ATTACHED_TO_ANY_PED(friend.hPhone)
DETACH_ENTITY(friend.hPhone)
ENDIF
DELETE_OBJECT(friend.hPhone)
ENDIF
IF Private_CanFriendExitPlummet(friend)
CLEAR_PED_TASKS(friend.hPed)
IF NOT IS_PED_GROUP_MEMBER(friend.hPed, PLAYER_GROUP_ID())
IF NOT IS_PED_RAGDOLL(friend.hPed)
IF IS_BUDDY_GOOD_TO_JOIN_PLAYERS_GROUP(gActivity.locateData, friend.hPed, CONST_fBuddyJoinDist)
SET_PED_AS_GROUP_MEMBER(friend.hPed, PLAYER_GROUP_ID())
ENDIF
ENDIF
ENDIF
IF IS_PED_GROUP_MEMBER(friend.hPed, PLAYER_GROUP_ID())
Private_SetFriendState(friend, FRIEND_GROUP)
ELSE
Private_SetFriendState(friend, FRIEND_LOST)
ENDIF
ELSE
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_PAUSE)
CLEAR_PED_TASKS(friend.hPed)
TASK_PAUSE(friend.hPed, -1)
ENDIF
ENDIF
// Parachute -------------------------------------------------------------------------
ELIF friend.eState = FRIEND_PARACHUTE
IF DOES_ENTITY_EXIST(friend.hPhone)
IF IS_ENTITY_ATTACHED_TO_ANY_PED(friend.hPhone)
DETACH_ENTITY(friend.hPhone)
ENDIF
DELETE_OBJECT(friend.hPhone)
ENDIF
IF Private_CanFriendExitParachute(friend)
CLEAR_PED_TASKS(friend.hPed)
CLEAR_PED_PARACHUTE_PACK_VARIATION(friend.hPed)
IF NOT IS_PED_GROUP_MEMBER(friend.hPed, PLAYER_GROUP_ID())
IF NOT IS_PED_RAGDOLL(friend.hPed)
IF IS_BUDDY_GOOD_TO_JOIN_PLAYERS_GROUP(gActivity.locateData, friend.hPed, CONST_fBuddyJoinDist)
SET_PED_AS_GROUP_MEMBER(friend.hPed, PLAYER_GROUP_ID())
ENDIF
ENDIF
ENDIF
IF IS_PED_GROUP_MEMBER(friend.hPed, PLAYER_GROUP_ID())
Private_SetFriendState(friend, FRIEND_GROUP)
ELSE
Private_SetFriendState(friend, FRIEND_LOST)
ENDIF
ELSE
IF NOT IsPedPerformingTask(friend.hPed, SCRIPT_TASK_PARACHUTE_TO_TARGET)
friend.vParachuteTarget = GET_ENTITY_COORDS(PLAYER_PED_ID())
IF IS_ENTITY_AT_COORD(friend.hPed, friend.vParachuteTarget, <<20.0, 20.0, 20.0>>)
friend.vParachuteTarget.z += 40.0
ENDIF
CLEAR_PED_TASKS(friend.hPed)
TASK_PARACHUTE_TO_TARGET(friend.hPed, friend.vParachuteTarget)
friend.iParachuteUpdateTime = GET_GAME_TIMER() + 1000
ELIF friend.iParachuteUpdateTime < GET_GAME_TIMER()
OR IS_ENTITY_AT_COORD(friend.hPed, friend.vParachuteTarget, <<20.0, 20.0, 20.0>>)
friend.vParachuteTarget = GET_ENTITY_COORDS(PLAYER_PED_ID())
IF IS_ENTITY_AT_COORD(friend.hPed, friend.vParachuteTarget, <<20.0, 20.0, 20.0>>)
friend.vParachuteTarget.z += 40.0
ENDIF
SET_PARACHUTE_TASK_TARGET(friend.hPed, friend.vParachuteTarget)
friend.iParachuteUpdateTime = GET_GAME_TIMER() + 1000
ENDIF
IF GET_PED_PARACHUTE_STATE(friend.hPed) = PPS_SKYDIVING
FORCE_PED_TO_OPEN_PARACHUTE(friend.hPed)
ENDIF
ENDIF
ENDIF
ENDIF
ENDPROC
PROC Private_RemoveFailedFriend(structFriend& friend)
IF friend.eState = FRIEND_FAIL_INJURED
enumFriend eFriend
eFriend = GET_FRIEND_FROM_CHAR(friend.eChar)
START_FRIEND_FAIL_TIMER(eFriend, GET_COMM_ID_FOR_FRIEND_FAIL(eFriend))
Private_QueueFailReason(friend.eChar, FFR_Injured, Private_GetHospitalChargeCID(friend.hPed))
Private_CleanupFriend(friend, MC_Release)
ELIF friend.eState = FRIEND_FAIL_LATE
Private_QueueFailReason(friend.eChar, FFR_Late)
Private_CleanupFriend(friend, MC_Delete)
ELIF friend.eState = FRIEND_FAIL_LOST
Private_QueueFailReason(friend.eChar, FFR_Lost)
Private_CleanupFriend(friend, MC_Delete)
ELIF friend.eState = FRIEND_FAIL_REJECTED
Private_CleanupFriend(friend, MC_AmbientRejected)
ELIF friend.eState = FRIEND_FAIL_FLEE
Private_QueueFailReason(friend.eChar, FFR_Flee)
Private_CleanupFriend(friend, MC_AmbientFlee)
ELIF friend.eState = FRIEND_FAIL_ATTACKED
Private_QueueFailReason(friend.eChar, FFR_Attacked)
Private_CleanupFriend(friend, MC_AmbientFlee)
ELIF friend.eState = FRIEND_FAIL_WANDER
Private_CleanupFriend(friend, MC_AmbientWander)
ELIF friend.eState = FRIEND_CANCEL
// Private_QueueFailReason(friend.eChar, FFR_Cancel)
Private_CleanupFriend(friend, MC_AmbientWander)
ELSE
Private_CleanupFriend(friend, MC_AmbientWander)
ENDIF
ENDPROC