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

2872 lines
98 KiB
Scheme
Executable File

// *****************************************************************************************
// *****************************************************************************************
//
// MISSION NAME : friendActivity_memberSoldier_private.sch
// AUTHOR : Sam Hackett
// DESCRIPTION : State machine for individual battle buddies
//
// *****************************************************************************************
// *****************************************************************************************
//- commands headers -//
//- script headers -//
//- public headers -//
USING "ambient_common.sch"
//- private headers -//
USING "friendActivity_private.sch"
USING "battlebuddy_private.sch"
USING "RC_Helper_Functions.sch"
#IF IS_DEBUG_BUILD
//- debug headers -//
#ENDIF
//-------------------------------------------------------------------------------------------------------------------------------------------
// CONSTANTS
//-------------------------------------------------------------------------------------------------------------------------------------------
CONST_FLOAT CONST_fSoldierExtraMaxHealth 400.0
CONST_FLOAT CONST_fSoldierStandardMaxHealth 200.0
CONST_FLOAT CONST_fSoldierRejectTime 300.0//60.0//120.0//60.0*2.0
//-------------------------------------------------------------------------------------------------------------------------------------------
// DEBUG
//-------------------------------------------------------------------------------------------------------------------------------------------
#IF IS_DEBUG_BUILD
FUNC STRING GetLabel_enumSoldierState(enumSoldierState eState)
SWITCH eState
CASE SOLDIER_NULL RETURN "SOLDIER_NULL" BREAK
CASE SOLDIER_CREATE_IN_CAR RETURN "SOLDIER_CREATE_IN_CAR" BREAK
CASE SOLDIER_CREATE_ON_FOOT RETURN "SOLDIER_CREATE_ON_FOOT" BREAK
CASE SOLDIER_ARRIVE_IN_CAR RETURN "SOLDIER_ARRIVE_IN_CAR" BREAK
CASE SOLDIER_APPROACH RETURN "SOLDIER_APPROACH" BREAK
CASE SOLDIER_FOLLOW RETURN "SOLDIER_FOLLOW" BREAK
CASE SOLDIER_GROUP RETURN "SOLDIER_GROUP" BREAK
CASE SOLDIER_COMBAT RETURN "SOLDIER_COMBAT" BREAK
CASE SOLDIER_PARACHUTE RETURN "SOLDIER_PARACHUTE" BREAK
CASE SOLDIER_PASSENGER RETURN "SOLDIER_PASSENGER" BREAK
CASE SOLDIER_PLAYER RETURN "SOLDIER_PLAYER" BREAK
CASE SOLDIER_OVERRIDDEN RETURN "SOLDIER_OVERRIDDEN" BREAK
CASE SOLDIER_ARREST RETURN "SOLDIER_ARREST" BREAK
CASE SOLDIER_FAIL_INJURED RETURN "SOLDIER_FAIL_INJURED" BREAK
CASE SOLDIER_FAIL_LOST RETURN "SOLDIER_FAIL_LOST" BREAK
ENDSWITCH
SCRIPT_ASSERT("GetLabel_enumSoldierState() - Unknown state")
RETURN "<unknown state>"
ENDFUNC
PROC DEBUG_DisplaySquadMemberInfo(structSoldier& soldier)
IF IS_PLAYER_PED_PLAYABLE(soldier.eChar)
// Get line to print on
INT iRow = 4 + ENUM_TO_INT(soldier.eChar)//CONST_iActivityDebugPrintLineTop + ENUM_TO_INT(soldier.eChar)
// Build string
TEXT_LABEL_63 tName = GetLabel_enumFriend(GET_FRIEND_FROM_CHAR(soldier.eChar))
tName += ": "
tName += GetLabel_enumSoldierState(soldier.eState)
IF soldier.bInitState
tName += "*"
ENDIF
tName += " ["
IF DOES_ENTITY_EXIST(GET_BATTLEBUDDY_PED(soldier.eChar))
tName += "e"
ENDIF
IF IS_BIT_SET(g_bitfieldBattleBuddyOverridden, ENUM_TO_INT(soldier.eChar))
tName += "O"
ENDIF
IF IS_BIT_SET(g_bitfieldBattleBuddyAvailable, ENUM_TO_INT(soldier.eChar))
tName += "A"
// IF IS_TIMER_STARTED(soldier.mAvailableTimer)
// tName += FLOOR(GET_TIMER_IN_SECONDS(soldier.mAvailableTimer)) / 60
// ENDIF
ENDIF
IF HAS_FRIEND_FAILED(soldier.eChar)
tName += "F"
ENDIF
tName += "]"
IF NOT IS_PED_INJURED(soldier.hPed)
tName += " "
tName += (GET_ENTITY_HEALTH(soldier.hPed) * 100) / GET_ENTITY_MAX_HEALTH(soldier.hPed)
tName += "%"
ENDIF
DrawFriendLiteralString(tName, iRow, HUD_COLOUR_BLUELIGHT)
ENDIF
ENDPROC
#ENDIF
FUNC BOOL Private_CheckBehaviourFlag(enumBattleBuddyBehaviourFlag eFlag)
RETURN IS_BIT_SET(g_bitfieldBattleBuddyBehaviour, ENUM_TO_INT(eFlag))
ENDFUNC
//-------------------------------------------------------------------------------------------------------------------------------------------
// CREATION/DELETION/SETSTATE
//-------------------------------------------------------------------------------------------------------------------------------------------
PROC Private_InitSoldier(structSoldier& soldier, enumCharacterList eChar)
soldier.eChar = eChar
soldier.eState = SOLDIER_NULL
soldier.bInitState = FALSE
soldier.hPed = NULL
soldier.hBlip = NULL
soldier.hVehicle = NULL
soldier.eCreateInVehicleType = VEHICLE_TYPE_CAR
CANCEL_TIMER(soldier.mAvailableTimer)
soldier.hArrivalNode = NULL
soldier.iArrivalStage = 0
soldier.iArrivalStoppedTimer = 0
soldier.iArrivalHornTimer = 0
soldier.bRequestGreetingDialogue = FALSE
soldier.eCurrentBlip = SOLDIER_BLIP_OFF
soldier.bWasInCombat = FALSE
soldier.iShoutTimer = 0
soldier.bIsInParkingRange = FALSE
soldier.eCharToDriveTo = NO_CHARACTER
soldier.iCombatDelay = 0
soldier.iStealthDelay = 0
soldier.bDefendingCargobobArea = FALSE
ENDPROC
FUNC BOOL Private_IsSoldierGlobalFlagSet(structSoldier& soldier, INT iGlobalBitfield)
RETURN IS_BIT_SET(iGlobalBitfield, ENUM_TO_INT(soldier.eChar))
ENDFUNC
PROC Private_SetSoldierGlobalFlag(structSoldier& soldier, INT& iGlobalBitfield)
SET_BIT(iGlobalBitfield, ENUM_TO_INT(soldier.eChar))
ENDPROC
PROC Private_ClearSoldierGlobalFlag(structSoldier& soldier, INT& iGlobalBitfield)
CLEAR_BIT(iGlobalBitfield, ENUM_TO_INT(soldier.eChar))
ENDPROC
PROC Private_SetSoldierAvailable(structSoldier& soldier)
INT iIndex = ENUM_TO_INT(soldier.eChar)
IF NOT IS_BIT_SET(g_bitfieldBattleBuddyAvailable, iIndex)
CPRINTLN(DEBUG_FRIENDS, "Private_SetSoldierAvailable(", GetLabel_enumCharacterList(soldier.eChar), ")")
SET_BIT(g_bitfieldBattleBuddyAvailable, iIndex)
RESTART_TIMER_NOW(soldier.mAvailableTimer)
ENDIF
ENDPROC
PROC Private_ClearSoldierAvailable(structSoldier& soldier)
INT iIndex = ENUM_TO_INT(soldier.eChar)
IF IS_BIT_SET(g_bitfieldBattleBuddyAvailable, iIndex)
CPRINTLN(DEBUG_FRIENDS, "Private_ClearSoldierAvailable(", GetLabel_enumCharacterList(soldier.eChar), ")")
CLEAR_BIT(g_bitfieldBattleBuddyAvailable, iIndex)
// Reset timers with other available soldiers
enumCharacterList eLoopChar
REPEAT MAX_BATTLE_BUDDIES eLoopChar
IF soldier.eChar <> eLoopChar
AND Private_IsSoldierGlobalFlagSet(gActivity.mSoldiers[eLoopChar], g_bitfieldBattleBuddyAvailable)
RESET_FRIEND_LAST_CONTACT_TIMER(soldier.eChar, eLoopChar, FRIEND_CONTACT_FACE)
ENDIF
ENDREPEAT
ENDIF
ENDPROC
FUNC BOOL Private_SyncSoldierPed(structSoldier& soldier)
// Get the selector ped
soldier.hPed = g_sPlayerPedRequest.sSelectorPeds.pedID[GET_SELECTOR_SLOT_FROM_PLAYER_PED_ENUM(soldier.eChar)]
IF IS_PED_UNINJURED(soldier.hPed)
SET_ENTITY_AS_MISSION_ENTITY(soldier.hPed, TRUE, TRUE)
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
PROC Private_SwitchOnRoadsAroundMilitaryBase(BOOL bEnable)
VECTOR vPos1[TOTAL_SUB_AREAS], vPos2[TOTAL_SUB_AREAS]
FLOAT fWidth[TOTAL_SUB_AREAS]
INT iUpperZ = 0
vPos1[0] = <<-1600.287598,2806.558838,16.444683>> // Lower ramp, z is fine
vPos2[0] = <<-1665.018066,2881.735352,37.717834>>
fWidth[0] = 51.250000
vPos1[1] = <<-1776.896606,2879.143555,30>>
vPos2[1] = <<-2344.001953,3201.897705,250 + iUpperZ>>
fWidth[1] = 260.000000
vPos1[2] = <<-2112.905029,2869.362549,30>>
vPos2[2] = <<-2794.882568,3266.906250,250 + iUpperZ>>
fWidth[2] = 88.500000
vPos1[3] = <<-2028.532593,3305.804932,30>>
vPos2[3] = <<-1715.586060,3126.489258,250 + iUpperZ>>
fWidth[3] = 234.500000
vPos1[4] = <<-2376.502441,3144.741455,30>>
vPos2[4] = <<-2736.747803,3344.206299,250 + iUpperZ>>
fWidth[4] = 123.000000
vPos1[5] = <<-2049.336914,3264.578613,30>>
vPos2[5] = <<-2162.414307,3328.668457,250 + iUpperZ>>
fWidth[5] = 139.750000
vPos1[6] = <<-2317.464600,3377.807617,30>>
vPos2[6] = <<-2419.537354,3208.734131,250 + iUpperZ>>
fWidth[6] = 133.250000
vPos1[7] = <<-2207.642334,3389.073486,30>>
vPos2[7] = <<-2208.911377,3269.166748,250 + iUpperZ>>
fWidth[7] = 160.250000
vPos1[8] = <<-2545.383301,3270.769531,30>>
vPos2[8] = <<-2362.583984,3377.416016,250 + iUpperZ>>
fWidth[8] = 97.500000
vPos1[9] = <<-2500.651123,2908.789795,30>>
vPos2[9] = <<-2440.603271,3010.204834,250 + iUpperZ>>
fWidth[9] = 144.750000
vPos1[10] = <<-2139.515625,2787.671631,30>>
vPos2[10] = <<-1984.539063,2877.048340,250 + iUpperZ>>
fWidth[10] = 69.250000
vPos1[11] = <<-1823.778687,2795.451660,30>>
vPos2[11] = <<-2015.374390,2842.778320,250 + iUpperZ>>
fWidth[11] = 68.000000
vPos1[12] = <<-1752.250122,2806.126953,30>>
vPos2[12] = <<-1760.667969,3169.826172,250 + iUpperZ>>
fWidth[12] = 111.500000
vPos1[13] = <<-1676.144531,2843.249512,30>>
vPos2[13] = <<-1734.778687,3170.531250,250 + iUpperZ>>
fWidth[13] = 89.750000
vPos1[14] = <<-1963.187134,3375.750488,30>>
vPos2[14] = <<-2039.847290,3310.039551,250 + iUpperZ>>
fWidth[14] = 41.500000
IF bEnable
// Set wider area on
SET_ROADS_IN_ANGLED_AREA(<<-1218.494141, 2596.197998, 69.801003>>, <<-3017.008789, 3552.707275, 0.363300>>, 825.0, FALSE, TRUE)
// Set military base off
INT i
REPEAT TOTAL_SUB_AREAS i
SET_ROADS_IN_ANGLED_AREA(vPos1[i], vPos2[i], fWidth[i], FALSE, FALSE)
ENDREPEAT
ELSE
// Revert military base
INT iBackwards = TOTAL_SUB_AREAS
INT i
REPEAT TOTAL_SUB_AREAS i
iBackwards--
SET_ROADS_BACK_TO_ORIGINAL_IN_ANGLED_AREA(vPos1[iBackwards], vPos2[iBackwards], fWidth[iBackwards])
ENDREPEAT
// Revert wider area
SET_ROADS_BACK_TO_ORIGINAL_IN_ANGLED_AREA(<<-1218.494141, 2596.197998, 69.801003>>, <<-3017.008789, 3552.707275, 0.363300>>, 825.0)
ENDIF
ENDPROC
FUNC VEHICLE_NODE_ID Private_GetUnusedRoadNode(VECTOR vSourcePos, VECTOR& vPos, NODE_FLAGS flags)
//-- Get nearest node
INT n = 1
VEHICLE_NODE_ID hNode = GET_NTH_CLOSEST_VEHICLE_NODE_ID(vSourcePos, n, flags)
WHILE IS_VEHICLE_NODE_ID_VALID(hNode)
// If already used for arrival, get next closest
enumCharacterList eChar
REPEAT MAX_BATTLE_BUDDIES eChar
IF hNode <> NULL AND gActivity.mSoldiers[eChar].hArrivalNode = hNode
hNode = NULL
n++
ENDIF
ENDREPEAT
IF hNode = NULL
hNode = GET_NTH_CLOSEST_VEHICLE_NODE_ID(vSourcePos, n, flags)
ELSE
GET_VEHICLE_NODE_POSITION(hNode, vPos)
RETURN hNode
ENDIF
ENDWHILE
RETURN NULL
ENDFUNC
FUNC VEHICLE_NODE_ID Private_GetNearestDestNode(VECTOR vPlayerPos, VECTOR& vNodePos)
CPRINTLN(DEBUG_FRIENDS, "Private_GetNearestDestNode() - Player pos = << ", vPlayerPos.x, ", ", vPlayerPos.y, ", ", vPlayerPos.z, " >>")
// BOOL bNeedNonMilitaryNode = FALSE
// VEHICLE_NODE_ID hNode
//
// //-- Try nearest on-node
// hNode = Private_GetUnusedRoadNode(vPlayerPos, vNodePos, NF_IGNORE_SLIPLANES)
//
// IF IS_VEHICLE_NODE_ID_VALID(hNode) AND GET_DISTANCE_BETWEEN_COORDS(vPlayerPos, vNodePos) < 50.0
// IF IS_COORD_IN_SPECIFIED_AREA(vNodePos, AC_MILITARY_BASE) AND NOT IS_COORD_IN_SPECIFIED_AREA(vPlayerPos, AC_MILITARY_BASE)
// bNeedNonMilitaryNode = TRUE
// ELSE
// CPRINTLN(DEBUG_FRIENDS, "Private_GetNearestDestNode() - Using nearest switched-on-node = << ", vNodePos.x, ", ", vNodePos.y, ", ", vNodePos.z, " >>")
// RETURN hNode
// ENDIF
// ENDIF
//
// // Get nearest any-node
// IF bNeedNonMilitaryNode = FALSE
// hNode = Private_GetUnusedRoadNode(vPlayerPos, vNodePos, NF_IGNORE_SLIPLANES|NF_INCLUDE_SWITCHED_OFF_NODES)
//
// IF IS_VEHICLE_NODE_ID_VALID(hNode)
// IF IS_COORD_IN_SPECIFIED_AREA(vNodePos, AC_MILITARY_BASE) AND NOT IS_COORD_IN_SPECIFIED_AREA(vPlayerPos, AC_MILITARY_BASE)
// bNeedNonMilitaryNode = TRUE
// ELSE
// CPRINTLN(DEBUG_FRIENDS, "Private_GetNearestDestNode() - Using nearest any-state-node = << ", vNodePos.x, ", ", vNodePos.y, ", ", vNodePos.z, " >>")
// RETURN hNode
// ENDIF
// ENDIF
// ENDIF
//
// // Get nearest non-military node
// IF bNeedNonMilitaryNode
// Private_SwitchOnRoadsAroundMilitaryBase(TRUE)
// hNode = Private_GetUnusedRoadNode(vPlayerPos, vNodePos, NF_IGNORE_SLIPLANES)
// Private_SwitchOnRoadsAroundMilitaryBase(FALSE)
//
// IF IS_VEHICLE_NODE_ID_VALID(hNode)
// CPRINTLN(DEBUG_FRIENDS, "Private_GetNearestDestNode() - Using nearest non-military node = << ", vNodePos.x, ", ", vNodePos.y, ", ", vNodePos.z, " >>")
// RETURN hNode
// ENDIF
// ENDIF
//
// // Couldn't find node, use player pos
// CPRINTLN(DEBUG_FRIENDS, "Private_GetNearestDestNode() - couldn't get road node, using player pos")
// vNodePos = vPlayerPos
// RETURN NULL
VEHICLE_NODE_ID hNode
//-- Try nearest on-node
BOOL bForceSwitchedOnNode = Private_CheckBehaviourFlag(BBF_ArriveOnSwitchOnNodesOnly)
hNode = Private_GetUnusedRoadNode(vPlayerPos, vNodePos, NF_IGNORE_SLIPLANES)
IF IS_VEHICLE_NODE_ID_VALID(hNode) AND (GET_DISTANCE_BETWEEN_COORDS(vPlayerPos, vNodePos) < 50.0 OR bForceSwitchedOnNode)
CPRINTLN(DEBUG_FRIENDS, "Private_GetNearestDestNode() - Using nearest switched-on-node = << ", vNodePos.x, ", ", vNodePos.y, ", ", vNodePos.z, " >>")
RETURN hNode
ENDIF
// Get nearest any-node
IF bForceSwitchedOnNode = FALSE
hNode = Private_GetUnusedRoadNode(vPlayerPos, vNodePos, NF_IGNORE_SLIPLANES|NF_INCLUDE_SWITCHED_OFF_NODES)
IF IS_VEHICLE_NODE_ID_VALID(hNode)
CPRINTLN(DEBUG_FRIENDS, "Private_GetNearestDestNode() - Using nearest any-state-node = << ", vNodePos.x, ", ", vNodePos.y, ", ", vNodePos.z, " >>")
RETURN hNode
ENDIF
ENDIF
// Couldn't find node, use player pos
CPRINTLN(DEBUG_FRIENDS, "Private_GetNearestDestNode() - couldn't get road node, using player pos")
vNodePos = vPlayerPos
RETURN NULL
ENDFUNC
//FUNC VEHICLE_NODE_ID Private_GetNearestDestNode(VECTOR vPlayerPos, VECTOR& vParkPos)
//
// // Get nearest node
// VECTOR vNodePos
// VEHICLE_NODE_ID hNode = Private_GetUnusedRoadNode(vPlayerPos, vNodePos, NF_IGNORE_SLIPLANES|NF_INCLUDE_SWITCHED_OFF_NODES)
//
// IF IS_VEHICLE_NODE_ID_VALID(hNode)
//
// // If in military base but player isn't, Force non-military node
// IF IS_COORD_IN_SPECIFIED_AREA(vNodePos, AC_MILITARY_BASE) AND NOT IS_COORD_IN_SPECIFIED_AREA(vPlayerPos, AC_MILITARY_BASE)
// Private_SwitchOnRoadsAroundMilitaryBase(TRUE)
// hNode = Private_GetUnusedRoadNode(vPlayerPos, vNodePos, NF_IGNORE_SLIPLANES)
// Private_SwitchOnRoadsAroundMilitaryBase(FALSE)
//
// IF IS_VEHICLE_NODE_ID_VALID(hNode)
// CPRINTLN(DEBUG_FRIENDS, "Private_GetNearestDestNode() - Using nearest non-military node = << ", vNodePos.x, ", ", vNodePos.y, ", ", vNodePos.z, " >>")
// vParkPos = Private_GetRoadside(vPlayerPos, vNodePos)
// RETURN hNode
// ENDIF
//
// // Otherwise, Try and get switched on node
// ELSE
// VECTOR vOnNodePos
// VEHICLE_NODE_ID hOnNode = Private_GetUnusedRoadNode(vPlayerPos, vOnNodePos, NF_IGNORE_SLIPLANES)
//
// // If on-node usable?
// IF IS_VEHICLE_NODE_ID_VALID(hOnNode) AND GET_DISTANCE_BETWEEN_COORDS(vPlayerPos, vOnNodePos) < 50.0
// CPRINTLN(DEBUG_FRIENDS, "Private_GetNearestDestNode() - Using nearest switched-on-node = << ", vOnNodePos.x, ", ", vOnNodePos.y, ", ", vOnNodePos.z, " >>")
// vParkPos = Private_GetRoadside(vPlayerPos, vOnNodePos)
// RETURN hNode
//
// // Otherwise, use original closest node
// ELSE
// CPRINTLN(DEBUG_FRIENDS, "Private_GetNearestDestNode() - Using nearest any-state-node = << ", vNodePos.x, ", ", vNodePos.y, ", ", vNodePos.z, " >>")
// vParkPos = Private_GetRoadside(vPlayerPos, vNodePos)
// RETURN hNode
// ENDIF
// ENDIF
//
// ENDIF
//
// CPRINTLN(DEBUG_FRIENDS, "Private_GetNearestDestNode() - couldn't get road node, using player pos")
// vParkPos = vPlayerPos
// RETURN NULL
//
//ENDFUNC
FUNC VECTOR Private_GetNodeParkingPos(VECTOR vPlayerPos, VECTOR vNodePos)
BOOL bGotSideA, bGotSideB
VECTOR vSideA, vSideB
bGotSideA = GET_POSITION_BY_SIDE_OF_ROAD(vNodePos, 0, vSideA)
bGotSideB = GET_POSITION_BY_SIDE_OF_ROAD(vNodePos, 1, vSideB)
IF bGotSideA
vSideA += NORMALISE_VECTOR(vNodePos - vSideA) * 3.5
ENDIF
IF bGotSideB
vSideB += NORMALISE_VECTOR(vNodePos - vSideB) * 3.5
ENDIF
IF bGotSideA AND bGotSideB
IF VDIST2(vPlayerPos, vSideA) < VDIST2(vPlayerPos, vSideB)
RETURN vSideA
ELSE
RETURN vSideB
ENDIF
ELIF bGotSideA
RETURN vSideA
ELIF bGotSideB
RETURN vSideB
ELSE
RETURN vNodePos
ENDIF
ENDFUNC
FUNC BOOL Private_CreateSoldierInVehicle(structSoldier& soldier)
// Load required models
PED_VEH_DATA_STRUCT sVehData
GET_PLAYER_VEH_DATA(soldier.eChar, sVehData, soldier.eCreateInVehicleType)
MODEL_NAMES pedModel = GET_PLAYER_PED_MODEL(soldier.eChar)
MODEL_NAMES vehModel = sVehData.model
IF vehModel = DUMMY_MODEL_FOR_SCRIPT
vehModel = ASEA
ENDIF
REQUEST_MODEL(pedModel)
REQUEST_MODEL(vehModel)
IF HAS_MODEL_LOADED(vehModel)
AND HAS_MODEL_LOADED(pedModel)
// Find car spawn pos
VECTOR vPlayerPos = GET_ENTITY_COORDS(PLAYER_PED_ID())
BOOL bSpawnOnSwitchedOffNodes = Private_CheckBehaviourFlag(BBF_SpawnOnSwitchedOffNodes)
IF bSpawnOnSwitchedOffNodes
CPRINTLN(DEBUG_FRIENDS, "Private_CreateSoldierInVehicle(", GetLabel_enumCharacterList(soldier.eChar), ") - Attempting to spawn soldier in vehicle, allow switched off nodes")
ELSE
CPRINTLN(DEBUG_FRIENDS, "Private_CreateSoldierInVehicle(", GetLabel_enumCharacterList(soldier.eChar), ") - Attempting to spawn soldier in vehicle, disallow switched off nodes")
ENDIF
VECTOR vResultPos
VECTOR vDummyLinkDir
IF GENERATE_VEHICLE_CREATION_POS_FROM_PATHS(vPlayerPos, vResultPos, vDummyLinkDir, 0, 180, CONST_fBattleBuddyMinCreateDist, bSpawnOnSwitchedOffNodes, TRUE, TRUE)
// Result should be in/out of military base if player is
IF Private_CheckBehaviourFlag(BBF_SpawnCheckMilitaryBase)
// If it is on wrong side, search again from the arrival-dest node pos
BOOL bIsPlayerInBase = IS_COORD_IN_SPECIFIED_AREA(vPlayerPos, AC_MILITARY_BASE)
IF bIsPlayerInBase <> IS_COORD_IN_SPECIFIED_AREA(vResultPos, AC_MILITARY_BASE)
IF NOT GENERATE_VEHICLE_CREATION_POS_FROM_PATHS(soldier.vArrivalPos, vResultPos, vDummyLinkDir, 0, 180, CONST_fBattleBuddyMinCreateDist, bSpawnOnSwitchedOffNodes, TRUE, TRUE)
CPRINTLN(DEBUG_FRIENDS, "Private_CreateSoldierInVehicle(", GetLabel_enumCharacterList(soldier.eChar), ") - Rejecting emergency position, couldn't find one")
RETURN FALSE
ELIF VDIST2(vPlayerPos, vResultPos) < CONST_fBattleBuddyMinCreateDist*CONST_fBattleBuddyMinCreateDist
CPRINTLN(DEBUG_FRIENDS, "Private_CreateSoldierInVehicle(", GetLabel_enumCharacterList(soldier.eChar), ") - Rejecting emergency position, too close to player")
RETURN FALSE
ELIF bIsPlayerInBase <> IS_COORD_IN_SPECIFIED_AREA(vResultPos, AC_MILITARY_BASE)
CPRINTLN(DEBUG_FRIENDS, "Private_CreateSoldierInVehicle(", GetLabel_enumCharacterList(soldier.eChar), ") - Rejecting emergency position, doesn't match player's in-military-base status either")
RETURN FALSE
ENDIF
ENDIF
ENDIF
// Result shouldn't be at sea end of docks
IF Private_CheckBehaviourFlag(BBF_SpawnCheckDocks)
IF soldier.iCreateAttempts < 60
IF Private_IsPointInArea2D(vResultPos, <</*1074.0*//*981.58*/890.0, -3365.0, 0.0>>, <<1312.0, -2888.0, 0.0>>)
CPRINTLN(DEBUG_FRIENDS, "Private_CreateSoldierInVehicle(", GetLabel_enumCharacterList(soldier.eChar), ") - Rejecting position, too close to sea end of docks")
RETURN FALSE
ENDIF
ENDIF
ENDIF
// Result should be occluded
IF soldier.iCreateAttempts < 30
IF NOT WOULD_ENTITY_BE_OCCLUDED(vehModel, vResultPos)
CPRINTLN(DEBUG_FRIENDS, "Private_CreateSoldierInVehicle(", GetLabel_enumCharacterList(soldier.eChar), ") - Rejecting position, wouldn't be occluded")
RETURN FALSE
ENDIF
ENDIF
// Result should not intersect with another car
CLEAR_AREA_OF_VEHICLES(vResultPos, 10.0)
VECTOR vAreaMin = vResultPos - <<3,3,3>>
VECTOR vAreaMax = vResultPos + <<3,3,3>>
IF IS_AREA_OCCUPIED(vAreaMin, vAreaMax, FALSE, TRUE, FALSE, FALSE, FALSE)
CPRINTLN(DEBUG_FRIENDS, "Private_CreateSoldierInVehicle(", GetLabel_enumCharacterList(soldier.eChar), ") - Rejecting position, another car is in the area")
RETURN FALSE
ENDIF
// Accept position
CPRINTLN(DEBUG_FRIENDS, "Private_CreateSoldierInVehicle(", GetLabel_enumCharacterList(soldier.eChar), ") - Accepting position <<",vResultPos.x,", ",vResultPos.y,", ",vResultPos.z, ">> on ", soldier.iCreateAttempts, " attempts")
// Get the node heading
VECTOR vSpawnPos
FLOAT fSpawnHeading
INT iDummyLanes
GET_NTH_CLOSEST_VEHICLE_NODE_WITH_HEADING(vResultPos, 1, vSpawnPos, fSpawnHeading, iDummyLanes)
vSpawnPos = vResultPos
// Create car and ped
IF NOT DOES_ENTITY_EXIST(soldier.hVehicle)
IF CREATE_PLAYER_VEHICLE(soldier.hVehicle, soldier.eChar, vSpawnPos, fSpawnHeading, TRUE, soldier.eCreateInVehicleType)
IF DOT_PRODUCT_XY(GET_ENTITY_FORWARD_VECTOR(soldier.hVehicle), vSpawnPos-vPlayerPos) > 0.0
SET_ENTITY_HEADING(soldier.hVehicle, fSpawnHeading+180.0)
ENDIF
SET_VEHICLE_HAS_STRONG_AXLES(soldier.hVehicle, TRUE)
// SET_ENTITY_SHOULD_FREEZE_WAITING_ON_COLLISION(soldier.hVehicle, TRUE) // Can't use this, doesn't work if player's in an interior and driving under a freeway counts as an interior in some places.
SET_ENTITY_LOAD_COLLISION_FLAG(soldier.hVehicle, TRUE)
ENDIF
ENDIF
IF DOES_ENTITY_EXIST(soldier.hVehicle)
IF CREATE_PLAYER_PED_INSIDE_VEHICLE(soldier.hPed, soldier.eChar, soldier.hVehicle, VS_DRIVER, TRUE)
IF NOT HAS_PED_GOT_WEAPON(soldier.hPed, WEAPONTYPE_PISTOL)
GIVE_WEAPON_TO_PED(soldier.hPed, WEAPONTYPE_PISTOL, GET_WEAPON_CLIP_SIZE(WEAPONTYPE_PISTOL)*GET_RANDOM_INT_IN_RANGE(6, 10))
STORE_PLAYER_PED_WEAPONS(soldier.hPed)
ENDIF
soldier.bRequestGreetingDialogue = TRUE
RETURN TRUE
ENDIF
ENDIF
ELSE
CPRINTLN(DEBUG_FRIENDS, "Private_CreateSoldierInVehicle(", GetLabel_enumCharacterList(soldier.eChar), ") - GENERATE_VEHICLE_CREATION_POS_FROM_PATHS returned false")
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_CreateSoldierOnFoot(structSoldier& soldier)
// Load required models
MODEL_NAMES pedModel = GET_PLAYER_PED_MODEL(soldier.eChar)
REQUEST_MODEL(pedModel)
IF HAS_MODEL_LOADED(pedModel)
// Find ped spawn pos
VECTOR vPlayerPos = GET_ENTITY_COORDS(PLAYER_PED_ID())
VECTOR vSafePos
IF GET_SAFE_COORD_FOR_PED(vPlayerPos, FALSE, vSafePos)
IF VDIST(vPlayerPos, vSafePos) < CONST_fBattleBuddyMaxOnFootCreateDist
// Create car and ped
IF CREATE_PLAYER_PED_ON_FOOT(soldier.hPed, soldier.eChar, vSafePos, 0)
IF NOT HAS_PED_GOT_WEAPON(soldier.hPed, WEAPONTYPE_PISTOL)
GIVE_WEAPON_TO_PED(soldier.hPed, WEAPONTYPE_PISTOL, GET_WEAPON_CLIP_SIZE(WEAPONTYPE_PISTOL)*GET_RANDOM_INT_IN_RANGE(6, 10))
STORE_PLAYER_PED_WEAPONS(soldier.hPed)
ENDIF
soldier.bRequestGreetingDialogue = FALSE
soldier.hVehicle = NULL
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
PROC Private_SetSoldierState(structSoldier& soldier, enumSoldierState eState)
#IF IS_DEBUG_BUILD
TEXT_LABEL_63 tChar = GetLabel_enumCharacterList(soldier.eChar)
TEXT_LABEL_63 tState = GetLabel_enumSoldierState(eState)
CPRINTLN(DEBUG_FRIENDS, "BATTLEBUDDY - Private_SetSoldierState(", tChar, ", ", tState, ")")
#ENDIF
soldier.eState = eState
soldier.bInitState = TRUE
ENDPROC
PROC Private_SetSoldierInitialWeapons(structSoldier& soldier)
WEAPON_TYPE eBestWeapon = GET_BEST_PED_WEAPON(soldier.hPed)
#IF IS_DEBUG_BUILD
TEXT_LABEL tChar = GetLabel_enumCharacterList(soldier.eChar)
TEXT_LABEL tWeapon = GET_WEAPON_NAME(eBestWeapon)
CPRINTLN(DEBUG_FRIENDS, "BATTLEBUDDY - Setting ", tChar, " to best weapon: ", tWeapon)
#ENDIF
SET_PED_INFINITE_AMMO(soldier.hPed, TRUE)
SET_CURRENT_PED_WEAPON(soldier.hPed, eBestWeapon, TRUE)
ENDPROC
PROC Private_SetSoldierAttribs(structSoldier& soldier)
IF IS_PED_UNINJURED(soldier.hPed)
SET_GROUP_SEPARATION_RANGE(PLAYER_GROUP_ID(), CONST_fBattleBuddyLostDist)
// SET_GROUP_FORMATION(PLAYER_GROUP_ID(), FORMATION_LINE_ABREAST) //FORMATION_LOOSE)
IF Private_CheckBehaviourFlag(BBF_CombatGroupSpacing)
SET_GROUP_FORMATION(PLAYER_GROUP_ID(), FORMATION_SURROUND_FACING_INWARDS)
SET_GROUP_FORMATION_SPACING(PLAYER_GROUP_ID(), 4.0) // NOTE: This is set when group state starts too
ELIF Private_CheckBehaviourFlag(BBF_CloseGroupSpacing)
SET_GROUP_FORMATION(PLAYER_GROUP_ID(), FORMATION_LOOSE)
SET_GROUP_FORMATION_SPACING(PLAYER_GROUP_ID(), 2.0)
ELSE
SET_GROUP_FORMATION(PLAYER_GROUP_ID(), FORMATION_LOOSE)
// SET_GROUP_FORMATION_SPACING(PLAYER_GROUP_ID(), 3.0)
ENDIF
SET_PED_RELATIONSHIP_GROUP_HASH(soldier.hPed, RELGROUPHASH_PLAYER)
SET_PED_PATH_CAN_USE_CLIMBOVERS(soldier.hPed, TRUE)
SET_PED_PATH_CAN_USE_LADDERS(soldier.hPed, TRUE)
SET_PED_PATH_CAN_DROP_FROM_HEIGHT(soldier.hPed, TRUE)
SET_PED_PATH_MAY_ENTER_WATER(soldier.hPed, TRUE)
SET_PED_PATH_PREFER_TO_AVOID_WATER(soldier.hPed, TRUE)
SET_PED_DIES_INSTANTLY_IN_WATER(soldier.hPed, FALSE)
SET_PED_DIES_IN_WATER(soldier.hPed, FALSE)
IF Private_CheckBehaviourFlag(BBF_CombatDefensive)
SET_PED_COMBAT_MOVEMENT(soldier.hPed, CM_DEFENSIVE)
ELSE
SET_PED_COMBAT_MOVEMENT(soldier.hPed, CM_WILLADVANCE)
ENDIF
SET_PED_COMBAT_ATTRIBUTES(soldier.hPed, CA_AGGRESSIVE, TRUE)
SET_PED_COMBAT_ATTRIBUTES(soldier.hPed, CA_ALWAYS_FIGHT, TRUE)
SET_PED_COMBAT_ATTRIBUTES(soldier.hPed, CA_REQUIRES_LOS_TO_SHOOT, TRUE)
SET_PED_COMBAT_ATTRIBUTES(soldier.hPed, CA_CAN_SHOOT_WITHOUT_LOS, FALSE)
SET_PED_COMBAT_ATTRIBUTES(soldier.hPed, CA_CAN_FIGHT_ARMED_PEDS_WHEN_NOT_ARMED, TRUE)
SET_PED_COMBAT_ATTRIBUTES(soldier.hPed, CA_CAN_USE_DYNAMIC_STRAFE_DECISIONS, TRUE)
SET_PED_COMBAT_ATTRIBUTES(soldier.hPed, CA_DO_DRIVEBYS, TRUE)
SET_PED_COMBAT_ATTRIBUTES(soldier.hPed, CA_USE_COVER, TRUE)
SET_PED_COMBAT_ATTRIBUTES(soldier.hPed, CA_USE_VEHICLE, TRUE)
SET_PED_COMBAT_ATTRIBUTES(soldier.hPed, CA_LEAVE_VEHICLES, FALSE)
SET_PED_COMBAT_ABILITY(soldier.hPed, CAL_PROFESSIONAL)
SET_PED_FLEE_ATTRIBUTES(soldier.hPed, FA_NEVER_FLEE, TRUE)
SET_PED_SUFFERS_CRITICAL_HITS(soldier.hPed, FALSE)
SET_PED_CAN_BE_KNOCKED_OFF_VEHICLE(soldier.hPed, KNOCKOFFVEHICLE_NEVER)
SET_PED_CONFIG_FLAG(soldier.hPed, PCF_DontActivateRagdollFromBulletImpact, TRUE)
// SET_PED_CONFIG_FLAG(soldier.hPed, PCF_OnlyAttackLawIfPlayerIsWanted, TRUE)
SET_PED_CONFIG_FLAG(soldier.hPed, PCF_LawWillOnlyAttackIfPlayerIsWanted, TRUE)
SET_PED_CONFIG_FLAG(soldier.hPed, PCF_ForcedToUseSpecificGroupSeatIndex, Private_CheckBehaviourFlag(BBF_ForceFrontPassengerSeat))
// SET_PED_CONFIG_FLAG(soldier.hPed, PCF_AICanDrivePlayerAsRearPassenger, TRUE)
SET_PED_GROUP_MEMBER_PASSENGER_INDEX(soldier.hPed, VS_FRONT_RIGHT)
STOP_PED_SPEAKING(soldier.hPed, TRUE)
SET_ENTITY_LOAD_COLLISION_FLAG(soldier.hPed, TRUE)
ENDIF
ENDPROC
PROC Private_ClearSoldierAttribs(structSoldier& soldier)
IF IS_PED_UNINJURED(soldier.hPed)
// Set in attributes functions
// TODO: Might need to reset other attribs
SET_ENTITY_LOAD_COLLISION_FLAG(soldier.hPed, FALSE)
STOP_PED_SPEAKING(soldier.hPed, FALSE)
SET_PED_COMBAT_MOVEMENT(soldier.hPed, CM_WILLADVANCE)
SET_PED_SUFFERS_CRITICAL_HITS(soldier.hPed, TRUE)
SET_PED_CAN_BE_KNOCKED_OFF_VEHICLE(soldier.hPed, KNOCKOFFVEHICLE_DEFAULT)
SET_PED_CONFIG_FLAG(soldier.hPed, PCF_DontActivateRagdollFromBulletImpact, FALSE)
// SET_PED_CONFIG_FLAG(soldier.hPed, PCF_AICanDrivePlayerAsRearPassenger, FALSE)
SET_PED_CONFIG_FLAG(soldier.hPed, PCF_ForcedToUseSpecificGroupSeatIndex, FALSE)
// Not set in attributes function, but can be set elsewhere
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(soldier.hPed, FALSE)
SET_PED_INFINITE_AMMO(soldier.hPed, FALSE)
IF GET_PED_STEALTH_MOVEMENT(soldier.hPed)
SET_PED_STEALTH_MOVEMENT(soldier.hPed, FALSE)
ENDIF
ENDIF
ENDPROC
PROC Private_SetSoldierExtraHealth(structSoldier& soldier)
IF IS_PED_UNINJURED(soldier.hPed)
IF GET_ENTITY_MAX_HEALTH(soldier.hPed) < FLOOR(CONST_fSoldierExtraMaxHealth)
FLOAT fNewHealth = TO_FLOAT(GET_ENTITY_HEALTH(soldier.hPed)) * (CONST_fSoldierExtraMaxHealth / CONST_fSoldierStandardMaxHealth)
SET_ENTITY_MAX_HEALTH(soldier.hPed, FLOOR(CONST_fSoldierExtraMaxHealth))
SET_ENTITY_HEALTH(soldier.hPed, FLOOR(fNewHealth))
ENDIF
ENDIF
ENDPROC
PROC Private_ClearSoldierExtraHealth(structSoldier& soldier)
IF IS_PED_UNINJURED(soldier.hPed)
IF GET_ENTITY_MAX_HEALTH(soldier.hPed) > FLOOR(CONST_fSoldierStandardMaxHealth)
FLOAT fNewHealth = TO_FLOAT(GET_ENTITY_HEALTH(soldier.hPed)) * (CONST_fSoldierStandardMaxHealth / CONST_fSoldierExtraMaxHealth)
SET_ENTITY_MAX_HEALTH(soldier.hPed, FLOOR(CONST_fSoldierStandardMaxHealth))
SET_ENTITY_HEALTH(soldier.hPed, FLOOR(fNewHealth))
ENDIF
ENDIF
ENDPROC
PROC Private_CleanupSoldier(structSoldier& soldier, enumMemberCleanupStyle eStyle = MC_Release)
IF soldier.eState <> SOLDIER_NULL
#IF IS_DEBUG_BUILD
IF eStyle = MC_Delete
CPRINTLN(DEBUG_FRIENDS, "BATTLEBUDDY - Private_CleanupSoldier(", GetLabel_enumCharacterList(soldier.eChar), ", MC_Delete)")
ELIF eStyle = MC_Release
CPRINTLN(DEBUG_FRIENDS, "BATTLEBUDDY - Private_CleanupSoldier(", GetLabel_enumCharacterList(soldier.eChar), ", MC_Release)")
ELIF eStyle = MC_LeavePedIntact
CPRINTLN(DEBUG_FRIENDS, "BATTLEBUDDY - Private_CleanupSoldier(", GetLabel_enumCharacterList(soldier.eChar), ", MC_LeavePedIntact)")
ELSE
CPRINTLN(DEBUG_FRIENDS, "BATTLEBUDDY - Private_CleanupSoldier(", GetLabel_enumCharacterList(soldier.eChar), ", <unknown cleanup style>)")
ENDIF
#ENDIF
// Remove blips
SAFE_REMOVE_BLIP(soldier.hBlip)
// Clear extra health
Private_ClearSoldierExtraHealth(soldier)
IF DOES_ENTITY_EXIST(soldier.hPed)
// Take ped back if overridden
IF soldier.eState = SOLDIER_OVERRIDDEN AND eStyle <> MC_LeavePedIntact
IF NOT IS_ENTITY_DEAD(soldier.hPed) // (Really shouldn't have to check this)
IF NOT DOES_ENTITY_BELONG_TO_THIS_SCRIPT(soldier.hPed)
SET_ENTITY_AS_MISSION_ENTITY(soldier.hPed, TRUE, TRUE)
ENDIF
ENDIF
ENDIF
// Reset attributes
Private_ClearSoldierAttribs(soldier)
// Cleanup ped
IF soldier.hPed <> PLAYER_PED_ID()
#IF IS_DEBUG_BUILD
IF soldier.hPed <> GET_BATTLEBUDDY_PED(soldier.eChar)
SCRIPT_ASSERT("Private_CleanupSoldier() - Ped out of sync with global selector")
ENDIF
#ENDIF
IF IS_PED_IN_GROUP(soldier.hPed)
REMOVE_PED_FROM_GROUP(soldier.hPed)
ENDIF
IF eStyle = MC_Delete
DELETE_PED(soldier.hPed)
ELIF eStyle = MC_Release
SET_PED_AS_NO_LONGER_NEEDED(soldier.hPed)
ELSE
// For transfer, do nothing
ENDIF
ENDIF
ENDIF
// Add communication delay
// IF Private_IsSoldierGlobalFlagSet(soldier, g_bitfieldBattleBuddyAvailable)
// ADD_COMMUNICATION_DELAY_FOR_CHARACTER(soldier.eChar)
// ENDIF
// // Unblock family scenes
// Private_EnableFamilyScenes(friend.eChar, TRUE) // TODO: Add these in
//
// // Unblock scenarios
// Private_ClearFriendScenarioBlocking(friend)
// Reset flags
Private_ClearSoldierAvailable(soldier)
Private_ClearSoldierGlobalFlag(soldier, g_bitfieldBattleBuddyOverridden)
// Deactive char connections
DEACTIVATE_CHAR_CONNECTIONS(soldier.eChar)
// Reset structure
Private_InitSoldier(soldier, soldier.eChar)
ENDIF
ENDPROC
//-------------------------------------------------------------------------------------------------------------------------------------------
// UTILS
//-------------------------------------------------------------------------------------------------------------------------------------------
FUNC BOOL Private_IsSoldierOutsidePoliceGate(structSoldier& soldier)
VECTOR vGate = <<400.6273, -1610.1445, 28.2928>>
IF NOT IS_PED_INJURED(soldier.hPed)
IF IS_ENTITY_AT_COORD(soldier.hPed, vGate, <<7.0, 7.0, 7.0>>)
VECTOR vToSoldier = GET_ENTITY_COORDS(soldier.hPed) - vGate
VECTOR vGateNormal = GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS(<<0,0,0>>, 324.8394, <<0,1,0>>)
IF DOT_PRODUCT_XY(vToSoldier, vGateNormal) > 0.0
RETURN TRUE
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_IsSoldierOutOfRange(structSoldier& soldier)
// Can only go out of range once been available
IF IS_BIT_SET(g_bitfieldBattleBuddyAvailable, ENUM_TO_INT(soldier.eChar))
// If parachuting, don't test Z difference
IF (soldier.eState = SOLDIER_PARACHUTE) OR (GET_PED_PARACHUTE_STATE(PLAYER_PED_ID()) <> PPS_INVALID)
RETURN Util_IsPedOutsideRangePed(PLAYER_PED_ID(), soldier.hPed, CONST_fBattleBuddyAvailableFailDist)
ELSE
RETURN GET_DISTANCE_BETWEEN_ENTITIES(soldier.hPed, PLAYER_PED_ID()) > CONST_fBattleBuddyAvailableFailDist
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_IsPlayerArrested()
IF IS_PLAYER_BEING_ARRESTED(PLAYER_ID())
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_LoadSoldierArrestAnims()
REQUEST_ANIM_DICT("random@arrests")
RETURN HAS_ANIM_DICT_LOADED("random@arrests")
ENDFUNC
FUNC BOOL Private_ShouldSoldierFollow(structSoldier& soldier)
// Is player in car that ped can't fit in?
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
AND NOT IS_PED_IN_VEHICLE(soldier.hPed, GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID()))
AND NOT DOES_VEHICLE_HAVE_FREE_SEAT(GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID()))
RETURN TRUE
ENDIF
// If player+soldier each in own cars, don't get out
IF Private_CheckBehaviourFlag(BBF_DriveInConvoy)
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
AND IS_PED_IN_ANY_VEHICLE(soldier.hPed)
IF NOT IS_PED_IN_VEHICLE(soldier.hPed, GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID()))
RETURN TRUE
ENDIF
ENDIF
ENDIF
// // Is ped being driven by a mission entity
// IF IS_PED_SITTING_IN_ANY_VEHICLE(soldier.hPed)
// PED_INDEX hDriver = GET_PED_IN_VEHICLE_SEAT(GET_VEHICLE_PED_IS_IN(soldier.hPed))
//
// IF IS_PED_UNINJURED(hDriver)
// AND hDriver <> PLAYER_PED_ID()
// AND hDriver <> soldier.hPed
// AND DOES_ENTITY_BELONG_TO_THIS_SCRIPT(soldier.hPed)
// RETURN TRUE
// ENDIF
// ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_CanSoldierJoinGroup(structSoldier& soldier, BOOL bAlreadyInGroup = FALSE)
FLOAT fDist = 25.0
IF bAlreadyInGroup
fDist = 100.0
IF NOT IS_PED_GROUP_MEMBER(soldier.hPed, PLAYER_GROUP_ID())
RETURN FALSE
ENDIF
ENDIF
IF IS_ENTITY_AT_ENTITY(PLAYER_PED_ID(), soldier.hPed, <<fDist, fDist, 3.0>>, FALSE)//IS_BUDDY_GOOD_TO_JOIN_PLAYERS_GROUP(gActivity.locateData, soldier.hPed, fDist)//
IF NOT Private_ShouldSoldierFollow(soldier)
IF NOT IS_PED_GROUP_MEMBER(soldier.hPed, PLAYER_GROUP_ID())
SET_PED_AS_GROUP_MEMBER(soldier.hPed, PLAYER_GROUP_ID())
ENDIF
IF IS_PED_GROUP_MEMBER(soldier.hPed, PLAYER_GROUP_ID())
RETURN TRUE
ENDIF
ENDIF
ENDIF
IF IS_PED_IN_GROUP(soldier.hPed)
REMOVE_PED_FROM_GROUP(soldier.hPed)
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_AreAnyHatedPedsNearby(structSoldier& soldier, FLOAT fRadius = 100.0)
// Is soldier near to any peds that are in combat with player
// IF (COUNT_PEDS_IN_COMBAT_WITH_TARGET(PLAYER_PED_ID()) > 0)
// OR (COUNT_PEDS_IN_COMBAT_WITH_TARGET(soldier.hPed) > 0)
PED_INDEX nearbyPeds[25]
INT iNearCount = GET_PED_NEARBY_PEDS(soldier.hPed, nearbyPeds)
INT i
REPEAT iNearCount i
IF IS_PED_UNINJURED(nearbyPeds[i])
REL_GROUP_HASH hPedRelGroup = GET_PED_RELATIONSHIP_GROUP_HASH(nearbyPeds[i])
IF hPedRelGroup <> RELGROUPHASH_NO_RELATIONSHIP
IF GET_RELATIONSHIP_BETWEEN_GROUPS(RELGROUPHASH_PLAYER, hPedRelGroup) = ACQUAINTANCE_TYPE_PED_HATE
IF IS_ENTITY_AT_ENTITY(nearbyPeds[i], soldier.hPed, <<fRadius, fRadius, fRadius>>)
// IF CAN_PED_SEE_HATED_PED(soldier.hPed, nearbyPeds[i])
RETURN TRUE
// ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDREPEAT
// ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_CanSoldierEnterCombat(structSoldier& soldier)
IF IS_PED_ON_FOOT(PLAYER_PED_ID())
IF Private_CheckBehaviourFlag(BBF_CombatDefendCargobobArea)
RETURN TRUE
ENDIF
ENDIF
// Is player onfoot + nearby, and are hated peds are near?
IF (IS_PED_ON_FOOT(PLAYER_PED_ID()) OR (Private_CheckBehaviourFlag(BBF_DriveInConvoy) AND (soldier.eState = SOLDIER_FOLLOW OR soldier.eState = SOLDIER_COMBAT)))
IF IS_ENTITY_AT_ENTITY(PLAYER_PED_ID(), soldier.hPed, <<100.0, 100.0, 50.0>>)
// OR Private_CheckBehaviourFlag(BBF_CombatIgnorePlayerDist)
IF Private_AreAnyHatedPedsNearby(soldier, 100.0)
RETURN TRUE
ENDIF
ENDIF
ENDIF
// Couldn't join group
RETURN FALSE
ENDFUNC
FUNC BOOL Private_CanSoldierEnterParachute(structSoldier& soldier)
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(soldier.hPed)
IF NOT IS_PED_IN_ANY_VEHICLE(soldier.hPed)
IF GET_ENTITY_HEIGHT_ABOVE_GROUND(soldier.hPed) > 50.0
IF HAS_PED_GOT_WEAPON(soldier.hPed, GADGETTYPE_PARACHUTE)
RETURN TRUE
ELSE
CPRINTLN(DEBUG_FRIENDS, "Player parachuting and falling high in air, but ", GetLabel_enumCharacterList(soldier.eChar), " doesn't have parachute")
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_CanSoldierExitParachute(structSoldier& soldier)
IF NOT IS_ENTITY_IN_AIR(soldier.hPed)
IF GET_PED_PARACHUTE_STATE(soldier.hPed) <> PPS_LANDING
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_ShouldSoldierBePassenger(structSoldier& soldier)
IF IS_PED_IN_ANY_VEHICLE(soldier.hPed)
VEHICLE_INDEX hVehicle = GET_VEHICLE_PED_IS_IN(soldier.hPed)
IF IS_VEHICLE_DRIVEABLE(hVehicle)
PED_INDEX hDriverPed = GET_PED_IN_VEHICLE_SEAT(hVehicle, VS_DRIVER)
IF IS_PED_UNINJURED(hDriverPed)
enumCharacterList ePlayerChar = GET_CURRENT_PLAYER_PED_ENUM()
// Is another battle buddy (not player) driving the car I'm in?
enumCharacterList eChar
REPEAT MAX_BATTLE_BUDDIES eChar
IF eChar <> ePlayerChar
AND eChar <> soldier.eChar
IF gActivity.mSoldiers[eChar].hPed = hDriverPed
RETURN TRUE
ENDIF
ENDIF
ENDREPEAT
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_RegisterSoldierForSwapping(structSoldier& soldier, SELECTOR_PED_STRUCT& selector)
SELECTOR_SLOTS_ENUM eSlot = GET_SELECTOR_SLOT_FROM_PLAYER_PED_ENUM(soldier.eChar)
// If valid + within 300m fail range, allow switch
IF soldier.eState <> SOLDIER_NULL
AND NOT IS_PED_INJURED(soldier.hPed)
AND NOT IS_PED_INJURED(PLAYER_PED_ID())
AND NOT (soldier.hPed = PLAYER_PED_ID())
AND NOT Private_IsSoldierOutOfRange(soldier)
AND Private_IsSoldierGlobalFlagSet(soldier, g_bitfieldBattleBuddyAvailable)
// Allow switch
selector.pedID[eSlot] = soldier.hPed
IF selector.bBlockSelectorPed[eSlot] = TRUE
SET_SELECTOR_PED_BLOCKED(selector, eSlot, FALSE)
ENDIF
RETURN TRUE
ELSE
// Block switch
selector.pedID[eSlot] = NULL
IF selector.bBlockSelectorPed[eSlot] = FALSE
SET_SELECTOR_PED_BLOCKED(selector, eSlot, TRUE)
ENDIF
RETURN FALSE
ENDIF
ENDFUNC
FUNC BOOL Private_HasSoldierBeenAvailable(structSoldier& soldier, BOOL bMustBeAlive)
IF Private_IsSoldierGlobalFlagSet(soldier, g_bitfieldBattleBuddyAvailable)
IF bMustBeAlive = FALSE OR IS_PED_UNINJURED(soldier.hPed)
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_IsSoldierValid(structSoldier& soldier)
IF soldier.eState <> SOLDIER_NULL AND soldier.eState < SOLDIER_FAIL_INJURED
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_IsSoldierOutside(structSoldier& soldier)
IF NOT IS_PED_INJURED(soldier.hPed)
IF GET_INTERIOR_FROM_ENTITY(soldier.hPed) = NULL
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_CanSoldierConverse(structSoldier& soldier)
IF IS_PED_UNINJURED(PLAYER_PED_ID()) AND IS_PED_UNINJURED(soldier.hPed)
IF soldier.eChar = GET_CURRENT_PLAYER_PED_ENUM()
RETURN TRUE
ENDIF
IF soldier.eState = SOLDIER_APPROACH
OR soldier.eState = SOLDIER_GROUP
OR soldier.eState = SOLDIER_COMBAT
OR soldier.eState = SOLDIER_OVERRIDDEN
IF IS_ENTITY_AT_ENTITY(PLAYER_PED_ID(), soldier.hPed, <<CONST_fFriendConverseRadius, CONST_fFriendConverseRadius, CONST_fFriendConverseRadius>>)
RETURN TRUE
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_CanRejectSoldierForTime(structSoldier& soldier)
IF TIMER_DO_WHEN_READY(soldier.mAvailableTimer, CONST_fSoldierRejectTime)
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL Private_SetSoldierRejected(structSoldier& soldier, SP_MISSIONS eMission, BOOL bAllowSubtitles, enumFriendActivityPhrase ePhrase = FAP_REJECTION_OK)
IF soldier.eState <> SOLDIER_NULL AND soldier.eState < SOLDIER_FAIL_INJURED
#IF IS_DEBUG_BUILD
TEXT_LABEL_63 tChar = GetLabel_enumCharacterList(soldier.eChar)
TEXT_LABEL_63 tMission = GetLabel_SP_MISSIONS(eMission)
TEXT_LABEL_63 tSubtitles = "bAllowSubtitles"
TEXT_LABEL_63 tPhrase = GetLabel_enumFriendActivityPhrase(ePhrase)
IF bAllowSubtitles tSubtitles += "=TRUE"
ELSE tSubtitles += "=FALSE"
ENDIF
CPRINTLN(DEBUG_FRIENDS, "Private_SetSoldierRejected(", tChar, ", ", tMission, ", ", tSubtitles, ", ", tPhrase, ")")
#ENDIF
enumCharacterList ePlayerChar = GET_CURRENT_PLAYER_PED_ENUM()
IF IS_PED_UNINJURED(soldier.hPed)
AND soldier.eChar <> ePlayerChar
// Play audio comment
IF Private_CanSoldierConverse(soldier)
IF (ePhrase <> NO_FRIEND_ACTIVITY_PHRASE)
TEXT_LABEL tRoot
IF bAllowSubtitles = FALSE
IF PRIVATE_FriendDialogue_GetRawAudio(soldier.eChar, ePhrase, tRoot)
PLAY_PED_AMBIENT_SPEECH_WITH_VOICE(soldier.hPed, tRoot, PRIVATE_Get_SpeakerLabel_From_Char(soldier.eChar), SPEECH_PARAMS_FORCE_FRONTEND)
ENDIF
ELSE
IF Private_GetDialogueState(gActivity.mDialogue) <> FDIALOGUE_REJECTED
Private_SetDialogueState(gActivity.mDialogue, FDIALOGUE_REJECTED, soldier.eChar, ePhrase)
ENDIF
ENDIF
ENDIF
ENDIF
// Get mission position
BOOL bHasDestPos = FALSE
VECTOR vRejectionDest
IF eMission <> SP_MISSION_NONE
STATIC_BLIP_NAME_ENUM eMissionBlip
VECTOR vMissionPos
eMissionBlip = g_sMissionStaticData[eMission].blip
IF IS_STATIC_BLIP_MULTIMODE(eMissionBlip)
vMissionPos = GET_STATIC_BLIP_POSITION(eMissionBlip, GET_CURRENT_PLAYER_PED_INT())
ELSE
vMissionPos = GET_STATIC_BLIP_POSITION(eMissionBlip)
ENDIF
// Get rejection dest
FLOAT fHeadingDummy
INT iLanesDummy
vRejectionDest = vMissionPos + (NORMALISE_VECTOR(GET_ENTITY_COORDS(soldier.hPed) - vMissionPos) * 50.0)
IF GET_SAFE_VEHICLE_NODE(vRejectionDest, vRejectionDest, fHeadingDummy, iLanesDummy, 1, FALSE)
bHasDestPos = TRUE
ENDIF
ENDIF
// Remove from group
IF IS_PED_GROUP_MEMBER(soldier.hPed, PLAYER_GROUP_ID())
REMOVE_PED_FROM_GROUP(soldier.hPed)
ENDIF
// Don't allow ambient launch
DECOR_SET_BOOL(soldier.hPed, "BlockFriendGrab", TRUE)
SET_PED_KEEP_TASK(soldier.hPed, TRUE)
// Walk away from the mission
SEQUENCE_INDEX seq
OPEN_SEQUENCE_TASK(seq)
IF IS_PED_IN_ANY_VEHICLE(soldier.hPed, TRUE)
TASK_LEAVE_ANY_VEHICLE(null)
ENDIF
TASK_LOOK_AT_ENTITY(null, PLAYER_PED_ID(), 2000, SLF_WHILE_NOT_IN_FOV|SLF_USE_TORSO)
TASK_PAUSE(null, GET_RANDOM_INT_IN_RANGE(800, 1200))
IF bHasDestPos
TASK_FOLLOW_NAV_MESH_TO_COORD(null, vRejectionDest, PEDMOVE_WALK, DEFAULT_TIME_NEVER_WARP, DEFAULT_NAVMESH_RADIUS, ENAV_GO_FAR_AS_POSSIBLE_IF_TARGET_NAVMESH_NOT_LOADED|ENAV_NO_STOPPING)
ENDIF
TASK_WANDER_STANDARD(null)
CLOSE_SEQUENCE_TASK(seq)
TASK_PERFORM_SEQUENCE(soldier.hPed, seq)
CLEAR_SEQUENCE_TASK(seq)
Private_CleanupSoldier(soldier, MC_Release)
RETURN TRUE
ELSE
Private_CleanupSoldier(soldier, MC_Release)
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
//PROC Private_SetSoldierLeaving(structSoldier& soldier)
//
// enumCharacterList ePlayerChar = GET_CURRENT_PLAYER_PED_ENUM()
//
// IF soldier.eState <> SOLDIER_NULL
// AND soldier.eState < SOLDIER_FAIL_INJURED
//
// IF IS_PED_UNINJURED(soldier.hPed)
// AND soldier.eChar <> ePlayerChar
//
// BOOL bCanCleanup = FALSE
//
// //-- Try to do preliminary stuff, so clean up can take place
//
// // On foot, or slow moving vehicle?
// IF NOT IS_PED_IN_ANY_VEHICLE(soldier.hPed)
// OR GET_ENTITY_SPEED(GET_VEHICLE_PED_IS_IN(soldier.hPed)) < 5.0//10.0
//
// // Say "We kicked ass" and leave
// TEXT_LABEL tBlock, tRoot
//
// IF NOT IS_MESSAGE_BEING_DISPLAYED()
// AND NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
// AND Private_GetFriendActivityPhrase(GET_FRIEND_FROM_CHAR(ePlayerChar), GET_FRIEND_FROM_CHAR(soldier.eChar), FAP_SQUAD_PASSED, tBlock, tRoot)
//
// ADD_FRIEND_CHAR_FOR_DIALOGUE(gActivity.convPedsDefault, soldier.eChar, soldier.hPed)
// ADD_FRIEND_CHAR_FOR_DIALOGUE(gActivity.convPedsDefault, ePlayerChar, PLAYER_PED_ID())
//
// IF CREATE_CONVERSATION(gActivity.convPedsDefault, tBlock, tRoot, CONV_PRIORITY_AMBIENT_HIGH)
// bCanCleanup = TRUE
// ENDIF
//
// ELIF PRIVATE_FriendDialogue_GetRawAudio(soldier.eChar, FAP_SQUAD_PASSED, tRoot)
// PLAY_PED_AMBIENT_SPEECH_WITH_VOICE(soldier.hPed, tRoot, PRIVATE_Get_SpeakerLabel_From_Char(soldier.eChar), SPEECH_PARAMS_FORCE_FRONTEND)
// bCanCleanup = TRUE
// ENDIF
// ENDIF
//
// //-- If ready, leave and clean up
// IF bCanCleanUp
// // Remove from group
// IF IS_PED_GROUP_MEMBER(soldier.hPed, PLAYER_GROUP_ID())
// REMOVE_PED_FROM_GROUP(soldier.hPed)
// ENDIF
//
// // Don't allow ambient launch
// DECOR_SET_BOOL(soldier.hPed, "BlockFriendGrab", TRUE)
// SET_PED_KEEP_TASK(soldier.hPed, TRUE)
//// STORE_PLAYER_PED_INFO(soldier.hPed)
//
// // Walk away from the mission
// SEQUENCE_INDEX seq
// OPEN_SEQUENCE_TASK(seq)
// IF IS_PED_IN_ANY_VEHICLE(soldier.hPed, TRUE)
// TASK_LEAVE_ANY_VEHICLE(null)
// ENDIF
// TASK_LOOK_AT_ENTITY(null, PLAYER_PED_ID(), 2000, SLF_WHILE_NOT_IN_FOV|SLF_USE_TORSO)
// TASK_PAUSE(null, GET_RANDOM_INT_IN_RANGE(800, 1200))
// TASK_WANDER_STANDARD(null)
// CLOSE_SEQUENCE_TASK(seq)
// TASK_PERFORM_SEQUENCE(soldier.hPed, seq)
// CLEAR_SEQUENCE_TASK(seq)
//
// // Cleanup soldier
// Private_CleanupSoldier(soldier, MC_LeavePedIntact)
// ENDIF
//
// ELSE
// Private_CleanupSoldier(soldier, MC_LeavePedIntact)
// ENDIF
//
// ENDIF
//
//ENDPROC
//------------------------------------------------------------------------------------------------------------------------------
// TASKS
//------------------------------------------------------------------------------------------------------------------------------
PROC Private_UpdateSoldierBlip(structSoldier& soldier)
//-- Decide if blip is needed
BOOL bEnableBlip = FALSE
// Blip shouldn't ever be allowed in these states
IF soldier.eState >= SOLDIER_FAIL_INJURED
OR soldier.eState = SOLDIER_CREATE_IN_CAR
OR soldier.eState = SOLDIER_CREATE_ON_FOOT
OR IS_PED_INJURED(soldier.hPed)
OR soldier.hPed = PLAYER_PED_ID()
bEnableBlip = FALSE
// Blip should always be on when arriving
ELIF soldier.eState = SOLDIER_ARRIVE_IN_CAR
bEnableBlip = TRUE
// Blip should always be on when following in car
ELIF soldier.eState = SOLDIER_FOLLOW AND IS_PED_IN_ANY_VEHICLE(soldier.hPed) // When follow state not true anymore, ped told to get out, but task doesn't take effect until frame later, so get a one frame non signature blip
bEnableBlip = TRUE
// Blip should always be on when parachuting
ELIF soldier.eState = SOLDIER_PARACHUTE
bEnableBlip = TRUE
// Blip is currently off, switch it on if player > 100m away
ELIF soldier.eCurrentBlip = SOLDIER_BLIP_OFF
bEnableBlip = FALSE
IF Util_IsPedOutsideRangePed(soldier.hPed, PLAYER_PED_ID(), 100.0)
bEnableBlip = TRUE
ENDIF
// Blip is currently on, switch it off if player < 25m away and (onfoot/in same car as player)
ELSE
bEnableBlip = TRUE
IF Util_IsPedInsideRangePed(soldier.hPed, PLAYER_PED_ID(), 25.0)
AND ( NOT IS_PED_SITTING_IN_ANY_VEHICLE(soldier.hPed) OR ( IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID()) AND IS_PED_IN_VEHICLE(PLAYER_PED_ID(), GET_VEHICLE_PED_IS_IN(soldier.hPed)) ) )
bEnableBlip = FALSE
ENDIF
ENDIF
//-- Determine appropriate blip type
enumSoldierBlipType eRequestBlip = SOLDIER_BLIP_OFF
IF bEnableBlip
IF NOT Private_IsSoldierGlobalFlagSet(soldier, g_bitfieldBattleBuddyAvailable)
eRequestBlip = SOLDIER_BLIP_SIGNATURE
ELIF soldier.eState = SOLDIER_ARRIVE_IN_CAR
eRequestBlip = SOLDIER_BLIP_SIGNATURE
ELIF soldier.eState = SOLDIER_FOLLOW AND IS_PED_IN_ANY_VEHICLE(soldier.hPed)
eRequestBlip = SOLDIER_BLIP_SIGNATURE
ELIF IS_PED_IN_ANY_VEHICLE(soldier.hPed)
eRequestBlip = SOLDIER_BLIP_VEHICLE
ELSE
eRequestBlip = SOLDIER_BLIP_PED
ENDIF
ENDIF
//-- Update to requested type
IF soldier.eCurrentBlip <> eRequestBlip
SAFE_REMOVE_BLIP(soldier.hBlip)
soldier.eCurrentBlip = SOLDIER_BLIP_OFF
SWITCH eRequestBlip
CASE SOLDIER_BLIP_SIGNATURE
soldier.hBlip = CREATE_PED_BLIP(soldier.hPed, TRUE, TRUE)
IF DOES_BLIP_EXIST(soldier.hBlip)
IF soldier.eChar = CHAR_MICHAEL
SET_BLIP_SPRITE(soldier.hBlip, RADAR_TRACE_MICHAEL_FAMILY)
ELIF soldier.eChar = CHAR_FRANKLIN
SET_BLIP_SPRITE(soldier.hBlip, RADAR_TRACE_FRANKLIN_FAMILY)
ELIF soldier.eChar = CHAR_TREVOR
SET_BLIP_SPRITE(soldier.hBlip, RADAR_TRACE_TREVOR_FAMILY)
ENDIF
SET_BLIP_COLOUR(soldier.hBlip, BLIP_COLOUR_BLUE)
ENDIF
soldier.eCurrentBlip = SOLDIER_BLIP_SIGNATURE
BREAK
CASE SOLDIER_BLIP_VEHICLE
soldier.hBlip = CREATE_VEHICLE_BLIP(GET_VEHICLE_PED_IS_IN(soldier.hPed), TRUE)
soldier.eCurrentBlip = SOLDIER_BLIP_VEHICLE
BREAK
CASE SOLDIER_BLIP_PED
soldier.hBlip = CREATE_PED_BLIP(soldier.hPed, TRUE, TRUE)
soldier.eCurrentBlip = SOLDIER_BLIP_PED
BREAK
ENDSWITCH
ENDIF
ENDPROC
FUNC BOOL Private_CanSoldierExitArrival(structSoldier& soldier)
IF NOT IS_VEHICLE_DRIVEABLE(soldier.hVehicle)
OR NOT IS_PED_IN_VEHICLE(soldier.hPed, soldier.hVehicle)
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
PROC Private_UpdateSoldierArriveInCarTasks(structSoldier& soldier)
// Reset if required
IF soldier.bInitState
Private_SetSoldierAttribs(soldier)
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(soldier.hPed, TRUE)
CLEAR_PED_TASKS(soldier.hPed)
// Store the nearest road node, to use as dest
// soldier.hArrivalNode = Private_GetNearestRoadside(GET_ENTITY_COORDS(PLAYER_PED_ID()), soldier.vArrivalPos)
soldier.iArrivalStage = 0
soldier.iArrivalStoppedTimer = GET_GAME_TIMER() + 1000
soldier.iArrivalHornTimer = 0
soldier.bInitState = FALSE
ENDIF
//-- Parameters
FLOAT fPlayerDist = GET_DISTANCE_BETWEEN_ENTITIES(soldier.hPed, PLAYER_PED_ID())
FLOAT fDestDist = GET_DISTANCE_BETWEEN_ENTITY_AND_COORD(soldier.hPed, soldier.vArrivalPos)
INT iBasicMode = ENUM_TO_INT(DF_ChangeLanesAroundObstructions)|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_SteerAroundStationaryCars)|ENUM_TO_INT(DF_SwerveAroundAllCars)|ENUM_TO_INT(DF_SteerAroundPeds)
INT iParkAvoidMode = iBasicMode|ENUM_TO_INT(DF_SteerAroundStationaryCars)
INT iParkStopMode = iBasicMode|ENUM_TO_INT(DF_SteerAroundStationaryCars)|ENUM_TO_INT(DF_StopForCars)
FLOAT fDriveSpeed = CONST_fBattleBuddyArrivalSpeed
IF IS_BATTLEBUDDY_BEHAVIOUR_REQUESTED(BBF_DriveHighSpeed)
fDriveSpeed = CONST_fBattleBuddyArrivalSpeedFast
ENDIF
//-- Behaviour
BOOL bIsCarStopped = FALSE
IF GET_ENTITY_SPEED(soldier.hPed) > 1.0
soldier.iArrivalStoppedTimer = GET_GAME_TIMER() + 2000//1000
ELIF soldier.iArrivalStoppedTimer < GET_GAME_TIMER()
bIsCarStopped = TRUE
ENDIF
// Near player and stopped / very near player...
IF NOT IS_VEHICLE_DRIVEABLE(GET_VEHICLE_PED_IS_IN(soldier.hPed))
OR fPlayerDist < 12.0
OR (fPlayerDist < 20.0 AND fDestDist < 10.0 AND bIsCarStopped)
// Stop get out of vehicle
IF NOT IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_LEAVE_ANY_VEHICLE) OR soldier.iArrivalStage <> 0
CLEAR_PED_TASKS(soldier.hPed)
TASK_LEAVE_ANY_VEHICLE(soldier.hPed)
soldier.iArrivalStage = 0
ENDIF
// At dest...
ELIF fDestDist < 2.0
// Stop vehicle
IF NOT IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_VEHICLE_MISSION) OR soldier.iArrivalStage <> 1
TASK_VEHICLE_MISSION_COORS_TARGET(soldier.hPed, GET_VEHICLE_PED_IS_IN(soldier.hPed), soldier.vArrivalPos, MISSION_STOP, 0.0, INT_TO_ENUM(DRIVINGMODE, iParkAvoidMode), 2.0, 20.0/*15.0*/, TRUE)
soldier.iArrivalStage = 1
ENDIF
// Very close to dest...
ELIF fDestDist < 7.0
// Drive slowly (+ stop for cars)
IF NOT IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_VEHICLE_MISSION) OR soldier.iArrivalStage <> 2
CLEAR_PED_TASKS(soldier.hPed)
TASK_VEHICLE_MISSION_COORS_TARGET(soldier.hPed, GET_VEHICLE_PED_IS_IN(soldier.hPed), soldier.vArrivalPos, MISSION_GOTO, 10.0, INT_TO_ENUM(DRIVINGMODE, iParkStopMode), 2.0, 20.0/*15.0*/, TRUE)
soldier.iArrivalStage = 2
ENDIF
// Close to dest or player...
ELIF fPlayerDist < 30.0 OR fDestDist < 40.0
// Drive slowly (+ avoid cars)
IF NOT IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_VEHICLE_MISSION) OR soldier.iArrivalStage <> 3
CLEAR_PED_TASKS(soldier.hPed)
TASK_VEHICLE_MISSION_COORS_TARGET(soldier.hPed, GET_VEHICLE_PED_IS_IN(soldier.hPed), soldier.vArrivalPos, MISSION_GOTO, 10.0, INT_TO_ENUM(DRIVINGMODE, iParkAvoidMode), 2.0, 20.0/*15.0*/, TRUE)
soldier.iArrivalStage = 3
ENDIF
// Not close to dest/player...
ELSE
// Drive full speed
IF NOT IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_VEHICLE_MISSION) OR soldier.iArrivalStage <> 4
CLEAR_PED_TASKS(soldier.hPed)
TASK_VEHICLE_MISSION_COORS_TARGET(soldier.hPed, GET_VEHICLE_PED_IS_IN(soldier.hPed), soldier.vArrivalPos, MISSION_GOTO, fDriveSpeed, INT_TO_ENUM(DRIVINGMODE, iDriveAvoidMode), 10.0, 15.0, TRUE)
soldier.iArrivalStage = 4
ENDIF
ENDIF
// Update greeting horn
IF soldier.iArrivalHornTimer >= 0
IF soldier.iArrivalHornTimer = 0
IF (IS_PED_SITTING_IN_ANY_VEHICLE(soldier.hPed) AND NOT IS_PED_ON_ANY_BIKE(soldier.hPed))
AND (fDestDist < 2.0 AND bIsCarStopped AND fPlayerDist > 20.0)
SET_HORN_PERMANENTLY_ON_TIME(GET_VEHICLE_PED_IS_IN(soldier.hPed), 50)
soldier.iArrivalHornTimer = GET_GAME_TIMER() + 500
ENDIF
ELSE
IF NOT IS_PED_SITTING_IN_ANY_VEHICLE(soldier.hPed) OR IS_PED_ON_ANY_BIKE(soldier.hPed)
soldier.iArrivalHornTimer = -1
ELIF soldier.iArrivalHornTimer < GET_GAME_TIMER()
SET_HORN_PERMANENTLY_ON_TIME(GET_VEHICLE_PED_IS_IN(soldier.hPed), 500)
soldier.iArrivalHornTimer = -1
ENDIF
ENDIF
ENDIF
// Display
#IF IS_DEBUG_BUILD
IF g_flowUnsaved.bShowMissionFlowDebugScreen
IF (g_iDebugSelectedFriendConnDisplay > 0)
SET_DEBUG_LINES_AND_SPHERES_DRAWING_ACTIVE(TRUE)
INT r = 0
INT g = 0
INT b = 0
IF soldier.eChar = CHAR_MICHAEL
r = 255
g = 128
ENDIF
IF soldier.eChar = CHAR_FRANKLIN
g = 255
b = 128
ENDIF
IF soldier.eChar = CHAR_TREVOR
b = 255
r = 128
ENDIF
DRAW_DEBUG_SPHERE(soldier.vArrivalPos, 0.1, r, g, b, 255)
DRAW_DEBUG_LINE(soldier.vArrivalPos-<<0,0,5>>, soldier.vArrivalPos+<<0,0,5>>, r, g, b, 255)
ENDIF
ENDIF
#ENDIF
ENDPROC
PROC Private_UpdateSoldierApproachTasks(structSoldier& soldier)
// Reset if required
IF soldier.bInitState OR soldier.eCharToDriveTo <> GET_CURRENT_PLAYER_PED_ENUM()
Private_SetSoldierAttribs(soldier)
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(soldier.hPed, TRUE)
CLEAR_PED_TASKS(soldier.hPed)
soldier.eCharToDriveTo = GET_CURRENT_PLAYER_PED_ENUM()
soldier.bIsInParkingRange = FALSE
soldier.bInitState = FALSE
ENDIF
// Parameters
FLOAT fPlayerDist = GET_DISTANCE_BETWEEN_ENTITIES(soldier.hPed, PLAYER_PED_ID())
INT iDrivingMode = ENUM_TO_INT(DF_ChangeLanesAroundObstructions)|ENUM_TO_INT(DF_DriveIntoOncomingTraffic)|ENUM_TO_INT(DF_GoOffRoadWhenAvoiding)|ENUM_TO_INT(DF_SteerAroundObjects)|ENUM_TO_INT(DF_StopForPeds)|ENUM_TO_INT(DF_SwerveAroundAllCars)|ENUM_TO_INT(DF_SteerAroundStationaryCars)|ENUM_TO_INT(DF_SteerAroundPeds)//|ENUM_TO_INT(DF_UseSwitchedOffNodes)
INT iParkingMode = ENUM_TO_INT(DF_ChangeLanesAroundObstructions)|ENUM_TO_INT(DF_DriveIntoOncomingTraffic)|ENUM_TO_INT(DF_GoOffRoadWhenAvoiding)|ENUM_TO_INT(DF_SteerAroundObjects)|ENUM_TO_INT(DF_StopForPeds)|ENUM_TO_INT(DF_StopForCars)//|ENUM_TO_INT(DF_UseSwitchedOffNodes)
FLOAT fDriveSpeed = 25.0
IF IS_BATTLEBUDDY_BEHAVIOUR_REQUESTED(BBF_DriveHighSpeed)
fDriveSpeed = 40.0
ENDIF
// Behaviour
IF NOT IS_PED_IN_ANY_VEHICLE(soldier.hPed)
// Not in vehicle -> Run to player
IF NOT IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_GO_TO_ENTITY)
CLEAR_PED_TASKS(soldier.hPed)
TASK_GO_TO_ENTITY(soldier.hPed, PLAYER_PED_ID(), DEFAULT_TIME_NEVER_WARP, 5.0)
ENDIF
ELSE
// Vehicle undrivable -> Get out
IF NOT IS_VEHICLE_DRIVEABLE(GET_VEHICLE_PED_IS_IN(soldier.hPed))
IF NOT IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_LEAVE_ANY_VEHICLE)
CLEAR_PED_TASKS(soldier.hPed)
TASK_LEAVE_ANY_VEHICLE(soldier.hPed)
ENDIF
// Not within parking range -> Drive to player
ELIF fPlayerDist > 30.0
IF NOT IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_VEHICLE_MISSION) OR soldier.bIsInParkingRange = TRUE
CLEAR_PED_TASKS(soldier.hPed)
TASK_VEHICLE_MISSION_PED_TARGET(soldier.hPed, GET_VEHICLE_PED_IS_IN(soldier.hPed), PLAYER_PED_ID(), MISSION_GOTO, fDriveSpeed, INT_TO_ENUM(DRIVINGMODE, iDrivingMode), 10.0, 15.0, TRUE)
ENDIF
soldier.bIsInParkingRange = FALSE
// Within parking range + car is moving -> Slow down
ELIF fPlayerDist > 12.0 AND GET_ENTITY_SPEED(soldier.hPed) > 1.0
IF NOT IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_VEHICLE_MISSION) OR soldier.bIsInParkingRange = FALSE
CLEAR_PED_TASKS(soldier.hPed)
TASK_VEHICLE_MISSION_PED_TARGET(soldier.hPed, GET_VEHICLE_PED_IS_IN(soldier.hPed), PLAYER_PED_ID(), MISSION_GOTO, 10.0, INT_TO_ENUM(DRIVINGMODE, iParkingMode), 10.0, 15.0, TRUE)
ENDIF
soldier.bIsInParkingRange = TRUE
// Within parking range + car is stopped -> Get out
ELIF fPlayerDist > 12.0
IF NOT IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_LEAVE_ANY_VEHICLE) OR soldier.bIsInParkingRange = FALSE
CLEAR_PED_TASKS(soldier.hPed)
TASK_LEAVE_ANY_VEHICLE(soldier.hPed)
ENDIF
soldier.bIsInParkingRange = TRUE
// Very close -> Get out
ELSE
IF NOT IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_LEAVE_ANY_VEHICLE) OR soldier.bIsInParkingRange = FALSE
CLEAR_PED_TASKS(soldier.hPed)
TASK_LEAVE_ANY_VEHICLE(soldier.hPed)
ENDIF
soldier.bIsInParkingRange = TRUE
ENDIF
ENDIF
// // Within parking range
// IF fPlayerDist < 30.0
//
// // Very close or stopped -> Get out of car
// IF (fPlayerDist < 12.0) OR (GET_ENTITY_SPEED(soldier.hPed) < 1.0)
// IF NOT IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_LEAVE_ANY_VEHICLE) OR soldier.bIsInParkingRange = FALSE
// CLEAR_PED_TASKS(soldier.hPed)
// TASK_LEAVE_ANY_VEHICLE(soldier.hPed)
// ENDIF
// soldier.bIsInParkingRange = TRUE
//
// // Otherwise -> Drive+stop near player
// ELSE
// IF NOT IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_VEHICLE_MISSION) OR soldier.bIsInParkingRange = FALSE
// CLEAR_PED_TASKS(soldier.hPed)
// TASK_VEHICLE_MISSION_PED_TARGET(soldier.hPed, GET_VEHICLE_PED_IS_IN(soldier.hPed), PLAYER_PED_ID(), MISSION_GOTO, 10.0, INT_TO_ENUM(DRIVINGMODE, iParkingMode), 10.0, 15.0, TRUE)
// ENDIF
// soldier.bIsInParkingRange = TRUE
// ENDIF
//
// // Out of parking range -> Drive to player
// ELSE
// IF NOT IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_VEHICLE_MISSION) OR soldier.bIsInParkingRange = TRUE
// CLEAR_PED_TASKS(soldier.hPed)
// TASK_VEHICLE_MISSION_PED_TARGET(soldier.hPed, GET_VEHICLE_PED_IS_IN(soldier.hPed), PLAYER_PED_ID(), MISSION_GOTO, fDriveSpeed, INT_TO_ENUM(DRIVINGMODE, iDrivingMode), 10.0, 15.0, TRUE)
// ENDIF
// soldier.bIsInParkingRange = FALSE
// ENDIF
//
// ENDIF
ENDPROC
FUNC BOOL Private_UpdateSoldierVehicle(structSoldier& soldier)
// If have no target, try to get one nearby
IF NOT IS_VEHICLE_DRIVEABLE(soldier.hVehicle)
// Get nearby vehicles
CONST_INT CONST_iMaxNearbyVehicles 10
VEHICLE_INDEX nearbyVehicles[CONST_iMaxNearbyVehicles]
INT iNearCount = GET_PED_NEARBY_VEHICLES(soldier.hPed, nearbyVehicles)
// Remove any unusable vehicles from list
INT i
REPEAT iNearCount i
IF NOT IS_VEHICLE_DRIVEABLE(nearbyVehicles[i])
nearbyVehicles[i] = NULL
ELSE
IF DOES_ENTITY_EXIST(GET_PED_IN_VEHICLE_SEAT(nearbyVehicles[i], VS_DRIVER))
nearbyVehicles[i] = NULL
ELSE
VECTOR vPlayerPos = GET_ENTITY_COORDS(PLAYER_PED_ID())
VECTOR vSoldierPos = GET_ENTITY_COORDS(soldier.hPed)
VECTOR vCarPos = GET_ENTITY_COORDS(nearbyVehicles[i])
IF VDIST2(vSoldierPos, vCarPos) > 70.0*70.0
OR VDIST2(vPlayerPos, vCarPos) > (CONST_fBattleBuddyAvailableFailDist-30.0)*(CONST_fBattleBuddyAvailableFailDist-30.0)
nearbyVehicles[i] = NULL
ENDIF
ENDIF
ENDIF
ENDREPEAT
// Return first non-blacklisted vehicle if available
REPEAT iNearCount i
IF nearbyVehicles[i] <> NULL
IF NOT IS_VEHICLE_MODEL_ON_BLACKLIST(GET_ENTITY_MODEL(nearbyVehicles[i]))
soldier.hVehicle = nearbyVehicles[i]
RETURN TRUE
ENDIF
ENDIF
ENDREPEAT
// Return first available vehicle if any
REPEAT iNearCount i
IF nearbyVehicles[i] <> NULL
soldier.hVehicle = nearbyVehicles[i]
RETURN TRUE
ENDIF
ENDREPEAT
// Couldn't find usable vehicle
RETURN FALSE
ENDIF
RETURN TRUE
ENDFUNC
PROC Private_UpdateSoldierFollowTasks(structSoldier& soldier)
// Reset if required
IF soldier.bInitState
Private_SetSoldierAttribs(soldier)
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(soldier.hPed, TRUE)
CLEAR_PED_TASKS(soldier.hPed)
soldier.eCharToDriveTo = GET_CURRENT_PLAYER_PED_ENUM()
soldier.bIsInParkingRange = FALSE
soldier.bInitState = FALSE
ENDIF
// Parameters
FLOAT fPlayerDist = GET_DISTANCE_BETWEEN_ENTITIES(soldier.hPed, PLAYER_PED_ID())
INT iDrivingMode = ENUM_TO_INT(DF_ChangeLanesAroundObstructions)|ENUM_TO_INT(DF_DriveIntoOncomingTraffic)|ENUM_TO_INT(DF_GoOffRoadWhenAvoiding)|ENUM_TO_INT(DF_SteerAroundObjects)|ENUM_TO_INT(DF_StopForPeds)|ENUM_TO_INT(DF_SwerveAroundAllCars)|ENUM_TO_INT(DF_SteerAroundStationaryCars)|ENUM_TO_INT(DF_SteerAroundPeds)//|ENUM_TO_INT(DF_UseSwitchedOffNodes)
FLOAT fDriveSpeed = 25.0
IF IS_BATTLEBUDDY_BEHAVIOUR_REQUESTED(BBF_DriveHighSpeed) AND fPlayerDist > 50.0
fDriveSpeed = 40.0
ENDIF
// If close by mark as available (and beep horn)
// IF NOT Private_IsSoldierGlobalFlagSet(soldier, g_bitfieldBattleBuddyAvailable)
// IF fPlayerDist < 50.0
// // Set as available
// Private_SetSoldierGlobalFlag(soldier, g_bitfieldBattleBuddyAvailable)
//
// // Beep horn
// IF IS_PED_SITTING_IN_ANY_VEHICLE(soldier.hPed)
// AND GET_PED_IN_VEHICLE_SEAT(GET_VEHICLE_PED_IS_IN(soldier.hPed), VS_DRIVER) = soldier.hPed
// START_VEHICLE_HORN(GET_VEHICLE_PED_IS_IN(soldier.hPed), 1000)
// ENDIF
// ENDIF
// ENDIF
// Behaviour
//-- Not in vehicle
IF NOT IS_PED_IN_ANY_VEHICLE(soldier.hPed)
// Try to find vehicle and get in
IF Private_UpdateSoldierVehicle(soldier)
IF NOT IS_PED_IN_VEHICLE(soldier.hPed, soldier.hVehicle)
IF GET_DISTANCE_BETWEEN_ENTITIES(soldier.hPed, soldier.hVehicle) > 30.0
IF NOT IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_GO_TO_ENTITY)
CLEAR_PED_TASKS(soldier.hPed)
TASK_GO_TO_ENTITY(soldier.hPed, soldier.hVehicle, DEFAULT_TIME_NEVER_WARP)
ENDIF
ELSE
IF NOT IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_ENTER_VEHICLE)
CLEAR_PED_TASKS(soldier.hPed)
TASK_ENTER_VEHICLE(soldier.hPed, soldier.hVehicle, DEFAULT_TIME_NEVER_WARP)
ENDIF
ENDIF
ENDIF
// Can't find vehicle, follow player
ELSE
IF NOT IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_GO_TO_ENTITY)
CLEAR_PED_TASKS(soldier.hPed)
TASK_GO_TO_ENTITY(soldier.hPed, PLAYER_PED_ID(), DEFAULT_TIME_NEVER_WARP, 5.0)
ENDIF
ENDIF
//-- In vehicle
ELSE
// Exit if vehicle undrivable
IF NOT IS_VEHICLE_DRIVEABLE(GET_VEHICLE_PED_IS_IN(soldier.hPed))
IF NOT IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_LEAVE_ANY_VEHICLE)
CLEAR_PED_TASKS(soldier.hPed)
TASK_LEAVE_ANY_VEHICLE(soldier.hPed)
ENDIF
ELSE
// Follow player around
IF NOT IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_VEHICLE_MISSION)
CLEAR_PED_TASKS(soldier.hPed)
TASK_VEHICLE_MISSION_PED_TARGET(soldier.hPed, GET_VEHICLE_PED_IS_IN(soldier.hPed), PLAYER_PED_ID(), MISSION_FOLLOW, fDriveSpeed, INT_TO_ENUM(DRIVINGMODE, iDrivingMode), 2.0, 20.0, TRUE)
ENDIF
SET_DRIVE_TASK_MAX_CRUISE_SPEED(soldier.hPed, fDriveSpeed)
SET_DRIVE_TASK_CRUISE_SPEED(soldier.hPed, fDriveSpeed)
ENDIF
ENDIF
ENDPROC
PROC Private_UpdateSoldierGroupTasks(structSoldier& soldier)
// Reset if required
IF soldier.bInitState
Private_SetSoldierAttribs(soldier)
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(soldier.hPed, FALSE)
REGISTER_HATED_TARGETS_AROUND_PED(soldier.hPed, 100.0)
CLEAR_PED_TASKS(soldier.hPed)
IF Private_CheckBehaviourFlag(BBF_CombatGroupSpacing)
SET_GROUP_FORMATION(PLAYER_GROUP_ID(), FORMATION_SURROUND_FACING_INWARDS)
SET_GROUP_FORMATION_SPACING(PLAYER_GROUP_ID(), 4.0) // NOTE: This is set in attributes too
ELSE
SET_GROUP_FORMATION(PLAYER_GROUP_ID(), FORMATION_LOOSE)
// SET_GROUP_FORMATION_SPACING(PLAYER_GROUP_ID(), 3.0)
ENDIF
soldier.iStealthDelay = 0
soldier.bInitState = FALSE
ENDIF
// Makr sure in group
IF NOT IS_PED_GROUP_MEMBER(soldier.hPed, PLAYER_GROUP_ID())
SET_PED_AS_GROUP_MEMBER(soldier.hPed, PLAYER_GROUP_ID())
ENDIF
// Process
IF IS_PED_UNINJURED(PLAYER_PED_ID())
AND NOT Private_IsPlayerSwitching()
// Say "let's do this!" on first arrival
IF soldier.bRequestGreetingDialogue
IF (IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID()) AND IS_PED_IN_VEHICLE(soldier.hPed, GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID())))
OR (IS_PED_ON_FOOT(PLAYER_PED_ID()) AND IS_PED_ON_FOOT(soldier.hPed))
enumCharacterList ePlayerChar = GET_CURRENT_PLAYER_PED_ENUM()
TEXT_LABEL tBlock, tRoot
IF NOT IS_MESSAGE_BEING_DISPLAYED()
AND NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
AND Private_GetFriendActivityPhrase(ePlayerChar, soldier.eChar, FAP_SQUAD_START, tBlock, tRoot)
ADD_FRIEND_CHAR_FOR_DIALOGUE(gActivity.convPedsDefault, soldier.eChar, soldier.hPed)
ADD_FRIEND_CHAR_FOR_DIALOGUE(gActivity.convPedsDefault, ePlayerChar, PLAYER_PED_ID())
IF CREATE_CONVERSATION(gActivity.convPedsDefault, tBlock, tRoot, CONV_PRIORITY_AMBIENT_HIGH)
TASK_LOOK_AT_ENTITY(soldier.hPed, PLAYER_PED_ID(), 3500)
soldier.bRequestGreetingDialogue = FALSE
ENDIF
ELIF PRIVATE_FriendDialogue_GetRawAudio(soldier.eChar, FAP_SQUAD_START, tRoot)
PLAY_PED_AMBIENT_SPEECH_WITH_VOICE(soldier.hPed, tRoot, PRIVATE_Get_SpeakerLabel_From_Char(soldier.eChar), SPEECH_PARAMS_FORCE_FRONTEND)
TASK_LOOK_AT_ENTITY(soldier.hPed, PLAYER_PED_ID(), 3500)
soldier.bRequestGreetingDialogue = FALSE
ENDIF
ENDIF
ENDIF
// If in undrivable car, exit
IF IS_PED_IN_ANY_VEHICLE(soldier.hPed)
AND NOT IS_VEHICLE_DRIVEABLE(GET_VEHICLE_PED_IS_IN(soldier.hPed))
IF NOT IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_LEAVE_ANY_VEHICLE)
TASK_LEAVE_ANY_VEHICLE(soldier.hPed)
ENDIF
// If driver and in combat, drive randomly
ELIF NOT IS_BATTLEBUDDY_BEHAVIOUR_REQUESTED(BBF_DontFleeCopsWhenDriver)
AND IS_PED_SITTING_IN_ANY_VEHICLE(soldier.hPed)
AND IS_PED_SITTING_IN_VEHICLE(PLAYER_PED_ID(), GET_VEHICLE_PED_IS_IN(soldier.hPed))
AND GET_PED_IN_VEHICLE_SEAT(GET_VEHICLE_PED_IS_IN(soldier.hPed), VS_DRIVER) = soldier.hPed
AND GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) > 1
IF NOT IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_VEHICLE_DRIVE_WANDER)
TASK_VEHICLE_DRIVE_WANDER(soldier.hPed, GET_VEHICLE_PED_IS_IN(soldier.hPed), 60.0, DRIVINGMODE_AVOIDCARS_RECKLESS)
ENDIF
// Otherwise, clear tasks
ELSE
IF IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_VEHICLE_DRIVE_WANDER)
OR IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_LEAVE_ANY_VEHICLE)
CLEAR_PED_TASKS(soldier.hPed)
ENDIF
ENDIF
// If current weapon is empty, equip best weapon
IF soldier.hPed <> PLAYER_PED_ID()
WEAPON_TYPE eCurrentWeapon
GET_CURRENT_PED_WEAPON(soldier.hPed, eCurrentWeapon)
IF eCurrentWeapon = WEAPONTYPE_UNARMED
OR GET_AMMO_IN_PED_WEAPON(soldier.hPed, eCurrentWeapon) = 0
WEAPON_TYPE eBestWeapon = GET_BEST_PED_WEAPON(soldier.hPed)
IF eBestWeapon <> WEAPONTYPE_UNARMED
#IF IS_DEBUG_BUILD
TEXT_LABEL tChar = GetLabel_enumCharacterList(soldier.eChar)
TEXT_LABEL tWeapon = GET_WEAPON_NAME(eBestWeapon)
CPRINTLN(DEBUG_FRIENDS, "BATTLEBUDDY - Setting ", tChar, " to best weapon: ", tWeapon)
#ENDIF
SET_CURRENT_PED_WEAPON(soldier.hPed, eBestWeapon, TRUE)
ENDIF
ENDIF
ENDIF
// Crouch when player crouches
IF GET_PED_STEALTH_MOVEMENT(PLAYER_PED_ID())
IF NOT GET_PED_STEALTH_MOVEMENT(soldier.hPed)
IF soldier.iStealthDelay = 0
soldier.iStealthDelay = GET_GAME_TIMER() + GET_RANDOM_INT_IN_RANGE(200, 1200)
ELIF soldier.iStealthDelay < GET_GAME_TIMER()
SET_PED_STEALTH_MOVEMENT(soldier.hPed, TRUE)
soldier.iStealthDelay = 0
ENDIF
ELSE
soldier.iStealthDelay = 0
ENDIF
ELSE
IF GET_PED_STEALTH_MOVEMENT(soldier.hPed)
IF soldier.iStealthDelay = 0
soldier.iStealthDelay = GET_GAME_TIMER() + GET_RANDOM_INT_IN_RANGE(200, 1200)
ELIF soldier.iStealthDelay < GET_GAME_TIMER()
SET_PED_STEALTH_MOVEMENT(soldier.hPed, FALSE)
soldier.iStealthDelay = 0
ENDIF
ELSE
soldier.iStealthDelay = 0
ENDIF
ENDIF
ENDIF
ENDPROC
PROC Private_UpdateSoldierCombatTasks(structSoldier& soldier)
// Reset if required
IF soldier.bInitState
Private_SetSoldierAttribs(soldier)
IF IS_PED_IN_GROUP(soldier.hPed)
REMOVE_PED_FROM_GROUP(soldier.hPed)
ENDIF
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(soldier.hPed, TRUE)
CLEAR_PED_TASKS(soldier.hPed)
IF Private_CheckBehaviourFlag(BBF_CombatDefendCargobobArea)
SET_PED_SPHERE_DEFENSIVE_AREA(soldier.hPed, << -2145.4856, 3018.2944, 31.8100 >>, 10.0)
soldier.bDefendingCargobobArea = TRUE
ELSE
// IF IS_PED_UNINJURED(PLAYER_PED_ID())
// SET_PED_DEFENSIVE_SPHERE_ATTACHED_TO_PED(soldier.hPed, PLAYER_PED_ID(), <<0, 0, 0>>, 150.0)
// ELSE
REMOVE_PED_DEFENSIVE_AREA(soldier.hPed)
// ENDIF
soldier.bDefendingCargobobArea = FALSE
ENDIF
soldier.iCombatDelay = GET_GAME_TIMER() + GET_RANDOM_INT_IN_RANGE(0, 1000)
soldier.iStealthDelay = 0
soldier.bInitState = FALSE
ENDIF
// Reset state if defensive area changes
IF Private_CheckBehaviourFlag(BBF_CombatDefendCargobobArea) <> soldier.bDefendingCargobobArea
soldier.bInitState = TRUE
ENDIF
// Process
IF IS_PED_UNINJURED(PLAYER_PED_ID())
AND NOT Private_IsPlayerSwitching()
// Say "let's do this!" on first arrival
IF soldier.bRequestGreetingDialogue
IF (IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID()) AND IS_PED_IN_VEHICLE(soldier.hPed, GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID())))
OR (IS_PED_ON_FOOT(PLAYER_PED_ID()) AND IS_PED_ON_FOOT(soldier.hPed))
enumCharacterList ePlayerChar = GET_CURRENT_PLAYER_PED_ENUM()
TEXT_LABEL tBlock, tRoot
IF NOT IS_MESSAGE_BEING_DISPLAYED()
AND NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
AND Private_GetFriendActivityPhrase(ePlayerChar, soldier.eChar, FAP_SQUAD_START, tBlock, tRoot)
ADD_FRIEND_CHAR_FOR_DIALOGUE(gActivity.convPedsDefault, soldier.eChar, soldier.hPed)
ADD_FRIEND_CHAR_FOR_DIALOGUE(gActivity.convPedsDefault, ePlayerChar, PLAYER_PED_ID())
IF CREATE_CONVERSATION(gActivity.convPedsDefault, tBlock, tRoot, CONV_PRIORITY_AMBIENT_HIGH)
TASK_LOOK_AT_ENTITY(soldier.hPed, PLAYER_PED_ID(), 3500)
soldier.bRequestGreetingDialogue = FALSE
ENDIF
ELIF PRIVATE_FriendDialogue_GetRawAudio(soldier.eChar, FAP_SQUAD_START, tRoot)
PLAY_PED_AMBIENT_SPEECH_WITH_VOICE(soldier.hPed, tRoot, PRIVATE_Get_SpeakerLabel_From_Char(soldier.eChar), SPEECH_PARAMS_FORCE_FRONTEND)
TASK_LOOK_AT_ENTITY(soldier.hPed, PLAYER_PED_ID(), 3500)
soldier.bRequestGreetingDialogue = FALSE
ENDIF
ENDIF
ENDIF
// Set tasks
IF IS_PED_IN_ANY_VEHICLE(soldier.hPed)
VEHICLE_INDEX hVehicle = GET_VEHICLE_PED_IS_IN(soldier.hPed)
IF GET_ENTITY_SPEED(hVehicle) > 3.0
CLEAR_PED_TASKS(soldier.hPed)
BRING_VEHICLE_TO_HALT(hVehicle, 0.5, 10)
ELSE
IF NOT IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_LEAVE_ANY_VEHICLE)
CLEAR_PED_TASKS(soldier.hPed)
TASK_LEAVE_ANY_VEHICLE(soldier.hPed, 0, ECF_DONT_CLOSE_DOOR|ECF_RESUME_IF_INTERRUPTED)
ENDIF
ENDIF
ELSE
IF soldier.iCombatDelay < GET_GAME_TIMER()
IF NOT IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_COMBAT_HATED_TARGETS_AROUND_PED)
CLEAR_PED_TASKS(soldier.hPed)
TASK_COMBAT_HATED_TARGETS_AROUND_PED(soldier.hPed, 60.0)
ENDIF
ENDIF
ENDIF
// If current weapon is empty, equip best weapon
IF soldier.hPed <> PLAYER_PED_ID()
WEAPON_TYPE eCurrentWeapon
GET_CURRENT_PED_WEAPON(soldier.hPed, eCurrentWeapon)
IF eCurrentWeapon = WEAPONTYPE_UNARMED
OR GET_AMMO_IN_PED_WEAPON(soldier.hPed, eCurrentWeapon) = 0
WEAPON_TYPE eBestWeapon = GET_BEST_PED_WEAPON(soldier.hPed)
IF eBestWeapon <> WEAPONTYPE_UNARMED
#IF IS_DEBUG_BUILD
TEXT_LABEL tChar = GetLabel_enumCharacterList(soldier.eChar)
TEXT_LABEL tWeapon = GET_WEAPON_NAME(eBestWeapon)
CPRINTLN(DEBUG_FRIENDS, "BATTLEBUDDY - Setting ", tChar, " to best weapon: ", tWeapon)
#ENDIF
SET_CURRENT_PED_WEAPON(soldier.hPed, eBestWeapon, TRUE)
ENDIF
ENDIF
ENDIF
// Crouch when player crouches
IF GET_PED_STEALTH_MOVEMENT(PLAYER_PED_ID())
IF NOT GET_PED_STEALTH_MOVEMENT(soldier.hPed)
IF soldier.iStealthDelay = 0
soldier.iStealthDelay = GET_GAME_TIMER() + GET_RANDOM_INT_IN_RANGE(200, 1200)
ELIF soldier.iStealthDelay < GET_GAME_TIMER()
SET_PED_STEALTH_MOVEMENT(soldier.hPed, TRUE)
soldier.iStealthDelay = 0
ENDIF
ELSE
soldier.iStealthDelay = 0
ENDIF
ELSE
IF GET_PED_STEALTH_MOVEMENT(soldier.hPed)
IF soldier.iStealthDelay = 0
soldier.iStealthDelay = GET_GAME_TIMER() + GET_RANDOM_INT_IN_RANGE(200, 1200)
ELIF soldier.iStealthDelay < GET_GAME_TIMER()
SET_PED_STEALTH_MOVEMENT(soldier.hPed, FALSE)
soldier.iStealthDelay = 0
ENDIF
ELSE
soldier.iStealthDelay = 0
ENDIF
ENDIF
ENDIF
ENDPROC
PROC Private_UpdateSoldierParachuteTasks(structSoldier& soldier)
// Reset if required
IF soldier.bInitState
Private_SetSoldierAttribs(soldier)
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(soldier.hPed, TRUE)
CLEAR_PED_TASKS(soldier.hPed)
soldier.bInitState = FALSE
ENDIF
IF NOT IsPedPerformingTask(soldier.hPed, SCRIPT_TASK_PARACHUTE_TO_TARGET)
CLEAR_PED_TASKS(soldier.hPed)
TASK_PARACHUTE_TO_TARGET(soldier.hPed, GET_ENTITY_COORDS(PLAYER_PED_ID()))
soldier.iStealthDelay = GET_GAME_TIMER() + 1000
ELIF soldier.iStealthDelay < GET_GAME_TIMER()
SET_PARACHUTE_TASK_TARGET(soldier.hPed, GET_ENTITY_COORDS(PLAYER_PED_ID()))
soldier.iStealthDelay = GET_GAME_TIMER() + 1000
ENDIF
IF GET_PED_PARACHUTE_STATE(soldier.hPed) = PPS_SKYDIVING
FORCE_PED_TO_OPEN_PARACHUTE(soldier.hPed)
ENDIF
ENDPROC
PROC Private_UpdateSoldierPassengerTasks(structSoldier& soldier)
// Reset if required
IF soldier.bInitState
Private_SetSoldierAttribs(soldier)
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(soldier.hPed, TRUE)
CLEAR_PED_TASKS(soldier.hPed)
soldier.bInitState = FALSE
ENDIF
IF IS_PED_GROUP_MEMBER(soldier.hPed, PLAYER_GROUP_ID())
REMOVE_PED_FROM_GROUP(soldier.hPed)
ENDIF
ENDPROC
//------------------------------------------------------------------------------------------------------------------------------
// STATE MACHINE
//------------------------------------------------------------------------------------------------------------------------------
FUNC BOOL Private_AddSoldier(structSoldier& soldier, enumFriendConnectionMode eInitMode = FC_MODE_Squad)
enumCharacterList ePlayerChar = GET_CURRENT_PLAYER_PED_ENUM()
IF g_BattleBuddyMission = SP_MISSION_NONE
SCRIPT_ASSERT("Private_AddSoldier() - Squad mission is not set")
ENDIF
// Is player
IF soldier.eChar = ePlayerChar
soldier.hPed = PLAYER_PED_ID()
Private_SetSoldierState(soldier, SOLDIER_PLAYER)
// Creating a group char for replay - Create on foot by player
ELIF eInitMode = FC_MODE_ReplayGroup
soldier.iCreateAttempts = 0
Private_SetSoldierState(soldier, SOLDIER_CREATE_ON_FOOT)
// Doesn't exist, create ped
ELIF NOT Private_SyncSoldierPed(soldier)
soldier.iCreateAttempts = 0
// Choose vehicle type
IF soldier.eChar = CHAR_FRANKLIN
IF GET_RANDOM_INT_IN_RANGE(0, 100) < 25
soldier.eCreateInVehicleType = VEHICLE_TYPE_BIKE
ELSE
soldier.eCreateInVehicleType = VEHICLE_TYPE_CAR
ENDIF
ENDIF
Private_SetSoldierState(soldier, SOLDIER_CREATE_IN_CAR)
// Already exists, get ped
ELIF NOT IS_PED_INJURED(soldier.hPed)
Private_SetSoldierInitialWeapons(soldier)
Private_SetSoldierExtraHealth(soldier)
Private_SetSoldierState(soldier, SOLDIER_APPROACH)
// Already exists but dead, set failed
ELSE
Private_SetSoldierState(soldier, SOLDIER_FAIL_INJURED)
ENDIF
RETURN TRUE
ENDFUNC
FUNC BOOL Private_HasSoldierShotAnyPed(structSoldier& soldier)
PED_INDEX nearbyPeds[10]
INT i, iNearCount = GET_PED_NEARBY_PEDS(soldier.hPed, nearbyPeds)
REPEAT iNearCount i
IF DOES_ENTITY_EXIST(nearbyPeds[i])
// IF IS_PED_INJURED(nearbyPeds[i]) //OR IS_PED_WRITHING(nearbyPeds[i])
IF HAS_ENTITY_BEEN_DAMAGED_BY_ENTITY(nearbyPeds[i], soldier.hPed)
AND NOT HAS_ENTITY_BEEN_DAMAGED_BY_WEAPON(nearbyPeds[i], WEAPONTYPE_INVALID, GENERALWEAPON_TYPE_ANYMELEE)
CLEAR_ENTITY_LAST_DAMAGE_ENTITY(nearbyPeds[i])
RETURN TRUE
ENDIF
// ENDIF
ENDIF
ENDREPEAT
RETURN FALSE
ENDFUNC
PROC Private_UpdateSoldier(structSoldier& soldier)
IF IS_PED_UNINJURED(PLAYER_PED_ID()) AND soldier.eState <> SOLDIER_NULL
enumCharacterList ePlayerChar = GET_CURRENT_PLAYER_PED_ENUM()
// Set available for switch/override if within 100m
IF IS_PLAYER_PED_PLAYABLE(soldier.eChar) AND NOT IS_BIT_SET(g_bitfieldBattleBuddyAvailable, ENUM_TO_INT(soldier.eChar))
IF IS_PED_UNINJURED(soldier.hPed)
IF IS_ENTITY_AT_ENTITY(soldier.hPed, PLAYER_PED_ID(), <<100.0, 100.0, 100.0>>)
OR PLAYER_PED_ID() = soldier.hPed
Private_SetSoldierAvailable(soldier)
ENDIF
ENDIF
ENDIF
// Load anims
Private_LoadSoldierArrestAnims()
// Shout during combat
IF ePlayerChar <> soldier.eChar
IF NOT IS_PED_INJURED(soldier.hPed)
IF IS_ANY_SPEECH_PLAYING(soldier.hPed)
soldier.iShoutTimer = GET_GAME_TIMER() + 7000
ELIF IS_PED_IN_COMBAT(soldier.hPed)
IF Private_HasSoldierShotAnyPed(soldier)
CPRINTLN(DEBUG_FRIENDS, "PLAY_PED_AMBIENT_SPEECH_NATIVE(", GetLabel_enumCharacterList(soldier.eChar), ", \"SHOOT\") - Damaged ped ", GET_FRAME_COUNT())
PLAY_PED_AMBIENT_SPEECH_NATIVE(soldier.hPed, "SHOOT", AUDIO_SPEECH_GET_PARAM_STRING_FROM_ENUM(SPEECH_PARAMS_SHOUTED))
soldier.bWasInCombat = TRUE
// ELIF soldier.iShoutTimer < GET_GAME_TIMER()
// CPRINTLN(DEBUG_FRIENDS, "PLAY_PED_AMBIENT_SPEECH_NATIVE(", GetLabel_enumCharacterList(soldier.eChar), ", \"SHOOT\") - Idle ", GET_FRAME_COUNT())
// PLAY_PED_AMBIENT_SPEECH_NATIVE(soldier.hPed, "SHOOT", AUDIO_SPEECH_GET_PARAM_STRING_FROM_ENUM(SPEECH_PARAMS_SHOUTED))
ENDIF
ELIF soldier.bWasInCombat
CPRINTLN(DEBUG_FRIENDS, "PLAY_PED_AMBIENT_SPEECH_NATIVE(", GetLabel_enumCharacterList(soldier.eChar), ", \"SHOOT\") - Idle ", GET_FRAME_COUNT())
PLAY_PED_AMBIENT_SPEECH_NATIVE(soldier.hPed, "KILLED_ALL", AUDIO_SPEECH_GET_PARAM_STRING_FROM_ENUM(SPEECH_PARAMS_SHOUTED))
soldier.bWasInCombat = FALSE
ENDIF
ENDIF
ENDIF
// Process state
SWITCH soldier.eState
//------------------------------------------------------------------------------------------------- SOLDIER_CREATE_IN_CAR // TODO: Switching
CASE SOLDIER_CREATE_IN_CAR
IF soldier.bInitState
soldier.hArrivalNode = Private_GetNearestDestNode(GET_ENTITY_COORDS(PLAYER_PED_ID()), soldier.vArrivalPos)
IF IS_VEHICLE_NODE_ID_VALID(soldier.hArrivalNode)
soldier.vArrivalPos = Private_GetNodeParkingPos(GET_ENTITY_COORDS(PLAYER_PED_ID()), soldier.vArrivalPos)
ENDIF
soldier.bInitState = FALSE
ENDIF
IF Private_CreateSoldierInVehicle(soldier)
Private_SetSoldierInitialWeapons(soldier)
Private_SetSoldierExtraHealth(soldier)
IF NOT Private_CheckBehaviourFlag(BBF_DriveInConvoy)
Private_SetSoldierState(soldier, SOLDIER_ARRIVE_IN_CAR)
ELSE
Private_SetSoldierState(soldier, SOLDIER_FOLLOW)
ENDIF
ELSE
soldier.iCreateAttempts++
ENDIF
BREAK
//------------------------------------------------------------------------------------------------- SOLDIER_CREATE_ON_FOOT // TODO: Switching
CASE SOLDIER_CREATE_ON_FOOT
IF Private_CreateSoldierOnFoot(soldier)
Private_SetSoldierInitialWeapons(soldier)
Private_SetSoldierExtraHealth(soldier)
Private_SetSoldierState(soldier, SOLDIER_APPROACH)
ELIF soldier.iCreateAttempts < 30*5
soldier.iCreateAttempts++
ELSE
Private_CleanupSoldier(soldier, MC_Release)
DEACTIVATE_CHAR_CONNECTIONS(soldier.eChar)
#IF IS_DEBUG_BUILD
DEBUG_PRINT_ALL_CONNECTIONS()
#ENDIF
ENDIF
BREAK
//------------------------------------------------------------------------------------------------- SOLDIER_ARRIVE_IN_CAR
CASE SOLDIER_ARRIVE_IN_CAR
IF IS_PED_INJURED(soldier.hPed)
Private_SetSoldierState(soldier, SOLDIER_FAIL_INJURED)
ELIF Private_IsSoldierGlobalFlagSet(soldier, g_bitfieldBattleBuddyOverridden)
Private_SetSoldierState(soldier, SOLDIER_OVERRIDDEN)
ELIF soldier.hPed = PLAYER_PED_ID()
Private_SetSoldierState(soldier, SOLDIER_PLAYER)
ELIF Private_IsSoldierOutOfRange(soldier)
Private_SetSoldierState(soldier, SOLDIER_FAIL_LOST)
ELIF Private_IsPlayerArrested()
Private_SetSoldierState(soldier, SOLDIER_ARREST)
ELIF Private_CanSoldierExitArrival(soldier)
IF Private_CanSoldierEnterParachute(soldier)
Private_SetSoldierState(soldier, SOLDIER_PARACHUTE)
ELIF Private_CanSoldierEnterCombat(soldier)
Private_SetSoldierState(soldier, SOLDIER_COMBAT)
ELIF Private_CanSoldierJoinGroup(soldier)
Private_SetSoldierState(soldier, SOLDIER_GROUP)
ELIF Private_ShouldSoldierFollow(soldier)
Private_SetSoldierState(soldier, SOLDIER_FOLLOW)
ELSE
Private_SetSoldierState(soldier, SOLDIER_APPROACH)
ENDIF
ELSE
Private_UpdateSoldierArriveInCarTasks(soldier)
ENDIF
BREAK
//------------------------------------------------------------------------------------------------- SOLDIER_APPROACH
CASE SOLDIER_APPROACH
IF IS_PED_INJURED(soldier.hPed)
Private_SetSoldierState(soldier, SOLDIER_FAIL_INJURED)
ELIF Private_IsSoldierGlobalFlagSet(soldier, g_bitfieldBattleBuddyOverridden)
Private_SetSoldierState(soldier, SOLDIER_OVERRIDDEN)
ELIF soldier.hPed = PLAYER_PED_ID()
Private_SetSoldierState(soldier, SOLDIER_PLAYER)
ELIF Private_IsSoldierOutOfRange(soldier)
Private_SetSoldierState(soldier, SOLDIER_FAIL_LOST)
ELIF Private_IsPlayerArrested()
Private_SetSoldierState(soldier, SOLDIER_ARREST)
ELIF Private_ShouldSoldierBePassenger(soldier)
Private_SetSoldierState(soldier, SOLDIER_PASSENGER)
ELIF Private_CanSoldierEnterParachute(soldier)
Private_SetSoldierState(soldier, SOLDIER_PARACHUTE)
ELIF Private_CanSoldierEnterCombat(soldier)
Private_SetSoldierState(soldier, SOLDIER_COMBAT)
ELIF Private_CanSoldierJoinGroup(soldier)
Private_SetSoldierState(soldier, SOLDIER_GROUP)
ELIF Private_ShouldSoldierFollow(soldier)
Private_SetSoldierState(soldier, SOLDIER_FOLLOW)
ELSE
Private_UpdateSoldierApproachTasks(soldier)
ENDIF
BREAK
//------------------------------------------------------------------------------------------------- SOLDIER_FOLLOW
CASE SOLDIER_FOLLOW
IF IS_PED_INJURED(soldier.hPed)
Private_SetSoldierState(soldier, SOLDIER_FAIL_INJURED)
ELIF Private_IsSoldierGlobalFlagSet(soldier, g_bitfieldBattleBuddyOverridden)
Private_SetSoldierState(soldier, SOLDIER_OVERRIDDEN)
ELIF soldier.hPed = PLAYER_PED_ID()
Private_SetSoldierState(soldier, SOLDIER_PLAYER)
ELIF Private_IsSoldierOutOfRange(soldier)
Private_SetSoldierState(soldier, SOLDIER_FAIL_LOST)
ELIF Private_IsPlayerArrested()
Private_SetSoldierState(soldier, SOLDIER_ARREST)
ELIF Private_ShouldSoldierBePassenger(soldier)
Private_SetSoldierState(soldier, SOLDIER_PASSENGER)
ELIF Private_CanSoldierEnterParachute(soldier)
Private_SetSoldierState(soldier, SOLDIER_PARACHUTE)
ELIF Private_CanSoldierEnterCombat(soldier)
Private_SetSoldierState(soldier, SOLDIER_COMBAT)
ELIF Private_CanSoldierJoinGroup(soldier)
Private_SetSoldierState(soldier, SOLDIER_GROUP)
ELIF NOT Private_ShouldSoldierFollow(soldier)
Private_SetSoldierState(soldier, SOLDIER_APPROACH)
ELSE
Private_UpdateSoldierFollowTasks(soldier)
ENDIF
BREAK
//------------------------------------------------------------------------------------------------- SOLDIER_GROUP
CASE SOLDIER_GROUP
IF IS_PED_INJURED(soldier.hPed)
Private_SetSoldierState(soldier, SOLDIER_FAIL_INJURED)
ELIF Private_IsSoldierGlobalFlagSet(soldier, g_bitfieldBattleBuddyOverridden)
Private_SetSoldierState(soldier, SOLDIER_OVERRIDDEN)
ELIF soldier.hPed = PLAYER_PED_ID()
Private_SetSoldierState(soldier, SOLDIER_PLAYER)
ELIF Private_IsSoldierOutOfRange(soldier)
Private_SetSoldierState(soldier, SOLDIER_FAIL_LOST)
ELIF Private_IsPlayerArrested()
Private_SetSoldierState(soldier, SOLDIER_ARREST)
ELIF Private_ShouldSoldierBePassenger(soldier)
Private_SetSoldierState(soldier, SOLDIER_PASSENGER)
ELIF Private_CanSoldierEnterParachute(soldier)
Private_SetSoldierState(soldier, SOLDIER_PARACHUTE)
ELIF Private_CanSoldierEnterCombat(soldier)
Private_SetSoldierState(soldier, SOLDIER_COMBAT)
ELIF NOT Private_CanSoldierJoinGroup(soldier, TRUE)
IF Private_ShouldSoldierFollow(soldier)
Private_SetSoldierState(soldier, SOLDIER_FOLLOW)
ELSE
Private_SetSoldierState(soldier, SOLDIER_APPROACH)
ENDIF
ELSE
// Private_SetSoldierAvailable(soldier)
Private_UpdateSoldierGroupTasks(soldier)
ENDIF
BREAK
//------------------------------------------------------------------------------------------------- SOLDIER_COMBAT
CASE SOLDIER_COMBAT
IF IS_PED_INJURED(soldier.hPed)
Private_SetSoldierState(soldier, SOLDIER_FAIL_INJURED)
ELIF Private_IsSoldierGlobalFlagSet(soldier, g_bitfieldBattleBuddyOverridden)
Private_SetSoldierState(soldier, SOLDIER_OVERRIDDEN)
ELIF soldier.hPed = PLAYER_PED_ID()
Private_SetSoldierState(soldier, SOLDIER_PLAYER)
ELIF Private_IsSoldierOutOfRange(soldier)
Private_SetSoldierState(soldier, SOLDIER_FAIL_LOST)
ELIF Private_IsPlayerArrested()
Private_SetSoldierState(soldier, SOLDIER_ARREST)
ELIF Private_ShouldSoldierBePassenger(soldier)
Private_SetSoldierState(soldier, SOLDIER_PASSENGER)
ELIF Private_CanSoldierEnterParachute(soldier)
Private_SetSoldierState(soldier, SOLDIER_PARACHUTE)
ELIF NOT Private_CanSoldierEnterCombat(soldier)
IF Private_CanSoldierJoinGroup(soldier)
Private_SetSoldierState(soldier, SOLDIER_GROUP)
ELIF Private_ShouldSoldierFollow(soldier)
Private_SetSoldierState(soldier, SOLDIER_FOLLOW)
ELSE
Private_SetSoldierState(soldier, SOLDIER_APPROACH)
ENDIF
ELSE
// Private_SetSoldierAvailable(soldier)
Private_UpdateSoldierCombatTasks(soldier)
ENDIF
BREAK
//------------------------------------------------------------------------------------------------- SOLDIER_PARACHUTE
CASE SOLDIER_PARACHUTE
IF IS_PED_INJURED(soldier.hPed)
Private_SetSoldierState(soldier, SOLDIER_FAIL_INJURED)
ELIF Private_IsSoldierGlobalFlagSet(soldier, g_bitfieldBattleBuddyOverridden)
Private_SetSoldierState(soldier, SOLDIER_OVERRIDDEN)
ELIF soldier.hPed = PLAYER_PED_ID()
Private_SetSoldierState(soldier, SOLDIER_PLAYER)
ELIF Private_IsSoldierOutOfRange(soldier)
Private_SetSoldierState(soldier, SOLDIER_FAIL_LOST)
ELIF Private_IsPlayerArrested()
Private_SetSoldierState(soldier, SOLDIER_ARREST)
ELIF Private_ShouldSoldierBePassenger(soldier)
Private_SetSoldierState(soldier, SOLDIER_PASSENGER)
ELIF Private_CanSoldierExitParachute(soldier)
IF Private_CanSoldierEnterCombat(soldier)
Private_SetSoldierState(soldier, SOLDIER_COMBAT)
ELIF Private_CanSoldierJoinGroup(soldier)
Private_SetSoldierState(soldier, SOLDIER_GROUP)
ELIF Private_ShouldSoldierFollow(soldier)
Private_SetSoldierState(soldier, SOLDIER_FOLLOW)
ELSE
Private_SetSoldierState(soldier, SOLDIER_APPROACH)
ENDIF
ELSE
// Private_SetSoldierAvailable(soldier)
Private_UpdateSoldierParachuteTasks(soldier)
ENDIF
BREAK
//------------------------------------------------------------------------------------------------- SOLDIER_PASSENGER
CASE SOLDIER_PASSENGER
IF IS_PED_INJURED(soldier.hPed)
Private_SetSoldierState(soldier, SOLDIER_FAIL_INJURED)
ELIF Private_IsSoldierGlobalFlagSet(soldier, g_bitfieldBattleBuddyOverridden)
Private_SetSoldierState(soldier, SOLDIER_OVERRIDDEN)
ELIF soldier.hPed = PLAYER_PED_ID()
Private_SetSoldierState(soldier, SOLDIER_PLAYER)
ELIF Private_IsSoldierOutOfRange(soldier)
Private_SetSoldierState(soldier, SOLDIER_FAIL_LOST)
ELIF Private_IsPlayerArrested()
Private_SetSoldierState(soldier, SOLDIER_ARREST)
ELIF NOT Private_ShouldSoldierBePassenger(soldier)
IF Private_CanSoldierEnterParachute(soldier)
Private_SetSoldierState(soldier, SOLDIER_PARACHUTE)
ELIF Private_CanSoldierEnterCombat(soldier)
Private_SetSoldierState(soldier, SOLDIER_COMBAT)
ELIF Private_CanSoldierJoinGroup(soldier)
Private_SetSoldierState(soldier, SOLDIER_GROUP)
ELIF Private_ShouldSoldierFollow(soldier)
Private_SetSoldierState(soldier, SOLDIER_FOLLOW)
ELSE
Private_SetSoldierState(soldier, SOLDIER_APPROACH)
ENDIF
ELSE
Private_UpdateSoldierPassengerTasks(soldier)
ENDIF
BREAK
//------------------------------------------------------------------------------------------------- SOLDIER_PLAYER
CASE SOLDIER_PLAYER
IF IS_PED_INJURED(soldier.hPed)
Private_SetSoldierState(soldier, SOLDIER_FAIL_INJURED)
ELIF Private_IsSoldierGlobalFlagSet(soldier, g_bitfieldBattleBuddyOverridden)
Private_SetSoldierState(soldier, SOLDIER_OVERRIDDEN)
ELIF soldier.hPed <> PLAYER_PED_ID()
SET_ENTITY_AS_MISSION_ENTITY(soldier.hPed, TRUE, TRUE)
SET_PED_INFINITE_AMMO(soldier.hPed, FALSE)
IF Private_IsSoldierOutOfRange(soldier)
Private_SetSoldierState(soldier, SOLDIER_FAIL_LOST)
ELIF Private_IsPlayerArrested()
Private_SetSoldierState(soldier, SOLDIER_ARREST)
ELIF Private_ShouldSoldierBePassenger(soldier)
Private_SetSoldierState(soldier, SOLDIER_PASSENGER)
ELIF Private_CanSoldierEnterParachute(soldier)
Private_SetSoldierState(soldier, SOLDIER_PARACHUTE)
ELIF Private_CanSoldierEnterCombat(soldier)
Private_SetSoldierState(soldier, SOLDIER_COMBAT)
ELIF Private_CanSoldierJoinGroup(soldier)
Private_SetSoldierState(soldier, SOLDIER_GROUP)
ELIF Private_ShouldSoldierFollow(soldier)
Private_SetSoldierState(soldier, SOLDIER_FOLLOW)
ELSE
Private_SetSoldierState(soldier, SOLDIER_APPROACH)
ENDIF
ELSE
// Private_SetSoldierAvailable(soldier)
soldier.bInitState = FALSE
soldier.bRequestGreetingDialogue = FALSE
ENDIF
BREAK
//------------------------------------------------------------------------------------------------- SOLDIER_OVERRIDDEN
CASE SOLDIER_OVERRIDDEN
IF IS_PED_INJURED(soldier.hPed)
Private_SetSoldierState(soldier, SOLDIER_FAIL_INJURED)
ELIF NOT Private_IsSoldierGlobalFlagSet(soldier, g_bitfieldBattleBuddyOverridden)
IF soldier.eChar = ePlayerChar
Private_SetSoldierState(soldier, SOLDIER_PLAYER)
ELSE
SET_ENTITY_AS_MISSION_ENTITY(soldier.hPed, TRUE, TRUE)
IF Private_IsSoldierOutOfRange(soldier)
Private_SetSoldierState(soldier, SOLDIER_FAIL_LOST)
ELIF Private_IsPlayerArrested()
Private_SetSoldierState(soldier, SOLDIER_ARREST)
ELIF Private_ShouldSoldierBePassenger(soldier)
Private_SetSoldierState(soldier, SOLDIER_PASSENGER)
ELIF Private_CanSoldierEnterParachute(soldier)
Private_SetSoldierState(soldier, SOLDIER_PARACHUTE)
ELIF Private_CanSoldierEnterCombat(soldier)
Private_SetSoldierState(soldier, SOLDIER_COMBAT)
ELIF Private_CanSoldierJoinGroup(soldier)
Private_SetSoldierState(soldier, SOLDIER_GROUP)
ELIF Private_ShouldSoldierFollow(soldier)
Private_SetSoldierState(soldier, SOLDIER_FOLLOW)
ELSE
Private_SetSoldierState(soldier, SOLDIER_APPROACH)
ENDIF
ENDIF
ELSE
soldier.bRequestGreetingDialogue = FALSE
soldier.bInitState = FALSE
ENDIF
BREAK
//------------------------------------------------------------------------------------------------- SOLDIER_ARREST
CASE SOLDIER_ARREST
// Set as not available
CLEAR_BIT(g_bitfieldBattleBuddyAvailable, ENUM_TO_INT(soldier.eChar))
CLEAR_BIT(g_bitfieldBattleBuddyOverridden, ENUM_TO_INT(soldier.eChar))
// Play animation
IF soldier.bInitState
IF soldier.hPed <> PLAYER_PED_ID()
IF NOT IS_PED_INJURED(soldier.hPed)
IF IS_PED_ON_FOOT(soldier.hPed)
AND NOT IS_ENTITY_IN_AIR(soldier.hPed)
AND NOT IS_PED_RAGDOLL(soldier.hPed)
IF Private_LoadSoldierArrestAnims()
IF IS_PED_GROUP_MEMBER(soldier.hPed, PLAYER_GROUP_ID())
REMOVE_PED_FROM_GROUP(soldier.hPed)
ENDIF
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(soldier.hPed, TRUE)
// SET_PED_CONFIG_FLAG(soldier.hPed, PCF_PedIgnoresAnimInterruptEvents, TRUE)
SET_ENTITY_PROOFS(soldier.hPed, TRUE, TRUE, TRUE, TRUE, TRUE)
SET_PED_KEEP_TASK(soldier.hPed, TRUE)
CLEAR_PED_TASKS(soldier.hPed)
//PCF_TreatAsPlayerDuringTargeting
SEQUENCE_INDEX seq
OPEN_SEQUENCE_TASK(seq)
TASK_PLAY_ANIM(null, "random@arrests", "idle_2_hands_up", SLOW_BLEND_IN, SLOW_BLEND_OUT, -1, AF_NOT_INTERRUPTABLE)
TASK_PLAY_ANIM(null, "random@arrests", "kneeling_arrest_idle", SLOW_BLEND_IN, SLOW_BLEND_OUT, -1, AF_LOOPING|AF_NOT_INTERRUPTABLE)
CLOSE_SEQUENCE_TASK(seq)
TASK_PERFORM_SEQUENCE(soldier.hPed, seq)
CLEAR_SEQUENCE_TASK(seq)
soldier.bInitState = FALSE
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
BREAK
ENDSWITCH
ENDIF
Private_UpdateSoldierBlip(soldier)
ENDPROC
PROC Private_HandleSoldierFail(structSoldier& soldier, enumMemberCleanupStyle& eCleanup)
eCleanup = MC_Release
SWITCH soldier.eState
CASE SOLDIER_FAIL_INJURED
Private_QueueFailReason(soldier.eChar, FFR_Injured)
eCleanup = MC_Release
BREAK
CASE SOLDIER_FAIL_LOST
Private_QueueFailReason(soldier.eChar, FFR_Lost)
eCleanup = MC_Delete
BREAK
ENDSWITCH
// START_SOLDIER_FAIL_TIMER(soldier.eChar)
ENDPROC