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

1464 lines
57 KiB
Scheme
Executable File

USING "commands_debug.sch"
USING "commands_object.sch"
USING "commands_ped.sch"
USING "commands_task.sch"
USING "Script_player.sch"
USING "commands_interiors.sch"
USING "commands_path.sch"
USING "commands_camera.sch"
USING "commands_streaming.sch"
//USING "Minigame_private.sch"
ENUM ECOMPASS
ECOMPASS_NORTH = 0,
ECOMPASS_EAST,
ECOMPASS_WEST,
ECOMPASS_SOUTH
ENDENUM
ENUM ESIDESHIFT
ESIDESHIFT_LEFT = 0,
ESIDESHIFT_RIGHT,
ESIDESHIFT_NONE
ENDENUM
ENUM EACTIVESIDE
EACTIVESIDE_ONEWAY = 0,
EACTIVESIDE_OPPOSITES,
EACTIVESIDE_FOURCORNERS
ENDENUM
ENUM EACTIVEAPPROACH
EACTIVEAPPROACH_FRONT = 0,
EACTIVEAPPROACH_BACK,
EACTIVEAPPROACH_LEFT,
EACTIVEAPPROACH_RIGHT,
EACTIVEAPPROACH_ALLDIAGONALS,
EACTIVEAPPROACH_FRONTLEFT,
EACTIVEAPPROACH_FRONTRIGHT,
EACTIVEAPPROACH_BACKLEFT,
EACTIVEAPPROACH_BACKRIGHT
ENDENUM
ENUM EACTIVATIONEASE
EACTIVATIONEASE_HARD =0,
EACTIVATIONEASE_MEDIUM,
EACTIVATIONEASE_EASY
ENDENUM
PROC DrawDebug(ENTITY_INDEX anEntity, BOOL GreenLine, ESIDESHIFT ShiftDirection = ESIDESHIFT_NONE)
IF ShiftDirection = ESIDESHIFT_NONE ENDIF
IF GreenLine ENDIF
IF DOES_ENTITY_EXIST(anEntity)
#IF IS_DEBUG_BUILD
VECTOR PlayerPosition = GET_ENTITY_COORDS(PLAYER_PED_ID())
VECTOR anObjectPosition = GET_ENTITY_COORDS(anEntity)
//// FLOAT PlayerHeading = GET_ENTITY_HEADING(PLAYER_PED_ID())
//// FLOAT anObjectHeading = GET_ENTITY_HEADING(anEntity)
IF ShiftDirection = ESIDESHIFT_LEFT
anObjectPosition = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(anEntity, <<-0.5, 0, 0>>)
ELIF ShiftDirection = ESIDESHIFT_RIGHT
anObjectPosition = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(anEntity, <<0.5, 0, 0>>)
ENDIF
INTERIOR_INSTANCE_INDEX ObjectInterior = GET_INTERIOR_FROM_ENTITY(anEntity)
INTERIOR_INSTANCE_INDEX PlayerInterior = GET_INTERIOR_FROM_ENTITY(PLAYER_PED_ID())
IF ObjectInterior = PlayerInterior
IF IS_ENTITY_AT_ENTITY(anEntity, PLAYER_PED_ID(), <<7.0, 7.0, 7.0>>)
IF GreenLine = TRUE
DRAW_DEBUG_LINE(PlayerPosition, anObjectPosition, 0, 255, 0, 255) //Green Line
ELSE
DRAW_DEBUG_LINE(PlayerPosition, anObjectPosition, 255, 0, 0, 255) //Red Line
ENDIF
ENDIF
ENDIF
#ENDIF
ENDIF
ENDPROC
PROC DrawDebugPed(PED_INDEX Ped, BOOL GreenLine)
IF GreenLine ENDIF
IF NOT IS_ENTITY_DEAD(Ped)
#IF IS_DEBUG_BUILD
VECTOR PlayerPosition = GET_ENTITY_COORDS(PLAYER_PED_ID())
VECTOR PedPosition = GET_ENTITY_COORDS(Ped)
//// FLOAT PlayerHeading = GET_ENTITY_HEADING(PLAYER_PED_ID())
//// FLOAT PedHeading = GET_ENTITY_HEADING(Ped)
INTERIOR_INSTANCE_INDEX PedInterior = GET_INTERIOR_FROM_ENTITY(Ped)
INTERIOR_INSTANCE_INDEX PlayerInterior = GET_INTERIOR_FROM_ENTITY(PLAYER_PED_ID())
IF PedInterior = PlayerInterior
IF IS_ENTITY_AT_ENTITY(Ped, PLAYER_PED_ID(), <<7.0, 7.0, 7.0>>)
IF GreenLine = TRUE
DRAW_DEBUG_LINE(PlayerPosition, PedPosition, 0, 255, 0, 255) //Green Line
ELSE
DRAW_DEBUG_LINE(PlayerPosition, PedPosition, 255, 0, 0, 255) //Red Line
ENDIF
ENDIF
ENDIF
#ENDIF
ELSE
#IF IS_DEBUG_BUILD
// Display_Minigame_Assert_Message_Header()
SCRIPT_ASSERT("DrawDebugPed is using a dead ped - See Brenda")
// Display_Minigame_Assert_Message_Footer()
#ENDIF
ENDIF
ENDPROC
FUNC BOOL priv_PlayerMoveStateAcceptable(BOOL playerCanBeStill)
IF IS_PLAYER_CONTROL_ON(PLAYER_ID())
IF playerCanBeStill
IF (GET_PED_DESIRED_MOVE_BLEND_RATIO(PLAYER_PED_ID()) = PEDMOVEBLENDRATIO_WALK) OR (GET_PED_DESIRED_MOVE_BLEND_RATIO(PLAYER_PED_ID()) = PEDMOVEBLENDRATIO_STILL)
RETURN TRUE
ENDIF
ELSE
IF (GET_PED_DESIRED_MOVE_BLEND_RATIO(PLAYER_PED_ID()) = PEDMOVEBLENDRATIO_WALK)
RETURN TRUE
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL priv_ObjectActivationCriteriaMet(OBJECT_INDEX activeObject, VECTOR activationOffset, FLOAT activationAngle, FLOAT locateSize, BOOL CareIfUpright, BOOL playerCanBeStill)
FLOAT locateZ
IF locateSize < 1
locateZ = 1+0.5
ELSE
locateZ = locateSize+0.5
ENDIF
VECTOR vLocateSize = <<(locateSize+0.2), (locateSize+0.2), locateZ>>
IF NOT IS_ENTITY_DEAD(activeObject)
IF NOT IS_ENTITY_DEAD(PLAYER_PED_ID())
IF IS_ENTITY_AT_COORD(PLAYER_PED_ID(), GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(activeObject, activationOffset), vLocateSize, FALSE, TRUE)
AND IS_PED_HEADING_TOWARDS_POSITION(PLAYER_PED_ID(), GET_ENTITY_COORDS(activeObject), activationAngle)
AND priv_PlayerMoveStateAcceptable(playerCanBeStill)
AND IS_PED_ON_FOOT(PLAYER_PED_ID())
IF CareIfUpright
IF IS_ENTITY_UPRIGHT(activeObject, 60)
DrawDebug(activeObject, TRUE)
RETURN TRUE
ENDIF
ELSE
DrawDebug(activeObject, TRUE)
RETURN TRUE
ENDIF
ELSE
DrawDebug(activeObject, FALSE)
ENDIF
ENDIF
ELSE
SCRIPT_ASSERT("priv_ObjectActivationCriteriaMet: is using an object that doesn't exist")
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Gets Heading given 2 coords
/// PARAMS:
/// oldCoords - source point
/// newCoords - target point
/// invert - this is true (just keep it as true)
/// RETURNS:
/// Heading
FUNC FLOAT GET_HEADING_FROM_COORDS_LA(vector oldCoords,vector newCoords, bool invert=true)
float heading
float dX = newCoords.x - oldCoords.x
float dY = newCoords.y - oldCoords.y
if dY != 0
heading = ATAN2(dX,dY)
ELSE
if dX < 0
heading = -90
ELSE
heading = 90
ENDIF
ENDIF
//flip because for some odd reason the coders think west is a heading of 90 degrees, so this'll match the output of commands such as GET_ENTITY_HEADING()
IF invert = TRUE
heading *= -1.0
//below not necessary but helps for debugging
IF heading < 0
heading += 360.0
ENDIF
ENDIF
RETURN heading
ENDFUNC
/// PURPOSE:
/// Gets Heading from 1 entity to another
/// PARAMS:
/// oldEnd - source entity
/// newEnd - target entity
/// invert - this is true (just keep it as true)
/// RETURNS:
/// Heading
FUNC FLOAT GET_HEADING_FROM_ENTITIES_LA(entity_index oldEnd, entity_index newEnt, bool invert=true)
vector v1,v2
v1 = GET_ENTITY_COORDS(oldEnd,FALSE)
v2 = GET_ENTITY_COORDS(newEnt,FALSE)
RETURN GET_HEADING_FROM_COORDS_LA(v1,v2, invert)
ENDFUNC
FUNC BOOL priv_EntityActivationCriteriaMet(ENTITY_INDEX activeEntity, VECTOR activationOffset, FLOAT activationAngle, FLOAT locateSize, BOOL CareIfUpright, BOOL playerCanBeStill)
FLOAT locateZ
IF locateSize < 1
locateZ = 1+0.5
ELSE
locateZ = locateSize+0.5
ENDIF
VECTOR vLocateSize = <<(locateSize+0.2), (locateSize+0.2), locateZ>>
IF NOT IS_ENTITY_DEAD(activeEntity)
IF NOT IS_ENTITY_DEAD(PLAYER_PED_ID())
IF IS_ENTITY_AT_COORD(PLAYER_PED_ID(), GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(activeEntity, activationOffset), vLocateSize, FALSE, TRUE)
AND IS_PED_HEADING_TOWARDS_POSITION(PLAYER_PED_ID(), GET_ENTITY_COORDS(activeEntity), activationAngle)
AND priv_PlayerMoveStateAcceptable(playerCanBeStill)
AND IS_PED_ON_FOOT(PLAYER_PED_ID())
IF CareIfUpright
IF IS_ENTITY_UPRIGHT(activeEntity, 60)
DrawDebug(activeEntity, TRUE)
RETURN TRUE
ENDIF
ELSE
DrawDebug(activeEntity, TRUE)
RETURN TRUE
ENDIF
ELSE
DrawDebug(activeEntity, FALSE)
ENDIF
ENDIF
ELSE
SCRIPT_ASSERT("priv_ObjectActivationCriteriaMet: is using an object that doesn't exist")
ENDIF
RETURN FALSE
ENDFUNC
FUNC VECTOR APPROACH_OFFSET(ECOMPASS direction, EACTIVEAPPROACH approach, FLOAT locateSize)
VECTOR offset
SWITCH approach
CASE EACTIVEAPPROACH_FRONT
IF Direction = ECOMPASS_NORTH
Offset = <<0, LocateSize, 0>>
ELIF Direction = ECOMPASS_EAST
Offset = <<LocateSize,0 , 0>>
ELIF Direction = ECOMPASS_SOUTH
Offset = <<0, -LocateSize, 0>>
ELIF Direction = ECOMPASS_WEST
Offset = <<-LocateSize, 0, 0>>
ENDIF
BREAK
CASE EACTIVEAPPROACH_BACK
IF Direction = ECOMPASS_SOUTH
Offset = <<0, LocateSize, 0>>
ELIF Direction = ECOMPASS_WEST
Offset = <<LocateSize,0 , 0>>
ELIF Direction = ECOMPASS_NORTH
Offset = <<0, -LocateSize, 0>>
ELIF Direction = ECOMPASS_EAST
Offset = <<-LocateSize, 0, 0>>
ENDIF
BREAK
CASE EACTIVEAPPROACH_LEFT
IF Direction = ECOMPASS_WEST
Offset = <<0, LocateSize, 0>>
ELIF Direction = ECOMPASS_SOUTH
Offset = <<LocateSize,0 , 0>>
ELIF Direction = ECOMPASS_EAST
Offset = <<0, -LocateSize, 0>>
ELIF Direction = ECOMPASS_NORTH
Offset = <<-LocateSize, 0, 0>>
ENDIF
BREAK
CASE EACTIVEAPPROACH_RIGHT
IF Direction = ECOMPASS_EAST
Offset = <<0, LocateSize, 0>>
ELIF Direction = ECOMPASS_NORTH
Offset = <<LocateSize,0 , 0>>
ELIF Direction = ECOMPASS_WEST
Offset = <<0, -LocateSize, 0>>
ELIF Direction = ECOMPASS_SOUTH
Offset = <<-LocateSize, 0, 0>>
ENDIF
BREAK
//Diagonal approaches
CASE EACTIVEAPPROACH_FRONTLEFT
IF Direction = ECOMPASS_EAST
Offset = <<LocateSize, LocateSize, 0>>
ELIF Direction = ECOMPASS_NORTH
Offset = <<-LocateSize,LocateSize , 0>>
ELIF Direction = ECOMPASS_SOUTH
Offset = <<LocateSize, -LocateSize, 0>>
ELIF Direction = ECOMPASS_WEST
Offset = <<-LocateSize, -LocateSize, 0>>
ENDIF
BREAK
CASE EACTIVEAPPROACH_FRONTRIGHT
IF Direction = ECOMPASS_NORTH
Offset = <<LocateSize, LocateSize, 0>>
ELIF Direction = ECOMPASS_WEST
Offset = <<-LocateSize,LocateSize , 0>>
ELIF Direction = ECOMPASS_EAST
Offset = <<LocateSize, -LocateSize, 0>>
ELIF Direction = ECOMPASS_SOUTH
Offset = <<-LocateSize, -LocateSize, 0>>
ENDIF
BREAK
CASE EACTIVEAPPROACH_BACKLEFT
IF Direction = ECOMPASS_SOUTH
Offset = <<LocateSize, LocateSize, 0>>
ELIF Direction = ECOMPASS_EAST
Offset = <<-LocateSize,LocateSize , 0>>
ELIF Direction = ECOMPASS_WEST
Offset = <<LocateSize, -LocateSize, 0>>
ELIF Direction = ECOMPASS_NORTH
Offset = <<-LocateSize, -LocateSize, 0>>
ENDIF
BREAK
CASE EACTIVEAPPROACH_BACKRIGHT
IF Direction = ECOMPASS_WEST
Offset = <<LocateSize, LocateSize, 0>>
ELIF Direction = ECOMPASS_SOUTH
Offset = <<-LocateSize,LocateSize , 0>>
ELIF Direction = ECOMPASS_NORTH
Offset = <<LocateSize, -LocateSize, 0>>
ELIF Direction = ECOMPASS_EAST
Offset = <<-LocateSize, -LocateSize, 0>>
ENDIF
BREAK
ENDSWITCH
IF LocateSize < 0
LocateSize *=-1
ENDIF
RETURN offset
ENDFUNC
//// PURPOSE:
/// Is player activating an object by approaching it
/// PARAMS:
/// anObject - The object the player will be interacting with
/// RETURNS:
/// Returns true if the player is activating an object
FUNC BOOL IS_PLAYER_ACTIVATING_OBJECT(OBJECT_INDEX anObject, ECOMPASS Direction = ECOMPASS_SOUTH, FLOAT LocateSize = 0.4, BOOL CareIfUpright = TRUE, EACTIVESIDE activeSide = EACTIVESIDE_ONEWAY, EACTIVATIONEASE Ease_of_activation = EACTIVATIONEASE_HARD, BOOL Check_Stationary = FALSE)
// VECTOR Offset
FLOAT EaseOfActivation
IF Ease_of_activation = EACTIVATIONEASE_EASY
EaseOfActivation = 150.0
ELIF Ease_of_activation = EACTIVATIONEASE_MEDIUM
EaseOfActivation = 100.0
ELIF Ease_of_activation = EACTIVATIONEASE_HARD
EaseOfActivation = 40.0
ENDIF
// Checks will fallthru to the default activation side, EACTIVESIDE_ONEWAY
SWITCH activeSide
CASE EACTIVESIDE_FOURCORNERS
///Left Corner
IF priv_ObjectActivationCriteriaMet(anObject, APPROACH_OFFSET(direction, EACTIVEAPPROACH_LEFT, locateSize), EaseOfActivation, LocateSize, CareIfUpright, Check_stationary)
RETURN TRUE
ENDIF
//Right Corner
IF priv_ObjectActivationCriteriaMet(anObject, APPROACH_OFFSET(direction, EACTIVEAPPROACH_RIGHT, locateSize), EaseOfActivation, LocateSize, CareIfUpright, Check_stationary)
RETURN TRUE
ENDIF
//front left Diagonals Corner
IF priv_ObjectActivationCriteriaMet(anObject, APPROACH_OFFSET(direction, EACTIVEAPPROACH_FRONTLEFT, locateSize), EaseOfActivation, LocateSize, CareIfUpright, Check_stationary)
RETURN TRUE
ENDIF
//front right Diagonals Corner
IF priv_ObjectActivationCriteriaMet(anObject, APPROACH_OFFSET(direction, EACTIVEAPPROACH_FRONTRIGHT, locateSize), EaseOfActivation, LocateSize, CareIfUpright, Check_stationary)
RETURN TRUE
ENDIF
//back left Diagonals Corner
IF priv_ObjectActivationCriteriaMet(anObject, APPROACH_OFFSET(direction, EACTIVEAPPROACH_BACKLEFT, locateSize), EaseOfActivation, LocateSize, CareIfUpright, Check_stationary)
RETURN TRUE
ENDIF
//back right Diagonals Corner
IF priv_ObjectActivationCriteriaMet(anObject, APPROACH_OFFSET(direction, EACTIVEAPPROACH_BACKRIGHT, locateSize), EaseOfActivation, LocateSize, CareIfUpright, Check_stationary)
RETURN TRUE
ENDIF
FALLTHRU
CASE EACTIVESIDE_OPPOSITES
IF priv_ObjectActivationCriteriaMet(anObject, APPROACH_OFFSET(direction, EACTIVEAPPROACH_BACK, locateSize), EaseOfActivation, LocateSize, CareIfUpright, Check_stationary)
RETURN TRUE
ENDIF
FALLTHRU
CASE EACTIVESIDE_ONEWAY
IF priv_ObjectActivationCriteriaMet(anObject, APPROACH_OFFSET(direction, EACTIVEAPPROACH_FRONT, locateSize), EaseOfActivation, LocateSize, CareIfUpright, Check_stationary)
RETURN TRUE
ENDIF
BREAK
ENDSWITCH
RETURN FALSE
ENDFUNC
//// PURPOSE:
/// Is player activating an object by approaching it
/// PARAMS:
/// anObject - The object the player will be interacting with
/// RETURNS:
/// Returns true if the player is activating an object
FUNC BOOL IS_PLAYER_ACTIVATING_ENTITY(ENTITY_INDEX anEntity, ECOMPASS Direction = ECOMPASS_SOUTH, FLOAT LocateSize = 0.4, BOOL CareIfUpright = TRUE, EACTIVESIDE activeSide = EACTIVESIDE_ONEWAY, EACTIVATIONEASE Ease_of_activation = EACTIVATIONEASE_HARD, BOOL Check_Stationary = FALSE)
// VECTOR Offset
FLOAT EaseOfActivation
IF Ease_of_activation = EACTIVATIONEASE_EASY
EaseOfActivation = 150.0
ELIF Ease_of_activation = EACTIVATIONEASE_MEDIUM
EaseOfActivation = 100.0
ELIF Ease_of_activation = EACTIVATIONEASE_HARD
EaseOfActivation = 40.0
ENDIF
// Checks will fallthru to the default activation side, EACTIVESIDE_ONEWAY
SWITCH activeSide
CASE EACTIVESIDE_FOURCORNERS
///Left Corner
IF priv_EntityActivationCriteriaMet(anEntity, APPROACH_OFFSET(direction, EACTIVEAPPROACH_LEFT, locateSize), EaseOfActivation, LocateSize, CareIfUpright, Check_stationary)
RETURN TRUE
ENDIF
//Right Corner
IF priv_EntityActivationCriteriaMet(anEntity, APPROACH_OFFSET(direction, EACTIVEAPPROACH_RIGHT, locateSize), EaseOfActivation, LocateSize, CareIfUpright, Check_stationary)
RETURN TRUE
ENDIF
//Front left diagonal corner
IF priv_EntityActivationCriteriaMet(anEntity, APPROACH_OFFSET(direction, EACTIVEAPPROACH_FRONTLEFT, locateSize), EaseOfActivation, LocateSize, CareIfUpright, Check_stationary)
RETURN TRUE
ENDIF
//Front right diagonal corner
IF priv_EntityActivationCriteriaMet(anEntity, APPROACH_OFFSET(direction, EACTIVEAPPROACH_FRONTRIGHT, locateSize), EaseOfActivation, LocateSize, CareIfUpright, Check_stationary)
RETURN TRUE
ENDIF
//back left diagonal corner
IF priv_EntityActivationCriteriaMet(anEntity, APPROACH_OFFSET(direction, EACTIVEAPPROACH_BACKLEFT, locateSize), EaseOfActivation, LocateSize, CareIfUpright, Check_stationary)
RETURN TRUE
ENDIF
//back right diagonal corner
IF priv_EntityActivationCriteriaMet(anEntity, APPROACH_OFFSET(direction, EACTIVEAPPROACH_BACKRIGHT, locateSize), EaseOfActivation, LocateSize, CareIfUpright, Check_stationary)
RETURN TRUE
ENDIF
FALLTHRU
CASE EACTIVESIDE_OPPOSITES
IF priv_EntityActivationCriteriaMet(anEntity, APPROACH_OFFSET(direction, EACTIVEAPPROACH_BACK, locateSize), EaseOfActivation, LocateSize, CareIfUpright, Check_stationary)
RETURN TRUE
ENDIF
FALLTHRU
CASE EACTIVESIDE_ONEWAY
IF priv_EntityActivationCriteriaMet(anEntity, APPROACH_OFFSET(direction, EACTIVEAPPROACH_FRONT, locateSize), EaseOfActivation, LocateSize, CareIfUpright, Check_stationary)
RETURN TRUE
ENDIF
BREAK
ENDSWITCH
RETURN FALSE
ENDFUNC
FUNC BOOL priv_PedActivationCriteriaMet(PED_INDEX activePed, VECTOR activationOffset, FLOAT activationAngle, FLOAT locateSize, BOOL playerCanBeStill)
FLOAT locateZ
IF locateSize < 1
locateZ = 1+0.5
ELSE
locateZ = locateSize+0.5
ENDIF
VECTOR vLocateSize = <<(locateSize+0.2), (locateSize+0.2), locateZ>>
IF NOT IS_PED_INJURED(activePed)
// Is player close enough to ped to be activated
IF IS_ENTITY_AT_COORD(PLAYER_PED_ID(), GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(activePed, activationOffset), vLocateSize, FALSE, TRUE)
// Is player facing towards active ped's position
AND IS_PED_HEADING_TOWARDS_POSITION(PLAYER_PED_ID(), GET_ENTITY_COORDS(activePed), activationAngle)
// Is the player's move state acceptable for this activation
AND priv_PlayerMoveStateAcceptable(playerCanBeStill)
// Other checks
AND NOT IS_PED_RAGDOLL(activePed)
AND NOT IS_PED_GETTING_UP(activePed)
DrawDebugPed(activePed, TRUE)
RETURN TRUE
ELSE
DrawDebugPed(activePed, FALSE)
ENDIF
ELSE
SCRIPT_ASSERT("priv_PedActivationCriteriaMet: is using a ped that's dead")
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Is player activating a ped by approaching it
/// PARAMS:
/// aPed - The ped the player will be interacting with
/// RETURNS:
/// Returns true if the player is interacting with a ped
FUNC BOOL IS_PLAYER_ACTIVATING_PED(PED_INDEX aPed, ECOMPASS Direction = ECOMPASS_NORTH, FLOAT LocateSize = 0.5, EACTIVESIDE activeSide = EACTIVESIDE_ONEWAY, EACTIVATIONEASE Ease_of_activation = EACTIVATIONEASE_HARD, BOOL Check_stationary = FALSE)
// VECTOR Offset
FLOAT EaseOfActivation
IF Ease_of_activation = EACTIVATIONEASE_EASY
EaseOfActivation = 150.0
ELIF Ease_of_activation = EACTIVATIONEASE_MEDIUM
EaseOfActivation = 100.0
ELIF Ease_of_activation = EACTIVATIONEASE_HARD
EaseOfActivation = 40.0
ENDIF
// Checks will fallthru to the default activation side, EACTIVESIDE_ONEWAY
SWITCH activeSide
CASE EACTIVESIDE_FOURCORNERS
///Left Corner
IF priv_PedActivationCriteriaMet(aPed, APPROACH_OFFSET(direction, EACTIVEAPPROACH_LEFT, locateSize), EaseOfActivation, LocateSize, Check_stationary)
RETURN TRUE
ENDIF
//Right Corner
IF priv_PedActivationCriteriaMet(aPed, APPROACH_OFFSET(direction, EACTIVEAPPROACH_RIGHT, locateSize), EaseOfActivation, LocateSize, Check_stationary)
RETURN TRUE
ENDIF
//Front left diagonal corner
IF priv_PedActivationCriteriaMet(aPed, APPROACH_OFFSET(direction, EACTIVEAPPROACH_FRONTLEFT, locateSize), EaseOfActivation, LocateSize, Check_stationary)
RETURN TRUE
ENDIF
//Front right diagonal corner
IF priv_PedActivationCriteriaMet(aPed, APPROACH_OFFSET(direction, EACTIVEAPPROACH_FRONTRIGHT, locateSize), EaseOfActivation, LocateSize, Check_stationary)
RETURN TRUE
ENDIF
//back left diagonal corner
IF priv_PedActivationCriteriaMet(aPed, APPROACH_OFFSET(direction, EACTIVEAPPROACH_BACKLEFT, locateSize), EaseOfActivation, LocateSize, Check_stationary)
RETURN TRUE
ENDIF
//back right diagonal corner
IF priv_PedActivationCriteriaMet(aPed, APPROACH_OFFSET(direction, EACTIVEAPPROACH_BACKRIGHT, locateSize), EaseOfActivation, LocateSize, Check_stationary)
RETURN TRUE
ENDIF
FALLTHRU
CASE EACTIVESIDE_OPPOSITES
IF priv_PedActivationCriteriaMet(aPed, APPROACH_OFFSET(direction, EACTIVEAPPROACH_BACK, locateSize), EaseOfActivation, LocateSize, Check_stationary)
RETURN TRUE
ENDIF
FALLTHRU
CASE EACTIVESIDE_ONEWAY
IF priv_PedActivationCriteriaMet(aPed, APPROACH_OFFSET(direction, EACTIVEAPPROACH_FRONT, locateSize), EaseOfActivation, LocateSize, Check_stationary)
RETURN TRUE
ENDIF
BREAK
ENDSWITCH
RETURN FALSE
ENDFUNC
FUNC VECTOR GET_OFFSET_FROM_COORD_IN_WORLD_COORDS(VECTOR vPosition, FLOAT fCoordHeading, VECTOR vOffset)
VECTOR vOffsetRotated
FLOAT fHeading = fCoordHeading
FLOAT CosHead = COS(fHeading)
FLOAT SinHead = SIN(fHeading)
vOffsetRotated.x = (vOffset.x*CosHead) + (vOffset.y*SinHead)
vOffsetRotated.y = (vOffset.y*CosHead) - (vOffset.x*SinHead)
VECTOR vFinalPos = vPosition + vOffsetRotated
RETURN vFinalPos
ENDFUNC
FUNC BOOL priv_CoordActivationCriteriaMet(VECTOR activeCoord, FLOAT activeHeading, VECTOR activationOffset, FLOAT activationAngle, FLOAT locateSize, BOOL playerCanBeStill)
FLOAT locateZ
IF locateSize < 1
locateZ = 1+0.5
ELSE
locateZ = locateSize+0.5
ENDIF
VECTOR vLocate = <<(locateSize+0.2), (locateSize+0.2), locateZ>>
IF IS_ENTITY_AT_COORD(PLAYER_PED_ID(), GET_OFFSET_FROM_COORD_IN_WORLD_COORDS(activeCoord, activeHeading, activationOffset), vLocate, FALSE, TRUE)
AND IS_PED_HEADING_TOWARDS_POSITION(PLAYER_PED_ID(), activeCoord, activationAngle)
AND priv_PlayerMoveStateAcceptable(playerCanBeStill)
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
//// PURPOSE:
/// Is player activating an object by approaching it
/// PARAMS:
/// anObject - The object the player will be interacting with
/// RETURNS:
/// Returns true if the player is activating an object
FUNC BOOL IS_PLAYER_ACTIVATING_COORD(VECTOR Coord, FLOAT CoordHeading, ECOMPASS Direction = ECOMPASS_NORTH, FLOAT LocateSize = 0.4, EACTIVESIDE activeSide = EACTIVESIDE_ONEWAY, EACTIVATIONEASE Ease_of_activation = EACTIVATIONEASE_HARD, BOOL Check_stationary = FALSE)
FLOAT EaseOfActivation
IF Ease_of_activation = EACTIVATIONEASE_EASY
EaseOfActivation = 150.0
ELIF Ease_of_activation = EACTIVATIONEASE_MEDIUM
EaseOfActivation = 100.0
ELIF Ease_of_activation = EACTIVATIONEASE_HARD
EaseOfActivation = 40.0
ENDIF
// Checks will fallthru to the default activation side, EACTIVESIDE_ONEWAY
SWITCH activeSide
CASE EACTIVESIDE_FOURCORNERS
///Left Corner
IF priv_CoordActivationCriteriaMet(Coord, CoordHeading, APPROACH_OFFSET(direction, EACTIVEAPPROACH_LEFT, locateSize), EaseOfActivation, LocateSize, Check_stationary)
RETURN TRUE
ENDIF
//Right Corner
IF priv_CoordActivationCriteriaMet(Coord, CoordHeading, APPROACH_OFFSET(direction, EACTIVEAPPROACH_RIGHT, locateSize), EaseOfActivation, LocateSize, Check_stationary)
RETURN TRUE
ENDIF
//Front left diagonal corner
IF priv_CoordActivationCriteriaMet(Coord, CoordHeading, APPROACH_OFFSET(direction, EACTIVEAPPROACH_FRONTLEFT, locateSize), EaseOfActivation, LocateSize, Check_stationary)
RETURN TRUE
ENDIF
//Front right diagonal corner
IF priv_CoordActivationCriteriaMet(Coord, CoordHeading, APPROACH_OFFSET(direction, EACTIVEAPPROACH_FRONTRIGHT, locateSize), EaseOfActivation, LocateSize, Check_stationary)
RETURN TRUE
ENDIF
//back left diagonal corner
IF priv_CoordActivationCriteriaMet(Coord, CoordHeading, APPROACH_OFFSET(direction, EACTIVEAPPROACH_BACKLEFT, locateSize), EaseOfActivation, LocateSize, Check_stationary)
RETURN TRUE
ENDIF
//back right diagonal corner
IF priv_CoordActivationCriteriaMet(Coord, CoordHeading, APPROACH_OFFSET(direction, EACTIVEAPPROACH_BACKRIGHT, locateSize), EaseOfActivation, LocateSize, Check_stationary)
RETURN TRUE
ENDIF
FALLTHRU
CASE EACTIVESIDE_OPPOSITES
IF priv_CoordActivationCriteriaMet(Coord, CoordHeading, APPROACH_OFFSET(direction, EACTIVEAPPROACH_BACK, locateSize), EaseOfActivation, LocateSize, Check_stationary)
RETURN TRUE
ENDIF
FALLTHRU
CASE EACTIVESIDE_ONEWAY
IF priv_CoordActivationCriteriaMet(Coord, CoordHeading, APPROACH_OFFSET(direction, EACTIVEAPPROACH_FRONT, locateSize), EaseOfActivation, LocateSize, Check_stationary)
RETURN TRUE
ENDIF
BREAK
ENDSWITCH
RETURN FALSE
ENDFUNC
FUNC BOOL priv_DoorActivationCriteriaMet(OBJECT_INDEX activeObject, VECTOR activationOffset, ESIDESHIFT ShiftDirection, FLOAT activationAngle, FLOAT locateSize, BOOL playerCanBeStill)
VECTOR shiftOffset
FLOAT locateZ
IF locateSize < 1
locateZ = 1+0.5
ELSE
locateZ = locateSize+0.5
ENDIF
VECTOR vLocate = <<(locateSize+0.2), (locateSize+0.2), locateZ>>
IF DOES_ENTITY_EXIST(activeObject)
IF ShiftDirection = ESIDESHIFT_LEFT
shiftOffset = <<-0.5, 0, 0>>
ELIF ShiftDirection = ESIDESHIFT_RIGHT
shiftOffset = <<0.5, 0, 0>>
ENDIF
IF IS_ENTITY_AT_COORD(PLAYER_PED_ID(), GET_OFFSET_FROM_COORD_IN_WORLD_COORDS(GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(activeObject, shiftOffset), GET_ENTITY_HEADING(activeObject), activationOffset), vLocate, FALSE, TRUE)
AND IS_PED_HEADING_TOWARDS_POSITION(PLAYER_PED_ID(), GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(activeObject, shiftOffset), activationAngle)
AND priv_PlayerMoveStateAcceptable(playerCanBeStill)
DrawDebug(activeObject, TRUE, ShiftDirection)
RETURN TRUE
ELSE
DrawDebug(activeObject, FALSE, ShiftDirection)
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Is the player activiating a door
/// PARAMS:
/// anObject - The door object
/// Direction - Which compass point is the door situated, defaulted to NORTH
/// ShiftDirection - Does the door open to the left or to the right. The origin of the door is placed at the hinges
/// activeSide - Will this function return true if player approaches door from both sides or just one
/// LocateSize - The locate size for the door activation.
/// RETURNS:
/// True if the player is wanting to interact with the door
FUNC BOOL IS_PLAYER_ACTIVATING_DOOR(OBJECT_INDEX anObject, ECOMPASS Direction = ECOMPASS_NORTH, ESIDESHIFT ShiftDirection = ESIDESHIFT_LEFT, EACTIVESIDE activeSide = EACTIVESIDE_ONEWAY, FLOAT LocateSize = 0.4, EACTIVATIONEASE Ease_of_activation = EACTIVATIONEASE_HARD, BOOL Check_Stationary = FALSE )
FLOAT EaseOfActivation
IF Ease_of_activation = EACTIVATIONEASE_EASY
EaseOfActivation = 150.0
ELIF Ease_of_activation = EACTIVATIONEASE_MEDIUM
EaseOfActivation = 100.0
ELIF Ease_of_activation = EACTIVATIONEASE_HARD
EaseOfActivation = 40.0
ENDIF
// Checks will fallthru to the default activation side, EACTIVESIDE_ONEWAY
SWITCH activeSide
CASE EACTIVESIDE_FOURCORNERS
SCRIPT_ASSERT("IS_PLAYER_ACTIVATING_DOOR - EACTIVESIDE_FOURCORNERS is an unsupported activation type. Use EACTIVESIDE_OPPOSITES or EACTIVESIDE_ONEWAY instead.")
FALLTHRU
CASE EACTIVESIDE_OPPOSITES
IF priv_DoorActivationCriteriaMet(anObject, APPROACH_OFFSET(direction, EACTIVEAPPROACH_BACK, locateSize), ShiftDirection, EaseOfActivation, LocateSize, Check_stationary)
RETURN TRUE
ENDIF
FALLTHRU
CASE EACTIVESIDE_ONEWAY
IF priv_DoorActivationCriteriaMet(anObject, APPROACH_OFFSET(direction, EACTIVEAPPROACH_FRONT, locateSize), ShiftDirection, EaseOfActivation, LocateSize, Check_stationary)
RETURN TRUE
ENDIF
BREAK
ENDSWITCH
RETURN FALSE
ENDFUNC
FUNC BOOL IS_HEADING_ACCEPTABLE(FLOAT fHeading, FLOAT fIdealHeading, FLOAT fLeeway #IF IS_DEBUG_BUILD, BOOL bDebugPrints = FALSE #ENDIF)
FLOAT fUpperH
FLOAT fLowerH
fLowerH = fIdealHeading - fLeeway
IF fLowerH < 0.0
fLowerH += 360.0
ENDIF
fUpperH = fIdealHeading + fLeeway
IF fUpperH >= 360.0
fUpperH -= 360.0
ENDIF
IF fUpperH > fLowerH
IF fHeading < fUpperH
AND fHeading > fLowerH
#IF IS_DEBUG_BUILD
IF bDebugPrints
PRINTLN("<", GET_THIS_SCRIPT_NAME(), "> IS_HEADING_ACCEPTABLE, fHeading = ", fHeading, ", fIdealHeading = ", fIdealHeading, ", RETURN TRUE")
ENDIF
#ENDIF
RETURN TRUE
ENDIF
ELSE
IF fHeading < fUpperH
OR fHeading > fLowerH
#IF IS_DEBUG_BUILD
IF bDebugPrints
PRINTLN("<", GET_THIS_SCRIPT_NAME(), "> IS_HEADING_ACCEPTABLE, fHeading = ", fHeading, ", fIdealHeading = ", fIdealHeading, ", RETURN TRUE")
ENDIF
#ENDIF
RETURN TRUE
ENDIF
ENDIF
#IF IS_DEBUG_BUILD
IF bDebugPrints
PRINTLN("<", GET_THIS_SCRIPT_NAME(), "> IS_HEADING_ACCEPTABLE, fHeading = ", fHeading, ", fIdealHeading = ", fIdealHeading, ", RETURN FALSE")
ENDIF
#ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL IS_HEADING_ACCEPTABLE_CORRECTED(FLOAT fHeading, FLOAT fIdealHeading, FLOAT fLeeway #IF IS_DEBUG_BUILD, BOOL bDebugPrints = FALSE #ENDIF)
FLOAT fUpperH
FLOAT fLowerH
fLowerH = fIdealHeading - fLeeway
IF fLowerH < 0.0
fLowerH += 360.0
ENDIF
fUpperH = fIdealHeading + fLeeway
IF fUpperH >= 360.0
fUpperH -= 360.0
ENDIF
IF fUpperH < 0.0
fUpperH += 360.0
ENDIF
IF fUpperH > fLowerH
IF fHeading < fUpperH
AND fHeading > fLowerH
#IF IS_DEBUG_BUILD
IF bDebugPrints
PRINTLN("<", GET_THIS_SCRIPT_NAME(), "> IS_HEADING_ACCEPTABLE, fHeading = ", fHeading, ", fIdealHeading = ", fIdealHeading, ", RETURN TRUE")
ENDIF
#ENDIF
RETURN TRUE
ENDIF
ELSE
IF fHeading < fUpperH
OR fHeading > fLowerH
#IF IS_DEBUG_BUILD
IF bDebugPrints
PRINTLN("<", GET_THIS_SCRIPT_NAME(), "> IS_HEADING_ACCEPTABLE, fHeading = ", fHeading, ", fIdealHeading = ", fIdealHeading, ", RETURN TRUE")
ENDIF
#ENDIF
RETURN TRUE
ENDIF
ENDIF
#IF IS_DEBUG_BUILD
IF bDebugPrints
PRINTLN("<", GET_THIS_SCRIPT_NAME(), "> IS_HEADING_ACCEPTABLE, fHeading = ", fHeading, ", fIdealHeading = ", fIdealHeading, ", RETURN FALSE")
ENDIF
#ENDIF
RETURN FALSE
ENDFUNC
PROC GET_POSITION_FOR_WALLET_SCENE(PED_INDEX ped, VECTOR &returnPos, VECTOR &returnRot, STRING sHandoverDict, STRING sHandoverPed)
INT iStage = 0
INT iAttempts = 0
INT CounterRotate = 1
FLOAT originalHeading
FLOAT newHeading
SHAPETEST_INDEX HandoverShapetest
INT bHitResult
VECTOR vRetNormal
VECTOR vRetPos
VECTOR vTemp
VECTOR vDistance
ENTITY_INDEX hitEntity
// METHOD 1 - Use the player's position when triggered for the animation, and make the heading of the anim the heading between the player and victim ped
// This ALMOST works, but there are loads of edge cases where it fucks up - building corners for example (convenience store in ATM Robbery 16),
// where the anim offset is gets embedded in a building and the shapetest never returns true
returnPos = GET_ENTITY_COORDS(PLAYER_PED_ID(), FALSE)
returnRot.z = GET_HEADING_FROM_ENTITIES_LA(ped, PLAYER_PED_ID())
originalHeading = GET_HEADING_FROM_ENTITIES_LA(ped, PLAYER_PED_ID())
newHeading = GET_HEADING_FROM_ENTITIES_LA(ped, PLAYER_PED_ID())
vDistance = GET_ENTITY_COORDS(ped, FALSE) - GET_ENTITY_COORDS(PLAYER_PED_ID(), FALSE)
// METHOD 2 - Try and get the victim ped's position, and then get an offset using the heading between the player's position and theirs to place the animation
// This is returning weird results - returnPos isn't where I keep thinking it'll be or the debug isn't showing the right positions; I've probably fucked something up?
// This might be better than the first method though because it should guarantee that the anim offset is where the ped is and is never embedded in the wall
// The player would warp a little bit, but they're offscreen, so who cares
// returnRot.z = GET_HEADING_FROM_ENTITIES_LA(ped, PLAYER_PED_ID())
// newHeading = GET_HEADING_FROM_ENTITIES_LA(ped, PLAYER_PED_ID())
// vDistance = GET_ENTITY_COORDS(ped, FALSE) - GET_ENTITY_COORDS(PLAYER_PED_ID(), FALSE)
// vTemp = GET_ENTITY_COORDS(ped, FALSE)
// CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_POSITION_FOR_WALLET_SCENE: ped coords: ", vTemp, ". returnRot.z: ", newHeading, ". vDistance: ", vDistance)
IF HAS_ANIM_DICT_LOADED(sHandoverDict)
vTemp = GET_ANIM_INITIAL_OFFSET_POSITION(sHandoverDict, sHandoverPed, returnPos, returnRot)
ELSE
SCRIPT_ASSERT("GET_POSITION_FOR_WALLET_SCENE() - Passed an animation dictionary to this function that isn't loaded! Attempting to salvage but will probably screw up badstyle...")
vTemp = returnPos+<<5,5,0>>
ENDIF
WHILE iStage < 2
SWITCH iStage
CASE 0
// Start the shape test
#IF IS_DEBUG_BUILD
DRAW_DEBUG_SPHERE(returnPos, 0.5, 255, 0, 0, 128)
DRAW_DEBUG_SPHERE(vTemp+<<0,0,-1.0>>, 0.5, 0, 0, 255, 128)
#ENDIF
IF iAttempts > 20
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_POSITION_FOR_WALLET_SCENE, had more than 20 attempts at this... breaking out")
returnRot.z = GET_HEADING_FROM_COORDS_LA(GET_ENTITY_COORDS(ped, FALSE), returnPos) // Re-grab the heading so it's less terrible
returnPos = GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS(GET_ENTITY_COORDS(ped, FALSE), returnRot.z, vDistance)
iStage = 2
ELSE
HandoverShapetest = START_SHAPE_TEST_CAPSULE(returnPos, vTemp+<<0,0,-1.0>>, 0.5, SCRIPT_INCLUDE_MOVER|SCRIPT_INCLUDE_OBJECT)
iStage++
ENDIF
BREAK
CASE 1
#IF IS_DEBUG_BUILD
DRAW_DEBUG_SPHERE(returnPos, 0.5, 255, 0, 0, 128)
DRAW_DEBUG_SPHERE(vTemp+<<0,0,-1.0>>, 0.5, 0, 0, 255, 128)
#ENDIF
IF GET_SHAPE_TEST_RESULT(HandoverShapetest, bHitResult, vRetPos, vRetNormal, hitEntity) = SHAPETEST_STATUS_RESULTS_READY
IF bHitResult != 0
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_POSITION_FOR_WALLET_SCENE, bHitResult = ", bHitResult, " - trying again (current attempt: ", iAttempts)
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_POSITION_FOR_WALLET_SCENE, vRetPos = ", vRetPos, ", vRetNormal = ", vRetNormal)
// If we've hit something but the Z of that hit is >10m of the return pos, we'll assume it's just hit the world container and do this anyway
IF vRetPos.z > (returnPos.z+8.5)
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_POSITION_FOR_WALLET_SCENE, vRetPos.z is > returnPos.z+8.5 - fuck it, let's use it anyway...")
iStage++
ELSE
// Something is blocking the area we want to play the anim - rotate the animation
// Change the heading from the victim to the player by x degrees, so we'll rotate the player's position by roughly that much
IF iAttempts <= 3 // For the first n attempts go around counter-clockwise, +x degrees
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_POSITION_FOR_WALLET_SCENE, newHeading old = ", newHeading)
newHeading+=7.5//(x)
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_POSITION_FOR_WALLET_SCENE, newHeading + 7.5 = ", newHeading)
ELSE
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_POSITION_FOR_WALLET_SCENE, newHeading old = ", newHeading)
// Past n attempts, get the original heading and then -x it each time
newHeading = originalHeading - (7.5*CounterRotate)
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_POSITION_FOR_WALLET_SCENE, newHeading - 7.5 *",CounterRotate," = ", newHeading)
CounterRotate++
ENDIF
// Now get a new offset to move the player to so we can retest it
returnPos = GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS(GET_ENTITY_COORDS(PLAYER_PED_ID(), FALSE), newHeading, vDistance)
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_POSITION_FOR_WALLET_SCENE, new returnPos = ", returnPos)
iAttempts++ // Increase the number of attempts
iStage = 0 // Go back to the previous stage and try again
ENDIF
ELSE
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_POSITION_FOR_WALLET_SCENE, shape test okay!")
// Re-get the heading if it's changed after multiple shapetests
IF iAttempts > 0
returnRot.z = GET_HEADING_FROM_COORDS_LA(vTemp+<<0,0,-0.75>>, returnPos)
ENDIF
iStage++
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_POSITION_FOR_WALLET_SCENE, final returnPos = ", returnPos, ", returnRot.z = ", returnRot.z)
ENDIF
ELSE
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_POSITION_FOR_WALLET_SCENE, still waiting for shapetest results...")
ENDIF
BREAK
CASE 2
// Do nothing, While will break out here
BREAK
ENDSWITCH
WAIT(0)
ENDWHILE
ENDPROC
PROC GET_POSITION_FOR_HANDOOVER_SCENE(PED_INDEX ped, VECTOR &returnPos, FLOAT &returnHeading)
INT iNodeNumber, iNoOfLanes, iLanesSouth, iLanesNorth
VECTOR vSouth, vNorth, vTemp
FLOAT centralReservationWidth, fHeadingToVictim, fFlippedNodeHeading, fNodeHeading, offsetToKerb
FLOAT ROAD_LANE_OFFSET = 5.0
iNodeNumber = 1
INT iStage = 0
SHAPETEST_INDEX HandoverShapetest
INT bHitResult
VECTOR vRetNormal
VECTOR vRetPos
ENTITY_INDEX hitEntity
WHILE iStage < 2
SWITCH iStage
CASE 0
// Get the closest vehicle node
GET_NTH_CLOSEST_VEHICLE_NODE_WITH_HEADING(GET_ENTITY_COORDS(PLAYER_PED_ID()), iNodeNumber, returnPos, fNodeHeading, iNoOfLanes, NF_INCLUDE_SWITCHED_OFF_NODES|NF_IGNORE_SLIPLANES) // Is returning slip nodes.
PRINTLN("<", GET_THIS_SCRIPT_NAME(), "> - GET_NTH_CLOSEST_VEHICLE_NODE_WITH_HEADING, iNodeNumber, ", iNodeNumber, ", returnPos = ", returnPos, ", fNodeHeading = ", fNodeHeading, ", iNoOfLanes = ", iNoOfLanes)
GET_CLOSEST_ROAD(returnPos, 1, 1, vSouth, vNorth, iLanesSouth, iLanesNorth, centralReservationWidth, FALSE)
IF GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(PLAYER_PED_ID(), FALSE), <<-3044.66, 596.43, 6.58>>) < 25
PRINTLN("<", GET_THIS_SCRIPT_NAME(), "> - GET_POSITION_FOR_HANDOOVER_SCENE, we're at ATM Robbery 3, canning the returnPos in case the player enters from the beach")
returnPos = <<-3031.38, 605.32, 6.86>>
ENDIF
// Work out scene heading
// Get heading from player to victim
vTemp = GET_ENTITY_COORDS(PLAYER_PED_ID()) - GET_ENTITY_COORDS(ped)
fHeadingToVictim = GET_HEADING_FROM_VECTOR_2D(vTemp.x, vTemp.y)
// Get flipped node heading
fFlippedNodeHeading = fNodeHeading+180
IF fFlippedNodeHeading > 360
fFlippedNodeHeading -= 360
ENDIF
// Decide which heading works
IF IS_HEADING_ACCEPTABLE(fHeadingToVictim, fNodeHeading, 90)
returnHeading = fNodeHeading
ELSE
returnHeading = fFlippedNodeHeading
ENDIF
// Work out the scene position
// Get distance to kerb
IF centralReservationWidth < 0
offsetToKerb = 0
ELSE
IF GET_VEHICLE_NODE_IS_SWITCHED_OFF(GET_NTH_CLOSEST_VEHICLE_NODE_ID(returnPos, 1))
offsetToKerb = 0
ELSE
offsetToKerb = (ROAD_LANE_OFFSET*(TO_FLOAT(iNoOfLanes/2)))
IF offsetToKerb = 0
offsetToKerb += ROAD_LANE_OFFSET
ENDIF
IF iNoOfLanes = 5
offsetToKerb += ROAD_LANE_OFFSET
ENDIF
// Add half of kerb
// If we're near ATM Robbery 8 or 18, add an extra metre to the kerb offset to stop the victim walking through a tree/bus shelter
// This is a bit hacky and I don't like it, but I'm at a loss of what else to do
IF GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(PLAYER_PED_ID(), FALSE), <<294, -895, 28>>) < 25
OR GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(PLAYER_PED_ID(), FALSE), <<-713.01, -819.64, 22.63>>) < 25
offsetToKerb += 5.0
ELSE
offsetToKerb += 3.75
ENDIF
offsetToKerb += (centralReservationWidth/2)
ENDIF
ENDIF
// Get offset direction
IF (VDIST(GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS(returnPos, returnHeading, <<offsetToKerb,0,0>> ), GET_ENTITY_COORDS(PLAYER_PED_ID())) >
VDIST(GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS(returnPos, returnHeading, <<-offsetToKerb,0,0>> ), GET_ENTITY_COORDS(PLAYER_PED_ID())))
offsetToKerb = -offsetToKerb
ENDIF
PRINTLN("<", GET_THIS_SCRIPT_NAME(), "> - GET_POSITION_FOR_HANDOOVER_SCENE, offsetToKerb = ", offsetToKerb)
// Return the position
returnPos = GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS(returnPos, returnHeading, <<offsetToKerb,0,0>>)
PRINTLN("<", GET_THIS_SCRIPT_NAME(), "> - GET_POSITION_FOR_HANDOOVER_SCENE, returnPos = ", returnPos, ", returnHeading = ", returnHeading)
// Start the shape test
HandoverShapetest = START_SHAPE_TEST_CAPSULE(returnPos+(returnPos.z+4.5), returnPos+<<0.5,0.5,4.5>>, 2.5)
iStage++
BREAK
CASE 1
IF iNodeNumber <= 2
IF GET_SHAPE_TEST_RESULT(HandoverShapetest, bHitResult, vRetPos, vRetNormal, hitEntity) = SHAPETEST_STATUS_RESULTS_READY
IF bHitResult != 0
PRINTLN("<", GET_THIS_SCRIPT_NAME(), "> - GET_POSITION_FOR_HANDOOVER_SCENE, bHitResult = ", bHitResult, " - increasing rode node number and trying again")
PRINTLN("<", GET_THIS_SCRIPT_NAME(), "> - GET_POSITION_FOR_HANDOOVER_SCENE, vRetPos = ", vRetPos, ", vRetNormal = ", vRetNormal)
// If we've hit something but the Z of that hit is >10m of the return pos, we'll assume it's just hit the world container and do this anyway
IF vRetPos.z > (returnPos.z+8.5)
PRINTLN("<", GET_THIS_SCRIPT_NAME(), "> - GET_POSITION_FOR_HANDOOVER_SCENE, vRetPos.z is > returnPos.z+8.5 - fuck it, let's use it anyway...")
iStage++
ELSE
iNodeNumber++ // This node has building shit around it, check the next node
iStage = 0 // Go back to the previous stage and try again
ENDIF
ELSE
PRINTLN("<", GET_THIS_SCRIPT_NAME(), "> - GET_POSITION_FOR_HANDOOVER_SCENE, shape test okay!")
iStage++
ENDIF
ELSE
PRINTLN("<", GET_THIS_SCRIPT_NAME(), "> - GET_POSITION_FOR_HANDOOVER_SCENE, still waiting for shapetest results...")
ENDIF
ELSE
PRINTLN("<", GET_THIS_SCRIPT_NAME(), "> - GET_POSITION_FOR_HANDOOVER_SCENE, previous 2 nodes have failed shape test - fuck it, just use node 3")
iStage++
ENDIF
BREAK
CASE 2
// Do nothing, While will break out here
BREAK
ENDSWITCH
ENDWHILE
ENDPROC
INT iPlayerCarNodeNumber = 3
FUNC BOOL GET_POSITION_FOR_PLAYERS_CAR(VECTOR vCutPos, VECTOR &returnPos, FLOAT &returnHeading)
INT iNoOfLanes, iLanesSouth, iLanesNorth
VECTOR vSouth, vNorth, vTemp
FLOAT centralReservationWidth, fHeadingToCutscene, fFlippedNodeHeading, fNodeHeading, offsetToKerb
FLOAT ROAD_LANE_OFFSET = 4.2
GET_NTH_CLOSEST_VEHICLE_NODE_WITH_HEADING(GET_ENTITY_COORDS(PLAYER_PED_ID()), iPlayerCarNodeNumber, returnPos, fNodeHeading, iNoOfLanes, NF_INCLUDE_SWITCHED_OFF_NODES) // Is returning slip nodes.
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_NTH_CLOSEST_VEHICLE_NODE_WITH_HEADING, returnPos = ", returnPos, ", fNodeHeading = ", fNodeHeading, ", iNoOfLanes = ", iNoOfLanes)
GET_CLOSEST_ROAD(returnPos, 1, 1, vSouth, vNorth, iLanesSouth, iLanesNorth, centralReservationWidth, FALSE)
// Work out scene heading
// Get heading from player to victim
vTemp = vCutPos - returnPos
fHeadingToCutscene = GET_HEADING_FROM_VECTOR_2D(vTemp.x, vTemp.y)
// Get flipped node heading
fFlippedNodeHeading = fNodeHeading+180
IF fFlippedNodeHeading > 360
fFlippedNodeHeading -= 360
ENDIF
// Decide which heading works
IF IS_HEADING_ACCEPTABLE(fHeadingToCutscene, fNodeHeading, 90)
returnHeading = fNodeHeading
ELSE
returnHeading = fFlippedNodeHeading
ENDIF
// Work out the scene position
// Get distance to kerb
IF centralReservationWidth < 0
offsetToKerb = 0
ELSE
IF GET_VEHICLE_NODE_IS_SWITCHED_OFF(GET_NTH_CLOSEST_VEHICLE_NODE_ID(returnPos, 1))
offsetToKerb = 0
ELSE
offsetToKerb = (ROAD_LANE_OFFSET*(TO_FLOAT(iNoOfLanes/2)))
IF offsetToKerb = 0
offsetToKerb += ROAD_LANE_OFFSET
ENDIF
// Add half of kerb
offsetToKerb += (centralReservationWidth/2)
ENDIF
ENDIF
// Get offset direction
IF (VDIST(GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS(returnPos, returnHeading, <<offsetToKerb,0,0>> ), GET_ENTITY_COORDS(PLAYER_PED_ID())) >
VDIST(GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS(returnPos, returnHeading, <<-offsetToKerb,0,0>> ), GET_ENTITY_COORDS(PLAYER_PED_ID())))
offsetToKerb = -offsetToKerb
ENDIF
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_POSITION_FOR_PLAYERS_CAR, offsetToKerb = ", offsetToKerb)
// Return the position
returnPos = GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS(returnPos, returnHeading, <<offsetToKerb,0,0>>)
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_POSITION_FOR_PLAYERS_CAR, returnPos = ", returnPos, ", returnHeading = ", returnHeading)
IF NOT IS_POINT_OBSCURED_BY_A_MISSION_ENTITY(returnPos, <<4,4,4>>)
RETURN TRUE
ELSE
iPlayerCarNodeNumber++
RETURN FALSE
ENDIF
ENDFUNC
FUNC VECTOR GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR(VECTOR vScenePosition, VECTOR vVictimPosition)
VEHICLE_INDEX tempCar
VECTOR vReturn, vNode, vMin, vMax
INT iStage = 0
INT iIncrement = 1
INT bHitResult
VECTOR vRetNormal
VECTOR vRetPos
VECTOR vFront
VECTOR vBack
ENTITY_INDEX hitEntity
#if IS_DEBUG_BUILD
VECTOR vTemp
#ENDIF
tempCar = GET_PLAYERS_LAST_VEHICLE()
IF NOT IS_ENTITY_A_MISSION_ENTITY(tempCar)
SET_ENTITY_AS_MISSION_ENTITY(tempCar)
ENDIF
GET_MODEL_DIMENSIONS(GET_ENTITY_MODEL(tempCar), vMin, vMax)
GET_NTH_CLOSEST_VEHICLE_NODE(vScenePosition, 3, vNode) // Get the SECOND closest node to the scene - we don't want to spawn the car that close to the player...
#if IS_DEBUG_BUILD
vTemp = GET_ENTITY_COORDS(tempCar)
#ENDIF
CPRINTLN(DEBUG_AMBIENT,GET_THIS_SCRIPT_NAME(), " - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, the 2nd closest node is at ", vNode)
#if IS_DEBUG_BUILD
IF DOES_ENTITY_EXIST(tempCar)
IF NOT IS_ENTITY_DEAD(tempCar)
CPRINTLN(DEBUG_AMBIENT,GET_THIS_SCRIPT_NAME(), " - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, the player's car is at ", vTemp)
ENDIF
ENDIF
#ENDIF
CPRINTLN(DEBUG_AMBIENT,GET_THIS_SCRIPT_NAME(), " - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, vMin is ", vMin)
CPRINTLN(DEBUG_AMBIENT,GET_THIS_SCRIPT_NAME(), " - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, vMax is ", vMax)
SHAPETEST_INDEX VehicleShapetest
IF VDIST(vScenePosition, <<-3039.6548, 602.4346, 6.5719>>) <=25 // If we're less than 25m away from ATM Robbery 3, move the vehicle specifically
vReturn = <<-3045.49, 604.46, 7.02>>
iStage = 8 // Skip the while loop and shapetest checks
ELIF VDIST(vScenePosition, <<288.06, -1257.10, 28.44>>) <=25 // If we're less than 25m away from ATM Robbery 2, move the vehicle specifically
vReturn = <<278.97, -1255.06, 28.88>>
iStage = 8 // Skip the while loop and shapetest checks
ENDIF
WHILE iStage < 8
SWITCH iStage
CASE 0
IF VDIST(vNode, GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(tempCar, <<0, (vMax.y+iIncrement), 0>>)) < VDIST(vNode, GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(tempCar, <<0, -((vMax.y+iIncrement)), 0>>))
vReturn = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(tempCar, <<0, (vMax.y+iIncrement), 0>>)
ELSE
vReturn = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(tempCar, <<0, -((vMax.y+iIncrement)), 0>>)
ENDIF
IF DOES_ENTITY_EXIST(tempCar)
IF NOT IS_ENTITY_DEAD(tempCar)
vFront = GET_OFFSET_FROM_COORD_IN_WORLD_COORDS(vReturn, 0, vMax)
vBack = GET_OFFSET_FROM_COORD_IN_WORLD_COORDS(vReturn, 0, vMin)
VehicleShapetest = START_SHAPE_TEST_LOS_PROBE(vFront, vBack, SCRIPT_INCLUDE_ALL, tempCar, SCRIPT_SHAPETEST_OPTION_DEFAULT)
iStage++
ELSE
iStage = 8
ENDIF
ELSE
iStage = 8
ENDIF
BREAK
CASE 1
IF GET_SHAPE_TEST_RESULT(VehicleShapetest, bHitResult, vRetPos, vRetNormal, hitEntity) = SHAPETEST_STATUS_RESULTS_READY
IF bHitResult != 0
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, bHitResult = ", bHitResult, " - increasing rode node number and trying again")
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, vRetPos = ", vRetPos, ", vRetNormal = ", vRetNormal)
IF iIncrement >= 3
iIncrement = 1
iStage = 2
ELSE
iIncrement++
iStage = 0 // Go back to the previous stage and try again
ENDIF
ELSE
IF GET_DISTANCE_BETWEEN_COORDS(vReturn, vScenePosition) <= 5.0
OR GET_DISTANCE_BETWEEN_COORDS(vReturn, vVictimPosition) <= 5.0
CPRINTLN(DEBUG_AMBIENT,GET_THIS_SCRIPT_NAME(), " - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, found an unoccupied space, but too close to vScenePosition or vVictimPosition!")
IF iIncrement >= 3
iIncrement = 1
iStage = 2
ELSE
iIncrement++
iStage = 0 // Go back to the previous stage and try again
ENDIF
ELSE
CPRINTLN(DEBUG_AMBIENT,GET_THIS_SCRIPT_NAME(), " - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, this position should be clear = ", vReturn)
CPRINTLN(DEBUG_AMBIENT,GET_THIS_SCRIPT_NAME(), " - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, y+iIncrement of = ", iIncrement)
iStage = 8
ENDIF
ENDIF
ELSE
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, still waiting for shapetest results...")
ENDIF
BREAK
CASE 2 // -Y
IF VDIST(vNode, GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(tempCar, <<0, (vMax.y-iIncrement), 0>>)) < VDIST(vNode, GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(tempCar, <<0, -((vMax.y-iIncrement)), 0>>))
vReturn = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(tempCar, <<0, (vMax.y-iIncrement), 0>>)
ELSE
vReturn = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(tempCar, <<0, -((vMax.y-iIncrement)), 0>>)
ENDIF
IF DOES_ENTITY_EXIST(tempCar)
IF NOT IS_ENTITY_DEAD(tempCar)
vFront = GET_OFFSET_FROM_COORD_IN_WORLD_COORDS(vReturn, 0, vMax)
vBack = GET_OFFSET_FROM_COORD_IN_WORLD_COORDS(vReturn, 0, vMin)
VehicleShapetest = START_SHAPE_TEST_LOS_PROBE(vFront, vBack, SCRIPT_INCLUDE_ALL, tempCar, SCRIPT_SHAPETEST_OPTION_DEFAULT)
iStage++
ELSE
iStage = 8
ENDIF
ELSE
iStage = 8
ENDIF
BREAK
CASE 3 // Check results for -Y
IF GET_SHAPE_TEST_RESULT(VehicleShapetest, bHitResult, vRetPos, vRetNormal, hitEntity) = SHAPETEST_STATUS_RESULTS_READY
IF bHitResult != 0
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, bHitResult = ", bHitResult, " - increasing rode node number and trying again")
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, vRetPos = ", vRetPos, ", vRetNormal = ", vRetNormal)
IF iIncrement >= 3
iIncrement = 1
iStage = 4
ELSE
iIncrement++
iStage = 2 // Go back to the previous stage and try again
ENDIF
ELSE
IF GET_DISTANCE_BETWEEN_COORDS(vReturn, vScenePosition) <= 5.0
OR GET_DISTANCE_BETWEEN_COORDS(vReturn, vVictimPosition) <= 5.0
CPRINTLN(DEBUG_AMBIENT,GET_THIS_SCRIPT_NAME(), " - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, found an unoccupied space, but too close to vScenePosition or vVictimPosition!")
IF iIncrement >= 3
iIncrement = 1
iStage = 4
ELSE
iIncrement++
iStage = 2 // Go back to the previous stage and try again
ENDIF
ELSE
CPRINTLN(DEBUG_AMBIENT,GET_THIS_SCRIPT_NAME(), " - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, this position should be clear = ", vReturn)
CPRINTLN(DEBUG_AMBIENT,GET_THIS_SCRIPT_NAME(), " - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, y-iIncrement of = ", iIncrement)
iStage = 8
ENDIF
ENDIF
ELSE
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, still waiting for shapetest results...")
ENDIF
BREAK
CASE 4 // +X
IF VDIST(vNode, GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(tempCar, <<(vMax.x+iIncrement), 0, 0>>)) < VDIST(vNode, GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(tempCar, <<-((vMax.x+iIncrement)), 0, 0>>))
vReturn = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(tempCar, <<(vMax.x+iIncrement), 0, 0>>)
ELSE
vReturn = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(tempCar, <<-((vMax.x+iIncrement)), 0, 0>>)
ENDIF
IF DOES_ENTITY_EXIST(tempCar)
IF NOT IS_ENTITY_DEAD(tempCar)
vFront = GET_OFFSET_FROM_COORD_IN_WORLD_COORDS(vReturn, 0, vMax)
vBack = GET_OFFSET_FROM_COORD_IN_WORLD_COORDS(vReturn, 0, vMin)
VehicleShapetest = START_SHAPE_TEST_LOS_PROBE(vFront, vBack, SCRIPT_INCLUDE_ALL, tempCar, SCRIPT_SHAPETEST_OPTION_DEFAULT)
iStage++
ELSE
iStage = 8
ENDIF
ELSE
iStage = 8
ENDIF
BREAK
CASE 5 // Check results for +X
IF GET_SHAPE_TEST_RESULT(VehicleShapetest, bHitResult, vRetPos, vRetNormal, hitEntity) = SHAPETEST_STATUS_RESULTS_READY
IF bHitResult != 0
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, bHitResult = ", bHitResult, " - increasing rode node number and trying again")
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, vRetPos = ", vRetPos, ", vRetNormal = ", vRetNormal)
IF iIncrement >= 3
iIncrement = 1
iStage = 6
ELSE
iIncrement++
iStage = 4 // Go back to the previous stage and try again
ENDIF
ELSE
IF GET_DISTANCE_BETWEEN_COORDS(vReturn, vScenePosition) <= 5.0
OR GET_DISTANCE_BETWEEN_COORDS(vReturn, vVictimPosition) <= 5.0
CPRINTLN(DEBUG_AMBIENT,GET_THIS_SCRIPT_NAME(), " - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, found an unoccupied space, but too close to vScenePosition or vVictimPosition!")
IF iIncrement >= 3
iIncrement = 1
iStage = 6
ELSE
iIncrement++
iStage = 4 // Go back to the previous stage and try again
ENDIF
ELSE
CPRINTLN(DEBUG_AMBIENT,GET_THIS_SCRIPT_NAME(), " - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, this position should be clear = ", vReturn)
CPRINTLN(DEBUG_AMBIENT,GET_THIS_SCRIPT_NAME(), " - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, x+iIncrement of = ", iIncrement)
iStage = 8
ENDIF
ENDIF
ELSE
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, still waiting for shapetest results...")
ENDIF
BREAK
CASE 6 // -X
IF VDIST(vNode, GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(tempCar, <<(vMax.x-iIncrement), 0, 0>>)) < VDIST(vNode, GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(tempCar, <<-((vMax.x-iIncrement)), 0, 0>>))
vReturn = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(tempCar, <<(vMax.x-iIncrement), 0, 0>>)
ELSE
vReturn = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(tempCar, <<-((vMax.x-iIncrement)), 0, 0>>)
ENDIF
IF DOES_ENTITY_EXIST(tempCar)
IF NOT IS_ENTITY_DEAD(tempCar)
vFront = GET_OFFSET_FROM_COORD_IN_WORLD_COORDS(vReturn, 0, vMax)
vBack = GET_OFFSET_FROM_COORD_IN_WORLD_COORDS(vReturn, 0, vMin)
VehicleShapetest = START_SHAPE_TEST_LOS_PROBE(vFront, vBack, SCRIPT_INCLUDE_ALL, tempCar, SCRIPT_SHAPETEST_OPTION_DEFAULT)
iStage++
ELSE
iStage = 8
ENDIF
ELSE
iStage = 8
ENDIF
BREAK
CASE 7 // Check results for -X
IF GET_SHAPE_TEST_RESULT(VehicleShapetest, bHitResult, vRetPos, vRetNormal, hitEntity) = SHAPETEST_STATUS_RESULTS_READY
IF bHitResult != 0
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, bHitResult = ", bHitResult, " - increasing rode node number and trying again")
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, vRetPos = ", vRetPos, ", vRetNormal = ", vRetNormal)
IF iIncrement >= 3
iIncrement = 1
iStage = 8
CPRINTLN(DEBUG_AMBIENT,GET_THIS_SCRIPT_NAME(), " - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, failed to find clear position, returning = ", vReturn)
ELSE
iIncrement++
iStage = 6 // Go back to the previous stage and try again
ENDIF
ELSE
IF GET_DISTANCE_BETWEEN_COORDS(vReturn, vScenePosition) <= 5.0
OR GET_DISTANCE_BETWEEN_COORDS(vReturn, vVictimPosition) <= 5.0
CPRINTLN(DEBUG_AMBIENT,GET_THIS_SCRIPT_NAME(), " - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, found an unoccupied space, but too close to vScenePosition or vVictimPosition!")
IF iIncrement >= 3
iIncrement = 1
iStage = 8
CPRINTLN(DEBUG_AMBIENT,GET_THIS_SCRIPT_NAME(), " - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, couldn't find any unoccupied spaces...")
ELSE
iIncrement++
iStage = 6 // Go back to the previous stage and try again
ENDIF
ELSE
CPRINTLN(DEBUG_AMBIENT,GET_THIS_SCRIPT_NAME(), " - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, this position should be clear = ", vReturn)
iStage = 8
ENDIF
ENDIF
ELSE
CPRINTLN(DEBUG_AMBIENT,"<", GET_THIS_SCRIPT_NAME(), "> - GET_UNOCCUPIED_POSITION_FOR_PLAYERS_CAR, still waiting for shapetest results...")
ENDIF
BREAK
CASE 8
// Do nothing, break out
BREAK
ENDSWITCH
#IF IS_DEBUG_BUILD
DRAW_DEBUG_SPHERE(vFront, 0.5, 255, 255, 0, 128)
DRAW_DEBUG_SPHERE(vBack, 0.5, 0, 255, 255, 128)
#ENDIF
WAIT(0)
ENDWHILE
RETURN vReturn
ENDFUNC
PROC SET_CAMERA_OVER_THE_SHOULDER_FOR_CATCH_UP()
CAMERA_INDEX camCatchup = CREATE_CAMERA_WITH_PARAMS(CAMTYPE_SCRIPTED, GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(PLAYER_PED_ID(), <<0.5,-1,0.5>>), <<0,0,GET_ENTITY_HEADING(PLAYER_PED_ID())>>, DEFAULT, TRUE)
SET_CAM_ACTIVE(camCatchup, TRUE)
ENDPROC