Files
2025-09-29 00:52:08 +02:00

4582 lines
206 KiB
Python
Executable File

// SCRIPT NAME : chop.sc
// AUTHOR : Kenneth Ross / Kev Edwards
// DESCRIPTION : Spawns chop the dog in Franklins back garden, player can command Chop to perform various tricks depending on the skill level achieved in app
//Compile out Title Update changes to header functions.
//Must be before includes.
//CONST_INT USE_TU_CHANGES 0 // Removed by Kenneth R.
USING "pickups_weapon.sch"
USING "pickups_armour.sch"
USING "pickups_health.sch"
USING "pickups_letter.sch"
USING "pickups_spaceship.sch"
USING "pickups_query.sch"
USING "rgeneral_include.sch"
USING "RC_Helper_Functions.sch"
USING "RC_Area_public.sch"
USING "website_public.sch"
ENUM CHOP_STAGE_ENUM
STAGE_INIT = 0,
STAGE_PROCESS
ENDENUM
CHOP_STAGE_ENUM eStage
ENUM CHOP_TASK_ENUM
CHOP_TASK_NONE = 0,
CHOP_TASK_WAIT,
CHOP_TASK_WALK,
CHOP_TASK_RETURN,
CHOP_TASK_WANDER,
CHOP_TASK_SNIFF,
CHOP_TASK_ATTACK,
CHOP_TASK_BEG,
CHOP_TASK_SIT,
CHOP_TASK_PAW,
CHOP_TASK_PET,//10
CHOP_TASK_SPAWNED_IN_KENNEL,
CHOP_TASK_SIT_OUTSIDE_SHOP,
CHOP_TASK_SHIT,
CHOP_TASK_PISS,
CHOP_TASK_FETCH,
CHOP_TASK_CABLE_CAR,
CHOP_TASK_WAIT_PLAY_ANIM,
CHOP_TASK_GREET_FRANKLIN
ENDENUM
CHOP_TASK_ENUM eChopTask
INT iChopStage
CHOP_TASK_ENUM eNextChopTask = CHOP_TASK_NONE
ENUM HELP_FLAG
CHOP_HELP_INTERACT_WITH_CHOP = 0,
CHOP_HELP_BARK_NEAR_PICKUPS,
CHOP_HELP_VEHICLE_IS_SUITABLE,
CHOP_HELP_VEHICLE_NOT_SUITABLE,
CHOP_HELP_TOO_FAR_AWAY,
CHOP_HELP_FOLLOW_ON_BIKE,
CHOP_HELP_ATTACK_ENEMIES,
CHOP_HELP_PLAY_FETCH,
CHOP_HELP_WHISTLE,
CHOP_HELP_KILLED_BY_PLAYER,
CHOP_HELP_MISBEHAVE,
CHOP_HELP_ATTACK_AIM,
CHOP_HELP_KILLED_BY_SOMEONE_ELSE
ENDENUM
ENUM CHOP_INPUT_TYPE
CHOP_INPUT_ENGAGE = 0,
CHOP_INPUT_SEARCH,
CHOP_INPUT_SIT,
CHOP_INPUT_PAW,
CHOP_INPUT_BEG,
CHOP_INPUT_PET
ENDENUM
INT iRadioStationHowlTimer = -1
INT iDelayUntilNextHowl
INT iBarkTimer
INT iBarkInVehicleTimer // B*1801754 Prevent barking immediately after a vehicle shunt
INT iDelayUntilNextBark
INT iDelayUntilNextPickupScan
INT iNextWantedWhimperTimer
INT iDelayUntilNextWantedWhimper
INT iChopBehaviourNotMovingTimer
INT iChopFetchExcitedTimer
INT iChopPtfxTimer
INT iChopWarpToVehicleTimer
INT iChopWarpToVehicleTimeLimit
INT iChopReachedPickupTimer
INT iChopVehicleInWaterTimer
PED_INDEX chopID
BLIP_INDEX blipChop
REL_GROUP_HASH relGroupChop
BOOL bRelGroupExists = FALSE
BOOL bFranklinTaskedToLookAtChop
BOOL bIsChopTargettable
BOOL bPlayerChoseToWalkChop
VECTOR vLastChopBehaviourPosition
PTFX_ID ptfx_chop
VEHICLE_INDEX vehChop // The vehicle that Chop is sat inside
BOOL bPlayWaitStandAnim
STRING sAnimDict[3]
STRING sAnimPlaying = "null"
INT iNavBlockerPool = -1
INT iNavBlockerJacuzzi = -1
CONST_INT NUM_CHOP_CABLE_CAR_POSITIONS 4
VECTOR vChopCableCarPos[NUM_CHOP_CABLE_CAR_POSITIONS]
FLOAT fChopCableCarPos[NUM_CHOP_CABLE_CAR_POSITIONS]
ENUM CHOP_VEHICLE_STATUS
CHOP_VEHICLE_STATUS_FRANKLIN_NOT_IN_VEHICLE = 0,
CHOP_VEHICLE_STATUS_FRANKLIN_ENTERING_VEHICLE,
CHOP_VEHICLE_STATUS_FRANKLIN_PLAYING_ANIM_OPENING_DOOR,
CHOP_VEHICLE_STATUS_FRANKLIN_OPENING_DOOR,
CHOP_VEHICLE_STATUS_CHOP_ENTER_VEHICLE,
CHOP_VEHICLE_STATUS_CHOP_ENTERING_VEHICLE,
CHOP_VEHICLE_STATUS_FRANKLIN_PLAYING_ANIM_CLOSING_DOOR,
CHOP_VEHICLE_STATUS_FRANKLIN_CLOSING_DOOR,
CHOP_VEHICLE_STATUS_CHOP_SAT_IN_CAR,
CHOP_VEHICLE_STATUS_WAIT_UNTIL_PLAYER_EXITED,
CHOP_VEHICLE_STATUS_FRANKLIN_PLAYING_ANIM_OPENING_DOOR_FOR_EXIT,
CHOP_VEHICLE_STATUS_FRANKLIN_OPENING_DOOR_FOR_EXIT,
CHOP_VEHICLE_STATUS_CHOP_EXIT_VEHICLE,
CHOP_VEHICLE_STATUS_CHOP_EXITING_VEHICLE,
CHOP_VEHICLE_STATUS_CHOP_FOLLOWING_BIKE,
CHOP_VEHICLE_STATUS_CLEANUP
ENDENUM
CHOP_VEHICLE_STATUS chopInVehicleStatus
INT iSynchSceneChop
TEXT_LABEL_31 tlChopHelp
BOOL bPlayerEnteringRightSide
SC_DOOR_LIST doorChop = SC_DOOR_FRONT_RIGHT
SEQUENCE_INDEX taskSequence
TEST_POLY g_polyPlayArea
structPedsForConversation sConversation
BOOL bPlayedVehicleDialogue
ENUM CHOP_CURRENT_PICKUP_TYPE
CHOP_CURRENT_PICKUP_TYPE_ARMOUR,
CHOP_CURRENT_PICKUP_TYPE_HEALTH,
CHOP_CURRENT_PICKUP_TYPE_WEAPON,
CHOP_CURRENT_PICKUP_TYPE_LETTER,
CHOP_CURRENT_PICKUP_TYPE_SPACESHIP,
CHOP_CURRENT_PICKUP_TYPE_NONE
ENDENUM
CHOP_CURRENT_PICKUP_TYPE chopCurrentPickupType
CHOP_CURRENT_PICKUP_TYPE chopCurrentValidityScan
INT iClosestPickupTimer
INT iClosestPickupOfType[5]
FLOAT fDistanceToClosestPickupOfType[5]
VECTOR vClosestPickupOfType[5]
VECTOR vChopSitNearClosestPickupOfType[5]
INT iClosestPickup
VECTOR vClosestPickup
VECTOR vChopSitNearClosestPickup
FLOAT fMaxChopHuntDist = 200 // Maximum distance at which Chop will hunt for a pickup
CONST_INT CHOP_REPEAT_EXCITED_DELAY 10000
CONST_FLOAT CHOP_ATTACK_RANGE 100.0
BOOL bDoingTimetableSwitchChopNotInVehicle
BOOL bDoingTimetableSwitchChopIsInVehicle
BOOL bPlayerUsedApp
BOOL bOverrideHappiness = FALSE
#IF IS_DEBUG_BUILD
BOOL bOutputCameraToTTY = FALSE
INT iOverrideChopEnterVehicleCamera = -1
#ENDIF
STRUCT CHOP_DATA_STRUCT
VECTOR vSpawnCoords
FLOAT fSpawnHeading
SAVEHOUSE_NAME_ENUM eSavehouse
VECTOR vKennelCoords
FLOAT fKennelHeading
ENDSTRUCT
CHOP_DATA_STRUCT sChopData
INT iChopInKennelTimer
INT iChopExitKennelTime
BOOL bChopGreetedFranklinDone
INT iNavmeshAttempts
INT iGoToBallTasksGiven
FLOAT fBallZHeight
INT iWhistleTimer
INT iUnhappyTimer = GET_GAME_TIMER()
INT iUnhappyDelay = 10000
VECTOR vStartCoords
OBJECT_INDEX oChopShit
VECTOR vChopShit
BOOL bChopDidShit
OBJECT_INDEX oLamppost
INT iChopPissDirection
VECTOR vChopCurrentPositionAtLamppost
VECTOR vLamppostOffset
FLOAT fLamppostHeading
VECTOR vChopLastShitPosition
VECTOR vChopLastPissPosition
BOOL bChopDoneShitDialogue
BOOL bPlayedChopBusyDialogue
CONST_INT NUM_CHOP_WANDER_POSITIONS 8
VECTOR vChopWanderPositions[NUM_CHOP_WANDER_POSITIONS]
BOOL bChopWanderingBetweenPositions
INT iCurrentChopCollar = -1
ENTITY_INDEX eBall
VECTOR vBallPosition
BOOL bChopRetrievedBall
BOOL bPlayedFetchBallDialogue
WEAPON_TYPE wThrownWeapon
INT iChopFetchDelayTimer
VEHICLE_LAYOUT_TYPE vehLayoutHash = LAYOUT_STANDARD
STRING stringFranklinInVehicleAnim = "std_ds_open_door_for_chop"
STRING stringChopInVehicleAnimDict = "creatures@rottweiler@in_vehicle@std_car"
INT iDoingChopInVehicleCollisionAnim = 0
INT iFranklinVehicleDoorAnimTimer
INT iChopSicBallsMeter
CONST_INT CHOP_SIC_BALLS_CALL_COPS_LIMIT 3
CONST_INT CHOP_SIC_BALLS_COOLDOWN_TIMER 60000
INT iChopSicBallsTimer
BOOL bChopAlreadyFacingFranklin
BOOL bFranklinAlreadyFacingChop
INT iBulletNearChopTimer
INT iOldBallNearFranklinTimer = GET_GAME_TIMER()
CAMERA_INDEX camChopEnterVehicle
INT iNumTimesChopTakenForWalk
INT iStockMarketTimer = GET_GAME_TIMER()
INT i_DoorOpenTimer //B* 2336322: Make sure door is open when chop enters
INT iPickupBallSound = GET_SOUND_ID()
BOOL bPlayerHasBeenInCarWithChop = FALSE // B*1982159 Only latch the door for the player's last vehicle if the player got into a vehicle with Chop
BOOL bHintCamAlreadyACtive = FALSE //B* 2002205: Don't kill hint cam if already active when thread starts
/// PURPOSE:
/// Debug widget functions.
#IF IS_DEBUG_BUILD
BOOL bSetCollar
INT iCollarDraw, iCollarTex
PROC SETUP_CHOP_WIDGETS()
START_WIDGET_GROUP("Chop The Dog")
ADD_WIDGET_BOOL("Unlock all tricks", g_bUnlockAllChopTricks)
ADD_WIDGET_BOOL("Player has used app", bPlayerUsedApp)
ADD_WIDGET_BOOL("Set debug collar", bSetCollar)
ADD_WIDGET_INT_SLIDER("Collar drawable", iCollarDraw, 0, 10, 1)
ADD_WIDGET_INT_SLIDER("Collar texture", iCollarTex, 0, 10, 1)
ADD_WIDGET_FLOAT_READ_ONLY("Happiness from app (read only)", g_savedGlobals.sSocialData.sDogAppData.fHappiness)
ADD_WIDGET_BOOL("Override happiness to 100", bOverrideHappiness)
ADD_WIDGET_BOOL("Output cam pos and rot to tty", bOutputCameraToTTY)
ADD_WIDGET_INT_SLIDER("Override Chop enter vehicle camera", iOverrideChopEnterVehicleCamera, -1, 3, 1)
STOP_WIDGET_GROUP()
ENDPROC
PROC MAINTAIN_CHOP_WIDGETS()
IF bSetCollar = TRUE
IF NOT IS_PED_INJURED(chopID)
SET_PED_COMPONENT_VARIATION(chopID, PED_COMP_TORSO, iCollarDraw, iCollarTex)
ENDIF
bSetCollar = FALSE
ENDIF
// Check for player using XML debug menu to unlock tricks (needs app)
IF bPlayerUsedApp = FALSE
// These variables will now get overridden with SPCID_UNLOCK_CHOP_TRICKS
IF g_bDebugAppScreenOverride
AND g_bDebugChopPlayedStatusOverride
bPlayerUsedApp = TRUE
ENDIF
ENDIF
ENDPROC
#ENDIF
/// PURPOSE:
/// Returns the type of input to check for, depending on the current input mode
FUNC CONTROL_TYPE GET_CHOP_INPUT_CONTROL()
IF IS_USING_KEYBOARD_AND_MOUSE(PLAYER_CONTROL)
RETURN PLAYER_CONTROL
ELSE
RETURN FRONTEND_CONTROL
ENDIF
ENDFUNC
/// PURPOSE:
/// Returns the input button to check for, for a given action
FUNC CONTROL_ACTION GET_CHOP_INPUT_ACTION(CHOP_INPUT_TYPE eInputType)
IF IS_USING_KEYBOARD_AND_MOUSE(PLAYER_CONTROL)
SWITCH eInputType
CASE CHOP_INPUT_ENGAGE RETURN INPUT_ATTACK
CASE CHOP_INPUT_SEARCH RETURN INPUT_JUMP
CASE CHOP_INPUT_SIT RETURN INPUT_COVER
CASE CHOP_INPUT_PAW RETURN INPUT_RELOAD
CASE CHOP_INPUT_BEG RETURN INPUT_CONTEXT
CASE CHOP_INPUT_PET RETURN INPUT_ENTER
ENDSWITCH
CPRINTLN(DEBUG_AMBIENT, "GET_CHOP_INPUT_ACTION(): Unknown (keyboard+mouse) eInputType: ", eInputType)
SCRIPT_ASSERT( "GET_CHOP_INPUT_ACTION(): Unknown (keyboard+mouse) eInputType")
ELSE
SWITCH eInputType
CASE CHOP_INPUT_ENGAGE RETURN INPUT_SCRIPT_RB
CASE CHOP_INPUT_SEARCH RETURN INPUT_SCRIPT_RT
CASE CHOP_INPUT_SIT RETURN INPUT_SCRIPT_RLEFT
CASE CHOP_INPUT_PAW RETURN INPUT_SCRIPT_RRIGHT
CASE CHOP_INPUT_BEG RETURN INPUT_SCRIPT_RDOWN
CASE CHOP_INPUT_PET RETURN INPUT_SCRIPT_RUP
ENDSWITCH
CPRINTLN(DEBUG_AMBIENT, "GET_CHOP_INPUT_ACTION(): Unknown (joypad) eInputType: ", eInputType)
SCRIPT_ASSERT( "GET_CHOP_INPUT_ACTION(): Unknown (joypad) eInputType")
ENDIF
RETURN INPUT_NEXT_CAMERA // Error
ENDFUNC
/// PURPOSE:
/// Handles Franklin looking at Chop when within 10m.
PROC FRANKLIN_SHOULD_LOOK_AT_CHOP(BOOL b_franklin_should_look_at_chop)
IF b_franklin_should_look_at_chop = TRUE
//IF bFranklinTaskedToLookAtChop = FALSE // B*1501473 - Keep setting this on Franklin
IF IS_ENTITY_ALIVE(chopID)
TASK_LOOK_AT_ENTITY(PLAYER_PED_ID(), chopID, -1, SLF_WIDEST_YAW_LIMIT|SLF_WIDEST_PITCH_LIMIT)
bFranklinTaskedToLookAtChop = TRUE
//#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Set Franklin to look at Chop") #ENDIF
ENDIF
ELSE
IF bFranklinTaskedToLookAtChop = TRUE
AND IS_ENTITY_ALIVE(PLAYER_PED_ID())
TASK_CLEAR_LOOK_AT(PLAYER_PED_ID())
bFranklinTaskedToLookAtChop = FALSE
//#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Stopped Franklin looking at Chop") #ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Returns true if Chop should be deleted when the script cleans up.
FUNC BOOL SHOULD_CHOP_BE_DELETED()
IF IS_PLAYER_SWITCH_IN_PROGRESS()
AND IS_ENTITY_ALIVE(chopID)
IF IS_SYNCHRONIZED_SCENE_RUNNING(iSynchSceneChop)
OR IS_PED_IN_ANY_VEHICLE(chopID)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player switch in progress and Chop is in a car, so delete Chop") #ENDIF
RETURN TRUE
ENDIF
ENDIF
IF g_bSafehouseTutorialIsActive = TRUE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: g_bSafehouseTutorialIsActive is TRUE, so delete Chop") #ENDIF
RETURN TRUE
ENDIF
IF g_bPlayerIsInCinema = TRUE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: g_bPlayerIsInCinema is TRUE, so delete Chop") #ENDIF
g_bPlayerIsInCinema = FALSE // Immediately reset the flag
RETURN TRUE
ENDIF
IF IS_ENTITY_ALIVE(chopID)
IF eChopTask = CHOP_TASK_SPAWNED_IN_KENNEL
AND IS_ENTITY_OCCLUDED(chopID)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop is still in his kennel and occluded, so delete Chop because he won't be able to navigate out using AI") #ENDIF // B*1469198
RETURN TRUE
ENDIF
IF IS_POINT_VISIBLE(GET_ENTITY_COORDS(chopID, FALSE), 1) // B*1021716 - Don't delete Chop if he's visible
RETURN FALSE
ENDIF
ENDIF
IF NOT IS_CURRENTLY_ON_MISSION_TO_TYPE(MISSION_TYPE_STORY) // Don't delete if player isn't starting a mission
RETURN FALSE
ENDIF
IF IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_FRIEND_ACTIVITY) // B*1133827 - Don't delete if phoning a friend
OR IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_STORY_PREP)
RETURN FALSE
ENDIF
RETURN TRUE // B*1057917 - Delete Chop if a mission begins and he's not onscreen
ENDFUNC
/// PURPOSE:
/// Release the ball projectile entity.
PROC CLEANUP_BALL(BOOL b_delete)
IF DOES_ENTITY_EXIST(eBall)
VECTOR v_ball = GET_ENTITY_COORDS(eBall)
IF IS_ENTITY_ATTACHED_TO_ANY_PED(eBall)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: eBall was attached to Chop so unattached it") #ENDIF
DETACH_ENTITY(eBall)
ENDIF
SET_ENTITY_AS_NO_LONGER_NEEDED(eBall)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Released eBall") #ENDIF
IF b_delete = TRUE
AND wThrownWeapon = WEAPONTYPE_BALL
CLEAR_AREA_OF_PROJECTILES(v_ball, 0.1)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Cleared area of projectiles at eBall coords") #ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// B*1088351 - Suppress other dog models because Chop can't fight them.
PROC SUPPRESS_OTHER_CREATURES(BOOL b_suppress)
SET_PED_MODEL_IS_SUPPRESSED(A_C_RETRIEVER, b_suppress)
SET_PED_MODEL_IS_SUPPRESSED(A_C_ROTTWEILER, b_suppress)
SET_PED_MODEL_IS_SUPPRESSED(A_C_BOAR, b_suppress)
SET_PED_MODEL_IS_SUPPRESSED(A_C_COYOTE, b_suppress)
SET_PED_MODEL_IS_SUPPRESSED(A_C_DEER, b_suppress)
SET_PED_MODEL_IS_SUPPRESSED(A_C_MTLION, b_suppress)
SET_PED_MODEL_IS_SUPPRESSED(A_C_PIG, b_suppress)
ENDPROC
/// PURPOSE:
/// Used in while loops to check the entities are ok, to continue in those while loops.
FUNC BOOL ARE_ENTITIES_OK_FOR_CHOP_VEHICLE_EXIT()
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
AND IS_ENTITY_ALIVE(vehChop)
AND IS_ENTITY_ALIVE(chopID)
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Remove any help text that has been printed by the Cho script.
PROC CLEANUP_CHOP_HELP_TEXT()
IF IS_HELP_MESSAGE_BEING_DISPLAYED()
// Don't just do a CLEAR_HELP in case we're cleaning up because a mission has been triggered which may have displayed its own help text
IF IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_INTRO")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_0")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_1")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_2")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_3")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_4")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_H1")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_H2")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_H3")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_H4")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_HOME")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_0")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_1")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_2")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_3")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_4")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_H1")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_H2")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_H3")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_H4")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_HUNT")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_NOVEH")
//OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_RANGE")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_CAR")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_BIKE")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_ATTACK")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_BALL")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WHIS")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_NOAPP")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_BEHAVE")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_BEHAVA")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_AIM")
CLEAR_HELP()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop help text was onscreen when cleaning up so clearing help text") #ENDIF
ENDIF
ENDIF
IF IS_PC_VERSION()
IF IS_HELP_MESSAGE_BEING_DISPLAYED()
// Don't just do a CLEAR_HELP in case we're cleaning up because a mission has been triggered which may have displayed its own help text
IF IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_INTRO_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_0_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_1_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_2_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_3_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_4_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_H1_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_H2_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_H3_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_H4_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_HOME_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_0_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_1_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_2_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_3_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_4_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_H1_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_H2_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_H3_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_H4_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_NOAPP_KM")
CLEAR_HELP()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop help text was onscreen when cleaning up so clearing help text (PC version)") #ENDIF
ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Latch Chop's door on the last player vehicle
PROC SET_LATCH_DOOR_ON_LAST_PLAYER_VEHICLE()
IF bPlayerHasBeenInCarWithChop = TRUE
AND IS_ENTITY_ALIVE(GET_LAST_DRIVEN_VEHICLE())
AND GET_VEHICLE_MAX_NUMBER_OF_PASSENGERS(GET_LAST_DRIVEN_VEHICLE()) > 1
AND NOT IS_VEHICLE_DOOR_DAMAGED(GET_LAST_DRIVEN_VEHICLE(), doorChop)
SET_VEHICLE_DOOR_LATCHED(GET_LAST_DRIVEN_VEHICLE(), doorChop, FALSE, TRUE, FALSE) // B*1955858 Ensure Chop's door won't be flapping around post-mission
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Latched GET_LAST_DRIVEN_VEHICLE() door") #ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Clean up everything conceivably setup in this mission.
PROC CLEANUP_SCRIPT()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Cleaning up script") #ENDIF
IF GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH(GET_HASH_OF_THIS_SCRIPT_NAME()) > 1
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Another Chop.sc thread is already running so not doing full cleanup") #ENDIF
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: This is the only Chop.sc thread running so doing full cleanup") #ENDIF
g_bPlayerInteractingWithChop = FALSE
FRANKLIN_SHOULD_LOOK_AT_CHOP(FALSE)
IF IS_GAMEPLAY_HINT_ACTIVE()
AND NOT bHintCamAlreadyACtive //B* 2002205: Don't kill hint cam if already active
STOP_GAMEPLAY_HINT() // Ensure gameplay hint has been stopped, see B*1431829
ENDIF
// Remove Chop blip
SAFE_REMOVE_BLIP(blipChop)
// Chop has been unlocked
IF GET_MISSION_FLOW_FLAG_STATE(FLOWFLAG_CHOP_THE_DOG_UNLOCKED)
// Assert guard
IF IS_PLAYER_PLAYING(PLAYER_ID()) // B*1406239
// Enable static blip based on unlocked safehouse
IF GET_MISSION_FLOW_FLAG_STATE(FLOWFLAG_FRANKLIN_MOVED_TO_HILLS_APARTMENT)
// Restore static blip when outside Vinewood Hills Chop world point
IF GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(PLAYER_PED_ID()), <<19.30, 528.24, 169.63>>) > 50
SET_STATIC_BLIP_ACTIVE_STATE(STATIC_BLIP_AMBIENT_CHOP_VH, TRUE)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Displaying Chop blip STATIC_BLIP_AMBIENT_CHOP_VH") #ENDIF
ENDIF
ELSE
// Restore static blip when outside South Central Chop world point
IF GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(PLAYER_PED_ID()), <<-11.15, -1425.56, 29.67>>) > 50
SET_STATIC_BLIP_ACTIVE_STATE(STATIC_BLIP_AMBIENT_CHOP_SC, TRUE)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Displaying Chop blip STATIC_BLIP_AMBIENT_CHOP_SC") #ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
CLEANUP_BALL(TRUE)
SAFE_RELEASE_OBJECT(oChopShit)
SAFE_RELEASE_OBJECT(oLamppost)
IF DOES_PARTICLE_FX_LOOPED_EXIST(ptfx_chop)
STOP_PARTICLE_FX_LOOPED(ptfx_chop)
ENDIF
REMOVE_PTFX_ASSET()
IF HAS_PED_GOT_WEAPON(PLAYER_PED_ID(), WEAPONTYPE_BALL)
REMOVE_WEAPON_FROM_PED(PLAYER_PED_ID(), WEAPONTYPE_BALL)
ENDIF
IF IS_ENTITY_ALIVE(PLAYER_PED_ID()) AND NOT IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_STORY)
SET_PED_CONFIG_FLAG(PLAYER_PED_ID(), PCF_WillFlyThroughWindscreen, TRUE) // B*1434346
ENDIF
IF DOES_ENTITY_EXIST(chopID)
IF SHOULD_CHOP_BE_DELETED()
SAFE_DELETE_PED(chopID)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Deleted Chop") #ENDIF
ELSE
IF NOT IS_ENTITY_DEAD(chopID)
AND IS_ENTITY_ALIVE(PLAYER_PED_ID())
IF IS_PED_IN_GROUP(chopID)
REMOVE_PED_FROM_GROUP(chopID)
ENDIF
IF IS_SYNCHRONIZED_SCENE_RUNNING(iSynchSceneChop)
OR IS_PED_IN_ANY_VEHICLE(chopID)
// Chop is in a car, or doing a synched scene to get in/out, so we need to play anims to get him out
IF IS_ENTITY_ALIVE(vehChop)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop is in car so need to make him get out") #ENDIF
BOOL b_car_stopped = FALSE
WHILE NOT b_car_stopped
WAIT(0)
IF NOT ARE_ENTITIES_OK_FOR_CHOP_VEHICLE_EXIT() // Need to check again due to while loop
OR BRING_VEHICLE_TO_HALT_AND_DISABLE_VEH_CONTROLS(vehChop)
b_car_stopped = TRUE
ENDIF
ENDWHILE
IF ARE_ENTITIES_OK_FOR_CHOP_VEHICLE_EXIT() // Need to check again due to while loop
IF NOT IS_VEHICLE_DOOR_DAMAGED(vehChop, doorChop)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Opening doorChop so Chop can get out") #ENDIF
SET_VEHICLE_DOOR_OPEN(vehChop, doorChop)
ENDIF
REQUEST_ANIM_DICT(stringChopInVehicleAnimDict)
WHILE NOT HAS_ANIM_DICT_LOADED(stringChopInVehicleAnimDict)
WAIT(0)
ENDWHILE
ENDIF
IF ARE_ENTITIES_OK_FOR_CHOP_VEHICLE_EXIT() // Need to check again due to while loop
iSynchSceneChop = CREATE_SYNCHRONIZED_SCENE(<<0,0,0>>,<<0,0,0>>)
ATTACH_SYNCHRONIZED_SCENE_TO_ENTITY(iSynchSceneChop, vehChop, GET_ENTITY_BONE_INDEX_BY_NAME(vehChop, "seat_pside_f"))
CLEAR_PED_TASKS_IMMEDIATELY(chopID)
TASK_SYNCHRONIZED_SCENE(chopID, iSynchSceneChop, stringChopInVehicleAnimDict, "get_out", INSTANT_BLEND_IN, NORMAL_BLEND_OUT, SYNCED_SCENE_TAG_SYNC_OUT|SYNCED_SCENE_ON_ABORT_STOP_SCENE)
FORCE_PED_AI_AND_ANIMATION_UPDATE(chopID)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop was in car so making him get out") #ENDIF
BOOL b_chop_now_out = FALSE
WHILE NOT b_chop_now_out
WAIT(0)
IF NOT ARE_ENTITIES_OK_FOR_CHOP_VEHICLE_EXIT() // Need to check again due to while loop
OR (IS_SYNCHRONIZED_SCENE_RUNNING(iSynchSceneChop) AND GET_SYNCHRONIZED_SCENE_PHASE(iSynchSceneChop) > 0.99)
b_chop_now_out = TRUE
ENDIF
ENDWHILE
WAIT(0)
IF ARE_ENTITIES_OK_FOR_CHOP_VEHICLE_EXIT() // Need to check again due to while loop
AND NOT IS_VEHICLE_DOOR_DAMAGED(vehChop, doorChop)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop has got out so closing doorChop") #ENDIF
SET_VEHICLE_DOOR_SHUT(vehChop, doorChop, FALSE)
ENDIF
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop now outside car") #ENDIF
ENDIF
ELIF GET_CURRENT_PLAYER_PED_ENUM() = CHAR_FRANKLIN // B*1292832 - Don't warp Chop to player offset if he's switched to Trevor or Michael
IF eChopTask = CHOP_TASK_WALK // B*1562427 - Somehow Chop warped beside Franklin. This is a failsafe to ensure this only occurs if Chop was on a walk or fetch task
OR eChopTask = CHOP_TASK_FETCH
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop was in car but vehChop didn't exist so warping Chop out of whatever vehicle he's in") #ENDIF
CLEAR_PED_TASKS_IMMEDIATELY(chopID)
SAFE_TELEPORT_ENTITY(chopID, GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(PLAYER_PED_ID(), <<1.75,0,0>>), GET_ENTITY_HEADING(chopID), TRUE)
FORCE_PED_AI_AND_ANIMATION_UPDATE(chopID)
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop would have been warped beside Franklin but prevented this as his state wasn't walk or fetch") #ENDIF
ENDIF
ENDIF
ENDIF
IF IS_ENTITY_ALIVE(chopID) // Need to check again due to while loop
SET_PED_KEEP_TASK(chopID, TRUE)
TASK_SMART_FLEE_PED(chopID, PLAYER_PED_ID(), 100, -1) // If this Chop script is terminating we don't want Chop to still be walking around with the player unable to interact with Chop, so make Chop run away
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Made Chop flee player") #ENDIF
ENDIF
ENDIF
SAFE_RELEASE_PED(chopID)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Released Chop") #ENDIF
ENDIF
ENDIF
SET_LATCH_DOOR_ON_LAST_PLAYER_VEHICLE()
SAFE_RELEASE_VEHICLE(vehChop)
SUPPRESS_OTHER_CREATURES(FALSE)
REMOVE_ANIM_DICT("creatures@rottweiler@move")
REMOVE_ANIM_DICT("creatures@rottweiler@amb@world_dog_barking@enter")
REMOVE_ANIM_DICT("creatures@rottweiler@amb@world_dog_barking@base")
REMOVE_ANIM_DICT("creatures@rottweiler@amb@world_dog_barking@idle_a")
REMOVE_ANIM_DICT("creatures@rottweiler@amb@world_dog_barking@exit")
REMOVE_ANIM_DICT("creatures@rottweiler@in_vehicle@4x4")
REMOVE_ANIM_DICT("creatures@rottweiler@in_vehicle@low_car")
REMOVE_ANIM_DICT("creatures@rottweiler@in_vehicle@std_car")
REMOVE_ANIM_DICT("creatures@rottweiler@in_vehicle@van")
REMOVE_ANIM_DICT("creatures@rottweiler@amb@world_dog_sitting@enter")
REMOVE_ANIM_DICT("creatures@rottweiler@amb@world_dog_sitting@idle_a" )
REMOVE_ANIM_DICT("creatures@rottweiler@amb@world_dog_sitting@exit")
REMOVE_ANIM_DICT("creatures@rottweiler@tricks@")
REMOVE_ANIM_DICT("creatures@rottweiler@amb@sleep_in_kennel@")
REMOVE_ANIM_DICT("creatures@rottweiler@indication@")
REMOVE_ANIM_DICT("misschop_vehicleenter_exit")
REMOVE_ANIM_DICT("creatures@rottweiler@melee@streamed_taunts@")
REMOVE_PED_FOR_DIALOGUE(sConversation, 1)
REMOVE_PED_FOR_DIALOGUE(sConversation, 3)
CLEANUP_CHOP_HELP_TEXT()
DESTROY_ALL_CAMS()
IF iNavBlockerPool > -1
REMOVE_NAVMESH_BLOCKING_OBJECT(iNavBlockerPool)
ENDIF
IF iNavBlockerJacuzzi > -1
REMOVE_NAVMESH_BLOCKING_OBJECT(iNavBlockerJacuzzi)
ENDIF
IF bRelGroupExists = TRUE
REMOVE_RELATIONSHIP_GROUP(relGroupChop)
ENDIF
STOP_SOUND(iPickupBallSound)
ENDIF
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Script thread terminated") #ENDIF
TERMINATE_THIS_THREAD()
ENDPROC
/// PURPOSE:
/// Checks if the script should cleanup, if so returns true.
FUNC BOOL SHOULD_SCRIPT_CLEANUP()
IF eStage > STAGE_INIT
IF DOES_ENTITY_EXIST(chopID)
IF IS_CURRENTLY_ON_MISSION_TO_TYPE(MISSION_TYPE_STORY)
AND g_bIsOnCableCar = FALSE // Don't terminate Chop script if the cable car script has been started
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player is now on a story mission") #ENDIF
RETURN TRUE
ENDIF
IF NOT IS_PED_THE_CURRENT_PLAYER_PED(CHAR_FRANKLIN)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player is no longer Franklin") #ENDIF
RETURN TRUE
ENDIF
IF g_bRandomEventActive = TRUE // B*1328660 Terminate ambient Chop if a random event begins in full
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: g_bRandomEventActive is TRUE") #ENDIF
RETURN TRUE
ENDIF
IF g_bScriptsSetSafeForCutscene = TRUE
AND g_bIsOnCableCar = FALSE // Don't terminate Chop script if the cable car script has been started
AND g_HasCellphoneRequestedSave = FALSE // B*1498810 Prevent script terminating when quick saving
AND g_bCurrentlyUsingVendingMachine = FALSE // B*1567131 Prevent Chop running off when using a vending machine
AND g_bCurrentlyUsingTelescope = FALSE // Prevent Chop running off when using a telescope
AND g_bCurrentlyBuyingProperty = FALSE // B*1574904 Prevent Chop running off when buying a property
AND g_bInATM = FALSE // Prevent Chop running off when using an ATM
AND NOT Is_Player_Timetable_Scene_In_Progress() // B*1503527 Prevent script terminating if timetable scene sets g_bScriptsSetSafeForCutscene to true
AND NOT IS_PLAYER_IN_ANY_SHOP() // B*1485031 Prevent script terminating when using a shop
AND g_bShopCutsceneActive = FALSE // B*1557823 Prevent script terminating when showing a shop cutscene
AND IS_ENTITY_ALIVE(PLAYER_PED_ID())
AND NOT IS_PLAYER_CHANGING_CLOTHES() // B*1452680
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: g_bScriptsSetSafeForCutscene is TRUE") #ENDIF
RETURN TRUE
ENDIF
IF g_bPlayerLockedInToTrigger = TRUE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: g_bPlayerLockedInToTrigger is TRUE") #ENDIF
RETURN TRUE
ENDIF
IF IS_ENTITY_DEAD(chopID)
IF IS_PLAYER_PLAYING(PLAYER_ID())
AND HAS_ENTITY_BEEN_DAMAGED_BY_ENTITY(chopID, PLAYER_PED_ID())
g_bPlayerHasKilledChop = TRUE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop has been killed by the player") #ENDIF
IF NOT IS_BIT_SET(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_KILLED_BY_PLAYER))
AND NOT IS_PLAYER_SWITCH_IN_PROGRESS()
SET_BIT(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_KILLED_BY_PLAYER))
PRINT_HELP("CHOP_H_DEAD") // "Franklin killed Chop. Chop will soon return to Franklin's safehouse, but he's not happy."
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Displaying help text CHOP_H_DEAD") #ENDIF
ENDIF
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop has been killed by someone other than the player") #ENDIF
IF NOT IS_BIT_SET(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_KILLED_BY_SOMEONE_ELSE))
AND NOT IS_PLAYER_SWITCH_IN_PROGRESS()
SET_BIT(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_KILLED_BY_SOMEONE_ELSE))
PRINT_HELP("CHOP_H_DEAD2") // "Chop was killed. Chop will soon return to Franklin's safehouse."
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Displaying help text CHOP_H_DEAD2") #ENDIF
ENDIF
ENDIF
RETURN TRUE
ELSE
IF IS_PLAYER_SWITCH_IN_PROGRESS()
//RETURN TRUE // B*1395743 Don't cleanup Chop if switching to Franklin who is positioned near a Chop worldbrain position
CLEANUP_CHOP_HELP_TEXT() // But make sure any help text is removed as soon as the switch starts
ELSE // B*1514491 Don't check for distance if a switch scene is in progress
FLOAT f_cleanup_distance
IF eChopTask = CHOP_TASK_WALK
OR eChopTask = CHOP_TASK_SNIFF
OR eChopTask = CHOP_TASK_FETCH
f_cleanup_distance = 200.0
ELSE
f_cleanup_distance = 100.0
ENDIF
IF GET_DISTANCE_BETWEEN_ENTITIES(PLAYER_PED_ID(), chopID) > f_cleanup_distance
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop has been abandoned, distance = ", f_cleanup_distance) #ENDIF
IF NOT IS_BIT_SET(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_TOO_FAR_AWAY))
AND IS_PLAYER_PLAYING(PLAYER_ID()) // B*940495 Don't display if player isn't in control
AND NOT IS_PLAYER_SWITCH_IN_PROGRESS() // Don't display if player is switching to/from Franklin
IF eChopTask = CHOP_TASK_WAIT
OR eChopTask = CHOP_TASK_WAIT_PLAY_ANIM
OR eChopTask = CHOP_TASK_SPAWNED_IN_KENNEL
OR eChopTask = CHOP_TASK_WANDER // B*1280794 Don't display if player has dismissed Chop
OR eChopTask = CHOP_TASK_RETURN // B*1280794 Don't display if player has dismissed Chop
OR eChopTask = CHOP_TASK_SHIT // B*1306595 Don't display if Chop is taking a dump while mooching near his kennel
// Don't display any help message
ELSE // B*940630 Only display if player is interacting with Chop
SET_BIT(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_TOO_FAR_AWAY))
PRINT_HELP("CHOP_H_RANGE") // "Chop will stop following if you go too far away."
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Displaying help text CHOP_H_RANGE") #ENDIF
ENDIF
ENDIF
RETURN TRUE
ENDIF
ENDIF
IF NOT IS_PED_IN_ANY_VEHICLE(chopID)
AND GET_ENTITY_HEALTH(chopID) > 0
IF IS_ENTITY_IN_WATER(chopID)
VECTOR v_chop = GET_ENTITY_COORDS(chopID)
FLOAT f_water_height
IF GET_WATER_HEIGHT(v_chop, f_water_height)
IF f_water_height-v_chop.z > 0.1
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop is in deep water so killing him, water depth is ", f_water_height-v_chop.z) #ENDIF
SET_ENTITY_HEALTH(chopID, 0) // B*1413242 Ensure Chop is killed if in water
ENDIF
ENDIF
ENDIF
IF IS_ENTITY_ON_FIRE(chopID)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop is on fire so killing him") #ENDIF
SET_ENTITY_HEALTH(chopID, 0) // B*1563377 Ensure Chop is killed if on fire
ENDIF
ENDIF
ENDIF
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop doesn't exist") #ENDIF
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Creates Chop's bone blip.
PROC CREATE_CHOP_BLIP()
IF NOT DOES_BLIP_EXIST(blipChop)
AND IS_ENTITY_ALIVE(chopID)
AND NOT IS_PED_IN_ANY_VEHICLE(chopID, TRUE)
// Turn off relevant static blip as Chop's blip will be handled by this script whilst it is active
IF GET_MISSION_FLOW_FLAG_STATE(FLOWFLAG_FRANKLIN_MOVED_TO_HILLS_APARTMENT)
SET_STATIC_BLIP_ACTIVE_STATE(STATIC_BLIP_AMBIENT_CHOP_VH, FALSE)
ELSE
SET_STATIC_BLIP_ACTIVE_STATE(STATIC_BLIP_AMBIENT_CHOP_SC, FALSE)
ENDIF
// Create dynamic Chop blip
blipChop = CREATE_BLIP_FOR_PED(chopID, FALSE)
SET_BLIP_SPRITE(blipChop, RADAR_TRACE_CHOP)
ENDIF
ENDPROC
/// PURPOSE:
/// Puts Chop in the player's group.
PROC PUT_CHOP_IN_PLAYER_GROUP()
IF NOT IS_PED_IN_GROUP(chopID)
SET_PED_AS_GROUP_MEMBER(chopID, GET_PED_GROUP_INDEX(PLAYER_PED_ID()))
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Put Chop in player's group") #ENDIF
ENDIF
IF IS_PED_IN_GROUP(chopID)
SET_PED_NEVER_LEAVES_GROUP(chopID, TRUE)
SET_GROUP_FORMATION_SPACING(PLAYER_GROUP_ID(), 1.0, 0.9, 3.0)
SET_PED_CAN_TELEPORT_TO_GROUP_LEADER(chopID, GET_PED_GROUP_INDEX(PLAYER_PED_ID()), TRUE)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Setup Chop in player's group") #ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Sets the anim dict for Chop based on the player vehicle.
PROC SET_LAYOUT_HASH_FOR_CHOP_VEHICLE()
IF IS_ENTITY_ALIVE(vehChop)
vehLayoutHash = INT_TO_ENUM(VEHICLE_LAYOUT_TYPE, GET_VEHICLE_LAYOUT_HASH(vehChop))
SWITCH vehLayoutHash
CASE LAYOUT_VAN
CASE LAYOUT_VAN_BODHI
CASE LAYOUT_VAN_BOXVILLE
CASE LAYOUT_VAN_CADDY
CASE LAYOUT_VAN_JOURNEY
CASE LAYOUT_VAN_MULE
CASE LAYOUT_VAN_POLICE
CASE LAYOUT_VAN_TRASH
CASE LAYOUT_4X4_DUBSTA // B*1844650 - Hipster DLC vehicle
CASE LAYOUT_VAN_ROOSEVELT // B*1844650 - Hipster DLC vehicle
stringChopInVehicleAnimDict = "creatures@rottweiler@in_vehicle@van"
stringFranklinInVehicleAnim = "van_ds_open_door_for_chop"
BREAK
CASE LAYOUT_LOW
//CASE LAYOUT_LOW_BFINJECTION // B*1834123 use Chop's normal enter/exit anims for the BF Injection vehicle
CASE LAYOUT_LOW_DUNE
CASE LAYOUT_LOW_INFERNUS
CASE LAYOUT_LOW_RESTRICTED
CASE LAYOUT_LOW_SENTINEL2
CASE LAYOUT_LOW_CHEETAH
CASE LAYOUT_LOW_BLADE // B*1844650 - Hipster DLC vehicle
CASE LAYOUT_LOW_TURISMOR // B*1844650 - Hipster DLC vehicle
CASE LAYOUT_LOW_FURORE // B*1993428 - DLC vehicle
CASE LAYOUT_LOW_OSIRIS // Luxe DLC vehicle
stringChopInVehicleAnimDict = "creatures@rottweiler@in_vehicle@low_car"
stringFranklinInVehicleAnim = "low_ds_open_door_for_chop"
BREAK
CASE LAYOUT_4X4
CASE LAYOUT_BISON
CASE LAYOUT_RANGER
CASE LAYOUT_RANGER_SWAT
stringChopInVehicleAnimDict = "creatures@rottweiler@in_vehicle@4x4"
stringFranklinInVehicleAnim = "std_ds_open_door_for_chop"
BREAK
DEFAULT
stringChopInVehicleAnimDict = "creatures@rottweiler@in_vehicle@std_car"
stringFranklinInVehicleAnim = "std_ds_open_door_for_chop"
BREAK
ENDSWITCH
IF GET_ENTITY_MODEL(vehChop) = BRAWLER
stringChopInVehicleAnimDict = "creatures@rottweiler@in_vehicle@4x4"
stringFranklinInVehicleAnim = "std_ds_open_door_for_chop"
ENDIF
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Set vehLayoutHash to ", stringChopInVehicleAnimDict) #ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Sets which door should open/close for Chop based on the player vehicle.
PROC SET_CHOPS_DOOR_FOR_CHOP_VEHICLE()
IF IS_ENTITY_ALIVE(vehChop)
MODEL_NAMES model_player_car = GET_ENTITY_MODEL(vehChop)
IF model_player_car = COACH
doorChop = SC_DOOR_FRONT_LEFT
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Set doorChop to SC_DOOR_FRONT_LEFT") #ENDIF
ELSE
doorChop = SC_DOOR_FRONT_RIGHT
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Set doorChop to SC_DOOR_FRONT_RIGHT") #ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Updates Chop's collar if the player has selected a different one in the app.
PROC UPDATE_CHOP_COLLAR()
IF iCurrentChopCollar <> GET_CHOP_COLLAR_FROM_APP()
AND IS_ENTITY_ALIVE(chopID)
IF HAS_PLAYER_USED_CHOP_APP()
iCurrentChopCollar = GET_CHOP_COLLAR_FROM_APP()
SET_PED_COMPONENT_VARIATION(chopID, PED_COMP_TORSO, 0, iCurrentChopCollar)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Changed Chop's collar to ", iCurrentChopCollar) #ENDIF
ELIF iCurrentChopCollar <> 4 // Prevents this constantly being set
iCurrentChopCollar = 4 // B*1168557 Set to Ballas collar by default
SET_PED_COMPONENT_VARIATION(chopID, PED_COMP_TORSO, 0, iCurrentChopCollar)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player hasn't used app so set his collar to Ballas (colour 4)") #ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Grab Chop initially using g_pScene_buddy, or GET_PED_NEARBY_PEDS if for whatever reason g_pScene_buddy no longer exists (see B*1881917)
FUNC BOOL GRABBED_CHOP_FROM_TIMETABLE_SCENE()
IF IS_ENTITY_ALIVE(g_pScene_buddy) // Check to see if Chop already exists (from timetable scene)
AND IS_PED_MODEL(g_pScene_buddy, GET_CHOP_MODEL())
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Grabbed Chop from g_pScene_buddy, now waiting for timetable scene to end") #ENDIF
chopID = g_pScene_buddy
RETURN TRUE
ENDIF
// If for whatever reason g_pScene_buddy doesn't exist by the time the Chop script needs to setup Chop, as a backup plan check GET_PED_NEARBY_PEDS for a ped with Chop's model
PED_INDEX NearbyPeds[10]
INT iCount = GET_PED_NEARBY_PEDS(PLAYER_PED_ID(), NearbyPeds)
INT i
REPEAT iCount i
IF NearbyPeds[i] != NULL
AND IS_ENTITY_ALIVE(NearbyPeds[i])
AND GET_ENTITY_MODEL(NearbyPeds[i]) = GET_CHOP_MODEL()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Grabbed Chop using GET_PED_NEARBY_PEDS, now waiting for timetable scene to end") #ENDIF
chopID = NearbyPeds[i]
RETURN TRUE
ENDIF
ENDREPEAT
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Creates Chop or grabs him from timetable scene if he already exists in the world.
PROC INIT_CHOP()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Initialising Chop") #ENDIF
IF bDoingTimetableSwitchChopNotInVehicle = TRUE
IF GRABBED_CHOP_FROM_TIMETABLE_SCENE()
SET_ENTITY_AS_MISSION_ENTITY(chopID, TRUE, TRUE)
CLEAR_PED_TASKS(chopID)
UPDATE_CHOP_COLLAR()
WHILE Is_Player_Timetable_Scene_In_Progress()
WAIT(0)
// Playing this anim on Chop is causing 1449657 for some reason
/*IF Get_Player_Timetable_Scene_In_Progress() = PR_SCENE_F0_PLAYCHOP
OR Get_Player_Timetable_Scene_In_Progress() = PR_SCENE_F1_PLAYCHOP
IF IS_ENTITY_ALIVE(chopID)
AND NOT IsPedPerformingTask(chopID, SCRIPT_TASK_PLAY_ANIM)
REQUEST_ANIM_DICT("creatures@rottweiler@amb@world_dog_barking@base")
IF HAS_ANIM_DICT_LOADED("creatures@rottweiler@amb@world_dog_barking@base")
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(chopID, TRUE)
CLEAR_PED_TASKS_IMMEDIATELY(chopID)
TASK_PLAY_ANIM(chopID, "creatures@rottweiler@amb@world_dog_barking@base", "base", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_LOOPING|AF_NOT_INTERRUPTABLE)
REMOVE_ANIM_DICT("creatures@rottweiler@amb@world_dog_barking@base")
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Playing anim on Chop during timetable scene") #ENDIF
ENDIF
ENDIF
ENDIF*/
ENDWHILE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Timetable scene has now finished") #ENDIF
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Tried to create Chop for timetable switch, but couldn't grab g_pScene_buddy or using GET_PED_NEARBY_PEDS") #ENDIF
ENDIF
ELIF bDoingTimetableSwitchChopIsInVehicle = TRUE
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Creating Chop for in-vehicle timetable scene..") #ENDIF
vehChop = GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID())
SET_LAYOUT_HASH_FOR_CHOP_VEHICLE()
SET_CHOPS_DOOR_FOR_CHOP_VEHICLE()
CREATE_CHOP(chopID, GET_ENTITY_COORDS(PLAYER_PED_ID()))
IF IS_ENTITY_ALIVE(chopID)
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(chopID, TRUE)
SET_PED_ALLOW_VEHICLES_OVERRIDE(chopID, TRUE)
SET_PED_CAN_PLAY_AMBIENT_ANIMS(chopID, FALSE)
SET_PED_CAN_PLAY_AMBIENT_BASE_ANIMS(chopID, FALSE)
PUT_CHOP_IN_PLAYER_GROUP()
SET_ENTITY_INVINCIBLE(chopID, TRUE) // B*1337712
SET_PED_INTO_VEHICLE(chopID, vehChop, VS_FRONT_RIGHT)
chopInVehicleStatus = CHOP_VEHICLE_STATUS_CHOP_SAT_IN_CAR
UPDATE_CHOP_COLLAR()
FORCE_PED_AI_AND_ANIMATION_UPDATE(chopID)
ENDIF
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Created Chop for in-vehicle timetable scene now waiting for timetable scene to end") #ENDIF
WHILE Is_Player_Timetable_Scene_In_Progress()
IF IS_ENTITY_ALIVE(chopID)
AND NOT IsPedPerformingTask(chopID, SCRIPT_TASK_PLAY_ANIM)
REQUEST_ANIM_DICT(stringChopInVehicleAnimDict)
IF HAS_ANIM_DICT_LOADED(stringChopInVehicleAnimDict)
TASK_PLAY_ANIM(chopID, stringChopInVehicleAnimDict, "sit", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_LOOPING)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Playing anim on Chop in vehicle") #ENDIF
ENDIF
ENDIF
WAIT(0)
ENDWHILE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Timetable scene has now finished") #ENDIF
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Tried to create Chop for in-vehicle timetable switch, but the player isn't in a vehicle") #ENDIF
ENDIF
ENDIF
ENDIF
IF NOT DOES_ENTITY_EXIST(chopID) // This script needs to create Chop - not handed over from timetable scene
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Creating new Chop instance") #ENDIF
CREATE_CHOP(chopID, sChopData.vSpawnCoords, sChopData.fSpawnHeading, TRUE)
ENDIF
IF IS_ENTITY_ALIVE(chopID)
ADD_RELATIONSHIP_GROUP("rel_group_chop", relGroupChop)
bRelGroupExists = TRUE
SET_RELATIONSHIP_BETWEEN_GROUPS(ACQUAINTANCE_TYPE_PED_LIKE, RELGROUPHASH_PLAYER, relGroupChop)
SET_RELATIONSHIP_BETWEEN_GROUPS(ACQUAINTANCE_TYPE_PED_LIKE, relGroupChop, RELGROUPHASH_PLAYER)
SET_RELATIONSHIP_BETWEEN_GROUPS(ACQUAINTANCE_TYPE_PED_DISLIKE, RELGROUPHASH_CAT, relGroupChop)
SET_RELATIONSHIP_BETWEEN_GROUPS(ACQUAINTANCE_TYPE_PED_DISLIKE, relGroupChop, RELGROUPHASH_CAT)
SET_PED_RELATIONSHIP_GROUP_HASH(chopID, relGroupChop)
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(chopID, TRUE)
SET_PED_ALLOW_VEHICLES_OVERRIDE(chopID, TRUE) // Allow chop into vehicles - see B*893918
SET_PED_CONFIG_FLAG(chopID, PCF_UseKinematicModeWhenStationary, TRUE)
SET_PED_CONFIG_FLAG(chopID, PCF_DontAllowToBeDraggedOutOfVehicle, TRUE)
SET_PED_CONFIG_FLAG(chopID, PCF_WillFlyThroughWindscreen, FALSE)
SET_PED_CONFIG_FLAG(chopID, PCF_GetOutUndriveableVehicle, FALSE)
SET_PED_CONFIG_FLAG(chopID, PCF_GetOutBurningVehicle, FALSE)
SET_PED_CONFIG_FLAG(chopID, PCF_RunFromFiresAndExplosions, TRUE)
SET_PED_CONFIG_FLAG(chopID, PCF_KeepRelationshipGroupAfterCleanUp, TRUE)
SET_PED_CONFIG_FLAG(chopID, PCF_TeleportToLeaderVehicle, FALSE) // Don't warp Chop into Franklin's vehicle, he uses sync scenes attached to the vehicle
SET_PED_CONFIG_FLAG(chopID, PCF_DontActivateRagdollFromBulletImpact, TRUE) // B*1424904 Prevent Chop repeatedly being shot to the ground when in combat
SET_PED_CONFIG_FLAG(chopID, PCF_DisableGoToWritheWhenInjured, TRUE)
SET_PED_CONFIG_FLAG(chopID, PCF_RemoveDeadExtraFarAway, TRUE)
SET_PED_CONFIG_FLAG(chopID, PCF_DisableLadderClimbing, TRUE)
SET_PED_CONFIG_FLAG(chopID, PCF_CanBeArrested, FALSE)
SET_PED_CONFIG_FLAG(chopID, PCF_AllowLockonToFriendlyPlayers, TRUE) // B*1502571 Ensure Chop can target peds he's in the same rel group as
SET_PED_CONFIG_FLAG(chopID, PCF_DontEnterVehiclesInPlayersGroup, TRUE) // B*1583936 Ensure Chop won't automatically follow Franklin into a vehicle
SET_PED_CAN_PLAY_AMBIENT_ANIMS(chopID, FALSE)
SET_PED_CAN_PLAY_AMBIENT_BASE_ANIMS(chopID, FALSE)
GIVE_WEAPON_TO_PED(chopID, WEAPONTYPE_ANIMAL, 1, TRUE, TRUE)
SET_PED_COMBAT_ATTRIBUTES(chopID, CA_ALWAYS_FIGHT, TRUE)
SET_PED_COMBAT_ATTRIBUTES(chopID, CA_USE_COVER, FALSE)
SET_PED_COMBAT_ATTRIBUTES(chopID, CA_CAN_FIGHT_ARMED_PEDS_WHEN_NOT_ARMED, TRUE)
SET_PED_HEARING_RANGE(chopID, CHOP_ATTACK_RANGE)
SET_PED_SEEING_RANGE(chopID, CHOP_ATTACK_RANGE)
SET_PED_FLEE_ATTRIBUTES(chopID, FA_NEVER_FLEE, TRUE)
SET_PED_SUFFERS_CRITICAL_HITS(chopID, FALSE)
SET_PED_MAX_HEALTH(chopID, 800)
SET_ENTITY_HEALTH(chopID, 800)
SET_ENTITY_LOD_DIST(chopID, 200) // B*1405898 Ensure Chop never vanishes due to LOD dist
IF NOT IS_PED_IN_ANY_VEHICLE(chopID)
CREATE_CHOP_BLIP()
ENDIF
ADD_PED_FOR_DIALOGUE(sConversation, 3, chopID, "CHOP")
SET_PED_CAN_BE_TARGETTED(chopID, FALSE)
SET_ENTITY_IS_TARGET_PRIORITY(chopID, FALSE)
SET_ALLOW_LOCKON_TO_PED_IF_FRIENDLY(chopID, FALSE)
bIsChopTargettable = FALSE
g_bPlayerHasKilledChop = FALSE
iChopSicBallsMeter = 0
iChopSicBallsTimer = GET_GAME_TIMER()
ENDIF
ENDPROC
/// PURPOSE:
/// Resets the scan values used to find the closest pickup
PROC RESET_PICKUP_SCAN_VALUES()
INT i_pickup = 0
REPEAT 5 i_pickup // Reset the values
iClosestPickupOfType[i_pickup] = -1
fDistanceToClosestPickupOfType[i_pickup] = 999999
vClosestPickupOfType[i_pickup] = <<0,0,0>>
vChopSitNearClosestPickupOfType[i_pickup] = <<0,0,0>>
ENDREPEAT
ENDPROC
/// PURPOSE:
/// Creates Chop.
PROC DO_INITIALISE()
g_bPlayerInteractingWithChop = FALSE
REQUEST_MODEL(GET_CHOP_MODEL())
IF NOT HAS_MODEL_LOADED(GET_CHOP_MODEL())
EXIT
ENDIF
IF Is_Savehouse_Respawn_Available(SAVEHOUSE_FRANKLIN_VH)
sChopData.eSavehouse = SAVEHOUSE_FRANKLIN_VH
sChopData.vSpawnCoords = << 18.1531, 535.2469, 169.6324 >>
sChopData.fSpawnHeading = 204.8112
sChopData.vKennelCoords = << 19.527712,537.436035,170.143021 >>
sChopData.fKennelHeading = 151.306351
vChopShit = << 19.45963, 535.78174, 169.62770 >>
vChopWanderPositions[0] = <<17.36033, 528.89728, 169.62770>>
vChopWanderPositions[1] = <<19.46877, 529.17847, 169.62770>>
vChopWanderPositions[2] = <<20.82979, 531.51685, 169.62770>>
vChopWanderPositions[3] = <<22.83555, 532.32813, 169.62770>>
vChopWanderPositions[4] = <<20.79285, 535.00098, 169.62770>>
vChopWanderPositions[5] = <<18.05178, 535.63214, 169.62770>>
vChopWanderPositions[6] = <<18.92047, 534.09375, 169.62770>>
vChopWanderPositions[7] = <<17.51333, 533.47748, 169.62770>>
OPEN_TEST_POLY(g_polyPlayArea)
ADD_TEST_POLY_VERT(g_polyPlayArea, << 12.41124, 535.54688, 169.62770 >>)
ADD_TEST_POLY_VERT(g_polyPlayArea, << 19.04497, 538.57288, 169.62770 >>)
ADD_TEST_POLY_VERT(g_polyPlayArea, << 25.00015, 534.44141, 169.62770 >>)
ADD_TEST_POLY_VERT(g_polyPlayArea, << 28.09980, 527.76385, 169.42769 >>)
ADD_TEST_POLY_VERT(g_polyPlayArea, << 17.75896, 523.31293, 169.22769 >>)
CLOSE_TEST_POLY(g_polyPlayArea)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Data set for SAVEHOUSE_FRANKLIN_VH") #ENDIF
ELSE
sChopData.eSavehouse = SAVEHOUSE_FRANKLIN_SC
sChopData.vSpawnCoords = << -10.25168, -1422.90698, 29.67775 >>
sChopData.fSpawnHeading = 157.9037
sChopData.vKennelCoords = << -9.73, -1421.55, 30.10 >>
sChopData.fKennelHeading = 148.26
vChopShit = << -10.63790, -1424.56055, 29.67365 >>
vChopWanderPositions[0] = <<-10.03643, -1423.52869, 29.67602>>
vChopWanderPositions[1] = <<-10.78779, -1425.30566, 29.70937>>
vChopWanderPositions[2] = <<-12.70480, -1424.74805, 29.72222>>
vChopWanderPositions[3] = <<-12.47465, -1422.48462, 29.74699>>
vChopWanderPositions[4] = <<-14.26666, -1424.85522, 29.71964>>
vChopWanderPositions[5] = <<-14.55816, -1423.04810, 29.78372>>
vChopWanderPositions[6] = <<-16.13293, -1424.48914, 29.76139>>
vChopWanderPositions[7] = <<-16.25662, -1423.32495, 29.81294>>
OPEN_TEST_POLY(g_polyPlayArea)
ADD_TEST_POLY_VERT(g_polyPlayArea, << -7.26821, -1427.06482, 29.67468 >>)
ADD_TEST_POLY_VERT(g_polyPlayArea, << -7.39631, -1418.85083, 29.58580 >>)
ADD_TEST_POLY_VERT(g_polyPlayArea, << -14.27201, -1421.59021, 29.76819 >>)
ADD_TEST_POLY_VERT(g_polyPlayArea, << -22.85347, -1423.17273, 29.74042 >>)
ADD_TEST_POLY_VERT(g_polyPlayArea, << -22.77974, -1427.16797, 29.65953 >>)
CLOSE_TEST_POLY(g_polyPlayArea)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Data set for SAVEHOUSE_FRANKLIN_SC") #ENDIF
ENDIF
CPRINTLN(DEBUG_AMBIENT, "CHOP: ChopTrainingLevel = ", GET_CHOP_TRAINING_LEVEL_FROM_APP())
bPlayerUsedApp = HAS_PLAYER_USED_CHOP_APP()
CPRINTLN(DEBUG_AMBIENT, "CHOP: bPlayerUsedApp = ", bPlayerUsedApp)
CPRINTLN(DEBUG_AMBIENT, "CHOP: fMaxChopHuntDist = ", fMaxChopHuntDist)
INIT_CHOP()
IF GET_CHOP_BEHAVIOUR_FROM_APP() = CHOP_BEHAVIOUR_BAD
CLEAR_AREA_OF_OBJECTS(vChopShit, 2, CLEAROBJ_FLAG_FORCE) // B*1211717 Ensure previous Chop shit has been cleared
REQUEST_MODEL(PROP_BIG_SHIT_02)
WHILE NOT HAS_MODEL_LOADED(PROP_BIG_SHIT_02)
WAIT(0)
ENDWHILE
oChopShit = CREATE_OBJECT(PROP_BIG_SHIT_02, vChopShit)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop has been bad so create a turd prop near his kennel") #ENDIF
SET_MODEL_AS_NO_LONGER_NEEDED(PROP_BIG_SHIT_02)
bChopDoneShitDialogue = FALSE
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop hasn't been bad so not creating a turd prop") #ENDIF
bChopDoneShitDialogue = TRUE
ENDIF
bChopDidShit = FALSE
IF GET_RANDOM_INT_IN_RANGE(0, 2) = 0
iChopExitKennelTime = -1
ELSE
iChopExitKennelTime = GET_RANDOM_INT_IN_RANGE(20000, 30000)
ENDIF
IF bDoingTimetableSwitchChopNotInVehicle = TRUE
eChopTask = CHOP_TASK_BEG // B*1168773 Do something after the switch scene has ended
bPlayerChoseToWalkChop = TRUE // Ensures Chop will change to walk state after his trick
ELIF bDoingTimetableSwitchChopIsInVehicle = TRUE
eChopTask = CHOP_TASK_WALK
ELIF iChopExitKennelTime > -1
eChopTask = CHOP_TASK_SPAWNED_IN_KENNEL
bPlayerChoseToWalkChop = FALSE
ELSE
eChopTask = CHOP_TASK_WAIT
bPlayerChoseToWalkChop = FALSE
ENDIF
iNavBlockerPool = ADD_NAVMESH_BLOCKING_OBJECT(<<11.23, 515.40, 168>>, <<9, 21, 4>>, DEG_TO_RAD(113)) // B*1018859 Stop Chop going near the pool at Franklin's VH safehouse
iNavBlockerJacuzzi = ADD_NAVMESH_BLOCKING_OBJECT(<<24.0, 528.0, 168>>, <<5, 5, 4>>, DEG_TO_RAD(113)) // B*1463144 Stop Chop going near the jacuzzi at Franklin's VH safehouse
vChopCableCarPos[0] = <<444.1685, 5569.0161, 780.1895>>
vChopCableCarPos[1] = <<443.8968, 5574.9312, 780.1895>>
vChopCableCarPos[2] = <<-739.1624, 5596.7837, 40.6594>>
vChopCableCarPos[3] = <<-738.8002, 5593.2686, 40.6594>>
fChopCableCarPos[0] = 270.0
fChopCableCarPos[1] = 270.0
fChopCableCarPos[2] = 95.0
fChopCableCarPos[3] = 95.0
RESET_PICKUP_SCAN_VALUES()
bFranklinTaskedToLookAtChop = FALSE
bChopGreetedFranklinDone = FALSE
iWhistleTimer = GET_GAME_TIMER()
// Ensure this has a default value
INT i_chop_training_level = GET_CHOP_TRAINING_LEVEL_FROM_APP()
IF i_chop_training_level > 4
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: GET_CHOP_TRAINING_LEVEL_FROM_APP returned a value greater than 4") #ENDIF
i_chop_training_level = 4
ENDIF
tlChopHelp = "CHOP_H_WAIT_" // Ensure this has a default value
tlChopHelp += i_chop_training_level
IF IS_USING_KEYBOARD_AND_MOUSE(PLAYER_CONTROL)
tlChopHelp += "_KM"
ENDIF
g_bPlayerIsInCinema = FALSE // The player can't recruit Chop in a cinema so immediately reset the flag in case it got set to true
eStage = STAGE_PROCESS
ENDPROC
/// PURPOSE:
/// Returns TRUE if Chop is in the area allowed to perform tricks. See B*1040752.
FUNC BOOL IS_PED_IN_TRICK_AREA(PED_INDEX ped_to_check, BOOL b_check_z_diff = TRUE)
IF IS_ENTITY_ALIVE(chopID)
AND IS_ENTITY_ALIVE(PLAYER_PED_ID())
AND IS_ENTITY_IN_RANGE_COORDS_2D(ped_to_check, sChopData.vSpawnCoords, 30) // Do a quick check before the complex poly test
//DISPLAY_POLY(g_polyPlayArea)
IF b_check_z_diff = TRUE
IF IS_POINT_IN_POLY_2D(g_polyPlayArea, GET_ENTITY_COORDS(ped_to_check))
VECTOR v_chop = GET_ENTITY_COORDS(chopID)
VECTOR v_franklin = GET_ENTITY_COORDS(PLAYER_PED_ID())
IF ABSF(v_chop.z - v_franklin.z) < 2.5
RETURN TRUE
ENDIF
ENDIF
ELSE
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Is the letter scrap ambient running?
FUNC BOOL IS_LETTER_SCRAP_MISSION_ACTIVE()
RETURN g_savedGlobals.sAmbient.sLetterScrapData.bMissionActive
ENDFUNC
/// PURPOSE:
/// Has specified letter scrap been collected yet?
FUNC BOOL HAS_LETTER_SCRAP_BEEN_COLLECTED(INT iIndex)
// Invalid index
IF (iIndex < 0) OR (iIndex >= NUMBER_OF_LETTER_SCRAPS)
SCRIPT_ASSERT("HAS_LETTER_SCRAP_BEEN_COLLECTED() - Passed an invalid index")
RETURN FALSE
ENDIF
// if scrap index is 31 or less use first int
IF (iIndex <= 31)
RETURN IS_BIT_SET(g_savedGlobals.sAmbient.sLetterScrapData.iScrap0to31, iIndex)
ENDIF
// Other take 32 from index and use second int
RETURN IS_BIT_SET(g_savedGlobals.sAmbient.sLetterScrapData.iScrap32to63, iIndex - 32)
ENDFUNC
/// PURPOSE:
/// Is the spaceship part ambient running?
FUNC BOOL IS_SPACESHIP_PART_MISSION_ACTIVE()
RETURN g_savedGlobals.sAmbient.sSpaceshipPartData.bMissionActive
ENDFUNC
/// PURPOSE: Has specified spaceship part been collected yet?
FUNC BOOL HAS_SPACESHIP_PART_BEEN_COLLECTED(INT iIndex)
// Invalid index
IF (iIndex < 0) OR (iIndex >= NUMBER_OF_SPACESHIP_PARTS)
SCRIPT_ASSERT("HAS_SPACESHIP_PART_BEEN_COLLECTED() - Passed an invalid index")
RETURN FALSE
ENDIF
// if scrap index is 31 or less use first int
IF (iIndex <= 31)
RETURN IS_BIT_SET(g_savedGlobals.sAmbient.sSpaceshipPartData.iScrap0to31, iIndex)
ENDIF
// Other take 32 from index and use second int
RETURN IS_BIT_SET(g_savedGlobals.sAmbient.sSpaceshipPartData.iScrap32to63, iIndex - 32)
ENDFUNC
/// PURPOSE:
/// Continuous scan to find the nearest pickup of each type
PROC SCAN_FOR_NEAREST_PICKUP()
IF chopCurrentValidityScan != CHOP_CURRENT_PICKUP_TYPE_NONE // Don't allow continuous scanning if we're checking for validity prior to Chop sniffing the nearest pickup
OR (GET_GAME_TIMER() - iClosestPickupTimer) < 1000
OR bPlayerUsedApp = FALSE
EXIT
ENDIF
VECTOR v_pickup_pos
FLOAT f_current_dist
INT i_pickup
RESET_PICKUP_SCAN_VALUES()
i_pickup = 0
REPEAT NUMBER_OF_WEAPON_PICKUPS i_pickup
IF CAN_CHOP_REACH_WEAPON(i_pickup)
v_pickup_pos = GET_WEAPON_PICKUP_COORDS(i_pickup, TRUE)
IF NOT IS_VECTOR_ZERO(v_pickup_pos)
f_current_dist = GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(chopID), v_pickup_pos)
IF f_current_dist < fDistanceToClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_WEAPON]
AND f_current_dist < fMaxChopHuntDist
iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_WEAPON] = i_pickup
fDistanceToClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_WEAPON] = f_current_dist
vClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_WEAPON] = GET_WEAPON_PICKUP_COORDS(i_pickup, FALSE)
vChopSitNearClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_WEAPON] = v_pickup_pos
ENDIF
ENDIF
ENDIF
ENDREPEAT
i_pickup = 0
REPEAT NUMBER_OF_HEALTH_PICKUPS i_pickup
IF CAN_CHOP_REACH_HEALTH(i_pickup)
v_pickup_pos = GET_HEALTH_PICKUP_COORDS(i_pickup, TRUE)
IF NOT IS_VECTOR_ZERO(v_pickup_pos)
f_current_dist = GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(chopID), v_pickup_pos)
IF f_current_dist < fDistanceToClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_HEALTH]
AND f_current_dist < fMaxChopHuntDist
iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_HEALTH] = i_pickup
fDistanceToClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_HEALTH] = f_current_dist
vClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_HEALTH] = GET_HEALTH_PICKUP_COORDS(i_pickup, FALSE)
vChopSitNearClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_HEALTH] = v_pickup_pos
ENDIF
ENDIF
ENDIF
ENDREPEAT
i_pickup = 0
REPEAT NUMBER_OF_ARMOUR_PICKUPS i_pickup
IF CAN_CHOP_REACH_ARMOUR(i_pickup)
v_pickup_pos = GET_ARMOUR_PICKUP_COORDS(i_pickup, TRUE)
IF NOT IS_VECTOR_ZERO(v_pickup_pos)
f_current_dist = GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(chopID), v_pickup_pos)
IF f_current_dist < fDistanceToClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_ARMOUR]
AND f_current_dist < fMaxChopHuntDist
iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_ARMOUR] = i_pickup
fDistanceToClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_ARMOUR] = f_current_dist
vClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_ARMOUR] = GET_ARMOUR_PICKUP_COORDS(i_pickup, FALSE)
vChopSitNearClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_ARMOUR] = v_pickup_pos
ENDIF
ENDIF
ENDIF
ENDREPEAT
IF IS_LETTER_SCRAP_MISSION_ACTIVE()
i_pickup = 0
REPEAT NUMBER_OF_LETTER_SCRAPS i_pickup
IF NOT HAS_LETTER_SCRAP_BEEN_COLLECTED(i_pickup)
AND CAN_CHOP_REACH_LETTERSCRAP(i_pickup)
v_pickup_pos = GET_LETTERSCRAP_PICKUP_COORDS(i_pickup, TRUE)
IF NOT IS_VECTOR_ZERO(v_pickup_pos)
f_current_dist = GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(chopID), v_pickup_pos)
IF f_current_dist < fDistanceToClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_LETTER]
AND f_current_dist < fMaxChopHuntDist
iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_LETTER] = i_pickup
fDistanceToClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_LETTER] = f_current_dist
vClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_LETTER] = GET_LETTERSCRAP_PICKUP_COORDS(i_pickup, FALSE)
vChopSitNearClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_LETTER] = v_pickup_pos
ENDIF
ENDIF
ENDIF
ENDREPEAT
ENDIF
IF IS_SPACESHIP_PART_MISSION_ACTIVE()
i_pickup = 0
REPEAT NUMBER_OF_SPACESHIP_PARTS i_pickup
IF NOT HAS_SPACESHIP_PART_BEEN_COLLECTED(i_pickup)
AND CAN_CHOP_REACH_UFOSCRAP(i_pickup)
v_pickup_pos = GET_UFOSCRAP_PICKUP_COORDS(i_pickup, TRUE)
IF NOT IS_VECTOR_ZERO(v_pickup_pos)
f_current_dist = GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(chopID), v_pickup_pos)
IF f_current_dist < fDistanceToClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_SPACESHIP]
AND f_current_dist < fMaxChopHuntDist
iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_SPACESHIP] = i_pickup
fDistanceToClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_SPACESHIP] = f_current_dist
vClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_SPACESHIP] = GET_UFOSCRAP_PICKUP_COORDS(i_pickup, FALSE)
vChopSitNearClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_SPACESHIP] = v_pickup_pos
ENDIF
ENDIF
ENDIF
ENDREPEAT
ENDIF
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Closest.. armour = ", iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_ARMOUR], " health = ", iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_HEALTH],
" weapon = ", iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_WEAPON], " letter = ", iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_LETTER], " ufo = ", iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_SPACESHIP]) #ENDIF
iClosestPickupTimer = GET_GAME_TIMER()
ENDPROC
/// PURPOSE:
/// Returns TRUE if the validity check for the current closest pickup for all 5 types has been completed.
FUNC BOOL HAS_PICKUP_SCAN_BEEN_COMPLETED()
IF chopCurrentValidityScan = CHOP_CURRENT_PICKUP_TYPE_NONE
chopCurrentValidityScan = CHOP_CURRENT_PICKUP_TYPE_ARMOUR
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Starting HAS_PICKUP_SCAN_BEEN_COMPLETED") #ENDIF
ENDIF
SWITCH chopCurrentValidityScan
CASE CHOP_CURRENT_PICKUP_TYPE_ARMOUR
IF iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_ARMOUR] > -1
IF IS_QUERY_RESULT_INACTIVE()
INIT_PICKUP_QUERY_REQUEST(PC_QUERY_TYPE_ARMOUR, iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_ARMOUR])
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Starting armour check") #ENDIF
ENDIF
IF HAS_RETURNED_QUERY_RESULT()
IF HAS_QUERIED_PICKUP_BEEN_COLLECTED()
iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_ARMOUR] = -1
ENDIF
END_PICKUP_QUERY_REQUEST()
chopCurrentValidityScan = CHOP_CURRENT_PICKUP_TYPE_HEALTH
ENDIF
ELSE
chopCurrentValidityScan = CHOP_CURRENT_PICKUP_TYPE_HEALTH
ENDIF
BREAK
CASE CHOP_CURRENT_PICKUP_TYPE_HEALTH
IF iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_HEALTH] > -1
IF IS_QUERY_RESULT_INACTIVE()
INIT_PICKUP_QUERY_REQUEST(PC_QUERY_TYPE_HEALTH, iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_HEALTH])
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Starting health check") #ENDIF
ENDIF
IF HAS_RETURNED_QUERY_RESULT()
IF HAS_QUERIED_PICKUP_BEEN_COLLECTED()
iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_HEALTH] = -1
ENDIF
END_PICKUP_QUERY_REQUEST()
chopCurrentValidityScan = CHOP_CURRENT_PICKUP_TYPE_WEAPON
ENDIF
ELSE
chopCurrentValidityScan = CHOP_CURRENT_PICKUP_TYPE_WEAPON
ENDIF
BREAK
CASE CHOP_CURRENT_PICKUP_TYPE_WEAPON
IF iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_WEAPON] > -1
IF IS_QUERY_RESULT_INACTIVE()
INIT_PICKUP_QUERY_REQUEST(PC_QUERY_TYPE_WEAPON, iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_WEAPON])
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Starting weapon check") #ENDIF
ENDIF
IF HAS_RETURNED_QUERY_RESULT()
IF HAS_QUERIED_PICKUP_BEEN_COLLECTED()
iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_WEAPON] = -1
ENDIF
END_PICKUP_QUERY_REQUEST()
chopCurrentValidityScan = CHOP_CURRENT_PICKUP_TYPE_LETTER
ENDIF
ELSE
chopCurrentValidityScan = CHOP_CURRENT_PICKUP_TYPE_LETTER
ENDIF
BREAK
CASE CHOP_CURRENT_PICKUP_TYPE_LETTER // and SPACESHIP (can do both at the same time)
IF iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_LETTER] > -1
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Starting letter check") #ENDIF
IF HAS_LETTER_SCRAP_BEEN_COLLECTED(iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_LETTER])
iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_LETTER] = -1
ENDIF
ENDIF
IF iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_SPACESHIP] > -1
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Starting ufo check") #ENDIF
IF HAS_SPACESHIP_PART_BEEN_COLLECTED(iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_SPACESHIP])
iClosestPickupOfType[CHOP_CURRENT_PICKUP_TYPE_SPACESHIP] = -1
ENDIF
ENDIF
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Finished HAS_PICKUP_SCAN_BEEN_COMPLETED") #ENDIF
chopCurrentValidityScan = CHOP_CURRENT_PICKUP_TYPE_NONE
RETURN TRUE
BREAK
ENDSWITCH
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Checks if the player already has full armour / health.
FUNC BOOL IGNORE_FULL_HEALTH_AMMO_CHECK(BOOL b_ignore_check_for_full_health_armour, INT i_pickup_type)
IF b_ignore_check_for_full_health_armour = TRUE
RETURN TRUE
ENDIF
IF i_pickup_type = ENUM_TO_INT(CHOP_CURRENT_PICKUP_TYPE_HEALTH)
AND GET_ENTITY_HEALTH(PLAYER_PED_ID()) >= 200
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player already has full health so not including health in this check") #ENDIF
RETURN FALSE
ENDIF
IF i_pickup_type = ENUM_TO_INT(CHOP_CURRENT_PICKUP_TYPE_ARMOUR)
AND GET_PED_ARMOUR(PLAYER_PED_ID()) >= 100
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player already has full armour so not including armour in this check") #ENDIF
RETURN FALSE
ENDIF
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Works out the closest pickup.
PROC FIND_CLOSEST_PICKUP(BOOL b_ignore_check_for_full_health_armour)
FLOAT f_closest_dist = 9999
INT i_index = 0
REPEAT 5 i_index
IF iClosestPickupOfType[i_index] > -1 // Only include in calcs if the continuous scan has found a pickup of this type within fMaxChopHuntDist
AND fDistanceToClosestPickupOfType[i_index] < f_closest_dist
AND IGNORE_FULL_HEALTH_AMMO_CHECK(b_ignore_check_for_full_health_armour, i_index)
f_closest_dist = fDistanceToClosestPickupOfType[i_index]
iClosestPickup = iClosestPickupOfType[i_index]
chopCurrentPickupType = INT_TO_ENUM(CHOP_CURRENT_PICKUP_TYPE, i_index)
vClosestPickup = vClosestPickupOfType[i_index]
vChopSitNearClosestPickup = vChopSitNearClosestPickupOfType[i_index]
ENDIF
ENDREPEAT
ENDPROC
/// PURPOSE:
/// Find the closest weapon / letter scrap / spaceship part to Franklin.
FUNC BOOL IS_CHOP_WITHIN_RANGE_OF_NEAREST_PICKUP()
iClosestPickup = -1
vClosestPickup = <<0,0,0>>
vChopSitNearClosestPickup = <<0,0,0>>
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player health = ", GET_ENTITY_HEALTH(PLAYER_PED_ID()), " Player armour = ", GET_PED_ARMOUR(PLAYER_PED_ID())) #ENDIF
FIND_CLOSEST_PICKUP(FALSE) // First try to find the closest pickup, but not including health/armour if the player already has full
IF iClosestPickup = -1 // Still haven't found a closest pickup...
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Ignored health/armour if already full but didn't find another pickup type so trying again with a full check") #ENDIF
FIND_CLOSEST_PICKUP(TRUE) // ...so search again but this time allow health/armour if the player is already full
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Ignored health/armour if already full and found another pickup type so not trying again with a full check") #ENDIF
ENDIF
IF iClosestPickup > -1
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: chopCurrentPickupType = ", chopCurrentPickupType, " iClosestPickup = ", iClosestPickup, " fDistanceToClosestPickupOfType ", fDistanceToClosestPickupOfType[chopCurrentPickupType]) #ENDIF
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Plays a bark / howl type sound from Chop.
/// List of possible sounds to use:
/// BARK
/// BARK_SEQ (plays 2 or 3 barks in a sequence)
/// SNARL
/// GROWL
/// PLAYFUL (a friendly-type moan)
/// WHINE
/// BREATH_AGITATED
/// AGITATED
PROC PLAY_CHOP_SOUND(STRING chop_sound)
PLAY_ANIMAL_VOCALIZATION(chopID, AUD_ANIMAL_DOG_ROTTWEILER, chop_sound)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Playing Chop sound: ", chop_sound) #ENDIF
ENDPROC
/// PURPOSE:
/// Plays an in-vehicle bark anim on Chop then goes back to his sit loop.
PROC CHOP_BARK_IN_VEHICLE()
IF (GET_GAME_TIMER() - iBarkInVehicleTimer) > 500
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Performing Chop bark in vehicle") #ENDIF
iBarkInVehicleTimer = GET_GAME_TIMER()
IF IS_ENTITY_ALIVE(chopID)
AND IS_PED_IN_ANY_VEHICLE(chopID)
OPEN_SEQUENCE_TASK(taskSequence)
TASK_PLAY_ANIM(NULL, stringChopInVehicleAnimDict, "bark", SLOW_BLEND_IN, SLOW_BLEND_OUT, -1, AF_DEFAULT)
TASK_PLAY_ANIM(NULL, stringChopInVehicleAnimDict, "sit", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_LOOPING)
CLOSE_SEQUENCE_TASK(taskSequence)
TASK_PERFORM_SEQUENCE(chopID, taskSequence)
CLEAR_SEQUENCE_TASK(taskSequence)
FORCE_PED_AI_AND_ANIMATION_UPDATE(chopID)
ENDIF
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: NOT Performing Chop bark in vehicle") #ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Returns true if Franklin has got into a car without any doors.
FUNC BOOL IS_PLAYER_IN_CAR_CHOP_CANT_GET_INTO()
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID(), TRUE)
MODEL_NAMES model_player_car = GET_ENTITY_MODEL(GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID(), TRUE))
IF model_player_car = CADDY // B*1380043
OR model_player_car = CADDY2
OR model_player_car = DUNE
OR model_player_car = AIRTUG
OR model_player_car = BLAZER
OR model_player_car = BLAZER2
OR model_player_car = BULLDOZER
OR model_player_car = CUTTER
OR model_player_car = DUMP
OR model_player_car = FORKLIFT
OR model_player_car = HANDLER
OR model_player_car = MOWER
OR model_player_car = RHINO
OR model_player_car = TRACTOR
OR model_player_car = TRACTOR2
OR model_player_car = TRACTOR3
OR model_player_car = INT_TO_ENUM(MODEL_NAMES, GET_HASH_KEY("bifta")) // Beach Bum DLC
OR model_player_car = BLAZER3 // Beach Bum DLC
OR model_player_car = DUNE2 // Beach Bum DLC
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Some vehicles Chop should just warp into rather than playing Franklin's open/close door anims and Chop's entering/exiting anims.
FUNC BOOL SHOULD_CHOP_WARP_INTO_THIS_VEHICLE_MODEL()
IF IS_ENTITY_ALIVE(vehChop)
MODEL_NAMES model_player_car = GET_ENTITY_MODEL(GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID(), TRUE))
IF model_player_car = AIRBUS
OR model_player_car = BARRACKS
OR model_player_car = BARRACKS2
OR model_player_car = BIFF
OR model_player_car = BUS
OR model_player_car = COACH
OR model_player_car = HAULER
OR model_player_car = MIXER
OR model_player_car = MIXER2
OR model_player_car = PACKER
OR model_player_car = PBUS
OR model_player_car = PHANTOM
OR model_player_car = POUNDER
OR model_player_car = RIOT
OR model_player_car = RUBBLE
OR model_player_car = SCRAP
OR model_player_car = STOCKADE
OR model_player_car = STOCKADE3
OR model_player_car = TIPTRUCK
OR model_player_car = TIPTRUCK2
OR model_player_car = TOWTRUCK
OR model_player_car = TOWTRUCK2
OR model_player_car = INT_TO_ENUM(MODEL_NAMES, GET_HASH_KEY("monster")) // Independence Day DLC
OR model_player_car = INT_TO_ENUM(MODEL_NAMES, GET_HASH_KEY("marshall")) // B*2013329
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Returns true if Franklin has got into a vehicle that Chop can also get into too.
FUNC BOOL IS_PLAYER_IN_SUITABLE_VEHICLE_FOR_CHOP_TO_ENTER()
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
AND IS_VEHICLE_DRIVEABLE(GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID()))
AND NOT IS_PED_ON_ANY_BIKE(PLAYER_PED_ID())
AND NOT IS_PED_IN_ANY_PLANE(PLAYER_PED_ID())
AND NOT IS_PED_IN_ANY_HELI(PLAYER_PED_ID())
AND NOT IS_PED_IN_ANY_BOAT(PLAYER_PED_ID())
AND GET_VEHICLE_MAX_NUMBER_OF_PASSENGERS(GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID())) >= 1
AND IS_VEHICLE_SEAT_FREE(GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID()), VS_FRONT_RIGHT)
AND NOT IS_PLAYER_IN_CAR_CHOP_CANT_GET_INTO() // Chop can't get into these as they're awkward vehicles like the dune buggy etc
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// When riding in Franklin's car or following Franklin's bike, Chop will occasionally check for nearby pickups and alert the player if he finds one. See B*1007705.
PROC PERIODICALLY_CHECK_FOR_NEARBY_PICKUPS()
IF GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) = 0
AND bPlayerUsedApp = TRUE // B*1096431 Don't scan for pickups if player hasn't used the app
IF iDelayUntilNextPickupScan = -1
iDelayUntilNextPickupScan = GET_GAME_TIMER()
ENDIF
IF (GET_GAME_TIMER() - iDelayUntilNextPickupScan) > 10000
AND iDoingChopInVehicleCollisionAnim > -1 // Don't bark when doing a collision anim
AND HAS_PICKUP_SCAN_BEEN_COMPLETED()
IF IS_CHOP_WITHIN_RANGE_OF_NEAREST_PICKUP()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Silently scanned for nearby pickup and found one") #ENDIF
IF IS_ENTITY_ALIVE(chopID)
IF IS_PED_IN_ANY_VEHICLE(chopID) // B*1406358 Check Chop is in a vehicle so should play a bark anim
CHOP_BARK_IN_VEHICLE()
ELSE
PLAY_CHOP_SOUND("BARK")
ENDIF
ENDIF
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Silently scanned for nearby pickup but didn't find one") #ENDIF
ENDIF
iDelayUntilNextPickupScan = GET_GAME_TIMER()
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Returns true if the player either is locked onto Chop or is free-aiming at Chop
FUNC BOOL IS_PLAYER_UNARMED_AND_AIMING_AT_CHOP()
IF IS_PLAYER_TARGETTING_ENTITY(PLAYER_ID(), chopID)
OR (eChopTask = CHOP_TASK_PET AND IS_CONTROL_PRESSED(FRONTEND_CONTROL, INPUT_AIM)) // Petting Chop will cause the player to cease targetting Chop // Using INPUT_AIM to fix B*1499642
WEAPON_TYPE current_player_weapon
GET_CURRENT_PED_WEAPON(PLAYER_PED_ID(), current_player_weapon)
IF current_player_weapon = WEAPONTYPE_UNARMED
//#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player is targetting Chop") #ENDIF
RETURN TRUE
ENDIF
ENDIF
//#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player isn't targetting Chop") #ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Sets Chop task and resets task stage.
PROC GIVE_CHOP_TASK(CHOP_TASK_ENUM eTask, BOOL b_clear_chop_tasks = TRUE)
chopInVehicleStatus = CHOP_VEHICLE_STATUS_FRANKLIN_NOT_IN_VEHICLE
IF IS_ENTITY_ALIVE(chopID)
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(chopID, TRUE)
CLEAR_PED_LAST_WEAPON_DAMAGE(chopID) // Prevent Chop bailing out of a trick/shit straight away if he's previously been damaged
IF NOT IS_PED_IN_ANY_VEHICLE(chopID) // If Chop is spawned in vehicle from switch scene don't clear his anim
IF b_clear_chop_tasks = TRUE
CLEAR_PED_TASKS(chopID)
ENDIF
SET_ENTITY_INVINCIBLE(chopID, FALSE)
ENDIF
ENDIF
eChopTask = eTask
eNextChopTask = CHOP_TASK_NONE
iChopStage = 0
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: new primary task[", ENUM_TO_INT(eTask), "]") #ENDIF
iClosestPickup = -1
vClosestPickup = <<0,0,0>>
vChopSitNearClosestPickup = <<0,0,0>>
chopCurrentValidityScan = CHOP_CURRENT_PICKUP_TYPE_NONE
END_PICKUP_QUERY_REQUEST()
bChopWanderingBetweenPositions = FALSE
bPlayedChopBusyDialogue = FALSE
iBulletNearChopTimer = GET_GAME_TIMER()
SET_AUDIO_FLAG("DisableBarks", FALSE)
ENDPROC
/// PURPOSE:
/// See B*1445449 - Chop whines if player shoots near to him
PROC CHECK_FOR_PLAYER_BULLET_NEAR_CHOP()
IF (GET_GAME_TIMER() - iBulletNearChopTimer) > 2000
AND IS_ENTITY_ALIVE(chopID)
AND IS_BULLET_IN_AREA(GET_ENTITY_COORDS(chopID), 5)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player fired near Chop") #ENDIF
PLAY_CHOP_SOUND("WHINE")
iBulletNearChopTimer = GET_GAME_TIMER()
ENDIF
ENDPROC
/// PURPOSE:
/// Returns true if Franklin has got into a vehicle that Chop can follow.
FUNC BOOL IS_PLAYER_IN_SUITABLE_VEHICLE_FOR_CHOP_TO_FOLLOW()
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
AND IS_VEHICLE_DRIVEABLE(GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID()))
IF IS_PED_ON_ANY_BIKE(PLAYER_PED_ID())
OR IS_PLAYER_IN_CAR_CHOP_CANT_GET_INTO() // Chop can't get into these so just make him follow
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// See B*1502293 - Delete any nearby old balls.
PROC SCAN_FOR_OLD_BALLS_NEAR_FRANKLIN()
IF (GET_GAME_TIMER() - iOldBallNearFranklinTimer) > 500
iOldBallNearFranklinTimer = GET_GAME_TIMER()
IF eChopTask != CHOP_TASK_FETCH
AND GET_PROJECTILE_OF_PROJECTILE_TYPE_WITHIN_DISTANCE(PLAYER_PED_ID(), WEAPONTYPE_BALL, 1.5, vBallPosition, eBall)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Found old ball near Franklin so deleting it") #ENDIF
CLEANUP_BALL(TRUE)
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Returns TRUE if Franklin is in an interior that Chop can't follow into. Also see B*1006788 - Don't allow interaction with Chop if player is inside Franklin's house.
FUNC BOOL IS_PLAYER_IN_RESTRICTED_INTERIOR()
IF IS_VALID_INTERIOR(GET_INTERIOR_FROM_ENTITY(PLAYER_PED_ID()))
IF GET_INTERIOR_FROM_ENTITY(PLAYER_PED_ID()) = GET_INTERIOR_AT_COORDS(<<-14.5,-1437.2,31.1>>)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player is in Franklin's VC safehouse") #ENDIF
RETURN TRUE
ENDIF
IF GET_INTERIOR_FROM_ENTITY(PLAYER_PED_ID()) = GET_INTERIOR_AT_COORDS(<<7.6,537.3,176.0>>)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player is in Franklin's SH safehouse") #ENDIF
RETURN TRUE
ENDIF
IF GET_INTERIOR_FROM_ENTITY(PLAYER_PED_ID()) = GET_INTERIOR_AT_COORDS(<<130.2632, -1295.0345, 28.2695>>) // B*1407356 Prevent Chop following into the strip club
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player is in strip club") #ENDIF
RETURN TRUE
ENDIF
// Add any other interiors here
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Trigger conversation for if Franklin and Chop are in a car and the player puts on a radio station that Chop doesn't like.
PROC PLAY_RADIO_HOWL_DIALOGUE()
IF iDoingChopInVehicleCollisionAnim > -1 // Don't bark when doing a collision anim
AND (GET_GAME_TIMER() - iRadioStationHowlTimer) > iDelayUntilNextHowl
IF GET_PLAYER_RADIO_STATION_GENRE() = ENUM_TO_INT(RADIO_GENRE_LEFT_WING_TALK)
OR GET_PLAYER_RADIO_STATION_GENRE() = ENUM_TO_INT(RADIO_GENRE_RIGHT_WING_TALK)
OR GET_PLAYER_RADIO_STATION_GENRE() = ENUM_TO_INT(RADIO_GENRE_COUNTRY)
OR GET_PLAYER_RADIO_STATION_GENRE() = ENUM_TO_INT(RADIO_GENRE_JAZZ)
CHOP_BARK_IN_VEHICLE()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Making Chop howl due to disliking current radio station") #ENDIF
iRadioStationHowlTimer = GET_GAME_TIMER()
iDelayUntilNextHowl = GET_RANDOM_INT_IN_RANGE(6000, 8000)
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Updates Chop's anims when he's sat in a car.
PROC UPDATE_CHOPS_IN_VEHICLE_ANIMS()
IF IS_ENTITY_ALIVE(chopID)
AND IS_ENTITY_ALIVE(vehChop)
IF iDoingChopInVehicleCollisionAnim > -1
IF (GET_GAME_TIMER() - iDoingChopInVehicleCollisionAnim) > 500 // B*1433652 Prevent retriggering straight away
AND GET_ENTITY_SPEED(vehChop) > 5.0
AND HAS_ENTITY_COLLIDED_WITH_ANYTHING(vehChop)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: vehChop collided with something so playing shunt anim on Chop") #ENDIF
TASK_PLAY_ANIM(chopID, stringChopInVehicleAnimDict, "shunt_from_back")
FORCE_PED_AI_AND_ANIMATION_UPDATE(chopID)
PLAY_CHOP_SOUND("GROWL")
iDoingChopInVehicleCollisionAnim = -1
iBarkInVehicleTimer = GET_GAME_TIMER() // B*1801754 Prevent barking immediately after a vehicle shunt
ENDIF
ELSE
IF NOT IS_ENTITY_PLAYING_ANIM(chopID, stringChopInVehicleAnimDict, "shunt_from_back")
OR (IS_ENTITY_PLAYING_ANIM(chopID, stringChopInVehicleAnimDict, "shunt_from_back") AND GET_ENTITY_ANIM_CURRENT_TIME(chopID, stringChopInVehicleAnimDict, "shunt_from_back") > 0.98)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Restoring Chop's sit in vehicle anim") #ENDIF
TASK_PLAY_ANIM(chopID, stringChopInVehicleAnimDict, "sit", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_LOOPING)
FORCE_PED_AI_AND_ANIMATION_UPDATE(chopID)
iDoingChopInVehicleCollisionAnim = GET_GAME_TIMER()
iBarkInVehicleTimer = GET_GAME_TIMER() // B*1801754 Prevent barking immediately after a vehicle shunt
ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Chop barks to alert Franklin the nearer he gets to a hidden package or weapon pickup.
PROC PLAY_ALERT_BARK_DIALOGUE()
IF (GET_GAME_TIMER() - iBarkTimer) > iDelayUntilNextBark
FLOAT f_distance = GET_DISTANCE_BETWEEN_ENTITY_AND_COORD(chopID, vChopSitNearClosestPickup)
IF f_distance < 10
PLAY_CHOP_SOUND("BARK_SEQ")
ELSE
PLAY_CHOP_SOUND("BARK")
ENDIF
//#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Making Chop bark when sniffing out hidden package or weapon pickup") #ENDIF
iBarkTimer = GET_GAME_TIMER()
IF f_distance < 20.0
iDelayUntilNextBark = GET_RANDOM_INT_IN_RANGE(4000, 5000)
ELIF f_distance < 50.0
iDelayUntilNextBark = GET_RANDOM_INT_IN_RANGE(5000, 6000)
ELSE
iDelayUntilNextBark = GET_RANDOM_INT_IN_RANGE(8000, 12000)
ENDIF
//#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: iDelayUntilNextBark = ", iDelayUntilNextBark) #ENDIF
IF NOT IS_HELP_MESSAGE_BEING_DISPLAYED()
AND NOT IS_BIT_SET(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_BARK_NEAR_PICKUPS))
AND NOT IS_PLAYER_UNARMED_AND_AIMING_AT_CHOP()
AND NOT IS_PLAYER_SWITCH_IN_PROGRESS()
SET_BIT(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_BARK_NEAR_PICKUPS))
PRINT_HELP("CHOP_H_HUNT") // "Chop will bark more often when he nears hidden packages and pickups."
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Displaying help text CHOP_H_HUNT") #ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Plays dialogue for Franklin.
PROC PLAY_FRANKLIN_DIALOGUE(STRING s_string_to_play, enumSubtitlesState display_subs = DISPLAY_SUBTITLES)
ADD_PED_FOR_DIALOGUE(sConversation, 1, PLAYER_PED_ID(), "FRANKLIN")
ADD_NON_CRITICAL_STANDARD_CONVERSATION_TO_BUFFER(sConversation, "CHOP_AU", s_string_to_play, CONV_PRIORITY_AMBIENT_MEDIUM, display_subs)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Playing dialogue ", s_string_to_play) #ENDIF
ENDPROC
/// PURPOSE:
/// Plays Franklin's open/close car door anim.
PROC PLAY_FRANKLIN_DOOR_ANIM()
TASK_PLAY_ANIM(PLAYER_PED_ID(), "misschop_vehicleenter_exit", stringFranklinInVehicleAnim, NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_SECONDARY|AF_NOT_INTERRUPTABLE) // Needs to be secondary see B*1467740
iFranklinVehicleDoorAnimTimer = GET_GAME_TIMER()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Playing Franklin open door anim") #ENDIF
ENDPROC
/// PURPOSE:
/// Returns true when Franklin's open/close door anim reaches the point at which the door should be opened/closed.
FUNC BOOL SHOULD_DOOR_OPEN_OR_CLOSE_NOW()
IF IS_ENTITY_ALIVE(vehChop)
IF (GET_GAME_TIMER() - iFranklinVehicleDoorAnimTimer) > 500
OR IS_VEHICLE_DOOR_DAMAGED(vehChop, doorChop)
OR SHOULD_CHOP_WARP_INTO_THIS_VEHICLE_MODEL()
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Make Chop go to the passenger door of the player's car.
PROC SEND_CHOP_TO_PASSENGER_DOOR()
IF NOT IsPedPerformingTask(chopID, SCRIPT_TASK_FOLLOW_NAV_MESH_TO_COORD)
AND GET_DISTANCE_BETWEEN_ENTITIES(PLAYER_PED_ID(), chopID) > 1
TASK_FOLLOW_NAV_MESH_TO_COORD(chopID, GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID(), TRUE),<<2,0,0>>), PEDMOVE_RUN)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Sending Chop close to passenger's door") #ENDIF
ENDIF
ENDPROC
PROC START_CHOP_ENTER_VEHICLE_CAM(BOOL b_start, BOOL b_set_cam_behind_player = TRUE)
IF b_start = TRUE
IF NOT DOES_CAM_EXIST(camChopEnterVehicle)
AND IS_ENTITY_ALIVE(vehChop)
VECTOR v_cam_pos, v_lookat_pos_offset
INT i_random = GET_RANDOM_INT_IN_RANGE(0, 4)
#IF IS_DEBUG_BUILD
IF iOverrideChopEnterVehicleCamera > -1
i_random = iOverrideChopEnterVehicleCamera
ENDIF
#ENDIF
IF i_random = 0
v_cam_pos = << 1.11573, -1.40338, 0.555789 >>
v_lookat_pos_offset = << 0.5, 0, 0.3 >>
ELIF i_random = 1
v_cam_pos = << -1.15872, 1.75252, 0.761228 >>
v_lookat_pos_offset = << 0, 0, 0.3 >>
ELIF i_random = 2
v_cam_pos = << -1.89975, 0.339287, 0.661881 >>
v_lookat_pos_offset = << 0, 0, 0.3 >>
ELSE
v_cam_pos = << 1.38134, -0.1248, 0.580783 >>
v_lookat_pos_offset = << 0, 0, 0.3 >>
ENDIF
camChopEnterVehicle = CREATE_CAM("DEFAULT_SCRIPTED_CAMERA", TRUE)
VECTOR v_seat_bone_pos = GET_WORLD_POSITION_OF_ENTITY_BONE(vehChop, GET_ENTITY_BONE_INDEX_BY_NAME(vehChop, "seat_pside_f"))
VECTOR v_seat_bone_pos_offset = GET_OFFSET_FROM_ENTITY_GIVEN_WORLD_COORDS(vehChop, v_seat_bone_pos)
VECTOR v_cam_from_car = v_cam_pos + v_seat_bone_pos_offset
ATTACH_CAM_TO_ENTITY(camChopEnterVehicle, vehChop, v_cam_from_car)
POINT_CAM_AT_ENTITY(camChopEnterVehicle, vehChop, v_seat_bone_pos_offset+v_lookat_pos_offset)
SET_CAM_FOV(camChopEnterVehicle, 50.0)
SHAKE_CAM(camChopEnterVehicle, "HAND_SHAKE", 0.3)
SET_CAM_DOF_PLANES(camChopEnterVehicle, 0.0, 0.0, 100000.0, 100000.0) // Requested in B*1861426
RENDER_SCRIPT_CAMS(TRUE, FALSE)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Created Chop enter vehicle camera at position ", i_random) #ENDIF
ENDIF
ELSE
IF DOES_CAM_EXIST(camChopEnterVehicle)
IF b_set_cam_behind_player = TRUE
SET_GAMEPLAY_CAM_RELATIVE_PITCH(0.0)
SET_GAMEPLAY_CAM_RELATIVE_HEADING(0.0)
ENDIF
RENDER_SCRIPT_CAMS(FALSE, FALSE)
DESTROY_ALL_CAMS()
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// In case Chop can't get out of a vehicle using anims.
PROC WARP_CHOP_OUT_OF_VEHICLE(VECTOR v_offset, BOOL b_open_door_for_chop)
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
AND IS_ENTITY_ALIVE(chopID)
IF IS_ENTITY_ALIVE(vehChop)
IF b_open_door_for_chop = TRUE
AND NOT IS_VEHICLE_DOOR_DAMAGED(vehChop, doorChop)
AND GET_VEHICLE_DOOR_ANGLE_RATIO(vehChop, SC_DOOR_FRONT_RIGHT) < 0.9
SET_VEHICLE_DOOR_OPEN(vehChop, doorChop)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Opening door for Chop to warp out") #ENDIF
ENDIF
IF IS_SYNCHRONIZED_SCENE_RUNNING(iSynchSceneChop)
DETACH_SYNCHRONIZED_SCENE(iSynchSceneChop)
ENDIF
CLEAR_PED_TASKS_IMMEDIATELY(chopID)
SAFE_TELEPORT_ENTITY(chopID, GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(vehChop, v_offset), GET_ENTITY_HEADING(chopID), TRUE, FALSE)
FORCE_PED_AI_AND_ANIMATION_UPDATE(chopID)
START_CHOP_ENTER_VEHICLE_CAM(FALSE, FALSE) // B*1552775 Ensure cam is stopped if player is dragged out
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Warped Chop to offset from vehicle ", v_offset) #ENDIF
chopInVehicleStatus = CHOP_VEHICLE_STATUS_CLEANUP
ELSE
IF IS_SYNCHRONIZED_SCENE_RUNNING(iSynchSceneChop)
DETACH_SYNCHRONIZED_SCENE(iSynchSceneChop)
ENDIF
CLEAR_PED_TASKS_IMMEDIATELY(chopID)
SAFE_TELEPORT_ENTITY(chopID, GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(PLAYER_PED_ID(), <<v_offset.x+1,v_offset.y,v_offset.z>>), GET_ENTITY_HEADING(chopID), TRUE, FALSE)
FORCE_PED_AI_AND_ANIMATION_UPDATE(chopID)
START_CHOP_ENTER_VEHICLE_CAM(FALSE, FALSE) // B*1552775 Ensure cam is stopped if player is dragged out
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Warped Chop to offset from player ", <<v_offset.x+1,v_offset.y,v_offset.z>>) #ENDIF
chopInVehicleStatus = CHOP_VEHICLE_STATUS_CLEANUP
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Checks if the vehicle Chop is sat in is submerged too much in water. For B*1266256.
FUNC BOOL IS_CHOP_VEHICLE_SUBMERGED_IN_WATER()
IF IS_ENTITY_ALIVE(vehChop)
IF IS_ENTITY_IN_WATER(vehChop)
FLOAT f_water_height
VECTOR v_chop_vehicle = GET_ENTITY_COORDS(vehChop)
IF iChopVehicleInWaterTimer = -1
iChopVehicleInWaterTimer = GET_GAME_TIMER()
ENDIF
IF GET_WATER_HEIGHT(v_chop_vehicle, f_water_height)
IF f_water_height-v_chop_vehicle.z > 1.0 // Chop's vehicle is deep in water
//#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: v_chop_vehicle.z = ", v_chop_vehicle.z, " f_water_height = ", f_water_height, " Difference = ", f_water_height-v_chop_vehicle.z) #ENDIF
RETURN TRUE
ELIF (GET_GAME_TIMER() - iChopVehicleInWaterTimer) > 3000 // Chop's vehicle is in shallow water so make him whine occasionally
PLAY_CHOP_SOUND("AGITATED")
iChopVehicleInWaterTimer = GET_GAME_TIMER()
ENDIF
ENDIF
ELSE
iChopVehicleInWaterTimer = -1
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Gives a ball weapon to Franklin.
PROC GIVE_BALL_TO_PLAYER(BOOL b_force_into_hand = FALSE, BOOL bPlayPickupSound = FALSE, BOOL b_equip = FALSE)
IF NOT HAS_PED_GOT_WEAPON(PLAYER_PED_ID(), WEAPONTYPE_BALL)
OR GET_AMMO_IN_PED_WEAPON(PLAYER_PED_ID(), WEAPONTYPE_BALL) = 0
GIVE_WEAPON_TO_PED(PLAYER_PED_ID(), WEAPONTYPE_BALL, 1, b_force_into_hand, b_equip)
HUD_SET_WEAPON_WHEEL_TOP_SLOT(WEAPONTYPE_BALL) // B*1258230
IF bPlayPickupSound = TRUE // B*1701733 - Play sfx when Franklin picks up ball
PLAY_SOUND_FRONTEND(iPickupBallSound, "PICKUP_WEAPON_BALL", "HUD_FRONTEND_WEAPONS_PICKUPS_SOUNDSET")
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Playing ball pickup sound") #ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Warps Chop into the front passenger seat.
PROC WARP_CHOP_INTO_PLAYER_VEHICLE(BOOL b_reset_cam_behind_vehicle)
IF b_reset_cam_behind_vehicle = TRUE
SET_GAMEPLAY_CAM_RELATIVE_PITCH(0.0)
SET_GAMEPLAY_CAM_RELATIVE_HEADING(0.0)
ENDIF
SAFE_REMOVE_BLIP(blipChop)
SET_ENTITY_INVINCIBLE(chopID, TRUE) // B*1337712
SET_PED_INTO_VEHICLE(chopID, vehChop, VS_FRONT_RIGHT)
TASK_PLAY_ANIM(chopID, stringChopInVehicleAnimDict, "sit", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_LOOPING)
FORCE_PED_AI_AND_ANIMATION_UPDATE(chopID)
ENDPROC
/// PURPOSE:
/// Returns true when one of the conditions for Chop warping into the vehicle returns true.
FUNC BOOL SHOULD_CHOP_GET_INTO_VEHICLE_NOW()
IF IS_ENTITY_ALIVE(vehChop)
AND IS_ENTITY_ALIVE(chopID)
IF iChopWarpToVehicleTimeLimit = -1
FLOAT f_dist = GET_DISTANCE_BETWEEN_ENTITIES(vehChop, chopID)
iChopWarpToVehicleTimeLimit = FLOOR(f_dist * 1000) // B*1492723 - Increase warp failsafe time limit depending on distance
IF iChopWarpToVehicleTimeLimit < 5000
iChopWarpToVehicleTimeLimit = 5000
ENDIF
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: iChopWarpToVehicleTimeLimit = ", iChopWarpToVehicleTimeLimit) #ENDIF
ENDIF
IF GET_DISTANCE_BETWEEN_ENTITY_AND_COORD(chopID, GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(vehChop,<<2,0,0>>), FALSE) < 1
OR IS_CONTROL_PRESSED(PLAYER_CONTROL, INPUT_VEH_ACCELERATE)
OR IS_CONTROL_PRESSED(PLAYER_CONTROL, INPUT_VEH_BRAKE)
OR (iChopWarpToVehicleTimer > -1 AND (GET_GAME_TIMER() - iChopWarpToVehicleTimer) > iChopWarpToVehicleTimeLimit) // Safety timer
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
PROC SET_FRANKLIN_SIDE_OF_VEHICLE()
VECTOR char_pos, right_pos, left_pos, right_vec, left_vec
CONST_INT LEFT_PASSENGER 1
CONST_INT RIGHT_PASSENGER 2
char_pos = GET_ENTITY_COORDS(PLAYER_PED_ID())
right_pos = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID(), TRUE), <<2.0, 0.0, 0.0>>)
right_vec = right_pos - char_pos
left_pos = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID(), TRUE), <<-2.0, 0.0, 0.0>>)
left_vec = left_pos - char_pos
IF VMAG(right_vec) < VMAG(left_vec) // Choose the smaller of the two
bPlayerEnteringRightSide = TRUE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player entering vehicle from right hand side") #ENDIF
ELSE
bPlayerEnteringRightSide = FALSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player entering vehicle from left hand side") #ENDIF
ENDIF
ENDPROC
PROC PREVENT_PLAYER_ENTERING_EXITING_VEHICLE()
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_VEH_EXIT)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_ENTER)
ENDPROC
/// PURPOSE:
/// Handles what Chop does if Franklin gets into a vehicle.
PROC MANAGE_CHOP_IN_PLAYER_VEHICLE()
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
IF chopInVehicleStatus > CHOP_VEHICLE_STATUS_FRANKLIN_ENTERING_VEHICLE
AND chopInVehicleStatus < CHOP_VEHICLE_STATUS_CHOP_SAT_IN_CAR
SET_PED_RESET_FLAG(PLAYER_PED_ID(), PRF_DisableInVehicleActions, TRUE) // B*1431816 Prevent hotwiring as we have to play anims to open the door for Chop
ENDIF
IF bPlayerEnteringRightSide = TRUE
AND chopInVehicleStatus = CHOP_VEHICLE_STATUS_FRANKLIN_ENTERING_VEHICLE
SET_PED_RESET_FLAG(PLAYER_PED_ID(), PRF_DontCloseVehicleDoor, TRUE) // B*1412946 Prevent Franklin closing the passenger door only to have to reopen it
ENDIF
ENDIF
#IF IS_DEBUG_BUILD
IF bOutputCameraToTTY = TRUE
AND IS_ENTITY_ALIVE(vehChop)
// Work out the position of the camera relative to the front passenger seat bone of vehChop
VECTOR v_cam_pos = GET_FINAL_RENDERED_CAM_COORD()
VECTOR v_cam_pos_offset = GET_OFFSET_FROM_ENTITY_GIVEN_WORLD_COORDS(vehChop, v_cam_pos)
VECTOR v_seat_bone_pos = GET_WORLD_POSITION_OF_ENTITY_BONE(vehChop, GET_ENTITY_BONE_INDEX_BY_NAME(vehChop, "seat_pside_f"))
VECTOR v_seat_bone_pos_offset = GET_OFFSET_FROM_ENTITY_GIVEN_WORLD_COORDS(vehChop, v_seat_bone_pos)
VECTOR v_cam_from_bone = v_cam_pos_offset - v_seat_bone_pos_offset
// Work out the rotation of the camera relative to vehChop
VECTOR v_car_rot = GET_ENTITY_ROTATION(vehChop)
VECTOR v_cam_rot = GET_FINAL_RENDERED_CAM_ROT()
VECTOR v_cam_rot_minus_car = v_car_rot - v_cam_rot
// Output to TTY
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: cam pos offset relative to front passenger seat bone = ", v_cam_from_bone, " cam rot relative to car = ", v_cam_rot_minus_car) #ENDIF
ENDIF
#ENDIF
SWITCH chopInVehicleStatus
CASE CHOP_VEHICLE_STATUS_FRANKLIN_NOT_IN_VEHICLE
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID(), TRUE)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player started entering a vehicle") #ENDIF
iDoingChopInVehicleCollisionAnim = GET_GAME_TIMER()
iChopWarpToVehicleTimer = -1
iChopWarpToVehicleTimeLimit = -1
iBarkInVehicleTimer = GET_GAME_TIMER()
SET_FRANKLIN_SIDE_OF_VEHICLE()
SET_PED_CONFIG_FLAG(PLAYER_PED_ID(), PCF_WillFlyThroughWindscreen, FALSE) // B*1434346
bPlayerHasBeenInCarWithChop = TRUE
chopInVehicleStatus = CHOP_VEHICLE_STATUS_FRANKLIN_ENTERING_VEHICLE
ELSE // Player isn't in a vehicle or in the process of doing so - generally when Chop is on a walk it'll mostly be looping here
IF vehLayoutHash != LAYOUT_INVALID
vehLayoutHash = LAYOUT_INVALID
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Reset vehLayoutHash to LAYOUT_INVALID") #ENDIF
ENDIF
bPlayedVehicleDialogue = FALSE
iDelayUntilNextPickupScan = -1
IF IS_PLAYER_IN_RESTRICTED_INTERIOR()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player entered an interior so stop Chop following") #ENDIF
GIVE_CHOP_TASK(CHOP_TASK_RETURN)
ENDIF
ENDIF
BREAK
CASE CHOP_VEHICLE_STATUS_FRANKLIN_ENTERING_VEHICLE
IF NOT IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID(), TRUE)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player exited vehicle before Chop could enter it") #ENDIF
chopInVehicleStatus = CHOP_VEHICLE_STATUS_CLEANUP
ELIF IS_PED_SITTING_IN_ANY_VEHICLE(PLAYER_PED_ID())
IF IS_PLAYER_IN_SUITABLE_VEHICLE_FOR_CHOP_TO_ENTER()
IF GET_PED_IN_VEHICLE_SEAT(GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID()), VS_DRIVER) != PLAYER_PED_ID()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player is in a taxi as a passenger") #ENDIF
GIVE_CHOP_TASK(CHOP_TASK_RETURN)
ELSE
IF NOT IS_ENTITY_ALIVE(vehChop)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player sitting in a vehicle suitable for Chop to enter") #ENDIF
vehChop = GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID()) // Grab vehicle handle in case player exits it before Chop
SET_LAYOUT_HASH_FOR_CHOP_VEHICLE()
SET_CHOPS_DOOR_FOR_CHOP_VEHICLE()
ENDIF
REQUEST_ANIM_DICT(stringChopInVehicleAnimDict)
IF HAS_ANIM_DICT_LOADED(stringChopInVehicleAnimDict)
AND IS_ENTITY_ALIVE(vehChop)
IF IS_PED_IN_GROUP(chopID)
REMOVE_PED_FROM_GROUP(chopID)
ENDIF
IF SHOULD_CHOP_WARP_INTO_THIS_VEHICLE_MODEL()
IF iChopWarpToVehicleTimer = -1
iChopWarpToVehicleTimer = GET_GAME_TIMER()
ENDIF
IF SHOULD_CHOP_GET_INTO_VEHICLE_NOW()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: SHOULD_CHOP_WARP_INTO_THIS_VEHICLE_MODEL returned true so warp Chop in") #ENDIF
WARP_CHOP_INTO_PLAYER_VEHICLE(TRUE)
chopInVehicleStatus = CHOP_VEHICLE_STATUS_FRANKLIN_PLAYING_ANIM_CLOSING_DOOR
ELSE
SEND_CHOP_TO_PASSENGER_DOOR()
ENDIF
ELIF IS_ENTRY_POINT_FOR_SEAT_CLEAR(PLAYER_PED_ID(), vehChop, VS_FRONT_RIGHT)
IF IS_VEHICLE_DOOR_DAMAGED(vehChop, doorChop)
OR GET_VEHICLE_DOOR_ANGLE_RATIO(vehChop, SC_DOOR_FRONT_RIGHT) > 0.9
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Front right door is not present or is already open") #ENDIF
chopInVehicleStatus = CHOP_VEHICLE_STATUS_CHOP_ENTER_VEHICLE
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Franklin needs to open the front right door for Chop") #ENDIF
chopInVehicleStatus = CHOP_VEHICLE_STATUS_FRANKLIN_PLAYING_ANIM_OPENING_DOOR
ENDIF
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Seat entry point isn't clear so warp Chop in") #ENDIF
WARP_CHOP_INTO_PLAYER_VEHICLE(TRUE)
chopInVehicleStatus = CHOP_VEHICLE_STATUS_FRANKLIN_PLAYING_ANIM_CLOSING_DOOR
ENDIF
ENDIF
ENDIF
ELIF IS_PLAYER_IN_SUITABLE_VEHICLE_FOR_CHOP_TO_FOLLOW()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player sitting in a vehicle suitable for Chop to follow") #ENDIF
chopInVehicleStatus = CHOP_VEHICLE_STATUS_CHOP_FOLLOWING_BIKE
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player isn't in suitable vehicle for Chop to get into or follow") #ENDIF
IF NOT IS_HELP_MESSAGE_BEING_DISPLAYED()
AND NOT IS_BIT_SET(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_VEHICLE_NOT_SUITABLE))
AND NOT IS_PLAYER_UNARMED_AND_AIMING_AT_CHOP()
AND NOT IS_PLAYER_SWITCH_IN_PROGRESS()
SET_BIT(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_VEHICLE_NOT_SUITABLE))
PRINT_HELP("CHOP_H_NOVEH") // "Chop can only get into suitable cars with an empty front passenger seat."
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Displaying help text CHOP_H_NOVEH") #ENDIF
ENDIF
GIVE_CHOP_TASK(CHOP_TASK_RETURN)
ENDIF
ELSE
IF IS_PLAYER_IN_SUITABLE_VEHICLE_FOR_CHOP_TO_ENTER()
REQUEST_ANIM_DICT("misschop_vehicleenter_exit") // Preload
SEND_CHOP_TO_PASSENGER_DOOR()
ENDIF
IF bPlayedVehicleDialogue = FALSE // See B*1040633 - need to start the dialogue when the player starts to enter, not when he's finished
AND IS_VEHICLE_DRIVEABLE(GET_VEHICLE_PED_IS_ENTERING(PLAYER_PED_ID()))
SET_PED_CONFIG_FLAG(chopID, PCF_UseKinematicModeWhenStationary, FALSE) // B*1402589 Turn kinematic off
IF IS_THIS_MODEL_A_CAR(GET_ENTITY_MODEL(GET_VEHICLE_PED_IS_ENTERING(PLAYER_PED_ID())))
AND NOT IS_PLAYER_IN_CAR_CHOP_CANT_GET_INTO()
IF GET_ENTITY_MODEL(GET_VEHICLE_PED_IS_ENTERING(PLAYER_PED_ID())) = TAXI
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player getting into a taxi so don't play 'get in' dialogue in case he's getting in as a passenger") #ENDIF
ELSE
IF GET_VEHICLE_MAX_NUMBER_OF_PASSENGERS(GET_VEHICLE_PED_IS_ENTERING(PLAYER_PED_ID())) >= 1
AND IS_VEHICLE_SEAT_FREE(GET_VEHICLE_PED_IS_ENTERING(PLAYER_PED_ID()), VS_FRONT_RIGHT) // B*1550935 Don't play if front passenger seat is occupied because we'll soon be sending Chop home
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED() // B*1553941 Don't allow this to be queued if the is already dialogue going on
PLAY_FRANKLIN_DIALOGUE("CHOP_RIDE")
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: IS_ANY_CONVERSATION_ONGOING_OR_QUEUED returned false so not playing CHOP_RIDE dialogue") #ENDIF
ENDIF
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: VS_FRONT_RIGHT isn't free so not playing CHOP_RIDE dialogue") #ENDIF
ENDIF
ENDIF
ELSE
IF IS_THIS_MODEL_A_BIKE(GET_ENTITY_MODEL(GET_VEHICLE_PED_IS_ENTERING(PLAYER_PED_ID())))
OR IS_PLAYER_IN_CAR_CHOP_CANT_GET_INTO()
PLAY_FRANKLIN_DIALOGUE("CHOP_FOLLOW")
ENDIF
ENDIF
bPlayedVehicleDialogue = TRUE // Set to true, even if player is getting into a non-car/bike, so it won't check again (flag gets reset when player gets out)
ENDIF
ENDIF
BREAK
CASE CHOP_VEHICLE_STATUS_FRANKLIN_PLAYING_ANIM_OPENING_DOOR
PREVENT_PLAYER_ENTERING_EXITING_VEHICLE()
IF NOT IS_PED_SITTING_IN_ANY_VEHICLE(PLAYER_PED_ID())
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player exited vehicle (during CHOP_VEHICLE_STATUS_FRANKLIN_PLAYING_ANIM_OPENING_DOOR) before Chop could enter it") #ENDIF
chopInVehicleStatus = CHOP_VEHICLE_STATUS_CLEANUP
ELSE
REQUEST_ANIM_DICT("misschop_vehicleenter_exit")
IF HAS_ANIM_DICT_LOADED("misschop_vehicleenter_exit")
PLAY_FRANKLIN_DOOR_ANIM()
chopInVehicleStatus = CHOP_VEHICLE_STATUS_FRANKLIN_OPENING_DOOR
ENDIF
ENDIF
BREAK
CASE CHOP_VEHICLE_STATUS_FRANKLIN_OPENING_DOOR
PREVENT_PLAYER_ENTERING_EXITING_VEHICLE()
IF NOT IS_PED_SITTING_IN_ANY_VEHICLE(PLAYER_PED_ID())
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player exited vehicle (during CHOP_VEHICLE_STATUS_FRANKLIN_OPENING_DOOR) before Chop could enter it") #ENDIF
chopInVehicleStatus = CHOP_VEHICLE_STATUS_CLEANUP
ELSE
IF IS_ENTITY_ALIVE(vehChop)
AND SHOULD_DOOR_OPEN_OR_CLOSE_NOW()
IF NOT IS_VEHICLE_DOOR_DAMAGED(vehChop, doorChop)
SET_VEHICLE_DOOR_OPEN(vehChop, doorChop)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Opening door for Chop to get in") #ENDIF
ENDIF
i_DoorOpenTimer = GET_GAME_TIMER()
iChopWarpToVehicleTimer = GET_GAME_TIMER()
chopInVehicleStatus = CHOP_VEHICLE_STATUS_CHOP_ENTER_VEHICLE
ENDIF
ENDIF
BREAK
CASE CHOP_VEHICLE_STATUS_CHOP_ENTER_VEHICLE
IF NOT IS_PED_SITTING_IN_ANY_VEHICLE(PLAYER_PED_ID())
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player exited vehicle (during CHOP_VEHICLE_STATUS_CHOP_ENTER_VEHICLE) before Chop could enter it") #ENDIF
chopInVehicleStatus = CHOP_VEHICLE_STATUS_CLEANUP
ELSE
REQUEST_ANIM_DICT(stringChopInVehicleAnimDict)
IF IS_ENTITY_ALIVE(vehChop)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Passenger Door Open Angle: ", GET_VEHICLE_DOOR_ANGLE_RATIO(vehChop, doorChop)) #ENDIF
// url:bugstar:2336322 - Make sure door is open for chop to jump in
IF NOT IS_VEHICLE_DOOR_DAMAGED(vehChop, doorChop)
AND GET_VEHICLE_DOOR_ANGLE_RATIO(vehChop, doorChop) < 0.95
SET_VEHICLE_DOOR_OPEN(vehChop, doorChop)
i_DoorOpenTimer = GET_GAME_TIMER()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Door not fully open - Opening door") #ENDIF
ENDIF
IF SHOULD_CHOP_GET_INTO_VEHICLE_NOW()
AND GET_GAME_TIMER() >= i_DoorOpenTimer + 300 // url:bugstar:2336322
IF NOT IsPedPerformingTask(PLAYER_PED_ID(), SCRIPT_TASK_PLAY_ANIM)
AND HAS_ANIM_DICT_LOADED(stringChopInVehicleAnimDict)
PREVENT_PLAYER_ENTERING_EXITING_VEHICLE()
START_CHOP_ENTER_VEHICLE_CAM(TRUE)
CLEAR_PED_TASKS_IMMEDIATELY(chopID)
iSynchSceneChop = CREATE_SYNCHRONIZED_SCENE(<<0,0,0>>,<<0,0,0>>)
ATTACH_SYNCHRONIZED_SCENE_TO_ENTITY(iSynchSceneChop, vehChop, GET_ENTITY_BONE_INDEX_BY_NAME(vehChop, "seat_pside_f"))
TASK_SYNCHRONIZED_SCENE(chopID, iSynchSceneChop, stringChopInVehicleAnimDict, "get_in", INSTANT_BLEND_IN, NORMAL_BLEND_OUT, SYNCED_SCENE_DONT_INTERRUPT)
FORCE_PED_AI_AND_ANIMATION_UPDATE(chopID)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop getting into vehicle") #ENDIF
chopInVehicleStatus = CHOP_VEHICLE_STATUS_CHOP_ENTERING_VEHICLE
ENDIF
ELSE
SEND_CHOP_TO_PASSENGER_DOOR()
ENDIF
ENDIF
ENDIF
BREAK
CASE CHOP_VEHICLE_STATUS_CHOP_ENTERING_VEHICLE
PREVENT_PLAYER_ENTERING_EXITING_VEHICLE()
IF NOT IS_PED_SITTING_IN_ANY_VEHICLE(PLAYER_PED_ID())
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player exited vehicle (during CHOP_VEHICLE_STATUS_CHOP_ENTERING_VEHICLE) before Chop had finished getting inside so he needs to exit") #ENDIF
WARP_CHOP_OUT_OF_VEHICLE(<<2,0,0>>, TRUE)
ELSE
IF DOES_CAM_EXIST(camChopEnterVehicle)
SET_USE_HI_DOF() // Requested in B*1861426
ENDIF
REQUEST_ANIM_DICT(stringChopInVehicleAnimDict)
IF HAS_ANIM_DICT_LOADED(stringChopInVehicleAnimDict)
AND IS_SYNCHRONIZED_SCENE_RUNNING(iSynchSceneChop)
AND GET_SYNCHRONIZED_SCENE_PHASE(iSynchSceneChop) > 0.99
AND IS_ENTITY_ALIVE(vehChop)
START_CHOP_ENTER_VEHICLE_CAM(FALSE)
WARP_CHOP_INTO_PLAYER_VEHICLE(FALSE)
chopInVehicleStatus = CHOP_VEHICLE_STATUS_FRANKLIN_PLAYING_ANIM_CLOSING_DOOR
ENDIF
ENDIF
BREAK
CASE CHOP_VEHICLE_STATUS_FRANKLIN_PLAYING_ANIM_CLOSING_DOOR
PREVENT_PLAYER_ENTERING_EXITING_VEHICLE()
IF NOT IS_PED_SITTING_IN_ANY_VEHICLE(PLAYER_PED_ID())
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player exited vehicle (during CHOP_VEHICLE_STATUS_FRANKLIN_PLAYING_ANIM_CLOSING_DOOR) before Chop had finished getting inside so he needs to exit") #ENDIF
WARP_CHOP_OUT_OF_VEHICLE(<<2,0,0>>, TRUE)
ELSE
REQUEST_ANIM_DICT("misschop_vehicleenter_exit")
IF HAS_ANIM_DICT_LOADED("misschop_vehicleenter_exit")
AND IS_ENTITY_ALIVE(vehChop)
IF NOT IS_VEHICLE_DOOR_DAMAGED(vehChop, doorChop)
AND GET_VEHICLE_DOOR_ANGLE_RATIO(vehChop, SC_DOOR_FRONT_RIGHT) > 0.1
AND NOT SHOULD_CHOP_WARP_INTO_THIS_VEHICLE_MODEL()
PLAY_FRANKLIN_DOOR_ANIM()
ENDIF
chopInVehicleStatus = CHOP_VEHICLE_STATUS_FRANKLIN_CLOSING_DOOR
ENDIF
ENDIF
BREAK
CASE CHOP_VEHICLE_STATUS_FRANKLIN_CLOSING_DOOR
PREVENT_PLAYER_ENTERING_EXITING_VEHICLE()
IF NOT IS_PED_SITTING_IN_ANY_VEHICLE(PLAYER_PED_ID())
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player exited vehicle (during CHOP_VEHICLE_STATUS_FRANKLIN_CLOSING_DOOR) before Franklin had closed door so Chop needs to exit") #ENDIF
WARP_CHOP_OUT_OF_VEHICLE(<<2,0,0>>, TRUE)
ELSE
IF IS_ENTITY_ALIVE(vehChop)
AND SHOULD_DOOR_OPEN_OR_CLOSE_NOW()
IF NOT IS_VEHICLE_DOOR_DAMAGED(vehChop, doorChop)
SET_VEHICLE_DOOR_SHUT(vehChop, doorChop, FALSE)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Closing door for Chop") #ENDIF
ENDIF
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop is now sat in car") #ENDIF
chopInVehicleStatus = CHOP_VEHICLE_STATUS_CHOP_SAT_IN_CAR
ENDIF
ENDIF
BREAK
CASE CHOP_VEHICLE_STATUS_CHOP_SAT_IN_CAR
PREVENT_PLAYER_ENTERING_EXITING_VEHICLE()
SAFE_REMOVE_BLIP(blipChop)
CLEANUP_BALL(TRUE)
IF iRadioStationHowlTimer = -1
iRadioStationHowlTimer = GET_GAME_TIMER()
iDelayUntilNextHowl = 7000
ENDIF
IF IS_ENTITY_ALIVE(vehChop)
IF IS_CHOP_VEHICLE_SUBMERGED_IN_WATER()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: vehChop is in water, so warping Chop out") #ENDIF
WARP_CHOP_OUT_OF_VEHICLE(<<2,0,0>>, TRUE)
ELIF IS_ENTITY_ON_FIRE(vehChop)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: vehChop is on fire, so warping Chop out") #ENDIF
WARP_CHOP_OUT_OF_VEHICLE(<<2,0,0>>, TRUE)
ELIF NOT IS_PED_SITTING_IN_ANY_VEHICLE(PLAYER_PED_ID())
IF GET_ENTITY_SPEED(vehChop) < 5
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player dived out or was dragged out, vehChop speed is less than 5 so make Chop get out") #ENDIF
IF NOT IS_VEHICLE_DOOR_DAMAGED(vehChop, doorChop)
SET_VEHICLE_DOOR_OPEN(vehChop, doorChop)
ENDIF
chopInVehicleStatus = CHOP_VEHICLE_STATUS_CHOP_EXIT_VEHICLE
//ELSE commented out due to B*1469648
//#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player dived out or was dragged out, vehChop speed is greater than 5 so halting vehChop") #ENDIF
//BRING_VEHICLE_TO_HALT(vehChop, 30, 1) // B*1413372 Halt car before Chop warps out
ENDIF
ELIF IS_DISABLED_CONTROL_JUST_PRESSED(FRONTEND_CONTROL, INPUT_VEH_EXIT)
IF IS_ENTITY_UPSIDEDOWN(vehChop)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player vehicle upside down, so warping Chop out") #ENDIF
WARP_CHOP_OUT_OF_VEHICLE(<<2,0,0>>, TRUE)
ELIF SHOULD_CHOP_WARP_INTO_THIS_VEHICLE_MODEL()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: SHOULD_CHOP_WARP_INTO_THIS_VEHICLE_MODEL returned true so waiting until player exited vehicle to disguise warp before warping Chop out") #ENDIF
chopInVehicleStatus = CHOP_VEHICLE_STATUS_WAIT_UNTIL_PLAYER_EXITED
ELIF IS_ENTRY_POINT_FOR_SEAT_CLEAR(PLAYER_PED_ID(), vehChop, VS_FRONT_RIGHT)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player wants to exit vehicle, VS_FRONT_RIGHT is clear so getting Chop out using anims") #ENDIF
BRING_VEHICLE_TO_HALT(vehChop, 10.0, 1)
IF IS_VEHICLE_DOOR_DAMAGED(vehChop, doorChop)
chopInVehicleStatus = CHOP_VEHICLE_STATUS_CHOP_EXIT_VEHICLE
ELSE
chopInVehicleStatus = CHOP_VEHICLE_STATUS_FRANKLIN_PLAYING_ANIM_OPENING_DOOR_FOR_EXIT
ENDIF
ELIF IS_ENTRY_POINT_FOR_SEAT_CLEAR(PLAYER_PED_ID(), vehChop, VS_DRIVER)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player wants to exit vehicle, VS_FRONT_RIGHT isn't clear, VS_DRIVER is clear so warping Chop out") #ENDIF
BRING_VEHICLE_TO_HALT(vehChop, 10.0, 1)
WARP_CHOP_OUT_OF_VEHICLE(<<-2,0,0>>, FALSE)
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player wants to exit vehicle, VS_FRONT_RIGHT and VS_DRIVER aren't clear, so warping Chop out behind") #ENDIF
BRING_VEHICLE_TO_HALT(vehChop, 10.0, 1)
WARP_CHOP_OUT_OF_VEHICLE(<<0,-4,0>>, FALSE)
ENDIF
ELSE
PERIODICALLY_CHECK_FOR_NEARBY_PICKUPS()
PLAY_RADIO_HOWL_DIALOGUE()
UPDATE_CHOPS_IN_VEHICLE_ANIMS()
IF NOT IS_HELP_MESSAGE_BEING_DISPLAYED()
AND NOT IS_BIT_SET(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_VEHICLE_IS_SUITABLE))
AND NOT IS_PLAYER_UNARMED_AND_AIMING_AT_CHOP()
AND NOT IS_PLAYER_SWITCH_IN_PROGRESS()
SET_BIT(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_VEHICLE_IS_SUITABLE))
PRINT_HELP("CHOP_H_CAR") // "Chop will follow Franklin into suitable cars with an empty front passenger seat."
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Displaying help text CHOP_H_CAR") #ENDIF
ENDIF
ENDIF
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: IS_ENTITY_ALIVE(vehChop) returned false, so warping Chop out") #ENDIF
WARP_CHOP_OUT_OF_VEHICLE(<<2,0,0>>, TRUE)
ENDIF
BREAK
CASE CHOP_VEHICLE_STATUS_WAIT_UNTIL_PLAYER_EXITED
PREVENT_PLAYER_ENTERING_EXITING_VEHICLE()
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
AND NOT IsPedPerformingTask(PLAYER_PED_ID(), SCRIPT_TASK_LEAVE_ANY_VEHICLE)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Make Franklin exit vehicle that Chop will have to warp out of") #ENDIF
TASK_LEAVE_ANY_VEHICLE(PLAYER_PED_ID())
ENDIF
IF NOT IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID(), TRUE)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player now exited vehicle so warp Chop out") #ENDIF
WARP_CHOP_OUT_OF_VEHICLE(<<2,0,0>>, TRUE)
ENDIF
BREAK
CASE CHOP_VEHICLE_STATUS_FRANKLIN_PLAYING_ANIM_OPENING_DOOR_FOR_EXIT
PREVENT_PLAYER_ENTERING_EXITING_VEHICLE()
IF NOT IS_PED_SITTING_IN_ANY_VEHICLE(PLAYER_PED_ID())
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player exited vehicle (during CHOP_VEHICLE_STATUS_FRANKLIN_PLAYING_ANIM_OPENING_DOOR_FOR_EXIT) before Franklin had opened door to let Chop out") #ENDIF
WARP_CHOP_OUT_OF_VEHICLE(<<2,0,0>>, TRUE)
ELSE
REQUEST_ANIM_DICT("misschop_vehicleenter_exit")
IF HAS_ANIM_DICT_LOADED("misschop_vehicleenter_exit")
PLAY_FRANKLIN_DOOR_ANIM()
chopInVehicleStatus = CHOP_VEHICLE_STATUS_FRANKLIN_OPENING_DOOR_FOR_EXIT
ENDIF
ENDIF
BREAK
CASE CHOP_VEHICLE_STATUS_FRANKLIN_OPENING_DOOR_FOR_EXIT
PREVENT_PLAYER_ENTERING_EXITING_VEHICLE()
IF NOT IS_PED_SITTING_IN_ANY_VEHICLE(PLAYER_PED_ID())
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player exited vehicle (during CHOP_VEHICLE_STATUS_FRANKLIN_OPENING_DOOR_FOR_EXIT) before Franklin had opened door to let Chop out") #ENDIF
WARP_CHOP_OUT_OF_VEHICLE(<<2,0,0>>, TRUE)
ELSE
IF IS_ENTITY_ALIVE(vehChop)
AND SHOULD_DOOR_OPEN_OR_CLOSE_NOW()
IF NOT IS_VEHICLE_DOOR_DAMAGED(vehChop, doorChop)
SET_VEHICLE_DOOR_OPEN(vehChop, doorChop)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Opening door for Chop to get out") #ENDIF
ENDIF
chopInVehicleStatus = CHOP_VEHICLE_STATUS_CHOP_EXIT_VEHICLE
ENDIF
ENDIF
BREAK
CASE CHOP_VEHICLE_STATUS_CHOP_EXIT_VEHICLE
PREVENT_PLAYER_ENTERING_EXITING_VEHICLE()
REQUEST_ANIM_DICT(stringChopInVehicleAnimDict)
IF NOT IsPedPerformingTask(PLAYER_PED_ID(), SCRIPT_TASK_PLAY_ANIM)
AND HAS_ANIM_DICT_LOADED(stringChopInVehicleAnimDict)
AND IS_ENTITY_ALIVE(vehChop)
CLEAR_PED_TASKS_IMMEDIATELY(chopID)
iSynchSceneChop = CREATE_SYNCHRONIZED_SCENE(<<0,0,0>>,<<0,0,0>>)
ATTACH_SYNCHRONIZED_SCENE_TO_ENTITY(iSynchSceneChop, vehChop, GET_ENTITY_BONE_INDEX_BY_NAME(vehChop, "seat_pside_f"))
TASK_SYNCHRONIZED_SCENE(chopID, iSynchSceneChop, stringChopInVehicleAnimDict, "get_out", INSTANT_BLEND_IN, NORMAL_BLEND_OUT, SYNCED_SCENE_TAG_SYNC_OUT|SYNCED_SCENE_ON_ABORT_STOP_SCENE)
FORCE_PED_AI_AND_ANIMATION_UPDATE(chopID)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop getting out of vehicle") #ENDIF
chopInVehicleStatus = CHOP_VEHICLE_STATUS_CHOP_EXITING_VEHICLE
ENDIF
BREAK
CASE CHOP_VEHICLE_STATUS_CHOP_EXITING_VEHICLE
PREVENT_PLAYER_ENTERING_EXITING_VEHICLE()
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
AND (GET_GAME_TIMER() - iFranklinVehicleDoorAnimTimer) > 1500
AND NOT IsPedPerformingTask(PLAYER_PED_ID(), SCRIPT_TASK_LEAVE_ANY_VEHICLE)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Franklin finished opening door for Chop so exit, so now make Franklin exit") #ENDIF
TASK_LEAVE_ANY_VEHICLE(PLAYER_PED_ID())
ENDIF
IF NOT IS_SYNCHRONIZED_SCENE_RUNNING(iSynchSceneChop)
OR (IS_SYNCHRONIZED_SCENE_RUNNING(iSynchSceneChop) AND GET_SYNCHRONIZED_SCENE_PHASE(iSynchSceneChop) > 0.99)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop finished getting out of vehicle") #ENDIF
TASK_GO_STRAIGHT_TO_COORD_RELATIVE_TO_ENTITY(chopID, chopID, <<0, 5, 0>>, PEDMOVEBLENDRATIO_WALK) // B*1550917 Make Chop walk a few steps away to clear the door
chopInVehicleStatus = CHOP_VEHICLE_STATUS_CLEANUP
ENDIF
BREAK
CASE CHOP_VEHICLE_STATUS_CHOP_FOLLOWING_BIKE
IF NOT IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player got off bike") #ENDIF
chopInVehicleStatus = CHOP_VEHICLE_STATUS_CLEANUP
ELSE
PERIODICALLY_CHECK_FOR_NEARBY_PICKUPS()
IF NOT IS_HELP_MESSAGE_BEING_DISPLAYED()
AND NOT IS_BIT_SET(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_FOLLOW_ON_BIKE))
AND NOT IS_PLAYER_UNARMED_AND_AIMING_AT_CHOP()
AND NOT IS_PLAYER_SWITCH_IN_PROGRESS()
SET_BIT(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_FOLLOW_ON_BIKE))
PRINT_HELP("CHOP_H_BIKE") // "Chop will follow if you get on a bike."
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Displaying help text CHOP_H_BIKE") #ENDIF
ENDIF
ENDIF
BREAK
CASE CHOP_VEHICLE_STATUS_CLEANUP
PREVENT_PLAYER_ENTERING_EXITING_VEHICLE()
IF NOT IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Cleaning up Chop vehicle proc") #ENDIF
CREATE_CHOP_BLIP()
GIVE_BALL_TO_PLAYER() // B*1165651 - Ensure player has ball if he threw it while driving
REMOVE_ANIM_DICT("misschop_vehicleenter_exit")
REMOVE_ANIM_DICT(stringChopInVehicleAnimDict)
iChopBehaviourNotMovingTimer = GET_GAME_TIMER() // Reset this so Chop doesn't instantly go have a piss
SET_LATCH_DOOR_ON_LAST_PLAYER_VEHICLE()
SAFE_RELEASE_VEHICLE(vehChop)
vehChop = NULL
iRadioStationHowlTimer = -1
CLEAR_PED_TASKS(chopID)
SET_ENTITY_INVINCIBLE(chopID, FALSE)
SET_PED_CONFIG_FLAG(chopID, PCF_UseKinematicModeWhenStationary, TRUE) // B*1402589 Turn kinematic back on
SET_PED_CONFIG_FLAG(PLAYER_PED_ID(), PCF_WillFlyThroughWindscreen, TRUE) // B*1434346
GIVE_CHOP_TASK(CHOP_TASK_WALK)
ELIF NOT IsPedPerformingTask(PLAYER_PED_ID(), SCRIPT_TASK_LEAVE_ANY_VEHICLE)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Reached CHOP_VEHICLE_STATUS_CLEANUP and Franklin was still in vehicle so making him exit it") #ENDIF
TASK_LEAVE_ANY_VEHICLE(PLAYER_PED_ID())
ENDIF
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Handles Franklin whistling to retrieve Chop if he goes far away.
PROC HANDLE_FRANKLIN_WHISTLING_TO_CHOP()
IF NOT IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID()) // B*1122405 - Prevent conflicts with changing radio stations
AND NOT IS_ENTITY_IN_WATER(PLAYER_PED_ID()) // B*1151615 - Prevent whistling while in water
IF GET_DISTANCE_BETWEEN_PEDS(PLAYER_PED_ID(), chopID) > 10.0
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WHIS")
IF (GET_GAME_TIMER() - iWhistleTimer) > 4000 // Delay before allowing retrigger
AND IS_CONTROL_JUST_PRESSED(FRONTEND_CONTROL, INPUT_DETONATE)
iWhistleTimer = GET_GAME_TIMER()
PLAY_PED_AMBIENT_SPEECH(PLAYER_PED_ID(), "CALL_CHOP", SPEECH_PARAMS_FORCE) // B*1076691 B*1583074
IF IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WHIS")
CLEAR_HELP()
ENDIF
IF eChopTask = CHOP_TASK_FETCH
AND iChopStage > 1
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player whistled to Chop, but Chop in process of retrieving ball so just continue fetch task") #ENDIF
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player whistled to Chop") #ENDIF
CLEANUP_BALL(FALSE) // B*1381598 Ensure ball is dropped if playing fetch
IF eChopTask = CHOP_TASK_WALK
GIVE_CHOP_TASK(CHOP_TASK_WALK, FALSE) // B*1861149 Don't clear tasks if repeat whistling
ELSE
GIVE_CHOP_TASK(CHOP_TASK_WALK)
ENDIF
ENDIF
ENDIF
IF NOT IS_BIT_SET(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_WHISTLE))
AND NOT IS_HELP_MESSAGE_BEING_DISPLAYED()
AND NOT IS_PLAYER_UNARMED_AND_AIMING_AT_CHOP()
AND NOT IS_PLAYER_SWITCH_IN_PROGRESS()
SET_BIT(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_WHISTLE))
PRINT_HELP("CHOP_H_WHIS") // "Press ~INPUT_DETONATE~ to whistle to make Chop return to Franklin."
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Displaying help text CHOP_H_WHIS") #ENDIF
ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Handles Chop whining if he's unhappy.
PROC HANDLE_CHOP_WHINING_IF_UNHAPPY()
IF GET_CHOP_BEHAVIOUR_FROM_APP() = CHOP_BEHAVIOUR_BAD
AND (GET_GAME_TIMER() - iUnhappyTimer) > iUnhappyDelay // Delay before allowing retrigger
iUnhappyTimer = GET_GAME_TIMER()
iUnhappyDelay = GET_RANDOM_INT_IN_RANGE(10000, 15000)
//#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Next iUnhappyDelay = ", iUnhappyDelay) #ENDIF
PLAY_CHOP_SOUND("WHINE")
ENDIF
ENDPROC
/// PURPOSE:
/// Handles Chop snarling if the player is wanted.
PROC HANDLE_CHOP_WHEN_PLAYER_WANTED()
IF GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) > 0
IF iNextWantedWhimperTimer = -1
iNextWantedWhimperTimer = GET_GAME_TIMER()
iDelayUntilNextWantedWhimper = GET_RANDOM_INT_IN_RANGE(4000, 6000)
ENDIF
IF (GET_GAME_TIMER() - iNextWantedWhimperTimer) > iDelayUntilNextWantedWhimper
PLAY_CHOP_SOUND("SNARL")
iNextWantedWhimperTimer = -1
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Handles Chop pissing and shitting when following the player but idle.
PROC HANDLE_CHOP_NOT_MOVING()
IF GET_CHOP_BEHAVIOUR_FROM_APP() != CHOP_BEHAVIOUR_GOOD
AND (GET_GAME_TIMER() - iChopBehaviourNotMovingTimer) > 10000
AND NOT IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
AND chopInVehicleStatus = CHOP_VEHICLE_STATUS_FRANKLIN_NOT_IN_VEHICLE
AND wThrownWeapon != WEAPONTYPE_BALL // Prevent Chop taking a piss/shit when the player wants to play fetch
AND COUNT_PEDS_IN_COMBAT_WITH_TARGET_WITHIN_RADIUS(PLAYER_PED_ID(), GET_ENTITY_COORDS(PLAYER_PED_ID()), CHOP_ATTACK_RANGE) = 0 // B*1556780 Don't start a piss/shit task if there are peds in combat with Franklin
AND COUNT_PEDS_IN_COMBAT_WITH_TARGET_WITHIN_RADIUS(chopID, GET_ENTITY_COORDS(chopID), CHOP_ATTACK_RANGE) = 0 // B*1556780 Don't start a piss/shit task if there are peds in combat with Franklin
REQUEST_PTFX_ASSET()
IF HAS_PTFX_ASSET_LOADED()
VECTOR v_new_chop_position = GET_ENTITY_COORDS(chopID)
IF GET_DISTANCE_BETWEEN_COORDS(v_new_chop_position, vLastChopBehaviourPosition, FALSE) < 1
IF GET_DISTANCE_BETWEEN_COORDS(v_new_chop_position, vChopLastPissPosition, FALSE) > 20
SAFE_RELEASE_OBJECT(oLamppost)
oLamppost = GET_CLOSEST_OBJECT_OF_TYPE(v_new_chop_position, 10, prop_streetlight_01)
IF DOES_ENTITY_EXIST(oLamppost)
vChopLastPissPosition = GET_ENTITY_COORDS(oLamppost)
GIVE_CHOP_TASK(CHOP_TASK_PISS)
ENDIF
ELIF GET_DISTANCE_BETWEEN_COORDS(v_new_chop_position, vChopLastShitPosition, FALSE) > 20
vChopLastShitPosition = v_new_chop_position
GIVE_CHOP_TASK(CHOP_TASK_SHIT)
ENDIF
ENDIF
vLastChopBehaviourPosition = GET_ENTITY_COORDS(chopID)
iChopBehaviourNotMovingTimer = GET_GAME_TIMER()
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Handles Chop if the player enters a shop.
PROC HANDLE_PLAYER_ENTERING_SHOPS()
IF IS_PLAYER_IN_ANY_SHOP()
AND NOT IS_PLAYER_IN_SHOP_OF_TYPE(SHOP_TYPE_CARMOD) // B*1100720 Leave Chop inside the player car if in a car mod shop
IF IS_ENTITY_ALIVE(chopID)
AND chopInVehicleStatus = CHOP_VEHICLE_STATUS_FRANKLIN_NOT_IN_VEHICLE
AND IS_PED_IN_GROUP(chopID)
REMOVE_PED_FROM_GROUP(chopID)
ENDIF
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player has entered a shop") #ENDIF
GIVE_CHOP_TASK(CHOP_TASK_SIT_OUTSIDE_SHOP)
ENDIF
ENDPROC
/// PURPOSE:
/// Handles Franklin playing fetch with Chop.
PROC HANDLE_FRANKLIN_THROWING_BALL()
IF NOT IS_BIT_SET(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_PLAY_FETCH))
AND NOT IS_HELP_MESSAGE_BEING_DISPLAYED()
AND NOT IS_PLAYER_UNARMED_AND_AIMING_AT_CHOP()
AND NOT IS_PLAYER_SWITCH_IN_PROGRESS()
SET_BIT(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_PLAY_FETCH))
PRINT_HELP("CHOP_H_BALL") // When taking Chop for a walk Franklin can play fetch using the ball in the inventory.
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Displaying help text CHOP_H_BALL") #ENDIF
ENDIF
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
AND NOT IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID(), TRUE)
AND IS_ENTITY_ALIVE(chopID)
AND NOT IS_PED_IN_ANY_VEHICLE(chopID, TRUE)
GET_CURRENT_PED_WEAPON(PLAYER_PED_ID(), wThrownWeapon)
IF wThrownWeapon = WEAPONTYPE_BALL
OR wThrownWeapon = WEAPONTYPE_GRENADE
OR wThrownWeapon = WEAPONTYPE_SMOKEGRENADE
OR wThrownWeapon = WEAPONTYPE_STICKYBOMB
OR wThrownWeapon = WEAPONTYPE_MOLOTOV
OR wThrownWeapon = WEAPONTYPE_FLARE
IF IS_PED_SHOOTING(PLAYER_PED_ID())
GIVE_CHOP_TASK(CHOP_TASK_FETCH)
ELSE
IF (GET_GAME_TIMER() - iChopFetchExcitedTimer) > CHOP_REPEAT_EXCITED_DELAY
AND GET_ENTITY_SPEED(chopID) < 1 // Don't play excited barking if Chop is moving, only when stationary
AND GET_DISTANCE_BETWEEN_ENTITIES(PLAYER_PED_ID(), chopID) < 5
REQUEST_ANIM_DICT("creatures@rottweiler@amb@world_dog_barking@enter")
REQUEST_ANIM_DICT("creatures@rottweiler@amb@world_dog_barking@idle_a")
REQUEST_ANIM_DICT("creatures@rottweiler@amb@world_dog_barking@exit")
IF HAS_ANIM_DICT_LOADED("creatures@rottweiler@amb@world_dog_barking@enter")
AND HAS_ANIM_DICT_LOADED("creatures@rottweiler@amb@world_dog_barking@idle_a")
AND HAS_ANIM_DICT_LOADED("creatures@rottweiler@amb@world_dog_barking@exit")
CLEAR_PED_TASKS(chopID)
OPEN_SEQUENCE_TASK(taskSequence)
TASK_TURN_PED_TO_FACE_ENTITY(NULL, PLAYER_PED_ID())
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@amb@world_dog_barking@enter", "enter", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_DEFAULT)
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@amb@world_dog_barking@idle_a", "idle_a", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_DEFAULT)
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@amb@world_dog_barking@exit", "exit", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_DEFAULT)
CLOSE_SEQUENCE_TASK(taskSequence)
TASK_PERFORM_SEQUENCE(chopID, taskSequence)
CLEAR_SEQUENCE_TASK(taskSequence)
iChopFetchExcitedTimer = GET_GAME_TIMER()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop excited to be playing fetch") #ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Plays a sitting anim on Chop, used with CHOP_TASK_WAIT_PLAY_ANIM.
PROC PLAY_SITTING_ANIM_ON_CHOP()
INT i_random = GET_RANDOM_INT_IN_RANGE(0, 6)
IF i_random = 0
TASK_PLAY_ANIM(chopID, sAnimDict[1] , "idle_a")
sAnimPlaying = "idle_a"
ELIF i_random = 1
TASK_PLAY_ANIM(chopID, sAnimDict[1] , "idle_c")
sAnimPlaying = "idle_c"
ELSE
TASK_PLAY_ANIM(chopID, sAnimDict[1] , "idle_b")
sAnimPlaying = "idle_b"
ENDIF
ENDPROC
/// PURPOSE:
/// Makes Chop randomly wander around near his kennel then play idle anims.
PROC MAKE_CHOP_WANDER_NEAR_KENNEL()
IF bChopWanderingBetweenPositions = FALSE
INT i_random_pos = GET_RANDOM_INT_IN_RANGE(0, NUM_CHOP_WANDER_POSITIONS)
IF NOT IS_ENTITY_AT_COORD(chopID, vChopWanderPositions[i_random_pos], <<2, 2, LOCATE_SIZE_ON_FOOT_ONLY>>)
AND NOT IS_ENTITY_AT_COORD(PLAYER_PED_ID(), vChopWanderPositions[i_random_pos], <<2, 2, LOCATE_SIZE_ON_FOOT_ONLY>>)
TASK_FOLLOW_NAV_MESH_TO_COORD(chopID, vChopWanderPositions[i_random_pos], PEDMOVEBLENDRATIO_WALK, DEFAULT_TIME_BEFORE_WARP, DEFAULT_NAVMESH_RADIUS, ENAV_SUPPRESS_EXACT_STOP)
bChopWanderingBetweenPositions = TRUE
ENDIF
ELSE
IF NOT IsPedPerformingTask(chopID, SCRIPT_TASK_FOLLOW_NAV_MESH_TO_COORD)
INT i_dump_threshold
IF GET_CHOP_BEHAVIOUR_FROM_APP() = CHOP_BEHAVIOUR_BAD
i_dump_threshold = 15 // Percentage chance of Chop doing a shit
ELIF GET_CHOP_BEHAVIOUR_FROM_APP() = CHOP_BEHAVIOUR_MEDIUM
i_dump_threshold = 10 // Percentage chance of Chop doing a shit
ELSE
i_dump_threshold = -1 // Never let Chop do a shit if his behaviour is good
ENDIF
INT i_random = GET_RANDOM_INT_IN_RANGE(0, 100)
IF i_random < i_dump_threshold
AND bChopDidShit = FALSE
bChopDidShit = TRUE // Prevents Chop randomly doing two shits in succession at the same place
GIVE_CHOP_TASK(CHOP_TASK_SHIT)
ELIF i_random < 50
bPlayWaitStandAnim = TRUE
sAnimDict[0] = "creatures@rottweiler@amb@world_dog_barking@enter"
sAnimDict[1] = "creatures@rottweiler@amb@world_dog_barking@base"
sAnimDict[2] = "creatures@rottweiler@amb@world_dog_barking@exit"
GIVE_CHOP_TASK(CHOP_TASK_WAIT_PLAY_ANIM)
ELSE
bPlayWaitStandAnim = FALSE
sAnimDict[0] = "creatures@rottweiler@amb@world_dog_sitting@enter"
sAnimDict[1] = "creatures@rottweiler@amb@world_dog_sitting@idle_a"
sAnimDict[2] = "creatures@rottweiler@amb@world_dog_sitting@exit"
GIVE_CHOP_TASK(CHOP_TASK_WAIT_PLAY_ANIM)
ENDIF
ENDIF
ENDIF
ENDPROC
FUNC STRING DETERMINE_CHOP_INDICATE_ANIM()
STRING string_to_use
IF IS_ENTITY_ALIVE(chopID)
VECTOR v_chop = GET_ENTITY_COORDS(chopID)
IF v_chop.z + 1 < vClosestPickup.z
string_to_use = "indicate_high"
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop is more than 1m below the pickup") #ENDIF
ELIF v_chop.z - 1 > vClosestPickup.z
string_to_use = "indicate_low"
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop is more than 1m above the pickup") #ENDIF
ELSE
string_to_use = "indicate_ahead"
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop is roughly on same level as the pickup") #ENDIF
ENDIF
ENDIF
RETURN string_to_use
ENDFUNC
/// PURPOSE:
/// Makes Chop face the pickup when he's near to it then play an idle.
PROC MAKE_CHOP_FACE_PICKUP()
IF NOT IsPedPerformingTask(chopID, SCRIPT_TASK_PERFORM_SEQUENCE)
REQUEST_ANIM_DICT("creatures@rottweiler@indication@")
IF HAS_ANIM_DICT_LOADED("creatures@rottweiler@indication@")
CLEAR_PED_TASKS(chopID)
OPEN_SEQUENCE_TASK(taskSequence)
TASK_TURN_PED_TO_FACE_COORD(NULL, vClosestPickup)
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@indication@", DETERMINE_CHOP_INDICATE_ANIM(), NORMAL_BLEND_IN, SLOW_BLEND_OUT, -1, AF_LOOPING)
CLOSE_SEQUENCE_TASK(taskSequence)
TASK_PERFORM_SEQUENCE(chopID, taskSequence)
CLEAR_SEQUENCE_TASK(taskSequence)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop sitting at ", vChopSitNearClosestPickup, " facing pickup at ", vClosestPickup) #ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Handles sending a query to the pickup controller.
FUNC BOOL CHECK_PICKUP_QUERY_DOES_PICKUP_EXIST(QUERY_TYPE query_pickup_type)
IF IS_QUERY_RESULT_INACTIVE()
INIT_PICKUP_QUERY_REQUEST(query_pickup_type, iClosestPickup)
ENDIF
IF HAS_RETURNED_QUERY_RESULT()
IF HAS_QUERIED_PICKUP_BEEN_COLLECTED()
END_PICKUP_QUERY_REQUEST()
RETURN FALSE
ENDIF
END_PICKUP_QUERY_REQUEST()
ENDIF
RETURN TRUE // Either pickup still exists or still waiting for the query to return a result
ENDFUNC
/// PURPOSE:
/// Checks if the current pickup that Chop is sniffing out still exists.
FUNC BOOL DOES_CHOP_CURRENT_PICKUP_EXIST()
SWITCH chopCurrentPickupType
CASE CHOP_CURRENT_PICKUP_TYPE_WEAPON
IF NOT CHECK_PICKUP_QUERY_DOES_PICKUP_EXIST(PC_QUERY_TYPE_WEAPON)
PLAY_FRANKLIN_DIALOGUE("CHOP_RETURN1", DO_NOT_DISPLAY_SUBTITLES)
RETURN FALSE
ENDIF
BREAK
CASE CHOP_CURRENT_PICKUP_TYPE_HEALTH
IF NOT CHECK_PICKUP_QUERY_DOES_PICKUP_EXIST(PC_QUERY_TYPE_HEALTH)
PLAY_FRANKLIN_DIALOGUE("CHOP_RETURN1", DO_NOT_DISPLAY_SUBTITLES)
RETURN FALSE
ENDIF
BREAK
CASE CHOP_CURRENT_PICKUP_TYPE_ARMOUR
IF NOT CHECK_PICKUP_QUERY_DOES_PICKUP_EXIST(PC_QUERY_TYPE_ARMOUR)
PLAY_FRANKLIN_DIALOGUE("CHOP_RETURN1", DO_NOT_DISPLAY_SUBTITLES)
RETURN FALSE
ENDIF
BREAK
CASE CHOP_CURRENT_PICKUP_TYPE_LETTER
IF HAS_LETTER_SCRAP_BEEN_COLLECTED(iClosestPickup)
PLAY_FRANKLIN_DIALOGUE("CHOP_RETURN1", DO_NOT_DISPLAY_SUBTITLES)
RETURN FALSE
ENDIF
BREAK
CASE CHOP_CURRENT_PICKUP_TYPE_SPACESHIP
IF HAS_SPACESHIP_PART_BEEN_COLLECTED(iClosestPickup)
PLAY_FRANKLIN_DIALOGUE("CHOP_RETURN1", DO_NOT_DISPLAY_SUBTITLES)
RETURN FALSE
ENDIF
BREAK
ENDSWITCH
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Returns TRUE if Chop should exit his kennel now.
FUNC BOOL SHOULD_CHOP_EXIT_KENNEL()
IF IS_PED_IN_TRICK_AREA(PLAYER_PED_ID())
IF (GET_GAME_TIMER() - iChopInKennelTimer) > iChopExitKennelTime // Else wait a random period of time
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop should exit kennel now - timer expired") #ENDIF
RETURN TRUE
ENDIF
IF GET_DISTANCE_BETWEEN_ENTITY_AND_COORD(PLAYER_PED_ID(), sChopData.vKennelCoords) < 8 // Player is very close so just get Chop to exit immediately
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop should exit kennel now - closer than 8 metres") #ENDIF
PLAY_FRANKLIN_DIALOGUE("CHOP_KENNEL")
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Give Chop a task after performing a trick. See B*1092487 - Chop should only follow player if he was on a walk prior to being given a trick.
PROC CHOP_FINISHED_TRICK()
iChopBehaviourNotMovingTimer = GET_GAME_TIMER()
IF bPlayerChoseToWalkChop = FALSE
GIVE_CHOP_TASK(CHOP_TASK_WAIT)
ELSE
GIVE_CHOP_TASK(CHOP_TASK_WALK)
ENDIF
ENDPROC
/// PURPOSE:
/// Either Chop isn't close enough to anything or the pathfinding has failed.
PROC CHOP_CANT_FIND_ANYTHING_NEARBY()
VECTOR v_chop = GET_ENTITY_COORDS(chopID)
DUMMY_REFERENCE_VECTOR(v_chop)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop couldn't find anything near ", v_chop) #ENDIF
PLAY_CHOP_SOUND("WHINE")
PLAY_FRANKLIN_DIALOGUE("CHOP_NONEAR")
CHOP_FINISHED_TRICK()
ENDPROC
/// PURPOSE:
/// Handles targetting Chop.
PROC ALLOW_PLAYER_TO_TARGET_CHOP(BOOL b_allow)
WEAPON_TYPE current_player_weapon
GET_CURRENT_PED_WEAPON(PLAYER_PED_ID(), current_player_weapon)
IF bIsChopTargettable = TRUE
AND current_player_weapon != WEAPONTYPE_UNARMED // Scan for the player equiping a weapon
b_allow = FALSE
ENDIF
IF b_allow = TRUE
IF bIsChopTargettable = FALSE
AND current_player_weapon = WEAPONTYPE_UNARMED
SET_PED_CAN_BE_TARGETTED(chopID, TRUE)
SET_ENTITY_IS_TARGET_PRIORITY(chopID, TRUE)
SET_ALLOW_LOCKON_TO_PED_IF_FRIENDLY(chopID, TRUE)
SET_USE_CAMERA_HEADING_FOR_DESIRED_DIRECTION_LOCK_ON_TEST(chopID, TRUE)
bIsChopTargettable = TRUE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player allowed to target Chop") #ENDIF
ENDIF
ELSE
IF bIsChopTargettable = TRUE
SET_PED_CAN_BE_TARGETTED(chopID, FALSE)
SET_ENTITY_IS_TARGET_PRIORITY(chopID, FALSE)
SET_ALLOW_LOCKON_TO_PED_IF_FRIENDLY(chopID, FALSE)
bIsChopTargettable = FALSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player prevented targeting Chop") #ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Returns true if Chop has reached the ball.
FUNC BOOL HAS_CHOP_REACHED_BALL()
IF GET_DISTANCE_BETWEEN_ENTITIES(chopID, eBall) < DEFAULT_SEEK_RADIUS+0.25
VECTOR v_current_ball_position = GET_ENTITY_COORDS(eBall)
FLOAT f_current_ball_ground_z
IF GET_GROUND_Z_FOR_3D_COORD(<<v_current_ball_position.x, v_current_ball_position.y, v_current_ball_position.z+1>>, f_current_ball_ground_z)
IF ABSF(v_current_ball_position.z-f_current_ball_ground_z) < 0.1
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Ball is on the floor and Chop is near it") #ENDIF
RETURN TRUE
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Returns true if Chop has finished his anim task sequence or been damaged while doing it.
FUNC BOOL HAS_CHOP_FINISHED_ANIM_TASK_SEQUENCE()
IF GET_SCRIPT_TASK_STATUS(chopID, SCRIPT_TASK_PERFORM_SEQUENCE) = FINISHED_TASK
OR IS_PED_RAGDOLL(chopID) // B*1554726 If someone kicks Chop to the ground
OR HAS_ENTITY_BEEN_DAMAGED_BY_ANY_PED(chopID) // B*1554726 If someone kicks Chop to the ground
CLEAR_PED_LAST_WEAPON_DAMAGE(chopID)
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Process Chop's primary tasks such as wait, walk, and return.
PROC PROCESS_PRIMARY_TASKS()
NAVMESH_ROUTE_RESULT nrr_chop_route
INT i_chop_route
FLOAT f_chop_route
VECTOR v_offset
SWITCH eChopTask
CASE CHOP_TASK_SPAWNED_IN_KENNEL
IF iChopStage = 0
REQUEST_ANIM_DICT("creatures@rottweiler@amb@sleep_in_kennel@")
IF HAS_ANIM_DICT_LOADED("creatures@rottweiler@amb@sleep_in_kennel@")
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Putting Chop in kennel") #ENDIF
IF IS_ENTITY_ALIVE(chopID)
TASK_PLAY_ANIM(chopID, "creatures@rottweiler@amb@sleep_in_kennel@", "sleep_in_kennel", INSTANT_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_LOOPING)
SAFE_TELEPORT_ENTITY(chopID, sChopData.vKennelCoords, sChopData.fKennelHeading)
ENDIF
iChopInKennelTimer = GET_GAME_TIMER()
iChopStage++
ENDIF
ELIF iChopStage = 1
IF SHOULD_CHOP_EXIT_KENNEL()
REQUEST_ANIM_DICT("creatures@rottweiler@amb@sleep_in_kennel@")
IF HAS_ANIM_DICT_LOADED("creatures@rottweiler@amb@sleep_in_kennel@")
AND IS_ENTITY_ALIVE(chopID)
TASK_PLAY_ANIM(chopID, "creatures@rottweiler@amb@sleep_in_kennel@", "exit_kennel", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_DEFAULT)
PLAY_CHOP_SOUND("WHINE")
iChopStage++
ENDIF
ENDIF
ELIF iChopStage = 2
IF GET_SCRIPT_TASK_STATUS(chopID, SCRIPT_TASK_PLAY_ANIM) = FINISHED_TASK
REMOVE_ANIM_DICT("creatures@rottweiler@amb@sleep_in_kennel@")
PLAY_CHOP_SOUND("PLAYFUL")
GIVE_CHOP_TASK(CHOP_TASK_WAIT)
ENDIF
ENDIF
BREAK
CASE CHOP_TASK_WAIT
IF bChopGreetedFranklinDone = FALSE
AND IS_PED_IN_TRICK_AREA(PLAYER_PED_ID())
GIVE_CHOP_TASK(CHOP_TASK_GREET_FRANKLIN)
ELSE
HANDLE_CHOP_WHINING_IF_UNHAPPY()
CHECK_FOR_PLAYER_BULLET_NEAR_CHOP()
MAKE_CHOP_WANDER_NEAR_KENNEL()
ENDIF
BREAK
CASE CHOP_TASK_WAIT_PLAY_ANIM // Handles playing anims when Chop is mooching near his kennel
HANDLE_CHOP_WHINING_IF_UNHAPPY()
CHECK_FOR_PLAYER_BULLET_NEAR_CHOP()
IF iChopStage = 0 // Play enter anim
REQUEST_ANIM_DICT(sAnimDict[0])
REQUEST_ANIM_DICT(sAnimDict[1])
REQUEST_ANIM_DICT(sAnimDict[2])
IF HAS_ANIM_DICT_LOADED(sAnimDict[0])
AND HAS_ANIM_DICT_LOADED(sAnimDict[1])
AND HAS_ANIM_DICT_LOADED(sAnimDict[2])
TASK_PLAY_ANIM(chopID, sAnimDict[0], "enter")
iChopStage++
ENDIF
ELIF iChopStage = 1 // Play loop anim
IF NOT IsPedPerformingTask(chopID, SCRIPT_TASK_PLAY_ANIM)
OR (IS_ENTITY_PLAYING_ANIM(chopID, sAnimDict[0], "enter") AND GET_ENTITY_ANIM_CURRENT_TIME(chopID, sAnimDict[0], "enter") > 0.98) // Enter anim is almost finished
OR (IS_ENTITY_PLAYING_ANIM(chopID, sAnimDict[1], sAnimPlaying) AND GET_ENTITY_ANIM_CURRENT_TIME(chopID, sAnimDict[1], sAnimPlaying) > 0.98) // Loop anim is almost finished
IF eNextChopTask = CHOP_TASK_NONE
IF bPlayWaitStandAnim = TRUE
TASK_PLAY_ANIM(chopID, sAnimDict[1], "base")
sAnimPlaying = "base"
iChopStage++
ELSE
PLAY_SITTING_ANIM_ON_CHOP()
IF GET_RANDOM_INT_IN_RANGE(0, 4) = 0 // 75% of the time keep playing more sitting anims
iChopStage++
ENDIF
ENDIF
ELSE // Player has given an order so we need to exit
IF eNextChopTask != CHOP_TASK_SNIFF
PLAY_FRANKLIN_DIALOGUE("CHOP_WAIT")
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Not playing CHOP_WAIT") #ENDIF // See B*1516024
ENDIF
TASK_PLAY_ANIM(chopID, sAnimDict[2], "exit")
iChopStage = 3
ENDIF
ENDIF
ELIF iChopStage = 2 // Play exit anim
IF NOT IsPedPerformingTask(chopID, SCRIPT_TASK_PLAY_ANIM)
OR (IS_ENTITY_PLAYING_ANIM(chopID, sAnimDict[1], sAnimPlaying) AND GET_ENTITY_ANIM_CURRENT_TIME(chopID, sAnimDict[1], sAnimPlaying) > 0.98) // Loop anim is almost finished
TASK_PLAY_ANIM(chopID, sAnimDict[2], "exit")
iChopStage++
ENDIF
ELIF iChopStage = 3 // Cleanup play anim state
IF NOT IsPedPerformingTask(chopID, SCRIPT_TASK_PLAY_ANIM)
bChopDidShit = FALSE
IF eNextChopTask = CHOP_TASK_NONE
GIVE_CHOP_TASK(CHOP_TASK_WAIT)
ELSE // Player has given Chop a task while these anims have been playing..
GIVE_CHOP_TASK(eNextChopTask) // .. so set the stored task as Chop's new task
ENDIF
ENDIF
ENDIF
BREAK
CASE CHOP_TASK_GREET_FRANKLIN // Makes Chop greet Franklin. See B*1051650.
CHECK_FOR_PLAYER_BULLET_NEAR_CHOP()
IF iChopStage = 0
REQUEST_ANIM_DICT("creatures@rottweiler@amb@world_dog_barking@enter")
REQUEST_ANIM_DICT("creatures@rottweiler@amb@world_dog_barking@idle_a")
REQUEST_ANIM_DICT("creatures@rottweiler@amb@world_dog_barking@exit")
TASK_GO_TO_ENTITY(chopID, PLAYER_PED_ID(), DEFAULT_TIME_BEFORE_WARP, 3, PEDMOVEBLENDRATIO_WALK)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop sent to greet Franklin") #ENDIF
iChopStage++
ELIF iChopStage = 1
IF eNextChopTask = CHOP_TASK_NONE
IF NOT IsPedPerformingTask(chopID, SCRIPT_TASK_GO_TO_ENTITY)
IF HAS_ANIM_DICT_LOADED("creatures@rottweiler@amb@world_dog_barking@enter")
AND HAS_ANIM_DICT_LOADED("creatures@rottweiler@amb@world_dog_barking@idle_a")
AND HAS_ANIM_DICT_LOADED("creatures@rottweiler@amb@world_dog_barking@exit")
OPEN_SEQUENCE_TASK(taskSequence)
TASK_TURN_PED_TO_FACE_ENTITY(NULL, PLAYER_PED_ID())
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@amb@world_dog_barking@enter", "enter")
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@amb@world_dog_barking@idle_a", "idle_a")
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@amb@world_dog_barking@exit", "exit")
CLOSE_SEQUENCE_TASK(taskSequence)
TASK_PERFORM_SEQUENCE(chopID, taskSequence)
CLEAR_SEQUENCE_TASK(taskSequence)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop now greeting Franklin") #ENDIF
iChopStage++
ENDIF
ENDIF
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player gave Chop a task before Chop reached Franklin to greet him") #ENDIF
GIVE_CHOP_TASK(eNextChopTask)
ENDIF
ELIF iChopStage = 2
IF HAS_CHOP_FINISHED_ANIM_TASK_SEQUENCE()
bChopGreetedFranklinDone = TRUE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop greeting Franklin has finished") #ENDIF
IF eNextChopTask = CHOP_TASK_NONE
GIVE_CHOP_TASK(CHOP_TASK_WAIT)
ELSE // Player has given Chop a task while these anims have been playing..
GIVE_CHOP_TASK(eNextChopTask) // .. so set the stored task as Chop's new task
ENDIF
ENDIF
ENDIF
BREAK
CASE CHOP_TASK_WALK
HANDLE_FRANKLIN_WHISTLING_TO_CHOP()
IF iChopStage = 0
IF IS_ENTITY_ALIVE(chopID)
AND NOT IS_PED_IN_ANY_VEHICLE(chopID) // If Chop is spawned in vehicle from switch scene don't clear his anim
AND NOT IS_PED_IN_GROUP(chopID) // B*1861149 Don't clear tasks if repeat whistling
CLEAR_PED_TASKS(chopID)
ENDIF
PUT_CHOP_IN_PLAYER_GROUP()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop told to follow Franklin") #ENDIF
GIVE_BALL_TO_PLAYER()
SUPPRESS_OTHER_CREATURES(TRUE)
iNextWantedWhimperTimer = -1
iClosestPickupTimer = GET_GAME_TIMER()
iChopBehaviourNotMovingTimer = GET_GAME_TIMER()
iChopFetchExcitedTimer = GET_GAME_TIMER() - CHOP_REPEAT_EXCITED_DELAY // Allow playing the excited fetch bark straight away
vLastChopBehaviourPosition = GET_ENTITY_COORDS(chopID)
REQUEST_ANIM_DICT("creatures@rottweiler@melee@streamed_taunts@") // B*1555787 Ensure taunts are preloaded
iChopStage++
ELIF iChopStage = 1 // Chop is following Franklin around
SCAN_FOR_NEAREST_PICKUP()
MANAGE_CHOP_IN_PLAYER_VEHICLE()
HANDLE_CHOP_WHEN_PLAYER_WANTED()
HANDLE_PLAYER_ENTERING_SHOPS()
HANDLE_FRANKLIN_THROWING_BALL()
HANDLE_CHOP_NOT_MOVING()
HANDLE_CHOP_WHINING_IF_UNHAPPY()
ENDIF
BREAK
CASE CHOP_TASK_RETURN
IF iChopStage = 0
PLAY_CHOP_SOUND("WHINE")
PLAY_FRANKLIN_DIALOGUE("CHOP_GOHOME")
CLEANUP_BALL(FALSE)
REMOVE_ANIM_DICT("creatures@rottweiler@melee@streamed_taunts@")
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
AND HAS_PED_GOT_WEAPON(PLAYER_PED_ID(), WEAPONTYPE_BALL)
REMOVE_WEAPON_FROM_PED(PLAYER_PED_ID(), WEAPONTYPE_BALL)
ENDIF
IF IS_PED_IN_GROUP(chopID)
REMOVE_PED_FROM_GROUP(chopID) // B*1891517 remove Chop from player group to prevent the group leader code warping Chop nearby if the player gets teleported elsewhere (such as by the taxi script)
ENDIF
IF IS_PED_IN_TRICK_AREA(chopID)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop already near kennel") #ENDIF
GIVE_CHOP_TASK(CHOP_TASK_WAIT)
ELIF GET_DISTANCE_BETWEEN_ENTITY_AND_COORD(chopID, sChopData.vSpawnCoords) < 100
CLEAR_PED_TASKS(chopID)
IF GET_DISTANCE_BETWEEN_ENTITY_AND_COORD(chopID, sChopData.vSpawnCoords) < 20 // Walk back if Chop is close to his kennel (rather than sprinting if he's only a few metres away etc)
TASK_FOLLOW_NAV_MESH_TO_COORD(chopID, sChopData.vSpawnCoords, PEDMOVEBLENDRATIO_WALK, DEFAULT_TIME_BEFORE_WARP, DEFAULT_NAVMESH_RADIUS, ENAV_NEVER_ENTER_WATER, sChopData.fSpawnHeading)
ELSE
TASK_FOLLOW_NAV_MESH_TO_COORD(chopID, sChopData.vSpawnCoords, PEDMOVEBLENDRATIO_SPRINT, 60000, DEFAULT_NAVMESH_RADIUS, ENAV_NEVER_ENTER_WATER, sChopData.fSpawnHeading)
ENDIF
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop told to return to his kennel") #ENDIF
iChopStage++
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop is too far away to return to his kennel") #ENDIF
GIVE_CHOP_TASK(CHOP_TASK_WANDER)
ENDIF
ELIF iChopStage = 1
IF GET_SCRIPT_TASK_STATUS(chopID, SCRIPT_TASK_FOLLOW_NAV_MESH_TO_COORD) = FINISHED_TASK
IF IS_ENTITY_AT_COORD(chopID, sChopData.vSpawnCoords, <<2.5, 2.5, LOCATE_SIZE_ON_FOOT_ONLY>>)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop has returned back to his kennel") #ENDIF
GIVE_CHOP_TASK(CHOP_TASK_WAIT)
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop's return task has finished but he's not at his kennel, so need to reapply the return task") #ENDIF
GIVE_CHOP_TASK(CHOP_TASK_RETURN)
ENDIF
ENDIF
ENDIF
BREAK
CASE CHOP_TASK_WANDER
IF NOT IsPedPerformingTask(chopID, SCRIPT_TASK_SMART_FLEE_PED)
TASK_SMART_FLEE_PED(chopID, PLAYER_PED_ID(), 100, -1) // B*1109931 - Chop can't use a wander task
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop told to flee") #ENDIF
ENDIF
BREAK
CASE CHOP_TASK_SNIFF
IF iChopStage = 0
IF HAS_PICKUP_SCAN_BEEN_COMPLETED()
IF IS_CHOP_WITHIN_RANGE_OF_NEAREST_PICKUP()
IF IS_PED_IN_GROUP(chopID)
REMOVE_PED_FROM_GROUP(chopID)
ENDIF
CLEAR_PED_TASKS(chopID)
iBarkTimer = GET_GAME_TIMER()
iDelayUntilNextBark = 1000
iNavmeshAttempts = 0
iChopReachedPickupTimer = -1
END_PICKUP_QUERY_REQUEST()
iChopStage++
ELSE
CHOP_CANT_FIND_ANYTHING_NEARBY()
ENDIF
ENDIF
ELIF iChopStage = 1
IF NOT DOES_CHOP_CURRENT_PICKUP_EXIST()
OR (IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID()) AND NOT IS_PLAYER_IN_SUITABLE_VEHICLE_FOR_CHOP_TO_FOLLOW())
OR (iChopReachedPickupTimer > -1 AND (GET_GAME_TIMER() - iChopReachedPickupTimer) > 30000) // B*1226501 - Don't make Chop indefinitely wait at the pickup
OR IS_ENTITY_AT_COORD(PLAYER_PED_ID(), vClosestPickup, <<1, 1, 1>>) // B*1226501 - Player has reached the pickup but can't pick it up (already full)
iChopStage++
ELSE
IF IS_ENTITY_AT_COORD(chopID, vChopSitNearClosestPickup, <<3, 3, LOCATE_SIZE_ON_FOOT_ONLY>>)
MAKE_CHOP_FACE_PICKUP()
IF iChopReachedPickupTimer = -1
iChopReachedPickupTimer = GET_GAME_TIMER()
ENDIF
ELSE
PLAY_ALERT_BARK_DIALOGUE()
IF IsPedPerformingTask(chopID, SCRIPT_TASK_FOLLOW_NAV_MESH_TO_COORD)
nrr_chop_route = GET_NAVMESH_ROUTE_DISTANCE_REMAINING(chopID, f_chop_route, i_chop_route)
IF nrr_chop_route = NAVMESHROUTE_ROUTE_NOT_FOUND
iNavmeshAttempts++
ELIF nrr_chop_route = NAVMESHROUTE_ROUTE_FOUND
IF iNavmeshAttempts <> 0
iNavmeshAttempts = 0 // Reset back to 0 so all the attempts don't get used up if the AI has to recompute the route, see B*1226729
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: nrr_chop_route = NAVMESHROUTE_ROUTE_FOUND so reset iNavmeshAttempts to 0") #ENDIF
ENDIF
ENDIF
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Navmesh task = ", nrr_chop_route, " iNavmeshAttempts = ", iNavmeshAttempts, " dist = ", f_chop_route, " last section = ", i_chop_route) #ENDIF
IF iNavmeshAttempts > 9
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: nrr_chop_route = NAVMESHROUTE_ROUTE_NOT_FOUND so do GIVE_CHOP_TASK(CHOP_TASK_WALK)") #ENDIF
CHOP_CANT_FIND_ANYTHING_NEARBY()
ENDIF
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop sniffing out a hidden package or weapon pickup") #ENDIF
TASK_FOLLOW_NAV_MESH_TO_COORD(chopID, vChopSitNearClosestPickup, PEDMOVE_SPRINT, DEFAULT_TIME_NEVER_WARP, 3.0, ENAV_GO_FAR_AS_POSSIBLE_IF_TARGET_NAVMESH_NOT_LOADED|ENAV_NEVER_ENTER_WATER)
ENDIF
ENDIF
ENDIF
ELIF iChopStage = 2
CLEAR_PED_TASKS(chopID)
REMOVE_ANIM_DICT("creatures@rottweiler@indication@")
GIVE_CHOP_TASK(CHOP_TASK_WALK)
ENDIF
HANDLE_FRANKLIN_WHISTLING_TO_CHOP()
BREAK
CASE CHOP_TASK_ATTACK
IF iChopStage = 0
IF IS_PED_IN_GROUP(chopID)
REMOVE_PED_FROM_GROUP(chopID)
ENDIF
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(chopID, FALSE)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Starting CHOP_TASK_ATTACK") #ENDIF
iChopStage++
ELIF iChopStage = 1
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
IF (NOT IsPedPerformingTask(chopID, SCRIPT_TASK_COMBAT) AND NOT IS_PED_IN_COMBAT(chopID))
OR IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID(), TRUE) // B*1088354 Player has got into a vehicle that Chop can get into, call off Chop
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Ending CHOP_TASK_ATTACK") #ENDIF
iChopBehaviourNotMovingTimer = GET_GAME_TIMER()
GIVE_CHOP_TASK(CHOP_TASK_WALK)
ENDIF
ENDIF
ENDIF
HANDLE_FRANKLIN_WHISTLING_TO_CHOP()
BREAK
CASE CHOP_TASK_BEG
IF iChopStage = 0
REQUEST_ANIM_DICT("creatures@rottweiler@tricks@")
IF HAS_ANIM_DICT_LOADED("creatures@rottweiler@tricks@")
CLEAR_PED_TASKS(chopID)
OPEN_SEQUENCE_TASK(taskSequence)
IF NOT IS_PED_FACING_PED(chopID, PLAYER_PED_ID(), 10)
TASK_TURN_PED_TO_FACE_ENTITY(NULL, PLAYER_PED_ID())
ENDIF
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@tricks@", "beg_enter", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_EXTRACT_INITIAL_OFFSET)
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@tricks@", "beg_loop", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_EXTRACT_INITIAL_OFFSET)
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@tricks@", "beg_loop", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_EXTRACT_INITIAL_OFFSET)
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@tricks@", "beg_exit", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_EXTRACT_INITIAL_OFFSET)
CLOSE_SEQUENCE_TASK(taskSequence)
TASK_PERFORM_SEQUENCE(chopID, taskSequence)
CLEAR_SEQUENCE_TASK(taskSequence)
PLAY_CHOP_SOUND("PLAYFUL")
iChopStage++
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop performing a beg task") #ENDIF
ENDIF
ELIF iChopStage = 1
IF HAS_CHOP_FINISHED_ANIM_TASK_SEQUENCE()
PLAY_FRANKLIN_DIALOGUE("CHOP_RETURN1")
REMOVE_ANIM_DICT("creatures@rottweiler@tricks@")
CHOP_FINISHED_TRICK()
ENDIF
ENDIF
BREAK
CASE CHOP_TASK_SIT
IF iChopStage = 0
REQUEST_ANIM_DICT("creatures@rottweiler@tricks@")
IF HAS_ANIM_DICT_LOADED("creatures@rottweiler@tricks@")
CLEAR_PED_TASKS(chopID)
OPEN_SEQUENCE_TASK(taskSequence)
IF NOT IS_PED_FACING_PED(chopID, PLAYER_PED_ID(), 10)
TASK_TURN_PED_TO_FACE_ENTITY(NULL, PLAYER_PED_ID())
ENDIF
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@tricks@", "sit_enter", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_EXTRACT_INITIAL_OFFSET)
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@tricks@", "sit_loop", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_EXTRACT_INITIAL_OFFSET)
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@tricks@", "sit_loop", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_EXTRACT_INITIAL_OFFSET)
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@tricks@", "sit_exit", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_EXTRACT_INITIAL_OFFSET)
CLOSE_SEQUENCE_TASK(taskSequence)
TASK_PERFORM_SEQUENCE(chopID, taskSequence)
CLEAR_SEQUENCE_TASK(taskSequence)
PLAY_CHOP_SOUND("PLAYFUL")
iChopStage++
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop performing a sit task") #ENDIF
ENDIF
ELIF iChopStage = 1
IF HAS_CHOP_FINISHED_ANIM_TASK_SEQUENCE()
PLAY_FRANKLIN_DIALOGUE("CHOP_RETURN1")
REMOVE_ANIM_DICT("creatures@rottweiler@tricks@")
CHOP_FINISHED_TRICK()
ENDIF
ENDIF
BREAK
CASE CHOP_TASK_PAW
IF iChopStage = 0
REQUEST_ANIM_DICT("creatures@rottweiler@tricks@")
IF HAS_ANIM_DICT_LOADED("creatures@rottweiler@tricks@")
CLEAR_PED_TASKS(chopID)
OPEN_SEQUENCE_TASK(taskSequence)
IF NOT IS_PED_FACING_PED(chopID, PLAYER_PED_ID(), 10)
TASK_TURN_PED_TO_FACE_ENTITY(NULL, PLAYER_PED_ID())
ENDIF
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@tricks@", "sit_enter", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_EXTRACT_INITIAL_OFFSET)
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@tricks@", "paw_right_enter", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_EXTRACT_INITIAL_OFFSET)
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@tricks@", "paw_right_loop", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_EXTRACT_INITIAL_OFFSET)
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@tricks@", "paw_right_loop", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_EXTRACT_INITIAL_OFFSET)
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@tricks@", "paw_right_exit", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_EXTRACT_INITIAL_OFFSET)
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@tricks@", "sit_exit", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_EXTRACT_INITIAL_OFFSET)
CLOSE_SEQUENCE_TASK(taskSequence)
TASK_PERFORM_SEQUENCE(chopID, taskSequence)
CLEAR_SEQUENCE_TASK(taskSequence)
PLAY_CHOP_SOUND("PLAYFUL")
iChopStage++
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop performing a paw task") #ENDIF
ENDIF
ELIF iChopStage = 1
IF HAS_CHOP_FINISHED_ANIM_TASK_SEQUENCE()
PLAY_FRANKLIN_DIALOGUE("CHOP_RETURN1")
REMOVE_ANIM_DICT("creatures@rottweiler@tricks@")
CHOP_FINISHED_TRICK()
ENDIF
ENDIF
BREAK
CASE CHOP_TASK_PET
// Disable everything except aiming, in order to keep the menu onscreen. Massive list ahoy...
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_MOVE_UP_ONLY)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_MOVE_LEFT_ONLY)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_MOVE_RIGHT_ONLY)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_MOVE_DOWN_ONLY)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_MOVE_UD)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_MOVE_LR)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_JUMP)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_DUCK)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_COVER)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_MELEE_ATTACK_HEAVY)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_MELEE_ATTACK_LIGHT)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_MELEE_ATTACK1)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_MELEE_ATTACK2)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_MELEE_BLOCK)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_ATTACK)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_ATTACK2)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_NEXT_WEAPON)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_PREV_WEAPON)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_SELECT_WEAPON)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_SPRINT)
IF iChopStage = 0 // Turn Franklin to face Chop
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop performing a pet task") #ENDIF
REQUEST_ANIM_DICT("creatures@rottweiler@tricks@")
CLEAR_PED_TASKS(PLAYER_PED_ID())
TASK_TURN_PED_TO_FACE_ENTITY(PLAYER_PED_ID(), chopID)
iChopStage++
ELIF iChopStage = 1 // Move Chop into position and turn him to face Franklin
IF GET_SCRIPT_TASK_STATUS(PLAYER_PED_ID(), SCRIPT_TASK_TURN_PED_TO_FACE_ENTITY) = FINISHED_TASK
CLEAR_PED_TASKS(chopID)
v_offset = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(PLAYER_PED_ID(),<<0,1,1>>)
GET_GROUND_Z_FOR_3D_COORD(v_offset, v_offset.z)
IF GET_DISTANCE_BETWEEN_ENTITY_AND_COORD(chopID, v_offset, FALSE) > 0.3
TASK_FOLLOW_NAV_MESH_TO_COORD(chopID, v_offset, PEDMOVE_WALK, 5000)
ENDIF
iChopStage++
ENDIF
ELIF iChopStage = 2 // Check they're still facing each other
IF GET_SCRIPT_TASK_STATUS(chopID, SCRIPT_TASK_FOLLOW_NAV_MESH_TO_COORD) = FINISHED_TASK
OR GET_DISTANCE_BETWEEN_ENTITY_AND_COORD(chopID, v_offset, FALSE) < 0.3
IF NOT IS_PED_FACING_PED(chopID, PLAYER_PED_ID(), 15)
TASK_TURN_PED_TO_FACE_ENTITY(chopID, PLAYER_PED_ID())
bChopAlreadyFacingFranklin = FALSE
ELSE
bChopAlreadyFacingFranklin = TRUE
ENDIF
IF NOT IS_PED_FACING_PED(PLAYER_PED_ID(), chopID, 15)
TASK_TURN_PED_TO_FACE_ENTITY(PLAYER_PED_ID(), chopID)
bFranklinAlreadyFacingChop = FALSE
ELSE
bFranklinAlreadyFacingChop = TRUE
ENDIF
iChopStage++
ENDIF
ELIF iChopStage = 3 // Play their petting anims
REQUEST_ANIM_DICT("creatures@rottweiler@tricks@")
IF (bChopAlreadyFacingFranklin = TRUE OR GET_SCRIPT_TASK_STATUS(chopID, SCRIPT_TASK_TURN_PED_TO_FACE_ENTITY) = FINISHED_TASK)
AND (bFranklinAlreadyFacingChop = TRUE OR GET_SCRIPT_TASK_STATUS(PLAYER_PED_ID(), SCRIPT_TASK_TURN_PED_TO_FACE_ENTITY) = FINISHED_TASK)
AND HAS_ANIM_DICT_LOADED("creatures@rottweiler@tricks@")
TASK_PLAY_ANIM(chopID, "creatures@rottweiler@tricks@", "petting_chop", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_NOT_INTERRUPTABLE)
TASK_PLAY_ANIM(PLAYER_PED_ID(), "creatures@rottweiler@tricks@", "petting_franklin", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_NOT_INTERRUPTABLE)
PLAY_CHOP_SOUND("PLAYFUL")
iChopStage++
ENDIF
ELIF iChopStage = 4 // Petting finished
IF GET_SCRIPT_TASK_STATUS(chopID, SCRIPT_TASK_PLAY_ANIM) = FINISHED_TASK
PLAY_FRANKLIN_DIALOGUE("CHOP_RETURN1")
REMOVE_ANIM_DICT("creatures@rottweiler@tricks@")
CHOP_FINISHED_TRICK()
ENDIF
ENDIF
BREAK
CASE CHOP_TASK_SIT_OUTSIDE_SHOP
IF iChopStage = 0
IF IS_PED_IN_GROUP(chopID)
REMOVE_PED_FROM_GROUP(chopID)
ENDIF
REQUEST_ANIM_DICT("creatures@rottweiler@tricks@")
IF HAS_ANIM_DICT_LOADED("creatures@rottweiler@tricks@")
CLEAR_PED_TASKS(chopID)
OPEN_SEQUENCE_TASK(taskSequence)
TASK_TURN_PED_TO_FACE_ENTITY(NULL, PLAYER_PED_ID())
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@tricks@", "sit_enter", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_EXTRACT_INITIAL_OFFSET)
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@tricks@", "sit_loop", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_EXTRACT_INITIAL_OFFSET|AF_LOOPING)
CLOSE_SEQUENCE_TASK(taskSequence)
TASK_PERFORM_SEQUENCE(chopID, taskSequence)
CLEAR_SEQUENCE_TASK(taskSequence)
iChopStage++
ENDIF
ELIF iChopStage = 1
IF NOT IS_PLAYER_IN_ANY_SHOP()
REQUEST_ANIM_DICT("creatures@rottweiler@tricks@")
IF HAS_ANIM_DICT_LOADED("creatures@rottweiler@tricks@")
CLEAR_PED_TASKS(chopID)
TASK_PLAY_ANIM(chopID, "creatures@rottweiler@tricks@", "sit_exit", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_EXTRACT_INITIAL_OFFSET)
PLAY_FRANKLIN_DIALOGUE("CHOP_WALK")
iChopStage++
ENDIF
ENDIF
ELIF iChopStage = 2
IF GET_SCRIPT_TASK_STATUS(chopID, SCRIPT_TASK_PLAY_ANIM) = FINISHED_TASK
REMOVE_ANIM_DICT("creatures@rottweiler@tricks@")
iChopBehaviourNotMovingTimer = GET_GAME_TIMER()
GIVE_CHOP_TASK(CHOP_TASK_WALK)
ENDIF
ENDIF
BREAK
CASE CHOP_TASK_SHIT
IF iChopStage = 0
IF IS_PED_IN_GROUP(chopID)
REMOVE_PED_FROM_GROUP(chopID)
ENDIF
REQUEST_ANIM_DICT("creatures@rottweiler@move")
REQUEST_PTFX_ASSET()
IF HAS_ANIM_DICT_LOADED("creatures@rottweiler@move")
AND HAS_PTFX_ASSET_LOADED()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop doing a shit") #ENDIF
CLEAR_PED_TASKS(chopID)
OPEN_SEQUENCE_TASK(taskSequence)
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@move", "dump_enter", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_EXTRACT_INITIAL_OFFSET)
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@move", "dump_loop", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_EXTRACT_INITIAL_OFFSET)
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@move", "dump_exit", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_EXTRACT_INITIAL_OFFSET)
CLOSE_SEQUENCE_TASK(taskSequence)
TASK_PERFORM_SEQUENCE(chopID, taskSequence)
CLEAR_SEQUENCE_TASK(taskSequence)
PLAY_CHOP_SOUND("AGITATED")
iChopPtfxTimer = GET_GAME_TIMER()
iChopStage++
ENDIF
ELIF iChopStage = 1
IF HAS_CHOP_FINISHED_ANIM_TASK_SEQUENCE()
IF DOES_PARTICLE_FX_LOOPED_EXIST(ptfx_chop)
STOP_PARTICLE_FX_LOOPED(ptfx_chop)
ENDIF
IF IS_PED_IN_TRICK_AREA(chopID, FALSE)
GIVE_CHOP_TASK(CHOP_TASK_WAIT)
ELSE
GIVE_CHOP_TASK(CHOP_TASK_WALK)
ENDIF
ELSE
IF NOT DOES_PARTICLE_FX_LOOPED_EXIST(ptfx_chop)
IF iChopPtfxTimer > -1
AND (GET_GAME_TIMER() - iChopPtfxTimer) > 4000
ptfx_chop = START_PARTICLE_FX_LOOPED_ON_ENTITY("ent_anim_dog_poo", chopID, <<0,-0.15,-0.2>>, <<0,0,0>>)
ENDIF
ELIF (GET_GAME_TIMER() - iChopPtfxTimer) > 10000
STOP_PARTICLE_FX_LOOPED(ptfx_chop)
iChopPtfxTimer = -1
ENDIF
ENDIF
ENDIF
BREAK
CASE CHOP_TASK_PISS
IF iChopStage = 0
REQUEST_PTFX_ASSET()
IF HAS_PTFX_ASSET_LOADED()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop doing a piss") #ENDIF
IF IS_PED_IN_GROUP(chopID)
REMOVE_PED_FROM_GROUP(chopID)
ENDIF
PLAY_CHOP_SOUND("AGITATED")
iChopStage++
ENDIF
ELIF iChopStage = 1
REQUEST_ANIM_DICT("creatures@rottweiler@move")
IF NOT IsPedPerformingTask(chopID, SCRIPT_TASK_FOLLOW_NAV_MESH_TO_COORD)
TASK_FOLLOW_NAV_MESH_TO_COORD(chopID, GET_ENTITY_COORDS(oLamppost), PEDMOVE_RUN, DEFAULT_TIME_BEFORE_WARP, DEFAULT_NAVMESH_RADIUS, ENAV_GO_FAR_AS_POSSIBLE_IF_TARGET_NAVMESH_NOT_LOADED|ENAV_NEVER_ENTER_WATER)
ENDIF
IF GET_DISTANCE_BETWEEN_ENTITY_AND_COORD(chopID, GET_ENTITY_COORDS(oLamppost)) < 2
IF NOT IS_BIT_SET(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_MISBEHAVE)) // B*1382114
AND NOT IS_HELP_MESSAGE_BEING_DISPLAYED()
AND NOT IS_PLAYER_SWITCH_IN_PROGRESS()
SET_BIT(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_MISBEHAVE))
IF bPlayerUsedApp = FALSE
PRINT_HELP("CHOP_H_BEHAVE") // Chop is unhappy so is misbehaving. Download the GTA Chop The Dog app for your iOS, Android, Windows or Playstation device to train him.
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Displaying help text CHOP_H_BEHAVE") #ENDIF
ELSE
PRINT_HELP("CHOP_H_BEHAVA") // Chop is unhappy so is misbehaving. Use the GTA Chop The Dog app to train him.
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Displaying help text CHOP_H_BEHAVA") #ENDIF
ENDIF
ENDIF
CLEAR_PED_TASKS(chopID)
IF HAS_ANIM_DICT_LOADED("creatures@rottweiler@move")
AND GET_ENTITY_SPEED(chopID) < 1
vChopCurrentPositionAtLamppost = GET_ENTITY_COORDS(chopID)
iChopPissDirection = GET_RANDOM_INT_IN_RANGE(0, 2)
fLamppostHeading = GET_HEADING_BETWEEN_VECTORS_2D(vChopCurrentPositionAtLamppost, GET_ENTITY_COORDS(oLamppost))
IF iChopPissDirection = 0 // Turn to left, piss to right
vLamppostOffset = GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS(vChopCurrentPositionAtLamppost, fLamppostHeading, <<-0.5,1,0>>)
ELSE // Turn to right, piss to left
vLamppostOffset = GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS(vChopCurrentPositionAtLamppost, fLamppostHeading, <<0.5,1,0>>)
ENDIF
OPEN_SEQUENCE_TASK(taskSequence)
TASK_TURN_PED_TO_FACE_COORD(NULL, vLamppostOffset)
IF iChopPissDirection = 0
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@move", "pee_right_enter", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_DEFAULT)
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@move", "pee_right_idle", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_DEFAULT)
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@move", "pee_right_exit", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_DEFAULT)
ELSE
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@move", "pee_left_enter", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_DEFAULT)
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@move", "pee_left_idle", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_DEFAULT)
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@move", "pee_left_exit", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_DEFAULT)
ENDIF
CLOSE_SEQUENCE_TASK(taskSequence)
TASK_PERFORM_SEQUENCE(chopID, taskSequence)
CLEAR_SEQUENCE_TASK(taskSequence)
//SET_DEBUG_LINES_AND_SPHERES_DRAWING_ACTIVE(TRUE)
iChopPtfxTimer = GET_GAME_TIMER()
iChopStage++
ENDIF
ENDIF
ELIF iChopStage = 2
//DRAW_DEBUG_SPHERE(vChopCurrentPositionAtLamppost, 0.1)
//DRAW_DEBUG_SPHERE(vLamppostOffset, 0.1)
IF HAS_CHOP_FINISHED_ANIM_TASK_SEQUENCE()
IF DOES_PARTICLE_FX_LOOPED_EXIST(ptfx_chop)
STOP_PARTICLE_FX_LOOPED(ptfx_chop)
ENDIF
PLAY_CHOP_SOUND("BARK")
GIVE_CHOP_TASK(CHOP_TASK_WALK)
ELSE
IF NOT DOES_PARTICLE_FX_LOOPED_EXIST(ptfx_chop)
IF iChopPtfxTimer > -1
AND (GET_GAME_TIMER() - iChopPtfxTimer) > 7000
IF iChopPissDirection = 0
ptfx_chop = START_PARTICLE_FX_LOOPED_ON_ENTITY("ent_anim_dog_peeing", chopID, <<0.1,-0.32,-0.04>>, <<0,0,30>>)
ELSE
ptfx_chop = START_PARTICLE_FX_LOOPED_ON_ENTITY("ent_anim_dog_peeing", chopID, <<-0.1,-0.32,-0.04>>, <<0,0,150>>)
ENDIF
ENDIF
ELIF (GET_GAME_TIMER() - iChopPtfxTimer) > 16000
STOP_PARTICLE_FX_LOOPED(ptfx_chop)
iChopPtfxTimer = -1
ENDIF
ENDIF
ENDIF
BREAK
CASE CHOP_TASK_FETCH
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID(), TRUE)
CLEANUP_BALL(FALSE)
iChopBehaviourNotMovingTimer = GET_GAME_TIMER()
GIVE_CHOP_TASK(CHOP_TASK_WALK)
ELSE
IF iChopStage = 0 // Setup
CLEANUP_BALL(TRUE) // Ensure last eBall has been released
IF GET_PROJECTILE_OF_PROJECTILE_TYPE_WITHIN_DISTANCE(PLAYER_PED_ID(), wThrownWeapon, 50, vBallPosition, eBall)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Grabbed eBall ok") #ENDIF
REGISTER_SCRIPT_IN_COMPLETION_PERCENTAGE_TOTAL(CP_CHOP)
SET_CURRENT_PED_WEAPON(PLAYER_PED_ID(), WEAPONTYPE_UNARMED, TRUE) // B*1252713
bChopRetrievedBall = FALSE
bPlayedFetchBallDialogue = FALSE
iGoToBallTasksGiven = 0
IF IS_PED_IN_GROUP(chopID)
REMOVE_PED_FROM_GROUP(chopID)
ENDIF
iChopFetchDelayTimer = GET_GAME_TIMER()
iChopStage++
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Couldn't grab eBall") #ENDIF
ENDIF
ELIF iChopStage = 1 // Chop going to ball
IF DOES_ENTITY_EXIST(eBall)
IF NOT IS_ENTITY_IN_WATER(eBall)
IF (GET_GAME_TIMER() - iChopFetchDelayTimer) > 500 // B*1501715 Short delay before Chop runs to fetch the ball
IF HAS_CHOP_REACHED_BALL()
REQUEST_ANIM_DICT("creatures@rottweiler@move")
IF HAS_ANIM_DICT_LOADED("creatures@rottweiler@move")
OPEN_SEQUENCE_TASK(taskSequence)
//TASK_TURN_PED_TO_FACE_ENTITY(NULL, eBall) This makes Chop take a few seconds turning to the ball - looks a bit forced
IF wThrownWeapon = WEAPONTYPE_BALL
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@move", "fetch_pickup", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_TAG_SYNC_IN|AF_TAG_SYNC_OUT)
ENDIF
TASK_GO_TO_ENTITY(NULL, PLAYER_PED_ID(), DEFAULT_TIME_BEFORE_WARP, 4, PEDMOVE_SPRINT)
CLOSE_SEQUENCE_TASK(taskSequence)
TASK_PERFORM_SEQUENCE(chopID, taskSequence)
CLEAR_SEQUENCE_TASK(taskSequence)
IF wThrownWeapon = WEAPONTYPE_BALL
bChopRetrievedBall = TRUE
ENDIF
iChopStage++
ENDIF
ELIF NOT IsPedPerformingTask(chopID, SCRIPT_TASK_GO_TO_ENTITY)
TASK_GO_TO_ENTITY(chopID, eBall, 30000, DEFAULT_SEEK_RADIUS, PEDMOVE_SPRINT)
iGoToBallTasksGiven++
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop given TASK_GO_TO_ENTITY to eBall, iGoToBallTasksGiven = ", iGoToBallTasksGiven) #ENDIF
iNavmeshAttempts = 0
IF iGoToBallTasksGiven > 3
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: TASK_GO_TO_ENTITY can't reach the ball so do GIVE_CHOP_TASK(CHOP_TASK_WALK)") #ENDIF
TASK_GO_TO_ENTITY(chopID, PLAYER_PED_ID(), DEFAULT_TIME_BEFORE_WARP, 5, PEDMOVE_SPRINT)
CLEANUP_BALL(TRUE)
iChopStage++
ENDIF
ELSE
nrr_chop_route = GET_NAVMESH_ROUTE_DISTANCE_REMAINING(chopID, f_chop_route, i_chop_route)
//#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: nrr_chop_route = ", nrr_chop_route) #ENDIF
IF nrr_chop_route = NAVMESHROUTE_ROUTE_NOT_FOUND
fBallZHeight = GET_ENTITY_HEIGHT_ABOVE_GROUND(eBall)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: fBallZHeight = ", fBallZHeight) #ENDIF
IF fBallZHeight < 1 // B*1389305 Don't increase while waiting for the ball to land
iNavmeshAttempts++
ENDIF
ELIF nrr_chop_route = NAVMESHROUTE_ROUTE_FOUND
IF bPlayedFetchBallDialogue = FALSE
PLAY_CHOP_SOUND("BARK")
IF wThrownWeapon = WEAPONTYPE_BALL // B*1404094 Don't play this dialogue if throwing something other than the ball
PLAY_FRANKLIN_DIALOGUE("CHOP_FETCH")
ENDIF
bPlayedFetchBallDialogue = TRUE
ENDIF
ENDIF
//#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Navmesh task = ", nrr_chop_route, " iNavmeshAttempts = ", iNavmeshAttempts, " dist = ", f_chop_route, " last section = ", i_chop_route) #ENDIF
IF iNavmeshAttempts > 9
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: nrr_chop_route = NAVMESHROUTE_ROUTE_NOT_FOUND after 10 attempts so do GIVE_CHOP_TASK(CHOP_TASK_WALK)") #ENDIF
TASK_GO_TO_ENTITY(chopID, PLAYER_PED_ID(), DEFAULT_TIME_BEFORE_WARP, 5, PEDMOVE_SPRINT)
CLEANUP_BALL(TRUE)
iChopStage++
ENDIF
ENDIF
ENDIF
ELSE // B*1475993 - Make Chop give up if the ball goes into water
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: eBall is in water") #ENDIF
TASK_GO_TO_ENTITY(chopID, PLAYER_PED_ID(), DEFAULT_TIME_BEFORE_WARP, 5, PEDMOVE_SPRINT)
iChopStage++
ENDIF
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: eBall no longer exists") #ENDIF
TASK_GO_TO_ENTITY(chopID, PLAYER_PED_ID(), DEFAULT_TIME_BEFORE_WARP, 5, PEDMOVE_SPRINT)
iChopStage++
ENDIF
ELIF iChopStage = 2 // Chop picking up ball
IF bChopRetrievedBall = TRUE
IF DOES_ENTITY_EXIST(eBall)
AND IS_ENTITY_PLAYING_ANIM(chopID, "creatures@rottweiler@move", "fetch_pickup")
AND GET_ENTITY_ANIM_CURRENT_TIME(chopID, "creatures@rottweiler@move", "fetch_pickup") > 0.25
ATTACH_ENTITY_TO_ENTITY(eBall, chopID, 28, <<0.2042, 0.0, -0.0608>>, <<0,0,0>>)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Attached eBall to Chop") #ENDIF
SET_AUDIO_FLAG("DisableBarks", TRUE) // B*1342696 - Prevent Chop barking when carrying the ball
iChopStage++
ENDIF
ELSE
iChopStage++
ENDIF
ELIF iChopStage = 3 // Chop returning to Franklin
IF GET_DISTANCE_BETWEEN_ENTITIES(PLAYER_PED_ID(), chopID) < 5
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop returned to player") #ENDIF
IF wThrownWeapon = WEAPONTYPE_BALL
IF bChopRetrievedBall = TRUE
REQUEST_ANIM_DICT("creatures@rottweiler@move")
IF HAS_ANIM_DICT_LOADED("creatures@rottweiler@move")
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop retrieved ball so playing drop anim") #ENDIF
OPEN_SEQUENCE_TASK(taskSequence)
TASK_TURN_PED_TO_FACE_ENTITY(NULL, PLAYER_PED_ID())
TASK_PLAY_ANIM(NULL, "creatures@rottweiler@move", "fetch_drop", NORMAL_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_TAG_SYNC_IN)
CLOSE_SEQUENCE_TASK(taskSequence)
TASK_PERFORM_SEQUENCE(chopID, taskSequence)
CLEAR_SEQUENCE_TASK(taskSequence)
PLAY_FRANKLIN_DIALOGUE("CHOP_RETURN1")
SET_AUDIO_FLAG("DisableBarks", FALSE)
iChopStage++
ENDIF
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop didn't retrieve the ball") #ENDIF
PLAY_CHOP_SOUND("BREATH_AGITATED")
PLAY_FRANKLIN_DIALOGUE("CHOP_RETURN2")
GIVE_CHOP_TASK(CHOP_TASK_WALK)
ENDIF
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop returned after player threw a harmful weapon for Chop to chase") #ENDIF
PLAY_CHOP_SOUND("BARK_WHINE")
CLEANUP_BALL(TRUE)
GIVE_CHOP_TASK(CHOP_TASK_WALK)
ENDIF
ENDIF
ELIF iChopStage = 4 // Chop playing drop ball anim
IF NOT IsPedPerformingTask(chopID, SCRIPT_TASK_PERFORM_SEQUENCE) // Chop has finished his tasks
AND NOT DOES_ENTITY_EXIST(eBall)
IF IS_PHONE_ONSCREEN() // B*1433223
GIVE_BALL_TO_PLAYER(FALSE, TRUE)
ELIF GET_FOLLOW_PED_CAM_VIEW_MODE() = CAM_VIEW_MODE_FIRST_PERSON
GIVE_BALL_TO_PLAYER(FALSE, TRUE, TRUE)
ELSE
GIVE_BALL_TO_PLAYER(TRUE, TRUE, TRUE)
ENDIF
iChopBehaviourNotMovingTimer = GET_GAME_TIMER()
GIVE_CHOP_TASK(CHOP_TASK_WALK)
ELIF DOES_ENTITY_EXIST(eBall)
IF IS_ENTITY_ATTACHED(eBall)
IF IS_ENTITY_PLAYING_ANIM(chopID, "creatures@rottweiler@move", "fetch_drop")
AND GET_ENTITY_ANIM_CURRENT_TIME(chopID, "creatures@rottweiler@move", "fetch_drop") > 0.4
DETACH_ENTITY(eBall)
ENDIF
ELSE
IF GET_DISTANCE_BETWEEN_ENTITIES(PLAYER_PED_ID(), eBall) < 1.5
OR GET_DISTANCE_BETWEEN_ENTITIES(PLAYER_PED_ID(), eBall) > 20.0
OR IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
CLEANUP_BALL(TRUE)
ENDIF
IF NOT IsPedPerformingTask(chopID, SCRIPT_TASK_PERFORM_SEQUENCE) // Check here cos we need to wait for the player to collect the ball, so ensure Chop gets put in the player group if the player messes around
PUT_CHOP_IN_PLAYER_GROUP()
ENDIF
ENDIF
ENDIF
ENDIF
HANDLE_FRANKLIN_WHISTLING_TO_CHOP()
ENDIF
BREAK
CASE CHOP_TASK_CABLE_CAR
IF iChopStage = 0 // Setup
REQUEST_ANIM_DICT("creatures@rottweiler@in_vehicle@std_car")
IF HAS_ANIM_DICT_LOADED("creatures@rottweiler@in_vehicle@std_car")
IF IS_PED_IN_GROUP(chopID)
REMOVE_PED_FROM_GROUP(chopID)
ENDIF
ATTACH_ENTITY_TO_ENTITY(chopID, GET_ENTITY_ATTACHED_TO(PLAYER_PED_ID()), 0, <<0,0,-6.1>>, <<0,0,0>>, TRUE)
TASK_PLAY_ANIM(chopID, "creatures@rottweiler@in_vehicle@std_car", "sit", INSTANT_BLEND_IN, INSTANT_BLEND_OUT, -1, AF_LOOPING|AF_NOT_INTERRUPTABLE)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Set Chop into Franklin's cablecar") #ENDIF
iChopStage++
ENDIF
ELIF iChopStage = 1 // Wait until the cable car script warps Franklin back outside at the end
IF NOT IS_ENTITY_ATTACHED(PLAYER_PED_ID())
FLOAT f_closest_dist, f_current_dist
INT i_index, i_closest
f_closest_dist = 99999
REPEAT NUM_CHOP_CABLE_CAR_POSITIONS i_index // Work out the closest position to Franklin so we can warp Chop there
f_current_dist = GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(PLAYER_PED_ID()), vChopCableCarPos[i_index])
IF f_current_dist < f_closest_dist
f_closest_dist = f_current_dist
i_closest = i_index
ENDIF
ENDREPEAT
IF IS_ENTITY_ATTACHED(chopID)
DETACH_ENTITY(chopID)
ENDIF
SAFE_TELEPORT_ENTITY(chopID, vChopCableCarPos[i_closest], fChopCableCarPos[i_closest])
FORCE_PED_AI_AND_ANIMATION_UPDATE(chopID)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Warped Chop to cablecar position ", i_closest) #ENDIF
iChopBehaviourNotMovingTimer = GET_GAME_TIMER()
GIVE_CHOP_TASK(CHOP_TASK_WALK)
ENDIF
ENDIF
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Returns TRUE if any of the Chop menus are currently being displayed.
FUNC BOOL IS_CHOP_MENU_ONSCREEN()
IF IS_HELP_MESSAGE_BEING_DISPLAYED() // Do a quick check first
IF IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_0")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_1")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_2")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_3")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_4")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_H1")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_H2")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_H3")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_H4")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_HOME")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_0")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_1")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_2")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_3")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_4")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_H1")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_H2")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_H3")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_H4")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_NOAPP")
RETURN TRUE
ENDIF
ENDIF
IF IS_PC_VERSION()
IF IS_HELP_MESSAGE_BEING_DISPLAYED() // Do a quick check first
IF IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_0_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_1_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_2_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_3_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_4_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_H1_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_H2_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_H3_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WAIT_H4_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_HOME_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_0_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_1_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_2_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_3_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_4_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_H1_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_H2_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_H3_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_WALK_H4_KM")
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_NOAPP_KM")
RETURN TRUE
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Handles Chop attack any peds who are fighting with Franklin.
PROC MANAGE_CHOP_ATTACKING_PEDS_FIGHTING_FRANKLIN()
IF eChopTask != CHOP_TASK_ATTACK
AND chopInVehicleStatus = CHOP_VEHICLE_STATUS_FRANKLIN_NOT_IN_VEHICLE
AND NOT IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID(), TRUE)
IF eChopTask = CHOP_TASK_WALK // See B*1001112 - Chop should only join in with fights if the player has him following or sniffing (perhaps fetch too?)
OR eChopTask = CHOP_TASK_SNIFF
OR eChopTask = CHOP_TASK_FETCH // B*1453857 and when fetching ball
OR (eChopTask = CHOP_TASK_PISS AND NOT IsPedPerformingTask(chopID, SCRIPT_TASK_PERFORM_SEQUENCE)) // Chop on his way to have a piss
IF COUNT_PEDS_IN_COMBAT_WITH_TARGET_WITHIN_RADIUS(PLAYER_PED_ID(), GET_ENTITY_COORDS(PLAYER_PED_ID()), CHOP_ATTACK_RANGE) > 0
OR COUNT_PEDS_IN_COMBAT_WITH_TARGET_WITHIN_RADIUS(chopID, GET_ENTITY_COORDS(chopID), CHOP_ATTACK_RANGE) > 0
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: COUNT_PEDS_IN_COMBAT_WITH_TARGET_WITHIN_RADIUS > 0 so Chop needs to go into attack mode") #ENDIF
REQUEST_ANIM_DICT("creatures@rottweiler@melee@streamed_taunts@") // B*1555787 Ensure taunts are preloaded
INT i_ped
PED_INDEX temp_ped[5], attackerID
REPEAT GET_PED_NEARBY_PEDS(PLAYER_PED_ID(), temp_ped) i_ped
IF NOT IS_ENTITY_ALIVE(attackerID)
AND IS_ENTITY_ALIVE(temp_ped[i_ped])
AND (IS_PED_IN_COMBAT(temp_ped[i_ped], PLAYER_PED_ID()) OR IS_PED_IN_COMBAT(temp_ped[i_ped], chopID))
AND NOT IS_PED_IN_ANY_VEHICLE(temp_ped[i_ped]) // Chop can't attack peds in cars
AND temp_ped[i_ped] != chopID // Chop can't attack himself
attackerID = temp_ped[i_ped]
ENDIF
ENDREPEAT
IF IS_ENTITY_ALIVE(attackerID)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: SICK BALLS! Found an enemy for Chop to attack") #ENDIF
TASK_COMBAT_PED(chopID, attackerID)
PLAY_FRANKLIN_DIALOGUE("CHOP_ATTACK2")
IF NOT IS_HELP_MESSAGE_BEING_DISPLAYED()
AND NOT IS_BIT_SET(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_ATTACK_ENEMIES))
AND NOT IS_PLAYER_UNARMED_AND_AIMING_AT_CHOP()
AND NOT IS_PLAYER_SWITCH_IN_PROGRESS()
SET_BIT(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_ATTACK_ENEMIES))
PRINT_HELP("CHOP_H_ATTACK") // "Chop will attack anyone who attacks Franklin."
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Displaying help text CHOP_H_ATTACK") #ENDIF
ENDIF
CLEANUP_BALL(FALSE)
GIVE_CHOP_TASK(CHOP_TASK_ATTACK, FALSE)
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Couldn't find an enemy for Chop to attack") #ENDIF
ENDIF
ELSE
weapon_type wep_current
GET_CURRENT_PED_WEAPON(PLAYER_PED_ID(), wep_current)
IF wep_current != WEAPONTYPE_BALL // B*1434236
AND NOT (eChopTask = CHOP_TASK_FETCH AND iChopStage = 1) // B*1434236 Immediately after throwing the ball the player will be unarmed so don't check while Chop is running to the ball
ENTITY_INDEX target_entity
IF GET_PLAYER_TARGET_ENTITY(PLAYER_ID(), target_entity)
OR GET_ENTITY_PLAYER_IS_FREE_AIMING_AT(PLAYER_ID(), target_entity)
IF IS_ENTITY_A_PED(target_entity)
PED_INDEX attackerID = GET_PED_INDEX_FROM_ENTITY_INDEX(target_entity)
IF NOT IS_PED_INJURED(attackerID)
AND attackerID != chopID // Chop can't attack himself
AND NOT IS_PED_IN_ANY_VEHICLE(attackerID) // Don't send Chop to attack peds in cars
AND GET_DISTANCE_BETWEEN_ENTITIES(PLAYER_PED_ID(), attackerID) < 50 // Don't send Chop to attack targets in the player's crosshair that are far away
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player aiming at a ped so sending Chop to attack") #ENDIF
TASK_COMBAT_PED(chopID, attackerID)
iChopSicBallsMeter++
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: iChopSicBallsMeter increased to ", iChopSicBallsMeter, ", CHOP_SIC_BALLS_CALL_COPS_LIMIT is ", CHOP_SIC_BALLS_CALL_COPS_LIMIT) #ENDIF
IF iChopSicBallsMeter >= CHOP_SIC_BALLS_CALL_COPS_LIMIT
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: iChopSicBallsMeter >= CHOP_SIC_BALLS_CALL_COPS_LIMIT so giving player a wanted level") #ENDIF
SET_WANTED_LEVEL_DIFFICULTY(PLAYER_ID(), 0)
SET_PLAYER_WANTED_LEVEL_NO_DROP(PLAYER_ID(), 2)
ENDIF
PLAY_FRANKLIN_DIALOGUE("CHOP_ATTACK1")
IF NOT IS_HELP_MESSAGE_BEING_DISPLAYED()
AND NOT IS_BIT_SET(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_ATTACK_AIM))
AND NOT IS_PLAYER_UNARMED_AND_AIMING_AT_CHOP()
AND NOT IS_PLAYER_SWITCH_IN_PROGRESS()
SET_BIT(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_ATTACK_AIM))
PRINT_HELP("CHOP_H_AIM") // "Chop will attack anyone who Franklin targets."
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Displaying help text CHOP_H_AIM") #ENDIF
ENDIF
CLEANUP_BALL(FALSE)
GIVE_CHOP_TASK(CHOP_TASK_ATTACK, FALSE)
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
IF (GET_GAME_TIMER() - iChopSicBallsTimer) > CHOP_SIC_BALLS_COOLDOWN_TIMER // B*1431784
iChopSicBallsTimer = GET_GAME_TIMER()
IF iChopSicBallsMeter > 0
iChopSicBallsMeter--
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: iChopSicBallsMeter decreased to ", iChopSicBallsMeter, ", CHOP_SIC_BALLS_CALL_COPS_LIMIT is ", CHOP_SIC_BALLS_CALL_COPS_LIMIT) #ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// The player pressed dpad right to remove the menu or gave an order to Chop.
PROC PLAYER_REMOVED_MENU(BOOL b_stop_hint = TRUE)
IF IS_CHOP_MENU_ONSCREEN()
CLEAR_HELP()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Cleared menu") #ENDIF
ENDIF
IF b_stop_hint = TRUE
AND IS_GAMEPLAY_HINT_ACTIVE()
STOP_GAMEPLAY_HINT()
ENDIF
ENDPROC
/// PURPOSE:
/// Handles displaying the initial help text for how to interact with Chop.
PROC DISPLAY_INITIAL_CHOP_INTERACTION_HELP_TEXT()
IF NOT IS_BIT_SET(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_INTERACT_WITH_CHOP))
AND NOT (IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_INTRO") OR (IS_PC_VERSION() AND IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_INTRO_KM")) )
//B* 2005474: Don't clear help forever if the player's trying to take a snap of Chop for Wildlife Photo.
AND NOT IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("PW_HELP_1")
AND NOT IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("PW_HELP_2")
AND NOT Is_Player_Timetable_Scene_In_Progress() // Required to prevent B*1125295
AND NOT IS_PLAYER_SWITCH_IN_PROGRESS()
AND NOT IS_PHONE_ONSCREEN() // B*2013959 Prevent interacting with Chop when the phone is onscreen
AND NOT IS_CUSTOM_MENU_ON_SCREEN()
AND NOT IS_BROWSER_OPEN() // B*2121820 Prevent interacting with Chop when the internet browser in onscreen
IF eChopTask = CHOP_TASK_WAIT
OR eChopTask = CHOP_TASK_WAIT_PLAY_ANIM
OR eChopTask = CHOP_TASK_GREET_FRANKLIN
IF IS_PED_IN_TRICK_AREA(PLAYER_PED_ID())
CLEAR_HELP()
IF IS_USING_KEYBOARD_AND_MOUSE(PLAYER_CONTROL)
PRINT_HELP_FOREVER("CHOP_H_INTRO_KM") // Hold ~INPUT_AIM~ when unarmed to interact with Chop. // Using INPUT_AIM to fix B*1499642
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Displaying help text CHOP_H_INTRO_KM") #ENDIF
ELSE
PRINT_HELP_FOREVER("CHOP_H_INTRO") // Hold ~INPUT_AIM~ when unarmed to interact with Chop. // Using INPUT_AIM to fix B*1499642
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Displaying help text CHOP_H_INTRO") #ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
IF (IS_PC_VERSION() AND IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_INTRO_KM"))
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_INTRO")
IF Is_Player_Timetable_Scene_In_Progress()
OR IS_PLAYER_SWITCH_IN_PROGRESS()
OR IS_PHONE_ONSCREEN()
OR IS_CUSTOM_MENU_ON_SCREEN()
OR IS_BROWSER_OPEN()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: CHOP_H_INTRO or CHOP_H_INTRO_KM was onscreen but cleared it because the player can't now interact with Chop") #ENDIF
CLEAR_HELP()
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Returns TRUE if the player is close enough to Chop to give orders.
FUNC BOOL IS_PLAYER_WITHIN_CHOP_ORDER_RADIUS()
IF GET_DISTANCE_BETWEEN_PEDS(PLAYER_PED_ID(), chopID) < 10.0
IF eChopTask = CHOP_TASK_FETCH AND iChopStage < 4
// Chop hasn't yet returned ball so don't allow giving a new order
ELSE
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID(), TRUE)
ALLOW_PLAYER_TO_TARGET_CHOP(FALSE)
FRANKLIN_SHOULD_LOOK_AT_CHOP(FALSE)
RETURN FALSE
ELSE
DISPLAY_INITIAL_CHOP_INTERACTION_HELP_TEXT()
ALLOW_PLAYER_TO_TARGET_CHOP(TRUE)
FRANKLIN_SHOULD_LOOK_AT_CHOP(TRUE)
RETURN TRUE
ENDIF
ENDIF
ENDIF
ALLOW_PLAYER_TO_TARGET_CHOP(FALSE)
FRANKLIN_SHOULD_LOOK_AT_CHOP(FALSE)
IF IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_INTRO") // B*946872 - remove this help text if the player leaves Chop without interacting with him
OR (IS_PC_VERSION() AND IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("CHOP_H_INTRO_KM"))
CLEAR_HELP()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Cleared help text CHOP_H_INTRO/CHOP_H_INTRO_KM") #ENDIF
ENDIF
RETURN FALSE // More than 10m away
ENDFUNC
/// PURPOSE:
/// Returns true if Chop isn't already performing a trick.
FUNC BOOL CAN_CHOP_PERFORM_TRICK()
IF eChopTask = CHOP_TASK_SIT
OR eChopTask = CHOP_TASK_BEG
OR eChopTask = CHOP_TASK_PAW
OR eChopTask = CHOP_TASK_PET
RETURN FALSE
ENDIF
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Checks if we allow the menu to appear during the current Chop task.
FUNC BOOL SHOULD_MENU_BE_DISPLAYED_DURING_CURRENT_CHOP_TASK()
IF eChopTask = CHOP_TASK_SIT_OUTSIDE_SHOP // Don't allow menu if player is inside a shop
RETURN FALSE
ENDIF
IF eChopTask = CHOP_TASK_SHIT
OR (eChopTask = CHOP_TASK_PISS AND IsPedPerformingTask(chopID, SCRIPT_TASK_PERFORM_SEQUENCE))
IF bPlayedChopBusyDialogue = FALSE
AND IS_CONTROL_PRESSED(FRONTEND_CONTROL, INPUT_SCRIPT_LT)
PLAY_FRANKLIN_DIALOGUE("CHOP_BUSY")
bPlayedChopBusyDialogue = TRUE
ENDIF
RETURN FALSE
ENDIF
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Handles the player giving orders to Chop.
PROC MANAGE_PLAYER_GIVING_ORDERS_TO_CHOP()
IF IS_PLAYER_WITHIN_CHOP_ORDER_RADIUS()
AND SHOULD_MENU_BE_DISPLAYED_DURING_CURRENT_CHOP_TASK()
AND NOT IS_CUSTOM_MENU_ON_SCREEN() // B*1812064 Prevent interacting with Chop when a custom menu is onscreen
AND eNextChopTask = CHOP_TASK_NONE // Don't allow interaction if the player has given a task and Chop is in the process of doing it
AND eChopTask != CHOP_TASK_ATTACK // Don't allow interaction if Chop is in the process of attacking someone
AND IS_PLAYER_UNARMED_AND_AIMING_AT_CHOP()
AND NOT IS_PLAYER_SWITCH_IN_PROGRESS() // Fix for B*791889 - don't allow performing tasks if player is currently switching character
AND NOT IS_PED_IN_COMBAT(chopID)
AND NOT IS_PLAYER_IN_RESTRICTED_INTERIOR()
AND NOT IS_PLAYER_IN_ANY_SHOP()
AND NOT IS_PHONE_ONSCREEN() // B*2013959 Prevent interacting with Chop when the phone is onscreen
AND NOT IS_BROWSER_OPEN() // B*2121820 Prevent interacting with Chop when the internet browser in onscreen
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_CHARACTER_WHEEL)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_COVER)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_NEXT_WEAPON)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_PREV_WEAPON)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_SELECT_WEAPON) // B*1413360 Prevent weapon wheel when menu is onscreen
IF bOverrideHappiness = FALSE
DRAW_GENERIC_METER(FLOOR(g_savedGlobals.sSocialData.sDogAppData.fHappiness), 100, "CHOP_H_HAPPY", HUD_COLOUR_WHITE, -1, HUDORDER_DONTCARE, -1.0, -1.0, FALSE, TRUE, HUDFLASHING_NONE, 0, TRUE)
ELSE
DRAW_GENERIC_METER(100, 100, "CHOP_H_HAPPY", HUD_COLOUR_WHITE, -1, HUDORDER_DONTCARE, -1.0, -1.0, FALSE, TRUE, HUDFLASHING_NONE, 0, TRUE)
ENDIF
IF IS_CHOP_MENU_ONSCREEN()
IF g_bPlayerInteractingWithChop = FALSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Setting g_bPlayerInteractingWithChop to true") #ENDIF
g_bPlayerInteractingWithChop = TRUE
ENDIF
IF IS_GAMEPLAY_HINT_ACTIVE()
STOP_GAMEPLAY_HINT_BEING_CANCELLED_THIS_UPDATE(TRUE)
ELSE
SET_GAMEPLAY_ENTITY_HINT(chopID, <<0,0,0>>, TRUE, -1, 1000, 1000, HINTTYPE_CHOP_HINT_HELPER)
bHintCamAlreadyACtive = FALSE
ENDIF
SET_INPUT_EXCLUSIVE(GET_CHOP_INPUT_CONTROL(), GET_CHOP_INPUT_ACTION(CHOP_INPUT_ENGAGE))
SET_INPUT_EXCLUSIVE(GET_CHOP_INPUT_CONTROL(), GET_CHOP_INPUT_ACTION(CHOP_INPUT_SEARCH))
SET_INPUT_EXCLUSIVE(GET_CHOP_INPUT_CONTROL(), GET_CHOP_INPUT_ACTION(CHOP_INPUT_SIT))
SET_INPUT_EXCLUSIVE(GET_CHOP_INPUT_CONTROL(), GET_CHOP_INPUT_ACTION(CHOP_INPUT_PAW)) // B*1491983 Just disable these regardless of whether tricks have been unlocked if the menu is onscreen
SET_INPUT_EXCLUSIVE(GET_CHOP_INPUT_CONTROL(), GET_CHOP_INPUT_ACTION(CHOP_INPUT_BEG))
SET_INPUT_EXCLUSIVE(GET_CHOP_INPUT_CONTROL(), GET_CHOP_INPUT_ACTION(CHOP_INPUT_PET))
IF IS_USING_KEYBOARD_AND_MOUSE(PLAYER_CONTROL)
DISABLE_CONTROL_ACTION(GET_CHOP_INPUT_CONTROL(), GET_CHOP_INPUT_ACTION(CHOP_INPUT_ENGAGE))
DISABLE_CONTROL_ACTION(GET_CHOP_INPUT_CONTROL(), GET_CHOP_INPUT_ACTION(CHOP_INPUT_SEARCH))
DISABLE_CONTROL_ACTION(GET_CHOP_INPUT_CONTROL(), GET_CHOP_INPUT_ACTION(CHOP_INPUT_SIT))
DISABLE_CONTROL_ACTION(GET_CHOP_INPUT_CONTROL(), GET_CHOP_INPUT_ACTION(CHOP_INPUT_PAW))
DISABLE_CONTROL_ACTION(GET_CHOP_INPUT_CONTROL(), GET_CHOP_INPUT_ACTION(CHOP_INPUT_BEG))
DISABLE_CONTROL_ACTION(GET_CHOP_INPUT_CONTROL(), GET_CHOP_INPUT_ACTION(CHOP_INPUT_PET))
ENDIF
ENABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_AIM) // B*1397364 Re-enable aim to allow maintaining locking onto Chop
IF IS_DISABLED_CONTROL_JUST_RELEASED(GET_CHOP_INPUT_CONTROL(), GET_CHOP_INPUT_ACTION(CHOP_INPUT_ENGAGE))
AND CAN_CHOP_PERFORM_TRICK()
SWITCH eChopTask
CASE CHOP_TASK_WAIT
CASE CHOP_TASK_RETURN
CASE CHOP_TASK_WANDER
bPlayerChoseToWalkChop = TRUE
PLAYER_REMOVED_MENU(FALSE)
PLAY_CHOP_SOUND("PLAYFUL")
PLAY_FRANKLIN_DIALOGUE("CHOP_WALK")
GIVE_CHOP_TASK(CHOP_TASK_WALK)
BREAK
CASE CHOP_TASK_WALK
CASE CHOP_TASK_SNIFF
CASE CHOP_TASK_FETCH
bPlayerChoseToWalkChop = FALSE
PLAYER_REMOVED_MENU(FALSE)
GIVE_CHOP_TASK(CHOP_TASK_RETURN)
BREAK
CASE CHOP_TASK_WAIT_PLAY_ANIM
CASE CHOP_TASK_GREET_FRANKLIN
bPlayerChoseToWalkChop = TRUE
PLAYER_REMOVED_MENU(FALSE)
PLAY_FRANKLIN_DIALOGUE("CHOP_WALK")
eNextChopTask = CHOP_TASK_WALK
BREAK
ENDSWITCH
IF bPlayerChoseToWalkChop = TRUE
AND STAT_GET_INT(SP_CHOP_WALK_DONE, iNumTimesChopTakenForWalk)
iNumTimesChopTakenForWalk++
STAT_SET_INT(SP_CHOP_WALK_DONE, iNumTimesChopTakenForWalk) // B*1545868
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: SP_CHOP_WALK_DONE incremented to ", iNumTimesChopTakenForWalk) #ENDIF
ENDIF
ENDIF
IF bPlayerUsedApp = TRUE
IF IS_DISABLED_CONTROL_JUST_PRESSED(GET_CHOP_INPUT_CONTROL(), GET_CHOP_INPUT_ACTION(CHOP_INPUT_SEARCH)) // Sniff out nearest hidden package or weapon pickup
AND CAN_CHOP_PERFORM_TRICK()
IF eChopTask = CHOP_TASK_WAIT_PLAY_ANIM
OR eChopTask = CHOP_TASK_GREET_FRANKLIN
eNextChopTask = CHOP_TASK_SNIFF
ELSE
GIVE_CHOP_TASK(CHOP_TASK_SNIFF)
ENDIF
ENDIF
IF GET_CHOP_BEHAVIOUR_FROM_APP() = CHOP_BEHAVIOUR_BAD // B*1382082 Chop won't do tricks if he's unhappy
AND bOverrideHappiness = FALSE
IF (GET_CHOP_TRAINING_LEVEL_FROM_APP() >= 1)
IF IS_DISABLED_CONTROL_JUST_PRESSED(GET_CHOP_INPUT_CONTROL(), GET_CHOP_INPUT_ACTION(CHOP_INPUT_SIT))
PLAY_FRANKLIN_DIALOGUE("CHOP_CANT")
ENDIF
ENDIF
IF (GET_CHOP_TRAINING_LEVEL_FROM_APP() >= 2)
IF IS_DISABLED_CONTROL_JUST_PRESSED(GET_CHOP_INPUT_CONTROL(), GET_CHOP_INPUT_ACTION(CHOP_INPUT_PAW))
PLAY_FRANKLIN_DIALOGUE("CHOP_CANT")
ENDIF
ENDIF
IF (GET_CHOP_TRAINING_LEVEL_FROM_APP() >= 3)
IF IS_DISABLED_CONTROL_JUST_PRESSED(GET_CHOP_INPUT_CONTROL(), GET_CHOP_INPUT_ACTION(CHOP_INPUT_BEG))
PLAY_FRANKLIN_DIALOGUE("CHOP_CANT")
ENDIF
ENDIF
IF (GET_CHOP_TRAINING_LEVEL_FROM_APP() >= 4)
IF IS_DISABLED_CONTROL_JUST_PRESSED(GET_CHOP_INPUT_CONTROL(), GET_CHOP_INPUT_ACTION(CHOP_INPUT_PET))
PLAY_FRANKLIN_DIALOGUE("CHOP_CANT")
ENDIF
ENDIF
ELSE
IF (GET_CHOP_TRAINING_LEVEL_FROM_APP() >= 1)
IF IS_DISABLED_CONTROL_JUST_PRESSED(GET_CHOP_INPUT_CONTROL(), GET_CHOP_INPUT_ACTION(CHOP_INPUT_SIT))
AND CAN_CHOP_PERFORM_TRICK()
PLAY_FRANKLIN_DIALOGUE("CHOP_SIT")
IF eChopTask = CHOP_TASK_WAIT_PLAY_ANIM
OR eChopTask = CHOP_TASK_GREET_FRANKLIN
eNextChopTask = CHOP_TASK_SIT
ELSE
GIVE_CHOP_TASK(CHOP_TASK_SIT)
ENDIF
ENDIF
ENDIF
IF (GET_CHOP_TRAINING_LEVEL_FROM_APP() >= 2)
IF IS_DISABLED_CONTROL_JUST_PRESSED(GET_CHOP_INPUT_CONTROL(), GET_CHOP_INPUT_ACTION(CHOP_INPUT_PAW))
AND CAN_CHOP_PERFORM_TRICK()
PLAY_FRANKLIN_DIALOGUE("CHOP_PAW")
IF eChopTask = CHOP_TASK_WAIT_PLAY_ANIM
OR eChopTask = CHOP_TASK_GREET_FRANKLIN
eNextChopTask = CHOP_TASK_PAW
ELSE
GIVE_CHOP_TASK(CHOP_TASK_PAW)
ENDIF
ENDIF
ENDIF
IF (GET_CHOP_TRAINING_LEVEL_FROM_APP() >= 3)
IF IS_DISABLED_CONTROL_JUST_PRESSED(GET_CHOP_INPUT_CONTROL(), GET_CHOP_INPUT_ACTION(CHOP_INPUT_BEG))
AND CAN_CHOP_PERFORM_TRICK()
PLAY_FRANKLIN_DIALOGUE("CHOP_BEG")
IF eChopTask = CHOP_TASK_WAIT_PLAY_ANIM
OR eChopTask = CHOP_TASK_GREET_FRANKLIN
eNextChopTask = CHOP_TASK_BEG
ELSE
GIVE_CHOP_TASK(CHOP_TASK_BEG)
ENDIF
ENDIF
ENDIF
IF (GET_CHOP_TRAINING_LEVEL_FROM_APP() >= 4)
IF IS_DISABLED_CONTROL_JUST_PRESSED(GET_CHOP_INPUT_CONTROL(), GET_CHOP_INPUT_ACTION(CHOP_INPUT_PET))
AND CAN_CHOP_PERFORM_TRICK()
PLAY_FRANKLIN_DIALOGUE("CHOP_PET")
IF eChopTask = CHOP_TASK_WAIT_PLAY_ANIM
OR eChopTask = CHOP_TASK_GREET_FRANKLIN
eNextChopTask = CHOP_TASK_PET
ELSE
GIVE_CHOP_TASK(CHOP_TASK_PET)
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ELSE
CLEAR_HELP()
IF NOT IS_BIT_SET(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_INTERACT_WITH_CHOP))
SET_BIT(g_savedGlobals.sAmbient.iChopHintsDisplayed, ENUM_TO_INT(CHOP_HELP_INTERACT_WITH_CHOP)) // Only set this the first time the player interacts with Chop
ENDIF
IF bPlayerUsedApp = FALSE // B*1339036 - If this is set to false re-check whenever the player holds left trigger in case he's used the app while this Chop script is active
bPlayerUsedApp = HAS_PLAYER_USED_CHOP_APP()
ENDIF
IF bPlayerUsedApp = TRUE
INT i_chop_training_level = GET_CHOP_TRAINING_LEVEL_FROM_APP()
IF i_chop_training_level > 4
i_chop_training_level = 4
ENDIF
IF (eChopTask = CHOP_TASK_WAIT)
OR (eChopTask = CHOP_TASK_WAIT_PLAY_ANIM)
OR (eChopTask = CHOP_TASK_GREET_FRANKLIN)
OR (eChopTask = CHOP_TASK_RETURN)
OR (eChopTask = CHOP_TASK_WANDER)
IF GET_CHOP_BEHAVIOUR_FROM_APP() = CHOP_BEHAVIOUR_BAD
AND bOverrideHappiness = FALSE
AND i_chop_training_level > 0 // Required for B*1397363 - there is no CHOP_H_WALK_H0 text
tlChopHelp = "CHOP_H_WAIT_H"
ELSE
tlChopHelp = "CHOP_H_WAIT_"
ENDIF
tlChopHelp += i_chop_training_level
IF IS_USING_KEYBOARD_AND_MOUSE(PLAYER_CONTROL)
tlChopHelp += "_KM"
ENDIF
ELSE
IF (eChopTask = CHOP_TASK_WALK)
OR (eChopTask = CHOP_TASK_FETCH)
IF GET_CHOP_BEHAVIOUR_FROM_APP() = CHOP_BEHAVIOUR_BAD
AND bOverrideHappiness = FALSE
AND i_chop_training_level > 0 // Required for B*1397363 - there is no CHOP_H_WALK_H0 text
tlChopHelp = "CHOP_H_WALK_H"
ELSE
tlChopHelp = "CHOP_H_WALK_"
ENDIF
tlChopHelp += i_chop_training_level
IF IS_USING_KEYBOARD_AND_MOUSE(PLAYER_CONTROL)
tlChopHelp += "_KM"
ENDIF
ENDIF
ENDIF
PRINT_HELP_FOREVER(tlChopHelp) // Print the orders help text with a list of orders currently available to the player that he's unlocking in the training app
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Displaying menu ", tlChopHelp) #ENDIF
ELSE
IF eChopTask = CHOP_TASK_WALK
OR eChopTask = CHOP_TASK_FETCH
IF IS_USING_KEYBOARD_AND_MOUSE(PLAYER_CONTROL)
PRINT_HELP_FOREVER("CHOP_H_HOME_KM")
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Displaying CHOP_H_HOME_KM") #ENDIF
ELSE
PRINT_HELP_FOREVER("CHOP_H_HOME")
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Displaying CHOP_H_HOME") #ENDIF
ENDIF
ELSE
IF IS_USING_KEYBOARD_AND_MOUSE(PLAYER_CONTROL)
PRINT_HELP_FOREVER("CHOP_H_NOAPP_KM")
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Displaying CHOP_H_NOAPP_KM") #ENDIF
ELSE
PRINT_HELP_FOREVER("CHOP_H_NOAPP")
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Displaying CHOP_H_NOAPP") #ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ELSE
PLAYER_REMOVED_MENU() // See B*1043428 Ensure menu is removed
ENDIF
ENDPROC
/// PURPOSE:
/// Updates Chop's collar if the player has selected a different one in the app.
PROC MANAGE_CHOP_DONE_SHIT_DIALOGUE()
IF bChopDoneShitDialogue = FALSE
IF IS_PED_IN_TRICK_AREA(PLAYER_PED_ID())
PLAY_FRANKLIN_DIALOGUE("CHOP_DUMP")
bChopDoneShitDialogue = TRUE
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Handles Chop doing something sensible if Franklin uses the cable car.
PROC HANDLE_FRANKLIN_USING_CABLE_CAR()
IF eChopTask != CHOP_TASK_CABLE_CAR
AND g_bIsOnCableCar = TRUE
AND IS_ENTITY_ATTACHED_TO_ANY_OBJECT(PLAYER_PED_ID())
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Franklin is in the cablecar") #ENDIF
GIVE_CHOP_TASK(CHOP_TASK_CABLE_CAR)
ENDIF
ENDPROC
/// PURPOSE:
/// Update dog food value on the bawsaq stock market.
PROC MANAGE_BAWSAQ_STOCK_VALUES()
IF (GET_GAME_TIMER() - iStockMarketTimer) > 60000
iStockMarketTimer = GET_GAME_TIMER()
IF eChopTask = CHOP_TASK_WALK
OR eChopTask = CHOP_TASK_SNIFF
OR eChopTask = CHOP_TASK_FETCH
IF GET_CHOP_BEHAVIOUR_FROM_APP() = CHOP_BEHAVIOUR_GOOD
BAWSAQ_INCREMENT_MODIFIER(BSMF_SM_HDOG)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Incremented BSMF_SM_HDOG") #ENDIF
ELSE
BAWSAQ_INCREMENT_MODIFIER(BSMF_SM_UHDOG)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Incremented BSMF_SM_UHDOG") #ENDIF
ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Main processing function.
PROC DO_PROCESSING()
IF IS_PED_INJURED(PLAYER_PED_ID())
EXIT
ENDIF
IF IS_PED_INJURED(chopID)
EXIT
ENDIF
UPDATE_CHOP_COLLAR()
MANAGE_CHOP_DONE_SHIT_DIALOGUE()
IF eChopTask != CHOP_TASK_SPAWNED_IN_KENNEL
HANDLE_FRANKLIN_USING_CABLE_CAR()
MANAGE_CHOP_ATTACKING_PEDS_FIGHTING_FRANKLIN()
MANAGE_PLAYER_GIVING_ORDERS_TO_CHOP()
SCAN_FOR_OLD_BALLS_NEAR_FRANKLIN()
MANAGE_BAWSAQ_STOCK_VALUES()
ENDIF
PROCESS_PRIMARY_TASKS()
ENDPROC
#IF IS_DEBUG_BUILD
/// PURPOSE:
/// Checks for debug keys being pressed.
PROC DEBUG_Check_Debug_Keys()
IF (IS_KEYBOARD_KEY_JUST_PRESSED(KEY_S))
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player pressed S") #ENDIF
CLEANUP_SCRIPT()
ENDIF
IF (IS_KEYBOARD_KEY_JUST_PRESSED(KEY_F))
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player pressed F") #ENDIF
CLEANUP_SCRIPT()
ENDIF
ENDPROC
#ENDIF
SCRIPT(coords_struct in_coords)
IF HAS_FORCE_CLEANUP_OCCURRED()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Force cleanup has occurred") #ENDIF
CLEANUP_SCRIPT()
ENDIF
// B*2219053 - If the game is loading wait until it's faded in and everything in setup before we allow the chop script to continue, or else it may create its own chop
WHILE NOT IS_SCREEN_FADED_IN()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Waiting for screen to fade in") #ENDIF
WAIT(0)
ENDWHILE
//B* 2002205: Remember if hint cam was already active
bHintCamAlreadyACtive = IS_GAMEPLAY_HINT_ACTIVE()
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Script launched at ", in_coords.vec_coord[0]) #ENDIF
vStartCoords = in_coords.vec_coord[0]
IF GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH(GET_HASH_OF_THIS_SCRIPT_NAME()) > 1
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop script instance already running") #ENDIF
CLEANUP_SCRIPT()
ENDIF
IF NOT Get_Mission_Flow_Flag_State(FLOWFLAG_CHOP_THE_DOG_UNLOCKED)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Chop hasn't been unlocked") #ENDIF
CLEANUP_SCRIPT()
ENDIF
IF NOT Is_Savehouse_Respawn_Available(SAVEHOUSE_FRANKLIN_SC)
AND NOT Is_Savehouse_Respawn_Available(SAVEHOUSE_FRANKLIN_VH)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Neither safehouse is available yet") #ENDIF
CLEANUP_SCRIPT()
ENDIF
IF Is_Savehouse_Respawn_Available(SAVEHOUSE_FRANKLIN_VH)
AND GET_CLOSEST_SAVEHOUSE(vStartCoords, CHAR_FRANKLIN) = SAVEHOUSE_FRANKLIN_SC
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player returned to old SC safehouse") #ENDIF
CLEANUP_SCRIPT()
ENDIF
IF NOT IS_PED_THE_CURRENT_PLAYER_PED(CHAR_FRANKLIN)
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player isn't Franklin") #ENDIF
CLEANUP_SCRIPT()
ENDIF
IF g_bSafehouseTutorialIsActive = TRUE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: g_bSafehouseTutorialIsActive is TRUE") #ENDIF
CLEANUP_SCRIPT()
ENDIF
IF g_bPlayerLockedInToTrigger = TRUE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: g_bPlayerLockedInToTrigger is TRUE") #ENDIF
CLEANUP_SCRIPT()
ENDIF
IF IS_CURRENTLY_ON_MISSION_TO_TYPE(MISSION_TYPE_STORY)
IF GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH(HASH("SH_Intro_F_Hills")) = 0
//B* 1837051: And the mission running isn't Martin1 which requres the chop script at the end
AND GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH(HASH("martin1")) = 0
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player is on a mission") #ENDIF
CLEANUP_SCRIPT()
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player is on a mission but it's only the safehouse tutorial") #ENDIF
ENDIF
ELSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player is not on a mission") #ENDIF
ENDIF
IF Get_Player_Timetable_Scene_In_Progress() = PR_SCENE_F_WALKCHOP_a
OR Get_Player_Timetable_Scene_In_Progress() = PR_SCENE_F_WALKCHOP_b
OR Get_Player_Timetable_Scene_In_Progress() = PR_SCENE_F0_PLAYCHOP
OR Get_Player_Timetable_Scene_In_Progress() = PR_SCENE_F1_PLAYCHOP
OR Get_Player_Timetable_Scene_In_Progress() = PR_SCENE_F0_WALKCHOP
// OR Get_Player_Timetable_Scene_In_Progress() = PR_SCENE_F1_WALKCHOP //scene removed
bDoingTimetableSwitchChopNotInVehicle = TRUE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Timetable scene is in progress, Chop not in a vehicle") #ENDIF
ELIF Get_Player_Timetable_Scene_In_Progress() = PR_SCENE_Fa_EXILE2
bDoingTimetableSwitchChopIsInVehicle = TRUE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Timetable scene is in progress, Chop is in a vehicle") #ENDIF
ELSE
IF GET_DISTANCE_BETWEEN_COORDS(vStartCoords, << 154.0731, 765.5721, 209.6901 >>) <= 50 // PR_SCENE_F_WALKCHOP_a
OR GET_DISTANCE_BETWEEN_COORDS(vStartCoords, << -268.1390, 415.2881, 109.7258 >>) <= 50 // PR_SCENE_F_WALKCHOP_b
OR GET_DISTANCE_BETWEEN_COORDS(vStartCoords, << 314.4171, 965.2070, 208.4024 >>) <= 50 // PR_SCENE_Fa_EXILE2
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: Player is near timetable coords but not on a timetable scene") #ENDIF
CLEANUP_SCRIPT() // Prevent the Chop script launching any further if the player is just driving passed where the Chop timetable scenes take place
ENDIF
bDoingTimetableSwitchChopNotInVehicle = FALSE
bDoingTimetableSwitchChopIsInVehicle = FALSE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: No timetable scene in progress - player is near to the SC or VH safehouse") #ENDIF
IF g_bScriptsSetSafeForCutscene = TRUE
#IF IS_DEBUG_BUILD CPRINTLN(DEBUG_AMBIENT, "CHOP: g_bScriptsSetSafeForCutscene is TRUE") #ENDIF
CLEANUP_SCRIPT()
ENDIF
ENDIF
#IF IS_DEBUG_BUILD
SETUP_CHOP_WIDGETS()
#ENDIF
WHILE (TRUE)
#IF IS_DEBUG_BUILD
MAINTAIN_CHOP_WIDGETS()
DEBUG_Check_Debug_Keys()
#ENDIF
IF NOT SHOULD_SCRIPT_CLEANUP()
SWITCH eStage
CASE STAGE_INIT
DO_INITIALISE()
BREAK
CASE STAGE_PROCESS
DO_PROCESSING()
BREAK
ENDSWITCH
ELSE
CLEANUP_SCRIPT()
ENDIF
WAIT(0)
ENDWHILE
ENDSCRIPT