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

8289 lines
307 KiB
XML
Executable File

//╒═════════════════════════════════════════════════════════════════════════════╕
//│ Author: Adam Westwood Date: 07/02/14 │
//│ Michael Wadelin Date: 10/09/14 │
//╞═════════════════════════════════════════════════════════════════════════════╡
//│ │
//│ Crowd Control Minigame │
//│ │
//│ Minigame for controlling a crowd │
//│ │
//│ │
//│ │
//╘═════════════════════════════════════════════════════════════════════════════╛
USING "timer_public.sch"
USING "rage_builtins.sch"
USING "globals.sch"
USING "commands_audio.sch"
USING "script_audio.sch"
USING "commands_camera.sch"
USING "commands_clock.sch"
USING "commands_debug.sch"
USING "commands_fire.sch"
USING "commands_graphics.sch"
USING "commands_hud.sch"
USING "commands_misc.sch"
USING "commands_object.sch"
USING "commands_pad.sch"
USING "commands_ped.sch"
USING "commands_player.sch"
USING "commands_script.sch"
USING "commands_streaming.sch"
USING "commands_task.sch"
USING "commands_vehicle.sch"
USING "commands_interiors.sch"
USING "net_include.sch"
USING "script_ped.sch"
USING "model_enums.sch"
USING "script_player.sch"
USING "script_misc.sch"
USING "selector_public.sch"
USING "timer_public.sch"
USING "LineActivation.sch"
USING "clothes_shop_private.sch"
USING "script_audio.sch"
#IF IS_DEBUG_BUILD
USING "shared_debug.sch"
USING "script_debug.sch"
#ENDIF
// Enums
//----------------------------------------------------------------
ENUM CROWD_CONTROL_HEIST_TYPE
CCHEIST_NORMAL,
CCHEIST_CUSTOM_FLEECA,
CCHEIST_CUSTOM_ORNATE
ENDENUM
ENUM CROWD_CONTROL_PED_HERO_ACTION
CCHERO_NONE,
CCHERO_GUN,
CCHERO_PHONE,
CCHERO_ALARM
ENDENUM
ENUM CROWD_CONTROL_FAIL_REASON
CCFAIL_NONE,
CCFAIL_PEDHEROGUN,
CCFAIL_PEDHEROPHONE,
CCFAIL_PEDHEROALARM,
CCFAIL_PEDKILLED,
CCFAIL_PEDSKILLED,
CCFAIL_FEARLOW,
CCFAIL_TOOMANYKNOCKOUTS,
CCFAIL_NOHITSREMAINING
ENDENUM
ENUM CROWD_CONTROL_PED_ENUM
CCPED_CASHIER_A,
CCPED_CASHIER_B,
CCPED_CUSTOMER_A,
CCPED_CUSTOMER_B,
CCPED_CUSTOMER_C,
CCPED_CUSTOMER_D,
CCPED_CUSTOMER_E,
CCPED_CUSTOMER_F,
CCPED_CUSTOMER_G,
CCPED_CUSTOMER_H
ENDENUM
ENUM CROWD_CONTROL_PED_STATE
CCPS_INVALID = -1,
CCPS_INTRO,
CCPS_COWER,
CCPS_FLINCH,
CCPS_FLINCH_UNDERFIRE,
CCPS_BEATDOWN,
CCPS_HERO_ATTEMPT,
CCPS_HERO,
CCPS_COMBAT
ENDENUM
// List of all the anim types for the custom animated versions of the minigame
ENUM CROWD_CONTROL_PED_ANIM_TYPE
CCPANIM_INTRO_LOOP,
CCPANIM_INTRO,
CCPANIM_COWER_LOOP,
// Flinch
CCPANIM_COWER_TO_FLINCH_INTRO,
CCPANIM_FLINCH_LOOP,
CCPANIM_FLINCH_TO_IDLE_OUTRO,
// Flinch underfire
CCPANIM_COWER_TO_FLINCH_UNDERFIRE_INTRO,
CCPANIM_FLINCH_TO_FLINCH_UNDERFIRE_INTRO,
CCPANIM_FLINCH_UNDERFIRE_LOOP,
CCPANIM_FLINCH_UNDERFIRE_TO_FLINCH_OUTRO,
CCPANIM_BEATDOWN_INTRO,
CCPANIM_BEATDOWN_LOOP,
CCPANIM_BEATDOWN_HIT_A,
CCPANIM_BEATDOWN_HIT_B,
CCPANIM_BEATDOWN_DIE,
CCPANIM_BEATDOWN_DIE_LOOP,
CCPANIM_BEATDOWN_OUTRO,
CCPANIM_HERO_START,
CCPANIM_HERO_LOOP,
CCPANIM_HERO_PASS,
CCPANIM_HERO_FAIL
ENDENUM
// List of anims for the player ped to play in the custom anim version of this minigame
ENUM CROWD_CONTROL_PLAYER_ANIM_TYPE
CCPLAYERANIM_INVALID = -1,
CCPLAYERANIM_BEATDOWN_PISTOL_A,
CCPLAYERANIM_BEATDOWN_PISTOL_B,
CCPLAYERANIM_BEATDOWN_PISTOL_KICK_A,
CCPLAYERANIM_BEATDOWN_PISTOL_KICK_B,
CCPLAYERANIM_BEATDOWN_RIFLE_A,
CCPLAYERANIM_BEATDOWN_RIFLE_B,
CCPLAYERANIM_BEATDOWN_RIFLE_KICK_A,
CCPLAYERANIM_BEATDOWN_RIFLE_KICK_B,
CCPLAYERANIM_BEATDOWN_LONG_PISTOL_A,
CCPLAYERANIM_BEATDOWN_LONG_PISTOL_KICK_A,
CCPLAYERANIM_BEATDOWN_LONG_RIFLE_A,
CCPLAYERANIM_BEATDOWN_LONG_RIFLE_B,
CCPLAYERANIM_BEATDOWN_LONG_RIFLE_KICK_A,
CCPLAYERANIM_BEATDOWN_LONG_RIFLE_KICK_B
ENDENUM
// Represents the type of threat the player is imposing on a crowd control ped
ENUM CROWD_CONTROL_THREAT_TYPE
CCTHREAT_NONE,
CCTHREAT_PROXIMITY,
CCTHREAT_SHOUTING,
CCTHREAT_BEATING_NEIGHBOUR,
CCTHREAT_SHOUTING_CLOSE,
CCTHREAT_AIMED_AT,
CCTHREAT_BEATING,
CCTHREAT_SHOOTING,
CCTHREAT_KILLED_OTHER,
CCTHREAT_PROJECTILE_EXPLOSIVE,
CCTHREAT_PROJECTILE_EXPLOSIVE_STICKY_BOMB,
CCTHREAT_SHOOTING_CLOSE
ENDENUM
// PRIORITY ORDER
ENUM CROWD_CONTROL_DIALOGUE
CCDIA_NONE,
CCDIA_COWER_LOW_FEAR,
CCDIA_COWER_HIGH_FEAR,
CCDIA_APPROACH,
CCDIA_ALARM_GOES_OFF,
CCDIA_RECEIVES_SCARE,
CCDIA_STICKS_UP_FOR_OTHER,
CCDIA_SEES_OTHER_KILLED,
CCDIA_SHOT_NEAR,
CCDIA_SHOT_DIRECTLY_AT,
CCDIA_ALARM_GOES_OFF_INITIAL,
// Hero stuff should be higher than general stuff
CCDIA_HERO_ALARM_START,
CCDIA_HERO_ALARM_CAUGHT,
CCDIA_HERO_ALARM_COMPLETE,
CCDIA_HERO_GUN_START,
CCDIA_HERO_GUN_CAUGHT,
CCDIA_HERO_GUN_COMPLETE,
CCDIA_HERO_PHONE_START,
CCDIA_HERO_PHONE_CAUGHT,
CCDIA_HERO_PHONE_COMPLETE,
CCDIA_HERO_PHONE_APOLOGISE,
// 1 offs, high priority
CCDIA_INITIAL_PANIC
ENDENUM
// List of the different context timers
ENUM CROWD_CONTROL_DIALOGUE_CONTEXT
CCDIACONTEXT_SCARED_HIGH,
CCDIACONTEXT_SCARED_MEDIUM,
CCDIACONTEXT_SCARED_LOW,
CCDIACONTEXT_SCARED_SECOND_HAND,
CCDIACONTEXT_AMBIENT,
CCDIACONTEXT_NUM_CONTEXTS
ENDENUM
// List of all the sequences used to task the crowd ped animations
ENUM CROWD_CONTROL_SEQUENCES
CCSEQ_COWER_TO_FLINCH,
CCSEQ_COWER_TO_FLINCH_UNDERFIRE,
CCSEQ_FLINCH_TO_FLINCH_UNDERFIRE,
CCSEQ_FLINCH_UNDERFIRE_TO_FLINCH,
CCSEQ_FLINCH_TO_COWER,
// CCSEQ_OPEN_SAFE,
CCSEQ_HERO_START,
// CCSEQ_HERO_ALT,
CCSEQ_HERO_FAIL,
CCSEQ_HERO_PASS,
CCSEQ_BEATDOWN_TO_FLINCH,
CCSEQ_NUM_SEQUENCES
ENDENUM
// Server bitset flags
ENUM CROWD_CONTROL_DATA_BITSET_FLAGS
CCDATA_Active // 0
,CCDATA_Failed
,CCDATA_FailProcessed
,CCDATA_FailDisabled
,CCDATA_Restricted
,CCDATA_EasyPassComplete // 5
,CCDATA_EasyPassProcessed
,CCDATA_FearFailsBlocked
,CCDATA_RaiseAlarmDiagloue
#IF IS_DEBUG_BUILD
,CCDATA_DebugUnlimitedBeatings
,CCDATA_DebugDisableFails // 10
,CCDATA_RestrictedDebug
#ENDIF
ENDENUM
// Local bitset flags
ENUM CROWD_CONTROL_LOCAL_DATA_BITSET_FLAGS
CCLOCALDATA_IntroAnimsRequested // 0
,CCLOCALDATA_MainAnimsRequested
,CCLOCALDATA_StartedFailCutscene
,CCLOCALDATA_RecievedSyncedScene
,CCLOCALDATA_HelpText1
,CCLOCALDATA_HelpText2 // 5
,CCLOCALDATA_HelpText3
,CCLOCALDATA_DisplayHUD
,CCLOCALDATA_Initialised
,CCLOCALDATA_RaiseAlarms
,CCLOCALDATA_AlarmsPlaying // 10
,CCLOCALDATA_AwardGiven
,CCLOCALDATA_BeatdownInterrupt
,CCLOCALDATA_BeatdownFirst
,CCLOCALDATA_BeatdownInputTaken
,CCLOCALDATA_BeatdownProbeResultAllowHit // 15
,CCLOCALDATA_HUDWarning
,CCLOCALDATA_TimerSetFearFail
,CCLOCALDATA_TimerSetFearUnblocked
,CCLOCALDATA_TimeSetNextHeroPick
,CCLOCALDATA_BlockingActionMode // 20
#IF IS_DEBUG_BUILD
,CCLOCALDATA_DebugLogDialogue
#ENDIF
ENDENUM
// Server ped data bit flags
ENUM CROWD_CONTROL_PED_DATA_BITSET_FLAGS
CCPEDDATA_HasBeenInitialised
,CCPEDDATA_RunningBespokeAnims
,CCPEDDATA_KnockedOut
,CCPEDDATA_Dead
,CCPEDDATA_DeadProcessed
,CCPEDDATA_FearLocked
,CCPEDDATA_HeroComplete
,CCPEDDATA_HeroBroken
,CCPEDDATA_HeroAttempted
,CCPEDDATA_BeatdownSyncSceneStarted
,CCPEDDATA_IgnoreStickyBombs
ENDENUM
// Local ped data bit flags
ENUM CROWD_CONTROL_LOCAL_PED_DATA_BITSET_FLAGS
CCLOCALPEDDATA_OwnThisPed
,CCLOCALPEDDATA_LocallyInitialised
,CCLOCALPEDDATA_DialogueClientToHostForcePain
,CCLOCALPEDDATA_DialogueClientToHostForceCritical
,CCLOCALPEDDATA_DialogueHostToClientInterrupt
,CCLOCALPEDDATA_DialogueHostToClientPain
,CCLOCALPEDDATA_ShotAtFearWasClose
,CCLOCALPEDDATA_TimeLastThreatenedSet
,CCLOCALPEDDATA_TimeLastShotAtSet
,CCLOCALPEDDATA_TimeLastBeatSet
,CCLOCALPEDDATA_TimeSetStartThreaten
,CCLOCALPEDDATA_TimeSetHoldBeforeEscape
,CCLOCALPEDDATA_TimeSetStartFearProjectile
,CCLOCALPEDDATA_TimeSetStartProximityThreat
,CCLOCALPEDDATA_TimeSetShootingCloseFlinching
,CCLOCALPEDDATA_BeatdownFired
,CCLOCALPEDDATA_HeroPropDetatchedForLastTime
ENDENUM
ENUM CROWD_CONTROL_PED_DECORATOR
CCPDECOR_iState
,CCPDECOR_iStatePrev
,CCPDECOR_iBitSet
,CCPDECOR_fInfluenceDirectThreat
,CCPDECOR_fInfluenceShouting
,CCPDECOR_iBeatdownHitsRemaining
,CCPDECOR_iBeatdownRounds
ENDENUM
#IF IS_DEBUG_BUILD
ENUM CROWD_CONTROL_DEBUG_DISPLAY
CCDEBUG_OFF = -1,
CCDEBUG_ALL,
CCDEBUG_BASIC,
CCDEBUG_FEAR_AND_THREATS,
CCDEBUG_HERO,
CCDEBUG_BEATDOWNS,
CCDEBUG_DIALOGUE,
CCDEBUG_MISC,
CCDEBUG_NUM_DEBUG
ENDENUM
#ENDIF
// Constants
//----------------------------------------------------------------
CONST_INT ciCC_MaxActiveHeros 1
CONST_FLOAT cfCC_ShoutVolumeLevel 0.75
CONST_INT ciCC_DialogueBroadcastDelay 2000
CONST_INT ciCC_MaxNumberOfAmbientDialogue 1
CONST_INT ciCC_MaxNumberOfPain 3
CONST_INT ciCC_MaxNumberOfSpeakers ciCC_MaxNumberOfAmbientDialogue + ciCC_MaxNumberOfPain
CONST_INT ciCC_MaxBeatdownHits 2
CONST_INT ciCC_MaxBeatdownHitsTotal 6
CONST_INT ciCC_BeatdownHitsLostOnPedDeath 3
TWEAK_FLOAT cfCC_BeatdownDistMin 0.5
TWEAK_FLOAT cfCC_BeatdownDistMax 1.5
TWEAK_FLOAT cfCC_BeatdownLongAnimDistThreshold 1.25
TWEAK_FLOAT cfCC_BeatdownMeleeDisableDist 2.0
TWEAK_FLOAT cfCC_BeatdownFacingAngleChangeDistMin 0.75
TWEAK_FLOAT cfCC_BeatdownFacingAngleChangeDistMax 1.3
TWEAK_FLOAT cfCC_BeatdownFacingAngleRangeMin 23.5
TWEAK_FLOAT cfCC_BeatdownFacingAngleRangeMax 80.0
CONST_FLOAT cfCC_PlayerPedCapsuleRadiusStationary 0.25
CONST_FLOAT cfCC_PedBreakoutCowerThreshold 25.0
CONST_INT ciCC_PedThreatenCoolOffDuration 2000
CONST_INT ciCC_PedShotAtCoolOffDuration 1000
CONST_INT ciCC_PedShotAtCoolOffDurationClose 3000
CONST_INT ciCC_ProjectileCoolOffDuration 6000
CONST_INT ciCC_ProximityCoolOffDuration 5000
CONST_INT ciCC_FearFailMin 0
TWEAK_FLOAT cfCC_FearIncreaseRateMin 1.0
TWEAK_FLOAT cfCC_FearIncreaseRateMax 8.0
TWEAK_FLOAT cfCC_FearDecreaseRateMin 0.5
TWEAK_FLOAT cfCC_FearDecreaseRateMax 4.0
CONST_INT ciCC_Range 30
CONST_INT ciCC_ThreatTypeDataBitWidth 4 // max 16 numbers/states
CONST_INT ciCC_DialogueDataBitWidth 5 // max 32 numbers/states
CONST_INT ciCC_HeroAttemptsDataBitWidth 6 // max 64 numbers/states
CONST_INT ciCC_MaxAlarms 2
ANIMATION_FLAGS eCrowdControlAnimFlags = AF_USE_KINEMATIC_PHYSICS | AF_USE_MOVER_EXTRACTION // | AF_NOT_INTERRUPTABLE
#IF IS_DEBUG_BUILD
TWEAK_INT ciCC_DebugColNormalR 0
TWEAK_INT ciCC_DebugColNormalG 0
TWEAK_INT ciCC_DebugColNormalB 255
TWEAK_INT ciCC_DebugColWarningR 30
TWEAK_INT ciCC_DebugColWarningG 255
TWEAK_INT ciCC_DebugColWarningB 255
TWEAK_INT ciCC_DebugColActiveR 190
TWEAK_INT ciCC_DebugColActiveG 0
TWEAK_INT ciCC_DebugColActiveB 255
#ENDIF
// Structs
//----------------------------------------------------------------
STRUCT CROWD_CONTROL_SERVER_DATA
INT iMinigameStage = 0
INT iCrowdControlCloudIndex[FMMC_MAX_CROWD_PEDS] // can this be cached locally?
FLOAT fFear = -1.0 // the overall fear level of the crowd
INT iBitSet = 0
INT iCurrentActiveHeros = 0
INT iHeroAttempts[2] // use 6 bits per ped to save storage (instead of an int per ped i.e. 10 ints)
INT iSyncScene = -1
INT iPedIsNowInvolvedInCrowdControl[2] // can this be removed?
INT iNumCrowdControlPeds
INT iNumHitsRemaining = ciCC_MaxBeatdownHitsTotal
ENDSTRUCT
STRUCT CROWD_CONTROL_LOCAL_DATA
// Locals cached info
INT iCCRule[FMMC_MAX_TEAMS]
INT iThermiteTeam = -1
INT iThermiteRule = -1
CROWD_CONTROL_HEIST_TYPE eHeistType = CCHEIST_NORMAL
INT iCashierPed = -1
INTERIOR_INSTANCE_INDEX interiorHeist = NULL
// Local Variables
// General
INT iBitSet = 0
INT iSyncSceneIntroLoop = -1
// Heroes
TIME_DATATYPE timeNextHeroPick
// Dialogue
TIME_DATATYPE timeDialogueGlobalContextTimers[CCDIACONTEXT_NUM_CONTEXTS]
TIME_DATATYPE timeCrowdKickedOff
// Beating
INT iSyncSceneBeatdown = -1
INT iPlayerBeatingThisPed = -1
CROWD_CONTROL_PLAYER_ANIM_TYPE eLastBeatdown = CCPLAYERANIM_INVALID
SHAPETEST_INDEX shapetestBeatdownProbe = NULL
INT iCrowdPedBeatdownProbe = -1
// Fear
FLOAT fFear
FLOAT fIntimidationBar
FLOAT fIntimidationBarInterpRate
INT iUpdateTimer = 0
// Fail
INT iFailCutsceneHash = 0
INT iFailCausedByPed = -1
TIME_DATATYPE timeFailDelay
TIME_DATATYPE timeFearFail
TIME_DATATYPE timeFearFailsUnblocked
CROWD_CONTROL_FAIL_REASON eFailReason = CCFAIL_NONE
// Alarms
INT iSFXAlarms[ciCC_MaxAlarms]
#IF IS_DEBUG_BUILD
// Debug vars
CROWD_CONTROL_DEBUG_DISPLAY eDebugDisplay = CCDEBUG_OFF
#ENDIF
ENDSTRUCT
// Used locally for delaying the update of decorators
STRUCT CROWD_CONTROL_PED_DECORATOR_DATA
CROWD_CONTROL_PED_STATE eState
CROWD_CONTROL_PED_STATE eStatePrev
INT iBitSet
FLOAT fInfluenceDirectThreat
FLOAT fInfluenceShouting
INT iBeatdownHitsRemaining
INT iBeatdownRounds
ENDSTRUCT
STRUCT CROWD_CONTROL_LOCAL_PED_DATA
// Cached ped data
CROWD_CONTROL_PED_ENUM eCreatorAssignedPedID
// Local ped variables
INT iBitSet = 0
INT iUpdateTimer
// Prop related stuff
OBJECT_INDEX objProp = NULL //For props like phones
// Dialogue
CROWD_CONTROL_DIALOGUE eDialogueClientToHost = CCDIA_NONE // what a ped wants to play
CROWD_CONTROL_DIALOGUE eDialogueHostToClient = CCDIA_NONE // what they have been allowed to play
CROWD_CONTROL_DIALOGUE eDialogueCurrentlyPlaying = CCDIA_NONE // what they are currently playing
TIME_DATATYPE timeDialogueLastBroadcast // the last time a ped told the host it wanted to speak
TIME_DATATYPE timeDialogueContextTimers[CCDIACONTEXT_NUM_CONTEXTS] // last time dialogue from each context was spoken
// Threat
CROWD_CONTROL_THREAT_TYPE eBiggestThreat = CCTHREAT_NONE
PED_INDEX pedBiggestThreat = NULL
TIME_DATATYPE timeThreatUpdate // used to control how often we update the player threat BD
TIME_DATATYPE timeStartThreaten
TIME_DATATYPE timeLastThreatened
TIME_DATATYPE timeLastShotAt
TIME_DATATYPE timeLastBeat
TIME_DATATYPE timeStartProjectileFear
TIME_DATATYPE timeStartProximityThreat
TIME_DATATYPE timeShootingCloseFlinchingStarted
TIME_DATATYPE timeDied
// Sequences
SEQUENCE_INDEX seqs[CCSEQ_NUM_SEQUENCES]
#IF IS_DEBUG_BUILD
CROWD_CONTROL_THREAT_TYPE eDebugThreatFelt = CCTHREAT_NONE
CROWD_CONTROL_THREAT_TYPE eDebugThreatUsedForFear = CCTHREAT_NONE
#ENDIF
ENDSTRUCT
// Cleanup
//----------------------------------------------------------------------------
PROC RESET_CROWD_CONTROL_SERVER_DATA( CROWD_CONTROL_SERVER_DATA &sCrowdControlData )
CPRINTLN(DEBUG_NET_MINIGAME, "RESET_CROWD_CONTROL_SERVER_DATA()")
CROWD_CONTROL_SERVER_DATA sEmpty
sCrowdControlData = sEmpty
ENDPROC
PROC RESET_CROWD_CONTROL_LOCAL_DATA( CROWD_CONTROL_LOCAL_DATA &sLocalData )
CPRINTLN(DEBUG_NET_MINIGAME, "RESET_CROWD_CONTROL_LOCAL_DATA()")
CROWD_CONTROL_LOCAL_DATA sEmpty
sEmpty.iSFXAlarms[0] = -1
sEmpty.iSFXAlarms[1] = -1
sLocalData = sEmpty
ENDPROC
PROC RESET_CROWD_CONTROL_PED_DECOR_DATA(CROWD_CONTROL_PED_DECORATOR_DATA &sPedDecorData[] )
CPRINTLN(DEBUG_NET_MINIGAME, "RESET_CROWD_CONTROL_PED_DECOR_DATA()")
CROWD_CONTROL_PED_DECORATOR_DATA sEmpty
INT i
REPEAT COUNT_OF(sPedDecorData) i
sPedDecorData[i] = sEmpty
ENDREPEAT
ENDPROC
PROC RESET_CROWD_CONTROL_LOCAL_PED_DATA( CROWD_CONTROL_LOCAL_PED_DATA &sLocalPedData[] )
CPRINTLN(DEBUG_NET_MINIGAME, "RESET_CROWD_CONTROL_LOCAL_PED_DATA()")
CROWD_CONTROL_LOCAL_PED_DATA sEmpty
INT i
REPEAT COUNT_OF(sLocalPedData) i
sLocalPedData[i] = sEmpty
ENDREPEAT
ENDPROC
PROC CLEANUP_CROWD_CONTROL_MINIGAME( CROWD_CONTROL_LOCAL_DATA &sLocalData, CROWD_CONTROL_LOCAL_PED_DATA &sLocalPedData[] )
CPRINTLN(DEBUG_NET_MINIGAME, "CLEANUP_CROWD_CONTROL_MINIGAME()")
INT i
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_Initialised )
REPEAT COUNT_OF( sLocalData.iSFXAlarms ) i
IF sLocalData.iSFXAlarms[i] != -1
IF NOT HAS_SOUND_FINISHED( sLocalData.iSFXAlarms[i] )
STOP_SOUND( sLocalData.iSFXAlarms[i] )
ENDIF
sLocalData.iSFXAlarms[i] = -1
ENDIF
ENDREPEAT
REPEAT COUNT_OF( sLocalPedData ) i
IF DOES_ENTITY_EXIST( sLocalPedData[i].objProp )
DELETE_OBJECT( sLocalPedData[i].objProp )
ENDIF
INT j
REPEAT CCSEQ_NUM_SEQUENCES j
IF sLocalPedData[i].seqs[j] != NULL
CLEAR_SEQUENCE_TASK( sLocalPedData[i].seqs[j] )
ENDIF
ENDREPEAT
ENDREPEAT
ENDIF
ENDPROC
// Debug procs/funcs
//----------------------------------------------------------------------------
#IF IS_DEBUG_BUILD
PROC SETUP_CROWD_CONTROL_DEBUG( CROWD_CONTROL_SERVER_DATA &sServerData, CROWD_CONTROL_LOCAL_DATA &sLocalData )
START_WIDGET_GROUP( "Crowd Control" )
ADD_BIT_FIELD_WIDGET( "Server bitset", sServerData.iBitSet )
ADD_BIT_FIELD_WIDGET( "Local bitset", sLocalData.iBitSet )
START_WIDGET_GROUP( "Fear" )
ADD_WIDGET_FLOAT_SLIDER( "FearIncreaseRateMin", cfCC_FearIncreaseRateMin, 0.0, 20.0, 0.1 )
ADD_WIDGET_FLOAT_SLIDER( "FearIncreaseRateMax", cfCC_FearIncreaseRateMax, 0.0, 20.0, 0.1 )
ADD_WIDGET_FLOAT_SLIDER( "FearDecreaseRateMin", cfCC_FearDecreaseRateMin, 0.0, 20.0, 0.1 )
ADD_WIDGET_FLOAT_SLIDER( "FearDecreaseRateMax", cfCC_FearDecreaseRateMax, 0.0, 20.0, 0.1 )
STOP_WIDGET_GROUP()
START_WIDGET_GROUP( "Beatdown" )
ADD_WIDGET_FLOAT_SLIDER( "Distance Min", cfCC_BeatdownDistMin, 0.0, 10.0, 0.1 )
ADD_WIDGET_FLOAT_SLIDER( "Distance Max", cfCC_BeatdownDistMax, 0.0, 10.0, 0.1 )
ADD_WIDGET_FLOAT_SLIDER( "Long Anim Dist", cfCC_BeatdownLongAnimDistThreshold, 0.0, 10.0, 0.1 )
ADD_WIDGET_FLOAT_SLIDER( "Standard Melee Disable Dist", cfCC_BeatdownMeleeDisableDist, 0.0, 10.0, 0.1 )
ADD_WIDGET_FLOAT_SLIDER( "Facing Angle Change Dist Min", cfCC_BeatdownFacingAngleChangeDistMin, 0.0, 10.0, 0.1 )
ADD_WIDGET_FLOAT_SLIDER( "Facing Angle Change Dist Max", cfCC_BeatdownFacingAngleChangeDistMax, 0.0, 10.0, 0.1 )
ADD_WIDGET_FLOAT_SLIDER( "Facing Angle Min", cfCC_BeatdownFacingAngleRangeMin, 0.0, 360.0, 0.1 )
ADD_WIDGET_FLOAT_SLIDER( "Facing Angle Max", cfCC_BeatdownFacingAngleRangeMax, 0.0, 360.0, 0.1 )
STOP_WIDGET_GROUP()
START_WIDGET_GROUP( "Debug Display" )
ADD_WIDGET_INT_SLIDER( "ciCC_DebugColNormalR", ciCC_DebugColNormalR, 0, 255, 1 )
ADD_WIDGET_INT_SLIDER( "ciCC_DebugColNormalG", ciCC_DebugColNormalG, 0, 255, 1 )
ADD_WIDGET_INT_SLIDER( "ciCC_DebugColNormalB", ciCC_DebugColNormalB, 0, 255, 1 )
ADD_WIDGET_INT_SLIDER( "ciCC_DebugColWarningR", ciCC_DebugColWarningR, 0, 255, 1 )
ADD_WIDGET_INT_SLIDER( "ciCC_DebugColWarningG", ciCC_DebugColWarningG, 0, 255, 1 )
ADD_WIDGET_INT_SLIDER( "ciCC_DebugColWarningB", ciCC_DebugColWarningB, 0, 255, 1 )
ADD_WIDGET_INT_SLIDER( "ciCC_DebugColActiveR", ciCC_DebugColActiveR, 0, 255, 1 )
ADD_WIDGET_INT_SLIDER( "ciCC_DebugColActiveG", ciCC_DebugColActiveG, 0, 255, 1 )
ADD_WIDGET_INT_SLIDER( "ciCC_DebugColActiveB", ciCC_DebugColActiveB, 0, 255, 1 )
STOP_WIDGET_GROUP()
STOP_WIDGET_GROUP()
ENDPROC
FUNC STRING GET_CROWD_CONTROL_PED_STATE_STRING( CROWD_CONTROL_PED_STATE eState )
SWITCH eState
CASE CCPS_INVALID RETURN "CCPS_INVALID" BREAK
CASE CCPS_INTRO RETURN "CCPS_INTRO" BREAK
CASE CCPS_COWER RETURN "CCPS_COWER" BREAK
CASE CCPS_FLINCH RETURN "CCPS_FLINCH" BREAK
CASE CCPS_FLINCH_UNDERFIRE RETURN "CCPS_FLINCH_UNDERFIRE" BREAK
CASE CCPS_BEATDOWN RETURN "CCPS_BEATDOWN" BREAK
CASE CCPS_HERO_ATTEMPT RETURN "CCPS_HERO_ATTEMPT" BREAK
CASE CCPS_HERO RETURN "CCPS_HERO" BREAK
CASE CCPS_COMBAT RETURN "CCPS_COMBAT" BREAK
ENDSWITCH
RETURN ""
ENDFUNC
FUNC STRING GET_CROWD_CONTROL_PED_ANIM_ENUM_NAME( CROWD_CONTROL_PED_ANIM_TYPE eAnim )
SWITCH eAnim
CASE CCPANIM_INTRO_LOOP RETURN "CCPANIM_INTRO_LOOP" BREAK
CASE CCPANIM_INTRO RETURN "CCPANIM_INTRO" BREAK
CASE CCPANIM_COWER_LOOP RETURN "CCPANIM_COWER_LOOP" BREAK
CASE CCPANIM_COWER_TO_FLINCH_INTRO RETURN "CCPANIM_COWER_TO_FLINCH_INTRO" BREAK
CASE CCPANIM_COWER_TO_FLINCH_UNDERFIRE_INTRO RETURN "CCPANIM_COWER_TO_FLINCH_UNDERFIRE_INTRO" BREAK
CASE CCPANIM_FLINCH_LOOP RETURN "CCPANIM_FLINCH_LOOP" BREAK
CASE CCPANIM_FLINCH_TO_IDLE_OUTRO RETURN "CCPANIM_FLINCH_TO_IDLE_OUTRO" BREAK
CASE CCPANIM_FLINCH_TO_FLINCH_UNDERFIRE_INTRO RETURN "CCPANIM_FLINCH_TO_FLINCH_UNDERFIRE_INTRO" BREAK
CASE CCPANIM_FLINCH_UNDERFIRE_LOOP RETURN "CCPANIM_FLINCH_UNDERFIRE_LOOP" BREAK
CASE CCPANIM_FLINCH_UNDERFIRE_TO_FLINCH_OUTRO RETURN "CCPANIM_FLINCH_UNDERFIRE_TO_FLINCH_OUTRO" BREAK
CASE CCPANIM_BEATDOWN_INTRO RETURN "CCPANIM_BEATDOWN_INTRO" BREAK
CASE CCPANIM_BEATDOWN_LOOP RETURN "CCPANIM_BEATDOWN_LOOP" BREAK
CASE CCPANIM_BEATDOWN_HIT_A RETURN "CCPANIM_BEATDOWN_HIT_A" BREAK
CASE CCPANIM_BEATDOWN_HIT_B RETURN "CCPANIM_BEATDOWN_HIT_B" BREAK
CASE CCPANIM_BEATDOWN_DIE RETURN "CCPANIM_BEATDOWN_DIE" BREAK
CASE CCPANIM_BEATDOWN_DIE_LOOP RETURN "CCPANIM_BEATDOWN_DIE_LOOP" BREAK
CASE CCPANIM_HERO_START RETURN "CCPANIM_HERO_START" BREAK
CASE CCPANIM_HERO_LOOP RETURN "CCPANIM_HERO_LOOP" BREAK
CASE CCPANIM_HERO_PASS RETURN "CCPANIM_HERO_PASS" BREAK
CASE CCPANIM_HERO_FAIL RETURN "CCPANIM_HERO_FAIL" BREAK
ENDSWITCH
RETURN ""
ENDFUNC
FUNC STRING GET_CROWD_CONTROL_PED_NAME_FROM_ANIM_ID( CROWD_CONTROL_PED_ENUM eCrowdPedAnimID )
SWITCH eCrowdPedAnimID
CASE CCPED_CASHIER_A RETURN "Cashier A" BREAK
CASE CCPED_CASHIER_B RETURN "Cashier B" BREAK
CASE CCPED_CUSTOMER_A RETURN "Ped A" BREAK
CASE CCPED_CUSTOMER_B RETURN "Ped B" BREAK
CASE CCPED_CUSTOMER_C RETURN "Ped C" BREAK
CASE CCPED_CUSTOMER_D RETURN "Ped D" BREAK
CASE CCPED_CUSTOMER_E RETURN "Ped E" BREAK
CASE CCPED_CUSTOMER_F RETURN "Ped F" BREAK
CASE CCPED_CUSTOMER_G RETURN "Ped G" BREAK
CASE CCPED_CUSTOMER_H RETURN "Ped H" BREAK
ENDSWITCH
RETURN ""
ENDFUNC
FUNC STRING GET_CROWD_CONTROL_THREAT_TYPE_STRING( CROWD_CONTROL_THREAT_TYPE eThreat )
SWITCH eThreat
CASE CCTHREAT_NONE RETURN "CCTHREAT_NONE" BREAK
CASE CCTHREAT_PROXIMITY RETURN "CCTHREAT_PROXIMITY" BREAK
CASE CCTHREAT_SHOUTING RETURN "CCTHREAT_SHOUTING" BREAK
CASE CCTHREAT_SHOUTING_CLOSE RETURN "CCTHREAT_SHOUTING_CLOSE" BREAK
CASE CCTHREAT_BEATING_NEIGHBOUR RETURN "CCTHREAT_BEATING_NEIGHBOUR" BREAK
CASE CCTHREAT_KILLED_OTHER RETURN "CCTHREAT_KILLED_OTHER" BREAK
CASE CCTHREAT_AIMED_AT RETURN "CCTHREAT_AIMED_AT" BREAK
CASE CCTHREAT_BEATING RETURN "CCTHREAT_BEATING" BREAK
CASE CCTHREAT_SHOOTING RETURN "CCTHREAT_SHOOTING" BREAK
CASE CCTHREAT_PROJECTILE_EXPLOSIVE RETURN "CCTHREAT_PROJECTILE_EXPLOSIVE" BREAK
CASE CCTHREAT_PROJECTILE_EXPLOSIVE_STICKY_BOMB RETURN "CCTHREAT_PROJECTILE_EXPLOSIVE_STICKY_BOMB" BREAK
CASE CCTHREAT_SHOOTING_CLOSE RETURN "CCTHREAT_SHOOTING_CLOSE" BREAK
ENDSWITCH
RETURN "no debug name"
ENDFUNC
FUNC STRING GET_CROWD_CONTROL_HEIST_TYPE_STRING( CROWD_CONTROL_HEIST_TYPE eHeistType )
SWITCH eHeistType
CASE CCHEIST_NORMAL RETURN "CCHEIST_NORMAL" BREAK
CASE CCHEIST_CUSTOM_FLEECA RETURN "CCHEIST_CUSTOM_FLEECA" BREAK
CASE CCHEIST_CUSTOM_ORNATE RETURN "CCHEIST_CUSTOM_ORNATE" BREAK
ENDSWITCH
RETURN "no debug name"
ENDFUNC
FUNC STRING GET_CROWD_CONTROL_HERO_ACTION( CROWD_CONTROL_PED_HERO_ACTION eHeroAction )
SWITCH eHeroAction
CASE CCHERO_NONE RETURN "CCHERO_NONE" BREAK
CASE CCHERO_ALARM RETURN "CCHERO_ALARM" BREAK
CASE CCHERO_PHONE RETURN "CCHERO_PHONE" BREAK
CASE CCHERO_GUN RETURN "CCHERO_GUN" BREAK
ENDSWITCH
RETURN "no debug name"
ENDFUNC
FUNC STRING GET_CROWD_CONTROL_FAIL_REASON_STRING( CROWD_CONTROL_FAIL_REASON eFailReason )
SWITCH eFailReason
CASE CCFAIL_NONE RETURN "CCFAIL_NONE" BREAK
CASE CCFAIL_PEDKILLED RETURN "CCFAIL_PEDKILLED" BREAK
CASE CCFAIL_PEDSKILLED RETURN "CCFAIL_PEDSKILLED" BREAK
CASE CCFAIL_PEDHEROALARM RETURN "CCFAIL_PEDHEROALARM" BREAK
CASE CCFAIL_PEDHEROPHONE RETURN "CCFAIL_PEDHEROPHONE" BREAK
CASE CCFAIL_PEDHEROGUN RETURN "CCFAIL_PEDHEROGUN" BREAK
CASE CCFAIL_FEARLOW RETURN "CCFAIL_FEARLOW" BREAK
CASE CCFAIL_TOOMANYKNOCKOUTS RETURN "CCFAIL_TOOMANYKNOCKOUTS" BREAK
CASE CCFAIL_NOHITSREMAINING RETURN "CCFAIL_NOHITSREMAINING" BREAK
ENDSWITCH
RETURN "no debug name"
ENDFUNC
FUNC STRING GET_CROWD_CONTROL_DIALOGUE_ENUM_STRING( CROWD_CONTROL_DIALOGUE eDialogue )
SWITCH eDialogue
CASE CCDIA_NONE RETURN "CCDIA_NONE" BREAK
CASE CCDIA_APPROACH RETURN "CCDIA_APPROACH" BREAK
CASE CCDIA_ALARM_GOES_OFF RETURN "CCDIA_ALARM_GOES_OFF" BREAK
CASE CCDIA_ALARM_GOES_OFF_INITIAL RETURN "CCDIA_ALARM_GOES_OFF_INITIAL" BREAK
CASE CCDIA_COWER_HIGH_FEAR RETURN "CCDIA_COWER_HIGH_FEAR" BREAK
CASE CCDIA_COWER_LOW_FEAR RETURN "CCDIA_COWER_LOW_FEAR" BREAK
CASE CCDIA_HERO_ALARM_CAUGHT RETURN "CCDIA_HERO_ALARM_CAUGHT" BREAK
CASE CCDIA_HERO_ALARM_COMPLETE RETURN "CCDIA_HERO_ALARM_COMPLETE" BREAK
CASE CCDIA_HERO_ALARM_START RETURN "CCDIA_HERO_ALARM_START" BREAK
CASE CCDIA_HERO_GUN_CAUGHT RETURN "CCDIA_HERO_GUN_CAUGHT" BREAK
CASE CCDIA_HERO_GUN_COMPLETE RETURN "CCDIA_HERO_GUN_COMPLETE" BREAK
CASE CCDIA_HERO_GUN_START RETURN "CCDIA_HERO_GUN_START" BREAK
CASE CCDIA_HERO_PHONE_CAUGHT RETURN "CCDIA_HERO_PHONE_CAUGHT" BREAK
CASE CCDIA_HERO_PHONE_COMPLETE RETURN "CCDIA_HERO_PHONE_COMPLETE" BREAK
CASE CCDIA_HERO_PHONE_START RETURN "CCDIA_HERO_PHONE_START" BREAK
CASE CCDIA_HERO_PHONE_APOLOGISE RETURN "CCDIA_HERO_PHONE_APOLOGISE" BREAK
CASE CCDIA_INITIAL_PANIC RETURN "CCDIA_INITIAL_PANIC" BREAK
CASE CCDIA_RECEIVES_SCARE RETURN "CCDIA_RECEIVES_SCARE" BREAK
CASE CCDIA_SEES_OTHER_KILLED RETURN "CCDIA_SEES_OTHER_KILLED" BREAK
CASE CCDIA_SHOT_NEAR RETURN "CCDIA_SHOT_NEAR" BREAK
CASE CCDIA_SHOT_DIRECTLY_AT RETURN "CCDIA_SHOT_DIRECTLY_AT" BREAK
CASE CCDIA_STICKS_UP_FOR_OTHER RETURN "CCDIA_STICKS_UP_FOR_OTHER" BREAK
ENDSWITCH
RETURN "no debug name"
ENDFUNC
FUNC STRING GET_CROWD_CONTROL_SEQUENCE_NAME( CROWD_CONTROL_SEQUENCES eSeq )
SWITCH eSeq
CASE CCSEQ_BEATDOWN_TO_FLINCH RETURN "CCSEQ_BEATDOWN_TO_FLINCH" BREAK
CASE CCSEQ_COWER_TO_FLINCH RETURN "CCSEQ_COWER_TO_FLINCH" BREAK
CASE CCSEQ_COWER_TO_FLINCH_UNDERFIRE RETURN "CCSEQ_COWER_TO_FLINCH_UNDERFIRE" BREAK
CASE CCSEQ_FLINCH_TO_FLINCH_UNDERFIRE RETURN "CCSEQ_FLINCH_TO_FLINCH_UNDERFIRE" BREAK
CASE CCSEQ_FLINCH_UNDERFIRE_TO_FLINCH RETURN "CCSEQ_FLINCH_UNDERFIRE_TO_FLINCH" BREAK
CASE CCSEQ_FLINCH_TO_COWER RETURN "CCSEQ_FLINCH_TO_COWER" BREAK
CASE CCSEQ_HERO_FAIL RETURN "CCSEQ_HERO_FAIL" BREAK
CASE CCSEQ_HERO_PASS RETURN "CCSEQ_HERO_PASS" BREAK
CASE CCSEQ_HERO_START RETURN "CCSEQ_HERO_START" BREAK
CASE CCSEQ_NUM_SEQUENCES RETURN "CCSEQ_NUM_SEQUENCES" BREAK
ENDSWITCH
RETURN "no debug name"
ENDFUNC
FUNC STRING GET_CROWD_CONTROL_DEBUG_NAME( CROWD_CONTROL_DEBUG_DISPLAY eDebug )
SWITCH eDebug
CASE CCDEBUG_OFF RETURN "OFF" BREAK
CASE CCDEBUG_ALL RETURN "ALL" BREAK
CASE CCDEBUG_BASIC RETURN "BASIC" BREAK
CASE CCDEBUG_FEAR_AND_THREATS RETURN "FEAR & THREATS" BREAK
CASE CCDEBUG_HERO RETURN "HERO" BREAK
CASE CCDEBUG_BEATDOWNS RETURN "BEATDOWNS" BREAK
CASE CCDEBUG_DIALOGUE RETURN "DIALOGUE" BREAK
CASE CCDEBUG_MISC RETURN "MISC" BREAK
ENDSWITCH
RETURN "no debug name"
ENDFUNC
FUNC STRING GET_CROWD_CONTROL_DEBUG_ENUM_NAME( CROWD_CONTROL_DEBUG_DISPLAY eDebug )
SWITCH eDebug
CASE CCDEBUG_OFF RETURN "CCDEBUG_OFF" BREAK
CASE CCDEBUG_ALL RETURN "CCDEBUG_ALL" BREAK
CASE CCDEBUG_BASIC RETURN "CCDEBUG_BASIC" BREAK
CASE CCDEBUG_FEAR_AND_THREATS RETURN "CCDEBUG_FEAR_AND_THREATS" BREAK
CASE CCDEBUG_HERO RETURN "CCDEBUG_HERO" BREAK
CASE CCDEBUG_BEATDOWNS RETURN "CCDEBUG_BEATDOWNS" BREAK
CASE CCDEBUG_DIALOGUE RETURN "CCDEBUG_DIALOGUE" BREAK
CASE CCDEBUG_MISC RETURN "CCDEBUG_MISC" BREAK
ENDSWITCH
RETURN "no debug name"
ENDFUNC
#ENDIF
FUNC STRING GET_CROWD_CONTROL_PED_DECOR_NAME( CROWD_CONTROL_PED_DECORATOR eDecor )
SWITCH eDecor
CASE CCPDECOR_iState RETURN "CC_iState" BREAK
CASE CCPDECOR_iStatePrev RETURN "CC_iStatePrev" BREAK
CASE CCPDECOR_iBitSet RETURN "CC_iBitSet" BREAK
CASE CCPDECOR_fInfluenceDirectThreat RETURN "CC_fInfluenceDirectThreat" BREAK
CASE CCPDECOR_fInfluenceShouting RETURN "CC_fInfluenceShouting" BREAK
CASE CCPDECOR_iBeatdownHitsRemaining RETURN "CC_iBeatdownHitsRemaining" BREAK
CASE CCPDECOR_iBeatdownRounds RETURN "CC_iBeatdownRounds" BREAK
ENDSWITCH
RETURN ""
ENDFUNC
FUNC INTERIOR_INSTANCE_INDEX GET_CROWD_CONTROL_HEIST_INTERIOR( CROWD_CONTROL_HEIST_TYPE eHeistType )
SWITCH eHeistType
CASE CCHEIST_NORMAL
RETURN NULL
BREAK
CASE CCHEIST_CUSTOM_FLEECA
RETURN GET_INTERIOR_AT_COORDS_WITH_TYPE( <<-2962.8086, 482.2888, 14.7081>>, "hei_generic_bank_dlc" ) // v_genbank
BREAK
CASE CCHEIST_CUSTOM_ORNATE
RETURN GET_INTERIOR_AT_COORDS_WITH_TYPE( <<235.9299, 216.6042, 105.2867>>, "hei_heist_ornate_bank" ) // v_bank
BREAK
ENDSWITCH
RETURN NULL
ENDFUNC
FUNC BOOL HAS_PED_ENTERED_CROWD_CONTROL_TRIGGER_ZONE( PED_INDEX ped, CROWD_CONTROL_LOCAL_DATA &sLocalData, BOOL bSoftTrigger )
BOOL bInsideInterior = ( sLocalData.interiorHeist = GET_INTERIOR_FROM_ENTITY( ped ) )
IF NOT bSoftTrigger
// HARD TRIGGER - the actual trigger we go off for triggering the crowd to kick off
SWITCH sLocalData.eHeistType
CASE CCHEIST_CUSTOM_FLEECA
// If inside the bank or near the bank doors
IF bInsideInterior
RETURN TRUE
// In a car near the doors
ELIF IS_PED_IN_ANY_VEHICLE( ped )
VEHICLE_INDEX veh
veh = GET_VEHICLE_PED_IS_IN( ped )
IF HAS_ENTITY_COLLIDED_WITH_ANYTHING( veh )
AND IS_ENTITY_IN_ANGLED_AREA( ped, <<-2967.208496,481.097839,14.468699>>, <<-2967.038574,484.783356,17.786673>>, 3.062500 )
RETURN TRUE
ENDIF
ENDIF
BREAK
CASE CCHEIST_CUSTOM_ORNATE
// Player is inside the interior and main bank room
// Additional area check to make sure the player has fulled walked into the room
IF bInsideInterior
AND GET_ROOM_KEY_FROM_ENTITY( ped ) = HASH( "Bank_MainRm" )
// revised locate size for bug 2093267
AND IS_ENTITY_IN_ANGLED_AREA( ped, <<259.844116,215.006775,105.288048>>, <<237.827515,222.779510,109.395790>>, 11.500000 )
//AND IS_ENTITY_IN_ANGLED_AREA( ped, <<259.546387,214.301697,105.286835>>, <<238.274490,222.332184,109.029305>>, 10.687500 )
RETURN TRUE
ENDIF
BREAK
ENDSWITCH
ELSE
// SOFT TRIGGER - just outside the hard trigger area, this is used to check for the player shouting
SWITCH sLocalData.eHeistType
CASE CCHEIST_CUSTOM_FLEECA
// Inside the bank
IF bInsideInterior
OR IS_ENTITY_IN_ANGLED_AREA( ped, <<-2965.820801,482.966461,14.635263>>, <<-2967.116699,483.018555,17.441607>>, 3.562500 )
RETURN TRUE
ENDIF
BREAK
CASE CCHEIST_CUSTOM_ORNATE
// Inside the bank in the main room, not the little area by the door
IF bInsideInterior
AND GET_ROOM_KEY_FROM_ENTITY( ped ) = HASH( "Bank_MainRm" )
RETURN TRUE
ENDIF
BREAK
ENDSWITCH
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL HAS_TEAM_PASSED_CROWD_CONTROL_RULE( CROWD_CONTROL_LOCAL_DATA &sLocalData, INT &iCurrentHighestPriority, INT iTeam )
IF sLocalData.iCCRule[iTeam] != -1 AND sLocalData.iCCRule[iTeam] < FMMC_MAX_RULES
AND iCurrentHighestPriority <= sLocalData.iCCRule[iTeam]
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL HAVE_ALL_PLAYERS_PASSED_CROWD_CONTROL_RULE( CROWD_CONTROL_LOCAL_DATA &sLocalData, INT &iCurrentHighestPriority[], INT iLocalPlayersTeam )
BOOL bAllPassedRule
bAllPassedRule = TRUE
INT iTeam
FOR iTeam = 0 TO FMMC_MAX_TEAMS-1
IF NOT IS_FAKE_MULTIPLAYER_MODE_SET()
OR iTeam = iLocalPlayersTeam
IF HAS_TEAM_PASSED_CROWD_CONTROL_RULE( sLocalData, iCurrentHighestPriority[iTeam], iTeam )
bAllPassedRule = FALSE
ENDIF
ENDIF
ENDFOR
RETURN bAllPassedRule
ENDFUNC
FUNC BOOL SHOULD_DISABLE_CROWD_CONTROL_FAIL( CROWD_CONTROL_LOCAL_DATA &sLocalData, INT &iCurrentHighestPriority[], INT iLocalPlayersTeam )
IF g_bMissionEnding
RETURN TRUE
ELSE
SWITCH sLocalData.eHeistType
CASE CCHEIST_CUSTOM_FLEECA
// Fleeca never disables the fail
RETURN FALSE
BREAK
CASE CCHEIST_CUSTOM_ORNATE
// If we have passed the crowd control objective for all teams, disable the fail
RETURN HAVE_ALL_PLAYERS_PASSED_CROWD_CONTROL_RULE( sLocalData, iCurrentHighestPriority, iLocalPlayersTeam )
BREAK
ENDSWITCH
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL SHOULD_PLAYER_PLAY_CROWD_CONTROL_CUTSCENE( PED_INDEX pedPlayer,
CROWD_CONTROL_LOCAL_DATA &sLocalData, INT &iCurrentHighestPriority[], INT iLocalPlayersTeam )
INT iTeam
SWITCH sLocalData.eHeistType
CASE CCHEIST_CUSTOM_FLEECA
IF NOT HAVE_ALL_PLAYERS_PASSED_CROWD_CONTROL_RULE( sLocalData, iCurrentHighestPriority, iLocalPlayersTeam )
IF GET_INTERIOR_FROM_ENTITY( pedPlayer ) = sLocalData.interiorHeist
OR GET_DISTANCE_BETWEEN_ENTITY_AND_COORD( pedPlayer, <<-2962.8086, 482.2888, 14.7081>> ) < 50.0
RETURN TRUE
ENDIF
ENDIF
BREAK
CASE CCHEIST_CUSTOM_ORNATE
FOR iTeam = 0 TO FMMC_MAX_TEAMS-1
// Before or while the crowd control rule is active
IF sLocalData.iCCRule[iTeam] != -1 AND sLocalData.iCCRule[iTeam] < FMMC_MAX_RULES
AND iCurrentHighestPriority[iTeam] <= sLocalData.iCCRule[iTeam]
// Only those in the bank lobby area for ORNATE
IF GET_INTERIOR_FROM_ENTITY( pedPlayer ) = sLocalData.interiorHeist
INT iRoomHash
iRoomHash = GET_ROOM_KEY_FROM_ENTITY( pedPlayer )
IF iRoomHash = HASH( "Bank_MainRm" )
OR iRoomHash = HASH( "Bank_CashierRm" )
OR iRoomHash = HASH( "Bank_Lobby" )
OR iRoomHash = HASH( "Bank_ATMS" )
OR iRoomHash = HASH( "Bank_Vestibule" )
OR iRoomHash = HASH( "Bank_Upperhall1" )
OR iRoomHash = HASH( "Bank_Upperhall2" )
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDFOR
BREAK
ENDSWITCH
RETURN FALSE
ENDFUNC
FUNC INT GET_CROWD_CONTROL_NUMBER_OF_DEAD_PEDS_WITH_PED_ID( CROWD_CONTROL_PED_DECORATOR_DATA &sPedData[], CROWD_CONTROL_SERVER_DATA &sServerData, INT &iOptionalDeadPedID )
INT iCount
iOptionalDeadPedID = -1
INT i
FOR i = 0 TO sServerData.iNumCrowdControlPeds-1
// Count the number of dead peds
IF IS_BIT_SET_ENUM( sPedData[i].iBitSet, CCPEDDATA_Dead )
AND NOT IS_BIT_SET_ENUM( sPedData[i].iBitSet, CCPEDDATA_KnockedOut )
IF iOptionalDeadPedID = -1
iOptionalDeadPedID = sServerData.iCrowdControlCloudIndex[i]
ENDIF
iCount++
ENDIF
ENDFOR
RETURN iCount
ENDFUNC
FUNC INT GET_CROWD_CONTROL_NUMBER_OF_DEAD_PEDS( CROWD_CONTROL_PED_DECORATOR_DATA &sPedData[], CROWD_CONTROL_SERVER_DATA &sServerData )
INT iDummy
RETURN GET_CROWD_CONTROL_NUMBER_OF_DEAD_PEDS_WITH_PED_ID( sPedData, sServerData, iDummy )
ENDFUNC
FUNC INT GET_CROWD_CONTROL_NUMBER_OF_KNOCKED_OUT_PEDS_WITH_PED_ID( CROWD_CONTROL_PED_DECORATOR_DATA &sPedData[], CROWD_CONTROL_SERVER_DATA &sServerData, INT &iOptionalKnockedOutPedID )
INT iCount
iOptionalKnockedOutPedID = -1
INT i
FOR i = 0 TO sServerData.iNumCrowdControlPeds-1
// Count the number of knocked out peds
IF IS_BIT_SET_ENUM( sPedData[i].iBitSet, CCPEDDATA_KnockedOut )
IF iOptionalKnockedOutPedID = -1
iOptionalKnockedOutPedID = sServerData.iCrowdControlCloudIndex[i]
ENDIF
iCount++
ENDIF
ENDFOR
RETURN iCount
ENDFUNC
FUNC INT GET_CROWD_CONTROL_NUMBER_OF_KNOCKED_OUT_PEDS( CROWD_CONTROL_PED_DECORATOR_DATA &sPedData[], CROWD_CONTROL_SERVER_DATA &sServerData )
INT iDummy
RETURN GET_CROWD_CONTROL_NUMBER_OF_KNOCKED_OUT_PEDS_WITH_PED_ID( sPedData, sServerData, iDummy )
ENDFUNC
FUNC INT GET_CROWD_CONTROL_PED_DEAD_LIMIT( CROWD_CONTROL_HEIST_TYPE eHeistType )
INT iResult
SWITCH eHeistType
CASE CCHEIST_CUSTOM_ORNATE
iResult = 1
BREAK
DEFAULT
iResult = 0
BREAK
ENDSWITCH
RETURN iResult
ENDFUNC
FUNC FLOAT GET_CROWD_CONTROL_PED_ESCAPED_DISTANCE( CROWD_CONTROL_HEIST_TYPE eHeistType )
UNUSED_PARAMETER( eHeistType )
RETURN 20.0
ENDFUNC
FUNC INT GET_CROWD_CONTROL_FAIL_CUTSCENE( CROWD_CONTROL_HEIST_TYPE eHeistType, NETWORK_INDEX &niPeds[], INT &iCrowdCloudIndex[], CROWD_CONTROL_LOCAL_PED_DATA &sLocalPedData[], INT iNumCrowdControlPeds, TEXT_LABEL_63 &tlCutsceneNameOut, CUTSCENE_SECTION &eCutsceneSectionOut )
INT i
BOOL bDeadCashierA, bDeadCashierB
FOR i = 0 TO iNumCrowdControlPeds-1
IF sLocalPedData[i].eCreatorAssignedPedID = CCPED_CASHIER_A
OR sLocalPedData[i].eCreatorAssignedPedID = CCPED_CASHIER_B
IF NOT NETWORK_DOES_ENTITY_EXIST_WITH_NETWORK_ID( niPeds[iCrowdCloudIndex[i]] )
OR IS_PED_INJURED( NET_TO_PED( niPeds[iCrowdCloudIndex[i]] ) )
IF sLocalPedData[i].eCreatorAssignedPedID = CCPED_CASHIER_A
bDeadCashierA = TRUE
ELIF sLocalPedData[i].eCreatorAssignedPedID = CCPED_CASHIER_B
bDeadCashierB = TRUE
ENDIF
ENDIF
ENDIF
ENDFOR
SWITCH eHeistType
CASE CCHEIST_CUSTOM_FLEECA
IF NOT bDeadCashierA
tlCutsceneNameOut = "MPH_TUT_MCS1"
eCutsceneSectionOut = INT_TO_ENUM( CUTSCENE_SECTION, -1 )
ELSE
// cashier dead bail on cutscene
tlCutsceneNameOut = ""
ENDIF
BREAK
CASE CCHEIST_CUSTOM_ORNATE
IF NOT bDeadCashierA
tlCutsceneNameOut = "MPH_PAC_FIN_MCS2"
eCutsceneSectionOut = CS_SECTION_1
ELIF NOT bDeadCashierB
tlCutsceneNameOut = "MPH_PAC_FIN_MCS2"
eCutsceneSectionOut = CS_SECTION_2
ELSE
// both dead bail on cutscene
tlCutsceneNameOut = ""
ENDIF
BREAK
ENDSWITCH
// Create hash for comparison
TEXT_LABEL_63 tlTemp
tlTemp = tlCutsceneNameOut
tlTemp += ENUM_TO_INT( eCutsceneSectionOut )
RETURN GET_HASH_KEY( tlTemp )
ENDFUNC
FUNC BOOL GET_CROWD_CONTROL_FAIL_CUTSCENE_FROM_HASH( INT iHashIn, TEXT_LABEL_63 &tlName, CUTSCENE_SECTION &eSection )
TEXT_LABEL_63 tlTemp
tlTemp = "MPH_TUT_MCS1"
tlTemp += -1
IF iHashIn = GET_HASH_KEY( tlTemp )
tlName = "MPH_TUT_MCS1"
eSection = INT_TO_ENUM( CUTSCENE_SECTION, -1 )
RETURN TRUE
ENDIF
tlTemp = "MPH_PAC_FIN_MCS2"
tlTemp += ENUM_TO_INT( CS_SECTION_1 )
IF iHashIn = GET_HASH_KEY( tlTemp )
tlName = "MPH_PAC_FIN_MCS2"
eSection = CS_SECTION_1
RETURN TRUE
ENDIF
tlTemp = "MPH_PAC_FIN_MCS2"
tlTemp += ENUM_TO_INT( CS_SECTION_2 )
IF iHashIn = GET_HASH_KEY( tlTemp )
tlName = "MPH_PAC_FIN_MCS2"
eSection = CS_SECTION_2
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL GET_CROWD_CONTROL_PED_DETAILS_FOR_FAIL_CUTSCENE_FROM_HASH( INT iHashIn, NETWORK_INDEX &niPeds[], INT &iCrowdCloudIndex[], CROWD_CONTROL_LOCAL_PED_DATA &sLocalPedData[], INT iNumCrowdControlPeds, PED_INDEX &pedOut, TEXT_LABEL_63 &tlHandleOut, INT &iAnimEventHashAlarmOut )
TEXT_LABEL_63 tlCutsceneName
CUTSCENE_SECTION eSection
IF GET_CROWD_CONTROL_FAIL_CUTSCENE_FROM_HASH( iHashIn, tlCutsceneName, eSection )
TEXT_LABEL_63 tlHandleTemp
INT iAnimEventHashAlarmTemp
PED_INDEX pedTemp
CROWD_CONTROL_PED_ENUM ePedWanted
// Find the handle name and the ped enum
IF ARE_STRINGS_EQUAL( tlCutsceneName, "MPH_TUT_MCS1" )
tlHandleTemp = "Cashier"
ePedWanted = CCPED_CASHIER_A
iAnimEventHashAlarmTemp = HASH( "GET_HYPE" )
ELIF ARE_STRINGS_EQUAL( tlCutsceneName, "MPH_PAC_FIN_MCS2" )
SWITCH eSection
CASE CS_SECTION_1
ePedWanted = CCPED_CASHIER_A
tlHandleTemp = "Bank_Teller_02"
iAnimEventHashAlarmTemp = HASH( "GET_HYPE1" )
BREAK
CASE CS_SECTION_2
ePedWanted = CCPED_CASHIER_B
tlHandleTemp = "Bank_Teller_01"
iAnimEventHashAlarmTemp = HASH( "GET_HYPE2" )
BREAK
ENDSWITCH
ENDIF
// Find the ped ID
INT i
FOR i = 0 TO iNumCrowdControlPeds-1
IF sLocalPedData[i].eCreatorAssignedPedID = ePedWanted
IF NETWORK_DOES_ENTITY_EXIST_WITH_NETWORK_ID( niPeds[iCrowdCloudIndex[i]] )
pedTemp = NET_TO_PED( niPeds[iCrowdCloudIndex[i]] )
i = FMMC_MAX_CROWD_PEDS // break the loop
ENDIF
ENDIF
ENDFOR
IF NOT IS_STRING_NULL_OR_EMPTY( tlHandleTemp )
AND iAnimEventHashAlarmTemp != 0
AND DOES_ENTITY_EXIST( pedTemp )
tlHandleOut = tlHandleTemp
iAnimEventHashAlarmOut = iAnimEventHashAlarmTemp
pedOut = pedTemp
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC STRING GET_CROWD_CONTROL_PED_ANIM_DICTIONARY( CROWD_CONTROL_PED_ANIM_TYPE eAnimType, CROWD_CONTROL_PED_ENUM eCrowdControlAnimID, CROWD_CONTROL_HEIST_TYPE eHeistType )
IF eHeistType = CCHEIST_CUSTOM_FLEECA
SWITCH eCrowdControlAnimID
CASE CCPED_CASHIER_A eCrowdControlAnimID = CCPED_CUSTOMER_D BREAK
CASE CCPED_CUSTOMER_A eCrowdControlAnimID = CCPED_CUSTOMER_A BREAK
CASE CCPED_CUSTOMER_B eCrowdControlAnimID = CCPED_CUSTOMER_E BREAK
ENDSWITCH
ENDIF
// Common
SWITCH eAnimType
CASE CCPANIM_COWER_LOOP
CASE CCPANIM_COWER_TO_FLINCH_INTRO
CASE CCPANIM_COWER_TO_FLINCH_UNDERFIRE_INTRO
CASE CCPANIM_FLINCH_LOOP
CASE CCPANIM_FLINCH_TO_IDLE_OUTRO
CASE CCPANIM_FLINCH_TO_FLINCH_UNDERFIRE_INTRO
CASE CCPANIM_FLINCH_UNDERFIRE_LOOP
CASE CCPANIM_FLINCH_UNDERFIRE_TO_FLINCH_OUTRO
SWITCH eCrowdControlAnimID
CASE CCPED_CASHIER_A RETURN "anim@heists@ornate_bank@hostages@cashier_a@" BREAK
CASE CCPED_CASHIER_B RETURN "anim@heists@ornate_bank@hostages@cashier_b@" BREAK
CASE CCPED_CUSTOMER_A RETURN "anim@heists@ornate_bank@hostages@ped_a@" BREAK
CASE CCPED_CUSTOMER_B RETURN "anim@heists@ornate_bank@hostages@ped_b@" BREAK
CASE CCPED_CUSTOMER_C RETURN "anim@heists@ornate_bank@hostages@ped_c@" BREAK
CASE CCPED_CUSTOMER_D
IF eHeistType = CCHEIST_CUSTOM_FLEECA
RETURN "anim@heists@fleeca_bank@hostages@ped_d@"
ELSE
RETURN "anim@heists@ornate_bank@hostages@ped_d@"
ENDIF
BREAK
CASE CCPED_CUSTOMER_E RETURN "anim@heists@ornate_bank@hostages@ped_e@" BREAK
CASE CCPED_CUSTOMER_F RETURN "anim@heists@ornate_bank@hostages@ped_f@" BREAK
CASE CCPED_CUSTOMER_G RETURN "anim@heists@ornate_bank@hostages@ped_g@" BREAK
CASE CCPED_CUSTOMER_H RETURN "anim@heists@ornate_bank@hostages@ped_h@" BREAK
ENDSWITCH
BREAK
CASE CCPANIM_BEATDOWN_INTRO
CASE CCPANIM_BEATDOWN_LOOP
CASE CCPANIM_BEATDOWN_HIT_A
CASE CCPANIM_BEATDOWN_HIT_B
CASE CCPANIM_BEATDOWN_DIE
CASE CCPANIM_BEATDOWN_DIE_LOOP
CASE CCPANIM_BEATDOWN_OUTRO
RETURN "anim@heists@ornate_bank@hostages@hit"
BREAK
ENDSWITCH
// heist specific
SWITCH eHeistType
CASE CCHEIST_CUSTOM_FLEECA
SWITCH eAnimType
CASE CCPANIM_INTRO_LOOP
CASE CCPANIM_INTRO
RETURN "anim@heists@fleeca_bank@hostages@intro"
BREAK
ENDSWITCH
BREAK
CASE CCHEIST_CUSTOM_ORNATE
SWITCH eAnimType
CASE CCPANIM_INTRO_LOOP
CASE CCPANIM_INTRO
RETURN "anim@heists@ornate_bank@hostages@intro"
BREAK
CASE CCPANIM_HERO_START
CASE CCPANIM_HERO_LOOP
CASE CCPANIM_HERO_PASS
CASE CCPANIM_HERO_FAIL
SWITCH eCrowdControlAnimID
CASE CCPED_CASHIER_A RETURN "anim@heists@ornate_bank@hostages@cashier_a@" BREAK
CASE CCPED_CUSTOMER_B RETURN "anim@heists@ornate_bank@hostages@ped_b@" BREAK
CASE CCPED_CUSTOMER_C RETURN "anim@heists@ornate_bank@hostages@ped_c@" BREAK
CASE CCPED_CUSTOMER_D RETURN "anim@heists@ornate_bank@hostages@ped_d@" BREAK
CASE CCPED_CUSTOMER_G RETURN "anim@heists@ornate_bank@hostages@ped_g@" BREAK
ENDSWITCH
BREAK
ENDSWITCH
BREAK
ENDSWITCH
RETURN ""
ENDFUNC
FUNC STRING GET_CROWD_CONTROL_PED_ANIM_NAME( CROWD_CONTROL_PED_ANIM_TYPE eAnimType, CROWD_CONTROL_PED_ENUM eCrowdControlAnimID, CROWD_CONTROL_HEIST_TYPE eHeistType )
IF eHeistType = CCHEIST_CUSTOM_FLEECA
SWITCH eCrowdControlAnimID
CASE CCPED_CASHIER_A eCrowdControlAnimID = CCPED_CUSTOMER_D BREAK
CASE CCPED_CUSTOMER_A eCrowdControlAnimID = CCPED_CUSTOMER_A BREAK
CASE CCPED_CUSTOMER_B eCrowdControlAnimID = CCPED_CUSTOMER_E BREAK
ENDSWITCH
ENDIF
// Generic
SWITCH eAnimType
CASE CCPANIM_COWER_LOOP RETURN "idle" BREAK
CASE CCPANIM_COWER_TO_FLINCH_INTRO RETURN "flinch_intro" BREAK
CASE CCPANIM_FLINCH_LOOP RETURN "flinch_loop" BREAK
CASE CCPANIM_FLINCH_TO_IDLE_OUTRO RETURN "flinch_outro" BREAK
CASE CCPANIM_COWER_TO_FLINCH_UNDERFIRE_INTRO RETURN "flinch_under_fire_intro" BREAK
CASE CCPANIM_FLINCH_TO_FLINCH_UNDERFIRE_INTRO
SWITCH eCrowdControlAnimID
CASE CCPED_CUSTOMER_B
CASE CCPED_CUSTOMER_C
CASE CCPED_CUSTOMER_D
CASE CCPED_CUSTOMER_E
CASE CCPED_CUSTOMER_F
CASE CCPED_CUSTOMER_G
CASE CCPED_CUSTOMER_H
RETURN "flinch_to_underfire"
BREAK
ENDSWITCH
BREAK
CASE CCPANIM_FLINCH_UNDERFIRE_TO_FLINCH_OUTRO
SWITCH eCrowdControlAnimID
CASE CCPED_CUSTOMER_B
CASE CCPED_CUSTOMER_C
CASE CCPED_CUSTOMER_D
CASE CCPED_CUSTOMER_E
CASE CCPED_CUSTOMER_F
CASE CCPED_CUSTOMER_G
CASE CCPED_CUSTOMER_H
RETURN "flinch_under_fire_outro"
BREAK
ENDSWITCH
BREAK
CASE CCPANIM_FLINCH_UNDERFIRE_LOOP RETURN "flinch_loop_underfire" BREAK
CASE CCPANIM_BEATDOWN_INTRO
SWITCH eCrowdControlAnimID
CASE CCPED_CASHIER_A RETURN "hit_intro_cashier_a" BREAK
CASE CCPED_CASHIER_B RETURN "hit_intro_cashier_b" BREAK
CASE CCPED_CUSTOMER_A RETURN "hit_intro_ped_a" BREAK
CASE CCPED_CUSTOMER_B RETURN "hit_intro_ped_b" BREAK
CASE CCPED_CUSTOMER_C RETURN "hit_intro_ped_c" BREAK
CASE CCPED_CUSTOMER_D RETURN "hit_intro_ped_d" BREAK
CASE CCPED_CUSTOMER_E RETURN "hit_intro_ped_e" BREAK
CASE CCPED_CUSTOMER_F RETURN "hit_intro_ped_f" BREAK
CASE CCPED_CUSTOMER_G RETURN "hit_intro_ped_g" BREAK
CASE CCPED_CUSTOMER_H RETURN "hit_intro_ped_h" BREAK
ENDSWITCH
BREAK
CASE CCPANIM_BEATDOWN_LOOP
SWITCH eCrowdControlAnimID
CASE CCPED_CASHIER_A RETURN "hit_loop_cashier_a" BREAK
CASE CCPED_CASHIER_B RETURN "hit_loop_cashier_b" BREAK
CASE CCPED_CUSTOMER_A RETURN "hit_loop_ped_a" BREAK
CASE CCPED_CUSTOMER_B RETURN "hit_loop_ped_b" BREAK
CASE CCPED_CUSTOMER_C RETURN "hit_loop_ped_c" BREAK
CASE CCPED_CUSTOMER_D RETURN "hit_loop_ped_d" BREAK
CASE CCPED_CUSTOMER_E RETURN "hit_loop_ped_e" BREAK
CASE CCPED_CUSTOMER_F RETURN "hit_loop_ped_f" BREAK
CASE CCPED_CUSTOMER_G RETURN "hit_loop_ped_g" BREAK
CASE CCPED_CUSTOMER_H RETURN "hit_loop_ped_h" BREAK
ENDSWITCH
BREAK
CASE CCPANIM_BEATDOWN_HIT_A
SWITCH eCrowdControlAnimID
CASE CCPED_CASHIER_A RETURN "hit_react_a_cashier_a" BREAK
CASE CCPED_CASHIER_B RETURN "hit_react_a_cashier_b" BREAK
CASE CCPED_CUSTOMER_A RETURN "hit_react_a_ped_a" BREAK
CASE CCPED_CUSTOMER_B RETURN "hit_react_a_ped_b" BREAK
CASE CCPED_CUSTOMER_C RETURN "hit_react_a_ped_c" BREAK
CASE CCPED_CUSTOMER_D RETURN "hit_react_a_ped_d" BREAK
CASE CCPED_CUSTOMER_E RETURN "hit_react_a_ped_e" BREAK
CASE CCPED_CUSTOMER_F RETURN "hit_react_a_ped_f" BREAK
CASE CCPED_CUSTOMER_G RETURN "hit_react_a_ped_g" BREAK
CASE CCPED_CUSTOMER_H RETURN "hit_react_a_ped_h" BREAK
ENDSWITCH
BREAK
CASE CCPANIM_BEATDOWN_HIT_B
SWITCH eCrowdControlAnimID
CASE CCPED_CASHIER_A RETURN "hit_react_b_cashier_a" BREAK
CASE CCPED_CASHIER_B RETURN "hit_react_b_cashier_b" BREAK
CASE CCPED_CUSTOMER_A RETURN "hit_react_b_ped_a" BREAK
CASE CCPED_CUSTOMER_B RETURN "hit_react_b_ped_b" BREAK
CASE CCPED_CUSTOMER_C RETURN "hit_react_b_ped_c" BREAK
CASE CCPED_CUSTOMER_D RETURN "hit_react_b_ped_d" BREAK
CASE CCPED_CUSTOMER_E RETURN "hit_react_b_ped_e" BREAK
CASE CCPED_CUSTOMER_F RETURN "hit_react_b_ped_f" BREAK
CASE CCPED_CUSTOMER_G RETURN "hit_react_b_ped_g" BREAK
CASE CCPED_CUSTOMER_H RETURN "hit_react_b_ped_h" BREAK
ENDSWITCH
BREAK
CASE CCPANIM_BEATDOWN_DIE
SWITCH eCrowdControlAnimID
CASE CCPED_CASHIER_A RETURN "hit_react_die_cashier_a" BREAK
CASE CCPED_CASHIER_B RETURN "hit_react_die_cashier_b" BREAK
CASE CCPED_CUSTOMER_A RETURN "hit_react_die_ped_a" BREAK
CASE CCPED_CUSTOMER_B RETURN "hit_react_die_ped_b" BREAK
CASE CCPED_CUSTOMER_C RETURN "hit_react_die_ped_c" BREAK
CASE CCPED_CUSTOMER_D RETURN "hit_react_die_ped_d" BREAK
CASE CCPED_CUSTOMER_E RETURN "hit_react_die_ped_e" BREAK
CASE CCPED_CUSTOMER_F RETURN "hit_react_die_ped_f" BREAK
CASE CCPED_CUSTOMER_G RETURN "hit_react_die_ped_g" BREAK
CASE CCPED_CUSTOMER_H RETURN "hit_react_die_ped_h" BREAK
ENDSWITCH
BREAK
CASE CCPANIM_BEATDOWN_DIE_LOOP
SWITCH eCrowdControlAnimID
CASE CCPED_CASHIER_A RETURN "hit_react_die_loop_cashier_a" BREAK
CASE CCPED_CASHIER_B RETURN "hit_react_die_loop_cashier_b" BREAK
CASE CCPED_CUSTOMER_A RETURN "hit_react_die_loop_ped_a" BREAK
CASE CCPED_CUSTOMER_B RETURN "hit_react_die_loop_ped_b" BREAK
CASE CCPED_CUSTOMER_C RETURN "hit_react_die_loop_ped_c" BREAK
CASE CCPED_CUSTOMER_D RETURN "hit_react_die_loop_ped_d" BREAK
CASE CCPED_CUSTOMER_E RETURN "hit_react_die_loop_ped_e" BREAK
CASE CCPED_CUSTOMER_F RETURN "hit_react_die_loop_ped_f" BREAK
CASE CCPED_CUSTOMER_G RETURN "hit_react_die_loop_ped_g" BREAK
CASE CCPED_CUSTOMER_H RETURN "hit_react_die_loop_ped_h" BREAK
ENDSWITCH
BREAK
CASE CCPANIM_BEATDOWN_OUTRO
SWITCH eCrowdControlAnimID
CASE CCPED_CASHIER_A RETURN "hit_react_outro_cashier_a" BREAK
CASE CCPED_CASHIER_B RETURN "hit_react_outro_cashier_b" BREAK
CASE CCPED_CUSTOMER_A RETURN "hit_react_outro_ped_a" BREAK
CASE CCPED_CUSTOMER_B RETURN "hit_react_outro_ped_b" BREAK
CASE CCPED_CUSTOMER_C RETURN "hit_react_outro_ped_c" BREAK
CASE CCPED_CUSTOMER_D RETURN "hit_react_outro_ped_d" BREAK
CASE CCPED_CUSTOMER_E RETURN "hit_react_outro_ped_e" BREAK
CASE CCPED_CUSTOMER_F RETURN "hit_react_outro_ped_f" BREAK
CASE CCPED_CUSTOMER_G RETURN "hit_react_outro_ped_g" BREAK
CASE CCPED_CUSTOMER_H RETURN "hit_react_outro_ped_h" BREAK
ENDSWITCH
BREAK
CASE CCPANIM_HERO_START RETURN "reach" BREAK
CASE CCPANIM_HERO_LOOP RETURN "reach_loop" BREAK
CASE CCPANIM_HERO_PASS RETURN "pass" BREAK
CASE CCPANIM_HERO_FAIL RETURN "fail" BREAK
ENDSWITCH
// Heist specific
SWITCH eHeistType
CASE CCHEIST_CUSTOM_FLEECA
SWITCH eAnimType
CASE CCPANIM_INTRO_LOOP
SWITCH eCrowdControlAnimID
CASE CCPED_CUSTOMER_D RETURN "intro_loop_ped_d" BREAK
CASE CCPED_CUSTOMER_A RETURN "intro_loop_ped_a" BREAK
CASE CCPED_CUSTOMER_E RETURN "intro_loop_ped_e" BREAK
ENDSWITCH
BREAK
CASE CCPANIM_INTRO
SWITCH eCrowdControlAnimID
CASE CCPED_CUSTOMER_D RETURN "intro_standing_ped_d" BREAK // "intro_ped_d"
CASE CCPED_CUSTOMER_A RETURN "intro_ped_a" BREAK
CASE CCPED_CUSTOMER_E RETURN "intro_ped_e" BREAK
ENDSWITCH
BREAK
ENDSWITCH
BREAK
CASE CCHEIST_CUSTOM_ORNATE
SWITCH eAnimType
CASE CCPANIM_INTRO_LOOP
SWITCH eCrowdControlAnimID
CASE CCPED_CASHIER_A RETURN "intro_loop_cashier_a" BREAK
CASE CCPED_CASHIER_B RETURN "intro_loop_cashier_b" BREAK
CASE CCPED_CUSTOMER_A RETURN "intro_loop_ped_a" BREAK
CASE CCPED_CUSTOMER_B RETURN "intro_loop_ped_b" BREAK
CASE CCPED_CUSTOMER_C RETURN "intro_loop_ped_c" BREAK
CASE CCPED_CUSTOMER_D RETURN "intro_loop_ped_d" BREAK
CASE CCPED_CUSTOMER_E RETURN "intro_loop_ped_e" BREAK
CASE CCPED_CUSTOMER_F RETURN "intro_loop_ped_f" BREAK
CASE CCPED_CUSTOMER_G RETURN "intro_loop_ped_g" BREAK
CASE CCPED_CUSTOMER_H RETURN "intro_loop_ped_h" BREAK
ENDSWITCH
BREAK
CASE CCPANIM_INTRO
SWITCH eCrowdControlAnimID
CASE CCPED_CASHIER_A RETURN "intro_cashier_a" BREAK
CASE CCPED_CASHIER_B RETURN "intro_cashier_b" BREAK
CASE CCPED_CUSTOMER_A RETURN "intro_ped_a" BREAK
CASE CCPED_CUSTOMER_B RETURN "intro_ped_b" BREAK
CASE CCPED_CUSTOMER_C RETURN "intro_ped_c" BREAK
CASE CCPED_CUSTOMER_D RETURN "intro_ped_d" BREAK
CASE CCPED_CUSTOMER_E RETURN "intro_ped_e" BREAK
CASE CCPED_CUSTOMER_F RETURN "intro_ped_f" BREAK
CASE CCPED_CUSTOMER_G RETURN "intro_ped_g" BREAK
CASE CCPED_CUSTOMER_H RETURN "intro_ped_h" BREAK
ENDSWITCH
BREAK
ENDSWITCH
BREAK
ENDSWITCH
RETURN ""
ENDFUNC
FUNC STRING GET_CROWD_CONTROL_PLAYER_ANIM_DICTIONARY( CROWD_CONTROL_PLAYER_ANIM_TYPE eAnimType )
SWITCH eAnimType
CASE CCPLAYERANIM_BEATDOWN_PISTOL_A
CASE CCPLAYERANIM_BEATDOWN_PISTOL_B
CASE CCPLAYERANIM_BEATDOWN_PISTOL_KICK_A
CASE CCPLAYERANIM_BEATDOWN_PISTOL_KICK_B
CASE CCPLAYERANIM_BEATDOWN_RIFLE_A
CASE CCPLAYERANIM_BEATDOWN_RIFLE_B
CASE CCPLAYERANIM_BEATDOWN_RIFLE_KICK_A
CASE CCPLAYERANIM_BEATDOWN_RIFLE_KICK_B
CASE CCPLAYERANIM_BEATDOWN_LONG_PISTOL_A
CASE CCPLAYERANIM_BEATDOWN_LONG_PISTOL_KICK_A
CASE CCPLAYERANIM_BEATDOWN_LONG_RIFLE_A
CASE CCPLAYERANIM_BEATDOWN_LONG_RIFLE_B
CASE CCPLAYERANIM_BEATDOWN_LONG_RIFLE_KICK_A
CASE CCPLAYERANIM_BEATDOWN_LONG_RIFLE_KICK_B
RETURN "anim@heists@ornate_bank@hostages@hit"
BREAK
ENDSWITCH
RETURN ""
ENDFUNC
FUNC STRING GET_CROWD_CONTROL_PLAYER_ANIM_NAME( CROWD_CONTROL_PLAYER_ANIM_TYPE eAnimType )
SWITCH eAnimType
CASE CCPLAYERANIM_BEATDOWN_PISTOL_A RETURN "player_melee_pistol_a" BREAK
CASE CCPLAYERANIM_BEATDOWN_PISTOL_B RETURN "player_melee_pistol_b" BREAK
CASE CCPLAYERANIM_BEATDOWN_PISTOL_KICK_A RETURN "player_melee_pistol_kick_a" BREAK
CASE CCPLAYERANIM_BEATDOWN_PISTOL_KICK_B RETURN "player_melee_pistol_kick_b" BREAK
CASE CCPLAYERANIM_BEATDOWN_LONG_PISTOL_A RETURN "player_melee_long_pistol_a" BREAK
CASE CCPLAYERANIM_BEATDOWN_LONG_PISTOL_KICK_A RETURN "player_melee_long_pistol_kick_a" BREAK
CASE CCPLAYERANIM_BEATDOWN_RIFLE_A RETURN "player_melee_rifle_a" BREAK
CASE CCPLAYERANIM_BEATDOWN_RIFLE_B RETURN "player_melee_rifle_b" BREAK
CASE CCPLAYERANIM_BEATDOWN_RIFLE_KICK_A RETURN "player_melee_rifle_kick_a" BREAK
CASE CCPLAYERANIM_BEATDOWN_RIFLE_KICK_B RETURN "player_melee_rifle_kick_b" BREAK
CASE CCPLAYERANIM_BEATDOWN_LONG_RIFLE_A RETURN "player_melee_long_rifle_a" BREAK
CASE CCPLAYERANIM_BEATDOWN_LONG_RIFLE_B RETURN "player_melee_long_rifle_b" BREAK
CASE CCPLAYERANIM_BEATDOWN_LONG_RIFLE_KICK_A RETURN "player_melee_long_rifle_kick_a" BREAK
CASE CCPLAYERANIM_BEATDOWN_LONG_RIFLE_KICK_B RETURN "player_melee_long_rifle_kick_b" BREAK
ENDSWITCH
RETURN ""
ENDFUNC
FUNC CROWD_CONTROL_PED_HERO_ACTION GET_CROWD_CONTROL_PED_HERO_ACTION_FOR_ANIMATED_PED( CROWD_CONTROL_PED_ENUM eCrowdControlAnimID, CROWD_CONTROL_HEIST_TYPE eHeistType )
SWITCH eHeistType
CASE CCHEIST_CUSTOM_FLEECA
SWITCH eCrowdControlAnimID
CASE CCPED_CASHIER_A RETURN CCHERO_ALARM BREAK
ENDSWITCH
BREAK
CASE CCHEIST_CUSTOM_ORNATE
SWITCH eCrowdControlAnimID
// cashier A has a hero anim set
CASE CCPED_CASHIER_A RETURN CCHERO_ALARM BREAK
CASE CCPED_CUSTOMER_B RETURN CCHERO_GUN BREAK
CASE CCPED_CUSTOMER_C RETURN CCHERO_PHONE BREAK
CASE CCPED_CUSTOMER_D RETURN CCHERO_PHONE BREAK
CASE CCPED_CUSTOMER_G RETURN CCHERO_GUN BREAK
ENDSWITCH
BREAK
ENDSWITCH
RETURN CCHERO_NONE
ENDFUNC
FUNC VECTOR GET_CROWD_CONTROL_PED_ANIM_SCENE_COORD( CROWD_CONTROL_HEIST_TYPE eHeistType, CROWD_CONTROL_PED_ANIM_TYPE eAnim )
SWITCH eHeistType
// Fleeca uses an origin only for the intro loop and intro transition
// after which the anims are played in place so no origin needed
CASE CCHEIST_CUSTOM_FLEECA
IF eAnim = CCPANIM_INTRO OR eAnim = CCPANIM_INTRO_LOOP
RETURN << -2965.760, 484.220, 16.036 >>
ENDIF
BREAK
// All ornate anims use the same origin
CASE CCHEIST_CUSTOM_ORNATE
IF eAnim = CCPANIM_INTRO OR eAnim = CCPANIM_INTRO_LOOP
RETURN <<249.2121, 217.7811, 105.277>>
ENDIF
BREAK
ENDSWITCH
RETURN <<0,0,0>>
ENDFUNC
FUNC VECTOR GET_CROWD_CONTROL_PED_ANIM_SCENE_ROT( CROWD_CONTROL_HEIST_TYPE eHeistType )
SWITCH eHeistType
CASE CCHEIST_CUSTOM_FLEECA RETURN <<0,0,-92.750>> BREAK
CASE CCHEIST_CUSTOM_ORNATE RETURN <<0,0,-20>> BREAK
ENDSWITCH
RETURN <<0,0,0>>
ENDFUNC
FUNC BOOL DOES_CROWD_CONTROL_PED_HAVE_ANIM( CROWD_CONTROL_PED_ANIM_TYPE eAnimType, CROWD_CONTROL_PED_ENUM eCrowdControlAnimID, CROWD_CONTROL_HEIST_TYPE eHeistType )
IF NOT IS_STRING_NULL_OR_EMPTY( GET_CROWD_CONTROL_PED_ANIM_DICTIONARY( eAnimType, eCrowdControlAnimID, eHeistType ) )
AND NOT IS_STRING_NULL_OR_EMPTY( GET_CROWD_CONTROL_PED_ANIM_NAME( eAnimType, eCrowdControlAnimID, eHeistType ) )
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL IS_PED_PLAYING_ANIM( PED_INDEX ped, STRING strAnimDict, STRING strAnimName, BOOL bUseSyncSceneCheck = FALSE )
BOOL bRunning
ANIM_TYPE eAnimType
IF bUseSyncSceneCheck
bRunning = IS_SCRIPT_TASK_RUNNING_OR_STARTING( ped, SCRIPT_TASK_SYNCHRONIZED_SCENE )
eAnimType = ANIM_SYNCED_SCENE
ELSE
bRunning = IS_SCRIPT_TASK_RUNNING_OR_STARTING( ped, SCRIPT_TASK_PLAY_ANIM )
OR IS_SCRIPT_TASK_RUNNING_OR_STARTING( ped, SCRIPT_TASK_PERFORM_SEQUENCE )
eAnimType = ANIM_DEFAULT
ENDIF
IF bRunning
IF IS_ENTITY_PLAYING_ANIM( ped, strAnimDict, strAnimName, eAnimType )
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL IS_CROWD_CONTROL_PED_PLAYING_ANIM( PED_INDEX ped, CROWD_CONTROL_PED_ANIM_TYPE eAnim, CROWD_CONTROL_PED_ENUM eCrowdControlAnimID, CROWD_CONTROL_HEIST_TYPE eHeistType, BOOL bUseSyncSceneCheck = FALSE )
IF DOES_CROWD_CONTROL_PED_HAVE_ANIM( eAnim, eCrowdControlAnimID, eHeistType )
STRING strAnimDict = GET_CROWD_CONTROL_PED_ANIM_DICTIONARY( eAnim, eCrowdControlAnimID, eHeistType )
STRING strAnimName = GET_CROWD_CONTROL_PED_ANIM_NAME( eAnim, eCrowdControlAnimID, eHeistType )
RETURN IS_PED_PLAYING_ANIM( ped, strAnimDict, strAnimName, bUseSyncSceneCheck )
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL IS_CROWD_CONTROL_PLAYER_PLAYING_ANIM( PED_INDEX ped, CROWD_CONTROL_PLAYER_ANIM_TYPE eAnim, BOOL bUseSyncSceneCheck = FALSE )
STRING strAnimDict = GET_CROWD_CONTROL_PLAYER_ANIM_DICTIONARY( eAnim )
STRING strAnimName = GET_CROWD_CONTROL_PLAYER_ANIM_NAME( eAnim )
RETURN IS_PED_PLAYING_ANIM( ped, strAnimDict, strAnimName, bUseSyncSceneCheck )
ENDFUNC
FUNC BOOL HAS_PED_FINISHED_ANIM( PED_INDEX ped, STRING strAnimDict, STRING strAnimName, FLOAT fFinishPhase = 1.0, BOOL bUseSyncSceneCheck = FALSE, INT iNetSyncSceneID = -1, INT iAnimEndTagHash = 0 )
IF IS_PED_PLAYING_ANIM( ped, strAnimDict, strAnimName, bUseSyncSceneCheck )
BOOL bResult
FLOAT fPhase
IF bUseSyncSceneCheck
IF iNetSyncSceneID = -1
SCRIPT_ASSERT( "HAS_PED_FINISHED_ANIM() - Invalid sync scene ID, iNetSyncSceneID = -1" )
ELSE
INT iLocalSyncSceneID = NETWORK_GET_LOCAL_SCENE_FROM_NETWORK_ID( iNetSyncSceneID )
IF IS_SYNCHRONIZED_SCENE_RUNNING( iLocalSyncSceneID )
fPhase = GET_SYNCHRONIZED_SCENE_PHASE( iLocalSyncSceneID )
IF fPhase >= fFinishPhase
bResult = TRUE
ENDIF
ENDIF
ENDIF
ELSE
fPhase = GET_ENTITY_ANIM_CURRENT_TIME( ped, strAnimDict, strAnimName )
IF fPhase >= fFinishPhase
bResult = TRUE
ENDIF
ENDIF
IF iAnimEndTagHash != 0
AND HAS_ANIM_EVENT_FIRED( ped, iAnimEndTagHash )
CPRINTLN(DEBUG_NET_MINIGAME, "HAS_PED_FINISHED_ANIM() - ANIM EVENT \"", iAnimEndTagHash, "\" FIRED - PHASE = ", fPhase, " Dictionary = \"", strAnimDict, "\" AnimName = \"", strAnimName, "\"" )
bResult = TRUE
ENDIF
RETURN bResult
ENDIF
RETURN TRUE
ENDFUNC
FUNC BOOL HAS_CROWD_CONTROL_PED_FINISHED_ANIM( PED_INDEX ped, CROWD_CONTROL_PED_ANIM_TYPE eAnim, CROWD_CONTROL_PED_ENUM eCrowdControlAnimID, CROWD_CONTROL_HEIST_TYPE eHeistType, FLOAT fFinishPhase = 1.0, BOOL bUseSyncSceneCheck = FALSE, INT iNetSyncSceneID = -1, INT iAnimEndTagHash = 0 )
RETURN HAS_PED_FINISHED_ANIM( ped,
GET_CROWD_CONTROL_PED_ANIM_DICTIONARY( eAnim, eCrowdControlAnimID, eHeistType ),
GET_CROWD_CONTROL_PED_ANIM_NAME( eAnim, eCrowdControlAnimID, eHeistType ),
fFinishPhase, bUseSyncSceneCheck, iNetSyncSceneID, iAnimEndTagHash )
ENDFUNC
FUNC BOOL HAS_CROWD_CONTROL_PLAYER_FINISHED_ANIM( PED_INDEX ped, CROWD_CONTROL_PLAYER_ANIM_TYPE eAnim, FLOAT fFinishPhase = 1.0, BOOL bUseSyncSceneCheck = FALSE, INT iNetSyncSceneID = -1, INT iAnimEndTagHash = 0 )
RETURN HAS_PED_FINISHED_ANIM( ped,
GET_CROWD_CONTROL_PLAYER_ANIM_DICTIONARY( eAnim ),
GET_CROWD_CONTROL_PLAYER_ANIM_NAME( eAnim ),
fFinishPhase, bUseSyncSceneCheck, iNetSyncSceneID, iAnimEndTagHash )
ENDFUNC
PROC REQUEST_CROWD_CONTROL_PED_ANIM( CROWD_CONTROL_PED_ANIM_TYPE eAnimType, CROWD_CONTROL_HEIST_TYPE eHeist, CROWD_CONTROL_PED_ENUM eCrowdControlAnimID )
IF DOES_CROWD_CONTROL_PED_HAVE_ANIM( eAnimType, eCrowdControlAnimID, eHeist )
REQUEST_ANIM_DICT( GET_CROWD_CONTROL_PED_ANIM_DICTIONARY( eAnimType, eCrowdControlAnimID, eHeist ) )
ENDIF
ENDPROC
PROC RELEASE_CROWD_CONTROL_PED_ANIM( CROWD_CONTROL_PED_ANIM_TYPE eAnimType, CROWD_CONTROL_HEIST_TYPE eHeist, CROWD_CONTROL_PED_ENUM eCrowdControlAnimID )
IF DOES_CROWD_CONTROL_PED_HAVE_ANIM( eAnimType, eCrowdControlAnimID, eHeist )
REMOVE_ANIM_DICT( GET_CROWD_CONTROL_PED_ANIM_DICTIONARY( eAnimType, eCrowdControlAnimID, eHeist ) )
ENDIF
ENDPROC
FUNC BOOL HAS_CROWD_CONTROL_PED_ANIM_LOADED( CROWD_CONTROL_PED_ANIM_TYPE eAnimType, CROWD_CONTROL_HEIST_TYPE eHeist, CROWD_CONTROL_PED_ENUM eCrowdControlAnimID )
IF DOES_CROWD_CONTROL_PED_HAVE_ANIM( eAnimType, eCrowdControlAnimID, eHeist )
IF HAS_ANIM_DICT_LOADED( GET_CROWD_CONTROL_PED_ANIM_DICTIONARY( eAnimType, eCrowdControlAnimID, eHeist ) )
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
PROC REQUEST_CROWD_CONTROL_PED_ANIMS_FOR_SCENE( CROWD_CONTROL_PED_ANIM_TYPE eAnimType, CROWD_CONTROL_HEIST_TYPE eHeist, CROWD_CONTROL_LOCAL_PED_DATA &sCrowdControlLocalPedData[], INT iNumCrowdPeds )
INT i
FOR i = 0 TO iNumCrowdPeds
IF i < FMMC_MAX_CROWD_PEDS
REQUEST_CROWD_CONTROL_PED_ANIM( eAnimType, eHeist, sCrowdControlLocalPedData[i].eCreatorAssignedPedID )
ENDIF
ENDFOR
ENDPROC
PROC RELEASE_CROWD_CONTROL_PED_ANIMS_FOR_SCENE( CROWD_CONTROL_PED_ANIM_TYPE eAnimType, CROWD_CONTROL_HEIST_TYPE eHeist, CROWD_CONTROL_LOCAL_PED_DATA &sCrowdControlLocalPedData[], INT iNumCrowdPeds )
INT i
FOR i = 0 TO iNumCrowdPeds
IF i < FMMC_MAX_CROWD_PEDS
RELEASE_CROWD_CONTROL_PED_ANIM( eAnimType, eHeist, sCrowdControlLocalPedData[i].eCreatorAssignedPedID )
ENDIF
ENDFOR
ENDPROC
FUNC BOOL HAVE_CROWD_CONTROL_PED_ANIMS_LOADED_FOR_SCENE( CROWD_CONTROL_PED_ANIM_TYPE eAnimType, CROWD_CONTROL_HEIST_TYPE eHeist, CROWD_CONTROL_LOCAL_PED_DATA &sCrowdControlLocalPedData[], INT iNumCrowdPeds )
BOOL bFinished = TRUE
INT i
FOR i = 0 TO iNumCrowdPeds
IF i < FMMC_MAX_CROWD_PEDS
IF NOT HAS_CROWD_CONTROL_PED_ANIM_LOADED( eAnimType, eHeist, sCrowdControlLocalPedData[i].eCreatorAssignedPedID )
bFinished = FALSE
ENDIF
ENDIF
ENDFOR
RETURN bFinished
ENDFUNC
PROC PLAY_CROWD_CONTROL_ANIM( PED_INDEX pedCrowd,
CROWD_CONTROL_PED_ANIM_TYPE eAnim, CROWD_CONTROL_PED_ENUM eCreatorAssignedID, CROWD_CONTROL_HEIST_TYPE eHeistType,
FLOAT fBlendInDelta = NORMAL_BLEND_IN, FLOAT fBlendOutDelta = NORMAL_BLEND_OUT,
INT iAnimDuration = -1, ANIMATION_FLAGS eAnimFlags = AF_DEFAULT )
CPRINTLN( DEBUG_NET_MINIGAME, "PLAY_CROWD_CONTROL_ANIM() - ", GET_CROWD_CONTROL_PED_NAME_FROM_ANIM_ID( eCreatorAssignedID ),
" | AnimType = ", GET_CROWD_CONTROL_PED_ANIM_ENUM_NAME( eAnim ),
" | fBlendIn = ", fBlendInDelta,
" | fBlendOut = ", fBlendOutDelta,
" | iAnimDuration = ", iAnimDuration )
// Playing from where the ped is
IF IS_VECTOR_ZERO( GET_CROWD_CONTROL_PED_ANIM_SCENE_COORD( eHeistType, eAnim ) )
TASK_PLAY_ANIM( pedCrowd,
GET_CROWD_CONTROL_PED_ANIM_DICTIONARY( eAnim, eCreatorAssignedID, eHeistType ),
GET_CROWD_CONTROL_PED_ANIM_NAME( eAnim, eCreatorAssignedID, eHeistType ),
fBlendInDelta, fBlendOutDelta, iAnimDuration, eAnimFlags, DEFAULT, DEFAULT, AIK_DISABLE_LEG_IK )
CPRINTLN( DEBUG_NET_MINIGAME, "PLAY_CROWD_CONTROL_ANIM() Played with TASK_PLAY_ANIM" )
// Playing from an offset so use anim advanced and extract the initial offset
ELSE
TASK_PLAY_ANIM_ADVANCED( pedCrowd,
GET_CROWD_CONTROL_PED_ANIM_DICTIONARY( eAnim, eCreatorAssignedID, eHeistType ),
GET_CROWD_CONTROL_PED_ANIM_NAME( eAnim, eCreatorAssignedID, eHeistType ),
GET_CROWD_CONTROL_PED_ANIM_SCENE_COORD( eHeistType, eAnim ),
GET_CROWD_CONTROL_PED_ANIM_SCENE_ROT( eHeistType ),
fBlendInDelta, fBlendOutDelta, iAnimDuration, eAnimFlags | AF_EXTRACT_INITIAL_OFFSET, DEFAULT, DEFAULT, AIK_DISABLE_LEG_IK )
CPRINTLN( DEBUG_NET_MINIGAME, "PLAY_CROWD_CONTROL_ANIM() Played with TASK_PLAY_ANIM_ADVANCED" )
ENDIF
ENDPROC
PROC PLAY_CROWD_CONTROL_SYNC_SCENE( INT &iSyncSceneID, NETWORK_INDEX &pedsGlobalArray[], INT &iCloudPedIDLookupArray[], CROWD_CONTROL_LOCAL_PED_DATA &sCrowdControlLocalPedData[], INT iNumCrowdPeds, CROWD_CONTROL_PED_ANIM_TYPE eAnim, CROWD_CONTROL_HEIST_TYPE eHeistType, FLOAT fBlendInDelta, FLOAT fBlendOutDelta, BOOL bLoop, BOOL bHoldLastFrame, SYNCED_SCENE_PLAYBACK_FLAGS eSyncSceneFlags, RAGDOLL_BLOCKING_FLAGS eRagdollBlockingFlags )
iSyncSceneID = NETWORK_CREATE_SYNCHRONISED_SCENE(
GET_CROWD_CONTROL_PED_ANIM_SCENE_COORD( eHeistType, eAnim ),
GET_CROWD_CONTROL_PED_ANIM_SCENE_ROT( eHeistType ), DEFAULT, bHoldLastFrame, bLoop )
IF bLoop
eSyncSceneFlags = eSyncSceneFlags | SYNCED_SCENE_LOOP_WITHIN_SCENE
ENDIF
// Task all the peds with their intro loop anim
INT i
FOR i = 0 TO iNumCrowdPeds-1
IF NETWORK_DOES_ENTITY_EXIST_WITH_NETWORK_ID( pedsGlobalArray[iCloudPedIDLookupArray[i]] )
AND NOT IS_PED_INJURED( NET_TO_PED( pedsGlobalArray[iCloudPedIDLookupArray[i]] ) )
CPRINTLN( DEBUG_NET_MINIGAME, "PLAY_CROWD_CONTROL_SYNC_SCENE() - TASKING ANIM dict: ", GET_CROWD_CONTROL_PED_ANIM_DICTIONARY( eAnim, sCrowdControlLocalPedData[i].eCreatorAssignedPedID, eHeistType ),
" anim: ", GET_CROWD_CONTROL_PED_ANIM_NAME( eAnim, sCrowdControlLocalPedData[i].eCreatorAssignedPedID, eHeistType ))
NETWORK_ADD_PED_TO_SYNCHRONISED_SCENE( NET_TO_PED( pedsGlobalArray[iCloudPedIDLookupArray[i]] ),
iSyncSceneID,
GET_CROWD_CONTROL_PED_ANIM_DICTIONARY( eAnim, sCrowdControlLocalPedData[i].eCreatorAssignedPedID, eHeistType ),
GET_CROWD_CONTROL_PED_ANIM_NAME( eAnim, sCrowdControlLocalPedData[i].eCreatorAssignedPedID, eHeistType ),
fBlendInDelta, fBlendOutDelta,
eSyncSceneFlags, eRagdollBlockingFlags
)
ENDIF
ENDFOR
CPRINTLN( DEBUG_NET_MINIGAME, "PLAY_CROWD_CONTROL_SYNC_SCENE() - START SCENE" )
NETWORK_START_SYNCHRONISED_SCENE( iSyncSceneID )
ENDPROC
PROC SET_CROWD_CONTROL_PED_DECORATORS( PED_INDEX pedCrowd, CROWD_CONTROL_PED_DECORATOR_DATA &sPedNewDecorators )
DECOR_SET_INT( pedCrowd, GET_CROWD_CONTROL_PED_DECOR_NAME( CCPDECOR_iState ), ENUM_TO_INT( sPedNewDecorators.eState ) )
DECOR_SET_INT( pedCrowd, GET_CROWD_CONTROL_PED_DECOR_NAME( CCPDECOR_iStatePrev ), ENUM_TO_INT( sPedNewDecorators.eStatePrev ) )
DECOR_SET_INT( pedCrowd, GET_CROWD_CONTROL_PED_DECOR_NAME( CCPDECOR_iBitSet ), sPedNewDecorators.iBitSet )
DECOR_SET_FLOAT( pedCrowd, GET_CROWD_CONTROL_PED_DECOR_NAME( CCPDECOR_fInfluenceDirectThreat ), sPedNewDecorators.fInfluenceDirectThreat )
DECOR_SET_FLOAT( pedCrowd, GET_CROWD_CONTROL_PED_DECOR_NAME( CCPDECOR_fInfluenceShouting ), sPedNewDecorators.fInfluenceShouting )
DECOR_SET_INT( pedCrowd, GET_CROWD_CONTROL_PED_DECOR_NAME( CCPDECOR_iBeatdownHitsRemaining ), sPedNewDecorators.iBeatdownHitsRemaining )
DECOR_SET_INT( pedCrowd, GET_CROWD_CONTROL_PED_DECOR_NAME( CCPDECOR_iBeatdownRounds ), sPedNewDecorators.iBeatdownRounds )
ENDPROC
FUNC CROWD_CONTROL_PED_DECORATOR_DATA GET_CROWD_CONTROL_PED_DECORATOR_DATA( PED_INDEX pedCrowd )
CROWD_CONTROL_PED_DECORATOR_DATA sPedData
STRING strTemp = GET_CROWD_CONTROL_PED_DECOR_NAME( CCPDECOR_iState )
IF DECOR_EXIST_ON( pedCrowd, strTemp )
sPedData.eState = INT_TO_ENUM( CROWD_CONTROL_PED_STATE, DECOR_GET_INT( pedCrowd, strTemp ) )
ENDIF
strTemp = GET_CROWD_CONTROL_PED_DECOR_NAME( CCPDECOR_iStatePrev )
IF DECOR_EXIST_ON( pedCrowd, strTemp )
sPedData.eStatePrev = INT_TO_ENUM( CROWD_CONTROL_PED_STATE, DECOR_GET_INT( pedCrowd, strTemp ) )
ENDIF
strTemp = GET_CROWD_CONTROL_PED_DECOR_NAME( CCPDECOR_iBitSet )
IF DECOR_EXIST_ON( pedCrowd, strTemp )
sPedData.iBitSet = DECOR_GET_INT( pedCrowd, strTemp )
ENDIF
strTemp = GET_CROWD_CONTROL_PED_DECOR_NAME( CCPDECOR_fInfluenceDirectThreat )
IF DECOR_EXIST_ON( pedCrowd, strTemp )
sPedData.fInfluenceDirectThreat = DECOR_GET_FLOAT( pedCrowd, strTemp )
ENDIF
strTemp = GET_CROWD_CONTROL_PED_DECOR_NAME( CCPDECOR_fInfluenceShouting )
IF DECOR_EXIST_ON( pedCrowd, strTemp )
sPedData.fInfluenceShouting = DECOR_GET_FLOAT( pedCrowd, strTemp )
ENDIF
strTemp = GET_CROWD_CONTROL_PED_DECOR_NAME( CCPDECOR_iBeatdownHitsRemaining )
IF DECOR_EXIST_ON( pedCrowd, strTemp )
sPedData.iBeatdownHitsRemaining = DECOR_GET_INT( pedCrowd, strTemp )
ENDIF
strTemp = GET_CROWD_CONTROL_PED_DECOR_NAME( CCPDECOR_iBeatdownRounds )
IF DECOR_EXIST_ON( pedCrowd, strTemp )
sPedData.iBeatdownRounds = DECOR_GET_INT( pedCrowd, strTemp )
ENDIF
RETURN sPedData
ENDFUNC
FUNC BOOL CAN_WE_UPDATE_CROWD_CONTROL_DATA( PED_INDEX pedCrowd, CROWD_CONTROL_PED_DECORATOR_DATA &sPedNewDecorators, INT iUpdateTimer )
CROWD_CONTROL_PED_DECORATOR_DATA sPedData = GET_CROWD_CONTROL_PED_DECORATOR_DATA( pedCrowd )
// Critical stuff, should update regardless of time
IF sPedData.eState != sPedNewDecorators.eState
OR sPedData.iBeatDownRounds != sPedNewDecorators.iBeatDownRounds
OR sPedData.iBeatDownHitsRemaining != sPedNewDecorators.iBeatDownHitsRemaining
OR sPedData.iBitSet != sPedNewDecorators.iBitSet
RETURN TRUE
ENDIF
// Non critical stuff, should only update once enough time has passed
IF iUpdateTimer > 20 // minimum of 20 frames
IF sPedData.fInfluenceDirectThreat != sPedNewDecorators.fInfluenceDirectThreat
OR sPedData.fInfluenceShouting != sPedNewDecorators.fInfluenceShouting
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL IS_CROWD_CONTROL_PED_RESTRICTED( CROWD_CONTROL_PED_ENUM eAssignedCrowdID, CROWD_CONTROL_HEIST_TYPE eHeistType, CROWD_CONTROL_PED_STATE eState, BOOL bIsCrowdRestricted )
SWITCH eHeistType
// Always restrict both cashiers
CASE CCHEIST_NORMAL
IF eAssignedCrowdID = CCPED_CASHIER_A
OR eAssignedCrowdID = CCPED_CASHIER_B
RETURN TRUE
ENDIF
BREAK
CASE CCHEIST_CUSTOM_FLEECA
SWITCH eState
// Neither cashiers can attmped an hero action at any time
CASE CCPS_HERO_ATTEMPT
IF bIsCrowdRestricted
IF eAssignedCrowdID = CCPED_CASHIER_A
RETURN TRUE
ENDIF
ENDIF
BREAK
ENDSWITCH
BREAK
// Restrict the cashiers while waiting for the thermite to blow the door open objective
CASE CCHEIST_CUSTOM_ORNATE
SWITCH eState
CASE CCPS_HERO_ATTEMPT
// Cashier A cannot attempt a hero action until the crowd is unrestricted
IF eAssignedCrowdID = CCPED_CASHIER_A
IF bIsCrowdRestricted
RETURN TRUE
ENDIF
ENDIF
BREAK
ENDSWITCH
BREAK
ENDSWITCH
RETURN FALSE
ENDFUNC
FUNC BOOL IS_CROWD_CONTROL_PED_A_VALID_HERO( CROWD_CONTROL_PED_DECORATOR_DATA &sPedData, CROWD_CONTROL_PED_ENUM ePed, CROWD_CONTROL_HEIST_TYPE eHeistType, BOOL bIsPedRestricted )
// Ped doesn't have custom animation even though running anims
IF IS_BIT_SET_ENUM( sPedData.iBitSet, CCPEDDATA_RunningBespokeAnims )
AND NOT DOES_CROWD_CONTROL_PED_HAVE_ANIM( CCPANIM_HERO_START, ePed, eHeistType )
RETURN FALSE
// Ped is not allowed to do a hero attempt at this stage
ELIF IS_CROWD_CONTROL_PED_RESTRICTED( ePed, eHeistType, CCPS_HERO_ATTEMPT, bIsPedRestricted )
RETURN FALSE
// Already attempted
ELIF IS_BIT_SET_ENUM( sPedData.iBitSet, CCPEDDATA_HeroAttempted )
RETURN FALSE
// Hero prop has been broken, can't perform hero task any more.
ELIF IS_BIT_SET_ENUM( sPedData.iBitSet, CCPEDDATA_HeroBroken )
RETURN FALSE
ELIF IS_BIT_SET_ENUM( sPedData.iBitSet, CCPEDDATA_HeroComplete )
RETURN FALSE
ENDIF
RETURN TRUE
ENDFUNC
FUNC BOOL IS_CROWD_CONTROL_PED_HERO_ALWAYS_ACTIVE( CROWD_CONTROL_PED_ENUM ePed, CROWD_CONTROL_HEIST_TYPE eHeistType )
SWITCH eHeistType
CASE CCHEIST_CUSTOM_ORNATE
SWITCH ePed
CASE CCPED_CASHIER_A RETURN TRUE BREAK
ENDSWITCH
BREAK
ENDSWITCH
RETURN FALSE
ENDFUNC
FUNC BOOL SHOULD_CROWD_CONTROL_PED_DIRECT_THREAT_INFLUENCE_DECREASE( CROWD_CONTROL_THREAT_TYPE eThreat )
IF eThreat = CCTHREAT_SHOOTING_CLOSE
OR eThreat = CCTHREAT_AIMED_AT
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
PROC UPDATE_CROWD_CONTROL_PED_DIRECT_THREAT_INFLUENCE(
CROWD_CONTROL_PED_DECORATOR_DATA &sPedData,
CROWD_CONTROL_LOCAL_PED_DATA &sLocalPedData,
CROWD_CONTROL_THREAT_TYPE eThreat,
FLOAT fInfluenceDeficitPerPed )
INT iChangeSign = 0
FLOAT fChangeRate = 0.0
// Decrease because there is an over powered threat
IF SHOULD_CROWD_CONTROL_PED_DIRECT_THREAT_INFLUENCE_DECREASE( eThreat )
iChangeSign = -1
SWITCH eThreat
CASE CCTHREAT_SHOOTING_CLOSE fChangeRate = 0.2 BREAK
CASE CCTHREAT_AIMED_AT fChangeRate = 0.15 BREAK
ENDSWITCH
// Hold while a threat wears off unless theres a deficit
ELIF fInfluenceDeficitPerPed <= 0.0
AND ( IS_TIME_LESS_THAN( GET_NETWORK_TIME(), GET_TIME_OFFSET( sLocalPedData.timeLastShotAt, 8000 ) )
OR IS_TIME_LESS_THAN( GET_NETWORK_TIME(), GET_TIME_OFFSET( sLocalPedData.timeStartThreaten, 5000 ) ) )
iChangeSign = 0
fChangeRate = 0
// Increase now all threats have worn off
ELSE
iChangeSign = 1
fChangeRate = 0.1 + fInfluenceDeficitPerPed
ENDIF
sPedData.fInfluenceDirectThreat = CLAMP( sPedData.fInfluenceDirectThreat + ( iChangeSign * fChangeRate * TIMESTEP() ), 0.0, 1.0 )
ENDPROC
FUNC BOOL SHOULD_CROWD_CONTROL_PED_SHOUT_THREAT_INFLUENCE_DECREASE( CROWD_CONTROL_THREAT_TYPE eThreat )
IF eThreat = CCTHREAT_SHOUTING_CLOSE
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
PROC UPDATE_CROWD_CONTROL_PED_SHOUT_THREAT_INFLUENCE(
CROWD_CONTROL_PED_DECORATOR_DATA &sPedData,
CROWD_CONTROL_LOCAL_PED_DATA &sLocalPedData,
CROWD_CONTROL_THREAT_TYPE eThreat,
FLOAT fInfluenceDeficitPerPed )
INT iChangeSign = 0
FLOAT fChangeRate = 0.0
// Decrease because there is an over powered threat
IF SHOULD_CROWD_CONTROL_PED_SHOUT_THREAT_INFLUENCE_DECREASE( eThreat )
iChangeSign = -1
SWITCH eThreat
CASE CCTHREAT_SHOUTING_CLOSE fChangeRate = 0.15 BREAK
ENDSWITCH
// Hold while a threat wears off unless theres a deficit
ELIF fInfluenceDeficitPerPed <= 0.0
AND IS_TIME_LESS_THAN( GET_NETWORK_TIME(), GET_TIME_OFFSET( sLocalPedData.timeStartThreaten, 5000 ) )
iChangeSign = 0
fChangeRate = 0
// Increase now all threats have worn off
ELSE
iChangeSign = 1
fChangeRate = 0.1
fChangeRate += fInfluenceDeficitPerPed
ENDIF
sPedData.fInfluenceShouting = CLAMP( sPedData.fInfluenceShouting + ( iChangeSign * fChangeRate * TIMESTEP() ), 0.0, 1.0 )
ENDPROC
FUNC FLOAT GET_CROWD_CONTROL_PED_FEAR_DELTA( CROWD_CONTROL_SERVER_DATA &sData,
CROWD_CONTROL_LOCAL_DATA &sLocalData,
CROWD_CONTROL_PED_DECORATOR_DATA &sPedData,
PED_INDEX pedPlayer, CROWD_CONTROL_THREAT_TYPE eThreat,
INT iCrowdNum,
INT iCrowdSize)
// Initialise multipliers
FLOAT fCrowdSizeMultiplier = FMMC_MAX_CROWD_PEDS / TO_FLOAT( iCrowdSize )
FLOAT fThreatMultiplier = 1.0
FLOAT fHeroMultiplier = 1.0
FLOAT fHeistMultiplier = 1.0
FLOAT fFearInfluence = 1.0
INT iChangeSign = 0
// Increase
IF eThreat != CCTHREAT_NONE
fFearInfluence = sPedData.fInfluenceDirectThreat * sPedData.fInfluenceShouting
iChangeSign = 1
// Decide whether to apply a threat multipler
SWITCH eThreat
CASE CCTHREAT_PROXIMITY
fThreatMultiplier = 0.25
BREAK
CASE CCTHREAT_SHOUTING
fThreatMultiplier = 0.25
BREAK
CASE CCTHREAT_BEATING_NEIGHBOUR
fThreatMultiplier = 1.5
BREAK
CASE CCTHREAT_KILLED_OTHER
fThreatMultiplier = 4.0
BREAK
CASE CCTHREAT_BEATING
fThreatMultiplier = 5.0
BREAK
CASE CCTHREAT_SHOOTING
fThreatMultiplier = 1.0
BREAK
CASE CCTHREAT_SHOUTING_CLOSE
fThreatMultiplier = 10.0 // has diminishing effect so start it strong
BREAK
CASE CCTHREAT_AIMED_AT
fThreatMultiplier = 10.0 // has diminishing effect and only on 1 ped at a time so make it strong
BREAK
CASE CCTHREAT_SHOOTING_CLOSE // has diminishing effect so make it strong
WEAPON_TYPE eWeap
IF GET_CURRENT_PED_WEAPON( pedPlayer, eWeap )
// Weapon damage ( clamped between pistol and sawnoff )
FLOAT fWeaponDamageAlpha
fWeaponDamageAlpha = CLAMP( GET_WEAPON_DAMAGE( eWeap, WEAPONCOMPONENT_INVALID ), 26.0, 40.0 )
fWeaponDamageAlpha -= 26.0 // minus lower boundry
fWeaponDamageAlpha /= 14.0 // divide range
// between 400% and 900% faster depending on weapon damage
fThreatMultiplier = 5.0 + ( fWeaponDamageAlpha * 5.0 )
ENDIF
BREAK
CASE CCTHREAT_PROJECTILE_EXPLOSIVE
fThreatMultiplier = 5.0 // 400% Faster
BREAK
ENDSWITCH
// Decide whether to apply a hero multiplier
IF IS_BIT_SET( sData.iCurrentActiveHeros, iCrowdNum )
fHeroMultiplier = 0.5 // 50% slower (halved)
ENDIF
// Decrease
ELIF eThreat = CCTHREAT_NONE
iChangeSign = -1
IF sLocalData.eHeistType = CCHEIST_CUSTOM_FLEECA
// b* 2235971 - removed heist multiplier for Fleeca, was decreasing too fast
// fHeistMultiplier = 2.0 // 100% faster (double)
ENDIF
IF IS_BIT_SET( sData.iCurrentActiveHeros, iCrowdNum )
fHeroMultiplier = 0.85 // 15% slower
ENDIF
ENDIF
IF iChangeSign = 0
RETURN 0.0
ELSE
RETURN iChangeSign * fFearInfluence * fThreatMultiplier * fHeroMultiplier * fHeistMultiplier * fCrowdSizeMultiplier
ENDIF
ENDFUNC
FUNC FLOAT GET_CROWD_CONTROL_PED_CAPSULE_SIZE( CROWD_CONTROL_PED_ENUM ePed, CROWD_CONTROL_HEIST_TYPE eHeistType )
SWITCH eHeistType
CASE CCHEIST_CUSTOM_FLEECA
SWITCH ePed
CASE CCPED_CUSTOMER_A RETURN 0.4 BREAK
CASE CCPED_CUSTOMER_B RETURN 0.3 BREAK
ENDSWITCH
BREAK
CASE CCHEIST_CUSTOM_ORNATE
SWITCH ePed
CASE CCPED_CUSTOMER_A RETURN 0.4 BREAK
CASE CCPED_CUSTOMER_B RETURN 0.3 BREAK
ENDSWITCH
BREAK
ENDSWITCH
RETURN -1.0
ENDFUNC
//PURPOSE: Return True if the Player is in front of the Ped
FUNC BOOL IS_PLAYER_IN_FRONT_OF_PED(VECTOR vPlayerCoord, VECTOR vPedCoord, FLOAT fPedHeading)
VECTOR posA // Player position
VECTOR posB // Ped position
VECTOR posC
VECTOR vecBA // vector from Ped to Player
VECTOR vecPed // unit vector pointing in the direction the Ped is facing
FLOAT fTemp
// get positions
posA = vPlayerCoord
posB = vPedCoord
// get vec from Player to Ped
vecBA = posA - posB
// get unit vector pointing forwards from Ped
posC = GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS(vPedCoord, fPedHeading, <<0.0, 7.0, 0.0>>)
vecPed = posC - posB
// calculate dot product of vecBA and vecPed
fTemp = GET_ANGLE_BETWEEN_2D_VECTORS(vecPed.x,vecPed.y,vecBA.x,vecBA.y)
IF (fTemp < 80.0)
RETURN(TRUE)
ENDIF
RETURN(FALSE)
ENDFUNC
FUNC BOOL IS_CROWD_CONTROL_PLAYER_ABLE_TO_THREATEN_FROM_CURRENT_LOCATION( PED_INDEX pedPlayer, CROWD_CONTROL_LOCAL_DATA sLocalData )
SWITCH sLocalData.eHeistType
CASE CCHEIST_CUSTOM_FLEECA
IF GET_INTERIOR_FROM_ENTITY( pedPlayer ) = sLocalData.interiorHeist
INT iRoomKey
iRoomKey = GET_ROOM_KEY_FROM_ENTITY( pedPlayer )
IF iRoomKey = HASH("Bankvault")
RETURN FALSE
ENDIF
ELSE
// do not exempt the exterior of the bank as players can shoot through the glass front
ENDIF
BREAK
CASE CCHEIST_CUSTOM_ORNATE
IF GET_INTERIOR_FROM_ENTITY( pedPlayer ) = sLocalData.interiorHeist
INT iRoomKey
iRoomKey = GET_ROOM_KEY_FROM_ENTITY( pedPlayer )
IF iRoomKey = HASH("Bank_basementRm")
OR iRoomKey = HASH("_")
RETURN FALSE
ENDIF
// Not inside the bank, no possible way to threaten from outside this bank, return false
ELSE
RETURN FALSE
ENDIF
BREAK
ENDSWITCH
RETURN TRUE
ENDFUNC
FUNC BOOL IS_PLAYER_STARTING_CROWD_CONTROL_PED_REACTION( PED_INDEX playerPed, CROWD_CONTROL_LOCAL_DATA &sLocalData )
PLAYER_INDEX playerID = NETWORK_GET_PLAYER_INDEX_FROM_PED( playerPed )
IF playerID != INVALID_PLAYER_INDEX()
AND IS_NET_PLAYER_OK( playerID )
BOOL bPlayerLooksHostile
// ... with a gun
WEAPON_TYPE CurrentWeapon
IF GET_CURRENT_PED_WEAPON( playerPed, CurrentWeapon )
IF CurrentWeapon != WEAPONTYPE_UNARMED
AND CurrentWeapon != WEAPONTYPE_OBJECT
AND CurrentWeapon != WEAPONTYPE_FLARE
bPlayerLooksHostile = TRUE
ENDIF
ENDIF
// ... in a mask
IF IS_PED_WEARING_A_MASK( playerPed )
bPlayerLooksHostile = TRUE
ENDIF
IF IS_PED_IN_ANY_VEHICLE( playerPed )
bPlayerLooksHostile = TRUE
ENDIF
// Looks hostile and can be seen
IF bPlayerLooksHostile
// 2068869, this check only works on peds you own which fucks things up
//AND NETWORK_HAS_CONTROL_OF_ENTITY( crowdPed ) AND CAN_PED_SEE_HATED_PED( crowdPed, playerPed )
IF HAS_PED_ENTERED_CROWD_CONTROL_TRIGGER_ZONE( playerPed, sLocalData, FALSE )
RETURN TRUE
ENDIF
ENDIF
// Player is shouting
IF NETWORK_IS_PLAYER_TALKING( playerID ) AND NETWORK_GET_PLAYER_LOUDNESS( playerID ) > 0.75
IF HAS_PED_ENTERED_CROWD_CONTROL_TRIGGER_ZONE( playerPed, sLocalData, TRUE )
RETURN TRUE
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC CROWD_CONTROL_THREAT_TYPE IS_PLAYER_THREATENING_CROWD_CONTROL_PED(
PLAYER_INDEX threateningPlayer, PED_INDEX threateningPed,
PED_INDEX crowdPed, INT iCrowdNum,
CROWD_CONTROL_SERVER_DATA &sServerData,
CROWD_CONTROL_LOCAL_DATA &sLocalData,
CROWD_CONTROL_PED_DECORATOR_DATA &sPedData[FMMC_MAX_CROWD_PEDS],
CROWD_CONTROL_LOCAL_PED_DATA &sLocalPedData[FMMC_MAX_CROWD_PEDS],
PED_INDEX pedBeingBeat )
CONST_FLOAT cfCC_ShootingDistClose 2.0
CONST_FLOAT cfCC_ShootingDist 5.0
CONST_FLOAT cfCC_ProjectileDist 5.0
CONST_FLOAT cfCC_AimingDist 8.0
CONST_FLOAT cfCC_ShoutingDist 6.0
CONST_FLOAT cfCC_ShoutingDistClose 2.5
CONST_FLOAT cfCC_ProximityDistNormal 3.5
CONST_FLOAT cfCC_ProximityDistClose 2.0
CONST_FLOAT cfCC_BeatingNeighbourDist 5.0
CONST_FLOAT cfCC_DeadNeighbourDist 3.0
INT i
BOOL bIsThreateningPedOk
FLOAT fDistanceFromPed
IF (threateningPlayer != INVALID_PLAYER_INDEX())
AND IS_NET_PLAYER_OK(threateningPlayer)
fDistanceFromPed = GET_DISTANCE_BETWEEN_ENTITIES( threateningPed, crowdPed, FALSE )
bIsThreateningPedOk = TRUE
ENDIF
BOOL bIsArmed
WEAPON_TYPE CurrentWeapon
IF GET_CURRENT_PED_WEAPON(threateningPed, CurrentWeapon)
IF CurrentWeapon != WEAPONTYPE_UNARMED
bIsArmed = TRUE
ENDIF
ENDIF
BOOL bIsPlayerFacingPed = IS_PED_FACING_PED(threateningPed, crowdPed, 60.0)
BOOL bIsPedFacingPlayer = IS_PED_FACING_PED(crowdPed, threateningPed, 90.0)
BOOL bIsPlayerShouting = NETWORK_IS_PLAYER_TALKING(threateningPlayer) AND NETWORK_GET_PLAYER_LOUDNESS(threateningPlayer) > cfCC_ShoutVolumeLevel
VECTOR vPedCoord = GET_ENTITY_COORDS(crowdPed)
REPEAT COUNT_OF(CROWD_CONTROL_THREAT_TYPE) i
CROWD_CONTROL_THREAT_TYPE eThreatToTest = INT_TO_ENUM( CROWD_CONTROL_THREAT_TYPE, ( COUNT_OF(CROWD_CONTROL_THREAT_TYPE) - 1) - i )
SWITCH eThreatToTest
CASE CCTHREAT_SHOOTING_CLOSE
// Shooting Close
// Bullet impacted near the ped
// OR the player is within range of the ped and is shooting
IF bIsThreateningPedOk
IF IS_BULLET_IN_AREA(vPedCoord, cfCC_ShootingDistClose) // Bullet flew past them
OR HAS_BULLET_IMPACTED_IN_AREA(vPedCoord, cfCC_ShootingDistClose) // Bullet hit near them
OR ( fDistanceFromPed < cfCC_ShootingDistClose AND IS_PED_SHOOTING(threateningPed) ) // Shooting anywhere near them
RETURN CCTHREAT_SHOOTING_CLOSE
ENDIF
ENDIF
BREAK
CASE CCTHREAT_PROJECTILE_EXPLOSIVE_STICKY_BOMB
// Sticky Bomb
// Ped not set to ignore sticky bombs
// Sticky bomb in area near ped
IF NOT IS_BIT_SET_ENUM( sPedData[iCrowdNum].iBitSet, CCPEDDATA_IgnoreStickyBombs )
AND IS_PROJECTILE_TYPE_WITHIN_DISTANCE( vPedCoord, WEAPONTYPE_STICKYBOMB, cfCC_ProjectileDist )
RETURN CCTHREAT_PROJECTILE_EXPLOSIVE_STICKY_BOMB
ENDIF
BREAK
CASE CCTHREAT_PROJECTILE_EXPLOSIVE
// Projectile
// Is an explosive projectile within range of the ped
IF IS_PROJECTILE_TYPE_WITHIN_DISTANCE( vPedCoord, WEAPONTYPE_GRENADE, cfCC_ProjectileDist )
OR IS_PROJECTILE_TYPE_WITHIN_DISTANCE( vPedCoord, WEAPONTYPE_GRENADELAUNCHER, cfCC_ProjectileDist )
OR IS_PROJECTILE_TYPE_WITHIN_DISTANCE( vPedCoord, WEAPONTYPE_RPG, cfCC_ProjectileDist )
OR IS_PROJECTILE_TYPE_WITHIN_DISTANCE( vPedCoord, WEAPONTYPE_BZGAS, cfCC_ProjectileDist )
OR IS_PROJECTILE_TYPE_WITHIN_DISTANCE( vPedCoord, WEAPONTYPE_SMOKEGRENADE, cfCC_ProjectileDist )
OR IS_PROJECTILE_TYPE_WITHIN_DISTANCE( vPedCoord, WEAPONTYPE_MOLOTOV, cfCC_ProjectileDist )
OR IS_PROJECTILE_TYPE_WITHIN_DISTANCE( vPedCoord, WEAPONTYPE_FLARE, cfCC_ProjectileDist )
RETURN CCTHREAT_PROJECTILE_EXPLOSIVE
ENDIF
BREAK
CASE CCTHREAT_SHOOTING
// Shooting
// Bullet is near the ped
// OR Bullet impacted near the ped
// OR the player is within range of the ped and is shooting
IF bIsThreateningPedOk
AND bIsArmed
IF IS_BULLET_IN_AREA(vPedCoord, cfCC_ShootingDist) // Bullet flew past them
OR HAS_BULLET_IMPACTED_IN_AREA(vPedCoord, cfCC_ShootingDist) // Bullet hit near them
OR ( fDistanceFromPed < cfCC_ShootingDist AND IS_PED_SHOOTING(threateningPed) ) // Shooting anywhere near them
RETURN CCTHREAT_SHOOTING
ENDIF
ENDIF
BREAK
CASE CCTHREAT_BEATING
// Local data says the player is beating this ped
IF bIsThreateningPedOk
AND bIsArmed
IF sLocalData.iPlayerBeatingThisPed != -1
AND sLocalData.iPlayerBeatingThisPed = iCrowdNum
RETURN CCTHREAT_BEATING
ENDIF
ENDIF
BREAK
CASE CCTHREAT_AIMED_AT
// Aiming at ped
// Is facing the player
// Player not using the cellphone
// Player is within aim range OR is shouting to prompt the ped
IF bIsThreateningPedOk
AND bIsArmed
IF ( IS_PLAYER_FREE_AIMING_AT_ENTITY(threateningPlayer, crowdPed) OR IS_PLAYER_TARGETTING_ENTITY(threateningPlayer, crowdPed ) )
AND bIsPedFacingPlayer
AND NOT IS_CELLPHONE_CAMERA_IN_USE()
AND ( fDistanceFromPed < cfCC_AimingDist OR bIsPlayerShouting )
RETURN CCTHREAT_AIMED_AT
ENDIF
ENDIF
BREAK
CASE CCTHREAT_KILLED_OTHER
// Killed someone
// Player has killed someone near by
IF bIsThreateningPedOk
INT j
FOR j = 0 TO sServerData.iNumCrowdControlPeds-1
// exclude this ped from being checked
IF j != iCrowdNum
//IF IS_BIT_SET_ENUM( sPedData[j].iBitSet, CCPEDDATA_Dead )
//OR IS_BIT_SET_ENUM( sPedData[j].iBitSet, CCPEDDATA_KnockedOut )
IF IS_BIT_SET_ENUM( sPedData[j].iBitSet, CCPEDDATA_Dead )
IF IS_TIME_LESS_THAN( GET_NETWORK_TIME(), GET_TIME_OFFSET( sLocalPedData[j].timeDied, 3000 ) )
RETURN CCTHREAT_KILLED_OTHER
ENDIF
ENDIF
ENDIF
ENDFOR
ENDIF
BREAK
CASE CCTHREAT_BEATING_NEIGHBOUR
// Beating someone
// Player is beating someone near by
IF bIsThreateningPedOk
AND bIsArmed
IF sLocalData.iPlayerBeatingThisPed != -1
AND GET_DISTANCE_BETWEEN_ENTITIES( pedBeingBeat, crowdPed ) < cfCC_BeatingNeighbourDist
RETURN CCTHREAT_BEATING_NEIGHBOUR
ENDIF
ENDIF
BREAK
CASE CCTHREAT_SHOUTING_CLOSE
// Shouting close
// Within shout close range
IF bIsThreateningPedOk
IF bIsPlayerShouting AND fDistanceFromPed < cfCC_ShoutingDistClose
RETURN CCTHREAT_SHOUTING_CLOSE
ENDIF
ENDIF
BREAK
CASE CCTHREAT_SHOUTING
// Shouting
// Within shout range
IF bIsThreateningPedOk
IF bIsPlayerShouting AND fDistanceFromPed < cfCC_ShoutingDist
RETURN CCTHREAT_SHOUTING
ENDIF
ENDIF
BREAK
CASE CCTHREAT_PROXIMITY
// Proximity
// Player is within normal range both the ped and the player are facing
// OR player is within close range
IF bIsThreateningPedOk
IF ( fDistanceFromPed < cfCC_ProximityDistNormal AND bIsPlayerFacingPed AND bIsPedFacingPlayer )
OR fDistanceFromPed < cfCC_ProximityDistClose
RETURN CCTHREAT_PROXIMITY
ENDIF
ENDIF
BREAK
ENDSWITCH
ENDREPEAT
RETURN CCTHREAT_NONE
ENDFUNC
FUNC INT CREATE_CROWD_CONTROL_PED_SEQUENCES( CROWD_CONTROL_LOCAL_DATA &sLocalData, CROWD_CONTROL_LOCAL_PED_DATA &sLocalPedData )
INT iSeqCount
// Hero fail -> Cower
IF sLocalPedData.seqs[CCSEQ_HERO_FAIL] = NULL
AND DOES_CROWD_CONTROL_PED_HAVE_ANIM( CCPANIM_HERO_FAIL, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
AND DOES_CROWD_CONTROL_PED_HAVE_ANIM( CCPANIM_COWER_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
OPEN_SEQUENCE_TASK( sLocalPedData.seqs[CCSEQ_HERO_FAIL] )
PLAY_CROWD_CONTROL_ANIM( NULL, CCPANIM_HERO_FAIL, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType,
REALLY_SLOW_BLEND_IN, -0.1, -1, eCrowdControlAnimFlags )
PLAY_CROWD_CONTROL_ANIM( NULL, CCPANIM_COWER_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType,
SLOW_BLEND_IN, -0.1, -1, AF_LOOPING | eCrowdControlAnimFlags )
CLOSE_SEQUENCE_TASK( sLocalPedData.seqs[CCSEQ_HERO_FAIL] )
iSeqCount++
ENDIF
// Flinch -> Cower
IF sLocalPedData.seqs[CCSEQ_FLINCH_TO_COWER] = NULL
AND DOES_CROWD_CONTROL_PED_HAVE_ANIM( CCPANIM_FLINCH_TO_IDLE_OUTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
AND DOES_CROWD_CONTROL_PED_HAVE_ANIM( CCPANIM_COWER_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
OPEN_SEQUENCE_TASK( sLocalPedData.seqs[CCSEQ_FLINCH_TO_COWER] )
PLAY_CROWD_CONTROL_ANIM( NULL, CCPANIM_FLINCH_TO_IDLE_OUTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType,
REALLY_SLOW_BLEND_IN, -0.1, -1, eCrowdControlAnimFlags )
PLAY_CROWD_CONTROL_ANIM( NULL, CCPANIM_COWER_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType,
SLOW_BLEND_IN, -0.1, -1, AF_LOOPING | eCrowdControlAnimFlags )
CLOSE_SEQUENCE_TASK( sLocalPedData.seqs[CCSEQ_FLINCH_TO_COWER] )
iSeqCount++
ENDIF
// Cower -> Flinch
IF sLocalPedData.seqs[CCSEQ_COWER_TO_FLINCH] = NULL
AND DOES_CROWD_CONTROL_PED_HAVE_ANIM( CCPANIM_COWER_TO_FLINCH_INTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
AND DOES_CROWD_CONTROL_PED_HAVE_ANIM( CCPANIM_FLINCH_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
OPEN_SEQUENCE_TASK( sLocalPedData.seqs[CCSEQ_COWER_TO_FLINCH] )
PLAY_CROWD_CONTROL_ANIM( NULL, CCPANIM_COWER_TO_FLINCH_INTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType,
SLOW_BLEND_IN, -0.1, -1, eCrowdControlAnimFlags )
PLAY_CROWD_CONTROL_ANIM( NULL, CCPANIM_FLINCH_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType,
SLOW_BLEND_IN, -0.1, -1, AF_LOOPING | eCrowdControlAnimFlags )
CLOSE_SEQUENCE_TASK( sLocalPedData.seqs[CCSEQ_COWER_TO_FLINCH] )
iSeqCount++
ENDIF
// Cower -> Flinch underfire
IF sLocalPedData.seqs[CCSEQ_COWER_TO_FLINCH_UNDERFIRE] = NULL
AND DOES_CROWD_CONTROL_PED_HAVE_ANIM( CCPANIM_COWER_TO_FLINCH_INTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
AND DOES_CROWD_CONTROL_PED_HAVE_ANIM( CCPANIM_FLINCH_UNDERFIRE_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
OPEN_SEQUENCE_TASK( sLocalPedData.seqs[CCSEQ_COWER_TO_FLINCH_UNDERFIRE] )
PLAY_CROWD_CONTROL_ANIM( NULL, CCPANIM_COWER_TO_FLINCH_UNDERFIRE_INTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType,
SLOW_BLEND_IN, -0.1, -1, eCrowdControlAnimFlags )
PLAY_CROWD_CONTROL_ANIM( NULL, CCPANIM_FLINCH_UNDERFIRE_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType,
SLOW_BLEND_IN, -0.1, -1, AF_LOOPING | eCrowdControlAnimFlags )
CLOSE_SEQUENCE_TASK( sLocalPedData.seqs[CCSEQ_COWER_TO_FLINCH_UNDERFIRE] )
iSeqCount++
ENDIF
FLOAT fLoopBlendIn
// Flinch -> Flinch underfire
IF sLocalPedData.seqs[CCSEQ_FLINCH_TO_FLINCH_UNDERFIRE] = NULL
AND DOES_CROWD_CONTROL_PED_HAVE_ANIM( CCPANIM_FLINCH_UNDERFIRE_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
OPEN_SEQUENCE_TASK( sLocalPedData.seqs[CCSEQ_FLINCH_TO_FLINCH_UNDERFIRE] )
IF DOES_CROWD_CONTROL_PED_HAVE_ANIM( CCPANIM_FLINCH_TO_FLINCH_UNDERFIRE_INTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
PLAY_CROWD_CONTROL_ANIM( NULL, CCPANIM_FLINCH_TO_FLINCH_UNDERFIRE_INTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType,
SLOW_BLEND_IN, -0.1, -1, eCrowdControlAnimFlags )
ENDIF
PLAY_CROWD_CONTROL_ANIM( NULL, CCPANIM_FLINCH_UNDERFIRE_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType,
SLOW_BLEND_IN, -0.1, -1, AF_LOOPING | eCrowdControlAnimFlags )
CLOSE_SEQUENCE_TASK( sLocalPedData.seqs[CCSEQ_FLINCH_TO_FLINCH_UNDERFIRE] )
iSeqCount++
ENDIF
// Flinch underfire -> Flinch
IF sLocalPedData.seqs[CCSEQ_FLINCH_UNDERFIRE_TO_FLINCH] = NULL
AND DOES_CROWD_CONTROL_PED_HAVE_ANIM( CCPANIM_FLINCH_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
OPEN_SEQUENCE_TASK( sLocalPedData.seqs[CCSEQ_FLINCH_UNDERFIRE_TO_FLINCH] )
fLoopBlendIn = REALLY_SLOW_BLEND_IN
IF DOES_CROWD_CONTROL_PED_HAVE_ANIM( CCPANIM_FLINCH_UNDERFIRE_TO_FLINCH_OUTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
PLAY_CROWD_CONTROL_ANIM( NULL, CCPANIM_FLINCH_UNDERFIRE_TO_FLINCH_OUTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType,
SLOW_BLEND_IN, -0.1, -1, eCrowdControlAnimFlags )
fLoopBlendIn = SLOW_BLEND_IN
ENDIF
PLAY_CROWD_CONTROL_ANIM( NULL, CCPANIM_FLINCH_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType,
fLoopBlendIn, -0.1, -1, AF_LOOPING | eCrowdControlAnimFlags )
CLOSE_SEQUENCE_TASK( sLocalPedData.seqs[CCSEQ_FLINCH_UNDERFIRE_TO_FLINCH] )
iSeqCount++
ENDIF
// Beatdown -> Flinch
IF sLocalPedData.seqs[CCSEQ_BEATDOWN_TO_FLINCH] = NULL
AND DOES_CROWD_CONTROL_PED_HAVE_ANIM( CCPANIM_BEATDOWN_OUTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
AND DOES_CROWD_CONTROL_PED_HAVE_ANIM( CCPANIM_FLINCH_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
OPEN_SEQUENCE_TASK( sLocalPedData.seqs[CCSEQ_BEATDOWN_TO_FLINCH] )
PLAY_CROWD_CONTROL_ANIM( NULL, CCPANIM_BEATDOWN_OUTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType,
REALLY_SLOW_BLEND_IN, -0.1, -1, eCrowdControlAnimFlags )
PLAY_CROWD_CONTROL_ANIM( NULL, CCPANIM_FLINCH_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType,
SLOW_BLEND_IN, -0.1, -1, AF_LOOPING | eCrowdControlAnimFlags )
CLOSE_SEQUENCE_TASK( sLocalPedData.seqs[CCSEQ_BEATDOWN_TO_FLINCH] )
iSeqCount++
ENDIF
// Cower -> Hero start -> Hero loop
IF sLocalPedData.seqs[CCSEQ_HERO_START] = NULL
AND DOES_CROWD_CONTROL_PED_HAVE_ANIM( CCPANIM_COWER_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
AND DOES_CROWD_CONTROL_PED_HAVE_ANIM( CCPANIM_HERO_START, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
OPEN_SEQUENCE_TASK( sLocalPedData.seqs[CCSEQ_HERO_START] )
PLAY_CROWD_CONTROL_ANIM( NULL, CCPANIM_HERO_START, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType,
REALLY_SLOW_BLEND_IN, 0.1, -1, eCrowdControlAnimFlags )
PLAY_CROWD_CONTROL_ANIM( NULL, CCPANIM_HERO_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType,
SLOW_BLEND_IN, -0.1, -1, AF_LOOPING | eCrowdControlAnimFlags )
CLOSE_SEQUENCE_TASK( sLocalPedData.seqs[CCSEQ_HERO_START] )
iSeqCount++
ENDIF
// Hero loop -> Hero pass -> Cower
IF sLocalPedData.seqs[CCSEQ_HERO_PASS] = NULL
AND DOES_CROWD_CONTROL_PED_HAVE_ANIM( CCPANIM_HERO_PASS, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
AND DOES_CROWD_CONTROL_PED_HAVE_ANIM( CCPANIM_COWER_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
OPEN_SEQUENCE_TASK( sLocalPedData.seqs[CCSEQ_HERO_PASS] )
PLAY_CROWD_CONTROL_ANIM( NULL, CCPANIM_HERO_PASS, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType,
REALLY_SLOW_BLEND_IN, -0.1, -1, eCrowdControlAnimFlags )
PLAY_CROWD_CONTROL_ANIM( NULL, CCPANIM_COWER_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType,
SLOW_BLEND_IN, -0.1, -1, AF_LOOPING | eCrowdControlAnimFlags )
CLOSE_SEQUENCE_TASK( sLocalPedData.seqs[CCSEQ_HERO_PASS] )
iSeqCount++
ENDIF
RETURN iSeqCount
ENDFUNC
PROC PLAY_CROWD_CONTROL_SEQUENCE( PED_INDEX ped, CROWD_CONTROL_SEQUENCES eSeq, INT iCrowdNum, CROWD_CONTROL_LOCAL_PED_DATA &sLocalPedData )
TASK_PERFORM_SEQUENCE( ped, sLocalPedData.seqs[eSeq] )
#IF NOT IS_DEBUG_BUILD
UNUSED_PARAMETER( iCrowdNum )
#ENDIF
#IF IS_DEBUG_BUILD
CPRINTLN( DEBUG_NET_MINIGAME, "PLAY_CROWD_CONTROL_SEQUENCE() - ", GET_CROWD_CONTROL_PED_NAME_FROM_ANIM_ID( sLocalPedData.eCreatorAssignedPedID ),
"[", iCrowdNum,
"] - Seq = ", GET_CROWD_CONTROL_SEQUENCE_NAME( eSeq ) )
#ENDIF
ENDPROC
PROC CACHE_CROWD_CONTROL_LOCAL_DATA( CROWD_CONTROL_LOCAL_DATA &sLocalData )
// Cache which rule is the crowd control rule for each team
INT iTeam
INT iPed
FOR iTeam = 0 TO FMMC_MAX_TEAMS-1
sLocalData.iCCRule[iTeam] = -1
FOR iPed = 0 TO FMMC_MAX_PEDS-1
IF g_FMMC_STRUCT_ENTITIES.sPlacedPed[iPed].iRule[iTeam] = FMMC_OBJECTIVE_LOGIC_CROWD_CONTROL
AND g_FMMC_STRUCT_ENTITIES.sPlacedPed[iPed].iPriority[iTeam] < FMMC_MAX_RULES
sLocalData.iCCRule[iTeam] = g_FMMC_STRUCT_ENTITIES.sPlacedPed[iPed].iPriority[iTeam]
ENDIF
ENDFOR
ENDFOR
FOR iTeam = 0 TO FMMC_MAX_TEAMS-1
FOR iPed = 0 TO FMMC_MAX_PEDS-1
IF INT_TO_ENUM( CROWD_CONTROL_PED_ENUM, g_FMMC_STRUCT_ENTITIES.sPlacedPed[iPed].iCrowdControlID ) = CCPED_CASHIER_A
OR INT_TO_ENUM( CROWD_CONTROL_PED_ENUM, g_FMMC_STRUCT_ENTITIES.sPlacedPed[iPed].iCrowdControlID ) = CCPED_CASHIER_B
sLocalData.iCashierPed = iPed
ENDIF
ENDFOR
ENDFOR
// Determine which heist we are on
FOR iTeam = 0 TO FMMC_MAX_TEAMS-1
IF sLocalData.iCCRule[iTeam] > -1
AND sLocalData.iCCRule[iTeam] < FMMC_MAX_RULES
IF IS_BIT_SET(g_FMMC_STRUCT.sFMMCEndConditions[iTeam].iRuleBitset[sLocalData.iCCRule[iTeam]],ciBS_RULE_CUSTOM_CROWD_CONTROL_FLEECA)
sLocalData.eHeistType = CCHEIST_CUSTOM_FLEECA
ELIF IS_BIT_SET(g_FMMC_STRUCT.sFMMCEndConditions[iTeam].iRuleBitset[sLocalData.iCCRule[iTeam]],ciBS_RULE_CUSTOM_CROWD_CONTROL_ORNATE)
sLocalData.eHeistType = CCHEIST_CUSTOM_ORNATE
ENDIF
ENDIF
ENDFOR
// Cache which rule is the thermite rule and which team has it
IF sLocalData.iThermiteTeam = -1
OR sLocalData.iThermiteRule = -1
INT iObj
FOR iTeam = 0 TO FMMC_MAX_TEAMS-1
FOR iObj = 0 TO FMMC_MAX_NUM_OBJECTS-1
IF g_FMMC_STRUCT_ENTITIES.sPlacedObject[iObj].iRule[iTeam] = FMMC_OBJECTIVE_LOGIC_MINIGAME
AND g_FMMC_STRUCT_ENTITIES.sPlacedObject[iObj].iPriority[iTeam] < FMMC_MAX_RULES
AND IS_BIT_SET( g_FMMC_STRUCT_ENTITIES.sPlacedObject[iObj].iLegacyMiniGameBitSet, cibsLEGACY_FMMC_MINI_GAME_USE_THERMAL_CHARGE )
sLocalData.iThermiteTeam = iTeam
sLocalData.iThermiteRule = g_FMMC_STRUCT_ENTITIES.sPlacedObject[iObj].iPriority[iTeam]
// Break loop
iTeam = FMMC_MAX_TEAMS
iObj = FMMC_MAX_NUM_OBJECTS
ENDIF
ENDFOR
ENDFOR
ENDIF
// 2221150 - Cache the interior, too many calls per frame on per ped checks to fetching the interior (EXPENSIVE)
IF sLocalData.interiorHeist = NULL
sLocalData.interiorHeist = GET_CROWD_CONTROL_HEIST_INTERIOR( sLocalData.eHeistType )
ENDIF
ENDPROC
PROC CACHE_CROWD_CONTROL_LOCAL_PED_DATA( CROWD_CONTROL_LOCAL_PED_DATA &sPedData, INT iPlacedPedID )
sPedData.eCreatorAssignedPedID = INT_TO_ENUM( CROWD_CONTROL_PED_ENUM, g_FMMC_STRUCT_ENTITIES.sPlacedPed[iPlacedPedID].iCrowdControlID )
ENDPROC
PROC SET_CROWD_CONTROL_PED_STATE( CROWD_CONTROL_PED_DECORATOR_DATA &sPedData, CROWD_CONTROL_PED_STATE eNewState, INT iCrowdID, CROWD_CONTROL_PED_ENUM eCreatorAssignedPedID)
DEBUG_PRINTCALLSTACK()
CPRINTLN( DEBUG_NET_MINIGAME, "SET_CROWD_CONTROL_PED_STATE() - [", iCrowdID, "] ", GET_CROWD_CONTROL_PED_NAME_FROM_ANIM_ID( eCreatorAssignedPedID ), " - State: ", GET_CROWD_CONTROL_PED_STATE_STRING( sPedData.eState ), " -> ", GET_CROWD_CONTROL_PED_STATE_STRING( eNewState ) )
sPedData.eStatePrev = sPedData.eState
sPedData.eState = eNewState
ENDPROC
FUNC BOOL SETUP_CROWD_CONTROL_PED_DATA( PED_INDEX ped, CROWD_CONTROL_PED_DECORATOR_DATA &sPedData, CROWD_CONTROL_LOCAL_PED_DATA &sLocalPedData, CROWD_CONTROL_HEIST_TYPE eHeistType )
BOOL bReady = TRUE
// Hero struff
CROWD_CONTROL_PED_HERO_ACTION eHeroAction
IF eHeistType != CCHEIST_NORMAL
eHeroAction = GET_CROWD_CONTROL_PED_HERO_ACTION_FOR_ANIMATED_PED( sLocalPedData.eCreatorAssignedPedID, eHeistType )
ELSE
eHeroAction = INT_TO_ENUM( CROWD_CONTROL_PED_HERO_ACTION, sLocalPedData.eCreatorAssignedPedID )
IF eHeroAction = CCHERO_ALARM
eHeroAction = CCHERO_NONE
ENDIF
ENDIF
// Give gun hero a their gun
IF eHeroAction = CCHERO_GUN
GIVE_WEAPON_TO_PED( ped, WEAPONTYPE_PISTOL, INFINITE_AMMO, FALSE, FALSE )
ENDIF
IF bReady
// Initialise struct
SET_CROWD_CONTROL_PED_STATE( sPedData, CCPS_INTRO, -1, sLocalPedData.eCreatorAssignedPedID )
sPedData.iBeatdownHitsRemaining = ciCC_MaxBeatdownHits
sPedData.fInfluenceDirectThreat = 1.0
sPedData.fInfluenceShouting = 1.0
IF eHeistType != CCHEIST_NORMAL
SET_BIT_ENUM( sPedData.iBitSet, CCPEDDATA_RunningBespokeAnims )
// Has alt anims, set ped to initially use them
// IF DOES_CROWD_CONTROL_PED_HAVE_ANIM( CCPANIM_COWER_LOOP_ALT, sLocalPedData.eCreatorAssignedPedID, eHeistType )
// SET_BIT( sPedData.iBitSet, CCPEDDATA_UseAlternativeAnims )
// ENDIF
ENDIF
// Initialise ped
SET_PED_COMBAT_ATTRIBUTES(ped, CA_ALWAYS_FLEE, FALSE)
SET_PED_FLEE_ATTRIBUTES(ped, FA_DISABLE_COWER,TRUE)
SET_PED_FLEE_ATTRIBUTES(ped, FA_DISABLE_FLEE_FROM_INDIRECT_THREATS,TRUE)
SET_PED_FLEE_ATTRIBUTES(ped, FA_COWER_INSTEAD_OF_FLEE,TRUE)
SET_RAGDOLL_BLOCKING_FLAGS(ped, RBF_PLAYER_BUMP | RBF_PLAYER_IMPACT | RBF_PLAYER_RAGDOLL_BUMP )
SET_PED_CAN_BE_TARGETTED(ped, FALSE) // 2048337
SET_PED_CONFIG_FLAG(ped, PCF_DisableExplosionReactions, TRUE)
SET_PED_CONFIG_FLAG(ped, PCF_DontActivateRagdollFromExplosions, TRUE)
SET_PED_CONFIG_FLAG(ped, PCF_AvoidTearGas, TRUE ) // FALSE)
SET_ENTITY_HEALTH(ped, 101)
CLEAR_PED_PROP( ped, ANCHOR_LEFT_HAND )
CLEAR_PED_PROP( ped, ANCHOR_RIGHT_HAND )
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL SETUP_CROWD_CONTROL_PED_LOCAL_PROPS( PED_INDEX ped, CROWD_CONTROL_LOCAL_PED_DATA &sLocalPedData, CROWD_CONTROL_HEIST_TYPE eHeistType )
BOOL bReady = TRUE
CROWD_CONTROL_PED_HERO_ACTION eHeroAction
IF eHeistType != CCHEIST_NORMAL
eHeroAction = GET_CROWD_CONTROL_PED_HERO_ACTION_FOR_ANIMATED_PED( sLocalPedData.eCreatorAssignedPedID, eHeistType )
ELSE
eHeroAction = INT_TO_ENUM( CROWD_CONTROL_PED_HERO_ACTION, sLocalPedData.eCreatorAssignedPedID )
IF eHeroAction = CCHERO_ALARM
eHeroAction = CCHERO_NONE
ENDIF
ENDIF
CPRINTLN( DEBUG_NET_MINIGAME, "SETUP_CROWD_CONTROL_PED_LOCAL_PROPS() - ", GET_CROWD_CONTROL_PED_NAME_FROM_ANIM_ID( sLocalPedData.eCreatorAssignedPedID ), " - eHeroAction = ", GET_CROWD_CONTROL_HERO_ACTION( eHeroAction ), " - eHeistType = ", GET_CROWD_CONTROL_HEIST_TYPE_STRING( eHeistType ) )
IF NOT DOES_ENTITY_EXIST( sLocalPedData.objProp )
// Need to spawn a phone for the ped
IF eHeroAction = CCHERO_PHONE
CPRINTLN( DEBUG_NET_MINIGAME, "SETUP_CROWD_CONTROL_PED_LOCAL_PROPS() - Spawning phone for phone hero ped..." )
IF HAS_MODEL_LOADED( PROP_PHONE_ING )
CPRINTLN( DEBUG_NET_MINIGAME, "SETUP_CROWD_CONTROL_PED_LOCAL_PROPS() - ... phone created." )
sLocalPedData.objProp = CREATE_OBJECT( PROP_PHONE_ING, GET_ENTITY_COORDS( ped ), FALSE )
ATTACH_ENTITY_TO_ENTITY( sLocalPedData.objProp, ped, GET_PED_BONE_INDEX( ped, BONETAG_PH_R_HAND ), <<0,0,0>>, <<0,0,0>>, TRUE, FALSE )
ELSE
bReady = FALSE
ENDIF
ENDIF
ENDIF
RETURN bReady
ENDFUNC
FUNC CROWD_CONTROL_DIALOGUE_CONTEXT GET_CROWD_CONTROL_DIALOGUE_CONTEXT_TIMER( CROWD_CONTROL_DIALOGUE eDialogue )
SWITCH eDialogue
CASE CCDIA_SHOT_DIRECTLY_AT
RETURN CCDIACONTEXT_SCARED_HIGH
BREAK
CASE CCDIA_SHOT_NEAR
RETURN CCDIACONTEXT_SCARED_MEDIUM
BREAK
CASE CCDIA_INITIAL_PANIC
CASE CCDIA_RECEIVES_SCARE
RETURN CCDIACONTEXT_SCARED_LOW
BREAK
CASE CCDIA_SEES_OTHER_KILLED
CASE CCDIA_STICKS_UP_FOR_OTHER
RETURN CCDIACONTEXT_SCARED_SECOND_HAND
BREAK
CASE CCDIA_COWER_LOW_FEAR
CASE CCDIA_COWER_HIGH_FEAR
CASE CCDIA_ALARM_GOES_OFF
RETURN CCDIACONTEXT_AMBIENT
BREAK
ENDSWITCH
RETURN CCDIACONTEXT_NUM_CONTEXTS
ENDFUNC
FUNC BOOL RESET_CROWD_CONTROL_GLOBAL_DIALOGUE_CONTEXT_TIMER( CROWD_CONTROL_DIALOGUE eDialogue, CROWD_CONTROL_LOCAL_DATA &sLocalData )
CROWD_CONTROL_DIALOGUE_CONTEXT eContext = GET_CROWD_CONTROL_DIALOGUE_CONTEXT_TIMER( eDialogue )
IF eContext != CCDIACONTEXT_NUM_CONTEXTS
INT iDelay
SWITCH eContext
CASE CCDIACONTEXT_SCARED_HIGH iDelay = 500 + ( GET_RANDOM_INT_IN_RANGE( 0, 6 ) * 100 ) /* 0.5 -> 1 secs */ BREAK
CASE CCDIACONTEXT_SCARED_MEDIUM iDelay = 1000 + ( GET_RANDOM_INT_IN_RANGE( 0, 5 ) * 500 ) /* 1 -> 3 secs */ BREAK
CASE CCDIACONTEXT_SCARED_LOW iDelay = 2000 + ( GET_RANDOM_INT_IN_RANGE( 0, 5 ) * 500 ) /* 2 -> 4 secs */ BREAK
CASE CCDIACONTEXT_SCARED_SECOND_HAND iDelay = 2000 + ( GET_RANDOM_INT_IN_RANGE( 0, 5 ) * 500 ) /* 2 -> 4 secs */ BREAK
CASE CCDIACONTEXT_AMBIENT iDelay = 5000 + ( GET_RANDOM_INT_IN_RANGE( 0, 6 ) * 1000 ) /* 5 -> 10 secs */ BREAK
ENDSWITCH
sLocalData.timeDialogueGlobalContextTimers[eContext] = GET_TIME_OFFSET( GET_NETWORK_TIME(), iDelay )
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL HAS_CROWD_CONTROL_GLOBAL_DIALOGUE_CONTEXT_TIMER_EXPIRED( CROWD_CONTROL_DIALOGUE eDialogue, CROWD_CONTROL_LOCAL_DATA &sLocalData )
CROWD_CONTROL_DIALOGUE_CONTEXT eContext = GET_CROWD_CONTROL_DIALOGUE_CONTEXT_TIMER( eDialogue )
// No context timer exists, ignore timer and return TRUE
// Context exists, check its timer
IF eContext = CCDIACONTEXT_NUM_CONTEXTS
OR IS_TIME_MORE_THAN( GET_NETWORK_TIME(), sLocalData.timeDialogueGlobalContextTimers[eContext] )
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL RESET_CROWD_CONTROL_PED_DIALOGUE_CONTEXT_TIMER( CROWD_CONTROL_DIALOGUE eDialogue, CROWD_CONTROL_LOCAL_PED_DATA &sLocalPedData )
CROWD_CONTROL_DIALOGUE_CONTEXT eContext = GET_CROWD_CONTROL_DIALOGUE_CONTEXT_TIMER( eDialogue )
IF eContext != CCDIACONTEXT_NUM_CONTEXTS
INT iDelay
SWITCH eContext
CASE CCDIACONTEXT_SCARED_HIGH iDelay = 1000 + ( GET_RANDOM_INT_IN_RANGE( 0, 3 ) * 500 ) /* 1 -> 2 secs */ BREAK
CASE CCDIACONTEXT_SCARED_MEDIUM iDelay = 3000 + ( GET_RANDOM_INT_IN_RANGE( 0, 3 ) * 500 ) /* 3 -> 4 secs */ BREAK
CASE CCDIACONTEXT_SCARED_LOW iDelay = 5000 + ( GET_RANDOM_INT_IN_RANGE( 0, 5 ) * 500 ) /* 5 -> 7 secs */ BREAK
CASE CCDIACONTEXT_SCARED_SECOND_HAND iDelay = 5000 + ( GET_RANDOM_INT_IN_RANGE( 0, 5 ) * 500 ) /* 5 -> 7 secs */ BREAK
CASE CCDIACONTEXT_AMBIENT iDelay = 12000 + ( GET_RANDOM_INT_IN_RANGE( 0, 6 ) * 1000 ) /* 12 -> 17 secs */ BREAK
ENDSWITCH
sLocalPedData.timeDialogueContextTimers[eContext] = GET_TIME_OFFSET( GET_NETWORK_TIME(), iDelay )
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL HAS_CROWD_CONTROL_PED_DIALOGUE_CONTEXT_TIMER_EXPIRED( CROWD_CONTROL_DIALOGUE eDialogue, CROWD_CONTROL_LOCAL_PED_DATA &sLocalPedData )
CROWD_CONTROL_DIALOGUE_CONTEXT eContext = GET_CROWD_CONTROL_DIALOGUE_CONTEXT_TIMER( eDialogue )
// No context timer exists, ignore timer and return TRUE
// Context exists, check its timer
IF eContext = CCDIACONTEXT_NUM_CONTEXTS
OR IS_TIME_MORE_THAN( GET_NETWORK_TIME(), sLocalPedData.timeDialogueContextTimers[eContext] )
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL IS_CROWD_CONTROL_PED_DIALOGUE_PRIORITY( CROWD_CONTROL_DIALOGUE eDialogueNew, CROWD_CONTROL_DIALOGUE eDialogueOld )
IF eDialogueNew != CCDIA_NONE
AND eDialogueNew > eDialogueOld
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL IS_CROWD_CONTROL_PED_DIALOGUE_CRITICAL( CROWD_CONTROL_DIALOGUE eDialogue )
IF eDialogue = CCDIA_HERO_ALARM_START
OR eDialogue = CCDIA_HERO_ALARM_CAUGHT
OR eDialogue = CCDIA_HERO_ALARM_COMPLETE
OR eDialogue = CCDIA_HERO_GUN_START
OR eDialogue = CCDIA_HERO_GUN_CAUGHT
OR eDialogue = CCDIA_HERO_GUN_COMPLETE
OR eDialogue = CCDIA_HERO_PHONE_START
OR eDialogue = CCDIA_HERO_PHONE_CAUGHT
OR eDialogue = CCDIA_HERO_PHONE_COMPLETE
OR eDialogue = CCDIA_ALARM_GOES_OFF_INITIAL
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL CHECK_CROWD_CONTROL_PED_DIALOGUE_CONTEXT_RANDOM_CHANCE( CROWD_CONTROL_DIALOGUE eDialogue )
SWITCH eDialogue
CASE CCDIA_SHOT_DIRECTLY_AT
RETURN GET_RANDOM_FLOAT_IN_RANGE(0, 1.0) <= 0.3 // 30% chance of playing
BREAK
ENDSWITCH
RETURN TRUE
ENDFUNC
FUNC BOOL GET_CROWD_CONTROL_VOICE_RACE_GENDER_PREFIX( CROWD_CONTROL_PED_ENUM ePed, CROWD_CONTROL_HEIST_TYPE eHeistType, TEXT_LABEL_15 &tlRaceGenderPrefix, TEXT_LABEL_23 &tlVoiceName )
BOOL bIsPedMale
BOOL bIsPedBlack
INT iPedNumber
SWITCH eHeistType
CASE CCHEIST_CUSTOM_FLEECA
SWITCH ePed
CASE CCPED_CASHIER_A
bIsPedMale = TRUE
bIsPedBlack = FALSE
iPedNumber = 1
BREAK
CASE CCPED_CUSTOMER_A
bIsPedMale = TRUE
bIsPedBlack = FALSE
iPedNumber = 1
BREAK
CASE CCPED_CUSTOMER_B
bIsPedMale = FALSE
bIsPedBlack = FALSE
iPedNumber = 1
BREAK
DEFAULT
RETURN FALSE
BREAK
ENDSWITCH
BREAK
CASE CCHEIST_CUSTOM_ORNATE
SWITCH ePed
CASE CCPED_CASHIER_A
bIsPedMale = TRUE
bIsPedBlack = FALSE
iPedNumber = 1
BREAK
CASE CCPED_CASHIER_B
bIsPedMale = TRUE
bIsPedBlack = FALSE
iPedNumber = 2
BREAK
CASE CCPED_CUSTOMER_A
bIsPedMale = TRUE
bIsPedBlack = FALSE
iPedNumber = 2
BREAK
CASE CCPED_CUSTOMER_B
bIsPedMale = TRUE
bIsPedBlack = TRUE
iPedNumber = 1
BREAK
CASE CCPED_CUSTOMER_C
bIsPedMale = FALSE
bIsPedBlack = FALSE
iPedNumber = 1
BREAK
CASE CCPED_CUSTOMER_D
bIsPedMale = TRUE
bIsPedBlack = FALSE
iPedNumber = 2
BREAK
CASE CCPED_CUSTOMER_E
bIsPedMale = FALSE
bIsPedBlack = TRUE
iPedNumber = 1
BREAK
CASE CCPED_CUSTOMER_F
bIsPedMale = FALSE
bIsPedBlack = FALSE
iPedNumber = 2
BREAK
CASE CCPED_CUSTOMER_G
bIsPedMale = TRUE
bIsPedBlack = FALSE
iPedNumber = 1
BREAK
CASE CCPED_CUSTOMER_H
bIsPedMale = FALSE
bIsPedBlack = FALSE
iPedNumber = 1
BREAK
DEFAULT
RETURN FALSE
BREAK
ENDSWITCH
BREAK
DEFAULT
RETURN FALSE
BREAK
ENDSWITCH
// Add hostages prefix
IF ePed = CCPED_CASHIER_A
OR ePed = CCPED_CASHIER_B
tlRaceGenderPrefix += "B"
tlVoiceName += "BANK"
ELSE
tlRaceGenderPrefix += "H"
tlVoiceName += "HOSTAGE"
ENDIF
// Add ethnicity prefix
IF bIsPedBlack
tlRaceGenderPrefix += "B"
tlVoiceName += "B"
ELSE
tlRaceGenderPrefix += "W"
tlVoiceName += "W"
ENDIF
// Add gender prefix
IF bIsPedMale
tlRaceGenderPrefix += "M"
tlVoiceName += "M"
ELSE
tlRaceGenderPrefix += "F"
tlVoiceName += "F"
ENDIF
// Add optional id number
IF iPedNumber != -1
tlRaceGenderPrefix += iPedNumber
tlVoiceName += iPedNumber
ENDIF
RETURN TRUE
ENDFUNC
FUNC BOOL GET_CROWD_CONTROL_PED_DIALOGUE_CONVO_SUFFIX( CROWD_CONTROL_DIALOGUE eDialogue, TEXT_LABEL_15 &tlConvoSuffix, INT &iLineNumber )
SWITCH eDialogue
// Generic, everyone should have these
CASE CCDIA_INITIAL_PANIC tlConvoSuffix += "C" BREAK
CASE CCDIA_COWER_LOW_FEAR tlConvoSuffix += "RE" BREAK
CASE CCDIA_COWER_HIGH_FEAR tlConvoSuffix += "SC" BREAK
CASE CCDIA_RECEIVES_SCARE tlConvoSuffix += "RS" BREAK
CASE CCDIA_STICKS_UP_FOR_OTHER tlConvoSuffix += "SF" BREAK
CASE CCDIA_SEES_OTHER_KILLED tlConvoSuffix += "HK" BREAK
CASE CCDIA_SHOT_NEAR tlConvoSuffix += "SN" BREAK
CASE CCDIA_SHOT_DIRECTLY_AT tlConvoSuffix += "SA" BREAK
CASE CCDIA_ALARM_GOES_OFF tlConvoSuffix += "AL" BREAK
CASE CCDIA_ALARM_GOES_OFF_INITIAL tlConvoSuffix += "GO" BREAK
// Hero stuff, specific peds will have these
CASE CCDIA_HERO_ALARM_START
tlConvoSuffix += "GP"
iLineNumber = 1
BREAK
CASE CCDIA_HERO_ALARM_CAUGHT
tlConvoSuffix += "DPA"
iLineNumber = 1
BREAK
CASE CCDIA_HERO_ALARM_COMPLETE
tlConvoSuffix += "PA"
iLineNumber = 1
BREAK
CASE CCDIA_HERO_PHONE_START
tlConvoSuffix += "DP"
iLineNumber = 1
BREAK
CASE CCDIA_HERO_PHONE_CAUGHT
tlConvoSuffix += "PA"
iLineNumber = 1
BREAK
CASE CCDIA_HERO_PHONE_COMPLETE
tlConvoSuffix += "CC"
iLineNumber = 1
BREAK
CASE CCDIA_HERO_PHONE_APOLOGISE
tlConvoSuffix += "SS"
iLineNumber = 1
BREAK
CASE CCDIA_HERO_GUN_START
tlConvoSuffix += "DG"
iLineNumber = 1
BREAK
CASE CCDIA_HERO_GUN_CAUGHT
tlConvoSuffix += "GA"
iLineNumber = 1
BREAK
CASE CCDIA_HERO_GUN_COMPLETE
tlConvoSuffix += "A"
iLineNumber = 1
BREAK
DEFAULT
RETURN FALSE
BREAK
ENDSWITCH
RETURN TRUE
ENDFUNC
FUNC BOOL GET_CROWD_CONTROL_PED_DIALOGUE_ROOT_LABEL( CROWD_CONTROL_PED_ENUM ePed, CROWD_CONTROL_DIALOGUE eDialogue, CROWD_CONTROL_HEIST_TYPE eHeistType, TEXT_LABEL_23 &tlRootLabel, TEXT_LABEL_23 &tlVoiceName, INT &iLineNumber )
TEXT_LABEL_15 tlRaceGenderPrefix
IF GET_CROWD_CONTROL_VOICE_RACE_GENDER_PREFIX( ePed, eHeistType, tlRaceGenderPrefix, tlVoiceName )
TEXT_LABEL_15 tlConvoSuffix
IF GET_CROWD_CONTROL_PED_DIALOGUE_CONVO_SUFFIX( eDialogue, tlConvoSuffix, iLineNumber )
tlRootLabel = "OBHF_"
tlRootLabel += tlRaceGenderPrefix
tlRootLabel += tlConvoSuffix
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
PROC PLAY_CROWD_CONTROL_AMBIENT_SPEECH( PED_INDEX pedCrowd, CROWD_CONTROL_PED_ENUM ePed, CROWD_CONTROL_DIALOGUE eDialogueToPlay, CROWD_CONTROL_HEIST_TYPE eHeistType, BOOL bNeedsToInterrupt, BOOL bSyncOverNetwork = FALSE )
TEXT_LABEL_23 tlConvo
TEXT_LABEL_23 tlVoiceName
INT iLineNumber = -1
IF GET_CROWD_CONTROL_PED_DIALOGUE_ROOT_LABEL( ePed, eDialogueToPlay, eHeistType, tlConvo, tlVoiceName, iLineNumber )
SPEECH_PARAMS eParams
// IF bNeedsToInterrupt
// eParams = SPEECH_PARAMS_INTERRUPT_SHOUTED_CRITICAL
// ELSE
// eParams = SPEECH_PARAMS_FORCE_SHOUTED_CRITICAL
// ENDIF
UNUSED_PARAMETER(bNeedsToInterrupt)
eParams = SPEECH_PARAMS_FORCE_SHOUTED_CRITICAL
PLAY_AMBIENT_SPEECH_USING_LINE_FROM_CONVERSATION( pedCrowd, tlConvo, tlVoiceName, iLineNumber, eParams, bSyncOverNetwork )
CPRINTLN( DEBUG_NET_MINIGAME, "PLAY_CROWD_CONTROL_AMBIENT_SPEECH() - Conversation Root = ", tlConvo, " Voice Name = ", tlVoiceName, " iLineNumber = ", iLineNumber, " Params = ", AUDIO_SPEECH_GET_PARAM_STRING_FROM_ENUM( eParams ) )
ENDIF
ENDPROC
FUNC AUD_DAMAGE_REASON GET_CROWD_CONTROL_PED_ALTERNATIVE_PAIN_DIALOGUE( CROWD_CONTROL_DIALOGUE eDialogue )
/* For screams only use the following:
SCREAM_PANIC_SHORT = really short "huh"
SCREAM_PANIC = longer "huh"
SCREAM_SHOCKED = quick "aaahh"
SCREAM_TERROR = longer "ahhhh"
Other screams may or may not be present on a per ped basis */
SWITCH eDialogue
CASE CCDIA_APPROACH
RETURN AUD_DAMAGE_REASON_SCREAM_PANIC_SHORT
BREAK
CASE CCDIA_COWER_HIGH_FEAR
CASE CCDIA_COWER_LOW_FEAR
CASE CCDIA_ALARM_GOES_OFF
CASE CCDIA_ALARM_GOES_OFF_INITIAL
SWITCH GET_RANDOM_INT_IN_RANGE( 0, 2 )
CASE 0 RETURN AUD_DAMAGE_REASON_WHIMPER BREAK
CASE 1 RETURN AUD_DAMAGE_REASON_COWER BREAK
ENDSWITCH
BREAK
CASE CCDIA_STICKS_UP_FOR_OTHER
RETURN AUD_DAMAGE_REASON_SCREAM_PANIC
BREAK
CASE CCDIA_INITIAL_PANIC
SWITCH GET_RANDOM_INT_IN_RANGE( 0, 2 )
CASE 0 RETURN AUD_DAMAGE_REASON_SCREAM_SHOCKED BREAK
CASE 1 RETURN AUD_DAMAGE_REASON_SCREAM_PANIC BREAK
ENDSWITCH
BREAK
CASE CCDIA_SHOT_DIRECTLY_AT
RETURN AUD_DAMAGE_REASON_SCREAM_TERROR
BREAK
CASE CCDIA_SHOT_NEAR
CASE CCDIA_SEES_OTHER_KILLED
RETURN AUD_DAMAGE_REASON_SCREAM_SHOCKED
BREAK
CASE CCDIA_RECEIVES_SCARE
SWITCH GET_RANDOM_INT_IN_RANGE( 0, 2 )
CASE 0 RETURN AUD_DAMAGE_REASON_SCREAM_SHOCKED BREAK
CASE 1 RETURN AUD_DAMAGE_REASON_SCREAM_PANIC BREAK
ENDSWITCH
BREAK
ENDSWITCH
RETURN AUD_DAMAGE_REASON_DEFAULT
ENDFUNC
ENUM CROWD_CONTROL_DIALOGUE_PLAYBACK_FLAGS
CCDIAPLAYBACK_NONE = 0
,CCDIAPLAYBACK_FORCE_PAIN = 1
,CCDIAPLAYBACK_FORCE_CRITICAL = 2
ENDENUM
/// PURPOSE:
/// Queues up a conversation locally and priority checks against what is already stored. Keeps this local ready to be packed and sent to the host later.
/// PARAMS:
/// sLocalPedData - peds local data where the info will be stored
/// eDialogue - the dialogue you want to queue up
/// bIsCritical - is it critical? this will affect if this conversation ignores timers, max number of speakers, interrupts, etc.
PROC QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( CROWD_CONTROL_LOCAL_DATA &sLocalData, CROWD_CONTROL_LOCAL_PED_DATA &sLocalPedData, CROWD_CONTROL_DIALOGUE eDialogue, CROWD_CONTROL_DIALOGUE_PLAYBACK_FLAGS eFlags = CCDIAPLAYBACK_NONE )
#IF NOT IS_DEBUG_BUILD
UNUSED_PARAMETER( sLocalData )
#ENDIF
IF IS_CROWD_CONTROL_PED_DIALOGUE_PRIORITY( eDialogue, sLocalPedData.eDialogueClientToHost )
OR IS_BITMASK_ENUM_AS_ENUM_SET( eFlags, CCDIAPLAYBACK_FORCE_CRITICAL )
// Update the local ped owners local ped data, this is needed for case we attempt to play more than 1 convo on 1 ped in the same update
// we would decide here which takes priority for this ped
sLocalPedData.eDialogueClientToHost = eDialogue
IF IS_BITMASK_ENUM_AS_ENUM_SET( eFlags, CCDIAPLAYBACK_FORCE_PAIN )
SET_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_DialogueClientToHostForcePain )
ELSE
CLEAR_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_DialogueClientToHostForcePain )
ENDIF
IF IS_BITMASK_ENUM_AS_ENUM_SET( eFlags, CCDIAPLAYBACK_FORCE_CRITICAL )
SET_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_DialogueClientToHostForceCritical )
ELSE
CLEAR_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_DialogueClientToHostForceCritical )
ENDIF
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CDEBUG1LN( DEBUG_NET_MINIGAME, "QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST() - ", GET_CROWD_CONTROL_PED_NAME_FROM_ANIM_ID( sLocalPedData.eCreatorAssignedPedID ),
" - Conversation queued = ", GET_CROWD_CONTROL_DIALOGUE_ENUM_STRING( sLocalPedData.eDialogueClientToHost ),
" - Force Pain = ", BOOL_TO_STRING( IS_BITMASK_ENUM_AS_ENUM_SET( eFlags, CCDIAPLAYBACK_FORCE_PAIN ) ),
" - Force Critical = ", BOOL_TO_STRING( IS_BITMASK_ENUM_AS_ENUM_SET( eFlags, CCDIAPLAYBACK_FORCE_CRITICAL ) ) )
ENDIF
#ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Collects all the conversations on peds this machine owns and sends them on to the host
/// PARAMS:
/// sLocalPedData - local ped data which will have all the
/// iNumCrowdPeds - number of crowd control peds
PROC PROCESS_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST_QUEUE( INT iCrowdPed, CROWD_CONTROL_LOCAL_DATA &sLocalData, CROWD_CONTROL_LOCAL_PED_DATA &sLocalPedData,
INT &iPackedDialogueConversations[], INT &iPackedDialoguePainFlags, INT &iPackedDialogueCriticalFlags )
BOOL bDiscardDialogue
bDiscardDialogue = FALSE
// Theres something to send, add it to the packed data
// To avoid spamming
IF sLocalPedData.eDialogueClientToHost != CCDIA_NONE
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitset, CCLOCALDATA_DebugLogDialogue )
CDEBUG1LN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST_QUEUE() - ",
GET_CROWD_CONTROL_PED_NAME_FROM_ANIM_ID( sLocalPedData.eCreatorAssignedPedID ),
" - Dialogue = ", GET_CROWD_CONTROL_DIALOGUE_ENUM_STRING( sLocalPedData.eDialogueClientToHost ) )
ENDIF
#ENDIF
#IF NOT IS_DEBUG_BUILD
UNUSED_PARAMETER(sLocalData)
#ENDIF
IF IS_TIME_MORE_THAN( GET_NETWORK_TIME(), sLocalPedData.timeDialogueLastBroadcast )
OR IS_CROWD_CONTROL_PED_DIALOGUE_CRITICAL( sLocalPedData.eDialogueClientToHost )
OR IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_DialogueClientToHostForceCritical )
SET_PACKED_BITFIELD_VALUE_IN_ARRAY( iPackedDialogueConversations, iCrowdPed, ciCC_DialogueDataBitWidth, ENUM_TO_INT( sLocalPedData.eDialogueClientToHost ) )
IF IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_DialogueClientToHostForcePain )
SET_BIT( iPackedDialoguePainFlags, iCrowdPed )
ELSE
CLEAR_BIT( iPackedDialoguePainFlags, iCrowdPed )
ENDIF
IF IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_DialogueClientToHostForceCritical )
SET_BIT( iPackedDialogueCriticalFlags, iCrowdPed )
ELSE
CLEAR_BIT( iPackedDialogueCriticalFlags, iCrowdPed )
ENDIF
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitset, CCLOCALDATA_DebugLogDialogue )
CDEBUG1LN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST_QUEUE() packed for broadcast" )
ENDIF
#ENDIF
sLocalPedData.timeDialogueLastBroadcast = GET_TIME_OFFSET( GET_NETWORK_TIME(), 1000 + ( GET_RANDOM_INT_IN_RANGE( 0, 5 ) * 250 ) ) // 1 -> 2 seconds (250ms intervals)
ELSE
bDiscardDialogue = TRUE
ENDIF
// Reset this machines local dialogue data if not the host (leave alone for the host unless bDiscardDialogue = TRUE)
IF bDiscardDialogue
OR NOT NETWORK_IS_HOST_OF_THIS_SCRIPT()
sLocalPedData.eDialogueClientToHost = CCDIA_NONE
CLEAR_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_DialogueClientToHostForcePain )
CLEAR_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_DialogueClientToHostForceCritical )
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitset, CCLOCALDATA_DebugLogDialogue )
IF NOT NETWORK_IS_HOST_OF_THIS_SCRIPT()
CDEBUG1LN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST_QUEUE() discarded(not host)" )
ELSE
CDEBUG1LN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST_QUEUE() discarded(timer not ready)" )
ENDIF
ENDIF
#ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Processes the queue of conversations that have come in from other clients (and itself) to decide who is allowed to speak (and who to ignore).
/// PARAMS:
/// niPed - array of mission controller peds
/// sLocalData - local data
/// sLocalPedData - local ped data (this will have the received CLIENT to HOST conversations stored )
/// iCrowdControlCloudIndex - lookup indices for crowd control peds
/// iNumCrowdControlPeds - number of crowd control peds in this crowd
PROC PROCESS_CROWD_CONTROL_DIALOGUE_SELECTION( NETWORK_INDEX &niPed[],
CROWD_CONTROL_SERVER_DATA &sData,
CROWD_CONTROL_LOCAL_DATA &sLocalData,
CROWD_CONTROL_LOCAL_PED_DATA &sLocalPedData[],
INT &iCrowdControlCloudIndex[], INT iNumCrowdControlPeds )
INT i
// INITIAL PASS - Last time talking timer & number of current speakers
//------------------------------------------------------------------------------
INT iNumberOfPedsCurrentlySpeaking
INT iNumberOfPedsCurrentlyPlayingPain
PED_INDEX pedsTemp[FMMC_MAX_CROWD_PEDS]
BOOL bPedInjured[FMMC_MAX_CROWD_PEDS]
BOOL bIsAmbientSpeechPlaying[FMMC_MAX_CROWD_PEDS]
BOOL bIsAnySpeechPlaying[FMMC_MAX_CROWD_PEDS]
FOR i = 0 TO iNumCrowdControlPeds-1
INT iPed = iCrowdControlCloudIndex[i]
IF NETWORK_DOES_ENTITY_EXIST_WITH_NETWORK_ID( niPed[iPed] )
pedsTemp[i] = NET_TO_PED( niPed[iPed] )
bPedInjured[i] = IS_PED_INJURED( pedsTemp[i] )
IF NOT bPedInjured[i]
bIsAnySpeechPlaying[i] = IS_ANY_SPEECH_PLAYING( pedsTemp[i] )
bIsAmbientSpeechPlaying[i] = IS_AMBIENT_SPEECH_PLAYING( pedsTemp[i] )
ELSE
bIsAnySpeechPlaying[i] = FALSE
bIsAmbientSpeechPlaying[i] = FALSE
ENDIF
IF NOT bPedInjured[i]
IF bIsAmbientSpeechPlaying[i]
iNumberOfPedsCurrentlySpeaking++
ELIF bIsAnySpeechPlaying[i]
iNumberOfPedsCurrentlyPlayingPain++
ENDIF
ENDIF
ENDIF
ENDFOR
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "\n" )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() <1st PASS> - Count the speakers - Total speakers = ", iNumberOfPedsCurrentlySpeaking + iNumberOfPedsCurrentlyPlayingPain, "/", ciCC_MaxNumberOfSpeakers,
" Dialogue = ", iNumberOfPedsCurrentlySpeaking, "/", ciCC_MaxNumberOfAmbientDialogue,
" Pain = ", iNumberOfPedsCurrentlyPlayingPain, "/", ciCC_MaxNumberOfPain )
CPRINTLN( DEBUG_NET_MINIGAME, "\n" )
ENDIF
#ENDIF
// 2nd PASS - Find the highest priority dialogues to play
//------------------------------------------------------------------------------
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() <2nd PASS> - Populating the queue.")
CPRINTLN( DEBUG_NET_MINIGAME, "\n" )
ENDIF
#ENDIF
// Create random look up table to mix up the queue
INT iRnd[FMMC_MAX_CROWD_PEDS]
FOR i = 0 TO iNumCrowdControlPeds-1
// Initialise numbers
iRnd[i] = i
ENDFOR
FOR i = 0 TO iNumCrowdControlPeds-1
// Randomise numbers
INT iRndIndex = GET_RANDOM_INT_IN_RANGE( 0, iNumCrowdControlPeds )
INT iTemp = iRnd[i]
iRnd[i] = iRnd[iRndIndex]
iRnd[iRndIndex] = iTemp
ENDFOR
// Initialise priority queue to -1
INT iCrowdNumSpeakerPriorityQueue[FMMC_MAX_CROWD_PEDS]
FOR i = 0 TO COUNT_OF( iCrowdNumSpeakerPriorityQueue ) - 1
iCrowdNumSpeakerPriorityQueue[i] = -1
ENDFOR
// Go through each ped wanting to speak and check them against the priority queue
FOR i = 0 TO iNumCrowdControlPeds-1
INT iCrowdPed = iRnd[i] // randomises the
IF sLocalPedData[iCrowdPed].eDialogueClientToHost != CCDIA_NONE
IF pedsTemp[iCrowdPed] != NULL
IF NOT bPedInjured[iCrowdPed]
AND ( NOT bIsAnySpeechPlaying[iCrowdPed]
//OR IS_BIT_SET_ENUM( sLocalPedData[iCrowdPed].iBitset, CCLOCALPEDDATA_DialogueClientToHostCritical ) )
OR IS_CROWD_CONTROL_PED_DIALOGUE_PRIORITY( sLocalPedData[iCrowdPed].eDialogueClientToHost, sLocalPedData[iCrowdPed].eDialogueCurrentlyPlaying ) )
BOOL bConvoStored
INT j
// Run through the queue comparing with whats already stored
FOR j = 0 TO COUNT_OF( iCrowdNumSpeakerPriorityQueue ) - 1
// Can store this, is higher priority
IF iCrowdNumSpeakerPriorityQueue[j] = -1
OR IS_CROWD_CONTROL_PED_DIALOGUE_PRIORITY(
sLocalPedData[iCrowdPed].eDialogueClientToHost,
sLocalPedData[iCrowdNumSpeakerPriorityQueue[j]].eDialogueClientToHost )
// Push everything down 1 entry
INT k
k = COUNT_OF( iCrowdNumSpeakerPriorityQueue ) - 1
// If theres anything currently stored in the bottom of the queue, get rid of it as we're not going to use it
IF iCrowdNumSpeakerPriorityQueue[k] != -1
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() - [", iCrowdNumSpeakerPriorityQueue[k], "] ",
GET_CROWD_CONTROL_PED_NAME_FROM_ANIM_ID( sLocalPedData[iCrowdNumSpeakerPriorityQueue[k]].eCreatorAssignedPedID ),
" - Dialogue = ", GET_CROWD_CONTROL_DIALOGUE_ENUM_STRING( sLocalPedData[iCrowdNumSpeakerPriorityQueue[k]].eDialogueClientToHost ),
" discared from SLOT[", k, "]" )
ENDIF
#ENDIF
sLocalPedData[k].eDialogueClientToHost = CCDIA_NONE
CLEAR_BIT_ENUM( sLocalPedData[k].iBitSet, CCLOCALPEDDATA_DialogueClientToHostForcePain )
ENDIF
WHILE k >= j
IF k = j
iCrowdNumSpeakerPriorityQueue[k] = iCrowdPed
ELSE
iCrowdNumSpeakerPriorityQueue[k] = iCrowdNumSpeakerPriorityQueue[k-1]
ENDIF
k--
ENDWHILE
bConvoStored = TRUE
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() - [", iCrowdPed, "] ",
GET_CROWD_CONTROL_PED_NAME_FROM_ANIM_ID( sLocalPedData[iCrowdPed].eCreatorAssignedPedID ),
" - Dialogue = ", GET_CROWD_CONTROL_DIALOGUE_ENUM_STRING( sLocalPedData[iCrowdPed].eDialogueClientToHost ),
" inserted into queue SLOT[", j, "]" )
ENDIF
#ENDIF
j = COUNT_OF( iCrowdNumSpeakerPriorityQueue ) // break loop
ENDIF
ENDFOR
// Convo wasn't high enough priority to beat other conversations, erase
IF NOT bConvoStored
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() - ", GET_CROWD_CONTROL_DIALOGUE_ENUM_STRING( sLocalPedData[iCrowdPed].eDialogueClientToHost ), " not high enough priority, cleared dialogue" )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() - [", iCrowdPed, "] ",
GET_CROWD_CONTROL_PED_NAME_FROM_ANIM_ID( sLocalPedData[iCrowdPed].eCreatorAssignedPedID ),
" - Dialogue = ", GET_CROWD_CONTROL_DIALOGUE_ENUM_STRING( sLocalPedData[iCrowdPed].eDialogueClientToHost ),
" discarded, priority not high enough" )
ENDIF
#ENDIF
sLocalPedData[iCrowdPed].eDialogueClientToHost = CCDIA_NONE
CLEAR_BIT_ENUM( sLocalPedData[iCrowdPed].iBitSet, CCLOCALPEDDATA_DialogueClientToHostForcePain )
ENDIF
ELSE
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CDEBUG1LN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() - [", iCrowdPed, "] ",
GET_CROWD_CONTROL_PED_NAME_FROM_ANIM_ID( sLocalPedData[iCrowdPed].eCreatorAssignedPedID ),
" - Dialogue = ", GET_CROWD_CONTROL_DIALOGUE_ENUM_STRING( sLocalPedData[iCrowdPed].eDialogueClientToHost ),
" - Unable to start queue up of this dialogue, ped already speaking " )
ENDIF
#ENDIF
ENDIF
ENDIF
ENDIF
ENDFOR
// FINAL(3rd) PASS - Play the dialogue we have queued
//------------------------------------------------------------------------------
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "\n" )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() - <3rd PASS> Processing the queue." )
CPRINTLN( DEBUG_NET_MINIGAME, "\n" )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() - Queue Snapshop" )
ENDIF
#ENDIF
// Print the queue
FOR i = 0 TO COUNT_OF( iCrowdNumSpeakerPriorityQueue ) - 1
IF iCrowdNumSpeakerPriorityQueue[i] != -1
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() SLOT[", i, "] = ",
"[", iCrowdNumSpeakerPriorityQueue[i], "] ", GET_CROWD_CONTROL_PED_NAME_FROM_ANIM_ID( sLocalPedData[iCrowdNumSpeakerPriorityQueue[i]].eCreatorAssignedPedID ),
" - Dialogue = ", GET_CROWD_CONTROL_DIALOGUE_ENUM_STRING( sLocalPedData[iCrowdNumSpeakerPriorityQueue[i]].eDialogueClientToHost ) )
ENDIF
#ENDIF
ENDIF
ENDFOR
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "\n" )
ENDIF
#ENDIF
INT iPackedDialogueConversations[2]
INT iPackedDialogueIsPainFlags
FOR i = 0 TO COUNT_OF( iCrowdNumSpeakerPriorityQueue )-1
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() - Processing SLOT[", i, "]" )
ENDIF
#ENDIF
// Ped queued
IF iCrowdNumSpeakerPriorityQueue[i] != -1
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() [", iCrowdNumSpeakerPriorityQueue[i], "] ",
GET_CROWD_CONTROL_PED_NAME_FROM_ANIM_ID( sLocalPedData[iCrowdNumSpeakerPriorityQueue[i]].eCreatorAssignedPedID ),
" - Dialogue = ", GET_CROWD_CONTROL_DIALOGUE_ENUM_STRING( sLocalPedData[iCrowdNumSpeakerPriorityQueue[i]].eDialogueClientToHost ) )
ENDIF
#ENDIF
INT iCrowdPed = iCrowdNumSpeakerPriorityQueue[i]
IF sLocalPedData[iCrowdPed].eDialogueClientToHost != CCDIA_NONE
IF pedsTemp[iCrowdPed] != NULL
IF NOT bPedInjured[iCrowdPed]
// Ped is not already speaking
AND ( NOT bIsAnySpeechPlaying[iCrowdPed]
// OR the new dialogue is a higher priority than what is already being said
// AND is critical
OR ( ( IS_CROWD_CONTROL_PED_DIALOGUE_CRITICAL( sLocalPedData[iCrowdPed].eDialogueClientToHost )
OR IS_BIT_SET_ENUM( sLocalPedDAta[iCrowdPed].iBitSet, CCLOCALPEDDATA_DialogueClientToHostForceCritical ) )
AND IS_CROWD_CONTROL_PED_DIALOGUE_PRIORITY( sLocalPedData[iCrowdPed].eDialogueClientToHost, sLocalPedData[iCrowdPed].eDialogueCurrentlyPlaying ) ) )
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() Attempting dialogue - Speakers(Total) = ", iNumberOfPedsCurrentlySpeaking + iNumberOfPedsCurrentlyPlayingPain, "/", ciCC_MaxNumberOfSpeakers,
" Dialogue = ", iNumberOfPedsCurrentlySpeaking, "/", ciCC_MaxNumberOfAmbientDialogue,
" Pain = ", iNumberOfPedsCurrentlyPlayingPain, "/", ciCC_MaxNumberOfPain )
ENDIF
#ENDIF
BOOL bCanPlayThisDialogue
TEXT_LABEL_23 tlConvo = ""
TEXT_LABEL_23 tlDummy = ""
INT iLine = -1
IF GET_CROWD_CONTROL_PED_DIALOGUE_ROOT_LABEL( sLocalPedData[iCrowdPed].eCreatorAssignedPedID, sLocalPedData[iCrowdPed].eDialogueClientToHost, sLocalData.eHeistType, tlConvo, tlDummy, iLine )
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() - Found Root Label \"", tlConvo, "\" iLine = ", iLine )
ENDIF
#ENDIF
TEXT_LABEL_23 tlConvoFilename = GET_CONVERSATION_LINE_FILENAME_FROM_CONVERSATION_ROOT( tlConvo, iLine )
IF NOT IS_STRING_NULL_OR_EMPTY( tlConvoFilename )
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() - Convo Filename \"", tlConvoFilename, "\" does exist!" )
ENDIF
#ENDIF
bCanPlayThisDialogue = TRUE
ELSE
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() - Convo Filename \"", tlConvoFilename, "\" does not exist" )
ENDIF
#ENDIF
ENDIF
ENDIF
// SELECT - PLAY AMBIENT SPEECH
//----------------------------------------------------------------
// IF this dialogue context exists
// AND we don't specifically want to play pain
// AND we've checked the random chance for this context
IF bCanPlayThisDialogue
AND NOT IS_BIT_SET_ENUM( sLocalPedData[iCrowdPed].iBitSet, CCLOCALPEDDATA_DialogueClientToHostForcePain )
AND CHECK_CROWD_CONTROL_PED_DIALOGUE_CONTEXT_RANDOM_CHANCE( sLocalPedData[iCrowdPed].eDialogueClientToHost )
// Only ever have CCDIA_ALARM_GOES_OFF_INITIAL said once
// by a ped that definitely has the dialogue
AND ( sLocalPedData[iCrowdPed].eDialogueClientToHost != CCDIA_ALARM_GOES_OFF_INITIAL
OR NOT IS_BIT_SET_ENUM( sData.iBitSet, CCDATA_RaiseAlarmDiagloue ) )
// AND is this a critical line of dialogue (actual or forced)
AND ( ( IS_CROWD_CONTROL_PED_DIALOGUE_CRITICAL( sLocalPedData[iCrowdPed].eDialogueClientToHost )
OR IS_BIT_SET_ENUM( sLocalPedData[iCrowdPed].iBitSet, CCLOCALPEDDATA_DialogueClientToHostForceCritical ) )
// OR context timer for this dialogue has expired and there is room for another speaker
OR ( iNumberOfPedsCurrentlySpeaking < ciCC_MaxNumberOfAmbientDialogue
AND HAS_CROWD_CONTROL_GLOBAL_DIALOGUE_CONTEXT_TIMER_EXPIRED( sLocalPedData[iCrowdPed].eDialogueClientToHost, sLocalData )
AND HAS_CROWD_CONTROL_PED_DIALOGUE_CONTEXT_TIMER_EXPIRED( sLocalPedData[iCrowdPed].eDialogueClientToHost, sLocalPedData[iCrowdPed] ) ) )
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() HOST wants to play ambient speech." )
ENDIF
#ENDIF
// Already speaking/playing pain
IF bIsAnySpeechPlaying[iCrowdPed]
// Was playing dialogue, counters not change
IF NOT bIsAmbientSpeechPlaying[iCrowdPed]
iNumberOfPedsCurrentlyPlayingPain--
iNumberOfPedsCurrentlySpeaking++
ENDIF
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() Ambient speech already playing - OVERRIDING" )
ENDIF
#ENDIF
// Wasn't playing anything
ELSE
iNumberOfPedsCurrentlySpeaking++
ENDIF
IF sLocalPedData[iCrowdPed].eDialogueClientToHost = CCDIA_ALARM_GOES_OFF_INITIAL
SET_BIT_ENUM( sData.iBitSet, CCDATA_RaiseAlarmDiagloue )
ENDIF
// Store ready to be played (for this machine)
sLocalPedData[iCrowdPed].eDialogueHostToClient = sLocalPedData[iCrowdPed].eDialogueClientToHost
CLEAR_BIT_ENUM( sLocalPedData[iCrowdPed].iBitSet, CCLOCALPEDDATA_DialogueHostToClientPain )
// Host resets the context timer and sends it to the remote players too as it uses a random delay
// (if done on each machine the delay would be different for each machine)
RESET_CROWD_CONTROL_PED_DIALOGUE_CONTEXT_TIMER( sLocalPedData[iCrowdPed].eDialogueHostToClient, sLocalPedData[iCrowdPed] )
RESET_CROWD_CONTROL_GLOBAL_DIALOGUE_CONTEXT_TIMER( sLocalPedData[iCrowdPed].eDialogueHostToClient, sLocalData )
// Also pack ready for broadcast (to all other remote machines)
SET_PACKED_BITFIELD_VALUE_IN_ARRAY_FROM_ENUM( iPackedDialogueConversations, iCrowdPed, ciCC_DialogueDataBitWidth, sLocalPedData[iCrowdPed].eDialogueClientToHost )
CLEAR_BIT( iPackedDialogueIsPainFlags, iCrowdPed )
// SELECT - PLAY PAIN
//----------------------------------------------------------------
ELIF NOT bIsAnySpeechPlaying[iCrowdPed]
OR IS_CROWD_CONTROL_PED_DIALOGUE_CRITICAL( sLocalPedData[iCrowdPed].eDialogueClientToHost )
OR IS_BIT_SET_ENUM( sLocalPedData[iCrowdPed].iBitSet, CCLOCALPEDDATA_DialogueClientToHostForceCritical )
// ... Check if the ped has an alternative Pain based dialogue
// Force through critical pain
IF iNumberOfPedsCurrentlyPlayingPain < ciCC_MaxNumberOfPain
OR sLocalPedData[iCrowdPed].eDialogueClientToHost = CCDIA_INITIAL_PANIC
OR IS_BIT_SET_ENUM( sLocalPedData[iCrowdPed].iBitSet, CCLOCALPEDDATA_DialogueClientToHostForceCritical )
IF GET_CROWD_CONTROL_PED_ALTERNATIVE_PAIN_DIALOGUE( sLocalPedData[iCrowdPed].eDialogueClientToHost ) != AUD_DAMAGE_REASON_DEFAULT
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() HOST wants to play pain audio" )
ENDIF
#ENDIF
// Store ready to be played
sLocalPedData[iCrowdPed].eDialogueHostToClient = sLocalPedData[iCrowdPed].eDialogueClientToHost
SET_BIT_ENUM( sLocalPedData[iCrowdPed].iBitSet, CCLOCALPEDDATA_DialogueHostToClientPain )
// Also pack ready for broadcast
SET_PACKED_BITFIELD_VALUE_IN_ARRAY_FROM_ENUM( iPackedDialogueConversations, iCrowdPed, ciCC_DialogueDataBitWidth, sLocalPedData[iCrowdPed].eDialogueClientToHost )
SET_BIT( iPackedDialogueIsPainFlags, iCrowdPed )
// Already speaking/playing pain
IF bIsAnySpeechPlaying[iCrowdPed]
// Was playing dialogue, counters not change
IF NOT bIsAmbientSpeechPlaying[iCrowdPed]
iNumberOfPedsCurrentlyPlayingPain++
iNumberOfPedsCurrentlySpeaking--
ENDIF
ELSE
iNumberOfPedsCurrentlyPlayingPain++
ENDIF
ELSE
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() No alt pain version of this dialogue" )
ENDIF
#ENDIF
ENDIF
ELSE
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() Too many speakers, can't play pain audio" )
ENDIF
#ENDIF
ENDIF
ELSE
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() Ped already speaking, cant play pain audio" )
ENDIF
#ENDIF
ENDIF
ELSE
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() Ped is dead" )
ENDIF
#ENDIF
ENDIF
ELSE
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() Net id invalid" )
ENDIF
#ENDIF
ENDIF
ELSE
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() No dialogue stored" )
ENDIF
#ENDIF
ENDIF
ELSE
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_DIALOGUE_QUEUE() SLOT empty" )
ENDIF
#ENDIF
ENDIF
ENDFOR
// Broadcast dialogue to be played to all clients, if there is something to play
IF iPackedDialogueConversations[0] != 0
OR iPackedDialogueConversations[1] != 0
BROADCAST_FMMC_CC_DIALOGUE_HOST_TO_CLIENTS( iPackedDialogueConversations, iPackedDialogueIsPainFlags )
ENDIF
// Reset client to host data (DO NOT ERASE HostToClient data, otherwise this machine won't play any dialogue locally)
FOR i = 0 TO iNumCrowdControlPeds-1
sLocalPedData[i].eDialogueClientToHost = CCDIA_NONE
CLEAR_BIT_ENUM( sLocalPedData[i].iBitSet, CCLOCALPEDDATA_DialogueClientToHostForcePain )
CLEAR_BIT_ENUM( sLocalPedData[i].iBitSet, CCLOCALPEDDATA_DialogueClientToHostForceCritical )
ENDFOR
ENDPROC
/// PURPOSE:
/// Dialogue for each ped that has come in from the host and now needs to be player is played here locally on this machine. Each machine will need to call this in order to process playing the dialogue locally.
/// PARAMS:
/// niPeds - array of mission controller peds
/// iCrowdPedCloudIndex - lookup array used to index crowd control peds
/// iNumCrowdPeds - number of crowd control peds available
/// sLocalPedData - local ped data (this will have the received HOST to CLIENT conversations stored )
/// sLocalData - other local cached data
PROC PROCESS_CROWD_CONTROL_DIALOGUE_HOST_TO_CLIENT_QUEUE( PED_INDEX ped, CROWD_CONTROL_LOCAL_PED_DATA &sLocalPedData, CROWD_CONTROL_LOCAL_DATA &sLocalData )
IF sLocalPedData.eDialogueHostToClient != CCDIA_NONE
BOOL bIsAnySpeechPlaying = IS_ANY_SPEECH_PLAYING( ped )
BOOL bInterrupt
IF bIsAnySpeechPlaying
// Only interrupt if higher priority and its either critical or is we are interrupting dialogue with pain audio
IF IS_CROWD_CONTROL_PED_DIALOGUE_PRIORITY( sLocalPedData.eDialogueHostToClient, sLocalPedData.eDialogueCurrentlyPlaying )
AND ( IS_CROWD_CONTROL_PED_DIALOGUE_CRITICAL( sLocalPedData.eDialogueHostToClient )
OR ( IS_AMBIENT_SPEECH_PLAYING( ped )
AND IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_DialogueHostToClientPain ) ) )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_PLAYING_LOCAL_DIALOGUE() - IS_ANY_SPEECH_PLAYING() = true, Calling STOP_CURRENT_PLAYING_SPEECH()" )
STOP_CURRENT_PLAYING_SPEECH( ped )
bInterrupt = TRUE
ENDIF
ELSE
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_PLAYING_LOCAL_DIALOGUE() - IS_ANY_SPEECH_PLAYING() = false, nothing to stop." )
ENDIF
IF NOT bIsAnySpeechPlaying
OR bInterrupt
IF NOT IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_DialogueHostToClientPain )
PLAY_CROWD_CONTROL_AMBIENT_SPEECH( ped,
sLocalPedData.eCreatorAssignedPedID,
sLocalPedData.eDialogueHostToClient,
sLocalData.eHeistType, bInterrupt )
#IF IS_DEBUG_BUILD
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DebugLogDialogue )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_PLAYING_LOCAL_DIALOGUE() - ",
GET_CROWD_CONTROL_PED_NAME_FROM_ANIM_ID( sLocalPedData.eCreatorAssignedPedID ),
" - Played Ambient Speech = ",
GET_CROWD_CONTROL_DIALOGUE_ENUM_STRING( sLocalPedData.eDialogueHostToClient ) )
ENDIF
#ENDIF
ELSE
AUD_DAMAGE_REASON ePainAudio = GET_CROWD_CONTROL_PED_ALTERNATIVE_PAIN_DIALOGUE( sLocalPedData.eDialogueHostToClient )
PLAY_PAIN( ped, ePainAudio, 0, FALSE )
#IF IS_DEBUG_BUILD
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_PLAYING_LOCAL_DIALOGUE() - ",
GET_CROWD_CONTROL_PED_NAME_FROM_ANIM_ID( sLocalPedData.eCreatorAssignedPedID ),
" - Played Pain Dialogue = ",
GET_CROWD_CONTROL_DIALOGUE_ENUM_STRING( sLocalPedData.eDialogueHostToClient ),
" - Pain Audio = ",
ePainAudio )
#ENDIF
ENDIF
sLocalPedData.eDialogueCurrentlyPlaying = sLocalPedData.eDialogueHostToClient
// Reset peds local dialogue data
sLocalPedData.eDialogueHostToClient = CCDIA_NONE
CLEAR_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_DialogueHostToClientPain )
ELSE
// Keep dialogue until the ped is no longer speaking
ENDIF
ENDIF
ENDPROC
PROC PROCESS_CROWD_CONTROL_FEAR( CROWD_CONTROL_SERVER_DATA &sData, CROWD_CONTROL_LOCAL_DATA &sLocalData,
NETWORK_INDEX &niPeds[], CROWD_CONTROL_PED_DECORATOR_DATA &sPedData[], CROWD_CONTROL_LOCAL_PED_DATA &sLocalPedData[],
BOOL bRestrictCrowdPed )
INT i
BOOL bBlockFearDrop
// Do not update fear if fear is currently blocked for that rule
IF IS_BIT_SET_ENUM( sData.iBitSet, CCDATA_FearFailsBlocked )
CLEAR_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_TimerSetFearUnblocked )
EXIT
ELSE
// 2184755
// also do not update the fear for fleeca for the first 5 seconds of the fear becoming unblocked
IF sLocalData.eHeistType = CCHEIST_CUSTOM_FLEECA
IF NOT IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_TimerSetFearUnblocked )
sLocalData.timeFearFailsUnblocked = GET_NETWORK_TIME()
SET_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_TimerSetFearUnblocked )
ENDIF
IF IS_TIME_LESS_THAN( GET_NETWORK_TIME(), GET_TIME_OFFSET( sLocalData.timeFearFailsUnblocked, 5000 ) )
bBlockFearDrop = TRUE
ENDIF
ENDIF
ENDIF
PED_INDEX pedsTemp[FMMC_MAX_CROWD_PEDS]
BOOL bPedInjured[FMMC_MAX_CROWD_PEDS]
BOOL bHasControl[FMMC_MAX_CROWD_PEDS]
// Pre-update
//----------------------------------------------------------------------------------------------------
// Count the number of peds alive left to influence the fear level
INT iCrowdSize
FLOAT fInfluenceDirectThreatTotal, fInfluenceShoutingTotal
FOR i = 0 TO sData.iNumCrowdControlPeds-1
INT iPed = sData.iCrowdControlCloudIndex[i]
IF NETWORK_DOES_ENTITY_EXIST_WITH_NETWORK_ID( niPeds[iPed] )
pedsTemp[i] = NET_TO_PED( niPeds[iPed] )
bPedInjured[i] = IS_PED_INJURED( pedsTemp[i] )
bHasControl[i] = NETWORK_HAS_CONTROL_OF_NETWORK_ID( niPeds[iPed] )
IF NOT bPedInjured[i]
iCrowdSize++
fInfluenceDirectThreatTotal += sPedData[i].fInfluenceDirectThreat
fInfluenceShoutingTotal += sPedData[i].fInfluenceShouting
ENDIF
ELSE
pedsTemp[i] = NULL
bPedInjured[i] = FALSE
ENDIF
ENDFOR
// Work out the deficit of fear and redistribute it to other peds that are no currently being intimidated
FLOAT fLowerInfluenceThreshold = 0.5 * iCrowdSize
FLOAT fInfluenceDirectThreatDeficit = FMAX( fLowerInfluenceThreshold - fInfluenceDirectThreatTotal, 0.0 )
FLOAT fInfluenceShoutingDeficit = FMAX( fLowerInfluenceThreshold - fInfluenceShoutingTotal, 0.0 )
// Find the threat we want to use for fear
//----------------------------------------------------------------------------------------------------
CROWD_CONTROL_THREAT_TYPE eThreatUsedForFear[FMMC_MAX_CROWD_PEDS]
INT iRedistibuteDirectThreatDeficitCount, iRedistributeShoutDeficitCount
BOOL bShouldUpdateFear[FMMC_MAX_CROWD_PEDS]
FOR i = 0 TO sData.iNumCrowdControlPeds-1
IF pedsTemp[i] != NULL
IF NOT bPedInjured[i]
// HOST & OWNER ONLY: Work out this peds contribution to fear change
IF NETWORK_IS_HOST_OF_THIS_SCRIPT()
OR bHasControl[i]
eThreatUsedForFear[i] = sLocalPedData[i].eBiggestThreat
BOOL bPlayingIntro
IF IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedsTemp[i], CCPANIM_INTRO_LOOP, sLocalPedData[i].eCreatorAssignedPedID, sLocalData.eHeistType, TRUE )
OR IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedsTemp[i], CCPANIM_INTRO, sLocalPedData[i].eCreatorAssignedPedID, sLocalData.eHeistType, TRUE )
bPlayingIntro = TRUE
ENDIF
// Don't update this ped's fear while they are forcibly restricted
IF NOT IS_CROWD_CONTROL_PED_RESTRICTED( sLocalPedData[i].eCreatorAssignedPedID, sLocalData.eHeistType, CCPS_HERO_ATTEMPT, bRestrictCrowdPed )
// and not until the intro scene has finished
AND NOT bPlayingIntro
AND sPedData[i].eState != CCPS_COMBAT
AND IS_BIT_SET_ENUM( sPedData[i].iBitSet, CCDATA_Active )
bShouldUpdateFear[i] = TRUE
IF IS_BIT_SET_ENUM( sPedData[i].iBitSet, CCPEDDATA_FearLocked )
CLEAR_BIT_ENUM( sPedData[i].iBitSet, CCPEDDATA_FearLocked )
ENDIF
// Update ped's fear level
// Use threat type overrides for different states
SWITCH sPedData[i].eState
CASE CCPS_INVALID
CASE CCPS_INTRO
CASE CCPS_HERO
// NO FEAR!
eThreatUsedForFear[i] = CCTHREAT_NONE
BREAK
CASE CCPS_COWER
// no override
BREAK
CASE CCPS_FLINCH
IF eThreatUsedForFear[i] < CCTHREAT_PROXIMITY
eThreatUsedForFear[i] = CCTHREAT_PROXIMITY
ENDIF
BREAK
CASE CCPS_BEATDOWN
// If in the beat down state force the fear to be at least the beating fear
IF eThreatUsedForFear[i] < CCTHREAT_BEATING
eThreatUsedForFear[i] = CCTHREAT_BEATING
ENDIF
BREAK
CASE CCPS_HERO_ATTEMPT
// Only decrease the fear at this point as we will snap their fear when caught instead
eThreatUsedForFear[i] = CCTHREAT_NONE
BREAK
ENDSWITCH
// OWNER ONLY: Count the number of peds whos influence should increase
IF bHasControl[i]
IF NOT SHOULD_CROWD_CONTROL_PED_DIRECT_THREAT_INFLUENCE_DECREASE( eThreatUsedForFear[i] )
iRedistibuteDirectThreatDeficitCount++
ENDIF
IF NOT SHOULD_CROWD_CONTROL_PED_SHOUT_THREAT_INFLUENCE_DECREASE( eThreatUsedForFear[i] )
iRedistributeShoutDeficitCount++
ENDIF
ENDIF
ELSE
// Only mark as locked while not playing the intro
IF NOT bPlayingIntro
IF NOT IS_BIT_SET_ENUM( sPedData[i].iBitSet, CCPEDDATA_FearLocked )
SET_BIT_ENUM( sPedData[i].iBitSet, CCPEDDATA_FearLocked )
ENDIF
ELSE
IF IS_BIT_SET_ENUM( sPedData[i].iBitSet, CCPEDDATA_FearLocked )
CLEAR_BIT_ENUM( sPedData[i].iBitSet, CCPEDDATA_FearLocked )
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDFOR
// Redistribute any deficit of influence and update the peds fear
//----------------------------------------------------------------------------------------------------
FLOAT fFearDelta
FLOAT fInfluenceDirectThreatDeficitPerPed = fInfluenceDirectThreatDeficit / iRedistibuteDirectThreatDeficitCount
FLOAT fInfluenceShoutingDeficitPerPed = fInfluenceShoutingDeficit / iRedistributeShoutDeficitCount
FOR i = 0 TO sData.iNumCrowdControlPeds-1
IF bShouldUpdateFear[i]
IF pedsTemp[i] != NULL
IF NOT bPedInjured[i]
// OWNER ONLY: Update the influence
IF bHasControl[i]
UPDATE_CROWD_CONTROL_PED_DIRECT_THREAT_INFLUENCE(
sPedData[i],
sLocalPedData[i],
eThreatUsedForFear[i],
fInfluenceDirectThreatDeficitPerPed )
UPDATE_CROWD_CONTROL_PED_SHOUT_THREAT_INFLUENCE(
sPedData[i],
sLocalPedData[i],
eThreatUsedForFear[i],
fInfluenceShoutingDeficitPerPed )
ENDIF
// HOST ONLY: Take fear contribution
IF NETWORK_IS_HOST_OF_THIS_SCRIPT()
fFearDelta += GET_CROWD_CONTROL_PED_FEAR_DELTA(
sData,
sLocalData,
sPedData[i],
sLocalPedData[i].pedBiggestThreat,
eThreatUsedForFear[i],
i,
iCrowdSize )
ENDIF
ENDIF
ENDIF
ENDIF
ENDFOR
// HOST ONLY: calculate new fear value and update broadcast data when possible
//----------------------------------------------------------------------------------------------------
IF NETWORK_IS_HOST_OF_THIS_SCRIPT()
IF fFearDelta != 0
AND ( NOT bBlockFearDrop OR fFearDelta > 0 )
FLOAT fChangeRate
IF fFearDelta > 0.0
// Adjust the change rate in the ranges 0.0 -> 75.0
FLOAT fRange = 100.0 - cfCC_PedBreakoutCowerThreshold
FLOAT fAlpha = CLAMP( FMIN( sLocalData.fFear / fRange, 100.0 ), 0.0, 1.0 )
// As the fear increases, make the increase rate decrease
fChangeRate = INTERP_FLOAT( cfCC_FearIncreaseRateMax, cfCC_FearIncreaseRateMin, fAlpha )
ELIF fFearDelta < 0.0
// Adjust the change rate in the ranges 25.0 -> 100.0
FLOAT fRange = 100.0 - cfCC_PedBreakoutCowerThreshold
FLOAT fAlpha = CLAMP( FMAX( sLocalData.fFear - cfCC_PedBreakoutCowerThreshold, 0.0 ) / fRange, 0.0, 1.0 )
// As the fear decreases, make the decrease rate decrease
fChangeRate = INTERP_FLOAT( cfCC_FearDecreaseRateMin, cfCC_FearDecreaseRateMax, fAlpha )
ENDIF
// Adjust for ACCEL/DECEL
fFearDelta *= fChangeRate
// Adjust for framerate
fFearDelta *= TIMESTEP()
// Apply the fear change
sLocalData.fFear = CLAMP( sLocalData.fFear + fFearDelta, 0.0, 100.0 )
ENDIF
ENDIF
// HOST
BOOL bFearUpdated
IF NETWORK_IS_HOST_OF_THIS_SCRIPT()
// Check if we can update the server fear value
IF ( sLocalData.fFear != sData.fFear AND sLocalData.iUpdateTimer >= 10 ) // values are different but wait for at least 10 frames to have elapsed since previous update
OR ABSF( sData.fFear - sLocalData.fFear ) >= 3.0 // difference is greater than 3%, update now
// Update the server fear value
sData.fFear = sLocalData.fFear
bFearUpdated = TRUE
sLocalData.iUpdateTimer = 0 // reset the timer
ELSE
sLocalData.iUpdateTimer++ // increment timer
ENDIF
// REMOTE
ELSE
// Server value differs to ours
IF sData.fFear != sLocalData.fFear
// pull the updated value from the server
sLocalData.fFear = sData.fFear
bFearUpdated = TRUE
ENDIF
ENDIF
// An update has occurred on the host or a remote player, update interpolation rate
IF bFearUpdated
sLocalData.fIntimidationBarInterpRate = ABSF( sLocalData.fIntimidationBar - sLocalData.fFear )
ENDIF
ENDPROC
PROC PROCESS_CROWD_CONTROL_PED_AI( PED_INDEX pedCrowd,
CROWD_CONTROL_SERVER_DATA &sCrowdControlData, CROWD_CONTROL_LOCAL_DATA &sLocalData,
CROWD_CONTROL_PED_DECORATOR_DATA &sCrowdControlPedData, CROWD_CONTROL_LOCAL_PED_DATA &sLocalPedData,
INT iPed, INT iCrowdStructID, BOOL bRestrictCrowdPed, BOOL bIsInjured )
IF IS_BIT_SET_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_HasBeenInitialised )
CROWD_CONTROL_THREAT_TYPE eThreatActual = sLocalPedData.eBiggestThreat
CROWD_CONTROL_THREAT_TYPE eThreatFelt = sLocalPedData.eBiggestThreat
// Ped state
//------------------------------------------------
// Ped has been killed
IF IS_BIT_SET_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_Dead )
OR ( DOES_ENTITY_EXIST( pedCrowd ) AND bIsInjured )
IF NOT IS_BIT_SET_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_KnockedOut )
IF NOT IS_BIT_SET_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_Dead )
sLocalPedData.timeDied = GET_NETWORK_TIME()
SET_BIT_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_Dead )
ENDIF
IF NOT IS_BIT_SET_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_DeadProcessed )
IF NETWORK_IS_HOST_OF_THIS_SCRIPT()
sCrowdControlData.iNumHitsRemaining -= ciCC_BeatdownHitsLostOnPedDeath
ELSE
BROADCAST_FMMC_CC_PED_BEATDOWN_HIT( -ciCC_BeatdownHitsLostOnPedDeath )
ENDIF
SET_BIT_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_DeadProcessed )
ENDIF
ENDIF
ENDIF
// Hero details
//------------------------------------------------
CROWD_CONTROL_PED_HERO_ACTION eHeroAction = CCHERO_NONE
// Animated hero
//IF IS_BIT_SET_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_RunningBespokeAnims )
IF DOES_CROWD_CONTROL_PED_HAVE_ANIM( CCPANIM_HERO_START, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
eHeroAction = GET_CROWD_CONTROL_PED_HERO_ACTION_FOR_ANIMATED_PED( sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
// None anim hero (set by content creators)
ELSE
eHeroAction = INT_TO_ENUM( CROWD_CONTROL_PED_HERO_ACTION, g_FMMC_STRUCT_ENTITIES.sPlacedPed[iPed].iCrowdControlAction )
IF eHeroAction = CCHERO_ALARM
eHeroAction = CCHERO_NONE
ENDIF
ENDIF
// Reset hero attempted flag if this ped is no longer a hero
IF IS_BIT_SET_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_HeroAttempted )
IF NOT IS_BIT_SET( sCrowdControlData.iCurrentActiveHeros, iCrowdStructID )
CLEAR_BIT_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_HeroAttempted )
ENDIF
ENDIF
// Fail checks
//------------------------------------------------
// While crowd control is active and no other fail has already occured
// Also don't fail if we have already passed
IF IS_BIT_SET_ENUM( sCrowdControlData.iBitSet, CCDATA_Active )
AND NOT IS_BIT_SET_ENUM( sCrowdControlData.iBitSet, CCDATA_Failed )
AND NOT IS_BIT_SET_ENUM( sCrowdControlData.iBitSet, CCDATA_FailDisabled )
AND sLocalData.eFailReason = CCFAIL_NONE
IF IS_BIT_SET_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_HeroComplete )
IF IS_BIT_SET_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_Dead )
IF eHeroAction = CCHERO_GUN
sLocalData.eFailReason = CCFAIL_PEDHEROGUN
sLocalData.iFailCausedByPed = iPed
sLocalData.timeFailDelay = GET_TIME_OFFSET( GET_NETWORK_TIME(), 5000 )
ENDIF
ELSE
SWITCH eHeroAction
CASE CCHERO_PHONE
sLocalData.eFailReason = CCFAIL_PEDHEROPHONE
sLocalData.iFailCausedByPed = iPed
sLocalData.timeFailDelay = GET_TIME_OFFSET( GET_NETWORK_TIME(), 5000 )
BREAK
CASE CCHERO_ALARM
sLocalData.eFailReason = CCFAIL_PEDHEROALARM
sLocalData.iFailCausedByPed = sLocalData.iCashierPed
sLocalData.timeFailDelay = GET_NETWORK_TIME() // no delay
BREAK
ENDSWITCH
CPRINTLN(DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_PED_AI() - [", iCrowdStructID,"] FAIL CHECKS - HeroComplete = ", GET_CROWD_CONTROL_HERO_ACTION( eHeroAction ) )
ENDIF
ENDIF
// A fail has been set locally & broadcast it
IF sLocalData.eFailReason != CCFAIL_NONE
// If we are the server set the server data to indicate a fail
IF NETWORK_IS_HOST_OF_THIS_SCRIPT()
SET_BIT_ENUM( sCrowdControlData.iBitSet, CCDATA_Failed )
ENDIF
CPRINTLN(DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_PED_AI() - [", iCrowdStructID,"] FAIL DETECTED - Fail Reason = ", GET_CROWD_CONTROL_FAIL_REASON_STRING( sLocalData.eFailReason ) )
BROADCAST_FMMC_CC_FAIL( ENUM_TO_INT( sLocalData.eFailReason ), sLocalData.iFailCausedByPed, sLocalData.timeFailDelay )
ENDIF
ENDIF
// Check to see if the a hero peds prop(phone) has been broken
// IF NOT IS_BIT_SET( sCrowdControlPedData.iBitSet, CCPEDDATA_HeroBroken )
// IF NETWORK_DOES_ENTITY_EXIST_WITH_NETWORK_ID( sCrowdControlPedData.niProp )
// IF HAS_ENTITY_BEEN_DAMAGED_BY_ANY_PED( NET_TO_OBJ( sCrowdControlPedData.niProp ) )
// SET_BIT( sCrowdControlPedData.iBitSet, CCPEDDATA_HeroBroken )
// ENDIF
// ENDIF
// ENDIF
// Update Ped
//------------------------------------------------
IF DOES_ENTITY_EXIST( pedCrowd )
AND NOT bIsInjured
FLOAT fCapSize
fCapSize = GET_CROWD_CONTROL_PED_CAPSULE_SIZE( sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
IF fCapSize != -1
SET_PED_CAPSULE( pedCrowd, fCapSize )
ENDIF
// Check here if the ped should get knocked out of the animated state and go to AI logic
IF IS_BIT_SET_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_RunningBespokeAnims )
SET_PED_RESET_FLAG( pedCrowd, PRF_PreventAllMeleeTakedowns, TRUE )
SET_PED_RESET_FLAG( pedCrowd, PRF_DisableMeleeHitReactions, TRUE )
// Any ped has shot or hurt the ped to kick them out of their anim.
IF HAS_ENTITY_BEEN_DAMAGED_BY_ANY_PED( pedCrowd )
CLEAR_BIT_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_RunningBespokeAnims )
//sCrowdControlPedData.eState = CCPS_COWER
SET_CROWD_CONTROL_PED_STATE( sCrowdControlPedData, CCPS_COWER, iCrowdStructID, sLocalPedData.eCreatorAssignedPedID )
IF NOT HAS_ENTITY_BEEN_DAMAGED_BY_WEAPON( pedCrowd, WEAPONTYPE_SMOKEGRENADE )
AND NOT HAS_ENTITY_BEEN_DAMAGED_BY_WEAPON( pedCrowd, WEAPONTYPE_BZGAS )
CLEAR_PED_TASKS( pedCrowd )
ENDIF
ENDIF
ENDIF
// Threat
//--------------------------------------------------------------------------------------------
// Update last time shot at
IF eThreatActual != CCTHREAT_SHOOTING
AND eThreatActual != CCTHREAT_SHOOTING_CLOSE
IF NOT IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeLastShotAtSet )
sLocalPedData.timeLastShotAt = GET_NETWORK_TIME()
SET_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeLastShotAtSet )
ENDIF
ELSE
IF eThreatActual = CCTHREAT_SHOOTING
CLEAR_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_ShotAtFearWasClose )
ELIF eThreatActual = CCTHREAT_SHOOTING_CLOSE
SET_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_ShotAtFearWasClose )
ENDIF
CLEAR_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeLastShotAtSet )
ENDIF
// Update projectile fear timer
// Don't start the timer if the threat is a sticky bomb and this ped is set to ignore sticky bombs
IF ( NOT IS_BIT_SET_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_IgnoreStickyBombs )
AND eThreatActual = CCTHREAT_PROJECTILE_EXPLOSIVE_STICKY_BOMB )
OR eThreatActual = CCTHREAT_PROJECTILE_EXPLOSIVE
IF NOT IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeSetStartFearProjectile )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_PED_AI() - [", iCrowdStructID,"] Projectile fear time stored" )
SET_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeSetStartFearProjectile )
sLocalPedData.timeStartProjectileFear = GET_NETWORK_TIME()
ENDIF
ENDIF
// Update proxmity fear timer
IF eThreatActual = CCTHREAT_PROXIMITY
IF NOT IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeSetStartProximityThreat )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_PED_AI() - [", iCrowdStructID,"] Proximity fear time stored" )
sLocalPedData.timeStartProximityThreat = GET_NETWORK_TIME()
SET_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeSetStartProximityThreat )
ENDIF
ELSE
IF IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeSetStartProximityThreat )
CLEAR_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeSetStartProximityThreat )
ENDIF
ENDIF
// Fear overrides
IF IS_BIT_SET_ENUM( sCrowdControlData.iBitSet, CCDATA_Active )
// Shooting fear adjustments
// IF IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_ShotAtFearWasClose )
//
// IF eThreatFelt < CCTHREAT_SHOOTING_CLOSE
//
// IF NOT IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeLastShotAtSet )
// OR IS_TIME_LESS_THAN( GET_NETWORK_TIME(), GET_TIME_OFFSET( sLocalPedData.timeLastShotAt, ciCC_PedShotAtCoolOffDurationClose ) )
//
// eThreatFelt = CCTHREAT_SHOOTING_CLOSE
//
// ENDIF
//
// ENDIF
//
// ELSE
IF eThreatFelt < CCTHREAT_SHOOTING
IF NOT IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeLastShotAtSet )
OR IS_TIME_LESS_THAN( GET_NETWORK_TIME(), GET_TIME_OFFSET( sLocalPedData.timeLastShotAt, ciCC_PedShotAtCoolOffDuration ) )
eThreatFelt = CCTHREAT_SHOOTING
ENDIF
ENDIF
// ENDIF
// Projectile fear adjustments
IF IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeSetStartFearProjectile )
// Keep the ped scared of the projectile for a period of time
IF IS_TIME_LESS_THAN( GET_NETWORK_TIME(),
GET_TIME_OFFSET( sLocalPedData.timeStartProjectileFear, ciCC_ProjectileCoolOffDuration ) )
IF eThreatFelt <= CCTHREAT_PROJECTILE_EXPLOSIVE
eThreatFelt = CCTHREAT_PROJECTILE_EXPLOSIVE
ENDIF
// Timer has run out and the threat was a sticky bomb, set this ped to ignore them from now on
ELSE
IF eThreatFelt = CCTHREAT_PROJECTILE_EXPLOSIVE_STICKY_BOMB
SET_BIT_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_IgnoreStickyBombs )
ENDIF
// only reset the timer if theres no longer a projectile threat
IF eThreatFelt != CCTHREAT_PROJECTILE_EXPLOSIVE
CLEAR_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeSetStartFearProjectile )
ENDIF
ENDIF
ENDIF
// Proximity fear adjustment
IF IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeSetStartProximityThreat )
IF IS_TIME_MORE_THAN( GET_NETWORK_TIME(),
GET_TIME_OFFSET( sLocalPedData.timeStartProximityThreat, ciCC_ProximityCoolOffDuration ) )
IF eThreatFelt = CCTHREAT_PROXIMITY
eThreatFelt = CCTHREAT_NONE
ENDIF
ENDIF
ENDIF
ENDIF
// Update last time threatened
IF eThreatFelt = CCTHREAT_NONE
//AND sCrowdControlPedData.eState != CCPS_FLINCH
AND sCrowdControlPedData.eState != CCPS_BEATDOWN
IF NOT IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeLastThreatenedSet )
sLocalPedData.timeLastThreatened = GET_NETWORK_TIME()
SET_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeLastThreatenedSet )
ENDIF
ELSE
CLEAR_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeLastThreatenedSet )
ENDIF
#IF IS_DEBUG_BUILD
// Store out for debug rendering
sLocalPedData.eDebugThreatFelt = eThreatFelt
#ENDIF
// Brain
//------------------------------------------------
// Cancel look at while flinching or being beat
IF sCrowdControlPedData.eState = CCPS_FLINCH
OR sCrowdControlPedData.eState = CCPS_FLINCH_UNDERFIRE
OR sCrowdControlPedData.eState = CCPS_BEATDOWN
TASK_CLEAR_LOOK_AT( pedCrowd )
// Look at potential threat regardless of wheather they are threatening at the time
ELIF sCrowdControlPedData.eState != CCPS_INTRO
IF NOT IS_SCRIPT_TASK_RUNNING_OR_STARTING( pedCrowd, SCRIPT_TASK_LOOK_AT_ENTITY )
TASK_LOOK_AT_ENTITY( pedCrowd, sLocalPedData.pedBiggestThreat, -1, SLF_WHILE_NOT_IN_FOV | SLF_NARROWEST_YAW_LIMIT )
ENDIF
ENDIF
BOOL bSafeToTransition
// Start beating
IF sCrowdControlPedData.eState != CCPS_BEATDOWN
IF IS_BIT_SET_ENUM( sLocalPedData.iBitset, CCLOCALPEDDATA_BeatdownFired )
// Set the number of hits this ped can take this round
sCrowdControlPedData.iBeatdownHitsRemaining = CLAMP_INT( ciCC_MaxBeatdownHits - sCrowdControlPedData.iBeatdownRounds, 1, ciCC_MaxBeatdownHits )
sCrowdControlPedData.iBeatdownRounds++
// Take 1 off for the first hit
sCrowdControlPedData.iBeatdownHitsRemaining--
BROADCAST_FMMC_CC_PED_BEATDOWN_CLEAR( iCrowdStructID )
CLEAR_BIT_ENUM( sLocalPedData.iBitset, CCLOCALPEDDATA_BeatdownFired )
SET_CROWD_CONTROL_PED_STATE( sCrowdControlPedData, CCPS_BEATDOWN, iCrowdStructID, sLocalPedData.eCreatorAssignedPedID )
ENDIF
// Already in beating
ELSE
IF IS_BIT_SET_ENUM( sLocalPedData.iBitset, CCLOCALPEDDATA_BeatdownFired )
#IF IS_DEBUG_BUILD
IF NOT IS_BIT_SET_ENUM( sCrowdControlData.iBitSet, CCDATA_DebugUnlimitedBeatings )
#ENDIF
// Beatdown loop -> Beatdown hit a/b -> Beatdown loop
IF sCrowdControlPedData.iBeatdownHitsRemaining > 0
sCrowdControlPedData.iBeatdownHitsRemaining--
// Beatdown loop -> Beatdown die -> Beatdown die loop
ELSE
SET_BIT_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_KnockedOut )
sLocalPedData.timeDied = GET_NETWORK_TIME()
sCrowdControlPedData.iBeatdownHitsRemaining = 0
ENDIF
#IF IS_DEBUG_BUILD
ENDIF
#ENDIF
BROADCAST_FMMC_CC_PED_BEATDOWN_CLEAR( iCrowdStructID )
CLEAR_BIT_ENUM( sLocalPedData.iBitset, CCLOCALPEDDATA_BeatdownFired )
// Playing the loop, wait until the threat has finished then start the timer
ELIF eThreatFelt = CCTHREAT_NONE
AND IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_BEATDOWN_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
IF NOT IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeLastBeatSet )
sLocalPedData.timeLastBeat = GET_NETWORK_TIME()
SET_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeLastBeatSet )
ENDIF
IF IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeLastBeatSet )
AND IS_TIME_MORE_THAN( GET_NETWORK_TIME(), GET_TIME_OFFSET( sLocalPedData.timeLastBeat, 3000 ) )
//sCrowdControlPedData.eState = CCPS_FLINCH
SET_CROWD_CONTROL_PED_STATE( sCrowdControlPedData, CCPS_FLINCH, iCrowdStructID, sLocalPedData.eCreatorAssignedPedID )
ENDIF
ELSE
CLEAR_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeLastBeatSet )
ENDIF
ENDIF
SWITCH sCrowdControlPedData.eState
CASE CCPS_INTRO
// Crowd has been activated
IF IS_BIT_SET_ENUM( sCrowdControlData.iBitSet, CCDATA_Active )
// this ped has been threatened
OR eThreatActual != CCTHREAT_NONE
// this ped has been attacked
OR HAS_ENTITY_BEEN_DAMAGED_BY_ANY_PED( pedCrowd )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_PED_AI() - [", iCrowdStructID,"] CCPS_INTRO going to CCPS_COWER - CCDATA_Active: ", BOOL_TO_STRING( IS_BIT_SET_ENUM( sCrowdControlData.iBitSet, CCDATA_Active ) ), " WasThreatened?: ", BOOL_TO_STRING( eThreatActual != CCTHREAT_NONE ) )
CPRINTLN( DEBUG_NET_MINIGAME, " - Threat felt = ", GET_CROWD_CONTROL_THREAT_TYPE_STRING( eThreatActual ), "\n" )
// Set a random start delay for broadcasting dialogue, this is to help avoid synchronised screaming and dialogue
// by stopping all the dialogue kicking off in the same frame.
sLocalPedData.timeDialogueLastBroadcast = GET_TIME_OFFSET( GET_NETWORK_TIME(), ( GET_RANDOM_INT_IN_RANGE( 0, 11 ) * 50 ) ) // 0 -> 0.5 seconds (50ms intervals)
//sCrowdControlPedData.eState = CCPS_COWER
SET_CROWD_CONTROL_PED_STATE( sCrowdControlPedData, CCPS_COWER, iCrowdStructID, sLocalPedData.eCreatorAssignedPedID )
ENDIF
BREAK
CASE CCPS_COWER
IF IS_BIT_SET_ENUM( sCrowdControlPedData.iBitset, CCPEDDATA_RunningBespokeAnims )
//IF HAS_CROWD_CONTROL_PED_FINISHED_ANIM( pedCrowd, CCPANIM_INTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType, DEFAULT, TRUE, sCrowdControlData.iSyncScene, HASH( "Event" ) )
IF IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_COWER_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
OR IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_FLINCH_TO_IDLE_OUTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
bSafeToTransition = TRUE
ENDIF
ELSE
bSafeToTransition = TRUE
ENDIF
IF bSafeToTransition
// Use a timer to make sure the player has aimed at a ped and not just moved their aim past them
IF eThreatActual = CCTHREAT_AIMED_AT
IF NOT IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeSetStartThreaten )
// 250ms + random 0-250ms increments of 50ms
sLocalPedData.timeStartThreaten = GET_TIME_OFFSET( GET_NETWORK_TIME(), 250 + ( GET_RANDOM_INT_IN_RANGE(0, 6 ) * 50 ) )
SET_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeSetStartThreaten )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_PED_AI() - [", iCrowdStructID,"] Escape/Hero attempt threat react timer started" )
ENDIF
ELSE
CLEAR_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeSetStartThreaten )
ENDIF
// Every threat is instant apart from aiming at, this is so the player can't just spin or weave there aim over peds quickly to keep their fear up
IF eThreatFelt != CCTHREAT_NONE
IF ( eThreatFelt != CCTHREAT_AIMED_AT
OR ( eThreatFelt = CCTHREAT_AIMED_AT
AND IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeSetStartThreaten )
AND IS_TIME_MORE_THAN( GET_NETWORK_TIME(), sLocalPedData.timeStartThreaten ) ) )
SWITCH eThreatFelt
CASE CCTHREAT_SHOOTING_CLOSE
QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( sLocalData, sLocalPedData, CCDIA_SHOT_DIRECTLY_AT, CCDIAPLAYBACK_FORCE_PAIN | CCDIAPLAYBACK_FORCE_CRITICAL )
BREAK
CASE CCTHREAT_SHOOTING
QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( sLocalData, sLocalPedData, CCDIA_SHOT_NEAR, CCDIAPLAYBACK_FORCE_PAIN | CCDIAPLAYBACK_FORCE_CRITICAL )
BREAK
CASE CCTHREAT_AIMED_AT
CASE CCTHREAT_PROJECTILE_EXPLOSIVE
CASE CCTHREAT_PROJECTILE_EXPLOSIVE_STICKY_BOMB
QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( sLocalData, sLocalPedData, CCDIA_RECEIVES_SCARE, CCDIAPLAYBACK_FORCE_PAIN | CCDIAPLAYBACK_FORCE_CRITICAL )
BREAK
DEFAULT
QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( sLocalData, sLocalPedData, CCDIA_APPROACH, CCDIAPLAYBACK_FORCE_PAIN | CCDIAPLAYBACK_FORCE_CRITICAL )
BREAK
ENDSWITCH
IF eThreatFelt = CCTHREAT_SHOOTING_CLOSE
SET_CROWD_CONTROL_PED_STATE( sCrowdControlPedData, CCPS_FLINCH_UNDERFIRE, iCrowdStructID, sLocalPedData.eCreatorAssignedPedID )
CLEAR_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeSetShootingCloseFlinching )
ELSE
SET_CROWD_CONTROL_PED_STATE( sCrowdControlPedData, CCPS_FLINCH, iCrowdStructID, sLocalPedData.eCreatorAssignedPedID )
ENDIF
CLEAR_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeSetStartThreaten )
ENDIF
// Fear level dropped, attempt to escape
ELIF sLocalData.fIntimidationBar < cfCC_PedBreakoutCowerThreshold
AND eThreatActual = CCTHREAT_NONE
AND IS_BIT_SET_ENUM( sCrowdControlPedData.iBitset, CCPEDDATA_RunningBespokeAnims ) // can only transition to this if ped is playing anims
// Is a valid hero & the minigame's not failed
IF eHeroAction != CCHERO_NONE AND IS_BIT_SET( sCrowdControlData.iCurrentActiveHeros, iCrowdStructID )
AND IS_CROWD_CONTROL_PED_A_VALID_HERO( sCrowdControlPedData, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType, bRestrictCrowdPed )
AND NOT IS_BIT_SET_ENUM( sCrowdControlData.iBitset, CCDATA_Failed )
SWITCH eHeroAction
CASE CCHERO_ALARM
QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( sLocalData, sLocalPedData, CCDIA_HERO_ALARM_START )
BREAK
CASE CCHERO_PHONE
QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( sLocalData, sLocalPedData, CCDIA_HERO_PHONE_START )
BREAK
CASE CCHERO_GUN
QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( sLocalData, sLocalPedData, CCDIA_HERO_GUN_START )
BREAK
ENDSWITCH
CPRINTLN(DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_PED_AI() - [", iCrowdStructID,"] CCPS_COWER going to CCPS_HERO_ATTEMPT")
SET_BIT_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_HeroAttempted )
//sCrowdControlPedData.eState = CCPS_HERO_ATTEMPT
SET_CROWD_CONTROL_PED_STATE( sCrowdControlPedData, CCPS_HERO_ATTEMPT, iCrowdStructID, sLocalPedData.eCreatorAssignedPedID )
ENDIF
ENDIF
ENDIF
BREAK
CASE CCPS_FLINCH
// Is it safe to transition out of this state?
IF IS_BIT_SET_ENUM( sCrowdControlPedData.iBitset, CCPEDDATA_RunningBespokeAnims )
IF IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_FLINCH_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
bSafeToTransition = TRUE
ENDIF
ELSE
bSafeToTransition = TRUE
ENDIF
IF eThreatFelt = CCTHREAT_SHOOTING_CLOSE
QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( sLocalData, sLocalPedData, CCDIA_SHOT_DIRECTLY_AT, CCDIAPLAYBACK_FORCE_PAIN | CCDIAPLAYBACK_FORCE_CRITICAL )
SET_CROWD_CONTROL_PED_STATE( sCrowdControlPedData, CCPS_FLINCH_UNDERFIRE, iCrowdStructID, sLocalPedData.eCreatorAssignedPedID )
CLEAR_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeSetShootingCloseFlinching )
// No threat, go back to regular cower
ELIF bSafeToTransition
IF eThreatFelt = CCTHREAT_NONE
// 2121009 - removed as part of this bug in order to get a more immediate response.
// UNDONE now we have new anims to go in
AND ( NOT IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeLastThreatenedSet )
OR IS_TIME_MORE_THAN( GET_NETWORK_TIME(), GET_TIME_OFFSET( sLocalPedData.timeLastThreatened, 5000 ) ) )
AND ( NOT IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeLastShotAtSet )
OR IS_TIME_MORE_THAN( GET_NETWORK_TIME(), GET_TIME_OFFSET( sLocalPedData.timeLastShotAt, 8000 ) ) )
SET_CROWD_CONTROL_PED_STATE( sCrowdControlPedData, CCPS_COWER, iCrowdStructID, sLocalPedData.eCreatorAssignedPedID )
ENDIF
ENDIF
BREAK
CASE CCPS_FLINCH_UNDERFIRE
// Is it safe to transition out of this state?
IF IS_BIT_SET_ENUM( sCrowdControlPedData.iBitset, CCPEDDATA_RunningBespokeAnims )
IF IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_FLINCH_UNDERFIRE_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
IF NOT IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeSetShootingCloseFlinching )
sLocalPedData.timeShootingCloseFlinchingStarted = GET_NETWORK_TIME()
SET_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeSetShootingCloseFlinching )
ENDIF
// Minimum 1 second of frantic flinching
IF IS_TIME_MORE_THAN( GET_NETWORK_TIME(), GET_TIME_OFFSET( sLocalPedData.timeShootingCloseFlinchingStarted, 1000 ) )
bSafeToTransition = TRUE
ENDIF
ENDIF
ELSE
bSafeToTransition = TRUE
ENDIF
IF bSafeToTransition
IF eThreatFelt != CCTHREAT_SHOOTING_CLOSE
SET_CROWD_CONTROL_PED_STATE( sCrowdControlPedData, CCPS_FLINCH, iCrowdStructID, sLocalPedData.eCreatorAssignedPedID )
CLEAR_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_TimeSetShootingCloseFlinching )
ENDIF
ENDIF
BREAK
CASE CCPS_BEATDOWN
// see top of switch for this logic
BREAK
CASE CCPS_HERO_ATTEMPT
// Any threat to the ped, instant response
IF eThreatFelt != CCTHREAT_NONE
// Hero prop damaged
OR IS_BIT_SET_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_HeroBroken )
CLEAR_BIT_ENUM( sCrowdControlPedData.iBitSet, CCLOCALPEDDATA_TimeSetHoldBeforeEscape )
SWITCH eHeroAction
CASE CCHERO_ALARM
QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( sLocalData, sLocalPedData, CCDIA_HERO_ALARM_CAUGHT )
BREAK
CASE CCHERO_PHONE
QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( sLocalData, sLocalPedData, CCDIA_HERO_PHONE_CAUGHT )
BREAK
CASE CCHERO_GUN
QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( sLocalData, sLocalPedData, CCDIA_HERO_GUN_CAUGHT )
BREAK
ENDSWITCH
// Ped has become too scared to finish the escape, return to cower
CPRINTLN(DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_PED_AI() - [", iCrowdStructID,"] CCPS_HERO_ATTEMPT going to CCPS_COWER")
//sCrowdControlPedData.eState = CCPS_COWER
SET_CROWD_CONTROL_PED_STATE( sCrowdControlPedData, CCPS_COWER, iCrowdStructID, sLocalPedData.eCreatorAssignedPedID )
ELSE
BOOL bReadyToTransition
// Animated version can only transition once it reaches the Escape/Hero loop
IF IS_BIT_SET_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_RunningBespokeAnims )
IF IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_HERO_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
bReadyToTransition = TRUE
ENDIF
// Non animated version can go straight away (may add additional conditions, i.e. a timer)
ELSE
bReadyToTransition = TRUE
ENDIF
IF bReadyToTransition
// Go to combat
IF eHeroAction = CCHERO_GUN
QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( sLocalData, sLocalPedData, CCDIA_HERO_GUN_COMPLETE )
CPRINTLN(DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_PED_AI() - [", iCrowdStructID,"] CCPS_HERO_ATTEMPT going to CCPS_COMBAT")
SET_PED_CAN_BE_TARGETTED( pedCrowd, TRUE ) // 2048337
// Rob - 2167482 - make sure all teams can target the hero combat ped
INT iRepeat
FOR iRepeat = 0 TO (FMMC_MAX_TEAMS-1)
SET_PED_CAN_BE_TARGETTED_BY_TEAM(pedCrowd, iRepeat, TRUE)
ENDFOR
//sCrowdControlPedData.eState = CCPS_COMBAT
SET_CROWD_CONTROL_PED_STATE( sCrowdControlPedData, CCPS_COMBAT, iCrowdStructID, sLocalPedData.eCreatorAssignedPedID )
// go to special hero state
ELIF eHeroAction = CCHERO_ALARM
OR eHeroAction = CCHERO_PHONE
SWITCH eHeroAction
CASE CCHERO_ALARM
QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( sLocalData, sLocalPedData, CCDIA_HERO_ALARM_COMPLETE )
BREAK
CASE CCHERO_PHONE
QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( sLocalData, sLocalPedData, CCDIA_HERO_PHONE_COMPLETE )
BREAK
ENDSWITCH
CPRINTLN(DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_PED_AI() - [", iCrowdStructID,"] CCPS_HERO_ATTEMPT going to CCPS_HERO")
SET_CROWD_CONTROL_PED_STATE( sCrowdControlPedData, CCPS_HERO, iCrowdStructID, sLocalPedData.eCreatorAssignedPedID )
ENDIF
ENDIF
ENDIF
BREAK
// For successful heros with a non violent action (phone call or button press)
CASE CCPS_HERO
IF IS_BIT_SET_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_RunningBespokeAnims )
BOOL bReadyToTransition
IF eHeroAction = CCHERO_ALARM
// Alarm guy signals a hero complete fail immediately
// and then waits to move on until the fail has been processed
SET_BIT_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_HeroComplete )
IF IS_BIT_SET_ENUM( sCrowdControlData.iBitSet, CCDATA_FailProcessed )
QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( sLocalData, sLocalPedData, CCDIA_ALARM_GOES_OFF )
bReadyToTransition = TRUE
ENDIF
ELSE
IF IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_COWER_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
SET_BIT_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_HeroComplete )
bReadyToTransition = TRUE
ENDIF
ENDIF
IF bReadyToTransition
CPRINTLN(DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_PED_AI() - [", iCrowdStructID,"] CCPS_HERO going to CCPS_COWER")
SET_CROWD_CONTROL_PED_STATE( sCrowdControlPedData, CCPS_COWER, iCrowdStructID, sLocalPedData.eCreatorAssignedPedID )
ENDIF
ENDIF
BREAK
CASE CCPS_COMBAT
BREAK
ENDSWITCH
// Body - Animated state logic ONLY
//------------------------------------------------
IF IS_BIT_SET_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_RunningBespokeAnims )
AND IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_RecievedSyncedScene )
SWITCH sCrowdControlPedData.eState
CASE CCPS_COWER
// Hero start/loop -> Hero fail -> Cower loop
IF sCrowdControlPedData.eStatePrev = CCPS_HERO_ATTEMPT
AND ( ( IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_HERO_START, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
AND HAS_CROWD_CONTROL_PED_FINISHED_ANIM( pedCrowd, CCPANIM_HERO_START, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType, 0.5 ) )
OR IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_HERO_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType ) )
IF HAS_CROWD_CONTROL_PED_ANIM_LOADED( CCPANIM_HERO_FAIL, sLocalData.eHeistType, sLocalPedData.eCreatorAssignedPedID )
PLAY_CROWD_CONTROL_SEQUENCE( pedCrowd, CCSEQ_HERO_FAIL, iCrowdStructID, sLocalPedData )
ENDIF
// Flinch loop -> Flinch outro -> Cower loop
ELIF sCrowdControlPedData.eStatePrev = CCPS_FLINCH
AND IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_FLINCH_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
IF HAS_CROWD_CONTROL_PED_ANIM_LOADED( CCPANIM_FLINCH_TO_IDLE_OUTRO, sLocalData.eHeistType, sLocalPedData.eCreatorAssignedPedID )
PLAY_CROWD_CONTROL_SEQUENCE( pedCrowd, CCSEQ_FLINCH_TO_COWER, iCrowdStructID, sLocalPedData )
ENDIF
// X -> Cower loop
ELIF NOT IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_COWER_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
// Make sure we don't interupt any flinching or transitions back into the cower
// Has finished intro
IF ( sCrowdControlPedData.eStatePrev = CCPS_INTRO
AND HAS_CROWD_CONTROL_PED_FINISHED_ANIM( pedCrowd, CCPANIM_INTRO_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType, DEFAULT, TRUE, sLocalData.iSyncSceneIntroLoop )
AND HAS_CROWD_CONTROL_PED_FINISHED_ANIM( pedCrowd, CCPANIM_INTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType, DEFAULT, TRUE, sCrowdControlData.iSyncScene, HASH( "blendout" ) ) )
// Has finished hero attempt fail
OR ( sCrowdControlPedData.eStatePrev = CCPS_HERO_ATTEMPT
AND ( NOT DOES_CROWD_CONTROL_PED_HAVE_ANIM( CCPANIM_HERO_FAIL, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
OR HAS_CROWD_CONTROL_PED_FINISHED_ANIM( pedCrowd, CCPANIM_HERO_FAIL, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType, DEFAULT ) ) )
// Has finished flinch
OR ( sCrowdControlPedData.eStatePrev = CCPS_FLINCH
AND NOT IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_COWER_TO_FLINCH_INTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
AND NOT IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_FLINCH_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
AND HAS_CROWD_CONTROL_PED_FINISHED_ANIM( pedCrowd, CCPANIM_FLINCH_TO_IDLE_OUTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType ) )
IF HAS_CROWD_CONTROL_PED_ANIM_LOADED( CCPANIM_COWER_LOOP, sLocalData.eHeistType, sLocalPedData.eCreatorAssignedPedID )
PLAY_CROWD_CONTROL_ANIM( pedCrowd, CCPANIM_COWER_LOOP,
sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType,
REALLY_SLOW_BLEND_IN, -0.1, -1,
AF_LOOPING | eCrowdControlAnimFlags )
ENDIF
ENDIF
ENDIF
BREAK
CASE CCPS_FLINCH
// Cower loop -> Flinch intro -> Flinch loop
IF ( sCrowdControlPedData.eStatePrev = CCPS_COWER
AND ( IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_COWER_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
OR IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_FLINCH_TO_IDLE_OUTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType ) )
AND NOT IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_COWER_TO_FLINCH_INTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType ) )
IF HAS_CROWD_CONTROL_PED_ANIM_LOADED( CCPANIM_COWER_TO_FLINCH_INTRO, sLocalData.eHeistType, sLocalPedData.eCreatorAssignedPedID )
PLAY_CROWD_CONTROL_SEQUENCE( pedCrowd, CCSEQ_COWER_TO_FLINCH, iCrowdStructID, sLocalPedData )
ENDIF
// Flinch underfire -> Flinch loop
ELIF ( sCrowdControlPedData.eStatePrev = CCPS_FLINCH_UNDERFIRE
AND IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_FLINCH_UNDERFIRE_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
AND NOT IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_FLINCH_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType ) )
IF HAS_CROWD_CONTROL_PED_ANIM_LOADED( CCPANIM_FLINCH_LOOP, sLocalData.eHeistType, sLocalPedData.eCreatorAssignedPedID )
PLAY_CROWD_CONTROL_SEQUENCE( pedCrowd, CCSEQ_FLINCH_UNDERFIRE_TO_FLINCH, iCrowdStructID, sLocalPedData )
// PLAY_CROWD_CONTROL_ANIM( pedCrowd,
// CCPANIM_FLINCH_LOOP,
// sLocalPedData.eCreatorAssignedPedID,
// sLocalData.eHeistType,
// REALLY_SLOW_BLEND_IN, -0.1, -1,
// AF_LOOPING | eCrowdControlAnimFlags )
ENDIF
// Beatdown loop -> Beatdown outro -> Flinch loop
ELIF ( sCrowdControlPedData.eStatePrev = CCPS_BEATDOWN
AND IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_BEATDOWN_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
AND NOT IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_BEATDOWN_OUTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType ) )
IF HAS_CROWD_CONTROL_PED_ANIM_LOADED( CCPANIM_BEATDOWN_OUTRO, sLocalData.eHeistType, sLocalPedData.eCreatorAssignedPedID )
PLAY_CROWD_CONTROL_SEQUENCE( pedCrowd, CCSEQ_BEATDOWN_TO_FLINCH, iCrowdStructID, sLocalPedData )
ENDIF
// // bugs 2114141 & 2123699
// // trying to avoid bug where peds are standing doing nothing, this will task them with the flinch anims
// // always seem to be in the flinch state but no idea how they go here, looks like a migration problem
// ELIF NOT IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_COWER_TO_FLINCH_INTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
// AND NOT IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_FLINCH_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
// AND NOT IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_BEATDOWN_OUTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
//
// IF HAS_CROWD_CONTROL_PED_ANIM_LOADED( CCPANIM_COWER_TO_FLINCH_INTRO, sLocalData.eHeistType, sLocalPedData.eCreatorAssignedPedID )
//
// CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_PED_AI() - EMERGENCY SEQUENCE TASK, PED HAS LOST ANIMATION TASK" )
// PLAY_CROWD_CONTROL_SEQUENCE( pedCrowd, CCSEQ_COWER_TO_FLINCH, iCrowdStructID, sLocalPedData )
//
// ENDIF
//
ENDIF
BREAK
CASE CCPS_FLINCH_UNDERFIRE
// Cower loop /Flinch outro -> Flinch intro -> Flinch loop shot at
IF ( sCrowdControlPedData.eStatePrev = CCPS_COWER
AND ( IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_COWER_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
OR IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_FLINCH_TO_IDLE_OUTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType ) )
AND NOT IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_COWER_TO_FLINCH_INTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
AND NOT IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_FLINCH_UNDERFIRE_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType ) )
IF HAS_CROWD_CONTROL_PED_ANIM_LOADED( CCPANIM_COWER_TO_FLINCH_INTRO, sLocalData.eHeistType, sLocalPedData.eCreatorAssignedPedID )
PLAY_CROWD_CONTROL_SEQUENCE( pedCrowd, CCSEQ_COWER_TO_FLINCH_UNDERFIRE, iCrowdStructID, sLocalPedData )
ENDIF
// Flinch loop -> Flinch underfire
ELIF ( sCrowdControlPedData.eStatePrev = CCPS_FLINCH
AND ( IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_FLINCH_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
OR IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_COWER_TO_FLINCH_INTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
OR IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_FLINCH_UNDERFIRE_TO_FLINCH_OUTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType ) )
AND NOT IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_FLINCH_TO_FLINCH_UNDERFIRE_INTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
AND NOT IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_FLINCH_UNDERFIRE_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType ) )
IF HAS_CROWD_CONTROL_PED_ANIM_LOADED( CCPANIM_FLINCH_UNDERFIRE_LOOP, sLocalData.eHeistType, sLocalPedData.eCreatorAssignedPedID )
PLAY_CROWD_CONTROL_SEQUENCE( pedCrowd, CCSEQ_FLINCH_TO_FLINCH_UNDERFIRE, iCrowdStructID, sLocalPedData )
// PLAY_CROWD_CONTROL_ANIM( pedCrowd,
// CCPANIM_FLINCH_UNDERFIRE_LOOP,
// sLocalPedData.eCreatorAssignedPedID,
// sLocalData.eHeistType,
// SLOW_BLEND_IN, -0.1, -1,
// AF_LOOPING | eCrowdControlAnimFlags )
ENDIF
ENDIF
BREAK
CASE CCPS_BEATDOWN
SET_PED_RESET_FLAG( pedCrowd, PRF_UseKinematicPhysics, TRUE )
IF NOT IS_BIT_SET_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_BeatdownSyncSceneStarted )
IF IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_BEATDOWN_INTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType, TRUE )
OR IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_BEATDOWN_HIT_A, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType, TRUE )
OR IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_BEATDOWN_HIT_B, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType, TRUE )
OR IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_BEATDOWN_DIE, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType, TRUE )
SET_BIT_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_BeatdownSyncSceneStarted )
ENDIF
ELSE
IF NOT IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_BEATDOWN_INTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType, TRUE )
AND NOT IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_BEATDOWN_HIT_A, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType, TRUE )
AND NOT IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_BEATDOWN_HIT_B, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType, TRUE )
AND NOT IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_BEATDOWN_DIE, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType, TRUE )
AND NOT IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_BEATDOWN_DIE_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType, FALSE )
AND NOT IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_BEATDOWN_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType, FALSE )
IF IS_BIT_SET_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_KnockedOut )
PLAY_CROWD_CONTROL_ANIM( pedCrowd, CCPANIM_BEATDOWN_DIE_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType,
REALLY_SLOW_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_ENDS_IN_DEAD_POSE | eCrowdControlAnimFlags )
ELSE
PLAY_CROWD_CONTROL_ANIM( pedCrowd, CCPANIM_BEATDOWN_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType,
REALLY_SLOW_BLEND_IN, NORMAL_BLEND_OUT, -1, AF_LOOPING | eCrowdControlAnimFlags )
ENDIF
CLEAR_BIT_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_BeatdownSyncSceneStarted )
ENDIF
ENDIF
BREAK
CASE CCPS_HERO_ATTEMPT
// Cower loop -> Hero start -> Hero loop
IF ( sCrowdControlPedData.eStatePrev = CCPS_COWER
AND IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_COWER_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType ) // playing cower?
AND NOT IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_HERO_START, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType ) ) // not playing escape start?
IF HAS_CROWD_CONTROL_PED_ANIM_LOADED( CCPANIM_HERO_START, sLocalData.eHeistType, sLocalPedData.eCreatorAssignedPedID )
PLAY_CROWD_CONTROL_SEQUENCE( pedCrowd, CCSEQ_HERO_START, iCrowdStructID, sLocalPedData )
ENDIF
ENDIF
BREAK
CASE CCPS_HERO
// Hero loop -> Hero pass -> Cower
IF sCrowdControlPedData.eStatePrev = CCPS_HERO_ATTEMPT
AND IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_HERO_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType ) // playing hero start?
AND NOT IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_HERO_PASS, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType ) // not playing hero pass?
IF eHeroAction = CCHERO_ALARM
IF HAS_CROWD_CONTROL_PED_ANIM_LOADED( CCPANIM_HERO_FAIL, sLocalData.eHeistType, sLocalPedData.eCreatorAssignedPedID )
PLAY_CROWD_CONTROL_SEQUENCE( pedCrowd, CCSEQ_HERO_FAIL, iCrowdStructID, sLocalPedData )
ENDIF
ELSE
IF HAS_CROWD_CONTROL_PED_ANIM_LOADED( CCPANIM_HERO_PASS, sLocalData.eHeistType, sLocalPedData.eCreatorAssignedPedID )
PLAY_CROWD_CONTROL_SEQUENCE( pedCrowd, CCSEQ_HERO_PASS, iCrowdStructID, sLocalPedData )
ENDIF
ENDIF
ENDIF
BREAK
CASE CCPS_COMBAT
// Hero loop -> Hero Pass
IF IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_HERO_LOOP, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType ) // playing escape loop?
AND NOT IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_HERO_PASS, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType ) // not playing escape pass?
IF HAS_CROWD_CONTROL_PED_ANIM_LOADED( CCPANIM_HERO_PASS, sLocalData.eHeistType, sLocalPedData.eCreatorAssignedPedID )
WEAPON_TYPE eWeap
GET_CURRENT_PED_WEAPON( pedCrowd, eWeap )
IF eWeap != WEAPONTYPE_PISTOL
SET_CURRENT_PED_WEAPON( pedCrowd, WEAPONTYPE_PISTOL, TRUE )
ENDIF
PLAY_CROWD_CONTROL_ANIM( pedCrowd, CCPANIM_HERO_PASS,
sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType,
REALLY_SLOW_BLEND_IN, -0.1, -1,
AF_HOLD_LAST_FRAME | eCrowdControlAnimFlags )
SET_PED_USING_ACTION_MODE( pedCrowd, TRUE, -1, "DEFAULT_ACTION" )
ENDIF
// Hero pass -> AI combat
ELIF IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_HERO_PASS, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType ) // playing escape pass?
AND HAS_CROWD_CONTROL_PED_FINISHED_ANIM( pedCrowd, CCPANIM_HERO_PASS, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType ) // finished escape pass?
// TRANSFER TO AI COMBAT
//SET_PED_RESET_FLAG( pedCrowd, PRF_InstantBlendToAim, TRUE )
CLEAR_BIT_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_RunningBespokeAnims )
SET_BIT_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_HeroComplete )
ENDIF
BREAK
ENDSWITCH
ENDIF
// Body - AI driven logic ONLY
//------------------------------------------------
IF NOT IS_BIT_SET_ENUM( sCrowdControlPedData.iBitSet, CCPEDDATA_RunningBespokeAnims )
// Run AI state machine
SWITCH sCrowdControlPedData.eState
// Idle, unalert to player's prescence
CASE CCPS_INTRO
BREAK
CASE CCPS_COWER
CASE CCPS_FLINCH
IF NOT IS_SCRIPT_TASK_RUNNING_OR_STARTING( pedCrowd, SCRIPT_TASK_HANDS_UP )
AND NOT IS_SCRIPT_TASK_RUNNING_OR_STARTING( pedCrowd, SCRIPT_TASK_COWER )
SET_PED_COMBAT_ATTRIBUTES( pedCrowd, CA_ALWAYS_FLEE, FALSE )
SET_PED_FLEE_ATTRIBUTES( pedCrowd, FA_DISABLE_COWER, TRUE )
SET_PED_FLEE_ATTRIBUTES( pedCrowd, FA_DISABLE_FLEE_FROM_INDIRECT_THREATS, TRUE )
SET_PED_FLEE_ATTRIBUTES( pedCrowd, FA_COWER_INSTEAD_OF_FLEE, TRUE )
TASK_HANDS_UP( pedCrowd,-1, sLocalPedData.pedBiggestThreat, -1, HANDS_UP_NOTHING )
sLocalPedData.timeLastThreatened = GET_NETWORK_TIME()
ELIF IS_SCRIPT_TASK_RUNNING_OR_STARTING( pedCrowd, SCRIPT_TASK_HANDS_UP )
AND NOT IS_SCRIPT_TASK_RUNNING_OR_STARTING( pedCrowd, SCRIPT_TASK_COWER )
AND eThreatFelt = CCTHREAT_NONE
AND IS_TIME_MORE_THAN( GET_NETWORK_TIME(), GET_TIME_OFFSET( sLocalPedData.timeLastThreatened, 5000 ) )
TASK_COWER( pedCrowd, -1 )
ENDIF
BREAK
CASE CCPS_COMBAT
WEAPON_TYPE eWeap
GET_CURRENT_PED_WEAPON( pedCrowd, eWeap )
IF eWeap != WEAPONTYPE_PISTOL
SET_CURRENT_PED_WEAPON( pedCrowd, WEAPONTYPE_PISTOL, FALSE )
ENDIF
IF NOT IS_SCRIPT_TASK_RUNNING_OR_STARTING( pedCrowd, SCRIPT_TASK_COMBAT )
SET_PED_COMBAT_ATTRIBUTES(pedCrowd, CA_ALWAYS_FLEE, FALSE)
TASK_COMBAT_PED( pedCrowd, sLocalPedData.pedBiggestThreat )
ENDIF
BREAK
ENDSWITCH
ENDIF
// Other dialogue which plays based on threats and/or state
//-----------------------------------------------------------------------
IF IS_BIT_SET_ENUM( sCrowdControlData.iBitSet, CCDATA_Active )
AND NOT IS_PED_IN_COMBAT( pedCrowd )
AND sCrowdControlPedData.eState != CCPS_COMBAT
AND sCrowdControlPedData.eState != CCPS_HERO_ATTEMPT
AND sCrowdControlPedData.eState != CCPS_HERO
BOOL bPassedInitialScreaming = IS_TIME_MORE_THAN( GET_NETWORK_TIME(), GET_TIME_OFFSET( sLocalData.timeCrowdKickedOff, 4000 ) )
// THREAT BASED
IF eThreatFelt != CCTHREAT_NONE
AND bPassedInitialScreaming
SWITCH eThreatFelt
CASE CCTHREAT_PROXIMITY
QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( sLocalData, sLocalPedData, CCDIA_COWER_HIGH_FEAR )
BREAK
CASE CCTHREAT_BEATING_NEIGHBOUR
QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( sLocalData, sLocalPedData, CCDIA_STICKS_UP_FOR_OTHER )
BREAK
CASE CCTHREAT_KILLED_OTHER
QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( sLocalData, sLocalPedData, CCDIA_SEES_OTHER_KILLED )
BREAK
CASE CCTHREAT_AIMED_AT
CASE CCTHREAT_PROJECTILE_EXPLOSIVE
CASE CCTHREAT_PROJECTILE_EXPLOSIVE_STICKY_BOMB
QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( sLocalData, sLocalPedData, CCDIA_RECEIVES_SCARE )
BREAK
CASE CCTHREAT_SHOOTING
QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( sLocalData, sLocalPedData, CCDIA_SHOT_NEAR )
BREAK
CASE CCTHREAT_SHOOTING_CLOSE
QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( sLocalData, sLocalPedData, CCDIA_SHOT_DIRECTLY_AT )
BREAK
ENDSWITCH
ENDIF
// STATE BASED
SWITCH sCrowdControlPedData.eState
// While
CASE CCPS_COWER
IF IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_INTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType, TRUE )
// Initially only allow the peds crowd to scream
IF NOT bPassedInitialScreaming
QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( sLocalData, sLocalPedData, CCDIA_INITIAL_PANIC, CCDIAPLAYBACK_FORCE_PAIN )
ELSE
QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( sLocalData, sLocalPedData, CCDIA_INITIAL_PANIC )
ENDIF
ENDIF
BREAK
ENDSWITCH
// AMBIENT
BOOL bCanPlayLowFearAmbientDialogue = IS_TIME_MORE_THAN( GET_NETWORK_TIME(), GET_TIME_OFFSET( sLocalData.timeCrowdKickedOff, 20000 ) )
CROWD_CONTROL_DIALOGUE eAmbientRandomDialogue
// Alarm has just gone off
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_RaiseAlarms )
AND NOT IS_BIT_SET_ENUM( sCrowdControlData.iBitSet, CCDATA_RaiseAlarmDiagloue )
AND NOT IS_CUTSCENE_PLAYING() // dnt try and fire off this line behind a cutscene as it might get missed
eAmbientRandomDialogue = CCDIA_ALARM_GOES_OFF_INITIAL
// Otherwise select a line based off fear level
ELSE
// Fear low
IF sLocalData.fIntimidationBar < 40.0
AND sCrowdControlPedData.eState != CCPS_FLINCH
AND sCrowdControlPedData.eState != CCPS_BEATDOWN
AND bCanPlayLowFearAmbientDialogue
// Once the alarm has gone off theres a random chance of hearing alarm/cops are coming related dialogue
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_RaiseAlarms )
AND GET_RANDOM_BOOL()
eAmbientRandomDialogue = CCDIA_ALARM_GOES_OFF
ELSE
eAmbientRandomDialogue = CCDIA_COWER_LOW_FEAR
ENDIF
// Fear high
ELSE
eAmbientRandomDialogue = CCDIA_COWER_HIGH_FEAR
ENDIF
ENDIF
// Play ambient context
QUEUE_CROWD_CONTROL_DIALOGUE_CLIENT_TO_HOST( sLocalData, sLocalPedData, eAmbientRandomDialogue )
ENDIF
ENDIF
ENDIF
ENDPROC
PROC PROCESS_CROWD_CONTROL_PED_LOCAL( PED_INDEX pedCrowd, INT iCrowdID,
CROWD_CONTROL_PED_DECORATOR_DATA &sPedData,
CROWD_CONTROL_LOCAL_PED_DATA &sLocalPedData,
CROWD_CONTROL_SERVER_DATA &sServerData,
CROWD_CONTROL_LOCAL_DATA &sLocalData )
#IF NOT IS_DEBUG_BUILD
UNUSED_PARAMETER( iCrowdID )
#ENDIF
CROWD_CONTROL_PED_HERO_ACTION eHeroAction = CCHERO_NONE
IF DOES_CROWD_CONTROL_PED_HAVE_ANIM( CCPANIM_HERO_START, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
eHeroAction = GET_CROWD_CONTROL_PED_HERO_ACTION_FOR_ANIMATED_PED( sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
ENDIF
IF IS_BIT_SET_ENUM( sPedData.iBitSet, CCPEDDATA_RunningBespokeAnims )
SWITCH eHeroAction
CASE CCHERO_PHONE
SWITCH sPedData.eState
CASE CCPS_COWER
// Detach the phone
IF DOES_ENTITY_EXIST( sLocalPedData.objProp )
IF IS_ENTITY_ATTACHED( sLocalPedData.objProp )
IF sServerData.iSyncScene != -1
AND IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_INTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType, TRUE )
AND HAS_CROWD_CONTROL_PED_FINISHED_ANIM( pedCrowd, CCPANIM_INTRO, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType, DEFAULT, TRUE, sServerData.iSyncScene, HASH( "drop" ) )
CPRINTLN(DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_PED_LOCAL() - [", iCrowdID,"] Detatched phone for CCPANIM_INTRO")
DETACH_ENTITY( sLocalPedData.objProp )
FREEZE_ENTITY_POSITION( sLocalPedData.objProp, TRUE )
ENDIF
ENDIF
ENDIF
BREAK
CASE CCPS_HERO
IF DOES_ENTITY_EXIST( sLocalPedData.objProp )
// Attach the phone, as they start playing the hero anim
IF NOT IS_ENTITY_ATTACHED( sLocalPedData.objProp )
IF NOT IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_HeroPropDetatchedForLastTime )
IF IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_HERO_PASS, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
CPRINTLN(DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_PED_LOCAL() - [", iCrowdID,"] Attached phone for CCPANIM_HERO_PASS")
FREEZE_ENTITY_POSITION( sLocalPedData.objProp, FALSE )
ATTACH_ENTITY_TO_ENTITY( sLocalPedData.objProp, pedCrowd, GET_PED_BONE_INDEX( pedCrowd, BONETAG_PH_R_HAND ), <<0,0,0>>, <<0,0,0>>, TRUE, FALSE )
ENDIF
ENDIF
ELSE
// Detach phone during hero pass
IF IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_HERO_PASS, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType )
AND HAS_CROWD_CONTROL_PED_FINISHED_ANIM( pedCrowd, CCPANIM_HERO_PASS, sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType, 0.9 )
// During the HERO PASS anim
CPRINTLN(DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_PED_LOCAL() - [", iCrowdID,"] Detatched phone for CCPANIM_HERO_PASS")
DETACH_ENTITY( sLocalPedData.objProp )
FREEZE_ENTITY_POSITION( sLocalPedData.objProp, TRUE )
SET_BIT_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_HeroPropDetatchedForLastTime )
ENDIF
ENDIF
ENDIF
BREAK
ENDSWITCH
BREAK
ENDSWITCH
ENDIF
ENDPROC
FUNC BOOL IS_WEAPON_SUITABLE_FOR_CROWD_CONTROL_BEATDOWN_1H( WEAPON_TYPE eWeapType, BOOL bIncludeHit = FALSE )
IF IS_WEAPON_A_PISTOL( eWeapType )
OR eWeapType = WEAPONTYPE_STUNGUN
OR eWeapType = WEAPONTYPE_MICROSMG
OR eWeapType = WEAPONTYPE_DLC_FLAREGUN
// 2183699
OR eWeapType = WEAPONTYPE_KNIFE
OR eWeapType = WEAPONTYPE_DLC_BOTTLE
OR eWeapType = WEAPONTYPE_DLC_DAGGER
RETURN TRUE
// kick only - 1H weapons that we want to only allow kicking
// 2183699
ELIF eWeapType = WEAPONTYPE_CROWBAR
OR eWeapType = WEAPONTYPE_UNARMED
OR eWeapType = WEAPONTYPE_HAMMER
OR eWeapType = WEAPONTYPE_NIGHTSTICK
OR eWeapType = WEAPONTYPE_DLC_KNUCKLE
OR eWeapType = WEAPONTYPE_DLC_HATCHET
IF NOT bIncludeHit
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL IS_WEAPON_SUITABLE_FOR_CROWD_CONTROL_BEATDOWN_2H( WEAPON_TYPE eWeapType, BOOL bIncludeHit = FALSE )
IF IS_WEAPON_SUITABLE_FOR_TWO_HANDED_ANIMS( eWeapType )
OR eWeapType = WEAPONTYPE_SMG
OR eWeapType = WEAPONTYPE_MG
OR eWeapType = WEAPONTYPE_COMBATMG
OR eWeapType = WEAPONTYPE_GRENADELAUNCHER
OR eWeapType = WEAPONTYPE_DLC_ASSAULTMG
OR eWeapType = WEAPONTYPE_DLC_ASSAULTSMG
RETURN TRUE
// kick only
ELIF eWeapType = WEAPONTYPE_RPG
OR eWeapType = WEAPONTYPE_DLC_FIREWORK
IF NOT bIncludeHit
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL IS_CROWD_CONTROL_PLAYER_MELEE_ANIM_A_KICK( CROWD_CONTROL_PLAYER_ANIM_TYPE eAnim )
IF eAnim = CCPLAYERANIM_BEATDOWN_PISTOL_KICK_A
OR eAnim = CCPLAYERANIM_BEATDOWN_PISTOL_KICK_B
OR eAnim = CCPLAYERANIM_BEATDOWN_RIFLE_KICK_A
OR eAnim = CCPLAYERANIM_BEATDOWN_RIFLE_KICK_B
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL IS_CROWD_CONTROL_PLAYER_PERFORMING_A_BEATDOWN( PED_INDEX pedPlayer )
IF IS_CROWD_CONTROL_PLAYER_PLAYING_ANIM( pedPlayer, CCPLAYERANIM_BEATDOWN_PISTOL_A, TRUE )
OR IS_CROWD_CONTROL_PLAYER_PLAYING_ANIM( pedPlayer, CCPLAYERANIM_BEATDOWN_PISTOL_B, TRUE )
OR IS_CROWD_CONTROL_PLAYER_PLAYING_ANIM( pedPlayer, CCPLAYERANIM_BEATDOWN_PISTOL_KICK_A, TRUE )
OR IS_CROWD_CONTROL_PLAYER_PLAYING_ANIM( pedPlayer, CCPLAYERANIM_BEATDOWN_PISTOL_KICK_B, TRUE )
OR IS_CROWD_CONTROL_PLAYER_PLAYING_ANIM( pedPlayer, CCPLAYERANIM_BEATDOWN_RIFLE_A, TRUE )
OR IS_CROWD_CONTROL_PLAYER_PLAYING_ANIM( pedPlayer, CCPLAYERANIM_BEATDOWN_RIFLE_B, TRUE )
OR IS_CROWD_CONTROL_PLAYER_PLAYING_ANIM( pedPlayer, CCPLAYERANIM_BEATDOWN_RIFLE_KICK_A, TRUE )
OR IS_CROWD_CONTROL_PLAYER_PLAYING_ANIM( pedPlayer, CCPLAYERANIM_BEATDOWN_RIFLE_KICK_B, TRUE )
OR IS_CROWD_CONTROL_PLAYER_PLAYING_ANIM( pedPlayer, CCPLAYERANIM_BEATDOWN_LONG_PISTOL_A, TRUE )
OR IS_CROWD_CONTROL_PLAYER_PLAYING_ANIM( pedPlayer, CCPLAYERANIM_BEATDOWN_LONG_PISTOL_KICK_A, TRUE )
OR IS_CROWD_CONTROL_PLAYER_PLAYING_ANIM( pedPlayer, CCPLAYERANIM_BEATDOWN_LONG_RIFLE_A, TRUE )
OR IS_CROWD_CONTROL_PLAYER_PLAYING_ANIM( pedPlayer, CCPLAYERANIM_BEATDOWN_LONG_RIFLE_B, TRUE )
OR IS_CROWD_CONTROL_PLAYER_PLAYING_ANIM( pedPlayer, CCPLAYERANIM_BEATDOWN_LONG_RIFLE_KICK_A, TRUE )
OR IS_CROWD_CONTROL_PLAYER_PLAYING_ANIM( pedPlayer, CCPLAYERANIM_BEATDOWN_LONG_RIFLE_KICK_B, TRUE )
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL CAN_CROWD_CONTROL_PLAYER_PERFORM_A_BEATDOWN( PLAYER_INDEX playerID, PED_INDEX pedPlayer )
IF NOT IS_PED_INJURED( pedPlayer )
AND NOT IS_PED_RELOADING( pedPlayer )
AND NOT IS_PLAYER_FREE_AIMING( playerID )
AND NOT IS_PLAYER_TARGETTING_ANYTHING( playerID )
WEAPON_TYPE eWeapType
GET_CURRENT_PED_WEAPON( PLAYER_PED_ID(), eWeapType )
IF IS_WEAPON_SUITABLE_FOR_CROWD_CONTROL_BEATDOWN_1H( eWeapType )
CDEBUG2LN( DEBUG_NET_MINIGAME, "CAN_CROWD_CONTROL_PLAYER_PERFORM_A_BEATDOWN() - Player armed with a 1 handed weapon" )
RETURN TRUE
ELIF IS_WEAPON_SUITABLE_FOR_CROWD_CONTROL_BEATDOWN_2H( eWeapType )
CDEBUG2LN( DEBUG_NET_MINIGAME, "CAN_CROWD_CONTROL_PLAYER_PERFORM_A_BEATDOWN() - Player armed with a 2 handed weapon" )
RETURN TRUE
ENDIF
ELSE
CDEBUG2LN( DEBUG_NET_MINIGAME, "CAN_CROWD_CONTROL_PLAYER_PERFORM_A_BEATDOWN() - Player is aiming/reloading" )
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL CAN_CROWD_CONTROL_PED_BE_BEATDOWN( PED_INDEX pedCrowd, CROWD_CONTROL_PED_DECORATOR_DATA &sPedDecorData, CROWD_CONTROL_PED_ENUM eAssignedPedID, CROWD_CONTROL_HEIST_TYPE eHeistType )
IF NOT IS_BIT_SET_ENUM( sPedDecorData.iBitSet, CCPEDDATA_Dead )
AND NOT IS_BIT_SET_ENUM( sPedDecorData.iBitSet, CCPEDDATA_KnockedOut )
AND DOES_CROWD_CONTROL_PED_HAVE_ANIM( CCPANIM_BEATDOWN_INTRO, eAssignedPedID, eHeistType )
IF IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_COWER_LOOP, eAssignedPedID, eHeistType )
OR IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_COWER_TO_FLINCH_INTRO, eAssignedPedID, eHeistType )
OR IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_FLINCH_LOOP, eAssignedPedID, eHeistType )
OR IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_FLINCH_TO_IDLE_OUTRO, eAssignedPedID, eHeistType )
OR IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_BEATDOWN_LOOP, eAssignedPedID, eHeistType )
OR IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_BEATDOWN_OUTRO, eAssignedPedID, eHeistType )
OR IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_BEATDOWN_INTRO, eAssignedPedID, eHeistType, TRUE )
OR IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_BEATDOWN_HIT_A, eAssignedPedID, eHeistType, TRUE )
OR IS_CROWD_CONTROL_PED_PLAYING_ANIM( pedCrowd, CCPANIM_BEATDOWN_HIT_B, eAssignedPedID, eHeistType, TRUE )
RETURN TRUE
ELSE
CDEBUG2LN( DEBUG_NET_MINIGAME, "CAN_CROWD_CONTROL_PED_BE_BEATDOWN() - Not playing a suitable anim to hit in from" )
ENDIF
ELSE
CDEBUG2LN( DEBUG_NET_MINIGAME, "CAN_CROWD_CONTROL_PED_BE_BEATDOWN() - Cannot beat dead/knockedout ped OR does not have animation" )
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL GET_CROWD_CONTROL_PED_BEATDOWN_MOVER_OFFSET_MIN_MAX( CROWD_CONTROL_PED_ENUM ePedCrowd, CROWD_CONTROL_HEIST_TYPE eHeistType, VECTOR &vMin, VECTOR &vMax )
SWITCH eHeistType
CASE CCHEIST_CUSTOM_FLEECA
SWITCH ePedCrowd
CASE CCPED_CUSTOMER_A
vMin = <<0,0,0>>
vMax = <<0,0.5,0>>
RETURN TRUE
BREAK
CASE CCPED_CUSTOMER_B
vMin = <<0,0,0>>
vMax = <<0,0.4,0>>
RETURN TRUE
BREAK
ENDSWITCH
BREAK
CASE CCHEIST_CUSTOM_ORNATE
SWITCH ePedCrowd
CASE CCPED_CASHIER_A
vMin = <<0,-0.25,0>>
vMax = <<0,0.25,0>>
RETURN TRUE
BREAK
CASE CCPED_CASHIER_B
vMin = <<0,-0.25,0>>
vMax = <<0,0.25,0>>
RETURN TRUE
BREAK
CASE CCPED_CUSTOMER_A
vMin = <<0,0,0>>
vMax = <<0,0.5,0>>
RETURN TRUE
BREAK
CASE CCPED_CUSTOMER_B
vMin = <<0,0,0>>
vMax = <<0,0.4,0>>
RETURN TRUE
BREAK
CASE CCPED_CUSTOMER_D
vMin = <<0,-0.1,0>>
vMax = <<0,0.4,0>>
RETURN TRUE
BREAK
CASE CCPED_CUSTOMER_G
vMin = <<0,0,0>>
vMax = <<0,0.25,0>>
RETURN TRUE
BREAK
CASE CCPED_CUSTOMER_H
vMin = <<0,-0.25,0>>
vMax = <<0,0.25,0>>
RETURN TRUE
BREAK
ENDSWITCH
BREAK
ENDSWITCH
// no custom mover offsets for this ped, return false
RETURN FALSE
ENDFUNC
FUNC VECTOR GET_CROWD_CONTROL_PED_BEATDOWN_MOVER_OFFSET( PED_INDEX pedCrowd, CROWD_CONTROL_PED_ENUM ePedCrowd, VECTOR vPlayerCoord, CROWD_CONTROL_HEIST_TYPE eHeistType )
VECTOR vMin
VECTOR vMax
IF GET_CROWD_CONTROL_PED_BEATDOWN_MOVER_OFFSET_MIN_MAX( ePedCrowd, eHeistType, vMin, vMax )
vMin = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS( pedCrowd, vMin )
vMax = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS( pedCrowd, vMax )
RETURN GET_CLOSEST_POINT_ON_LINE( vPlayerCoord, vMin, vMax, TRUE )
ENDIF
RETURN GET_ENTITY_COORDS( pedCrowd, FALSE )
ENDFUNC
/// PURPOSE:
/// Obtains the angular range based on the probe's distance from the target ped's coords.
/// PARAMS:
/// vProbeCoord - The coord we're using to query (usually the players coord)
/// vPedCoord - The coord we're queryng against (usually the victims coord)
/// RETURNS:
/// FLOAT angular range, this is the range that is acceptable for the probes facing direction against the ped queried
FUNC FLOAT GET_CROWD_CONTROL_PED_BEATDOWN_FACING_ANGLE_RANGE( VECTOR vProbeCoord, VECTOR vPedCoord )
FLOAT fDist = GET_DISTANCE_BETWEEN_COORDS( vProbeCoord, vPedCoord, FALSE )
FLOAT fDistanceAlpha = CLAMP( ( fDist - cfCC_BeatdownFacingAngleChangeDistMin ) / ( cfCC_BeatdownFacingAngleChangeDistMax - cfCC_BeatdownFacingAngleChangeDistMin ), 0.0, 1.0 )
RETURN LERP_FLOAT( cfCC_BeatdownFacingAngleRangeMax, cfCC_BeatdownFacingAngleRangeMin, fDistanceAlpha )
ENDFUNC
FUNC BOOL IS_CROWD_CONTROL_PED_WITHIN_BEATDOWN_RANGE( PED_INDEX pedCrowd, PED_INDEX pedPlayer, CROWD_CONTROL_LOCAL_DATA &sLocalData, CROWD_CONTROL_LOCAL_PED_DATA &sLocalPedData )
BOOL bResult
VECTOR vPlayerCoord = GET_ENTITY_COORDS( pedPlayer )
VECTOR vPedMoverOffset = GET_CROWD_CONTROL_PED_BEATDOWN_MOVER_OFFSET( pedCrowd, sLocalPedData.eCreatorAssignedPedID, vPlayerCoord, sLocalData.eHeistType )
FLOAT fCurrentFacingAngleRange = cfCC_BeatdownFacingAngleRangeMin
IF GET_INTERIOR_FROM_ENTITY( pedPlayer ) = GET_INTERIOR_FROM_ENTITY( pedCrowd )
IF ABSF( vPlayerCoord.z - vPedMoverOffset.z ) <= 1.0
FLOAT fDist = GET_DISTANCE_BETWEEN_COORDS( vPlayerCoord, vPedMoverOffset, FALSE )
IF fDist <= cfCC_BeatdownDistMax
// is player facing ped within an angular range
fCurrentFacingAngleRange = GET_CROWD_CONTROL_PED_BEATDOWN_FACING_ANGLE_RANGE( vPlayerCoord, vPedMoverOffset )
IF IS_ENTITY_FACING_COORD_WITHIN_RANGE( PLAYER_PED_ID(), vPedMoverOffset, fCurrentFacingAngleRange )
bResult = TRUE
ELSE
CDEBUG2LN( DEBUG_NET_MINIGAME, "IS_CROWD_CONTROL_PED_WITHIN_BEATDOWN_RANGE() - not within the facing limits for the ped" )
ENDIF
ELSE
CDEBUG2LN( DEBUG_NET_MINIGAME, "IS_CROWD_CONTROL_PED_WITHIN_BEATDOWN_RANGE() - not close enough to the ped" )
ENDIF
ELSE
CDEBUG2LN( DEBUG_NET_MINIGAME, "IS_CROWD_CONTROL_PED_WITHIN_BEATDOWN_RANGE() - z difference is too big" )
ENDIF
ELSE
CDEBUG2LN( DEBUG_NET_MINIGAME, "IS_CROWD_CONTROL_PED_WITHIN_BEATDOWN_RANGE() - not in the same interior, cannot hit ped" )
ENDIF
RETURN bResult
ENDFUNC
FUNC BOOL PLAY_CROWD_CONTROL_BEATDOWN( NETWORK_INDEX niPed,
INT iCrowd,
WEAPON_TYPE eWeapType,
CROWD_CONTROL_SERVER_DATA &sData,
CROWD_CONTROL_LOCAL_DATA &sLocalData,
CROWD_CONTROL_PED_DECORATOR_DATA &sPedData,
CROWD_CONTROL_LOCAL_PED_DATA &sPedLocalData )
BOOL bCanUseWeaponButt
BOOL bCanUseKick
// First time, force gun butt
IF NOT IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_BeatdownFirst )
bCanUseWeaponButt = TRUE
bCanUseKick = FALSE
// no follow up, start on a random
ELIF sLocalData.eLastBeatdown = CCPLAYERANIM_INVALID
bCanUseWeaponButt = TRUE
bCanUseKick = TRUE
// follow up kick
ELIF IS_CROWD_CONTROL_PLAYER_MELEE_ANIM_A_KICK( sLocalData.eLastBeatdown )
bCanUseWeaponButt = FALSE
bCanUseKick = TRUE
// follow up gun butt
ELSE
bCanUseWeaponButt = TRUE
bCanUseKick = FALSE
ENDIF
PED_INDEX pedCrowd = NET_TO_PED( niPed )
BOOL bUseLongAnim = GET_DISTANCE_BETWEEN_ENTITIES( PLAYER_PED_ID(), pedCrowd ) >= cfCC_BeatdownLongAnimDistThreshold
// Select anim
CROWD_CONTROL_PLAYER_ANIM_TYPE eAnim
INT iStart
INT iEnd
IF IS_WEAPON_SUITABLE_FOR_CROWD_CONTROL_BEATDOWN_1H( eWeapType )
IF NOT IS_WEAPON_SUITABLE_FOR_CROWD_CONTROL_BEATDOWN_1H( eWeapType, TRUE )
bCanUseWeaponButt = FALSE
bCanUseKick = TRUE
ELSE
// Disabled long kicks - player slides on planted leg, looks bad
IF bUseLongAnim
bCanUseKick = FALSE
ENDIF
ENDIF
IF bUseLongAnim
IF bCanUseWeaponButt
iStart = 0
ELSE
iStart = 1
ENDIF
IF bCanUseKick
iEnd = 2
ELSE
iEnd = 1
ENDIF
IF iStart >= iEnd
SCRIPT_ASSERT( "PLAY_CROWD_CONTROL_BEATDOWN() - random anim selection for 1H PISTOL LONG melee broken, iStart >= iEnd, see logs for details, bug Michael Wadelin" )
CPRINTLN( DEBUG_NET_MINIGAME, "PLAY_CROWD_CONTROL_BEATDOWN() - random anim selection for 1H PISTOL LONG melee broken, iStart >= iEnd. iStart = ", iStart, " iEnd = ", iEnd )
CPRINTLN( DEBUG_NET_MINIGAME, "PLAY_CROWD_CONTROL_BEATDOWN() bCanUseWeaponButt = ", BOOL_TO_STRING( bCanUseWeaponButt ) )
CPRINTLN( DEBUG_NET_MINIGAME, "PLAY_CROWD_CONTROL_BEATDOWN() bCanUseKick = ", BOOL_TO_STRING( bCanUseKick ) )
CPRINTLN( DEBUG_NET_MINIGAME, "PLAY_CROWD_CONTROL_BEATDOWN() bUseLongAnim = ", BOOL_TO_STRING( bUseLongAnim ) )
ENDIF
SWITCH GET_RANDOM_INT_IN_RANGE( iStart, iEnd )
CASE 0 eAnim = CCPLAYERANIM_BEATDOWN_LONG_PISTOL_A BREAK
CASE 1 eAnim = CCPLAYERANIM_BEATDOWN_LONG_PISTOL_KICK_A BREAK
ENDSWITCH
ELSE
IF bCanUseWeaponButt
iStart = 0
ELSE
iStart = 2
ENDIF
IF bCanUseKick
iEnd = 4
ELSE
iEnd = 2
ENDIF
IF iStart >= iEnd
SCRIPT_ASSERT( "PLAY_CROWD_CONTROL_BEATDOWN() - random anim selection for 1H PISTOL NORMAL melee broken, iStart >= iEnd, see logs for details, bug Michael Wadelin" )
CPRINTLN( DEBUG_NET_MINIGAME, "PLAY_CROWD_CONTROL_BEATDOWN() - random anim selection for 1H PISTOL NORMAL melee broken, iStart >= iEnd. iStart = ", iStart, " iEnd = ", iEnd )
CPRINTLN( DEBUG_NET_MINIGAME, "PLAY_CROWD_CONTROL_BEATDOWN() bCanUseWeaponButt = ", BOOL_TO_STRING( bCanUseWeaponButt ) )
CPRINTLN( DEBUG_NET_MINIGAME, "PLAY_CROWD_CONTROL_BEATDOWN() bCanUseKick = ", BOOL_TO_STRING( bCanUseKick ) )
CPRINTLN( DEBUG_NET_MINIGAME, "PLAY_CROWD_CONTROL_BEATDOWN() bUseLongAnim = ", BOOL_TO_STRING( bUseLongAnim ) )
ENDIF
SWITCH GET_RANDOM_INT_IN_RANGE( iStart, iEnd )
CASE 0 eAnim = CCPLAYERANIM_BEATDOWN_PISTOL_A BREAK
CASE 1 eAnim = CCPLAYERANIM_BEATDOWN_PISTOL_B BREAK
CASE 2 eAnim = CCPLAYERANIM_BEATDOWN_PISTOL_KICK_A BREAK
CASE 3 eAnim = CCPLAYERANIM_BEATDOWN_PISTOL_KICK_B BREAK
ENDSWITCH
ENDIF
ELIF IS_WEAPON_SUITABLE_FOR_CROWD_CONTROL_BEATDOWN_2H( eWeapType )
// only kicks allowed for weapons such as RPGs
IF NOT IS_WEAPON_SUITABLE_FOR_CROWD_CONTROL_BEATDOWN_2H( eWeapType, TRUE )
bCanUseWeaponButt = FALSE
bCanUseKick = TRUE
ELSE
// Disabled long kicks - player slides on planted leg, looks bad
IF bUseLongAnim
bCanUseKick = FALSE
ENDIF
ENDIF
IF bCanUseWeaponButt
iStart = 0
ELSE
iStart = 2
ENDIF
IF bCanUseKick
iEnd = 4
ELSE
iEnd = 2
ENDIF
IF iStart >= iEnd
SCRIPT_ASSERT( "PLAY_CROWD_CONTROL_BEATDOWN() - random anim selection for 2H RIFLE melee broken, iStart >= iEnd, see logs for details, bug Michael Wadelin" )
CPRINTLN( DEBUG_NET_MINIGAME, "PLAY_CROWD_CONTROL_BEATDOWN() - random anim selection for 2H RIFLE melee broken, iStart >= iEnd. iStart = ", iStart, " iEnd = ", iEnd )
CPRINTLN( DEBUG_NET_MINIGAME, "PLAY_CROWD_CONTROL_BEATDOWN() bCanUseWeaponButt = ", BOOL_TO_STRING( bCanUseWeaponButt ) )
CPRINTLN( DEBUG_NET_MINIGAME, "PLAY_CROWD_CONTROL_BEATDOWN() bCanUseKick = ", BOOL_TO_STRING( bCanUseKick ) )
CPRINTLN( DEBUG_NET_MINIGAME, "PLAY_CROWD_CONTROL_BEATDOWN() bUseLongAnim = ", BOOL_TO_STRING( bUseLongAnim ) )
ENDIF
IF bUseLongAnim
SWITCH GET_RANDOM_INT_IN_RANGE( iStart, iEnd )
CASE 0 eAnim = CCPLAYERANIM_BEATDOWN_LONG_RIFLE_A BREAK
CASE 1 eAnim = CCPLAYERANIM_BEATDOWN_LONG_RIFLE_B BREAK
CASE 2 eAnim = CCPLAYERANIM_BEATDOWN_LONG_RIFLE_KICK_A BREAK
CASE 3 eAnim = CCPLAYERANIM_BEATDOWN_LONG_RIFLE_KICK_B BREAK
ENDSWITCH
ELSE
SWITCH GET_RANDOM_INT_IN_RANGE( iStart, iEnd )
CASE 0 eAnim = CCPLAYERANIM_BEATDOWN_RIFLE_A BREAK
CASE 1 eAnim = CCPLAYERANIM_BEATDOWN_RIFLE_B BREAK
CASE 2 eAnim = CCPLAYERANIM_BEATDOWN_RIFLE_KICK_A BREAK
CASE 3 eAnim = CCPLAYERANIM_BEATDOWN_RIFLE_KICK_B BREAK
ENDSWITCH
ENDIF
ENDIF
CROWD_CONTROL_PED_ANIM_TYPE eVictimAnim
IF sPedData.iBeatdownHitsRemaining <= 0
eVictimAnim = CCPANIM_BEATDOWN_DIE
ELIF sPedData.eState != CCPS_BEATDOWN
eVictimAnim = CCPANIM_BEATDOWN_INTRO
ELSE
IF GET_RANDOM_BOOL()
eVictimAnim = CCPANIM_BEATDOWN_HIT_A
ELSE
eVictimAnim = CCPANIM_BEATDOWN_HIT_B
ENDIF
ENDIF
IF HAS_ANIM_DICT_LOADED( GET_CROWD_CONTROL_PLAYER_ANIM_DICTIONARY( eAnim ) )
AND HAS_ANIM_DICT_LOADED( GET_CROWD_CONTROL_PED_ANIM_DICTIONARY( eVictimAnim, sPedLocalData.eCreatorAssignedPedID, sLocalData.eHeistType ) )
// Calculate position and orientation we want to play the anim at
VECTOR vPlayerCoord = GET_ENTITY_COORDS( PLAYER_PED_ID() )
//VECTOR vVictimCoord = GET_ENTITY_COORDS( pedCrowd )
// Get the peds mover coord, this is the coord we will offset the animation from
// We don't just use the mover directly as some peds need their mover adjusting depending on the player's angle of approach
VECTOR vMoverCoord = GET_CROWD_CONTROL_PED_BEATDOWN_MOVER_OFFSET( pedCrowd, sPedLocalData.eCreatorAssignedPedID, vPlayerCoord, sLocalData.eHeistType )
// Calculate a distance that means the ped capsules won't intersect
FLOAT fOffsetDistance = cfCC_BeatdownDistMin
//= FMAX( cfCC_BeatdownDistMin, cfCC_PlayerPedCapsuleRadiusStationary + GET_CROWD_CONTROL_PED_CAPSULE_SIZE( sPedLocalData.eCreatorAssignedPedID, sLocalData.eHeistType ) )
VECTOR vPos = ( NORMALISE_VECTOR( vPlayerCoord - vMoverCoord ) * fOffsetDistance ) + vMoverCoord
FLOAT fRot = ATAN2( -(vMoverCoord.X - vPlayerCoord.X), vMoverCoord.Y - vPlayerCoord.Y )
// VECTOR vPos = ( NORMALISE_VECTOR( vPlayerCoord - vVictimCoord ) * fOffsetDistance ) + vVictimCoord
// FLOAT fRot = ATAN2( -(vVictimCoord.X - vPlayerCoord.X), vVictimCoord.Y - vPlayerCoord.Y )
sLocalData.iSyncSceneBeatdown = NETWORK_CREATE_SYNCHRONISED_SCENE( vPos, <<0,0,fRot>>, DEFAULT, FALSE, FALSE )
// Player
NETWORK_ADD_PED_TO_SYNCHRONISED_SCENE(
PLAYER_PED_ID(),
sLocalData.iSyncSceneBeatdown,
GET_CROWD_CONTROL_PLAYER_ANIM_DICTIONARY( eAnim ),
GET_CROWD_CONTROL_PLAYER_ANIM_NAME( eAnim ),
WALK_BLEND_IN, NORMAL_BLEND_OUT,
DEFAULT,
RBF_PLAYER_BUMP | RBF_PLAYER_IMPACT | RBF_PLAYER_RAGDOLL_BUMP | RBF_PED_RAGDOLL_BUMP,
WALK_BLEND_IN )
// Victim
NETWORK_ADD_PED_TO_SYNCHRONISED_SCENE(
pedCrowd,
sLocalData.iSyncSceneBeatdown,
GET_CROWD_CONTROL_PED_ANIM_DICTIONARY( eVictimAnim, sPedLocalData.eCreatorAssignedPedID, sLocalData.eHeistType ),
GET_CROWD_CONTROL_PED_ANIM_NAME( eVictimAnim, sPedLocalData.eCreatorAssignedPedID, sLocalData.eHeistType ),
WALK_BLEND_IN, -0.1,
SYNCED_SCENE_BLOCK_MOVER_UPDATE,
RBF_PLAYER_BUMP | RBF_PLAYER_IMPACT | RBF_PLAYER_RAGDOLL_BUMP | RBF_PED_RAGDOLL_BUMP,
DEFAULT )
NETWORK_START_SYNCHRONISED_SCENE( sLocalData.iSyncSceneBeatdown )
sLocalData.iPlayerBeatingThisPed = iCrowd
sLocalData.eLastBeatdown = eAnim
SET_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_BeatdownFirst )
CLEAR_BIT_ENUM( sLocalData.iBitset, CCLOCALDATA_BeatdownInterrupt )
// REMOVED: causes animations to look strange, bent necks, etc
// Make them look at the ped for a short while when kicking them
//TASK_LOOK_AT_ENTITY( PLAYER_PED_ID(), pedCrowd, 5000, DEFAULT, SLF_LOOKAT_VERY_HIGH )
// Mark ped as being hit
// If we own the ped update it directly
IF NETWORK_HAS_CONTROL_OF_NETWORK_ID( niPed )
SET_BIT_ENUM( sPedLocalData.iBitset, CCLOCALPEDDATA_BeatdownFired )
CPRINTLN( DEBUG_NET_MINIGAME, "PLAY_CROWD_CONTROL_BEATDOWN() - Setting ped flag CCPEDDATA_BeatdownFired" )
// Otherwise broadcast event
ELSE
BROADCAST_FMMC_CC_PED_BEATDOWN( iCrowd )
CPRINTLN( DEBUG_NET_MINIGAME, "PLAY_CROWD_CONTROL_BEATDOWN() - broadcasting ped flag CCPEDDATA_BeatdownFired" )
ENDIF
// Decrement hits remaining
IF NETWORK_IS_HOST_OF_THIS_SCRIPT()
sData.iNumHitsRemaining--
ELSE
BROADCAST_FMMC_CC_PED_BEATDOWN_HIT()
ENDIF
RETURN TRUE
ELSE
CPRINTLN( DEBUG_NET_MINIGAME, "PLAY_CROWD_CONTROL_BEATDOWN() - Anims not loaded" )
ENDIF
RETURN FALSE
ENDFUNC
PROC PROCESS_CROWD_CONTROL_BEATDOWNS( NETWORK_INDEX &niPeds[],
CROWD_CONTROL_SERVER_DATA &sData,
CROWD_CONTROL_LOCAL_DATA &sLocalData,
CROWD_CONTROL_PED_DECORATOR_DATA &sPedData[],
CROWD_CONTROL_LOCAL_PED_DATA &sLocalPedData[] )
// Weapon Blocking
//-------------------------------------------------------------
BOOL bPlayerCurrentlyPerformingBeatdown = IS_CROWD_CONTROL_PLAYER_PERFORMING_A_BEATDOWN( PLAYER_PED_ID() )
// While in a beatdown
IF bPlayerCurrentlyPerformingBeatdown
// Don't allow player to change weapon while doing 1 of the beat down anims
DISABLE_CONTROL_ACTION( PLAYER_CONTROL, INPUT_SELECT_WEAPON )
IF HAS_ANIM_EVENT_FIRED( PLAYER_PED_ID(), HASH( "vibrate" ) )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_BEATDOWNS() - Melee impact vibration triggered" )
SET_CONTROL_SHAKE( PLAYER_CONTROL, 150, 255 ) // 150, 200
ENDIF
// Break out of the anim early
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_BeatdownInterrupt )
OR HAS_ANIM_EVENT_FIRED( PLAYER_PED_ID(), HASH( "breakout" ) )
IF NOT IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_BeatdownInterrupt )
SET_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_BeatdownInterrupt )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_BEATDOWNS() - Beatdown interrupt hit" )
ENDIF
IF IS_CONTROL_PRESSED( PLAYER_CONTROL, INPUT_MOVE_UD )
OR IS_CONTROL_PRESSED( PLAYER_CONTROL, INPUT_MOVE_LR )
OR IS_CONTROL_PRESSED( PLAYER_CONTROL, INPUT_AIM )
CLEAR_PED_TASKS( PLAYER_PED_ID() )
CLEAR_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_BeatdownInputTaken )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_BEATDOWNS() - Beatdown has been interrupted by player input" )
ENDIF
ENDIF
ELSE
// Reset the ped being hit and last anim
sLocalData.iPlayerBeatingThisPed = -1
sLocalData.eLastBeatdown = CCPLAYERANIM_INVALID
ENDIF
// Check if the player can beat the ped, no anim playing or anim ready to interrupt and player has suitible weapon and in suitible state
BOOL bCanPlayerBeat
IF NOT bPlayerCurrentlyPerformingBeatdown
OR IS_BIT_SET_ENUM( sLocalData.iBitset, CCLOCALDATA_BeatdownInterrupt )
IF CAN_CROWD_CONTROL_PLAYER_PERFORM_A_BEATDOWN( PLAYER_ID(), PLAYER_PED_ID() )
bCanPlayerBeat = TRUE
ENDIF
ENDIF
// Take input - we can detect this earlier than the interrupt window so that it doesn't feel unresponsive.
BOOL bNeedsAReload
WEAPON_TYPE eWeaponCurrent
IF GET_CURRENT_PED_WEAPON( PLAYER_PED_ID(), eWeaponCurrent )
INT iAmmoInClip
IF GET_AMMO_IN_CLIP( PLAYER_PED_ID(), eWeaponCurrent, iAmmoInClip )
INT iAmmoInClipMax, iWeaponAmmo
iWeaponAmmo = GET_AMMO_IN_PED_WEAPON( PLAYER_PED_ID(), eWeaponCurrent )
iAmmoInClipMax = GET_MAX_AMMO_IN_CLIP( PLAYER_PED_ID(), eWeaponCurrent )
IF iAmmoInClip < iWeaponAmmo
AND iAmmoInClip < iAmmoInClipMax
bNeedsAReload = TRUE
ENDIF
ENDIF
ENDIF
// If control is pressed or early input is made
BOOL bMeleePressed
IF NOT bNeedsAReload
AND ( IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_BeatdownInputTaken )
OR IS_CONTROL_JUST_PRESSED( PLAYER_CONTROL, INPUT_MELEE_ATTACK_LIGHT )
OR IS_DISABLED_CONTROL_JUST_PRESSED( PLAYER_CONTROL, INPUT_MELEE_ATTACK_LIGHT ) )
// Look out for early input
IF bPlayerCurrentlyPerformingBeatdown
AND sLocalData.eLastBeatdown != CCPLAYERANIM_INVALID
AND HAS_CROWD_CONTROL_PLAYER_FINISHED_ANIM( PLAYER_PED_ID(), sLocalData.eLastBeatdown, 0.25, TRUE, sLocalData.iSyncSceneBeatdown )
SET_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_BeatdownInputTaken )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_BEATDOWNS() - Early input taken and stored" )
ENDIF
bMeleePressed = TRUE
ENDIF
BOOL bDisableMelee
INT iPedToBeat = -1
INT iCrowd
IF bCanPlayerBeat
REPEAT sData.iNumCrowdControlPeds iCrowd
BOOL bResetProbe = FALSE
bResetProbe = FALSE
IF NETWORK_DOES_ENTITY_EXIST_WITH_NETWORK_ID( niPeds[sData.iCrowdControlCloudIndex[iCrowd]] )
PED_INDEX pedCrowd = NET_TO_PED( niPeds[sData.iCrowdControlCloudIndex[iCrowd]] )
IF NOT IS_PED_INJURED( pedCrowd )
AND IS_BIT_SET_ENUM( sPedData[iCrowd].iBitSet, CCPEDDATA_RunningBespokeAnims )
// disable regular melee buttons for player while near a crowd ped
IF GET_DISTANCE_BETWEEN_ENTITIES( PLAYER_PED_ID(), pedCrowd ) < cfCC_BeatdownMeleeDisableDist
bDisableMelee = TRUE
ENDIF
// found a ped we want for the beat down, but need to do 1 final test to probe for collision
IF IS_CROWD_CONTROL_PED_WITHIN_BEATDOWN_RANGE( pedCrowd, PLAYER_PED_ID(), sLocalData, sLocalPedData[iCrowd] )
IF CAN_CROWD_CONTROL_PED_BE_BEATDOWN( pedCrowd, sPedData[iCrowd], sLocalPedData[iCrowd].eCreatorAssignedPedID, sLocalData.eHeistType )
INT iWasSomethingHit = 0
VECTOR vHitPos = <<0,0,0>>
VECTOR vHitNormal = <<0,0,0>>
ENTITY_INDEX entityHit = NULL
SHAPETEST_STATUS eStatus = GET_SHAPE_TEST_RESULT( sLocalData.shapetestBeatdownProbe, iWasSomethingHit, vHitPos, vHitNormal, entityHit )
// Start a new shape test
// either 1 hadn't been started or we want to test a different ped
IF eStatus = SHAPETEST_STATUS_NONEXISTENT
OR sLocalData.iCrowdPedBeatdownProbe != iCrowd
VECTOR vCrowdPedCoord = GET_CROWD_CONTROL_PED_BEATDOWN_MOVER_OFFSET( pedCrowd, sLocalPedData[iCrowd].eCreatorAssignedPedID, GET_ENTITY_COORDS( PLAYER_PED_ID() ), sLocalData.eHeistType )
sLocalData.shapetestBeatdownProbe = START_SHAPE_TEST_CAPSULE( GET_ENTITY_COORDS( PLAYER_PED_ID() ),
vCrowdPedCoord,
0.25,
SCRIPT_INCLUDE_ALL,
PLAYER_PED_ID(),
SCRIPT_SHAPETEST_OPTION_IGNORE_NO_COLLISION )
sLocalData.iCrowdPedBeatdownProbe = iCrowd
CDEBUG2LN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_BEATDOWNS() iCrowd = ", iCrowd, " - Starting new probe." )
// Results ready, process them
ELIF eStatus = SHAPETEST_STATUS_RESULTS_READY
// We only update this flag once the test result comes back, we do not clear it and only set it to true
// for the frames the result comes back on, this would lead to nothing happening a lot of the time input is pressed.
IF iWasSomethingHit = 0
OR ( DOES_ENTITY_EXIST( entityHit )
AND IS_ENTITY_A_PED( entityHit )
AND GET_PED_INDEX_FROM_ENTITY_INDEX( entityHit ) = pedCrowd )
SET_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_BeatdownProbeResultAllowHit )
CDEBUG2LN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_BEATDOWNS() iCrowd = ", iCrowd, " - Probe successfull, Setting flag CCLOCALDATA_BeatdownProbeResultAllowHit" )
sLocalData.shapetestBeatdownProbe = NULL
ELSE
CDEBUG2LN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_BEATDOWNS() iCrowd = ", iCrowd, " - Probe hit something, reset probe." )
bResetProbe = TRUE
ENDIF
ENDIF
// SUCCESS, we are allowed to hit the ped
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_BeatdownProbeResultAllowHit )
iPedToBeat = iCrowd
CDEBUG2LN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_BEATDOWNS() iCrowd = ", iCrowd, " - CCLOCALDATA_BeatdownProbeResultAllowHit is set, allow ped to be hit." )
ENDIF
// Stop testing other peds
iCrowd = sData.iNumCrowdControlPeds // BREAK LOOP
ELSE
CDEBUG2LN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_BEATDOWNS() iCrowd = ", iCrowd, " - CAN_CROWD_CONTROL_PED_BE_BEATDOWN() failed." )
IF sLocalData.iCrowdPedBeatdownProbe = iCrowd
CDEBUG2LN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_BEATDOWNS() iCrowd = ", iCrowd, " - mark probe for reset." )
bResetProbe = TRUE
ENDIF
ENDIF
ELSE
CDEBUG2LN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_BEATDOWNS() iCrowd = ", iCrowd, " - IS_CROWD_CONTROL_PED_WITHIN_BEATDOWN_RANGE() failed." )
IF sLocalData.iCrowdPedBeatdownProbe = iCrowd
CDEBUG2LN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_BEATDOWNS() iCrowd = ", iCrowd, " - mark probe for reset." )
bResetProbe = TRUE
ENDIF
ENDIF
ELSE
CDEBUG2LN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_BEATDOWNS() iCrowd = ", iCrowd, " - ped dead or not running custom anims." )
IF sLocalData.iCrowdPedBeatdownProbe = iCrowd
CDEBUG2LN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_BEATDOWNS() iCrowd = ", iCrowd, " - mark probe for reset." )
bResetProbe = TRUE
ENDIF
ENDIF
ELSE
CDEBUG2LN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_BEATDOWNS() iCrowd = ", iCrowd, " - net id does not exist." )
IF sLocalData.iCrowdPedBeatdownProbe = iCrowd
CDEBUG2LN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_BEATDOWNS() iCrowd = ", iCrowd, " - mark probe for reset." )
bResetProbe = TRUE
ENDIF
ENDIF
IF bResetProbe
CDEBUG2LN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_BEATDOWNS() iCrowd = ", iCrowd, " - resetting probe, clearing flag CCLOCALDATA_BeatdownProbeResultAllowHit, sLocalData.iCrowdPedBeatdownProbe = ", sLocalData.iCrowdPedBeatdownProbe )
sLocalData.shapetestBeatdownProbe = NULL
sLocalData.iCrowdPedBeatdownProbe = -1
CLEAR_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_BeatdownProbeResultAllowHit )
ENDIF
ENDREPEAT
ENDIF
IF bDisableMelee
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_ATTACK_ALTERNATE )
SET_PED_CONFIG_FLAG( PLAYER_PED_ID(), PCF_DisableMelee, TRUE )
SET_PED_RESET_FLAG( PLAYER_PED_ID(), PRF_DisableAmbientMeleeMoves, TRUE )
SET_PED_RESET_FLAG( PLAYER_PED_ID(), PRF_SuspendInitiatedMeleeActions, TRUE )
ELSE
SET_PED_CONFIG_FLAG( PLAYER_PED_ID(), PCF_DisableMelee, FALSE )
ENDIF
IF bCanPlayerBeat
IF iPedToBeat != -1
IF bMeleePressed
IF bPlayerCurrentlyPerformingBeatdown
AND IS_BIT_SET_ENUM( sLocalData.iBitset, CCLOCALDATA_BeatdownInterrupt )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_BEATDOWNS() - Beatdown has been interrupted by new melee request" )
ENDIF
WEAPON_TYPE eWeapType
GET_CURRENT_PED_WEAPON( PLAYER_PED_ID(), eWeapType )
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_BEATDOWNS() - Playing beatdown on ped[", iPedToBeat, "] = ", GET_CROWD_CONTROL_PED_NAME_FROM_ANIM_ID( sLocalPedData[iPedToBeat].eCreatorAssignedPedID ) )
PLAY_CROWD_CONTROL_BEATDOWN( niPeds[sData.iCrowdControlCloudIndex[iPedToBeat]],
iPedToBeat,
eWeapType,
sData,
sLocalData,
sPedData[iPedToBeat],
sLocalPedData[iPedToBeat] )
CLEAR_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_BeatdownInputTaken )
ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Run by each player manages the minigame state for the host and also various local assets, cutscenes, etc for each player.
PROC PROCESS_CROWD_CONTROL_MINIGAME_STATE(
CROWD_CONTROL_SERVER_DATA &sCrowdControlData, CROWD_CONTROL_LOCAL_DATA &sLocalData,
CROWD_CONTROL_PED_DECORATOR_DATA &sCrowdControlPedData[], CROWD_CONTROL_LOCAL_PED_DATA &sCrowdControlLocalPedData[],
INT &iCrowdControlPedCloudIndex[], INT iNumCrowdPeds, NETWORK_INDEX &niPeds[],
INT &iCurrentHighestPriority[], INT iLocalPlayersTeam )
UNUSED_PARAMETER(niPeds)
UNUSED_PARAMETER(iCrowdControlPedCloudIndex)
INT i
// this is here cus stupid language won't let me declare is inside a switch case, BOOOOOOOOOOOOOOOOOO
INT iLookupArray[FMMC_MAX_CROWD_PEDS]
VECTOR vAlarmCoords[ciCC_MaxAlarms]
// Custom anims required
BOOL bRequireCustomAnims
IF sLocalData.eHeistType = CCHEIST_CUSTOM_FLEECA
OR sLocalData.eHeistType = CCHEIST_CUSTOM_ORNATE
bRequireCustomAnims = TRUE
ENDIF
//STAGE ZERO - Wait for peds to initialise and start their intro idle scene
SWITCH sCrowdControlData.iMinigameStage
CASE 0
IF bRequireCustomAnims
REQUEST_MODEL( PROP_PHONE_ING )
ENDIF
IF NETWORK_IS_HOST_OF_THIS_SCRIPT()
// Check if all peds are initialised
BOOL bAllPedsReady
bAllPedsReady = TRUE
FOR i = 0 TO iNumCrowdPeds-1
IF NOT IS_BIT_SET_ENUM( sCrowdControlPedData[i].iBitSet, CCPEDDATA_HasBeenInitialised )
bAllPedsReady = FALSE
ENDIF
ENDFOR
IF bAllPedsReady
// has already been reset else where, doubling up causes some issues
//RESET_CROWD_CONTROL_LOCAL_DATA( sLocalData )
// Crowd is restricted to start with
SET_BIT_ENUM( sCrowdControlData.iBitset, CCDATA_Restricted )
// Set hero pick timer make sure an initial hero is selected
SET_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_TimeSetNextHeroPick )
sLocalData.fFear = 0.0
sCrowdControlData.iMinigameStage++
ENDIF
ENDIF
BREAK
//STAGE ONE - Kick off the crowd panic
CASE 1
IF bRequireCustomAnims
AND NOT IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_IntroAnimsRequested )
REQUEST_CROWD_CONTROL_PED_ANIMS_FOR_SCENE( CCPANIM_INTRO, sLocalData.eHeistType, sCrowdControlLocalPedData, iNumCrowdPeds )
REQUEST_CROWD_CONTROL_PED_ANIMS_FOR_SCENE( CCPANIM_COWER_LOOP, sLocalData.eHeistType, sCrowdControlLocalPedData, iNumCrowdPeds )
IF sLocalData.eHeistType != CCHEIST_CUSTOM_ORNATE
REQUEST_ADDITIONAL_TEXT_FOR_DLC( "OBHFAU", DLC_TEXT_SLOT0 )
ENDIF
SET_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_IntroAnimsRequested )
ENDIF
IF NETWORK_IS_HOST_OF_THIS_SCRIPT()
BOOL bKickOffCrowd
// Has at least 1 ped kicked off?
FOR i = 0 TO iNumCrowdPeds-1
// Crowd ped is no longer in intro state
IF sCrowdControlPedData[i].eState != CCPS_INTRO
bKickOffCrowd = TRUE
i = FMMC_MAX_CROWD_PEDS // break loop
ENDIF
ENDFOR
IF bRequireCustomAnims
// SYNC SCENE: Intro transition to Cower
// (note: only 1 machine can create and start the sync scene including on peds it doesnt own this is why we are doing it this way)
IF HAVE_CROWD_CONTROL_PED_ANIMS_LOADED_FOR_SCENE( CCPANIM_INTRO, sLocalData.eHeistType, sCrowdControlLocalPedData, iNumCrowdPeds )
AND HAVE_CROWD_CONTROL_PED_ANIMS_LOADED_FOR_SCENE( CCPANIM_COWER_LOOP, sLocalData.eHeistType, sCrowdControlLocalPedData, iNumCrowdPeds )
IF NOT bKickOffCrowd
// Intro loop not running, run it
IF sLocalData.iSyncSceneIntroLoop = -1
OR NOT IS_SYNCHRONIZED_SCENE_RUNNING( NETWORK_GET_LOCAL_SCENE_FROM_NETWORK_ID( sLocalData.iSyncSceneIntroLoop ) )
PLAY_CROWD_CONTROL_SYNC_SCENE( sLocalData.iSyncSceneIntroLoop,
niPeds, iCrowdControlPedCloudIndex, sCrowdControlLocalPedData, iNumCrowdPeds,
CCPANIM_INTRO_LOOP, sLocalData.eHeistType,
REALLY_SLOW_BLEND_IN, -0.1, TRUE, FALSE,
SYNCED_SCENE_USE_PHYSICS,
RBF_PLAYER_IMPACT
)
ENDIF
ELSE
// Intro loop -> Intro main (transition to cower where it all kicks off)
PLAY_CROWD_CONTROL_SYNC_SCENE( sCrowdControlData.iSyncScene,
niPeds, iCrowdControlPedCloudIndex, sCrowdControlLocalPedData, iNumCrowdPeds,
CCPANIM_INTRO, sLocalData.eHeistType,
REALLY_SLOW_BLEND_IN, -0.1, FALSE, TRUE,
SYNCED_SCENE_USE_PHYSICS,
RBF_PLAYER_IMPACT
)
SET_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_RecievedSyncedScene ) // host will have the sync scene straight way so set this
sCrowdControlData.iMinigameStage++
ENDIF
ENDIF
// No anim req so just go to the next stage
ELSE
sCrowdControlData.iMinigameStage++
ENDIF
IF sCrowdControlData.iMinigameStage > 1
// Initialise the fear to full
sLocalData.fFear = 100.0
// Set when the panic should be over
sLocalData.timeCrowdKickedOff = GET_NETWORK_TIME()
CPRINTLN( DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_MINIGAME_STATE() - bit set CCDATA_Active" )
SET_BIT_ENUM( sCrowdControlData.iBitSet, CCDATA_Active ) // Set active to inform other peds they need to kick off
ENDIF
ENDIF
BREAK
//STAGE TWO - Main minigame controller
CASE 2
IF bRequireCustomAnims
IF NOT IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_MainAnimsRequested )
REQUEST_CROWD_CONTROL_PED_ANIMS_FOR_SCENE( CCPANIM_FLINCH_LOOP, sLocalData.eHeistType, sCrowdControlLocalPedData, iNumCrowdPeds )
REQUEST_CROWD_CONTROL_PED_ANIMS_FOR_SCENE( CCPANIM_HERO_LOOP, sLocalData.eHeistType, sCrowdControlLocalPedData, iNumCrowdPeds )
REQUEST_ANIM_DICT( GET_CROWD_CONTROL_PLAYER_ANIM_DICTIONARY( CCPLAYERANIM_BEATDOWN_RIFLE_A ) )
SET_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_MainAnimsRequested )
ENDIF
// release the intro stuff now, we dont need it again
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_IntroAnimsRequested )
RELEASE_CROWD_CONTROL_PED_ANIMS_FOR_SCENE( CCPANIM_INTRO_LOOP, sLocalData.eHeistType, sCrowdControlLocalPedData, iNumCrowdPeds )
CLEAR_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_IntroAnimsRequested )
ENDIF
ENDIF
IF NETWORK_IS_HOST_OF_THIS_SCRIPT()
// Fail/Pass logic
//-------------------------------------------------------------------------
// Fail not currently disabled
IF NOT IS_BIT_SET_ENUM( sCrowdControlData.iBitset, CCDATA_FailDisabled )
IF SHOULD_DISABLE_CROWD_CONTROL_FAIL( sLocalData, iCurrentHighestPriority, iLocalPlayersTeam )
SET_BIT_ENUM( sCrowdControlData.iBitset, CCDATA_FailDisabled )
// Not picked up a fail from a ped
ELIF NOT IS_BIT_SET_ENUM( sCrowdControlData.iBitset, CCDATA_Failed )
INT iDeadCount, iKnockedOutPeds
INT iDeadPedID, iKnockedOutPedID
iDeadPedID = -1
iDeadCount = GET_CROWD_CONTROL_NUMBER_OF_DEAD_PEDS_WITH_PED_ID( sCrowdControlPedData, sCrowdControlData, iDeadPedID )
iKnockedOutPedID = -1
iKnockedOutPeds = GET_CROWD_CONTROL_NUMBER_OF_KNOCKED_OUT_PEDS_WITH_PED_ID( sCrowdControlPedData, sCrowdControlData, iKnockedOutPedID )
INT iDeadLimit, iKnockoutLimit
iDeadLimit = GET_CROWD_CONTROL_PED_DEAD_LIMIT( sLocalData.eHeistType )
SWITCH sLocalData.eHeistType
CASE CCHEIST_CUSTOM_ORNATE
iKnockoutLimit = 2
BREAK
DEFAULT
iKnockoutLimit = -1
BREAK
ENDSWITCH
// dead limit reached - fail
IF iDeadCount != -1
AND iDeadCount > iDeadLimit
IF iDeadCount <= 1
sLocalData.eFailReason = CCFAIL_PEDKILLED
ELSE
sLocalData.eFailReason = CCFAIL_PEDSKILLED
ENDIF
sLocalData.iFailCausedByPed = iDeadPedID
sLocalData.timeFailDelay = GET_TIME_OFFSET( GET_NETWORK_TIME(), 2000 ) // 2 second delay
// knocked out limit reached, fail
ELIF iKnockoutLimit != -1
AND iKnockedOutPeds > iKnockoutLimit
sLocalData.eFailReason = CCFAIL_TOOMANYKNOCKOUTS
sLocalData.iFailCausedByPed = sLocalData.iCashierPed
sLocalData.timeFailDelay = GET_TIME_OFFSET( GET_NETWORK_TIME(), 3000 )
// no hits remaining
ELIF sCrowdControlData.iNumHitsRemaining <= 0
sLocalData.eFailReason = CCFAIL_NOHITSREMAINING
sLocalData.iFailCausedByPed = sLocalData.iCashierPed
sLocalData.timeFailDelay = GET_TIME_OFFSET( GET_NETWORK_TIME(), 3000 )
ELSE
CLEAR_BIT_ENUM( sCrowdControlData.iBitset, CCDATA_FearFailsBlocked )
FOR i = 0 TO FMMC_MAX_TEAMS-1
IF iCurrentHighestPriority[i] < FMMC_MAX_RULES
IF IS_BIT_SET( g_FMMC_STRUCT.sFMMCEndConditions[i].iRuleBitsetTwo[iCurrentHighestPriority[i]], ciBS_RULE2_DISABLE_CROWD_FEAR )
SET_BIT_ENUM( sCrowdControlData.iBitset, CCDATA_FearFailsBlocked )
ENDIF
ENDIF
ENDFOR
// Fail for overall fear level not acceptable
IF IS_BIT_SET_ENUM( sCrowdControlData.iBitset, CCDATA_Active )
AND NOT IS_BIT_SET_ENUM( sCrowdControlData.iBitset, CCDATA_FearFailsBlocked )
AND NOT HAVE_ALL_PLAYERS_PASSED_CROWD_CONTROL_RULE( sLocalData, iCurrentHighestPriority, iLocalPlayersTeam )
AND sLocalData.fFear != -1
AND sLocalData.fIntimidationBar != -1
AND sLocalData.fIntimidationBar <= ciCC_FearFailMin
// start the timer
IF NOT IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_TimerSetFearFail )
sLocalData.timeFearFail = GET_TIME_OFFSET( GET_NETWORK_TIME(), 2000 )
SET_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_TimerSetFearFail )
ENDIF
// Fail by fear timer has expired and the fear is still not withint acceptable ranges
IF IS_TIME_MORE_THAN( GET_NETWORK_TIME(), sLocalData.timeFearFail )
sLocalData.eFailReason = CCFAIL_FEARLOW
sLocalData.timeFailDelay = GET_NETWORK_TIME() // no delay
sLocalData.iFailCausedByPed = sLocalData.iCashierPed
ENDIF
ELSE
// reset the fail timer
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_TimerSetFearFail )
CLEAR_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_TimerSetFearFail )
ENDIF
ENDIF
ENDIF
IF sLocalData.eFailReason != CCFAIL_NONE
SET_BIT_ENUM( sCrowdControlData.iBitSet, CCDATA_Failed )
CPRINTLN(DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_MINIGAME_STATE() - FAIL DETECTED - Fail Reason = ", GET_CROWD_CONTROL_FAIL_REASON_STRING( sLocalData.eFailReason ) )
BROADCAST_FMMC_CC_FAIL( ENUM_TO_INT( sLocalData.eFailReason ), sLocalData.iFailCausedByPed, sLocalData.timeFailDelay )
ENDIF
ENDIF
ENDIF
// Hero picking logic
//-------------------------------------------------------------------------
SWITCH sLocalData.eHeistType
CASE CCHEIST_CUSTOM_FLEECA
// Restrict the crowd after the easy pass and the cashier is into normal cower
IF IS_BIT_SET_ENUM( sCrowdControlData.iBitset, CCDATA_EasyPassComplete )
IF NOT IS_BIT_SET_ENUM( sCrowdControlData.iBitset, CCDATA_Restricted )
SET_BIT_ENUM( sCrowdControlData.iBitset, CCDATA_Restricted )
ENDIF
ELSE
IF IS_BIT_SET_ENUM( sCrowdControlData.iBitset, CCDATA_Restricted )
CLEAR_BIT_ENUM( sCrowdControlData.iBitset, CCDATA_Restricted )
ENDIF
ENDIF
BREAK
CASE CCHEIST_CUSTOM_ORNATE
// Restrict the cashiers while waiting for the thermite to blow the door open objective
IF ( sLocalData.iThermiteTeam != -1 AND sLocalData.iThermiteTeam < FMMC_MAX_TEAMS
AND sLocalData.iThermiteRule != -1 AND sLocalData.iThermiteRule < FMMC_MAX_RULES
AND iCurrentHighestPriority[sLocalData.iThermiteTeam] > sLocalData.iThermiteRule )
#IF IS_DEBUG_BUILD
OR IS_BIT_SET_ENUM( sCrowdControlData.iBitset, CCDATA_RestrictedDebug )
#ENDIF
IF IS_BIT_SET_ENUM( sCrowdControlData.iBitset, CCDATA_Restricted )
CLEAR_BIT_ENUM( sCrowdControlData.iBitset, CCDATA_Restricted )
ENDIF
ENDIF
BREAK
ENDSWITCH
// Clear any active heros that are no longer valid
INT iLowestHeroAttempts
iLowestHeroAttempts = -1
FOR i = 0 TO iNumCrowdPeds-1
INT iCurrentHeroAttemptsValue
iCurrentHeroAttemptsValue = GET_PACKED_BITFIELD_VALUE_FROM_ARRAY( sCrowdControlData.iHeroAttempts, i, ciCC_HeroAttemptsDataBitWidth )
IF IS_BIT_SET( sCrowdControlData.iCurrentActiveHeros, i )
// If the mission has failed or passed or the hero is no longer valid or the crowd is now locked in clear the hero
IF IS_BIT_SET_ENUM( sCrowdControlPedData[i].iBitSet, CCPEDDATA_Dead )
OR IS_BIT_SET_ENUM( sCrowdControlPedData[i].iBitSet, CCPEDDATA_KnockedOut )
OR IS_BIT_SET_ENUM( sCrowdControlData.iBitset, CCDATA_Failed )
OR IS_BIT_SET_ENUM( sCrowdControlData.iBitset, CCDATA_FailDisabled )
OR ( NOT IS_CROWD_CONTROL_PED_A_VALID_HERO( sCrowdControlPedData[i], sCrowdControlLocalPedData[i].eCreatorAssignedPedID, sLocalData.eHeistType, IS_BIT_SET_ENUM( sCrowdControlData.iBitset, CCDATA_Restricted ) )
AND sCrowdControlPedData[i].eState != CCPS_HERO_ATTEMPT
AND sCrowdControlPedData[i].eState != CCPS_HERO
AND sCrowdControlPedData[i].eState != CCPS_COMBAT )
// Increment the attempts by 1
IF IS_BIT_SET_ENUM( sCrowdControlPedData[i].iBitSet, CCPEDDATA_HeroAttempted )
iCurrentHeroAttemptsValue++
SET_PACKED_BITFIELD_VALUE_IN_ARRAY( sCrowdControlData.iHeroAttempts, i, ciCC_HeroAttemptsDataBitWidth, iCurrentHeroAttemptsValue )
ENDIF
CLEAR_BIT( sCrowdControlData.iCurrentActiveHeros, i )
ENDIF
ENDIF
// Check out all valid heroes who are not always active and see who has the lowest number of attempts
IF NOT IS_BIT_SET_ENUM( sCrowdControlPedData[i].iBitSet, CCPEDDATA_Dead )
AND NOT IS_BIT_SET_ENUM( sCrowdControlPedData[i].iBitSet, CCPEDDATA_KnockedOut )
AND NOT IS_CROWD_CONTROL_PED_HERO_ALWAYS_ACTIVE( sCrowdControlLocalPedData[i].eCreatorAssignedPedID, sLocalData.eHeistType )
AND IS_CROWD_CONTROL_PED_A_VALID_HERO( sCrowdControlPedData[i], sCrowdControlLocalPedData[i].eCreatorAssignedPedID, sLocalData.eHeistType, IS_BIT_SET_ENUM( sCrowdControlData.iBitset, CCDATA_Restricted ) )
IF iLowestHeroAttempts = -1
OR iCurrentHeroAttemptsValue < iLowestHeroAttempts
iLowestHeroAttempts = iCurrentHeroAttemptsValue
ENDIF
ENDIF
ENDFOR
// Either minigame has failed or the fail has now been disabled, don't assign heros and go to next stage
IF IS_BIT_SET_ENUM( sCrowdControlData.iBitset, CCDATA_FailDisabled )
OR IS_BIT_SET_ENUM( sCrowdControlData.iBitset, CCDATA_Failed )
sCrowdControlData.iMinigameStage++
ELSE
INT iHeroCount
FOR i = 0 TO iNumCrowdPeds-1
// Initialse look up array with start numbers
iLookupArray[i] = i
// Count the current number of active regular heroes
IF IS_BIT_SET( sCrowdControlData.iCurrentActiveHeros, i )
IF NOT IS_CROWD_CONTROL_PED_HERO_ALWAYS_ACTIVE( sCrowdControlLocalPedData[i].eCreatorAssignedPedID, sLocalData.eHeistType )
iHeroCount++
ENDIF
ENDIF
// Make sure always active heros are turned on now
IF NOT IS_BIT_SET_ENUM( sCrowdControlPedData[i].iBitSet, CCPEDDATA_Dead )
AND NOT IS_BIT_SET_ENUM( sCrowdControlPedData[i].iBitSet, CCPEDDATA_KnockedOut )
AND IS_CROWD_CONTROL_PED_HERO_ALWAYS_ACTIVE( sCrowdControlLocalPedData[i].eCreatorAssignedPedID, sLocalData.eHeistType )
AND IS_CROWD_CONTROL_PED_A_VALID_HERO( sCrowdControlPedData[i], sCrowdControlLocalPedData[i].eCreatorAssignedPedID, sLocalData.eHeistType, IS_BIT_SET_ENUM( sCrowdControlData.iBitset, CCDATA_Restricted ) )
CPRINTLN(DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_MINIGAME_STATE() - HERO SELECTED(always on hero) = ", GET_CROWD_CONTROL_PED_NAME_FROM_ANIM_ID( sCrowdControlLocalPedData[i].eCreatorAssignedPedID ) )
SET_BIT( sCrowdControlData.iCurrentActiveHeros, i )
ENDIF
ENDFOR
// Setup to only pick 1 new normal hero each time, even if we increase the max active heroes we still want this
// so that we have a delay between heroes becoming active to avoid getting 2 becoming active at the same time
// Only pick a new hero if there is space
IF iHeroCount < ciCC_MaxActiveHeros
IF NOT IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_TimeSetNextHeroPick )
// Random time between 10 - 25 seconds (in 1 second intervals)
sLocalData.timeNextHeroPick = GET_TIME_OFFSET( GET_NETWORK_TIME(), GET_RANDOM_INT_IN_RANGE( 7, 15 ) * 1000 )
SET_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_TimeSetNextHeroPick )
ENDIF
// If the hero pick timer has expired then select a new hero
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_TimeSetNextHeroPick )
AND IS_TIME_MORE_THAN( GET_NETWORK_TIME(), sLocalData.timeNextHeroPick )
// Randomise array by using random value switching
FOR i = 0 TO iNumCrowdPeds-1
INT iRand, iTemp
iRand = GET_RANDOM_INT_IN_RANGE( 0, iNumCrowdPeds )
iTemp = iLookupArray[i]
iLookupArray[i] = iLookupArray[iRand]
iLookupArray[iRand] = iTemp
ENDFOR
// Set new heros in the random array
FOR i = 0 TO iNumCrowdPeds-1
// Check if we can set this hero active
IF NOT IS_BIT_SET( sCrowdControlData.iCurrentActiveHeros, iLookupArray[i] )
AND NOT IS_BIT_SET_ENUM( sCrowdControlPedData[i].iBitSet, CCPEDDATA_Dead )
AND NOT IS_BIT_SET_ENUM( sCrowdControlPedData[i].iBitSet, CCPEDDATA_KnockedOut )
AND IS_CROWD_CONTROL_PED_A_VALID_HERO( sCrowdControlPedData[iLookupArray[i]], sCrowdControlLocalPedData[iLookupArray[i]].eCreatorAssignedPedID, sLocalData.eHeistType, IS_BIT_SET_ENUM( sCrowdControlData.iBitset, CCDATA_Restricted ) )
AND NOT IS_CROWD_CONTROL_PED_HERO_ALWAYS_ACTIVE( sCrowdControlLocalPedData[iLookupArray[i]].eCreatorAssignedPedID, sLocalData.eHeistType )
AND GET_PACKED_BITFIELD_VALUE_FROM_ARRAY( sCrowdControlData.iHeroAttempts, iLookupArray[i], ciCC_HeroAttemptsDataBitWidth ) = iLowestHeroAttempts
CPRINTLN(DEBUG_NET_MINIGAME, "PROCESS_CROWD_CONTROL_MINIGAME_STATE() - HERO SELECTED = ", GET_CROWD_CONTROL_PED_NAME_FROM_ANIM_ID( sCrowdControlLocalPedData[iLookupArray[i]].eCreatorAssignedPedID ) )
SET_BIT( sCrowdControlData.iCurrentActiveHeros, iLookupArray[i] )
// Reset the hero pick timer and break the loop, only want to pick 1 this timer around
CLEAR_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_TimeSetNextHeroPick )
i = FMMC_MAX_CROWD_PEDS // break the loop
ENDIF
ENDFOR
ENDIF
ENDIF
ENDIF
ENDIF
// Client machines check to see if their synced scene has started and if they can start updating the individual
// peds that they own
IF NOT IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_RecievedSyncedScene )
IF sCrowdControlData.iSyncScene != -1
INT iLocalSSID
iLocalSSID = NETWORK_GET_LOCAL_SCENE_FROM_NETWORK_ID( sCrowdControlData.iSyncScene )
IF IS_SYNCHRONIZED_SCENE_RUNNING( iLocalSSID )
AND GET_SYNCHRONIZED_SCENE_PHASE( iLocalSSID ) > 0.5 // gives time for the anims to start
SET_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_RecievedSyncedScene )
ENDIF
ENDIF
ENDIF
BREAK
//STAGE THREE - Post Crowd Control - Pass/Fail
CASE 3
// Cleanup assets
//----------------------------------------------------------------------------------
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_MainAnimsRequested )
BOOL bAllCleanedUp
bAllCleanedUp = TRUE
FOR i = 0 TO sCrowdControlData.iNumCrowdControlPeds-1
IF NOT NETWORK_DOES_ENTITY_EXIST_WITH_NETWORK_ID( niPeds[sCrowdControlData.iCrowdControlCloudIndex[i]] )
OR IS_PED_INJURED( NET_TO_PED( niPeds[sCrowdControlData.iCrowdControlCloudIndex[i]] ) )
RELEASE_CROWD_CONTROL_PED_ANIM( CCPANIM_COWER_LOOP, sLocalData.eHeistType, sCrowdControlLocalPedData[i].eCreatorAssignedPedID )
RELEASE_CROWD_CONTROL_PED_ANIM( CCPANIM_FLINCH_LOOP, sLocalData.eHeistType, sCrowdControlLocalPedData[i].eCreatorAssignedPedID )
RELEASE_CROWD_CONTROL_PED_ANIM( CCPANIM_HERO_LOOP, sLocalData.eHeistType, sCrowdControlLocalPedData[i].eCreatorAssignedPedID )
ELSE
bAllCleanedUp = FALSE
ENDIF
ENDFOR
IF bAllCleanedUp
REMOVE_ANIM_DICT( GET_CROWD_CONTROL_PLAYER_ANIM_DICTIONARY( CCPLAYERANIM_BEATDOWN_PISTOL_A ) )
CLEAR_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_MainAnimsRequested )
ENDIF
ENDIF
// Raise the alarms
//----------------------------------------------------------------------------------
IF IS_BIT_SET_ENUM( sCrowdControlData.iBitset, CCDATA_Failed )
BOOL bAlarmAvailable
TEXT_LABEL_23 tlAlarmSoundBank
TEXT_LABEL_23 tlAlarmName
TEXT_LABEL_23 tlAlarmSountSet
SWITCH sLocalData.eHeistType
CASE CCHEIST_CUSTOM_FLEECA
bAlarmAvailable = TRUE
tlAlarmSoundBank = "ALARM_BELL_01"
tlAlarmName = "Bell_01"
tlAlarmSountSet = "ALARMS_SOUNDSET"
vAlarmCoords[0] = <<-2965.2305, 486.2513, 17.5822>>
BREAK
CASE CCHEIST_CUSTOM_ORNATE
bAlarmAvailable = TRUE
tlAlarmSoundBank = "ALARM_BELL_01"
tlAlarmName = "Bell_01"
tlAlarmSountSet = "ALARMS_SOUNDSET"
vAlarmCoords[0] = <<246.5,228.0,113.3>>
vAlarmCoords[1] = <<260.3,216.0,104.1>>
BREAK
ENDSWITCH
// Process loading and playing the alarms, if one is avilable
IF bAlarmAvailable
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_RaiseAlarms )
AND NOT IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_AlarmsPlaying )
IF REQUEST_SCRIPT_AUDIO_BANK( tlAlarmSoundBank )
REPEAT ciCC_MaxAlarms i
IF sLocalData.iSFXAlarms[i] = -1
sLocalData.iSFXAlarms[i] = GET_SOUND_ID()
ENDIF
IF sLocalData.iSFXAlarms[i] != -1
IF HAS_SOUND_FINISHED( sLocalData.iSFXAlarms[i] )
PLAY_SOUND_FROM_COORD( sLocalData.iSFXAlarms[i], tlAlarmName, vAlarmCoords[i], tlAlarmSountSet )
ENDIF
ENDIF
ENDREPEAT
SET_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_AlarmsPlaying )
ENDIF
ENDIF
ENDIF
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC PROCESS_CROWD_CONTROL_MINIGAME_HUD(
CROWD_CONTROL_SERVER_DATA &sCrowdControlServerData,
CROWD_CONTROL_LOCAL_DATA &sLocalData,
BOOL bObjectiveIsCrowdControl )
IF bObjectiveIsCrowdControl
// Player has entered the bank
IF NOT IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DisplayHUD )
IF HAS_PED_ENTERED_CROWD_CONTROL_TRIGGER_ZONE( PLAYER_PED_ID(), sLocalData, TRUE )
OR IS_BIT_SET_ENUM( sCrowdControlServerData.iBitset, CCDATA_Active )
SET_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_DisplayHUD )
ENDIF
ENDIF
ELSE
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DisplayHUD )
CLEAR_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_DisplayHUD )
ENDIF
ENDIF
IF NOT IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_StartedFailCutscene )
// Initialise the bar to the current level of fear
IF sLocalData.fIntimidationBar = -1
sLocalData.fIntimidationBar = 0.0
ELIF IS_BIT_SET_ENUM( sCrowdControlServerData.iBitset, CCDATA_Active )
// Interpolate the current bars value towards the desired one
IF sLocalData.fIntimidationBar < sLocalData.fFear
sLocalData.fIntimidationBar = CLAMP( sLocalData.fIntimidationBar + ( sLocalData.fIntimidationBarInterpRate * TIMESTEP() ), sLocalData.fIntimidationBar, sLocalData.fFear )
ELIF sLocalData.fIntimidationBar > sLocalData.fFear
sLocalData.fIntimidationBar = CLAMP( sLocalData.fIntimidationBar - ( sLocalData.fIntimidationBarInterpRate * TIMESTEP() ), sLocalData.fFear, sLocalData.fIntimidationBar )
ENDIF
ENDIF
ENDIF
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DisplayHUD )
//AND NOT IS_BIT_SET(iLocalBoolCheck5,LBOOL5_PED_INVOLVED_IN_CUTSCENE)
//AND NOT IS_BIT_SET(MC_playerBD[iPartToUse].iClientBitSet,PBBOOL_FINISHED)
AND NOT g_bMissionEnding
AND NOT IS_CUTSCENE_PLAYING()
// Intimidation bar
//--------------------------------------------------------------------------------
HUD_COLOURS eColor
INT iFlashTimer
// Enable the colour change and flashing once the bar has gone above the threshold for the first time
IF NOT IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_HUDWarning )
AND sLocalData.fIntimidationBar > cfCC_PedBreakoutCowerThreshold
SET_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_HUDWarning )
ENDIF
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_HUDWarning )
AND sLocalData.fIntimidationBar <= cfCC_PedBreakoutCowerThreshold
eColor = HUD_COLOUR_RED
ELSE
eColor = HUD_COLOUR_YELLOW
ENDIF
IF IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_HUDWarning )
AND sLocalData.fIntimidationBar <= 15.0
iFlashTimer = 999999999
ELSE
iFlashTimer = 0
ENDIF
DRAW_GENERIC_METER( ROUND( sLocalData.fIntimidationBar ), 100, "MC_CRCON_L", eColor, iFlashTimer, HUDORDER_DONTCARE, -1, -1, FALSE, TRUE )
// Help text
//--------------------------------------------------------------------------------
// HELP 1 - Brief
IF NOT IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_HelpText1 )
PRINT_HELP( "CC_HELP1", 7000 )
SET_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_HelpText1 )
// HELP 2 - Intimidation instructions
ELIF NOT IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_HelpText2 )
IF IS_BIT_SET_ENUM( sCrowdControlServerData.iBitset, CCDATA_Active )
AND NOT IS_THIS_HELP_MESSAGE_BEING_DISPLAYED( "CC_HELP1" )
PRINT_HELP( "CC_HELP2" )
SET_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_HelpText2 )
ENDIF
// HELP 3 - Warning about hitting peds too much
ELIF NOT IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_HelpText3 )
IF IS_BIT_SET_ENUM( sCrowdControlServerData.iBitset, CCDATA_Active )
IF sCrowdControlServerData.iNumHitsRemaining < ciCC_MaxBeatdownHitsTotal / 2
PRINT_HELP( "CC_HELP3" )
SET_BIT_ENUM( sLocalData.iBitSet, CCLOCALDATA_HelpText3 )
ENDIF
ENDIF
ENDIF
ELSE
IF IS_THIS_HELP_MESSAGE_BEING_DISPLAYED( "CC_HELP1" )
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED( "CC_HELP2" )
CLEAR_HELP()
ENDIF
ENDIF
ENDPROC
#IF IS_DEBUG_BUILD
CONST_FLOAT cfXOffsetStartHeader 0.03
CONST_FLOAT cfXOffsetStart 0.05
CONST_FLOAT cfYOffsetStart 0.15
CONST_FLOAT cfYOffset 0.015
PROC DISPLAY_DEBUG_PROGRESS_BAR( STRING strBarName, FLOAT fValue, INT &iCounter, FLOAT fMaxValue = 1.0, FLOAT fMinValue = 0.0, BOOL bIncludeWrittenValue = TRUE, FLOAT fUpperThreshold = -1.0, FLOAT fLowerThreshold = -1.0 )
TEXT_LABEL_63 tlDebug = strBarName
tlDebug += ": "
IF bIncludeWrittenValue
tlDebug += GET_STRING_FROM_FLOAT( fValue, 2 )
ENDIF
tlDebug += " ["
// Clamp and adjust value for rendering the bar
FLOAT fBarValue = CLAMP( fValue, fMinValue, fMaxValue )
fBarValue = ( fBarValue - fMinValue ) / ( fMaxValue - fMinValue )
INT i
FOR i = 0 TO 9
IF fBarValue > ( i / 10.0 )
tlDebug += "x"
ELSE
tlDebug += "."
ENDIF
ENDFOR
tlDebug += "]"
IF ( fUpperThreshold != -1 AND fValue >= fUpperThreshold )
OR ( fLowerThreshold != -1 AND fValue <= fLowerThreshold )
DRAW_DEBUG_TEXT_2D( tlDebug, <<cfXOffsetStart, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColWarningR, ciCC_DebugColWarningG, ciCC_DebugColWarningB, 255 )
ELSE
DRAW_DEBUG_TEXT_2D( tlDebug, <<cfXOffsetStart, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
ENDIF
iCounter++
ENDPROC
PROC DISPLAY_CROWD_CONTROL_DEBUG(
CROWD_CONTROL_SERVER_DATA &sCrowdControlData, CROWD_CONTROL_LOCAL_DATA &sLocalData,
INT &iThreatenData[], INT iNumCrowdPeds, INT &iCurrentHighestPriority[], INT iThisTeam )
IF sLocalData.eDebugDisplay != CCDEBUG_OFF
INT iCounter
TEXT_LABEL_63 tlDebug
tlDebug = "CROWD CONTROL DEBUG: "
tlDebug += GET_CROWD_CONTROL_DEBUG_NAME( sLocalData.eDebugDisplay )
DRAW_DEBUG_TEXT_2D( tlDebug, <<cfXOffsetStartHeader, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, 255, 255, 255, 255 )
iCounter++
tlDebug = "STATE DATA"
DRAW_DEBUG_TEXT_2D( tlDebug, <<cfXOffsetStartHeader, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, 255, 255, 255, 255 )
iCounter++
IF NETWORK_IS_HOST_OF_THIS_SCRIPT()
tlDebug = "I AM HOST"
DRAW_DEBUG_TEXT_2D( tlDebug, <<cfXOffsetStart, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColActiveR, ciCC_DebugColActiveG, ciCC_DebugColActiveB, 255 )
ELSE
tlDebug = "I AM A CLIENT"
DRAW_DEBUG_TEXT_2D( tlDebug, <<cfXOffsetStart, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
ENDIF
iCounter++
tlDebug = "eHeistType: "
tlDebug += GET_CROWD_CONTROL_HEIST_TYPE_STRING( sLocalData.eHeistType )
DRAW_DEBUG_TEXT_2D( tlDebug, <<cfXOffsetStart, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
tlDebug = "iMinigameStage: "
tlDebug += sCrowdControlData.iMinigameStage
DRAW_DEBUG_TEXT_2D( tlDebug, <<cfXOffsetStart, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
INT i
FOR i = 0 TO FMMC_MAX_TEAMS-1
tlDebug = "iPriority["
tlDebug += i
tlDebug += "]: "
tlDebug += iCurrentHighestPriority[i]
DRAW_DEBUG_TEXT_2D( tlDebug, <<cfXOffsetStart, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
ENDFOR
iCounter++ // space
// Bit flags
tlDebug = "CrowdControlActive: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sCrowdControlData.iBitSet, CCDATA_Active ) )
DRAW_DEBUG_TEXT_2D( tlDebug, <<cfXOffsetStart, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
tlDebug = "CrowdRestricted: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sCrowdControlData.iBitSet, CCDATA_Restricted ) )
DRAW_DEBUG_TEXT_2D( tlDebug, <<0.05, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
tlDebug = "iNumHitsRemaining: "
tlDebug += sCrowdControlData.iNumHitsRemaining
DRAW_DEBUG_TEXT_2D( tlDebug, <<0.05, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
iCounter++ // space
tlDebug = "PASS/FAIL"
DRAW_DEBUG_TEXT_2D( tlDebug, <<cfXOffsetStartHeader, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, 255, 255, 255, 255 )
iCounter++
tlDebug = "CrowdControlFailed: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sCrowdControlData.iBitSet, CCDATA_Failed ) )
DRAW_DEBUG_TEXT_2D( tlDebug, <<0.05, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
tlDebug = "FailDisabled: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sCrowdControlData.iBitSet, CCDATA_FailDisabled ) )
DRAW_DEBUG_TEXT_2D( tlDebug, <<0.05, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
tlDebug = "FailProcessed: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sCrowdControlData.iBitSet, CCDATA_FailProcessed ) )
DRAW_DEBUG_TEXT_2D( tlDebug, <<0.05, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
tlDebug = "FailReason: "
tlDebug += GET_CROWD_CONTROL_FAIL_REASON_STRING( sLocalData.eFailReason )
DRAW_DEBUG_TEXT_2D( tlDebug, <<0.05, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
tlDebug = "EasyPassComplete: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sCrowdControlData.iBitSet, CCDATA_EasyPassComplete ) )
DRAW_DEBUG_TEXT_2D( tlDebug, <<0.05, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
tlDebug = "EasyPassProcessed: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sCrowdControlData.iBitSet, CCDATA_EasyPassProcessed ) )
DRAW_DEBUG_TEXT_2D( tlDebug, <<0.05, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
tlDebug = "FearFailsBlocked: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sCrowdControlData.iBitSet, CCDATA_FearFailsBlocked ) )
DRAW_DEBUG_TEXT_2D( tlDebug, <<0.05, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
tlDebug = "RaiseAlarms: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_RaiseAlarms ) )
DRAW_DEBUG_TEXT_2D( tlDebug, <<0.05, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
tlDebug = "RaiseAlarmDialogue: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sCrowdControlData.iBitSet, CCDATA_RaiseAlarmDiagloue ) )
DRAW_DEBUG_TEXT_2D( tlDebug, <<0.05, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
iCounter++ // space
IF sLocalData.eDebugDisplay = CCDEBUG_FEAR_AND_THREATS
OR sLocalData.eDebugDisplay = CCDEBUG_ALL
OR sLocalData.eDebugDisplay = CCDEBUG_BASIC
tlDebug = "FEAR & THREATS"
DRAW_DEBUG_TEXT_2D( tlDebug, <<cfXOffsetStartHeader, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, 255, 255, 255, 255 )
iCounter++
DISPLAY_DEBUG_PROGRESS_BAR( "Fear(Local)", sLocalData.fFear, iCounter, 100.0, 0.0, DEFAULT, -1, cfCC_PedBreakoutCowerThreshold )
DISPLAY_DEBUG_PROGRESS_BAR( "Fear(Server)", sCrowdControlData.fFear, iCounter, 100.0, 0.0, DEFAULT, -1, cfCC_PedBreakoutCowerThreshold )
DISPLAY_DEBUG_PROGRESS_BAR( "Intimidation", sLocalData.fIntimidationBar, iCounter, 100.0, 0.0, DEFAULT, -1, cfCC_PedBreakoutCowerThreshold )
tlDebug = "BarInterpRate: "
tlDebug += GET_STRING_FROM_FLOAT( sLocalData.fIntimidationBarInterpRate )
DRAW_DEBUG_TEXT_2D( tlDebug, <<0.05, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
DISPLAY_DEBUG_PROGRESS_BAR( "PlayerVoice", NETWORK_GET_PLAYER_LOUDNESS( PLAYER_ID() ), iCounter, DEFAULT, DEFAULT, DEFAULT, cfCC_ShoutVolumeLevel )
// Players threats to each crowd member
FOR i = 0 TO iNumCrowdPeds-1
tlDebug = "["
tlDebug += i
tlDebug += "]: "
tlDebug += GET_CROWD_CONTROL_THREAT_TYPE_STRING( INT_TO_ENUM( CROWD_CONTROL_THREAT_TYPE, GET_PACKED_BITFIELD_VALUE_FROM_ARRAY( iThreatenData, i, ciCC_ThreatTypeDataBitWidth ) ) )
DRAW_DEBUG_TEXT_2D( tlDebug, <<cfXOffsetStart, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
ENDFOR
iCounter++ // space
ENDIF
// BEATDOWNS
//--------------------------------------------------------------------------------
IF sLocalData.eDebugDisplay = CCDEBUG_BEATDOWNS
OR sLocalData.eDebugDisplay = CCDEBUG_ALL
OR sLocalData.eDebugDisplay = CCDEBUG_BASIC
tlDebug = "BEATDOWN"
DRAW_DEBUG_TEXT_2D( tlDebug, <<cfXOffsetStartHeader, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, 255, 255, 255, 255 )
iCounter++
tlDebug = "iCrowdPedBeatdownProbe: "
tlDebug += sLocalData.iCrowdPedBeatdownProbe
DRAW_DEBUG_TEXT_2D( tlDebug, <<cfXOffsetStart, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
tlDebug = "ProbeResultAllowHit: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_BeatdownProbeResultAllowHit ) )
DRAW_DEBUG_TEXT_2D( tlDebug, <<cfXOffsetStart, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
iCounter++ // space
ENDIF
// Misc
//--------------------------------------------------------------------------------
IF sLocalData.eDebugDisplay = CCDEBUG_MISC
OR sLocalData.eDebugDisplay = CCDEBUG_ALL
OR sLocalData.eDebugDisplay = CCDEBUG_BASIC
tlDebug = "MISC DATA"
DRAW_DEBUG_TEXT_2D( tlDebug, <<cfXOffsetStartHeader, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, 255, 255, 255, 255 )
iCounter++
tlDebug = "DisplayHUD: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_DisplayHUD ) )
DRAW_DEBUG_TEXT_2D( tlDebug, <<0.05, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
tlDebug = "ReceivedSyncScene: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_RecievedSyncedScene ) )
DRAW_DEBUG_TEXT_2D( tlDebug, <<0.05, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
tlDebug = "SSIntroToCower: "
tlDebug += sCrowdControlData.iSyncScene
INT iSyncSceneLocal = NETWORK_GET_LOCAL_SCENE_FROM_NETWORK_ID( sCrowdControlData.iSyncScene )
tlDebug += "("
tlDebug += iSyncSceneLocal
tlDebug += ")"
DRAW_DEBUG_TEXT_2D( tlDebug, <<cfXOffsetStart, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
tlDebug = "SSIntroLoop: "
tlDebug += sLocalData.iSyncSceneIntroLoop
iSyncSceneLocal = NETWORK_GET_LOCAL_SCENE_FROM_NETWORK_ID( sCrowdControlData.iSyncScene )
tlDebug += "("
tlDebug += iSyncSceneLocal
tlDebug += ")"
DRAW_DEBUG_TEXT_2D( tlDebug, <<cfXOffsetStart, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
tlDebug = "iCCRule["
tlDebug += iThisTeam
tlDebug += "]: "
tlDebug += sLocalData.iCCRule[iThisTeam]
DRAW_DEBUG_TEXT_2D( tlDebug, <<cfXOffsetStart, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
tlDebug = "BlockingActionMode"
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sLocalData.iBitSet, CCLOCALDATA_BlockingActionMode ) )
DRAW_DEBUG_TEXT_2D( tlDebug, <<cfXOffsetStart, cfYOffsetStart + (cfYOffset * iCounter), 0.0>>, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, 255 )
iCounter++
iCounter++ // space
ENDIF
ENDIF
ENDPROC
PROC DISPLAY_CROWD_CONTROL_PED_DEBUG( PED_INDEX ped, CROWD_CONTROL_SERVER_DATA &sData, CROWD_CONTROL_LOCAL_DATA &sLocalData, CROWD_CONTROL_PED_DECORATOR_DATA &sPedData, CROWD_CONTROL_LOCAL_PED_DATA &sLocalPedData, INT iCrowdNum, BOOL bIsOwned )
IF sLocalData.eDebugDisplay != CCDEBUG_OFF
CONST_INT icXOffset 10
CONST_INT icYOffset 10
IF DOES_ENTITY_EXIST( ped )
VECTOR vPlayerCoord = GET_ENTITY_COORDS( PLAYER_PED_ID(), FALSE )
VECTOR vPedCoord = GET_ENTITY_COORDS( ped, FALSE )
IF NOT IS_VECTOR_ZERO( vPedCoord )
vPedCoord.Z += 0.75
FLOAT fDist = GET_DISTANCE_BETWEEN_COORDS( GET_FINAL_RENDERED_CAM_COORD(), vPedCoord )
INT iAlpha = ROUND( 255 * ( 1 - CLAMP( FMAX( fDist - 5.0, 0.0 ) / 10.0, 0.0, 1.0 ) ) )
INT iCounter
TEXT_LABEL_63 tlDebug
// BASIC
//--------------------------------------------------------------------------------
tlDebug = "BASIC"
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, 0, icYOffset * iCounter, 255, 255, 255, iAlpha )
iCounter++
IF bIsOwned
tlDebug = "OWNED"
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColActiveR, ciCC_DebugColActiveG, ciCC_DebugColActiveB, iAlpha )
ELSE
tlDebug = "REMOTE"
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
ENDIF
iCounter++
tlDebug = "["
tlDebug += iCrowdNum
tlDebug += "] "
tlDebug += GET_CROWD_CONTROL_PED_NAME_FROM_ANIM_ID( sLocalPedData.eCreatorAssignedPedID )
IF IS_BIT_SET( sData.iCurrentActiveHeros, iCrowdNum )
OR IS_BIT_SET_ENUM( sPedData.iBitSet, CCPEDDATA_HeroComplete )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColWarningR, ciCC_DebugColWarningG, ciCC_DebugColWarningB, iAlpha )
ELSE
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
ENDIF
iCounter++
tlDebug = "State: "
tlDebug += GET_CROWD_CONTROL_PED_STATE_STRING( sPedData.eState )
SWITCH sPedData.eState
CASE CCPS_COMBAT
CASE CCPS_HERO_ATTEMPT
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColWarningR, ciCC_DebugColWarningG, ciCC_DebugColWarningB, iAlpha )
BREAK
DEFAULT
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
BREAK
ENDSWITCH
iCounter++
tlDebug = "StatePrev: "
tlDebug += GET_CROWD_CONTROL_PED_STATE_STRING( sPedData.eStatePrev )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
IF NOT IS_PED_INJURED( ped )
tlDebug = "Speech: "
IF IS_ANY_SPEECH_PLAYING( ped )
IF IS_AMBIENT_SPEECH_PLAYING( ped )
tlDebug += "Dialogue"
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColActiveR, ciCC_DebugColActiveG, ciCC_DebugColActiveB, iAlpha )
ELSE
tlDebug += "Pain"
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColWarningR, ciCC_DebugColWarningG, ciCC_DebugColWarningB, iAlpha )
ENDIF
ELSE
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
ENDIF
ENDIF
iCounter++
iCounter++ // space
// FEAR
//--------------------------------------------------------------------------------
IF sLocalData.eDebugDisplay = CCDEBUG_FEAR_AND_THREATS
OR sLocalData.eDebugDisplay = CCDEBUG_ALL
tlDebug = "FEAR & THREATS"
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, 0, icYOffset * iCounter, 255, 255, 255, iAlpha )
iCounter++
tlDebug = "InfluenceDirectThreat: "
tlDebug += GET_STRING_FROM_FLOAT( sPedData.fInfluenceDirectThreat, 2 )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
tlDebug = "InfluenceShouting: "
tlDebug += GET_STRING_FROM_FLOAT( sPedData.fInfluenceShouting, 2 )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
tlDebug = "CCPEDDATA_FearLocked: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sPedData.iBitSet, CCPEDDATA_FearLocked ) )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
iCounter++ // space
IF bIsOwned
tlDebug = "*OWNER ONLY* eBiggestThreat: "
tlDebug += GET_CROWD_CONTROL_THREAT_TYPE_STRING( sLocalPedData.eBiggestThreat )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
tlDebug = "*OWNER ONLY* eThreatFelt: "
tlDebug += GET_CROWD_CONTROL_THREAT_TYPE_STRING( sLocalPedData.eDebugThreatFelt )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
// draw line between ped and the player threatening them
IF DOES_ENTITY_EXIST( sLocalPedData.pedBiggestThreat )
DRAW_DEBUG_LINE( vPedCoord, GET_ENTITY_COORDS( sLocalPedData.pedBiggestThreat ), 255, 0, 0, 255 )
ENDIF
iCounter++ // space
ENDIF
ENDIF
// HERO
//--------------------------------------------------------------------------------
IF sLocalData.eDebugDisplay = CCDEBUG_HERO
OR sLocalData.eDebugDisplay = CCDEBUG_ALL
tlDebug = "HERO"
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, 0, icYOffset * iCounter, 255, 255, 255, iAlpha )
iCounter++
tlDebug = "CCPEDDATA_HeroAttempted: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sPedData.iBitSet, CCPEDDATA_HeroAttempted ) )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
tlDebug = "CCPEDDATA_HeroComplete: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sPedData.iBitSet, CCPEDDATA_HeroComplete ) )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
tlDebug = "CCPEDDATA_HeroBroken: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sPedData.iBitSet, CCPEDDATA_HeroBroken ) )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
tlDebug = "iHeroAttempts: "
tlDebug += GET_PACKED_BITFIELD_VALUE_FROM_ARRAY( sData.iHeroAttempts, iCrowdNum, ciCC_HeroAttemptsDataBitWidth )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
iCounter++ // space
ENDIF
// BEATDOWNS
//--------------------------------------------------------------------------------
IF sLocalData.eDebugDisplay = CCDEBUG_BEATDOWNS
OR sLocalData.eDebugDisplay = CCDEBUG_ALL
tlDebug = "BEATDOWNS"
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, 0, icYOffset * iCounter, 255, 255, 255, iAlpha )
iCounter++
tlDebug = "iBeatdownRounds: "
tlDebug += sPedData.iBeatdownRounds
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
tlDebug = "iBeatdownHitsRemaining: "
tlDebug += sPedData.iBeatdownHitsRemaining
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
tlDebug = "BeatdownFired: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_BeatdownFired ) )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
tlDebug = "BeatdownSyncSceneStarted: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sPedData.iBitSet, CCPEDDATA_BeatdownSyncSceneStarted ) )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
iCounter++ // space
IF IS_BIT_SET_ENUM( sPedData.iBitSet, CCPEDDATA_RunningBespokeAnims )
VECTOR vMoverOffset
VECTOR vGroundCoord
vMoverOffset = GET_CROWD_CONTROL_PED_BEATDOWN_MOVER_OFFSET( ped, sLocalPedData.eCreatorAssignedPedID, GET_ENTITY_COORDS( PLAYER_PED_ID(), FALSE ), sLocalData.eHeistType )
vGroundCoord = vMoverOffset
GET_GROUND_Z_FOR_3D_COORD( vMoverOffset, vGroundCoord.Z )
// Melee disable range
INT iColR, iColG, iColB
// Animated melee range
FLOAT fDistToMover
fDistToMover = GET_DISTANCE_BETWEEN_ENTITY_AND_COORD( PLAYER_PED_ID(), vMoverOffset, FALSE )
IF fDistToMover <= cfCC_BeatdownDistMax
// Set the color to green
iColR = 0
iColG = 255
iColB = 0
// Draw debug showing the mover offset
VECTOR vMin, vMax
IF GET_CROWD_CONTROL_PED_BEATDOWN_MOVER_OFFSET_MIN_MAX( sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType, vMin, vMax )
vMin = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS( ped, vMin )
vMax = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS( ped, vMax )
// joining line
DRAW_DEBUG_LINE( vMin, vMax, 0, 0, 255, 255 )
// min node and line to ground
DRAW_DEBUG_LINE( vMin, <<vMin.x, vMin.y, vGroundCoord.z >>, 0, 0, 255, 255 )
DRAW_DEBUG_SPHERE( vMin, 0.03, 0, 0, 255, 255 )
// max node and line to ground
DRAW_DEBUG_LINE( vMax, <<vMax.x, vMax.y, vGroundCoord.z >>, 0, 0, 255, 255 )
DRAW_DEBUG_SPHERE( vMax, 0.03, 0, 0, 255, 255 )
ENDIF
// Mover offset
DRAW_DEBUG_SPHERE( vMoverOffset, 0.05, 255, 0, 0, 255 )
ELSE
iColR = 255
iColG = 0
iColB = 0
ENDIF
DRAW_MARKER(MARKER_CONE, vGroundCoord+<<0,0,0.01>>, <<0,0,0>>, <<0,0,0>>, <<cfCC_BeatdownDistMax*2, cfCC_BeatdownDistMax*2, 0.0>>, iColR, iColG, iColB, iAlpha )
FLOAT fCoronaHeight
IF fDistToMover <= cfCC_BeatdownMeleeDisableDist
// Draw the angle range debug once within melee disable range
FLOAT fCurrentFacingAngleRange, fPlayerHeading
VECTOR vLeftBoundary, vRightBoundary
fCurrentFacingAngleRange = GET_CROWD_CONTROL_PED_BEATDOWN_FACING_ANGLE_RANGE( vPlayerCoord, vMoverOffset )
fPlayerHeading = GET_ENTITY_HEADING( PLAYER_PED_ID() )
vLeftBoundary = GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS( vPlayerCoord, fPlayerHeading - fCurrentFacingAngleRange, << 0, cfCC_BeatdownDistMax, 0 >> )
vRightBoundary = GET_OFFSET_FROM_COORD_AND_HEADING_IN_WORLD_COORDS( vPlayerCoord, fPlayerHeading + fCurrentFacingAngleRange, << 0, cfCC_BeatdownDistMax, 0 >> )
IF IS_ENTITY_FACING_COORD_WITHIN_RANGE( PLAYER_PED_ID(), vMoverOffset, fCurrentFacingAngleRange )
iColR = 0
iColG = 255
iColB = 0
ELSE
iColR = 255
iColG = 50
iColB = 0
ENDIF
tlDebug = GET_STRING_FROM_FLOAT( -fCurrentFacingAngleRange, 2 )
DRAW_DEBUG_LINE( vPlayerCoord, vLeftBoundary, iColR, iColG, iColB, 255 )
DRAW_DEBUG_TEXT( tlDebug, vLeftBoundary, iColR, iColG, iColB, 255 )
tlDebug = GET_STRING_FROM_FLOAT( fCurrentFacingAngleRange, 2 )
DRAW_DEBUG_LINE( vPlayerCoord, vRightBoundary, iColR, iColG, iColB, 255 )
DRAW_DEBUG_TEXT( tlDebug, vRightBoundary, iColR, iColG, iColB, 255 )
// Melee disable corona color & height
iColR = 255
iColG = 0
iColB = 0
fCoronaHeight = 0.1
ELSE
iColR = 255
iColG = 255
iColB = 255
fCoronaHeight = 0.05
ENDIF
// Draw the disable melee corona
DRAW_MARKER(MARKER_CYLINDER, vGroundCoord, <<0,0,0>>, <<0,0,0>>, <<cfCC_BeatdownMeleeDisableDist*2, cfCC_BeatdownMeleeDisableDist*2, fCoronaHeight>>, iColR, iColG, iColB, iAlpha )
ENDIF
ENDIF
// Dialogue
//--------------------------------------------------------------------------------
IF sLocalData.eDebugDisplay = CCDEBUG_DIALOGUE
tlDebug = "DIALOGUE"
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, 0, icYOffset * iCounter, 255, 255, 255, iAlpha )
iCounter++
tlDebug = "Current: "
tlDebug += GET_CROWD_CONTROL_DIALOGUE_ENUM_STRING( sLocalPedData.eDialogueCurrentlyPlaying )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
tlDebug = "Context: "
IF IS_ANY_SPEECH_PLAYING( ped )
IF IS_AMBIENT_SPEECH_PLAYING( ped )
TEXT_LABEL_23 tlConvo, tlDummy
INT iLine
IF GET_CROWD_CONTROL_PED_DIALOGUE_ROOT_LABEL( sLocalPedData.eCreatorAssignedPedID, sLocalPedData.eDialogueCurrentlyPlaying, sLocalData.eHeistType, tlConvo, tlDummy, iLine )
tlDebug += tlConvo
tlDebug += " ("
tlDebug += GET_CONVERSATION_LINE_FILENAME_FROM_CONVERSATION_ROOT( tlConvo, iLine )
tlDebug += ")"
ENDIF
ELSE
tlDebug += GET_AUD_DAMAGE_REASON_DEBUG_STRING( GET_CROWD_CONTROL_PED_ALTERNATIVE_PAIN_DIALOGUE( sLocalPedData.eDialogueCurrentlyPlaying ) )
ENDIF
ENDIF
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
tlDebug = "Client->Host: "
tlDebug += GET_CROWD_CONTROL_DIALOGUE_ENUM_STRING( sLocalPedData.eDialogueClientToHost )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
tlDebug = "Host->Client: "
tlDebug += GET_CROWD_CONTROL_DIALOGUE_ENUM_STRING( sLocalPedData.eDialogueHostToClient )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
iCounter++ // space
TEXT_LABEL_15 tlProfile
TEXT_LABEL_23 tlDummy
GET_CROWD_CONTROL_VOICE_RACE_GENDER_PREFIX( sLocalPedData.eCreatorAssignedPedID, sLocalData.eHeistType, tlProfile, tlDummy )
tlDebug = "Profile: "
tlDebug += tlProfile
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
tlDebug = "LastTriggered: "
tlDebug += GET_TIME_DIFFERENCE( sLocalPedData.timeDialogueLastBroadcast, GET_NETWORK_TIME() )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
iCounter++ // space
tlDebug = "Timer(SCARED_HIGH): "
tlDebug += IMAX( 0, GET_TIME_DIFFERENCE( sLocalPedData.timeDialogueContextTimers[CCDIACONTEXT_SCARED_HIGH], GET_NETWORK_TIME() ) )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
tlDebug = "Timer(SCARED_MEDIUM): "
tlDebug += IMAX( 0, GET_TIME_DIFFERENCE( sLocalPedData.timeDialogueContextTimers[CCDIACONTEXT_SCARED_MEDIUM], GET_NETWORK_TIME() ) )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
tlDebug = "Timer(SCARED_LOW): "
tlDebug += IMAX( 0, GET_TIME_DIFFERENCE( sLocalPedData.timeDialogueContextTimers[CCDIACONTEXT_SCARED_LOW], GET_NETWORK_TIME() ) )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
tlDebug = "Timer(SCARED_SECOND_HAND): "
tlDebug += IMAX( 0, GET_TIME_DIFFERENCE( sLocalPedData.timeDialogueContextTimers[CCDIACONTEXT_SCARED_SECOND_HAND], GET_NETWORK_TIME() ) )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
tlDebug = "Timer(AMBIENT): "
tlDebug += IMAX( 0, GET_TIME_DIFFERENCE( sLocalPedData.timeDialogueContextTimers[CCDIACONTEXT_AMBIENT], GET_NETWORK_TIME() ) )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
iCounter++ // space
ENDIF
// MISC
//--------------------------------------------------------------------------------
IF sLocalData.eDebugDisplay = CCDEBUG_MISC
OR sLocalData.eDebugDisplay = CCDEBUG_ALL
tlDebug = "MISC"
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, 0, icYOffset * iCounter, 255, 255, 255, iAlpha )
iCounter++
tlDebug = "CCPEDDATA_Dead: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sPedData.iBitSet, CCPEDDATA_Dead ) )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
tlDebug = "CCPEDDATA_KnockedOut: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sPedData.iBitSet, CCPEDDATA_KnockedOut ) )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
tlDebug = "CCPEDDATA_HasBeenInitialised: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sPedData.iBitSet, CCPEDDATA_HasBeenInitialised ) )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
tlDebug = "CCLOCALPEDDATA_LocallyInitialised: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sLocalPedData.iBitSet, CCLOCALPEDDATA_LocallyInitialised ) )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
tlDebug = "CCPEDDATA_RunningBespokeAnims: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sPedData.iBitSet, CCPEDDATA_RunningBespokeAnims ) )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
tlDebug = "CCPEDDATA_IgnoreStickyBombs: "
tlDebug += BOOL_TO_STRING( IS_BIT_SET_ENUM( sPedData.iBitSet, CCPEDDATA_IgnoreStickyBombs ) )
DRAW_DEBUG_TEXT_WITH_OFFSET( tlDebug, vPedCoord, icXOffset, icYOffset * iCounter, ciCC_DebugColNormalR, ciCC_DebugColNormalG, ciCC_DebugColNormalB, iAlpha )
iCounter++
iCounter++ // space
ENDIF
ENDIF
ENDIF
ENDIF
ENDPROC
#ENDIF