4582 lines
206 KiB
Python
Executable File
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
|