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

988 lines
33 KiB
Python
Executable File

//////////////////////////////////////////////////////////////////////////////////////////
// //
// SCRIPT NAME : stats_controller.sc //
// AUTHOR : Kenneth Ross, Imran Scriptmaster making a comeback Sarwar //
// DESCRIPTION : Sets up the default stat data for each player character. //
// Displays stat updates whenever a player RPG stat has been //
// modified. //
// //
//////////////////////////////////////////////////////////////////////////////////////////
USING "rage_builtins.sch"
USING "globals.sch"
USING "commands_script.sch"
USING "stats_public.sch"
USING "stats_private.sch"
USING "player_ped_public.sch"
USING "context_control_public.sch"
USING "ambient_common.sch"
USING "net_rank_rewards.sch"
TWEAK_INT STAT_UPDATE_ALL_WHEELS_COOL_DOWN_TIME 1000
TWEAK_INT STAT_UPDATE_ALL_WHEELS_AIR_TIME 1500
TWEAK_INT STAT_UPDATE_ALL_WHEELS_LAND_TIME 20
ENUM STAS_INIT_ENUM
INITIALISED_IN_SP,
#IF FEATURE_COPS_N_CROOKS
INITIALISED_IN_MP_ARCADE_CNC,
#ENDIF
#IF FEATURE_ENDLESS_WINTER
INITIALISED_IN_MP_ARCADE_ENDLESS_WINTER,
#ENDIF
INITIALISED_IN_MP_FREEMODE,
NOT_INITIALISED
ENDENUM
STAS_INIT_ENUM eInit = NOT_INITIALISED
ENUM STAGE_CONTROL_ENUM
SC_STAGE_INIT = 0,
SC_STAGE_UPDATE,
SC_STAGE_RESET
ENDENUM
STAGE_CONTROL_ENUM eStage = SC_STAGE_INIT
INT iCurrentStat
INT iStatUpdateDelay
TIME_DATATYPE tdStatUpdateDelay
enumCharacterList eCurrentStatPed
BOOL bProcessAllStatsThisFrame
BOOL bBlockStatIncrement
BOOL bProcessFeed
enumCharacterList eFeedChar
PLAYER_STATS_ENUM eFeedStat
INT iFeedTotal
INT iFeedCurrent
// Landing on all 4s check
BOOL bPlayerLanding_CheckLand
BOOL bPlayerLanding_CheckLand2
INT iPlayerLanding_AirTimer
TIME_DATATYPE tdPlayerLanding_AirTimer
INT iPlayerLanding_LandTimer
TIME_DATATYPE tdPlayerLanding_LandTimer
INT iPlayerLanding_CoolDownTimer
TIME_DATATYPE tdPlayerLanding_CoolDownTimer
// Top speed
BOOL bTopSpeed_Check
INT iTopSpeed_CoolDownTimer
TIME_DATATYPE tdTopSpeed_CoolDownTimer
// Wheelies
INT iWheelieStatAtStartOfDay
INT iWheelieStatUpdates
INT iWheelieDayTimer
BOOL bWheelieStartTimeSet
BOOL bWheelieTrackAllowed
/* Vehicle repair script - vehicle repair is to be removed
INT iVehicleRepairControl
BOOL bVehicleRepairScriptRequested
INT iVehicleRepairContextID = NEW_CONTEXT_INTENTION
VEHICLE_INDEX vehID_Repair
*/
BOOL bFeedDelayTimerSet
INT iFeedDelayTimer
TIME_DATATYPE tdFeedDelayTimer
#IF IS_DEBUG_BUILD
BOOL bKillPlayerCar = FALSE
BOOL bUndrivePlayerCar = FALSE
BOOL bBoostDrivingStat = FALSE
BOOL bBashBoostDrivingStat = FALSE
#ENDIF
/// PURPOSE: Works out what the stat value should be based on code stats
FUNC BOOL HAS_PLAYER_STAT_VALUE_INCREASED(enumCharacterList ePed, PLAYER_STATS_ENUM ePlayerStat, INT iCurrentStatValue, INT &iNewStatValue)
iNewStatValue = CALCULATE_PLAYER_STAT_VALUE(ePed, ePlayerStat, FALSE)
IF iNewStatValue > iCurrentStatValue
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
PROC STAT_CHECK_4_WHEEL_LANDING(VEHICLE_INDEX &vehID, BOOL &bVehicleSafe)
BOOL bClearFlagsAndSetCoolDown = FALSE
IF bVehicleSafe
IF bPlayerLanding_CheckLand
// Player no longer in the air so check how long they were up for
IF NOT IS_ENTITY_IN_AIR(vehID)
IF (NOT NETWORK_IS_GAME_IN_PROGRESS() AND (GET_GAME_TIMER() - iPlayerLanding_AirTimer > STAT_UPDATE_ALL_WHEELS_AIR_TIME))
OR (NETWORK_IS_GAME_IN_PROGRESS() AND IS_TIME_MORE_THAN(GET_NETWORK_TIME(), GET_TIME_OFFSET(tdPlayerLanding_AirTimer, STAT_UPDATE_ALL_WHEELS_AIR_TIME)))
iPlayerLanding_LandTimer = GET_GAME_TIMER()
IF NETWORK_IS_GAME_IN_PROGRESS()
tdPlayerLanding_LandTimer = GET_NETWORK_TIME()
ENDIF
bPlayerLanding_CheckLand2 = TRUE
ENDIF
bPlayerLanding_CheckLand = FALSE
ENDIF
ENDIF
IF bPlayerLanding_CheckLand2
IF NOT IS_ENTITY_IN_AIR(vehID)
IF IS_VEHICLE_ON_ALL_WHEELS(vehID)
IF (NOT NETWORK_IS_GAME_IN_PROGRESS() AND (GET_GAME_TIMER() - iPlayerLanding_LandTimer > STAT_UPDATE_ALL_WHEELS_LAND_TIME))
OR (NOT NETWORK_IS_GAME_IN_PROGRESS() AND (GET_GAME_TIMER() - iPlayerLanding_LandTimer = 0))
OR (NETWORK_IS_GAME_IN_PROGRESS() AND IS_TIME_MORE_THAN(GET_NETWORK_TIME(), GET_TIME_OFFSET(tdPlayerLanding_LandTimer, STAT_UPDATE_ALL_WHEELS_LAND_TIME)))
OR (NETWORK_IS_GAME_IN_PROGRESS() AND IS_TIME_EQUAL_TO(GET_NETWORK_TIME(), tdPlayerLanding_LandTimer))
// Failed to land in specified time
ELSE
// Managed to land in specificed time, woohoo
INCREMENT_PLAYER_PED_STAT(eCurrentStatPed, PS_DRIVING_ABILITY, 1)
ENDIF
TEXT_LABEL_63 tlTimeToLand = "Player landed on all wheels in "
tlTimeToLand += (GET_GAME_TIMER() - iPlayerLanding_LandTimer)
tlTimeToLand += "ms"
PRINTLN(tlTimeToLand)
bClearFlagsAndSetCoolDown = TRUE
ENDIF
ELSE
bClearFlagsAndSetCoolDown = TRUE
ENDIF
ENDIF
IF NOT bPlayerLanding_CheckLand
AND NOT bPlayerLanding_CheckLand2
IF (NOT NETWORK_IS_GAME_IN_PROGRESS() AND (GET_GAME_TIMER() - iPlayerLanding_CoolDownTimer > STAT_UPDATE_ALL_WHEELS_COOL_DOWN_TIME))
OR (NETWORK_IS_GAME_IN_PROGRESS() AND IS_TIME_MORE_THAN(GET_NETWORK_TIME(), GET_TIME_OFFSET(tdPlayerLanding_CoolDownTimer, STAT_UPDATE_ALL_WHEELS_COOL_DOWN_TIME)))
IF IS_ENTITY_IN_AIR(vehID)
// Player is in air so take a time stamp
IF NOT bPlayerLanding_CheckLand
iPlayerLanding_AirTimer = GET_GAME_TIMER()
IF NETWORK_IS_GAME_IN_PROGRESS()
tdPlayerLanding_AirTimer = GET_NETWORK_TIME()
ENDIF
bPlayerLanding_CheckLand = TRUE
ENDIF
ENDIF
ENDIF
ENDIF
ELSE
IF bPlayerLanding_CheckLand
OR bPlayerLanding_CheckLand2
bClearFlagsAndSetCoolDown = TRUE
ENDIF
ENDIF
IF bClearFlagsAndSetCoolDown
iPlayerLanding_CoolDownTimer = GET_GAME_TIMER()
IF NETWORK_IS_GAME_IN_PROGRESS()
tdPlayerLanding_CoolDownTimer = GET_NETWORK_TIME()
ENDIF
bPlayerLanding_CheckLand = FALSE
bPlayerLanding_CheckLand2 = FALSE
ENDIF
ENDPROC
PROC STAT_CHECK_TOP_SPEED(VEHICLE_INDEX &vehID, BOOL &bVehicleSafe)
IF bTopSpeed_Check
BOOL bResetFlag = TRUE
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
AND bVehicleSafe
AND NOT IS_ENTITY_IN_AIR(vehID)
bResetFlag = FALSE
IF GET_ENTITY_SPEED(vehID) >= 53.0
INCREMENT_PLAYER_PED_STAT(eCurrentStatPed, PS_DRIVING_ABILITY, 2)
bResetFlag = TRUE
ENDIF
ENDIF
IF bResetFlag
bTopSpeed_Check = FALSE
iTopSpeed_CoolDownTimer = GET_GAME_TIMER()
IF NETWORK_IS_GAME_IN_PROGRESS()
tdTopSpeed_CoolDownTimer = GET_NETWORK_TIME()
ENDIF
ENDIF
ELSE
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
AND bVehicleSafe
AND GET_ENTITY_SPEED(vehID) < 20.0
AND NOT IS_ENTITY_IN_AIR(vehID)
IF (NOT NETWORK_IS_GAME_IN_PROGRESS() AND (GET_GAME_TIMER() - iTopSpeed_CoolDownTimer > 10000))
OR (NETWORK_IS_GAME_IN_PROGRESS() AND IS_TIME_MORE_THAN(GET_NETWORK_TIME(), GET_TIME_OFFSET(tdTopSpeed_CoolDownTimer, 10000)))
bTopSpeed_Check = TRUE
ENDIF
ENDIF
ENDIF
ENDPROC
PROC STAT_CHECK_WHEELIE()
IF NOT bWheelieStartTimeSet
IF eCurrentStatPed = CHAR_MULTIPLAYER
IF g_savedMPGlobalsNew.g_savedMPGlobals[GET_SAVE_GAME_ARRAY_SLOT()].MpSavedGeneral.bGrabCurrentWheelieTime
iWheelieStatAtStartOfDay = GET_TOTAL_NUMBER_OF_SECONDS_FOR_UNSIGNED_INT_STAT(GET_STAT_FROM_MP_INT_CHARACTER_STAT(MP_STAT_TOTAL_WHEELIE_TIME))
g_savedMPGlobalsNew.g_savedMPGlobals[GET_SAVE_GAME_ARRAY_SLOT()].MpSavedGeneral.bGrabCurrentWheelieTime = FALSE
ELSE
iWheelieStatAtStartOfDay = g_savedMPGlobalsNew.g_savedMPGlobals[GET_SAVE_GAME_ARRAY_SLOT()].MpSavedGeneral.iWheelieTimeAtStartOfDay
ENDIF
IF g_savedMPGlobalsNew.g_savedMPGlobals[GET_SAVE_GAME_ARRAY_SLOT()].MpSavedGeneral.iWheelieDayTimer = 0
g_savedMPGlobalsNew.g_savedMPGlobals[GET_SAVE_GAME_ARRAY_SLOT()].MpSavedGeneral.iWheelieDayTimer = GET_CLOUD_TIME_AS_INT()
ENDIF
iWheelieDayTimer = g_savedMPGlobalsNew.g_savedMPGlobals[GET_SAVE_GAME_ARRAY_SLOT()].MpSavedGeneral.iWheelieDayTimer
iWheelieStatUpdates = g_savedMPGlobalsNew.g_savedMPGlobals[GET_SAVE_GAME_ARRAY_SLOT()].MpSavedGeneral.iWheelieUpdatesThisDay
ELSE
IF g_savedGlobals.sPlayerData.sInfo.bGrabCurrentWheelieTime[eCurrentStatPed]
SWITCH eCurrentStatPed
CASE CHAR_MICHAEL
iWheelieStatAtStartOfDay = GET_TOTAL_NUMBER_OF_SECONDS_FOR_UNSIGNED_INT_STAT(SP0_TOTAL_WHEELIE_TIME)
BREAK
CASE CHAR_FRANKLIN
iWheelieStatAtStartOfDay = GET_TOTAL_NUMBER_OF_SECONDS_FOR_UNSIGNED_INT_STAT(SP1_TOTAL_WHEELIE_TIME)
BREAK
CASE CHAR_TREVOR
iWheelieStatAtStartOfDay = GET_TOTAL_NUMBER_OF_SECONDS_FOR_UNSIGNED_INT_STAT(SP2_TOTAL_WHEELIE_TIME)
BREAK
ENDSWITCH
g_savedGlobals.sPlayerData.sInfo.bGrabCurrentWheelieTime[eCurrentStatPed] = FALSE
ELSE
iWheelieStatAtStartOfDay = g_savedGlobals.sPlayerData.sInfo.iWheelieTimeAtStartOfDay[eCurrentStatPed]
ENDIF
IF g_savedGlobals.sPlayerData.sInfo.iWheelieDayTimer[eCurrentStatPed] = 0
g_savedGlobals.sPlayerData.sInfo.iWheelieDayTimer[eCurrentStatPed] = GET_CLOUD_TIME_AS_INT()
ENDIF
iWheelieDayTimer = g_savedGlobals.sPlayerData.sInfo.iWheelieDayTimer[eCurrentStatPed]
iWheelieStatUpdates = g_savedGlobals.sPlayerData.sInfo.iWheelieUpdatesThisDay[eCurrentStatPed]
ENDIF
bWheelieStartTimeSet = TRUE
bWheelieTrackAllowed = TRUE
ELSE
#IF IS_DEBUG_BUILD
IF IS_KEYBOARD_KEY_JUST_PRESSED(KEY_PLUS)
iWheelieDayTimer -= (60*60)
ELIF IS_KEYBOARD_KEY_JUST_PRESSED(KEY_MINUS)
iWheelieDayTimer += (60*60)
ENDIF
#ENDIF
// Reset some stats each time a day passes
IF (GET_CLOUD_TIME_AS_INT() - iWheelieDayTimer) > (86400)
bWheelieStartTimeSet = FALSE
bWheelieTrackAllowed = FALSE
iWheelieDayTimer = GET_CLOUD_TIME_AS_INT()
iWheelieStatUpdates = 0
IF eCurrentStatPed = CHAR_MULTIPLAYER
g_savedMPGlobalsNew.g_savedMPGlobals[GET_SAVE_GAME_ARRAY_SLOT()].MpSavedGeneral.bGrabCurrentWheelieTime = TRUE
ELSE
g_savedGlobals.sPlayerData.sInfo.bGrabCurrentWheelieTime[eCurrentStatPed] = TRUE
ENDIF
ENDIF
ENDIF
IF bWheelieTrackAllowed
INT iWheelieStatCurrent
SWITCH eCurrentStatPed
CASE CHAR_MICHAEL
iWheelieStatCurrent = GET_TOTAL_NUMBER_OF_SECONDS_FOR_UNSIGNED_INT_STAT(SP0_TOTAL_WHEELIE_TIME)
BREAK
CASE CHAR_FRANKLIN
iWheelieStatCurrent = GET_TOTAL_NUMBER_OF_SECONDS_FOR_UNSIGNED_INT_STAT(SP1_TOTAL_WHEELIE_TIME)
BREAK
CASE CHAR_TREVOR
iWheelieStatCurrent = GET_TOTAL_NUMBER_OF_SECONDS_FOR_UNSIGNED_INT_STAT(SP2_TOTAL_WHEELIE_TIME)
BREAK
CASE CHAR_MULTIPLAYER
iWheelieStatCurrent = GET_TOTAL_NUMBER_OF_SECONDS_FOR_UNSIGNED_INT_STAT(GET_STAT_FROM_MP_INT_CHARACTER_STAT(MP_STAT_TOTAL_WHEELIE_TIME))
BREAK
ENDSWITCH
INT iStatDifference = (iWheelieStatCurrent-iWheelieStatAtStartOfDay)
INT iStatIncrememnt = FLOOR((TO_FLOAT(iStatDifference)/15))-iWheelieStatUpdates
// Give 1% boost for every 15 seconds of wheelie
IF (iStatIncrememnt > 0)
INCREMENT_PLAYER_PED_STAT(eCurrentStatPed, PS_DRIVING_ABILITY, iStatIncrememnt)
iWheelieStatUpdates += iStatIncrememnt
ENDIF
// Only allow the player to get this stat for 1 in-game hour worth of wheelie
IF (iWheelieStatCurrent > (iWheelieStatAtStartOfDay+((GET_MILLISECONDS_PER_GAME_MINUTE()/1000)*60)))
bWheelieTrackAllowed = FALSE
ENDIF
ENDIF
// Update save data
IF eCurrentStatPed = CHAR_MULTIPLAYER
g_savedMPGlobalsNew.g_savedMPGlobals[GET_SAVE_GAME_ARRAY_SLOT()].MpSavedGeneral.iWheelieDayTimer = iWheelieDayTimer
g_savedMPGlobalsNew.g_savedMPGlobals[GET_SAVE_GAME_ARRAY_SLOT()].MpSavedGeneral.iWheelieUpdatesThisDay = iWheelieStatUpdates
ELSE
g_savedGlobals.sPlayerData.sInfo.iWheelieDayTimer[eCurrentStatPed] = iWheelieDayTimer
g_savedGlobals.sPlayerData.sInfo.iWheelieUpdatesThisDay[eCurrentStatPed] = iWheelieStatUpdates
ENDIF
ENDPROC
PROC DO_PROCESS_SCRIPT_STAT_UPDATES()
VEHICLE_INDEX vehID
BOOL bVehicleSafe = FALSE
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
vehID = GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID())
IF DOES_ENTITY_EXIST(vehID)
AND IS_VEHICLE_DRIVEABLE(vehID)
AND GET_PED_IN_VEHICLE_SEAT(vehID, VS_DRIVER) = PLAYER_PED_ID()
AND (IS_THIS_MODEL_A_CAR(GET_ENTITY_MODEL(vehID)) OR IS_THIS_MODEL_A_BIKE(GET_ENTITY_MODEL(vehID)) OR IS_THIS_MODEL_A_BICYCLE(GET_ENTITY_MODEL(vehID)) OR IS_THIS_MODEL_A_QUADBIKE(GET_ENTITY_MODEL(vehID)))
bVehicleSafe = TRUE
ENDIF
ENDIF
STAT_CHECK_4_WHEEL_LANDING(vehID, bVehicleSafe)
STAT_CHECK_TOP_SPEED(vehID, bVehicleSafe)
STAT_CHECK_WHEELIE()
ENDPROC
/// PURPOSE: Reset data and make copy of current stat values
PROC DO_INITIALISE()
IF GET_INDEX_OF_CURRENT_LEVEL() != LEVEL_GTA5
#IF IS_DEBUG_BUILD
IF g_bDisplayShopDebug
PRINTSTRING("\n stats_controller: Init - not in LEVEL_GTA5.")PRINTNL()
ENDIF
#ENDIF
EXIT
ENDIF
// Wait for MP player to be in main game
IF NETWORK_IS_GAME_IN_PROGRESS()
IF NOT IS_PLAYER_ACTIVE_IN_MP_LAUNCH_SCRIPT(PLAYER_ID())
EXIT
ENDIF
IF GET_CURRENT_GAMEMODE() != GAMEMODE_FM
EXIT
ENDIF
ENDIF
IF NETWORK_IS_GAME_IN_PROGRESS()
IF GET_CURRENT_GAMEMODE() = GAMEMODE_FM
#IF FEATURE_FREEMODE_ARCADE
IF IS_FREEMODE_ARCADE()
SWITCH GET_ARCADE_MODE()
#IF FEATURE_COPS_N_CROOKS
CASE ARC_COPS_CROOKS
eInit = INITIALISED_IN_MP_ARCADE_CNC
PRINTSTRING("\n stats_controller: INITIALISING in FM Arcade - CNC")PRINTNL()
BREAK
#ENDIF
#IF FEATURE_ENDLESS_WINTER
CASE ARC_ENDLESS_WINTER
eInit = INITIALISED_IN_MP_ARCADE_ENDLESS_WINTER
PRINTSTRING("\n stats_controller: INITIALISING in FM Arcade - Endless winter")PRINTNL()
BREAK
#ENDIF
DEFAULT
SCRIPT_ASSERT("DO_INITIALISE - Unknown arcade mode!")
BREAK
ENDSWITCH
ELSE
#ENDIF
eInit = INITIALISED_IN_MP_FREEMODE
PRINTSTRING("\n stats_controller: INITIALISING in FM")PRINTNL()
#IF FEATURE_FREEMODE_ARCADE
ENDIF
#ENDIF
ENDIF
ELSE
eInit = INITIALISED_IN_SP
PRINTSTRING("\n stats_controller: INITIALISING in SP")PRINTNL()
ENDIF
IF NOT HAVE_DEFAULT_PLAYER_STATS_BEEN_SET()
SETUP_DEFAULT_PLAYER_STATS()
ENDIF
iCurrentStat = 0
iStatUpdateDelay = 0
IF NETWORK_IS_GAME_IN_PROGRESS()
tdStatUpdateDelay = GET_NETWORK_TIME()
ENDIF
iPlayerLanding_CoolDownTimer = GET_GAME_TIMER()
IF NETWORK_IS_GAME_IN_PROGRESS()
tdPlayerLanding_CoolDownTimer = GET_NETWORK_TIME()
ENDIF
// Fix for Strength stat getting reset back to 0
IF NETWORK_IS_GAME_IN_PROGRESS()
IF NOT IS_BIT_SET(g_savedMPGlobalsNew.g_savedMPGlobals[GET_SAVE_GAME_ARRAY_SLOT()].MpSavedGeneral.iGeneralBS,MP_SAVE_GENERAL_BS_STRENGTH_PATCHED)
FLOAT fFinalStatValue = (TO_FLOAT(GET_MP_INT_CHARACTER_STAT(MP_STAT_UNARMED_HITS)) / 20)
INT iFinalStatValue = FLOOR(fFinalStatValue)
iFinalStatValue = CLAMP_INT(iFinalStatValue, 0, 100)
INCREMENT_PLAYER_PED_STAT(CHAR_MULTIPLAYER, PS_STRENGTH, iFinalStatValue)
SET_BIT(g_savedMPGlobalsNew.g_savedMPGlobals[GET_SAVE_GAME_ARRAY_SLOT()].MpSavedGeneral.iGeneralBS,MP_SAVE_GENERAL_BS_STRENGTH_PATCHED)
PRINTLN("STRENGTH STAT HAS BEEN PATCHED - ", iFinalStatValue)
ENDIF
ENDIF
bProcessAllStatsThisFrame = TRUE
eStage = SC_STAGE_UPDATE
ENDPROC
PROC DO_FEED_UPDATE_FOR_STAT()
IF bProcessFeed
IF eFeedChar != eCurrentStatPed
bProcessFeed = FALSE
EXIT
ENDIF
STRING sChar = "CHAR_DEFAULT"
SWITCH eFeedChar
CASE CHAR_MICHAEL sChar = "CHAR_MICHAEL" BREAK
CASE CHAR_FRANKLIN sChar = "CHAR_FRANKLIN" BREAK
CASE CHAR_TREVOR sChar = "CHAR_TREVOR" BREAK
CASE CHAR_MULTIPLAYER
PEDHEADSHOT_ID theHeadshotID
theHeadshotID = Get_HeadshotID_For_Player(PLAYER_ID())
IF (theHeadshotID = NULL)
// wait for this to generate...
EXIT
ELSE
sChar = GET_PEDHEADSHOT_TXD_STRING(theHeadshotID)
ENDIF
BREAK
ENDSWITCH
PRINTLN("DO_FEED_UPDATE_FOR_STAT")
PRINTLN("...iFeedTotal = ", iFeedTotal)
PRINTLN("...iFeedCurrent = ", iFeedCurrent)
BEGIN_TEXT_COMMAND_THEFEED_POST("PS_UPDATE")
ADD_TEXT_COMPONENT_INTEGER(iFeedCurrent)
END_TEXT_COMMAND_THEFEED_POST_STATS(GET_PLAYER_STAT_FEED_NAME(eFeedStat), GET_PLAYER_STAT_FEED_ICON(eFeedStat), iFeedTotal, iFeedCurrent-iFeedTotal, FALSE, sChar, sChar)
// Fix fo bug #1370214 - Can we have it so the stats don't increase all at the same time.
// Default time is 3000ms so make sure we have a 6000ms delay between feed updates.
bFeedDelayTimerSet = TRUE
iFeedDelayTimer = GET_GAME_TIMER()+5000
IF NETWORK_IS_GAME_IN_PROGRESS()
tdFeedDelayTimer = GET_TIME_OFFSET(GET_NETWORK_TIME(), 5000)
ENDIF
bProcessFeed = FALSE
ENDIF
ENDPROC
/// PURPOSE: Check for any player RPG stats getting updated
PROC DO_CHECK_FOR_STAT_UPDATE()
// We only process one stat per frame so increment
IF bBlockStatIncrement
bBlockStatIncrement = FALSE
ELSE
iCurrentStat++
ENDIF
bProcessAllStatsThisFrame = FALSE
// Determine which character we need to process
enumCharacterList ePedToProcess
IF NETWORK_IS_GAME_IN_PROGRESS()
ePedToProcess = CHAR_MULTIPLAYER
ELSE
ePedToProcess = GET_CURRENT_PLAYER_PED_ENUM()
ENDIF
IF ePedToProcess != eCurrentStatPed
eCurrentStatPed = ePedToProcess
// Process all stats now
bProcessAllStatsThisFrame = TRUE
// Clear some flags
bPlayerLanding_CheckLand = FALSE
bPlayerLanding_CheckLand2 = FALSE
bTopSpeed_Check = FALSE
bWheelieStartTimeSet = FALSE
ENDIF
// Switching sessions so wait.
IF eCurrentStatPed = NO_CHARACTER
EXIT
ENDIF
// Bail out if it's not safe to process stats
BOOL bIgnoreBailChecks = FALSE
IF ((iCurrentStat = ENUM_TO_INT(PS_SHOOTING_ABILITY)) AND IS_SHOOTING_RANGE_UNSAVED_BITFLAG_SET(SRB_RangeInSession))
OR ((iCurrentStat = ENUM_TO_INT(PS_FLYING_ABILITY)) AND GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH(HASH("pilot_school")) > 0)
bIgnoreBailChecks = TRUE
bBlockStatIncrement = TRUE
ENDIF
// See if we can increment stats
#IF FEATURE_FREEMODE_ARCADE
IF NOT IS_FREEMODE_ARCADE()
#ENDIF
DO_PROCESS_SCRIPT_STAT_UPDATES()
#IF FEATURE_FREEMODE_ARCADE
ENDIF
#ENDIF
// Perform any routine updates
IF (g_bPerformCustomStatUpdate)
OR (g_bPerformCustomStatUpdateWhenMissionEnds AND NOT IS_CURRENTLY_ON_MISSION_TO_TYPE())
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
INT iCustomStatUpdate
REPEAT NUMBER_OF_PLAYER_STATS iCustomStatUpdate
UPDATE_PLAYER_STAT_SETTINGS(eCurrentStatPed, INT_TO_ENUM(PLAYER_STATS_ENUM, iCustomStatUpdate))
ENDREPEAT
g_bPerformCustomStatUpdate = FALSE
ENDIF
ENDIF
IF NOT bIgnoreBailChecks
IF IS_PED_INJURED(PLAYER_PED_ID())
OR (NOT NETWORK_IS_GAME_IN_PROGRESS() AND NOT IS_PLAYER_PED_PLAYABLE(eCurrentStatPed))
OR IS_PLAYER_SWITCH_IN_PROGRESS()
OR IS_PLAYER_PED_SWITCH_IN_PROGRESS()
OR IS_CUTSCENE_PLAYING()
OR IS_SCREEN_FADED_OUT()
OR NOT IS_PLAYER_CONTROL_ON(PLAYER_ID())
OR g_bScriptsSetSafeForCutscene
OR g_bDisableStatUpdatesThisFrame
OR bProcessFeed
/*IF IS_PED_INJURED(PLAYER_PED_ID())
PRINTLN("Not safe to display stats - ped injured")
ELIF (NOT NETWORK_IS_GAME_IN_PROGRESS() AND NOT IS_PLAYER_PED_PLAYABLE(eCurrentStatPed))
PRINTLN("Not safe to display stats - not playable character")
ELIF IS_PLAYER_SWITCH_IN_PROGRESS()
PRINTLN("Not safe to display stats - switching (code)")
ELIF IS_PLAYER_PED_SWITCH_IN_PROGRESS()
PRINTLN("Not safe to display stats - switching (script)")
ELIF IS_CUTSCENE_PLAYING()
PRINTLN("Not safe to display stats - cutscene playing")
ELIF NOT IS_PLAYER_CONTROL_ON(PLAYER_ID())
PRINTLN("Not safe to display stats - no player control")
ELIF g_bScriptsSetSafeForCutscene
PRINTLN("Not safe to display stats - cutscene flag set")
ENDIF*/
bFeedDelayTimerSet = TRUE
iFeedDelayTimer = GET_GAME_TIMER()
IF NETWORK_IS_GAME_IN_PROGRESS()
tdFeedDelayTimer = GET_NETWORK_TIME()
ENDIF
EXIT
ENDIF
IF (bFeedDelayTimerSet)
IF (NOT NETWORK_IS_GAME_IN_PROGRESS() AND GET_GAME_TIMER() - iFeedDelayTimer > 3000)
OR (NETWORK_IS_GAME_IN_PROGRESS() AND IS_TIME_MORE_THAN(GET_NETWORK_TIME(), GET_TIME_OFFSET(tdFeedDelayTimer, 3000)))
bFeedDelayTimerSet = FALSE
ELSE
//PRINTLN("Not safe to display stats - timer delay set")
EXIT
ENDIF
ENDIF
ENDIF
// Wait for the delay to pass
IF (NOT NETWORK_IS_GAME_IN_PROGRESS() AND ((GET_GAME_TIMER() - iStatUpdateDelay) > 2000))
OR (NETWORK_IS_GAME_IN_PROGRESS() AND (IS_TIME_MORE_THAN(GET_NETWORK_TIME(), GET_TIME_OFFSET(tdStatUpdateDelay, 2000))))
OR bProcessAllStatsThisFrame
// If we have done a loop, delay the next stat process.
IF iCurrentStat >= NUMBER_OF_PLAYER_STATS
iCurrentStat = 0
iStatUpdateDelay = GET_GAME_TIMER()
IF NETWORK_IS_GAME_IN_PROGRESS()
tdStatUpdateDelay = GET_NETWORK_TIME()
ENDIF
ENDIF
INT iCurrentStatValue, iNewStatValue, iCurrentStatValuePreClamp
STATSENUM eSPStat
MP_INT_STATS eMPStat
PLAYER_STATS_ENUM eStatToProcess
STATSENUM eSPMaxTimeStat
INT iTime
INT iStatsToProcess = 1
IF bProcessAllStatsThisFrame
iStatsToProcess = NUMBER_OF_PLAYER_STATS
ENDIF
INT iStatLoop
REPEAT iStatsToProcess iStatLoop
BOOL bDisplayStatUpdate = TRUE
IF bProcessAllStatsThisFrame
eStatToProcess = INT_TO_ENUM(PLAYER_STATS_ENUM, iStatLoop)
ELSE
eStatToProcess = INT_TO_ENUM(PLAYER_STATS_ENUM, iCurrentStat)
ENDIF
// Grab the stat value
GET_PLAYER_PED_STAT_ENUM(eCurrentStatPed, eStatToProcess, eSPStat, eMPStat)
IF NETWORK_IS_GAME_IN_PROGRESS()
iCurrentStatValue = GET_MP_INT_CHARACTER_STAT(eMPStat)
ELSE
IF eStatToProcess = PS_SPECIAL_ABILITY
iCurrentStatValue = g_savedGlobals.sPlayerData.sInfo.iTrackedSpecialAbilityUnlock[eCurrentStatPed]
ELSE
STAT_GET_INT(eSPStat, iCurrentStatValue)
ENDIF
ENDIF
// Keep value in the range of 0-100
iCurrentStatValuePreClamp = iCurrentStatValue
iCurrentStatValue = CLAMP_INT(iCurrentStatValue, 0, 100)
// Update stat if it has changed
IF HAS_PLAYER_STAT_VALUE_INCREASED(eCurrentStatPed, eStatToProcess, iCurrentStatValue, iNewStatValue)
IF NETWORK_IS_GAME_IN_PROGRESS()
SET_MP_INT_CHARACTER_STAT(eMPStat, iNewStatValue)
ELSE
IF eStatToProcess = PS_SPECIAL_ABILITY
IF g_savedGlobals.sPlayerData.sInfo.iTrackedSpecialAbilityUnlock[eCurrentStatPed] = 0
// Ignore first speical ability stat update.
bDisplayStatUpdate = FALSE
ENDIF
g_savedGlobals.sPlayerData.sInfo.iTrackedSpecialAbilityUnlock[eCurrentStatPed] = iNewStatValue
ELSE
STAT_SET_INT(eSPStat, iNewStatValue)
ENDIF
ENDIF
#IF IS_DEBUG_BUILD
PRINTLN(GET_STRING_FROM_TEXT_FILE(GET_PLAYER_STAT_FEED_NAME(eStatToProcess)), " has increased from ", iCurrentStatValue, " to ", iNewStatValue, " for ", GET_PLAYER_PED_STRING(eCurrentStatPed))
#ENDIF
// Display on the feed
IF iNewStatValue > GET_INITIAL_STAT_VALUE(eCurrentStatPed, eStatToProcess)
IF NETWORK_IS_GAME_IN_PROGRESS()
#IF FEATURE_FREEMODE_ARCADE
IF IS_FREEMODE_ARCADE()
bDisplayStatUpdate = FALSE
ENDIF
#ENDIF
IF NETWORK_IS_ACTIVITY_SESSION()
AND IS_BIT_SET(g_FMMC_STRUCT.iOptionsMenuBitSetTwentyNine, ciOptionsBS29_SimulateSinglePlayerHUD)
bDisplayStatUpdate = FALSE
ENDIF
ELSE
IF (NOT g_savedGlobals.sFlow.isGameflowActive AND NOT IS_REPEAT_PLAY_ACTIVE())
OR NOT IS_BIT_SET(g_savedGlobals.sFlow.strandSavedVars[STRAND_PROLOGUE].savedBitflags,SAVED_BITS_STRAND_TERMINATED)
bDisplayStatUpdate = FALSE
ENDIF
ENDIF
// Only display in feed if we have reached a milestone.
INT iFinalStatValue = iNewStatValue
INT iNextStep = (iFinalStatValue-((iFinalStatValue%GET_FEED_UPDATE_FREQ(eCurrentStatPed, eStatToProcess))))
IF (iFinalStatValue%GET_FEED_UPDATE_FREQ(eCurrentStatPed, eStatToProcess) >= 0)
iNextStep += GET_FEED_UPDATE_FREQ(eCurrentStatPed, eStatToProcess)
ENDIF
IF iFinalStatValue >= iNextStep
iFinalStatValue = iNextStep
ELSE
iFinalStatValue = (iNextStep-GET_FEED_UPDATE_FREQ(eCurrentStatPed, eStatToProcess))
ENDIF
IF iNewStatValue > iFinalStatValue
bDisplayStatUpdate = FALSE
ENDIF
IF IS_GOON_ELIGABLE_FOR_STAT_BOOST() // disable feed if its for bonus gang boss stat increase (bug 2595234)
PRINTLN("DO_CHECK_FOR_STAT_UPDATE disable feed if its for bonus gang boss stat increase (bug 2595234) bDisplayStatUpdate = FALSE ")
bDisplayStatUpdate = FALSE
ENDIF
IF bDisplayStatUpdate
IF NOT bProcessAllStatsThisFrame
IF NETWORK_IS_GAME_IN_PROGRESS()
IF GET_MP_BOOL_CHARACTER_STAT(MP_STAT_FM_CHAR_STATS1) = FALSE
IF NOT IS_HELP_MESSAGE_BEING_DISPLAYED()
AND CAN_DRAW_UNLOCKS_SCALEFORM()
PRINT_HELP("STAT_HELP2")
PRINTLN("PRINT_HELP(CHECK_AND_DISPLAY_HELP_TEXT_FOR_STATS STAT_HELP2)")
SET_MP_BOOL_CHARACTER_STAT(MP_STAT_FM_CHAR_STATS1, TRUE)
ENDIF
ENDIF
ENDIF
bProcessFeed = TRUE
iFeedTotal = iNewStatValue-iCurrentStatValue
// Fix for bug 1541985 - flash the whole bar
iFeedTotal += (iCurrentStatValue%GET_FEED_UPDATE_FREQ(eCurrentStatPed, eStatToProcess))
iFeedCurrent = iNewStatValue
eFeedChar = eCurrentStatPed
eFeedStat = eStatToProcess
ENDIF
ENDIF
// Maxed out?
IF iNewStatValue >= 100
IF GET_PLAYER_PED_MAXED_STAT_ENUM(eCurrentStatPed, eStatToProcess, eSPMaxTimeStat)
#IF IS_DEBUG_BUILD
PRINTLN(GET_STRING_FROM_TEXT_FILE(GET_PLAYER_STAT_FEED_NAME(eStatToProcess)), " has maxed out for ", GET_PLAYER_PED_STRING(eCurrentStatPed))
#ENDIF
IF eCurrentStatPed = CHAR_MICHAEL
STAT_GET_INT(SP0_TOTAL_PLAYING_TIME, iTime)
STAT_SET_INT(eSPMaxTimeStat, iTime)
ELIF eCurrentStatPed = CHAR_FRANKLIN
STAT_GET_INT(SP1_TOTAL_PLAYING_TIME, iTime)
STAT_SET_INT(eSPMaxTimeStat, iTime)
ELIF eCurrentStatPed = CHAR_TREVOR
STAT_GET_INT(SP2_TOTAL_PLAYING_TIME, iTime)
STAT_SET_INT(eSPMaxTimeStat, iTime)
ELIF eCurrentStatPed = CHAR_MULTIPLAYER
ENDIF
ENDIF
ENDIF
ENDIF
UPDATE_PLAYER_STAT_SETTINGS(eCurrentStatPed, eStatToProcess)
// If the stat has reduced, set it but do not display on feed
ELIF iNewStatValue < iCurrentStatValuePreClamp
BOOL bUpdateStatSettings = TRUE
IF NETWORK_IS_GAME_IN_PROGRESS()
SET_MP_INT_CHARACTER_STAT(eMPStat, iNewStatValue)
ELSE
IF eStatToProcess = PS_SPECIAL_ABILITY
g_savedGlobals.sPlayerData.sInfo.iTrackedSpecialAbilityUnlock[eCurrentStatPed] = iNewStatValue
bUpdateStatSettings = FALSE
ELSE
STAT_SET_INT(eSPStat, iNewStatValue)
ENDIF
ENDIF
IF bUpdateStatSettings
#IF IS_DEBUG_BUILD
PRINTLN(GET_STRING_FROM_TEXT_FILE(GET_PLAYER_STAT_FEED_NAME(eStatToProcess)), " has decreased from ", iCurrentStatValue, " to ", iNewStatValue, " for ", GET_PLAYER_PED_STRING(eCurrentStatPed))
#ENDIF
UPDATE_PLAYER_STAT_SETTINGS(eCurrentStatPed, eStatToProcess)
ENDIF
ENDIF
ENDREPEAT
ELSE
bBlockStatIncrement = TRUE
ENDIF
ENDPROC
/* [AG]: VEHICLE REPAIR IS BEING REMOVED
/// PURPOSE: Launch the vehicle repair minigame when player is trying to interact with a broken down vehicle
PROC DO_VEHICLE_REPAIR_PROCESSING()
BOOL bDisplayContext = FALSE
BOOL bVehicleRepairScriptRequired = FALSE
SWITCH iVehicleRepairControl
CASE 0
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
AND NOT IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
vehID_Repair = GET_CLOSEST_VEHICLE(GET_ENTITY_COORDS(PLAYER_PED_ID()), 5.0, DUMMY_MODEL_FOR_SCRIPT, VEHICLE_SEARCH_FLAG_RETURN_MISSION_VEHICLES | VEHICLE_SEARCH_FLAG_RETURN_RANDOM_VEHICLES)
IF DOES_ENTITY_EXIST(vehID_Repair)
AND NOT IS_VEHICLE_DRIVEABLE(vehID_Repair)
AND NOT IS_ENTITY_ON_FIRE(vehID_Repair)
AND GET_VEHICLE_ENGINE_HEALTH(vehID_Repair) >= 0
AND IS_VEHICLE_ON_ALL_WHEELS(vehID_Repair)
AND GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) = 0
AND NOT IS_PED_RAGDOLL(PLAYER_PED_ID())
AND NOT IS_ENTITY_IN_WATER(PLAYER_PED_ID())
AND NOT IS_ENTITY_IN_WATER(vehID_Repair)
AND NOT IS_VEHICLE_MODEL_ON_REPAIR_BLACKLIST(GET_ENTITY_MODEL(vehID_Repair))
AND IS_PLAYER_CONTROL_ON(PLAYER_ID())
REQUEST_SCRIPT("vehicleRepair")
bVehicleRepairScriptRequested = TRUE
bVehicleRepairScriptRequired = TRUE
IF HAS_SCRIPT_LOADED("vehicleRepair")
START_NEW_SCRIPT("vehicleRepair", SPECIAL_ABILITY_STACK_SIZE)
iVehicleRepairControl++
ENDIF
ENDIF
ENDIF
BREAK
CASE 1
// Wait for the script to terminate
IF GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH(HASH("vehicleRepair")) = 0
iVehicleRepairControl = 0
ENDIF
BREAK
ENDSWITCH
IF NOT bDisplayContext
IF iVehicleRepairContextID != NEW_CONTEXT_INTENTION
RELEASE_CONTEXT_INTENTION(iVehicleRepairContextID)
iVehicleRepairContextID = NEW_CONTEXT_INTENTION
ENDIF
ENDIF
IF NOT bVehicleRepairScriptRequired
AND bVehicleRepairScriptRequested
SET_SCRIPT_AS_NO_LONGER_NEEDED("vehicleRepair")
bVehicleRepairScriptRequested = FALSE
ENDIF
ENDPROC
*/
PROC DO_RESET()
eStage = SC_STAGE_INIT
PRINTSTRING("\n stats_controller: RESET")PRINTNL()
ENDPROC
/// PURPOSE: Checks to see if the stats controller should be re-initialised
PROC CHECK_CONTROLLER_RESET()
IF eStage != SC_STAGE_INIT
AND eInit != NOT_INITIALISED
// Reset if we have changed game modes
IF eInit = INITIALISED_IN_SP
IF NETWORK_IS_GAME_IN_PROGRESS()
eInit = NOT_INITIALISED
ENDIF
IF NOT HAVE_DEFAULT_PLAYER_STATS_BEEN_SET()
eInit = NOT_INITIALISED
ENDIF
ELIF eInit = INITIALISED_IN_MP_FREEMODE
#IF FEATURE_COPS_N_CROOKS
OR eInit = INITIALISED_IN_MP_ARCADE_CNC
#ENDIF
IF NOT NETWORK_IS_GAME_IN_PROGRESS()
OR NOT IS_PLAYER_ACTIVE_IN_MP_LAUNCH_SCRIPT(PLAYER_ID())
OR GET_CURRENT_GAMEMODE() != GAMEMODE_FM
eInit = NOT_INITIALISED
ENDIF
ENDIF
// Jump back to the initialisation stage if we have switched game modes
IF eInit = NOT_INITIALISED
eStage = SC_STAGE_RESET
ENDIF
ENDIF
ENDPROC
SCRIPT
PRINTSTRING("\nStarting stats controller")PRINTNL()
NETWORK_SET_SCRIPT_IS_SAFE_FOR_NETWORK_GAME()
// This script needs to cleanup only when run the magdemo
IF (HAS_FORCE_CLEANUP_OCCURRED(FORCE_CLEANUP_FLAG_MAGDEMO))
PRINTSTRING("...stats_controller.sc has been forced to cleanup (MAGDEMO)")
PRINTNL()
TERMINATE_THIS_THREAD()
ENDIF
#IF IS_DEBUG_BUILD
START_WIDGET_GROUP("Stats Controller")
ADD_WIDGET_INT_SLIDER("Cool down time", STAT_UPDATE_ALL_WHEELS_COOL_DOWN_TIME, 0, 10000, 100)
ADD_WIDGET_INT_SLIDER("Air time", STAT_UPDATE_ALL_WHEELS_AIR_TIME, 0, 10000, 100)
ADD_WIDGET_INT_SLIDER("Land time", STAT_UPDATE_ALL_WHEELS_LAND_TIME, 0, 10000, 1)
INT iTempStat
PLAYER_STATS_ENUM eTempStat
REPEAT NUMBER_OF_PLAYER_STATS iTempStat
eTempStat = INT_TO_ENUM(PLAYER_STATS_ENUM, iTempStat)
g_iDebugPlayerStatOffset[iTempStat] = -1
IF eTempStat != PS_SPECIAL_ABILITY
ADD_WIDGET_INT_SLIDER(GET_STRING_FROM_TEXT_FILE(GET_PLAYER_STAT_DISPLAY_NAME(eTempStat)), g_iDebugPlayerStatOffset[iTempStat], -1, 100, 1)
ENDIF
ENDREPEAT
ADD_WIDGET_BOOL("Default stats set", g_savedGlobals.sPlayerData.sInfo.bDefaultStatsSet)
ADD_WIDGET_BOOL("Undrive Player Car", bUndrivePlayerCar)
ADD_WIDGET_BOOL("Kill Player Car", bKillPlayerCar)
ADD_WIDGET_BOOL("Driving Stat +1", bBoostDrivingStat)
ADD_WIDGET_BOOL("Driving Stat -1", bBashBoostDrivingStat)
STOP_WIDGET_GROUP()
#ENDIF
WHILE (TRUE)
CHECK_CONTROLLER_RESET()
#IF IS_DEBUG_BUILD
IF bBoostDrivingStat
INCREMENT_PLAYER_PED_STAT(eCurrentStatPed, PS_DRIVING_ABILITY, 1)
bBoostDrivingStat = FALSE
ENDIF
IF bBashBoostDrivingStat
INCREMENT_PLAYER_PED_STAT(eCurrentStatPed, PS_DRIVING_ABILITY, -1)
bBashBoostDrivingStat = FALSE
ENDIF
IF bKillPlayerCar
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
SET_VEHICLE_ENGINE_HEALTH(GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID()), 0)
ENDIF
ENDIF
bKillPlayerCar = FALSE
ENDIF
IF bUndrivePlayerCar
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
SET_VEHICLE_UNDRIVEABLE(GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID()), TRUE)
ENDIF
ENDIF
bUndrivePlayerCar = FALSE
ENDIF
#ENDIF
SWITCH eStage
CASE SC_STAGE_INIT
DO_INITIALISE()
BREAK
CASE SC_STAGE_UPDATE
DO_FEED_UPDATE_FOR_STAT()
DO_CHECK_FOR_STAT_UPDATE()
g_bDisableStatUpdatesThisFrame = FALSE
// [AG] - vehicle repair needs to be removed
// DO_VEHICLE_REPAIR_PROCESSING()
BREAK
CASE SC_STAGE_RESET
DO_RESET()
BREAK
ENDSWITCH
WAIT(0)
ENDWHILE
ENDSCRIPT