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

2117 lines
71 KiB
Scheme
Executable File

//////////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// SCRIPT NAME : rappel_public.sch //
// AUTHOR : Rob Bray //
// DESCRIPTION : Common functions for rappel //
// //
//////////////////////////////////////////////////////////////////////////////////////////////////////////
USING "rage_builtins.sch"
USING "commands_misc.sch"
USING "commands_pad.sch"
USING "commands_script.sch"
USING "commands_player.sch"
USING "commands_vehicle.sch"
USING "commands_object.sch"
USING "commands_graphics.sch"
USING "commands_physics.sch"
USING "commands_streaming.sch"
USING "commands_camera.sch"
USING "commands_audio.sch"
USING "script_player.sch"
CONST_INT STICK_MOVEMENT_RAPPEL_DOWN 100
CONST_INT STICK_MOVEMENT_RAPPEL_SIDE 100
CONST_INT RAPPEL_HELP_DELAY 1500
CONST_FLOAT JUMP_DOWN_SPEED 4.0
CONST_FLOAT JUMP_DOWN_SPEED_ACCELERATING 9.0
CONST_FLOAT JUMP_DOWN_SPEED_BIG 13.0
CONST_FLOAT JUMP_DOWN_ACCELERATION 8.5
CONST_FLOAT JUMP_DOWN_BIG_ACCELERATION 25.0
CONST_FLOAT JUMP_DOWN_MIN_DECELERATION_SMALL 4.0
CONST_FLOAT JUMP_DOWN_MIN_DECELERATION_BIG 15.0
CONST_FLOAT JUMP_DOWN_MAX_DECELERATION_SMALL 24.0
CONST_FLOAT JUMP_DOWN_MAX_DECELERATION_BIG 30.0
CONST_FLOAT FAST_DECELERATION 40.0
CONST_FLOAT SLIDE_ACCEL 2.0
CONST_FLOAT SLIDE_DECEL 5.0
CONST_FLOAT DEFAULT_MAX_SLIDE_SPEED 3.0
CONST_FLOAT DEFAULT_MAX_FAST_SLIDE_SPEED 6.0
CONST_FLOAT JUMP_DOWN_START_MOVE_PHASE_BIG 0.045
CONST_FLOAT JUMP_DOWN_START_MOVE_PHASE 0.07
CONST_FLOAT JUMP_DOWN_END_MOVE_PHASE 0.76
CONST_FLOAT MAX_STRENGTH_JUMP_PHASE 0.30//0.12
CONST_FLOAT MIN_STOP_DESCEND_PHASE 0.30
CONST_FLOAT MAX_STOP_DESCEND_PHASE 0.65
CONST_FLOAT MIN_STOP_DESCEND_PHASE_BIG 0.30
CONST_FLOAT MAX_STOP_DESCEND_PHASE_BIG 0.61
CONST_FLOAT ALLOW_JUMP_DOWN_CONVERT_PHASE 0.2
CONST_FLOAT ALLOW_START_NEW_JUMP_PHASE 0.76
CONST_FLOAT DEFAULT_SMALL_JUMP_SPEED 1.0
CONST_FLOAT DEFAULT_BIG_JUMP_SPEED 0.75
CONST_FLOAT DEFAULT_JUMP_DOWN_SOUND_LENGTH 1.40
CONST_FLOAT DEFAULT_ROPE_WEIGHT 40.0
CONST_FLOAT RAPPEL_CAM_MAX_SPEED 2.6
CONST_FLOAT FREE_ROPE_HAND_OFFSET_X 0.1
CONST_FLOAT FREE_ROPE_HAND_OFFSET_Y 0.05
CONST_FLOAT DIRECT_PIN_NEXT_VERTEX_DIST 0.02
CONST_FLOAT VERTEX_INTERP_SPEED_MULT -0.741
CONST_FLOAT VERTEX_INTERP_SPEED_MOD 3.982
#IF IS_DEBUG_BUILD
BOOL bDebugRappelInfo = FALSE
#ENDIF
ENUM RAPPEL_STATE_ENUM
RAPPEL_STATE_IDLING = 0,
RAPPEL_STATE_WALKING,
RAPPEL_STATE_JUMPING_STATIC,
RAPPEL_STATE_JUMPING_DOWN,
RAPPEL_STATE_JUMPING_SIDEWAYS,
RAPPEL_STATE_SLIDING,
RAPPEL_STATE_TRANSITION_TO_FREE_ROPING,
RAPPEL_STATE_DISMOUNT_FREE_ROPING
ENDENUM
ENUM RAPPEL_AI_ENUM
RAPPEL_AI_STOP = 0,
RAPPEL_AI_MOVE_SLOW,
RAPPEL_AI_MOVE_FAST,
RAPPEL_AI_SWING_LEFT,
RAPPEL_AI_SWING_RIGHT
ENDENUM
ENUM RAPPEL_MATERIAL_ENUM
RAPPEL_MATERIAL_METAL = 0,
RAPPEL_MATERIAL_GLASS,
RAPPEL_MATERIAL_STONE
ENDENUM
ENUM RAPPEL_SOUND_ENUM
RAPPEL_SOUND_FEET_THUD = 0,
RAPPEL_SOUND_RAPPEL,
RAPPEL_SOUND_SWING_LAND,
RAPPEL_SOUND_WALK_DOWN,
RAPPEL_SOUND_FREE_ROPE
ENDENUM
STRUCT RAPPEL_DATA
RAPPEL_STATE_ENUM state
RAPPEL_AI_ENUM ai
RAPPEL_MATERIAL_ENUM material
PED_INDEX ped
CAMERA_INDEX cam
ROPE_INDEX rope
OBJECT_INDEX anchorObject
OBJECT_INDEX weightObject
OBJECT_INDEX pulleyObject
WEAPON_TYPE dismountWeapon
VECTOR vAttachPos
VECTOR vOverrideHand
VECTOR vOverrideRopeOrigin
VECTOR vCacheHand
FLOAT fSpeed
FLOAT fSideJumpSpeed
FLOAT fCurrentJumpStrength
FLOAT fJumpDeceleration
FLOAT fStopDescendPhase
FLOAT fLimitZ
FLOAT fSlowDownByLimitRange
FLOAT fMaxSlideSpeed
FLOAT fLength
FLOAT fRopeSegmentLength
FLOAT fOverrideMaxJumpPhase
FLOAT fOverrideBigJumpSpeed
FLOAT fOverrideSmallJumpSpeed
// cam detals
FLOAT fTargetCamHeight
FLOAT fCurrentCamHeight
FLOAT fTargetCamHeading
FLOAT fCurrentCamHeading
FLOAT fCamDefaultHeading
FLOAT fCamYDist
FLOAT fCamXYInputToDegreesFactor
FLOAT fCamClockwiseLimit
FLOAT fCamAnticlockwiseLimit
FLOAT fCamXYSpeed
FLOAT fCamZDist
FLOAT fCamZInputToHeightFactor
FLOAT fCamZUpperLimit
FLOAT fCamZLowerLimit
FLOAT fCamZSpeed
FLOAT fCamZMod
FLOAT fCamFOV
VECTOR vModifyCamPointAt
// end cam details
BOOL bRappelling
BOOL bFreeRoping
BOOL bRopeCreated
BOOL bAllowRopeMovement
BOOL bUnfurling
BOOL bAttachedWeight
BOOL bDirectlyPinNextVertex
BOOL bSetupCam
BOOL bShownHelp
BOOL bAllowSmallJumps
BOOL bAllowBigJumps
BOOL bAllowSideJumps
BOOL bForceSmallJumpOnBigJumpControl
BOOL bSetBigJumpControlAsNormal
BOOL bCurrentJumpIsBig
BOOL bCurrentSideJumpIsRight
BOOL bPinnedLastVertex
BOOL bOverrideRopeOrigin
BOOL bUsePulley
BOOL bUnpinnedForDismount
BOOL bResetRunFromFiresAndExplosionsFlagOnCancel
BOOL bResetDisableExplosionReactionsFlagOnCancel
BOOL bDisabledPainAudio
BOOL bDoneLandGrunt
BOOL bUseOlderRappelJumps
BOOL bSuppressUnpinAllVertices
INT iSoundID[COUNT_OF(RAPPEL_SOUND_ENUM)]
INT iJumpStartTime
INT iSetStateTime
INT iLastRopeVertex
INT iNextRopeVertex
INT iRopeVertexCount
INT iLastVertexTime
// To adjust the camera limits for mouse.
FLOAT fMouseLimitXYMultiplier = 1.0
FLOAT fMouseLimitZMultiplier = 1.0
FLOAT fMouseSensitivityX = 0.1
FLOAT fMouseSensitivityY = 0.005
ENDSTRUCT
// anim dictionary of rappel animations
FUNC STRING GET_RAPPEL_ANIM_DICT_NAME()
RETURN "MISSRAPPEL"
ENDFUNC
// are rappel anims loaded in?
FUNC BOOL HAVE_RAPPEL_ANIMS_LOADED()
IF HAS_ANIM_DICT_LOADED(GET_RAPPEL_ANIM_DICT_NAME())
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
// request rappel anims (optional wait for load)
PROC REQUEST_RAPPEL_ANIMS(BOOL bWaitForLoad = FALSE)
REQUEST_ANIM_DICT(GET_RAPPEL_ANIM_DICT_NAME())
IF bWaitForLoad
WHILE NOT HAVE_RAPPEL_ANIMS_LOADED()
WAIT(0)
ENDWHILE
ENDIF
ENDPROC
// model name of rappel anchor
FUNC MODEL_NAMES GET_RAPPEL_ANCHOR_MODEL_NAME()
RETURN Prop_LD_Test_01
ENDFUNC
// model name of rappel pulley
FUNC MODEL_NAMES GET_RAPPEL_PULLEY_MODEL_NAME()
RETURN P_RPulley_S
ENDFUNC
// is anchor model loaded in?
FUNC BOOL HAS_RAPPEL_ANCHOR_MODEL_LOADED()
IF HAS_MODEL_LOADED(GET_RAPPEL_ANCHOR_MODEL_NAME())
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
// is pulley model loaded in?
FUNC BOOL HAS_RAPPEL_PULLEY_MODEL_LOADED()
IF HAS_MODEL_LOADED(GET_RAPPEL_PULLEY_MODEL_NAME())
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
// request anchor model (optional wait for load)
PROC REQUEST_RAPPEL_ANCHOR_MODEL(BOOL bWaitForLoad = FALSE)
REQUEST_MODEL(GET_RAPPEL_ANCHOR_MODEL_NAME())
IF bWaitForLoad
WHILE NOT HAS_RAPPEL_ANCHOR_MODEL_LOADED()
WAIT(0)
ENDWHILE
ENDIF
ENDPROC
// request pulley model (optional wait for load)
PROC REQUEST_RAPPEL_PULLEY_MODEL(BOOL bWaitForLoad = FALSE)
REQUEST_MODEL(GET_RAPPEL_PULLEY_MODEL_NAME())
IF bWaitForLoad
WHILE NOT HAS_RAPPEL_PULLEY_MODEL_LOADED()
WAIT(0)
ENDWHILE
ENDIF
ENDPROC
/*
// name of rappel bank
FUNC STRING GET_RAPPEL_AUDIO_BANK_NAME()
RETURN "Abseiling"
ENDFUNC
// is audio loaded in?
FUNC BOOL HAS_RAPPEL_AUDIO_LOADED()
IF REQUEST_SCRIPT_AUDIO_BANK(GET_RAPPEL_AUDIO_BANK_NAME())
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
// request rappel audio (optional wait for load)
PROC REQUEST_RAPPEL_AUDIO(BOOL bWaitForLoad = FALSE)
REQUEST_SCRIPT_AUDIO_BANK(GET_RAPPEL_AUDIO_BANK_NAME())
IF bWaitForLoad
WHILE NOT HAS_RAPPEL_AUDIO_LOADED()
WAIT(0)
ENDWHILE
ENDIF
ENDPROC
*/
// request rappel assets (optional wait for load)
PROC REQUEST_RAPPEL_ASSETS(BOOL bWaitForLoad = FALSE, BOOL bHandleEnableTextures = TRUE)
REQUEST_RAPPEL_ANIMS(bWaitForLoad)
REQUEST_RAPPEL_ANCHOR_MODEL(bWaitForLoad)
REQUEST_RAPPEL_PULLEY_MODEL(bWaitForLoad)
IF bHandleEnableTextures
ROPE_LOAD_TEXTURES()
IF bWaitForLoad
WHILE NOT ROPE_ARE_TEXTURES_LOADED()
WAIT(0)
ENDWHILE
ENDIF
ENDIF
ENDPROC
// have rappel assets loaded?
FUNC BOOL HAVE_RAPPEL_ASSETS_LOADED(BOOL bCheckTextures = TRUE)
IF HAVE_RAPPEL_ANIMS_LOADED()
AND HAS_RAPPEL_ANCHOR_MODEL_LOADED()
AND HAS_RAPPEL_PULLEY_MODEL_LOADED()
AND (ROPE_ARE_TEXTURES_LOADED() OR NOT bCheckTextures)
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
// release any rappel audio
/*
PROC RELEASE_RAPPEL_AUDIO(RAPPEL_DATA &rappelData)
INT i
REPEAT COUNT_OF(RAPPEL_SOUND_ENUM) i
IF rappelData.iSoundID[i] <> 0
RELEASE_SOUND_ID(rappelData.iSoundID[i])
rappelData.iSoundID[i] = 0
ENDIF
ENDREPEAT
ENDPROC
*/
// set rappel assets no longer needed
PROC SET_RAPPEL_ASSETS_AS_NO_LONGER_NEEDED(BOOL bHandleDisableTextures = TRUE)
REMOVE_ANIM_DICT(GET_RAPPEL_ANIM_DICT_NAME())
SET_MODEL_AS_NO_LONGER_NEEDED(GET_RAPPEL_ANCHOR_MODEL_NAME())
SET_MODEL_AS_NO_LONGER_NEEDED(GET_RAPPEL_PULLEY_MODEL_NAME())
IF bHandleDisableTextures
ROPE_UNLOAD_TEXTURES()
ENDIF
//RELEASE_NAMED_SCRIPT_AUDIO_BANK(GET_RAPPEL_AUDIO_BANK_NAME())
ENDPROC
// stop a rappel sound
/*
PROC STOP_RAPPEL_SOUND(RAPPEL_DATA &rappelData, RAPPEL_SOUND_ENUM sound)
STOP_SOUND(rappelData.iSoundID[sound])
ENDPROC
// stop rappel sound
PROC STOP_ALL_RAPPEL_SOUND(RAPPEL_DATA &rappelData, BOOL bCancelFreeRopeSound)
INT i
REPEAT COUNT_OF(RAPPEL_SOUND_ENUM) i
STOP_RAPPEL_SOUND(rappelData, INT_TO_ENUM(RAPPEL_SOUND_ENUM, i))
ENDREPEAT
IF bCancelFreeRopeSound
ENDIF
ENDPROC
*/
// play rappel sound
/*
PROC PLAY_RAPPEL_SOUND(RAPPEL_DATA &rappelData, RAPPEL_SOUND_ENUM sound)
TEXT_LABEL_31 tSound
TEXT_LABEL_31 tSoundSet
SWITCH sound
CASE RAPPEL_SOUND_FEET_THUD
tSound = "Feet_Thud"
BREAK
CASE RAPPEL_SOUND_RAPPEL
tSound = "Rappel"
BREAK
CASE RAPPEL_SOUND_SWING_LAND
tSound = "Swing_Land"
BREAK
CASE RAPPEL_SOUND_WALK_DOWN
tSound = "Walk_Down"
BREAK
CASE RAPPEL_SOUND_FREE_ROPE
tSound = "Free_Rope"
BREAK
ENDSWITCH
// add material?
IF rappelData.ped = PLAYER_PED_ID()
SWITCH rappelData.material
CASE RAPPEL_MATERIAL_GLASS
tSoundSet = "Abseiling_Player_Glass"
BREAK
CASE RAPPEL_MATERIAL_METAL
tSoundSet = "Abseiling_Player_Metal"
BREAK
CASE RAPPEL_MATERIAL_STONE
tSoundSet = "Abseiling_Player_Stone"
BREAK
ENDSWITCH
ELSE
SWITCH rappelData.material
CASE RAPPEL_MATERIAL_GLASS
tSoundSet = "Abseiling_NPC_Glass"
BREAK
CASE RAPPEL_MATERIAL_METAL
tSoundSet = "Abseiling_NPC_Metal"
BREAK
CASE RAPPEL_MATERIAL_STONE
tSoundSet = "Abseiling_NPC_Stone"
BREAK
ENDSWITCH
ENDIF
SWITCH sound
CASE RAPPEL_SOUND_RAPPEL
STOP_RAPPEL_SOUND(rappelData, RAPPEL_SOUND_SWING_LAND)
STOP_RAPPEL_SOUND(rappelData, RAPPEL_SOUND_WALK_DOWN)
BREAK
ENDSWITCH
IF HAS_SOUND_FINISHED(rappelData.iSoundID[sound])
PLAY_SOUND_FROM_ENTITY(rappelData.iSoundID[sound], tSound, rappelData.ped, tSoundSet)
ENDIF
ENDPROC
*/
FUNC CONTROL_ACTION GET_NORMAL_JUMP_CONTROL()
RETURN INPUT_SCRIPT_RDOWN
ENDFUNC
FUNC CONTROL_ACTION GET_BIG_JUMP_CONTROL()
RETURN INPUT_SCRIPT_RLEFT
ENDFUNC
PROC PLAY_PUSH_OFF_PAIN(RAPPEL_DATA &rappelData)
IF NOT rappelData.bDisabledPainAudio
IF rappelData.ped = PLAYER_PED_ID()
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
IF GET_RANDOM_INT_IN_RANGE(0,2) = 0
PLAY_PAIN(PLAYER_PED_ID(), AUD_DAMAGE_REASON_CLIMB_SMALL,0)
ELSE
PLAY_PAIN(PLAYER_PED_ID(), AUD_DAMAGE_REASON_CLIMB_LARGE,0)
ENDIF
ENDIF
ENDIF
ENDIF
ENDPROC
// set rappel state
PROC SET_RAPPEL_STATE(RAPPEL_DATA &rappelData, RAPPEL_STATE_ENUM newRappelState, BOOL bForceState = FALSE, BOOL bAllowResetStage = FALSE, BOOL bPlayAnim = TRUE, FLOAT fInitPhase = 0.0, BOOL bForceSlowBlendIn = FALSE)
IF rappelData.state = newRappelState
AND NOT bAllowResetStage
AND NOT bForceState
// don't do anything if already in desired state
EXIT
ENDIF
IF NOT IS_PED_INJURED(rappelData.ped)
IF bPlayAnim
STRING sAnim
STRING sPulleyAnim
FLOAT fBlendIn = NORMAL_BLEND_IN
FLOAT fBlendOut = NORMAL_BLEND_OUT
IF bForceState
fBlendIn = INSTANT_BLEND_IN
ENDIF
IF bForceSlowBlendIn
fBlendIn = 2.0
ENDIF
SWITCH newRappelState
CASE RAPPEL_STATE_IDLING
ANIMATION_FLAGS animFlags
IF bForceState
animFlags = AF_LOOPING | AF_FORCE_START
ELSE
animFlags = AF_LOOPING
ENDIF
IF rappelData.bFreeRoping
sAnim = "Rope_Idle"
ELSE
sAnim = "Rappel_Idle"
sPulleyAnim = "Rappel_Idle_prop"
ENDIF
TASK_PLAY_ANIM(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), sAnim, fBlendIn, fBlendOut, -1, animFlags, fInitPhase)
rappelData.fSpeed = 0.0
IF NOT rappelData.bFreeRoping
IF DOES_ENTITY_EXIST(rappelData.pulleyObject)
IF DOES_ENTITY_HAVE_DRAWABLE(rappelData.pulleyObject)
PLAY_ENTITY_ANIM(rappelData.pulleyObject, sPulleyAnim, GET_RAPPEL_ANIM_DICT_NAME(), fBlendIn, TRUE, FALSE)
ENDIF
ENDIF
ENDIF
//STOP_RAPPEL_SOUND(rappelData, RAPPEL_SOUND_FEET_THUD)
//STOP_RAPPEL_SOUND(rappelData, RAPPEL_SOUND_WALK_DOWN)
//STOP_RAPPEL_SOUND(rappelData, RAPPEL_SOUND_SWING_LAND)
//STOP_RAPPEL_SOUND(rappelData, RAPPEL_SOUND_FREE_ROPE)
BREAK
CASE RAPPEL_STATE_WALKING
IF bForceState
animFlags = AF_LOOPING | AF_FORCE_START
ELSE
animFlags = AF_LOOPING
ENDIF
TASK_PLAY_ANIM(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), "Rappel_Walk", fBlendIn, fBlendOut, -1, animFlags, fInitPhase)
IF DOES_ENTITY_EXIST(rappelData.pulleyObject)
IF DOES_ENTITY_HAVE_DRAWABLE(rappelData.pulleyObject)
PLAY_ENTITY_ANIM(rappelData.pulleyObject, "Rappel_Walk_prop", GET_RAPPEL_ANIM_DICT_NAME(), fBlendIn, TRUE, FALSE)
ENDIF
ENDIF
//PLAY_RAPPEL_SOUND(rappelData, RAPPEL_SOUND_WALK_DOWN)
BREAK
CASE RAPPEL_STATE_JUMPING_STATIC
IF bForceState
animFlags = AF_HOLD_LAST_FRAME | AF_FORCE_START
ELSE
animFlags = AF_HOLD_LAST_FRAME
ENDIF
TASK_PLAY_ANIM(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), "Rappel_Jump_a", fBlendIn, fBlendOut, -1, animFlags, fInitPhase)
IF DOES_ENTITY_EXIST(rappelData.pulleyObject)
IF DOES_ENTITY_HAVE_DRAWABLE(rappelData.pulleyObject)
PLAY_ENTITY_ANIM(rappelData.pulleyObject, "Rappel_Jump_a_prop", GET_RAPPEL_ANIM_DICT_NAME(), fBlendIn, FALSE, TRUE)
ENDIF
ENDIF
PLAY_PUSH_OFF_PAIN(rappelData)
rappelData.bDoneLandGrunt = FALSE
rappelData.fCurrentJumpStrength = 0.0
rappelData.fStopDescendPhase = -1.0
rappelData.fJumpDeceleration = JUMP_DOWN_MAX_DECELERATION_SMALL
//PLAY_RAPPEL_SOUND(rappelData, RAPPEL_SOUND_SWING_LAND)
BREAK
CASE RAPPEL_STATE_JUMPING_DOWN
IF bForceState
animFlags = AF_HOLD_LAST_FRAME | AF_FORCE_START
ELSE
animFlags = AF_HOLD_LAST_FRAME
ENDIF
IF rappelData.bCurrentJumpIsBig
IF NOT rappelData.bUseOlderRappelJumps
sAnim = "rappel_jump_c"
sPulleyAnim = "Rappel_Jump_c_Prop"
ELSE
sAnim = "rappel_jump_a"
sPulleyAnim = "Rappel_Jump_a_Prop"
ENDIF
ELSE
sAnim = "Rappel_Jump_a"
sPulleyAnim = "Rappel_Jump_a_prop"
ENDIF
TASK_PLAY_ANIM(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), sAnim, fBlendIn, fBlendOut, -1, animFlags, fInitPhase)
IF DOES_ENTITY_EXIST(rappelData.pulleyObject)
IF DOES_ENTITY_HAVE_DRAWABLE(rappelData.pulleyObject)
PLAY_ENTITY_ANIM(rappelData.pulleyObject, sPulleyAnim, GET_RAPPEL_ANIM_DICT_NAME(), fBlendIn, FALSE, TRUE)
ENDIF
ENDIF
PLAY_PUSH_OFF_PAIN(rappelData)
rappelData.bDoneLandGrunt = FALSE
//PLAY_RAPPEL_SOUND(rappelData, RAPPEL_SOUND_RAPPEL)
rappelData.fCurrentJumpStrength = 0.0
rappelData.fStopDescendPhase = -1.0
rappelData.fJumpDeceleration = JUMP_DOWN_MAX_DECELERATION_SMALL
rappelData.iJumpStartTime = GET_GAME_TIMER()
BREAK
CASE RAPPEL_STATE_JUMPING_SIDEWAYS
IF bForceState
animFlags = AF_LOOPING | AF_FORCE_START
ELSE
animFlags = AF_LOOPING
ENDIF
PLAY_PUSH_OFF_PAIN(rappelData)
rappelData.bDoneLandGrunt = FALSE
TASK_PLAY_ANIM(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), "Rappel_Walk", fBlendIn, fBlendOut, -1, animFlags, fInitPhase)
BREAK
CASE RAPPEL_STATE_SLIDING
IF bForceState
animFlags = AF_LOOPING | AF_FORCE_START
ELSE
animFlags = AF_LOOPING
ENDIF
TASK_PLAY_ANIM(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), "Rope_Slide", fBlendIn, fBlendOut, -1, animFlags, fInitPhase)
//PLAY_RAPPEL_SOUND(rappelData, RAPPEL_SOUND_FREE_ROPE)
BREAK
CASE RAPPEL_STATE_TRANSITION_TO_FREE_ROPING
IF bForceState
animFlags = AF_HOLD_LAST_FRAME | AF_FORCE_START
ELSE
animFlags = AF_HOLD_LAST_FRAME
ENDIF
TASK_PLAY_ANIM(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), "rappel_to_free_rope", fBlendIn, fBlendOut, -1, animFlags, fInitPhase)
BREAK
CASE RAPPEL_STATE_DISMOUNT_FREE_ROPING
IF bForceState
animFlags = AF_FORCE_START
ELSE
animFlags = AF_DEFAULT
ENDIF
DETACH_ENTITY(rappelData.ped)
TASK_PLAY_ANIM(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), "land_action", fBlendIn, fBlendOut, -1, animFlags, fInitPhase)
BREAK
ENDSWITCH
ENDIF
IF bForceState
FORCE_PED_AI_AND_ANIMATION_UPDATE(rappelData.ped)
ENDIF
ENDIF
rappelData.iSetStateTime = GET_GAME_TIMER()
rappelData.state = newRappelState
ENDPROC
/// PURPOSE:
/// Sets multiplier values for the mouse limits
/// PARAMS:
/// rappelData - Rappel data struct
/// fXYLimit - Limit multiplier for horizontal movement
/// fZLimit - Limit multiplier for vertical movement
PROC INIT_RAPPEL_CAM_MOUSE_PARAMS( RAPPEL_DATA &rappelData, FLOAT fXYLimit, FLOAT fZLimit, FLOAT fMouseSensitivityX = 0.1, FLOAT fMouseSensitivityY = 0.005 )
rappelData.fMouseLimitXYMultiplier = fXYLimit
rappelData.fMouseLimitZMultiplier = fZLimit
rappelData.fMouseSensitivityX = fMouseSensitivityX
rappelData.fMouseSensitivityY = fMouseSensitivityY
ENDPROC
// initialise rappel camera with properties
PROC SETUP_RAPPEL_CAM(RAPPEL_DATA &rappelData, VECTOR vCamPos, BOOL bCreateCam = TRUE, BOOL bStartRenderingRappelCam = TRUE)
IF NOT IS_PED_INJURED(rappelData.ped)
IF bCreateCam
IF DOES_CAM_EXIST(rappelData.cam)
DESTROY_CAM(rappelData.cam)
ENDIF
rappelData.cam = CREATE_CAM("DEFAULT_SCRIPTED_CAMERA", TRUE)
SET_CAM_PARAMS(rappelData.cam, vCamPos, <<0,0,0>>, rappelData.fCamFOV)
POINT_CAM_AT_ENTITY(rappelData.cam, rappelData.ped,<<0,0,0>>)
IF bStartRenderingRappelCam
DISPLAY_HUD(FALSE)
DISPLAY_RADAR(TRUE)
RENDER_SCRIPT_CAMS(TRUE,FALSE)
ENDIF
ENDIF
ENDIF
rappelData.bSetupCam = TRUE
ENDPROC
// do rappel cam
PROC DO_RAPPEL_CAM(RAPPEL_DATA &rappelData, BOOL bForceCurrentPosUpdate = FALSE, INT iRX = 0, INT iRY = 0, BOOL bUpdateFromControls = TRUE, FLOAT fForceInitialOffset = -1000.0, BOOL bStartRenderingRappelCam = TRUE)
IF NOT IS_PED_INJURED(rappelData.ped)
PROCESS_ENTITY_ATTACHMENTS(rappelData.ped)
FLOAT fTimeSinceLastFrame = GET_FRAME_TIME()
FLOAT fPedHeading = GET_ENTITY_HEADING(rappelData.ped)
//VECTOR vPedCoords = GET_ENTITY_COORDS(rappelData.ped)
IF fPedHeading < 0
fPedHeading += 360.0
ENDIF
IF bUpdateFromControls
IF IS_USING_KEYBOARD_AND_MOUSE(FRONTEND_CONTROL)
// Reduce limits to prevent clipping when using mouse.
FLOAT fMouseCamAnticlockwiseLimit = rappelData.fCamAnticlockwiseLimit * rappelData.fMouseLimitXYMultiplier
FLOAT fMouseCamClockwiseLimit = rappelData.fCamClockwiseLimit * rappelData.fMouseLimitXYMultiplier
rappelData.fTargetCamHeading += (iRX * rappelData.fMouseSensitivityX)
IF rappelData.fTargetCamHeading > fMouseCamAnticlockwiseLimit
rappelData.fTargetCamHeading = fMouseCamAnticlockwiseLimit
ELIF rappelData.fTargetCamHeading < -fMouseCamClockwiseLimit
rappelData.fTargetCamHeading = -fMouseCamClockwiseLimit
ENDIF
ELSE
rappelData.fTargetCamHeading = (-iRX * rappelData.fCamXYInputToDegreesFactor)
IF rappelData.fTargetCamHeading > rappelData.fCamAnticlockwiseLimit
rappelData.fTargetCamHeading = rappelData.fCamAnticlockwiseLimit
ELIF rappelData.fTargetCamHeading < -rappelData.fCamClockwiseLimit
rappelData.fTargetCamHeading = -rappelData.fCamClockwiseLimit
ENDIF
ENDIF
// No need to invert on mouse
IF IS_LOOK_INVERTED()
AND NOT IS_USING_KEYBOARD_AND_MOUSE(FRONTEND_CONTROL)
iRY *= -1
ENDIF
// Debug for mouse
// DISPLAY_TEXT_WITH_FLOAT(0.0,0.0, "NUMBER", rappelData.fCamZUpperLimit, 3)
// DISPLAY_TEXT_WITH_FLOAT(0.0,0.1, "NUMBER", rappelData.fCamZLowerLimit, 3)
// DISPLAY_TEXT_WITH_FLOAT(0.0,0.2, "NUMBER", rappelData.fTargetCamHeight, 3)
IF IS_USING_KEYBOARD_AND_MOUSE(FRONTEND_CONTROL)
FLOAT fMouseCamZUpperLimit = rappelData.fCamZUpperLimit * rappelData.fMouseLimitZMultiplier
FLOAT fMouseCamZLowerLimit = rappelData.fCamZLowerLimit * rappelData.fMouseLimitZMultiplier
rappelData.fTargetCamHeight -= (iRY * rappelData.fMouseSensitivityY) // 0.005
IF rappelData.fTargetCamHeight > fMouseCamZUpperLimit
rappelData.fTargetCamHeight = fMouseCamZUpperLimit
ELIF rappelData.fTargetCamHeight < fMouseCamZLowerLimit
rappelData.fTargetCamHeight = fMouseCamZLowerLimit
ENDIF
ELSE
rappelData.fTargetCamHeight = iRY * rappelData.fCamZInputToHeightFactor
IF rappelData.fTargetCamHeight > rappelData.fCamZUpperLimit
rappelData.fTargetCamHeight = rappelData.fCamZUpperLimit
ELIF rappelData.fTargetCamHeight < rappelData.fCamZLowerLimit
rappelData.fTargetCamHeight = rappelData.fCamZLowerLimit
ENDIF
ENDIF
// Mouse uses faster interpolation for speed of response.
IF IS_USING_KEYBOARD_AND_MOUSE(FRONTEND_CONTROL)
FLOAT fMouseCamXYSpeed = rappelData.fCamXYSpeed * 20
FLOAT fMouseCamZSpeed = rappelData.fCamZSpeed * 5
rappelData.fCamZMod += (rappelData.fTargetCamHeight - rappelData.fCamZMod) * (fMouseCamZSpeed * fTimeSinceLastFrame)
rappelData.fCurrentCamHeading += (rappelData.fTargetCamHeading - rappelData.fCurrentCamHeading) * (fMouseCamXYSpeed * fTimeSinceLastFrame)
ELSE
rappelData.fCamZMod += (rappelData.fTargetCamHeight - rappelData.fCamZMod) * (rappelData.fCamZSpeed * fTimeSinceLastFrame)
rappelData.fCurrentCamHeading += (rappelData.fTargetCamHeading - rappelData.fCurrentCamHeading) * (rappelData.fCamXYSpeed * fTimeSinceLastFrame)
ENDIF
ENDIF
rappelData.fCurrentCamHeight = rappelData.fCamZDist + rappelData.fCamZMod
// get the distance between the mover and the root
VECTOR vOffsetFromMover
IF fForceInitialOffset <= -1000
vOffsetFromMover = GET_OFFSET_FROM_ENTITY_GIVEN_WORLD_COORDS(rappelData.ped, GET_PED_BONE_COORDS(rappelData.ped, BONETAG_ROOT, <<0,0,0>>))
vOffsetFromMover.y /= 4
//printstring("voffsetfrommover = ") printfloat(vOffsetFromMover.y) printnl()
ELSE
vOffsetFromMover.y = fForceInitialOffset
ENDIF
VECTOR vPedCoordsWithOffset = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(rappelData.ped, <<0,vOffsetFromMover.y,0>>)
//printstring("vpedcoords with offset = ") printvector(vPedCoordsWithOffset) PRINTNL()
VECTOR vCurrentCamPos = GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS(vPedCoordsWithOffset, rappelData.fCurrentCamHeading + rappelData.fCamDefaultHeading + fPedHeading , <<0, rappelData.fCamYDist, 0>>)
VECTOR vReconvertedPos = GET_OFFSET_FROM_ENTITY_GIVEN_WORLD_COORDS(rappelData.ped, vCurrentCamPos)
IF NOT rappelData.bSetupCam
OR bForceCurrentPosUpdate
SETUP_RAPPEL_CAM(rappelData, vCurrentCamPos, NOT bForceCurrentPosUpdate, bStartRenderingRappelCam)
ENDIF
//VECTOR vNewAttach = <<vCurrentCamPos.x - vPedCoordsWithOffset.x, vCurrentCamPos.y - vPedCoordsWithOffset.y,rappelData.fCurrentCamHeight>>
//PRINTSTRING("newattach") PRINTVECTOR(vNewAttach) PRINTNL()
vReconvertedPos.z = rappelData.fCurrentCamHeight
ATTACH_CAM_TO_ENTITY(rappelData.cam, rappelData.ped, vReconvertedPos, TRUE)//GET_OFFSET_FROM_ENTITY_GIVEN_WORLD_COORDS(rappelData.ped, vPedCoordsWithOffset))
POINT_CAM_AT_ENTITY(rappelData.cam, rappelData.ped, <<0,vOffsetFromMover.y,0.6>> + rappelData.vModifyCamPointAt)
//POINT_CAM_AT_COORD(rappelData.cam, GET_ENTITY_COORDS(rappelData.ped))
SET_CAM_PARAMS(rappelData.cam, vCurrentCamPos, <<0,0,0>>, rappelData.fCamFOV)
//SET_CAM_PARAMS(GET_GAME_CAM(), GET_CAM_POS(rappelData.cam), GET_CAM_ROT(rappelData.cam))
//SET_GAMEPLAY_CAM_RELATIVE_HEADING(rappelData.fCurrentCamHeading + rappelData.fCamDefaultHeading)
SET_CAM_CONTROLS_MINI_MAP_HEADING(rappelData.cam, TRUE)
ENDIF
ENDPROC
// should rappel jump be moving at current anim phase
FUNC BOOL SHOULD_RAPPEL_JUMP_BE_MOVING(RAPPEL_DATA rappelData, FLOAT fAnimPhase)
IF rappelData.state = RAPPEL_STATE_JUMPING_DOWN
IF rappelData.bCurrentJumpIsBig
AND NOT rappelData.bUseOlderRappelJumps
IF fAnimPhase >= JUMP_DOWN_START_MOVE_PHASE_BIG
AND fAnimPhase <= JUMP_DOWN_END_MOVE_PHASE
RETURN TRUE
ENDIF
ELSE
IF fAnimPhase >= JUMP_DOWN_START_MOVE_PHASE
AND fAnimPhase <= JUMP_DOWN_END_MOVE_PHASE
RETURN TRUE
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
// is free roping done
FUNC BOOL HAS_FREE_ROPING_FINISHED(RAPPEL_DATA &rappelData, BOOL bCheckPedHasStopped = FALSE)
IF rappelData.vAttachPos.z <= rappelData.fLimitZ
IF rappelData.fSpeed = 0.0
OR NOT bCheckPedHasStopped
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
// is a ped dismounting
FUNC BOOL IS_RAPPEL_DISMOUNTING(RAPPEL_DATA &rappelData)
IF NOT IS_PED_INJURED(rappelData.ped)
IF IS_ENTITY_PLAYING_ANIM(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), "land_action")
OR GET_GAME_TIMER() <= rappelData.iSetStateTime + 500
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
// convert to free roping
FUNC BOOL CONVERT_RAPPEL_IS_FREE_ROPING(RAPPEL_DATA &rappelData, BOOL bToFreeRoping)
IF rappelData.state = RAPPEL_STATE_IDLING
IF NOT IS_PED_INJURED(rappelData.ped)
IF bToFreeRoping AND NOT rappelData.bFreeRoping
//rappelData.bFreeRoping = TRUE
SET_RAPPEL_STATE(rappelData, RAPPEL_STATE_TRANSITION_TO_FREE_ROPING, FALSE, TRUE)
ELIF rappelData.bFreeRoping AND NOT bToFreeRoping
rappelData.bFreeRoping = FALSE
SET_RAPPEL_STATE(rappelData, RAPPEL_STATE_IDLING, FALSE, TRUE)
ENDIF
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
// unpin all except anchor
PROC UNPIN_ALL_ROPE_VERTICES_EXCEPT_ANCHOR(RAPPEL_DATA &rappelData)
INT i
IF NOT rappelData.bSuppressUnpinAllVertices
IF DOES_ROPE_EXIST(rappelData.rope)
REPEAT rappelData.iRopeVertexCount i
IF i <> 0
UNPIN_ROPE_VERTEX(rappelData.rope, i)
ENDIF
ENDREPEAT
ENDIF
ENDIF
ENDPROC
// cancel rappel
PROC CANCEL_RAPPEL(RAPPEL_DATA &rappelData, BOOL bCancelIntoCutscene = FALSE, WEAPON_TYPE restoreWeapon = WEAPONTYPE_UNARMED, BOOL bUnpin = TRUE, BOOL bClearTasks = TRUE, BOOL bDestroyPulley = TRUE)
IF rappelData.bRappelling
//STOP_ALL_RAPPEL_SOUND(rappelData, TRUE)
IF NOT IS_PED_INJURED(rappelData.ped)
IF rappelData.ped <> PLAYER_PED_ID()
IF rappelData.bResetRunFromFiresAndExplosionsFlagOnCancel
SET_PED_CONFIG_FLAG(rappelData.ped, PCF_RunFromFiresAndExplosions, TRUE)
ENDIF
IF rappelData.bResetDisableExplosionReactionsFlagOnCancel
SET_PED_CONFIG_FLAG(rappelData.ped, PCF_DisableExplosionReactions, FALSE)
ENDIF
ENDIF
IF IS_ENTITY_ATTACHED(rappelData.ped)
IF NOT IS_PED_IN_ANY_VEHICLE(rappelData.ped)
DETACH_ENTITY(rappelData.ped)
IF bClearTasks
CLEAR_PED_TASKS(rappelData.ped)
ENDIF
ENDIF
ENDIF
ENDIF
IF DOES_CAM_EXIST(rappelData.cam)
SET_CAM_ACTIVE(rappelData.cam, FALSE)
rappelData.bSetupCam = FALSE
ENDIF
IF rappelData.ped = PLAYER_PED_ID()
IF NOT bCancelIntoCutscene
SET_PLAYER_CONTROL(PLAYER_ID(),TRUE)
SET_GAMEPLAY_CAM_RELATIVE_HEADING(0)
RENDER_SCRIPT_CAMS(FALSE, FALSE)
DISPLAY_HUD(TRUE)
DISPLAY_RADAR(TRUE)
ENDIF
ENDIF
IF bDestroyPulley
IF DOES_ENTITY_EXIST(rappelData.pulleyObject)
DELETE_OBJECT(rappelData.pulleyObject)
ENDIF
ENDIF
IF restoreWeapon <> WEAPONTYPE_UNARMED
IF HAS_PED_GOT_WEAPON(rappelData.ped, restoreWeapon)
SET_CURRENT_PED_WEAPON(rappelData.ped, restoreWeapon, TRUE)
ENDIF
ENDIF
IF bUnpin
UNPIN_ALL_ROPE_VERTICES_EXCEPT_ANCHOR(rappelData)
ENDIF
//IF NOT IS_PED_INJURED(rappelData.ped)
// SET_ENTITY_ROTATION(rappelData.ped,<<0,0,GET_ENTITY_HEADING(rappelData.ped)>>)
//ENDIF
rappelData.bRappelling = FALSE
ENDIF
ENDPROC
// handle the player controlling the player character to move
PROC HANDLE_RAPPEL_PED(RAPPEL_DATA &rappelData, BOOL bIsAI = FALSE, BOOL bDoCam = TRUE, BOOL bAllowResetToIdle = TRUE, BOOL bAllowRapidConvertJump = TRUE, BOOL bForceSlowJumpDown = FALSE)
INT iLX, iLY, iRX, iRY
FLOAT fAnimPhase
IF rappelData.ped = PLAYER_PED_ID()
// southpaw control allowed
ALLOW_ALTERNATIVE_SCRIPT_CONTROLS_LAYOUT(PLAYER_CONTROL)
ALLOW_ALTERNATIVE_SCRIPT_CONTROLS_LAYOUT(FRONTEND_CONTROL)
//printstring("SOUTHPAW") printnl()
ENDIF
IF NOT bIsAI
OR bDoCam
GET_CONTROL_VALUE_OF_ANALOGUE_STICKS(iLX, iLY, iRX, iRY)
IF IS_USING_KEYBOARD_AND_MOUSE(FRONTEND_CONTROL)
iRX = FLOOR(GET_CONTROL_UNBOUND_NORMAL(FRONTEND_CONTROL, INPUT_SCALED_LOOK_LR) * 127)
iRY = FLOOR(GET_CONTROL_UNBOUND_NORMAL(FRONTEND_CONTROL, INPUT_SCALED_LOOK_UD) * 127)
ENDIF
ENDIF
IF bDoCam
DO_RAPPEL_CAM(rappelData, FALSE, iRX, iRY)
ENDIF
BOOL bDoRappelAction = FALSE
IF DOES_ENTITY_EXIST(rappelData.anchorObject)
AND NOT IS_PED_INJURED(rappelData.ped)
SWITCH rappelData.state
CASE RAPPEL_STATE_IDLING
IF NOT rappelData.bUnfurling
IF NOT rappelData.bFreeRoping
IF bIsAi
IF rappelData.ai = RAPPEL_AI_MOVE_SLOW
bDoRappelAction = TRUE
ENDIF
ELSE
IF iLY > STICK_MOVEMENT_RAPPEL_DOWN
bDoRappelAction = TRUE
ENDIF
ENDIF
IF bDoRappelAction
// walk down if pressing stick down
IF rappelData.vAttachPos.z > rappelData.fLimitZ
SET_RAPPEL_STATE(rappelData, RAPPEL_STATE_WALKING)
ENDIF
ENDIF
ELSE
IF bIsAi
IF rappelData.ai = RAPPEL_AI_MOVE_FAST
bDoRappelAction = TRUE
ENDIF
ELSE
IF IS_DISABLED_CONTROL_JUST_PRESSED(PLAYER_CONTROL, GET_NORMAL_JUMP_CONTROL())
OR (IS_DISABLED_CONTROL_JUST_PRESSED(PLAYER_CONTROL, GET_BIG_JUMP_CONTROL()) AND rappelData.bSetBigJumpControlAsNormal)
bDoRappelAction = TRUE
ENDIF
ENDIF
IF bDoRappelAction
IF NOT HAS_FREE_ROPING_FINISHED(rappelData)
OR rappelData.ped = PLAYER_PED_ID()
SET_RAPPEL_STATE(rappelData, RAPPEL_STATE_SLIDING)
ENDIF
ENDIF
ENDIF
ENDIF
BREAK
CASE RAPPEL_STATE_WALKING
// pop back into idle if not pressed
IF bIsAI
IF rappelData.ai <> RAPPEL_AI_MOVE_SLOW
bDoRappelAction = TRUE
ENDIF
ELSE
IF iLY < STICK_MOVEMENT_RAPPEL_DOWN
bDoRappelAction = TRUE
ENDIF
ENDIF
BOOL bAtLimit
bAtLimit = FALSE
IF rappelData.vAttachPos.z <= rappelData.fLimitZ
bDoRappelAction = TRUE
bAtLimit = TRUE
ENDIF
IF bDoRappelAction
IF IS_ENTITY_PLAYING_ANIM(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), "Rappel_Walk")
fAnimPhase = GET_ENTITY_ANIM_CURRENT_TIME(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), "Rappel_Walk")
IF (fAnimPhase >= 0.96 AND fAnimPhase <= 1.0)
SET_ENTITY_ANIM_SPEED(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), "rappel_walk", 0.5)
SET_RAPPEL_STATE(rappelData, RAPPEL_STATE_IDLING)
ELIF bAtLimit
SET_RAPPEL_STATE(rappelData, RAPPEL_STATE_IDLING, FALSE, FALSE, TRUE, 0, TRUE)
ENDIF
ELSE
SET_RAPPEL_STATE(rappelData, RAPPEL_STATE_IDLING)
ENDIF
ENDIF
BREAK
CASE RAPPEL_STATE_JUMPING_SIDEWAYS
// when anim finished, go back to idle
CONST_FLOAT SIDE_JUMP_DECELERATION 3.0
// swinging out
IF rappelData.bCurrentSideJumpIsRight
rappelData.fSideJumpSpeed = rappelData.fSideJumpSpeed -@ SIDE_JUMP_DECELERATION
ELSE
rappelData.fSideJumpSpeed = rappelData.fSideJumpSpeed +@ SIDE_JUMP_DECELERATION
ENDIF
rappelData.vAttachPos.x = rappelData.vAttachPos.x +@ rappelData.fSideJumpSpeed
IF rappelData.bCurrentSideJumpIsRight
IF rappelData.vAttachPos.x < 0.0
rappelData.vAttachPos.x = 0.0
SET_RAPPEL_STATE(rappelData, RAPPEL_STATE_IDLING)
ENDIF
ELSE
IF rappelData.vAttachPos.x > 0.0
rappelData.vAttachPos.x = 0.0
SET_RAPPEL_STATE(rappelData, RAPPEL_STATE_IDLING)
ENDIF
ENDIF
BREAK
CASE RAPPEL_STATE_JUMPING_STATIC
CASE RAPPEL_STATE_JUMPING_DOWN
// when anim finished, go back to idle
BOOL bResetToIdle
bResetToIdle = FALSE
IF IS_ENTITY_PLAYING_ANIM(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), "Rappel_Jump_a")
OR IS_ENTITY_PLAYING_ANIM(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), "rappel_jump_c")
IF IS_ENTITY_PLAYING_ANIM(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), "rappel_jump_c")
SET_ENTITY_ANIM_SPEED(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), "rappel_jump_c", rappelData.fOverrideBigJumpSpeed)
fAnimPhase = GET_ENTITY_ANIM_CURRENT_TIME(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), "rappel_jump_c")
IF DOES_ENTITY_EXIST(rappelData.pulleyObject)
IF IS_ENTITY_PLAYING_ANIM(rappelData.pulleyObject, GET_RAPPEL_ANIM_DICT_NAME(), "Rappel_Jump_c_Prop")
IF fAnimPhase >=0 AND fAnimPhase <= 1
SET_ENTITY_ANIM_CURRENT_TIME(rappelData.pulleyObject, GET_RAPPEL_ANIM_DICT_NAME(), "Rappel_Jump_c_Prop", fAnimPhase)
SET_ENTITY_ANIM_SPEED(rappelData.pulleyObject, GET_RAPPEL_ANIM_DICT_NAME(), "Rappel_Jump_c_Prop", rappelData.fOverrideBigJumpSpeed)
ENDIF
ENDIF
ENDIF
IF NOT rappelData.bDisabledPainAudio
IF rappelData.ped = PLAYER_PED_ID()
IF NOT rappelData.bDoneLandGrunt
IF fAnimPhase >= 0.82
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
IF GET_RANDOM_INT_IN_RANGE(0,4) = 0
PLAY_PAIN(PLAYER_PED_ID(), AUD_DAMAGE_REASON_CLIMB_SMALL,0)
ENDIF
ENDIF
rappelData.bDoneLandGrunt = TRUE
ENDIF
ENDIF
ENDIF
ENDIF
ELIF IS_ENTITY_PLAYING_ANIM(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), "Rappel_Jump_a")
SET_ENTITY_ANIM_SPEED(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), "rappel_jump_a", rappelData.fOverrideSmallJumpSpeed)
fAnimPhase = GET_ENTITY_ANIM_CURRENT_TIME(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), "Rappel_Jump_a")
IF DOES_ENTITY_EXIST(rappelData.pulleyObject)
IF IS_ENTITY_PLAYING_ANIM(rappelData.pulleyObject, GET_RAPPEL_ANIM_DICT_NAME(), "Rappel_Jump_A_Prop")
IF fAnimPhase >=0 AND fAnimPhase <= 1
SET_ENTITY_ANIM_CURRENT_TIME(rappelData.pulleyObject, GET_RAPPEL_ANIM_DICT_NAME(), "Rappel_Jump_A_Prop", fAnimPhase)
SET_ENTITY_ANIM_SPEED(rappelData.pulleyObject, GET_RAPPEL_ANIM_DICT_NAME(), "Rappel_Jump_A_Prop", rappelData.fOverrideSmallJumpSpeed)
ENDIF
ENDIF
ENDIF
IF NOT rappelData.bDisabledPainAudio
IF rappelData.ped = PLAYER_PED_ID()
IF NOT rappelData.bDoneLandGrunt
IF fAnimPhase >= 0.740
IF GET_RANDOM_INT_IN_RANGE(0,4) = 0
PLAY_PAIN(PLAYER_PED_ID(), AUD_DAMAGE_REASON_CLIMB_SMALL,0)
ENDIF
rappelData.bDoneLandGrunt = TRUE
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ELSE
IF GET_GAME_TIMER() >= rappelData.iJumpStartTime + 300
AND bAllowResetToIdle
bResetToIdle = TRUE
ENDIF
ENDIF
IF NOT bResetToIdle
IF fAnimPhase >= 0.99
bResetToIdle = TRUE
ELSE
BOOL bControlPressed
bControlPressed = FALSE
IF (bIsAI AND rappelData.AI <> RAPPEL_AI_STOP)
OR bForceSlowJumpDown
bControlPressed = TRUE
ELSE
IF rappelData.bCurrentJumpIsBig
IF IS_DISABLED_CONTROL_PRESSED(PLAYER_CONTROL, GET_BIG_JUMP_CONTROL())
bControlPressed = TRUE
ENDIF
ELSE
IF IS_DISABLED_CONTROL_PRESSED(PLAYER_CONTROL, GET_NORMAL_JUMP_CONTROL())
OR (IS_DISABLED_CONTROL_PRESSED(PLAYER_CONTROL, GET_BIG_JUMP_CONTROL()) AND rappelData.bSetBigJumpControlAsNormal)
bControlPressed = TRUE
ELSE
IF rappelData.bForceSmallJumpOnBigJumpControl
IF IS_DISABLED_CONTROL_PRESSED(PLAYER_CONTROL, GET_BIG_JUMP_CONTROL())
bControlPressed = TRUE
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
IF bControlPressed
AND fAnimPhase < MAX_STRENGTH_JUMP_PHASE
AND NOT (bIsAI AND fAnimPhase > rappelData.fOverrideMaxJumpPhase)
rappelData.fCurrentJumpStrength = fAnimPhase / MAX_STRENGTH_JUMP_PHASE
IF rappelData.fCurrentJumpStrength > 1.0
rappelData.fCurrentJumpStrength = 1.0
ELIF rappelData.fCurrentJumpStrength < 0.0
rappelData.fCurrentJumpStrength = 0.0
ENDIF
ELSE
// calculate the final anim phase the jump will stop on
IF rappelData.fStopDescendPhase < 0
IF bControlPressed
AND NOT (bIsAI AND fAnimPhase > rappelData.fOverrideMaxJumpPhase)
rappelData.fCurrentJumpStrength = 1.0
ENDIF
//IF NOT bIsAI
IF NOT rappelData.bCurrentJumpIsBig
rappelData.fStopDescendPhase = MIN_STOP_DESCEND_PHASE + ((MAX_STOP_DESCEND_PHASE - MIN_STOP_DESCEND_PHASE) * rappelData.fCurrentJumpStrength)
rappelData.fJumpDeceleration = JUMP_DOWN_MIN_DECELERATION_SMALL + ((JUMP_DOWN_MAX_DECELERATION_SMALL - JUMP_DOWN_MIN_DECELERATION_SMALL) * rappelData.fCurrentJumpStrength)
ELSE
rappelData.fStopDescendPhase = MIN_STOP_DESCEND_PHASE_BIG + ((MAX_STOP_DESCEND_PHASE_BIG - MIN_STOP_DESCEND_PHASE_BIG) * rappelData.fCurrentJumpStrength)
rappelData.fJumpDeceleration = JUMP_DOWN_MIN_DECELERATION_BIG + ((JUMP_DOWN_MAX_DECELERATION_BIG - JUMP_DOWN_MIN_DECELERATION_BIG) * rappelData.fCurrentJumpStrength)
ENDIF
//PRINTSTRING("stop descend phase = ") PRINTFLOAT(rappelData.fStopDescendPhase) PRINTNL()
//PRINTSTRING("decel = ") PRINTFLOAT(rappelData.fJumpDeceleration) PRINTNL()
//ELSE
// rappelData.fStopDescendPhase = fAnimPhase
// IF NOT rappelData.bCurrentJumpIsBig
/// rappelData.fJumpDeceleration = JUMP_DOWN_MAX_DECELERATION_SMALL
// ELSE
// rappelData.fJumpDeceleration = JUMP_DOWN_MAX_DECELERATION_BIG
// ENDIF
//ENDIF
ENDIF
ENDIF
// set speed
IF SHOULD_RAPPEL_JUMP_BE_MOVING(rappelData, fAnimPhase)
IF fAnimPhase <= rappelData.fStopDescendPhase
OR (bControlPressed AND rappelData.fStopDescendPhase < 0)
IF rappelData.bCurrentJumpIsBig
rappelData.fSpeed = rappelData.fSpeed +@ JUMP_DOWN_BIG_ACCELERATION
ELSE
rappelData.fSpeed = rappelData.fSpeed +@ JUMP_DOWN_ACCELERATION
ENDIF
ELSE
rappelData.fSpeed = rappelData.fSpeed -@ rappelData.fJumpDeceleration
ENDIF
FLOAT fMaxSpeed
IF NOT rappelData.bCurrentJumpIsBig
IF fAnimPhase <= rappelData.fStopDescendPhase
OR rappelData.fStopDescendPhase >= MAX_STOP_DESCEND_PHASE
fMaxSpeed = JUMP_DOWN_SPEED_ACCELERATING
ELSE
fMaxSpeed = JUMP_DOWN_SPEED
ENDIF
ELSE
fMaxSpeed = JUMP_DOWN_SPEED_BIG
ENDIF
IF rappelData.fSpeed > fMaxSpeed
rappelData.fSpeed = fMaxSpeed
ELIF rappelData.fSpeed < 0
rappelData.fSpeed = 0
ENDIF
IF bForceSlowJumpDown
rappelData.fSpeed /= 1.75
ENDIF
ELSE
rappelData.fSpeed = 0.0
ENDIF
ENDIF
ENDIF
IF bResetToIdle
SET_RAPPEL_STATE(rappelData, RAPPEL_STATE_IDLING)
ELSE
// allow convert static jump into jumping down
IF rappelData.state = RAPPEL_STATE_JUMPING_STATIC
IF fAnimPhase <= ALLOW_JUMP_DOWN_CONVERT_PHASE
IF bIsAI
IF rappelData.ai = RAPPEL_AI_MOVE_FAST
bDoRappelAction = TRUE
ENDIF
ELSE
IF iLY > STICK_MOVEMENT_RAPPEL_DOWN
bDoRappelAction = TRUE
ENDIF
ENDIF
IF bDoRappelAction
SET_RAPPEL_STATE(rappelData, RAPPEL_STATE_JUMPING_DOWN, FALSE, FALSE, FALSE)
//PLAY_RAPPEL_SOUND(rappelData, RAPPEL_SOUND_RAPPEL)
ENDIF
ENDIF
ENDIF
ENDIF
//IF rappelData.ped = PLAYER_PED_ID()
// printfloat(rappelData.fSpeed) printnl()
//ENDIF
BREAK
CASE RAPPEL_STATE_SLIDING
IF bIsAI
IF rappelData.ai = RAPPEL_AI_MOVE_FAST
bDoRappelAction = TRUE
ENDIF
ELSE
bDoRappelAction = TRUE
//IF IS_DISABLED_CONTROL_PRESSED(PLAYER_CONTROL, GET_NORMAL_JUMP_CONTROL())
// bDoRappelAction = TRUE
//ENDIF
ENDIF
IF HAS_FREE_ROPING_FINISHED(rappelData)
bDoRappelAction = FALSE
ENDIF
IF bDoRappelAction
IF IS_DISABLED_CONTROL_PRESSED(PLAYER_CONTROL, GET_NORMAL_JUMP_CONTROL())
OR bIsAI
rappelData.fSpeed = rappelData.fSpeed +@ SLIDE_ACCEL
IF rappelData.fSpeed >= DEFAULT_MAX_FAST_SLIDE_SPEED
rappelData.fSpeed = DEFAULT_MAX_FAST_SLIDE_SPEED
ENDIF
ELSE
FLOAT fMaxSpeed
fMaxSpeed = rappelData.fSpeed
IF fMaxSpeed < rappelData.fMaxSlideSpeed
fMaxSpeed = rappelData.fMaxSlideSpeed
ENDIF
rappelData.fSpeed = rappelData.fSpeed +@ SLIDE_ACCEL
IF rappelData.fSpeed >= fMaxSpeed
rappelData.fSpeed = fMaxSpeed
ENDIF
ENDIF
ELSE
rappelData.fSpeed = rappelData.fSpeed -@ SLIDE_DECEL
IF rappelData.fSpeed <= 0.0
rappelData.fSpeed = 0.0
SET_RAPPEL_STATE(rappelData, RAPPEL_STATE_IDLING)
ENDIF
ENDIF
printfloat(rappelData.fSpeed) printnl()
BREAK
CASE RAPPEL_STATE_TRANSITION_TO_FREE_ROPING
BOOL bGotoIdle
bGotoIdle = FALSE
IF IS_ENTITY_PLAYING_ANIM(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), "rappel_to_free_rope")
fAnimPhase = GET_ENTITY_ANIM_CURRENT_TIME(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), "rappel_to_free_rope")
IF fAnimPhase <= 0.74
AND fAnimPhase >= 0.38
rappelData.fSpeed = rappelData.fSpeed +@ SLIDE_ACCEL * 2
IF rappelData.fSpeed >= rappelData.fMaxSlideSpeed
rappelData.fSpeed = rappelData.fMaxSlideSpeed
ENDIF
ELSE
rappelData.fSpeed = rappelData.fSpeed -@ SLIDE_DECEL
IF rappelData.fSpeed <= 0.0
rappelData.fSpeed = 0.0
ENDIF
ENDIF
//printfloat(rappelData.fSpeed) printnl()
IF fAnimPhase >= 0.98
bGotoIdle = TRUE
ENDIF
ELSE
//bGotoIdle = TRUE
ENDIF
IF bGotoIdle
rappelData.bFreeRoping = TRUE
SET_RAPPEL_STATE(rappelData, RAPPEL_STATE_IDLING)
ENDIF
BREAK
CASE RAPPEL_STATE_DISMOUNT_FREE_ROPING
BOOL bCancel
bCancel = FALSE
IF IS_ENTITY_PLAYING_ANIM(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), "land_action")
IF GET_ENTITY_ANIM_CURRENT_TIME(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), "land_action") >= 0.64
bCancel = TRUE
ENDIF
ELSE
IF GET_GAME_TIMER() >= rappelData.iSetStateTime + 500
bCancel = TRUE
ENDIF
ENDIF
IF bCancel
CANCEL_RAPPEL(rappelData, FALSE, rappelData.dismountWeapon, TRUE, FALSE)
rappelData.bUnpinnedForDismount = TRUE
ENDIF
BREAK
ENDSWITCH
bDoRappelAction = FALSE
IF NOT rappelData.bFreeRoping
BOOL bDoingBigJump = FALSE
BOOL bDoingSideJump = FALSE
// handle jump
IF bIsAI
IF rappelData.ai = RAPPEL_AI_MOVE_FAST
bDoRappelAction = TRUE
bDoingBigJump = rappelData.bAllowBigJumps
ELIF rappelData.ai = RAPPEL_AI_SWING_LEFT
OR rappelData.ai = RAPPEL_AI_SWING_RIGHT
bDoRappelAction = TRUE
bDoingSideJump = rappelData.bAllowSideJumps
ENDIF
ELSE
IF IS_DISABLED_CONTROL_JUST_PRESSED(PLAYER_CONTROL, GET_NORMAL_JUMP_CONTROL())
OR (IS_DISABLED_CONTROL_PRESSED(PLAYER_CONTROL, GET_BIG_JUMP_CONTROL()) AND rappelData.bSetBigJumpControlAsNormal)
bDoRappelAction = TRUE
ELIF IS_DISABLED_CONTROL_PRESSED(PLAYER_CONTROL, GET_BIG_JUMP_CONTROL()) AND rappelData.bAllowBigJumps
bDoRappelAction = TRUE
bDoingBigJump = TRUE
ENDIF
ENDIF
IF bDoRappelAction
// jumping down
BOOL bAllowJump = FALSE
IF rappelData.state <= RAPPEL_STATE_WALKING
bAllowJump = TRUE
ELSE
IF fAnimPhase > ALLOW_START_NEW_JUMP_PHASE
IF bAllowRapidConvertJump
bAllowJump = TRUE
ENDIF
ENDIF
ENDIF
IF bAllowJump
IF iLY > STICK_MOVEMENT_RAPPEL_DOWN OR (bIsAI AND rappelData.ai = RAPPEL_AI_MOVE_FAST) OR bDoingBigJump OR bForceSlowJumpDown
// do a small or big jump
IF rappelData.bAllowSmallJumps
OR bDoingBigJump
rappelData.bCurrentJumpIsBig = bDoingBigJump
IF rappelData.bForceSmallJumpOnBigJumpControl
rappelData.bCurrentJumpIsBig = FALSE
ENDIF
SET_RAPPEL_STATE(rappelData, RAPPEL_STATE_JUMPING_DOWN, FALSE, TRUE)
ENDIF
ELIF iLX > STICK_MOVEMENT_RAPPEL_SIDE
OR iLX < -STICK_MOVEMENT_RAPPEL_SIDE
OR bDoingSideJump
// do a side jump
IF rappelData.bAllowSideJumps
SET_RAPPEL_STATE(rappelData, RAPPEL_STATE_JUMPING_SIDEWAYS, FALSE, TRUE)
IF iLX > 0
OR rappelData.ai = RAPPEL_AI_SWING_RIGHT
rappelData.bCurrentSideJumpIsRight = TRUE
rappelData.fSideJumpSpeed = 2.8
ELIF iLX < 0
OR rappelData.ai = RAPPEL_AI_SWING_LEFT
rappelData.bCurrentSideJumpIsRight = FALSE
rappelData.fSideJumpSpeed = -2.5
ENDIF
ENDIF
ELSE
// do a static jump
SET_RAPPEL_STATE(rappelData, RAPPEL_STATE_JUMPING_STATIC, FALSE, TRUE)
ENDIF
ENDIF
ENDIF
ENDIF
// move down by speed
IF rappelData.vAttachPos.z > rappelData.fLimitZ
IF rappelData.state <> RAPPEL_STATE_WALKING
IF rappelData.vAttachPos.z <= (rappelData.fLimitZ + rappelData.fSlowDownByLimitRange)
rappelData.fSpeed = rappelData.fSpeed -@ FAST_DECELERATION
IF rappelData.fSpeed < 0
rappelData.fSpeed = 0
ENDIF
ENDIF
rappelData.vAttachPos.z = rappelData.vAttachPos.z -@ rappelData.fSpeed
ELSE
VECTOR vDisplace = GET_PED_EXTRACTED_DISPLACEMENT(rappelData.ped, FALSE)
rappelData.vAttachPos.z += vDisplace.z
ENDIF
ENDIF
// apply additional attach offset for free roping ped
VECTOR vModAttach = <<0,0,0>>
/*
IF rappelData.bFreeRoping
vModAttach.x = -0.0137329
vModAttach.y = -0.160645
ELSE
vModAttach.x = -0.0141535
vModAttach.y = -0.385494
ENDIF
*/
FLOAT fConvertToFreeRopingPhase
IF rappelData.bFreeRoping
fConvertToFreeRopingPhase = 1.0
ELSE
IF rappelData.state <> RAPPEL_STATE_TRANSITION_TO_FREE_ROPING
fConvertToFreeRopingPhase = 0.0
ELSE
IF IS_ENTITY_PLAYING_ANIM(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), "rappel_to_free_rope")
fConvertToFreeRopingPhase = GET_ENTITY_ANIM_CURRENT_TIME(rappelData.ped, GET_RAPPEL_ANIM_DICT_NAME(), "rappel_to_free_rope")
ENDIF
ENDIF
ENDIF
IF fConvertToFreeRopingPhase < 0
fConvertToFreeRopingPhase = 0
ENDIF
vModAttach.x = -0.0141535 + (-0.0004206 * fConvertToFreeRopingPhase)
vModAttach.y = -0.385494 + (0.224849 * fConvertToFreeRopingPhase)
IF rappelData.state <> RAPPEL_STATE_DISMOUNT_FREE_ROPING
ATTACH_ENTITY_TO_ENTITY(rappelData.ped, rappelData.anchorObject, 0, rappelData.vAttachPos + vModAttach, <<0,0,0>>, TRUE)
ENDIF
// do help text
IF NOT bIsAI
IF NOT rappelData.bShownHelp
IF NOT rappelData.bFreeRoping
PRINT_HELP("RAPPEL_HELP")
ELSE
PRINT_HELP("FREEROPE_HELP")
ENDIF
rappelData.bShownHelp = TRUE
ENDIF
ENDIF
ELSE
CANCEL_RAPPEL(rappelData)
ENDIF
ENDPROC
// delete rappel rope
PROC DELETE_RAPPEL_ROPE(RAPPEL_DATA &rappelData)
IF rappelData.bRopeCreated
IF DOES_ROPE_EXIST(rappelData.rope)
DELETE_ROPE(rappelData.rope)
ENDIF
IF DOES_ENTITY_EXIST(rappelData.weightObject)
DELETE_OBJECT(rappelData.weightObject)
ENDIF
IF DOES_ENTITY_EXIST(rappelData.pulleyObject)
DELETE_OBJECT(rappelData.pulleyObject)
ENDIF
rappelData.bRopeCreated = FALSE
ENDIF
ENDPROC
// fill rope properties
PROC FILL_ROPE_PROPERTIES(RAPPEL_DATA &rappelData, FLOAT fLength)
rappelData.iRopeVertexCount = GET_ROPE_VERTEX_COUNT(rappelData.rope)
rappelData.iLastRopeVertex = 0
rappelData.iNextRopeVertex = rappelData.iLastRopeVertex + 1
rappelData.fLength = fLength
rappelData.fRopeSegmentLength = fLength / (rappelData.iRopeVertexCount - 1)
ENDPROC
// create the rappel rope
PROC CREATE_RAPPEL_ROPE(RAPPEL_DATA &rappelData, FLOAT fLength, BOOL b32Vertex = TRUE, BOOL bCreateAsUnfurled = TRUE, FLOAT fTimeMultiplier = DEFAULT_ROPE_WEIGHT, FLOAT fCreateRopeLength = -1.0)
INT i
DELETE_RAPPEL_ROPE(rappelData)
IF DOES_ENTITY_EXIST(rappelData.anchorObject)
PHYSICS_ROPE_TYPE ropeType
IF b32Vertex
ropeType = PHYSICS_ROPE_THIN_WIRE_32
ELSE
ropeType = PHYSICS_ROPE_DEFAULT
ENDIF
FLOAT fRopeLength
IF fCreateRopeLength < 0
fRopeLength = fLength
ELSE
fRopeLength = fCreateRopeLength
ENDIF
rappelData.rope = ADD_ROPE(GET_ENTITY_COORDS(rappelData.anchorObject), <<0.0, 90.0, 0.0>>, fRopeLength, ropeType, fRopeLength, 0.5, 0.5, TRUE, FALSE, TRUE, fTimeMultiplier)
FILL_ROPE_PROPERTIES(rappelData, fLength)
IF bCreateAsUnfurled
REPEAT rappelData.iRopeVertexCount i
PIN_ROPE_VERTEX(rappelData.rope, i, GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(rappelData.anchorObject, <<0,0,-(rappelData.fRopeSegmentLength * i)>>))
ENDREPEAT
rappelData.bUnfurling = FALSE
ELSE
VECTOR vPinOffset
REPEAT rappelData.iRopeVertexCount i
IF i=0
vPinOffset = <<0,0,0>>
ELSE
IF i%2 = 0
vPinOffset.x = 1
ELSE
vPinOffset.x = -1
ENDIF
vPinOffset.y = 0
vPinOffset.z = i * -0.02
ENDIF
PIN_ROPE_VERTEX(rappelData.rope, i, GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(rappelData.anchorObject, vPinOffset))
ENDREPEAT
rappelData.bUnfurling = TRUE
ENDIF
rappelData.bAttachedWeight = FALSE
rappelData.bAllowRopeMovement = FALSE
rappelData.bRopeCreated = TRUE
ENDIF
ENDPROC
// draw the rappel rope
PROC HANDLE_RAPPEL_ROPE(RAPPEL_DATA &rappelData, BOOL bSwapHands = FALSE, BOOL bOverrideHand = FALSE, BOOL bInstantSnap = FALSE, BOOL bAfterDismount = FALSE)
INT i
IF DOES_ENTITY_EXIST(rappelData.anchorObject)
IF NOT rappelData.bUnfurling
IF rappelData.bAllowRopeMovement
IF (bAfterDismount OR NOT IS_PED_INJURED(rappelData.ped))
AND NOT rappelData.bUnpinnedForDismount
VECTOR vHand
VECTOR vAnchor
VECTOR vUnitVector
VECTOR vPinVertex
VECTOR vInterpolatedVertexPos
FLOAT fAnchorToHandDist
FLOAT fDistanceToNext
FLOAT fRopeLengthToVertex
IF NOT bAfterDismount
PROCESS_ENTITY_ATTACHMENTS(rappelData.ped)
ENDIF
IF NOT rappelData.bOverrideRopeOrigin
vAnchor = GET_ENTITY_COORDS(rappelData.anchorObject)
ELSE
vAnchor = rappelData.vOverrideRopeOrigin
ENDIF
IF NOT bAfterDismount
IF rappelData.bFreeRoping
vHand = GET_PED_BONE_COORDS(rappelData.ped, BONETAG_R_HAND, <<FREE_ROPE_HAND_OFFSET_X, FREE_ROPE_HAND_OFFSET_Y, 0>>)
ELSE
PED_BONETAG handBone
IF NOT bSwapHands
handBone = BONETAG_PH_L_HAND
ELSE
handBone = BONETAG_PH_R_HAND
ENDIF
vHand = GET_PED_BONE_COORDS(rappelData.ped, handBone, <<0,0,0>>)
ENDIF
rappelData.vCacheHand = vHand
ELSE
vHand = rappelData.vCacheHand
IF vHand.x < vAnchor.x
vHand.x = vHand.x +@ 0.05
IF vHand.x > vAnchor.x
vHand.x = vAnchor.x
ENDIF
ELIF vHand.x > vAnchor.x
vHand.x = vHand.x -@ 0.05
IF vHand.x < vAnchor.x
vHand.x = vAnchor.x
ENDIF
ENDIF
IF vHand.y < vAnchor.y
vHand.y = vHand.y +@ 0.05
IF vHand.y > vAnchor.y
vHand.y = vAnchor.y
ENDIF
ELIF vHand.y > vAnchor.y
vHand.y = vHand.y -@ 0.05
IF vHand.y < vAnchor.y
vHand.y = vAnchor.y
ENDIF
ENDIF
rappelData.vCacheHand = vHand
ENDIF
IF bOverrideHand
vHand = rappelData.vOverrideHand
ENDIF
fAnchorToHandDist = GET_DISTANCE_BETWEEN_COORDS(vAnchor, vHand)
// update current last vertex passed
IF NOT bInstantSnap
AND NOT bAfterDismount
IF rappelData.iNextRopeVertex < (rappelData.iRopeVertexCount-1)
fDistanceToNext = rappelData.fRopeSegmentLength * rappelData.iNextRopeVertex
IF fAnchorToHandDist > fDistanceToNext
rappelData.iLastRopeVertex++
rappelData.iNextRopevertex++
rappelData.bDirectlyPinNextVertex = FALSE
ENDIF
ENDIF
ENDIF
REPEAT rappelData.iRopeVertexCount i
// pinning / unpinning
IF i = 0 // pin top of rope to anchor
PIN_ROPE_VERTEX(rappelData.rope, i, vAnchor)
ELIF i <= rappelData.iNextRopeVertex + 1
fRopeLengthToVertex = rappelData.fRopeSegmentLength * i
vUnitVector = (vHand - vAnchor) / fAnchorToHandDist
vPinVertex = <<vAnchor.x + (fRopeLengthToVertex * vUnitVector.x), vAnchor.y + (fRopeLengthToVertex * vUnitVector.y), vAnchor.z + (fRopeLengthToVertex * vUnitVector.z)>>
IF i <> rappelData.iNextRopeVertex + 1
OR rappelData.bDirectlyPinNextVertex
OR bInstantSnap
// direcly pin to desired point
PIN_ROPE_VERTEX(rappelData.rope, i, vPinVertex)
ELSE
// otherwise interpolate nicely to avoid pop
VECTOR vNextVertexPos = GET_ROPE_VERTEX_COORD(rappelData.rope, i)
FLOAT fNextVertexDistanceToPin = GET_DISTANCE_BETWEEN_COORDS(vPinVertex, vNextVertexPos)
IF fNextVertexDistanceToPin <= DIRECT_PIN_NEXT_VERTEX_DIST
rappelData.bDirectlyPinNextVertex = TRUE
PIN_ROPE_VERTEX(rappelData.rope, i, vPinVertex)
ELSE
FLOAT fRopeVertexInterpSpeed = fNextVertexDistanceToPin * 7.5
//PRINTSTRING("distance to pin = ") PRINTFLOAT(fNextVertexDistanceToPin) PRINTNL()
//PRINTSTRING("interp speed = ") PRINTFLOAT(fRopeVertexInterpSpeed) PRINTNL()
IF vPinVertex.x >= vNextVertexPos.x
vInterpolatedVertexPos.x = vNextVertexPos.x +@ fRopeVertexInterpSpeed
IF vInterpolatedVertexPos.x > vPinVertex.x
vInterpolatedVertexPos.x = vPinVertex.x
ENDIF
ELIF vPinVertex.x <= vNextVertexPos.x
vInterpolatedVertexPos.x = vNextVertexPos.x -@ fRopeVertexInterpSpeed
IF vInterpolatedVertexPos.x < vPinVertex.x
vInterpolatedVertexPos.x = vPinVertex.x
ENDIF
ENDIF
IF vPinVertex.y >= vNextVertexPos.y
vInterpolatedVertexPos.y = vNextVertexPos.y +@ fRopeVertexInterpSpeed
IF vInterpolatedVertexPos.y > vPinVertex.y
vInterpolatedVertexPos.y = vPinVertex.y
ENDIF
ELIF vPinVertex.y <= vNextVertexPos.y
vInterpolatedVertexPos.y = vNextVertexPos.y -@ fRopeVertexInterpSpeed
IF vInterpolatedVertexPos.y < vPinVertex.y
vInterpolatedVertexPos.y = vPinVertex.y
ENDIF
ENDIF
IF vPinVertex.z >= vNextVertexPos.z
vInterpolatedVertexPos.z = vNextVertexPos.z +@ fRopeVertexInterpSpeed
IF vInterpolatedVertexPos.z > vPinVertex.z
vInterpolatedVertexPos.z = vPinVertex.z
ENDIF
ELIF vPinVertex.z <= vNextVertexPos.z
vInterpolatedVertexPos.z = vNextVertexPos.z -@ fRopeVertexInterpSpeed
IF vInterpolatedVertexPos.z < vPinVertex.z
vInterpolatedVertexPos.z = vPinVertex.z
ENDIF
ENDIF
PIN_ROPE_VERTEX(rappelData.rope, i, vInterpolatedVertexPos)
ENDIF
ENDIF
ELSE
IF NOT bInstantSnap
AND NOT bAfterDismount
// handle last vertex
IF i=rappelData.iRopeVertexCount - 1
IF NOT rappelData.bPinnedLastVertex
UNPIN_ROPE_VERTEX(rappelData.rope, i)
IF GET_GAME_TIMER() >= rappelData.iLastVertexTime
VECTOR vLastVertex = GET_ROPE_VERTEX_COORD(rappelData.rope, i)
VECTOR vLastOffset = GET_OFFSET_FROM_ENTITY_GIVEN_WORLD_COORDS(rappelData.ped, vLastVertex)
CONST_FLOAT STOP_LAST_VERTEX_Y 0.68
IF vLastOffset.y > STOP_LAST_VERTEX_Y
PIN_ROPE_VERTEX(rappelData.rope, i, GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(rappelData.ped, <<vLastOffset.x, STOP_LAST_VERTEX_Y, vLastOffset.z>>))
rappelData.iLastVertexTime = GET_GAME_TIMER() + 50
rappelData.bPinnedLastVertex = TRUE
ENDIF
ENDIF
ELSE
IF GET_GAME_TIMER() >= rappelData.iLastVertexTime
UNPIN_ROPE_VERTEX(rappelData.rope, i)
rappelData.iLastVertexTime = GET_GAME_TIMER() + 1000
rappelData.bPinnedLastVertex = FALSE
ENDIF
ENDIF
ELSE
UNPIN_ROPE_VERTEX(rappelData.rope, i)
ENDIF
ENDIF
ENDIF
#IF IS_DEBUG_BUILD
IF bDebugRappelInfo
SET_DEBUG_LINES_AND_SPHERES_DRAWING_ACTIVE(TRUE)
DRAW_DEBUG_SPHERE(GET_ROPE_VERTEX_COORD(rappelData.rope, i), 0.05)
ENDIF
#ENDIF
ENDREPEAT
ENDIF
ELSE
rappelData.bAllowRopeMovement = TRUE
ENDIF
ELSE
IF NOT rappelData.bAllowRopeMovement
UNPIN_ALL_ROPE_VERTICES_EXCEPT_ANCHOR(rappelData)
rappelData.weightObject = CREATE_OBJECT_NO_OFFSET(GET_RAPPEL_ANCHOR_MODEL_NAME(), GET_ROPE_VERTEX_COORD(rappelData.rope, rappelData.iRopeVertexCount-1))
SET_ACTIVATE_OBJECT_PHYSICS_AS_SOON_AS_IT_IS_UNFROZEN(rappelData.weightObject, TRUE)
SET_ENTITY_VISIBLE(rappelData.weightObject, FALSE)
rappelData.bAllowRopeMovement = TRUE
ELSE
//REPEAT rappelData.iRopeVertexCount i
// SET_DEBUG_LINES_AND_SPHERES_DRAWING_ACTIVE(TRUE)
// DRAW_DEBUG_SPHERE(GET_ROPE_VERTEX_COORD(rappelData.rope, i), 0.1)
//ENDREPEAT
IF NOT rappelData.bAttachedWeight
IF DOES_ENTITY_HAVE_PHYSICS(rappelData.weightObject)
UNPIN_ROPE_VERTEX(rappelData.rope, 0)
APPLY_FORCE_TO_ENTITY(rappelData.weightObject, APPLY_TYPE_EXTERNAL_IMPULSE, <<0,0,-1>>, <<0,0,0>>, 0, TRUE, TRUE, TRUE)
//ATTACH_ROPE_TO_ENTITY(rappelData.rope, rappelData.weightObject, GET_ROPE_VERTEX_COORD(rappelData.rope, 0), 0)
ATTACH_ENTITIES_TO_ROPE(rappelData.rope, rappelData.anchorObject, rappelData.weightObject, GET_ENTITY_COORDS(rappelData.anchorObject), GET_ENTITY_COORDS(rappelData.weightObject), rappelData.fLength, 0, 0)
rappelData.bAttachedWeight = TRUE
ENDIF
ENDIF
// check to see if can stop unfurling stage
VECTOR vAnchorPos = GET_ENTITY_COORDS(rappelData.anchorObject)
BOOL bCanStopUnfurling = TRUE
FOR i=1 TO 5
CONST_FLOAT STOP_UNFURLING_DISTANCE_XY 1.0
CONST_FLOAT STOP_UNFURLING_DISTANCE_Z 0.7
VECTOR vVertexPos = GET_ROPE_VERTEX_COORD(rappelData.rope, i)
IF vAnchorPos.z - vVertexPos.z <= (rappelData.fRopeSegmentLength * i * STOP_UNFURLING_DISTANCE_Z)
bCanStopUnfurling = FALSE
ELSE
IF GET_DISTANCE_BETWEEN_COORDS(vAnchorPos, vVertexPos, FALSE) >= STOP_UNFURLING_DISTANCE_XY
bCanStopUnfurling = FALSE
ENDIF
ENDIF
ENDFOR
IF bCanStopUnfurling
rappelData.bUnfurling = FALSE
ENDIF
ENDIF
ENDIF
ENDIF
ENDPROC
// create anchor for rappel
PROC CREATE_RAPPEL_ANCHOR(RAPPEL_DATA &rappelData, VEHICLE_INDEX attachVehicle, VECTOR vAnchorPos, VECTOR vAnchorRot)
IF DOES_ENTITY_EXIST(rappelData.anchorObject)
DELETE_OBJECT(rappelData.anchorObject)
ENDIF
rappelData.anchorObject = CREATE_OBJECT(GET_RAPPEL_ANCHOR_MODEL_NAME(), vAnchorPos)
IF IS_VEHICLE_DRIVEABLE(attachVehicle)
ATTACH_ENTITY_TO_ENTITY(rappelData.anchorObject, attachVehicle, 0, vAnchorPos, vAnchorRot)
ELSE
SET_ENTITY_ROTATION(rappelData.anchorObject, vAnchorRot)
FREEZE_ENTITY_POSITION(rappelData.anchorObject, TRUE)
ENDIF
SET_ENTITY_VISIBLE(rappelData.anchorObject, FALSE)
ENDPROC
PROC CREATE_RAPPEL_PULLEY(RAPPEL_DATA &rappelData)
IF DOES_ENTITY_EXIST(rappelData.pulleyObject)
DELETE_OBJECT(rappelData.pulleyObject)
ENDIF
IF NOT IS_PED_INJURED(rappelData.ped)
rappelData.pulleyObject = CREATE_OBJECT(GET_RAPPEL_PULLEY_MODEL_NAME(), GET_ENTITY_COORDS(rappelData.ped))
ATTACH_ENTITY_TO_ENTITY(rappelData.pulleyObject, rappelData.ped, GET_PED_BONE_INDEX(rappelData.ped, BONETAG_PH_L_HAND), <<0,0,0>>, <<0,0,0>>)
ENDIF
ENDPROC
// set rappel AI
PROC SET_RAPPEL_AI(RAPPEL_DATA &rappelData, RAPPEL_AI_ENUM setAI)
rappelData.ai = setAI
ENDPROC
// set max free rope slide speed
PROC SET_RAPPEL_MAX_SLIDE_SPEED(RAPPEL_DATA &rappelData, FLOAT fMaxSpeed)
rappelData.fMaxSlideSpeed = fMaxSpeed
ENDPROC
// set rappel material
PROC SET_RAPPEL_MATERIAL(RAPPEL_DATA &rappelData, RAPPEL_MATERIAL_ENUM setMaterial)
rappelData.material = setMaterial
ENDPROC
// set allow small jumps
PROC SET_RAPPEL_ALLOW_SMALL_JUMPS(RAPPEL_DATA &rappelData, BOOL bAllow)
rappelData.bAllowSmallJumps = bAllow
ENDPROC
// set allow big jumps
PROC SET_RAPPEL_ALLOW_BIG_JUMPS(RAPPEL_DATA &rappelData, BOOL bAllow)
rappelData.bAllowBigJumps = bAllow
ENDPROC
// set force a small jump even if you hit the big jump button
PROC SET_RAPPEL_FORCE_SMALL_JUMP_ON_BIG_JUMP_CONTROL(RAPPEL_DATA &rappelData, BOOL bForce)
rappelData.bForceSmallJumpOnBigJumpControl = bForce
ENDPROC
// set allow side jumps
PROC SET_RAPPEL_ALLOW_SIDE_JUMPS(RAPPEL_DATA &rappelData, BOOL bAllow)
rappelData.bAllowSideJumps = bAllow
ENDPROC
// set big jump control as normal
PROC SET_RAPPEL_BIG_JUMP_CONTROL_AS_NORMAL(RAPPEL_DATA &rappelData, BOOL bSet)
rappelData.bSetBigJumpControlAsNormal = bSet
ENDPROC
// set to override rope origin
PROC SET_RAPPEL_OVERRIDE_ROPE_ORIGIN(RAPPEL_DATA &rappelData, BOOL bOverride, VECTOR vOverridePos)
rappelData.bOverrideRopeOrigin = bOverride
IF bOverride
rappelData.vOverrideRopeOrigin = vOverridePos
ENDIF
ENDPROC
// set to override bResetRunFromFiresAndExplosionsFlagOnCancel
PROC SET_RAPPEL_RESET_RUN_FROM_FIRES_AND_EXPLOSIONS_FLAG_ON_CANCEL(RAPPEL_DATA &rappelData, BOOL bReset)
rappelData.bResetRunFromFiresAndExplosionsFlagOnCancel = bReset
ENDPROC
// set to override bResetDisableExplosionReactionsFlagOnCancel
PROC SET_RAPPEL_RESET_DISABLE_EXPLOSIONS_FLAG_ON_CANCEL(RAPPEL_DATA &rappelData, BOOL bReset)
rappelData.bResetDisableExplosionReactionsFlagOnCancel = bReset
ENDPROC
// setup sound ID
PROC GET_RAPPEL_SOUND_ID(RAPPEL_DATA &rappelData)
INT i
REPEAT COUNT_OF(RAPPEL_SOUND_ENUM) i
IF rappelData.iSoundID[i] = 0
rappelData.iSoundID[i] = GET_SOUND_ID()
ENDIF
ENDREPEAT
ENDPROC
// set rappel cam
PROC DEFINE_RAPPEL_CAM_PROPERTIES(RAPPEL_DATA &rappelData,
// PROPERTIES ROTATING AROUND PED TO VIEW LEFT/RIGHT
FLOAT fCamDefaultHeading = 60.255, // default cam position x offset from ped
FLOAT fCamYDist = -3.22, // default cam position y offset from ped
FLOAT fCamXYInputToDegreesFactor = 1.2,//14.0, // amount stick movement is multiplied by when rotating around the ped - a higher value will make response more sensitive
FLOAT fCamClockwiseLimit = 150.0, // amount in degrees the camera can be moved clockwise from its default heading (defined with xdist and ydist)
FLOAT fCamAnticlockwiseLimit = 30.0, // amount in degrees the camera can be moved anticlockwise from its default heading (defined with xdist and ydist)
FLOAT fCamXYSpeed = 0.4,//0.46, // speed the camera will travel at trying to snap back to its default view x/y
// PROPERTIES ROTATING TO VIEW UP/DOWN
FLOAT fCamZDist = 2.7, // default cam position z offset from ped
FLOAT fCamZInputToHeightFactor = 0.02, // amount stick movement is multiplied by when panning z around the ped - a higher value will make response more sensitive
FLOAT fCamZUpperLimit = 7.0, // distance above the ped the camera can be moved from its default pos
FLOAT fCamZLowerLimit = -6.0, // distance below the ped the camera can be moved from its default pos
FLOAT fCamZSpeed = 2.0, // meters per second the camera will travel at trying to snap back to its default view z
// FOV
FLOAT fCamFOV = 48.0) // camera fov
rappelData.fCamDefaultHeading = fCamDefaultHeading
rappelData.fCamYDist = fCamYDist
rappelData.fCamXYInputToDegreesFactor = fCamXYInputToDegreesFactor
rappelData.fCamClockwiseLimit = fCamClockwiseLimit
rappelData.fCamAnticlockwiseLimit = fCamAnticlockwiseLimit
rappelData.fCamXYSpeed = fCamXYSpeed
rappelData.fCamZDist = fCamZDist
rappelData.fCamZInputToHeightFactor = fCamZInputToHeightFactor
rappelData.fCamZUpperLimit = fCamZUpperLimit
rappelData.fCamZLowerLimit = fCamZLowerLimit
rappelData.fCamZSpeed = fCamZSpeed
rappelData.fCamFOV = fCamFOV
IF DOES_CAM_EXIST(rappelData.cam)
SET_CAM_FOV(rappelData.cam, rappelData.fCamFOV)
ENDIF
ENDPROC
// Modify where the rappel cam points at
PROC MODIFY_RAPPEL_CAM_POINT_AT(RAPPEL_DATA &rappelData, VECTOR vMod)
rappelData.vModifyCamPointAt = vMod
ENDPROC
// initialise rappel
PROC INIT_RAPPEL(RAPPEL_DATA &rappelData, VEHICLE_INDEX attachVehicle, VECTOR vAnchorPos, VECTOR vAnchorRot, FLOAT fStartZ, FLOAT fLimitZ, BOOL bSetupCamera = TRUE, BOOL bShowHelp = TRUE, BOOL bIsFreeRoping = FALSE, PED_INDEX ped = NULL, BOOL bPutAwayWeapon = FALSE, RAPPEL_STATE_ENUM initState = RAPPEL_STATE_IDLING, FLOAT fInitPhase = 0.0, BOOL bUsePulley = TRUE, BOOL bStartRenderingRappelCam = TRUE)
IF NOT rappelData.bRappelling
IF NOT DOES_ENTITY_EXIST(ped)
rappelData.ped = PLAYER_PED_ID()
ELSE
rappelData.ped = ped
ENDIF
rappelData.ai = RAPPEL_AI_STOP
rappelData.material = RAPPEL_MATERIAL_GLASS
DEFINE_RAPPEL_CAM_PROPERTIES(rappelData)
IF rappelData.ped = PLAYER_PED_ID()
SET_PLAYER_CONTROL(PLAYER_ID(),FALSE)
ELSE
IF NOT IS_PED_INJURED(rappelData.ped)
SET_PED_CONFIG_FLAG(rappelData.ped, PCF_RunFromFiresAndExplosions, FALSE)
SET_PED_CONFIG_FLAG(rappelData.ped, PCF_DisableExplosionReactions, TRUE)
rappelData.bResetRunFromFiresAndExplosionsFlagOnCancel = TRUE
rappelData.bResetDisableExplosionReactionsFlagOnCancel = TRUE
ENDIF
ENDIF
IF bUsePulley
IF NOT bIsFreeRoping
CREATE_RAPPEL_PULLEY(rappelData)
ENDIF
ENDIF
CREATE_RAPPEL_ANCHOR(rappelData, attachVehicle, vAnchorPos, vAnchorRot)
GET_RAPPEL_SOUND_ID(rappelData)
rappelData.vAttachPos = <<0,0,fStartZ>>
rappelData.fLimitZ = fLimitZ
rappelData.bDirectlyPinNextVertex = FALSE
rappelData.bSetupCam = FALSE
rappelData.bShownHelp = FALSE
IF NOT bShowHelp
rappelData.bShownHelp = TRUE
ENDIF
rappelData.bFreeRoping = bIsFreeRoping
rappelData.bAllowSmallJumps = TRUE
rappelData.bAllowBigJumps = FALSE
rappelData.bAllowSideJumps = FALSE
rappelData.bForceSmallJumpOnBigJumpControl = FALSE
rappelData.bPinnedLastVertex = FALSE
rappelData.bUnpinnedForDismount = FALSE
rappelData.fMaxSlideSpeed = DEFAULT_MAX_SLIDE_SPEED
rappelData.fOverrideMaxJumpPhase = 1.0
rappelData.fOverrideBigJumpSpeed = DEFAULT_BIG_JUMP_SPEED
rappelData.fOverrideSmallJumpSpeed = DEFAULT_SMALL_JUMP_SPEED
IF bPutAwayWeapon
SET_CURRENT_PED_WEAPON(rappelData.ped, WEAPONTYPE_UNARMED, TRUE)
ENDIF
SET_RAPPEL_STATE(rappelData, initState, TRUE, FALSE, TRUE, fInitPhase)
HANDLE_RAPPEL_PED(rappelData, TRUE, FALSE)
IF bSetupCamera
//IF rappelData.ped = PLAYER_PED_ID()
// init camera position
rappelData.fTargetCamHeading = 0
rappelData.fCurrentCamHeading = rappelData.fTargetCamHeading
rappelData.fTargetCamHeight = 0
rappelData.fCamZMod = 0
DO_RAPPEL_CAM(rappelData, FALSE, 0, 0, FALSE, -1000, bStartRenderingRappelCam)
//ENDIF
ENDIF
rappelData.bRappelling = TRUE
ENDIF
ENDPROC
// init rappel and create a rope
PROC INIT_RAPPEL_WITH_ROPE(RAPPEL_DATA &rappelData, VEHICLE_INDEX attachVehicle, VECTOR vAnchorPos, VECTOR vAnchorRot, FLOAT fStartZ, FLOAT fLimitZ, FLOAT fRopeLength, BOOL bSetupCamera, BOOL bShowHelp = TRUE, BOOL bIsFreeRoping = FALSE, PED_INDEX ped = NULL, BOOL bCreateAsUnfurled = TRUE, BOOL bPutAwayWeapon = FALSE, FLOAT fRopeWeight = DEFAULT_ROPE_WEIGHT, FLOAT fCreateRopeLength = -1.0, BOOL bStartRenderingRappelCam = TRUE, RAPPEL_STATE_ENUM initState = RAPPEL_STATE_IDLING, FLOAT fInitPhase = 0.0)
INIT_RAPPEL(rappelData, attachVehicle, vAnchorPos, vAnchorRot, fStartZ, fLimitZ, bSetupCamera, bShowHelp, bIsFreeRoping, ped, bPutAwayWeapon, initState, fInitPhase, TRUE, bStartRenderingRappelCam)
CREATE_RAPPEL_ROPE(rappelData, fRopeLength, TRUE, bCreateAsUnfurled, fRopeWeight, fCreateRopeLength)
ENDPROC
// handle rappel player and rope
PROC HANDLE_RAPPEL(RAPPEL_DATA &rappelData, BOOL bIsAI = FALSE, BOOL bDoCam = TRUE)
HANDLE_RAPPEL_PED(rappelData, bIsAI, bDoCam)
HANDLE_RAPPEL_ROPE(rappelData)
ENDPROC
// dismount rappel
PROC DISMOUNT_RAPPEL(RAPPEL_DATA &rappelData, WEAPON_TYPE dismountWeapon = WEAPONTYPE_UNARMED)
rappelData.dismountWeapon = dismountWeapon
SET_RAPPEL_STATE(rappelData, RAPPEL_STATE_DISMOUNT_FREE_ROPING)
ENDPROC
/*
// has the rappel sound finished?
FUNC BOOL HAS_RAPPEL_SOUND_FINISHED(RAPPEL_DATA rappelData)
INT i
BOOL bFinished = TRUE
REPEAT COUNT_OF(RAPPEL_SOUND_ENUM) i
IF bFinished
IF rappelData.iSoundID[i] <> 0
bFinished = HAS_SOUND_FINISHED(rappelData.iSoundID[i])
ENDIF
ENDIF
ENDREPEAT
RETURN bFinished
ENDFUNC
*/
// cleanup rappel - to ensure all assets are properly disposed of for mission cleanup
PROC CLEANUP_RAPPEL(RAPPEL_DATA &rappelData)
IF DOES_ENTITY_EXIST(rappelData.weightObject)
DELETE_OBJECT(rappelData.weightObject)
ENDIF
CANCEL_RAPPEL(rappelData)
DELETE_RAPPEL_ROPE(rappelData)
IF DOES_ENTITY_EXIST(rappelData.anchorObject)
DELETE_OBJECT(rappelData.anchorObject)
ENDIF
IF DOES_ENTITY_EXIST(rappelData.pulleyObject)
DELETE_OBJECT(rappelData.pulleyObject)
ENDIF
//RELEASE_RAPPEL_AUDIO(rappelData)
ENDPROC