Files
gtav-src/script/dev_ng/singleplayer/scripts/RandomChar/RandomChar_Controller.sc
T
2025-09-29 00:52:08 +02:00

1301 lines
51 KiB
Python
Executable File

//Compile out Title Update changes to header functions.
//Must be before includes.
//CONST_INT USE_TU_CHANGES 0 // Removed by Kenneth R.
USING "rage_builtins.sch"
USING "globals.sch"
USING "commands_script.sch"
USING "script_clock.sch"
USING "script_player.sch"
USING "randomChar_private.sch"
USING "blip_control_public.sch"
USING "flow_public_core.sch"
USING "RC_Setup_public.sch"
// *****************************************************************************************
// *****************************************************************************************
// *****************************************************************************************
//
// MISSION NAME : RandomChar_Controller.sc
// AUTHOR : Keith
// MAINTAINED : Andrew Minghella
// DESCRIPTION : The Random Character Controller - ensures Random Characters
// are allowed to activate at appropriate times.
//
// *****************************************************************************************
// *****************************************************************************************
// *****************************************************************************************
CONST_INT MAX_FRAMES_TO_PROCESS_MISSIONS 60
CONST_INT MAX_RC_BLIPS 20
INT iCurrentMissionFrame
INT iRCMission, iBit, iBitset
BOOL bRCBlipActive[MAX_RC_BLIPS]
STATIC_BLIP_NAME_ENUM eRCBlips[MAX_RC_BLIPS]
BOOL bRCBlipsTurnedOff
TIMEOFDAY eTonya1HelpTime = INVALID_TIMEOFDAY
#IF IS_DEBUG_BUILD
g_eRC_MissionIDs eRCMissionToDebug = NO_RC_MISSION // Set this to print debug info for a particular RC mission only
#ENDIF
// -----------------------DEBUG FUNCTIONS ---------------------------------------------
#IF IS_DEBUG_BUILD
BOOL bOutputStates, bCompleteAll, bUnlockSpecificRC
/// PURPOSE:
/// Prints specified debug string if the RC mission being processed is the one specifed in eRCMissionToDebug
/// PARAMS:
/// eRCMission - the rc mission being processed
/// sDebugString - the string to print
PROC RC_CONTROLLER_DEBUG_PRINT(g_eRC_MissionIDs eRCMission, STRING sDebugString)
IF eRCMissionToDebug <> NO_RC_MISSION
AND eRCMission = eRCMissionToDebug
CPRINTLN(DEBUG_RANDOM_CHAR, sDebugString)
ENDIF
ENDPROC
/// PURPOSE:
/// Creates RC Controller debug widgets
PROC Setup_RC_Debug_Widgets()
START_WIDGET_GROUP("Random Character Controller")
ADD_WIDGET_BOOL("Output states", bOutputStates)
ADD_WIDGET_BOOL("Complete All", bCompleteAll)
ADD_WIDGET_BOOL("Unlock Barry 4", bUnlockSpecificRC)
STOP_WIDGET_GROUP()
ENDPROC
/// PURPOSE:
/// Maintains the RC Controller debug widgets
PROC Maintain_RC_Debug_Widgets()
IF bOutputStates
CPRINTLN(DEBUG_RANDOM_CHAR, "Random Character States")
INT iMission
g_eRC_MissionIDs eRCMission
g_structRCMissionsStatic sRCMissionDetails
TEXT_LABEL_23 tlMissionName
REPEAT MAX_RC_MISSIONS iMission
eRCMission = INT_TO_ENUM(g_eRC_MissionIDs, iMission)
Retrieve_Random_Character_Static_Mission_Details(eRCMission, sRCMissionDetails)
tlMissionName = "'"
tlMissionName += sRCMissionDetails.rcScriptName
tlMissionName += "':"
WHILE GET_LENGTH_OF_LITERAL_STRING(tlMissionName) < 20
tlMissionName+=" "
ENDWHILE
CPRINTLN(DEBUG_RANDOM_CHAR, "RC Mission ", tlMissionName, ": ",
PICK_STRING(IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_ACTIVATED)), "[Activated]", "[---------]"),
PICK_STRING(IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_READY_TO_PLAY)), "[Ready To Play]", "[-------------]"),
PICK_STRING(IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_COMPLETED)), "[Completed]", "[---------]"),
PICK_STRING(IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_ACTIVATED_IN_FLOW)),"[Available In Flow]", "[-----------------]"))
ENDREPEAT
bOutputStates = FALSE
ENDIF
IF bCompleteAll = TRUE
// debug for completing all RCs
// Can skip flow to after all story missions and use this too
// so all repeatable missions are available
INT iMission
FOR iMission = 0 TO ENUM_TO_INT(MAX_RC_MISSIONS)-1
Activate_RC_Mission(INT_TO_ENUM(g_eRC_MissionIDs, iMission), TRUE)
ENDFOR
bCompleteAll = FALSE
ENDIF
IF bUnlockSpecificRC = TRUE
// sets certain RCs as complete so we can force one to unloc
Activate_RC_Mission(RC_BARRY_1, TRUE)
Activate_RC_Mission(RC_BARRY_2, TRUE)
Activate_RC_Mission(RC_BARRY_3, TRUE)
Activate_RC_Mission(RC_BARRY_3A, TRUE)
Activate_RC_Mission(RC_BARRY_3C, TRUE)
Execute_Code_ID(CID_BARRY4_TEXT_RECEIVED)
bUnlockSpecificRC = FALSE
ENDIF
ENDPROC
#ENDIF
// -------------------FUNCTIONS---------------------------------------------------------
/// PURPOSE: Determines if the controller is allowed to run
FUNC BOOL Is_Controller_Safe_To_Run()
// Debug suspend checks
#IF IS_DEBUG_BUILD
IF NOT g_savedGlobals.sFlow.isGameflowActive
AND NOT g_bRandomCharsAvailableInDebug
RETURN FALSE
ENDIF
IF g_flowUnsaved.bUpdatingGameflow
RETURN FALSE
ENDIF
IF g_disable_for_smoketest
RETURN FALSE
ENDIF
#ENDIF
// No issues found so assume it is safe to run
RETURN TRUE
ENDFUNC
// ===========================================================================================================
// Initialise
// ===========================================================================================================
/// PURPOSE:
/// Intialises the default Random Character mission blip states
PROC Initialise_Random_Character_Mission_Blips()
// A pool of blips have been setup for all RC missions.
// When an RC mission requires a blip, it must request one from the pool.
eRCBlips[0] = STATIC_BLIP_RANDOMCHAR_00
eRCBlips[1] = STATIC_BLIP_RANDOMCHAR_01
eRCBlips[2] = STATIC_BLIP_RANDOMCHAR_02
eRCBlips[3] = STATIC_BLIP_RANDOMCHAR_03
eRCBlips[4] = STATIC_BLIP_RANDOMCHAR_04
eRCBlips[5] = STATIC_BLIP_RANDOMCHAR_05
eRCBlips[6] = STATIC_BLIP_RANDOMCHAR_06
eRCBlips[7] = STATIC_BLIP_RANDOMCHAR_07
eRCBlips[8] = STATIC_BLIP_RANDOMCHAR_08
eRCBlips[9] = STATIC_BLIP_RANDOMCHAR_09
eRCBlips[10] = STATIC_BLIP_RANDOMCHAR_10
eRCBlips[11] = STATIC_BLIP_RANDOMCHAR_11
eRCBlips[12] = STATIC_BLIP_RANDOMCHAR_12
eRCBlips[13] = STATIC_BLIP_RANDOMCHAR_13
eRCBlips[14] = STATIC_BLIP_RANDOMCHAR_14
eRCBlips[15] = STATIC_BLIP_RANDOMCHAR_15
eRCBlips[16] = STATIC_BLIP_RANDOMCHAR_16
eRCBlips[17] = STATIC_BLIP_RANDOMCHAR_17
eRCBlips[18] = STATIC_BLIP_RANDOMCHAR_18
eRCBlips[19] = STATIC_BLIP_RANDOMCHAR_19
INT iBlip
REPEAT MAX_RC_BLIPS iBlip
bRCBlipActive[iBlip] = FALSE
SET_STATIC_BLIP_ACTIVE_STATE(eRCBlips[iBlip], FALSE)
SET_STATIC_BLIP_MISSION_LAUNCH_LEVEL_LOCKED(eRCBlips[iBlip],MISSION_TYPE_RANDOM_CHAR)
SET_STATIC_BLIP_CATEGORY(eRCBlips[iBlip], STATIC_BLIP_CATEGORY_RANDOMCHAR)
SET_STATIC_BLIP_LONG_RANGE(eRCBlips[iBlip])
SET_STATIC_BLIP_COLOUR(eRCBlips[iBlip], BLIP_COLOUR_DEFAULT)
SET_STATIC_BLIP_ICON(eRCBlips[iBlip], RADAR_TRACE_RANDOM_CHARACTER)
RESET_STATIC_BLIP_CHARACTER_VISIBILITY(eRCBlips[iBlip])
ENDREPEAT
ENDPROC
/// PURPOSE:
/// Checks to see if there are any free Random Character mission blip indexes
/// PARAMS:
/// iBlipIndex - the available blip index
/// RETURNS:
/// Returns TRUE if a valid index was found or already set
FUNC BOOL Setup_Random_Character_Mission_Blip_Index(INT &iBlipIndex)
// Already using a valid blip index
IF iBlipIndex <> -1
RETURN TRUE
ENDIF
// Attempt to find a free blip
INT iBlip
REPEAT MAX_RC_BLIPS iBlip
IF NOT bRCBlipActive[iBlip]
iBlipIndex = iBlip
bRCBlipActive[iBlip] = TRUE
CPRINTLN(DEBUG_RANDOM_CHAR, "Setup_Random_Character_Mission_Blip_Index - Using index[", iBlipIndex, "].")
RETURN TRUE
ENDIF
ENDREPEAT
// Unable to find a valid blip index from the pool
SCRIPT_ASSERT("Ran out of RC mission blips. Tell Andy Minghella.")
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Frees up the specified Random Character mission blip index
/// PARAMS:
/// iBlipIndex - the blip index you want to clear
PROC Clear_Random_Character_Mission_Blip_Index(INT &iBlipIndex)
IF iBlipIndex > -1 AND iBlipIndex < MAX_RC_BLIPS
bRCBlipActive[iBlipIndex] = FALSE
CPRINTLN(DEBUG_RANDOM_CHAR, "Clear_Random_Character_Mission_Blip_Index - Clearing index[", iBlipIndex, "].")
ENDIF
iBlipIndex = -1
ENDPROC
// ===========================================================================================================
// Process
// ===========================================================================================================
/// PURPOSE:
/// Returns TRUE the mission isn't available during Exile and Michael and Trevor are currently exiled
FUNC BOOL IS_MISSION_BLOCKED_DUE_TO_EXILE(BOOL bIsBlocked)
IF bIsBlocked
IF GET_MISSION_FLOW_FLAG_STATE(FLOWFLAG_MICHAEL_TREVOR_EXILE_STARTED)
AND NOT GET_MISSION_FLOW_FLAG_STATE(FLOWFLAG_MICHAEL_TREVOR_EXILE_FINISHED)
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Returns true if the current time of day is within the specified range
/// PARAMS:
/// iStartTime - start of range
/// iEndTime - end of range
/// RETURNS:
/// BOOL: true if in range, false otherwise
FUNC BOOL IS_CURRENT_TIME_IN_RANGE(INT iStartTime, INT iEndTime)
// Check the current time is valid
INT iCurrentTime = ((GET_TIMEOFDAY_HOUR(GET_CURRENT_TIMEOFDAY())*100) + GET_TIMEOFDAY_MINUTE(GET_CURRENT_TIMEOFDAY()))
// AM->PM
IF iEndTime > iStartTime
IF iCurrentTime < iStartTime
OR iCurrentTime > iEndTime
RETURN FALSE
ENDIF
// PM->AM
ELSE
IF iCurrentTime < iStartTime
AND iCurrentTime > iEndTime
RETURN FALSE
ENDIF
ENDIF
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Return blip origin for area mission
FUNC VECTOR GET_ORIGIN_FOR_AREA_MISSION(g_eRC_MissionIDs eRCMission)
// Weed Stash
IF eRCMission = RC_BARRY_3A RETURN << 1161.31, -1326.52, 34.23 >>
ELIF eRCMission = RC_BARRY_3C RETURN << -533.15, -1691.25, 18.21 >>
// Celebrity Theft
ELIF eRCMission = RC_NIGEL_1A RETURN << -565.80, 293.14, 90.80 >>
ELIF eRCMission = RC_NIGEL_1B RETURN << -1036.65, 363.59, 79.82 >>
ELIF eRCMission = RC_NIGEL_1C RETURN << -620.37, -264.39, 37.81 >>
ELIF eRCMission = RC_NIGEL_1D RETURN << -1115.96, 31.42, 53.80 >>
// Photo Ops
ELIF eRCMission = RC_PAPARAZZO_3A RETURN << 305.52, 157.19, 102.94 >>
ELIF eRCMission = RC_PAPARAZZO_3B RETURN << 1040.96, -534.42, 60.17 >>
ENDIF
SCRIPT_ASSERT("RandomChar_Controller: Invalid mission passed to GET_ORIGIN_FOR_AREA_MISSION()")
RETURN <<0,0,0>>
ENDFUNC
/// PURPOSE:
/// Return blip origin for area mission
FUNC FLOAT GET_RADIUS_FOR_AREA_MISSION(g_eRC_MissionIDs eRCMission)
// Weed Stash
IF eRCMission = RC_BARRY_3A
OR eRCMission = RC_BARRY_3C
RETURN 250.0
// Celebrity Theft
ELIF eRCMission = RC_NIGEL_1A RETURN 35.0
ELIF eRCMission = RC_NIGEL_1B RETURN 37.5
ELIF eRCMission = RC_NIGEL_1C RETURN 45.0
ELIF eRCMission = RC_NIGEL_1D RETURN 150.0
// Photo Op
ELIF eRCMission = RC_PAPARAZZO_3A
OR eRCMission = RC_PAPARAZZO_3B
RETURN 90.0
ENDIF
SCRIPT_ASSERT("RandomChar_Controller: Invalid mission passed to GET_RADIUS_FOR_AREA_MISSION()")
RETURN 250.0
ENDFUNC
/// PURPOSE:
/// Is the blip for this mission represented by an area?
FUNC BOOL IS_AREA_MISSION(g_eRC_MissionIDs eRCMission)
IF eRCMission = RC_BARRY_3A
OR eRCMission = RC_BARRY_3C
OR eRCMission = RC_NIGEL_1A
OR eRCMission = RC_NIGEL_1B
OR eRCMission = RC_NIGEL_1C
OR eRCMission = RC_NIGEL_1D
OR eRCMission = RC_PAPARAZZO_3A
OR eRCMission = RC_PAPARAZZO_3B
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Set RC blip name once character has been discovered
FUNC BOOL IS_RAMPAGE_MISSION(g_eRC_MissionIDs eRCMission)
IF eRCMission = RC_RAMPAGE_1
OR eRCMission = RC_RAMPAGE_2
OR eRCMission = RC_RAMPAGE_3
OR eRCMission = RC_RAMPAGE_4
OR eRCMission = RC_RAMPAGE_5
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL DOES_RAMPAGE_MISSION_NEED_A_CHECKMARK(g_eRC_MissionIDs eRCMission)
IF NOT IS_RAMPAGE_MISSION(eRCMission)
RETURN FALSE
ENDIF
INT iFirstRCPercentIndex = ENUM_TO_INT(CP_OJ_RAM1)
INT i = ENUM_TO_INT(eRCMission) - ENUM_TO_INT(RC_RAMPAGE_1)
IF (GET_MISSION_FLOW_FLAG_STATE(FLOWFLAG_ALL_RAMPAGES_UNLOCKED) = TRUE)
RETURN TRUE
ENDIF
IF (g_savedGlobals.sRampageData.playerData[i].iMedalIndex >= ENUM_TO_INT(RAMPAGE_BRONZE))
RETURN TRUE
ENDIF
IF IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_COMPLETED))
RETURN TRUE
ENDIF
RETURN HAS_THIS_SCRIPT_BEEN_REGISTERED_IN_COMPLETION_PERCENTAGE_TOTAL(INT_TO_ENUM(enumCompletionPercentageEntries, iFirstRCPercentIndex + i), FALSE)
ENDFUNC
/// PURPOSE:
/// Do multiple RC's exist at the same mission trigger
FUNC BOOL ARE_MULTIPLE_MISSIONS_AVAILABLE_AT_SAME_LOCATE(g_eRC_MissionIDs eRCMission)
IF eRCMission = RC_BARRY_1
OR eRCMission = RC_BARRY_2
// Both missions have been activated
IF IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[RC_BARRY_1].rcFlags, ENUM_TO_INT(RC_FLAG_ACTIVATED))
AND IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[RC_BARRY_2].rcFlags, ENUM_TO_INT(RC_FLAG_ACTIVATED))
// Both missions haven't yet been completed
IF NOT IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[RC_BARRY_1].rcFlags, ENUM_TO_INT(RC_FLAG_COMPLETED))
AND NOT IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[RC_BARRY_2].rcFlags, ENUM_TO_INT(RC_FLAG_COMPLETED))
RETURN TRUE
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Has a RCM just completed at the same location as another RC trigger?
FUNC BOOL HAS_MISSION_JUST_COMPLETED_AT_SAME_LOCATE(g_eRC_MissionIDs eRCMission)
IF eRCMission = RC_BARRY_1
IF IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[RC_BARRY_2].rcFlags, ENUM_TO_INT(RC_FLAG_COMPLETED))
AND g_RandomChars[RC_BARRY_2].rcLeaveAreaCheck = TRUE
RETURN TRUE
ENDIF
ELIF eRCMission = RC_BARRY_2
IF IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[RC_BARRY_1].rcFlags, ENUM_TO_INT(RC_FLAG_COMPLETED))
AND g_RandomChars[RC_BARRY_1].rcLeaveAreaCheck = TRUE
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Set RC blip name once character has been discovered
PROC SET_BLIP_NAME_AND_COLOUR_FOR_RC_MISSION(STATIC_BLIP_NAME_ENUM name, g_eRC_MissionIDs eRCMission)
// Set blip colour
SET_STATIC_BLIP_COLOUR(name, BLIP_COLOUR_DEFAULT)
// Blip name
IF eRCMission = RC_ABIGAIL_2
SET_STATIC_BLIP_NAME(name, "B_ABI")
ELIF eRCMission = RC_BARRY_1
OR eRCMission = RC_BARRY_2
OR eRCMission = RC_BARRY_3
OR eRCMission = RC_BARRY_4
// Barry is blipped by name if any Barry missions have been completed
IF IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[RC_BARRY_1].rcFlags, ENUM_TO_INT(RC_FLAG_COMPLETED))
OR IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[RC_BARRY_2].rcFlags, ENUM_TO_INT(RC_FLAG_COMPLETED))
OR IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[RC_BARRY_3].rcFlags, ENUM_TO_INT(RC_FLAG_COMPLETED))
SET_STATIC_BLIP_NAME(name, "B_BAR")
ELSE
SET_STATIC_BLIP_NAME(name, "BLIP_66")
SET_STATIC_BLIP_ICON(name, RADAR_TRACE_RANDOM_CHARACTER)
ENDIF
ELIF eRCMission = RC_BARRY_3A
OR eRCMission = RC_BARRY_3C
SET_STATIC_BLIP_NAME(name, "B_STA")
ELIF eRCMission = RC_DREYFUSS_1
SET_STATIC_BLIP_NAME(name, "B_DRE")
ELIF eRCMission = RC_EPSILON_2
OR eRCMission = RC_EPSILON_3
OR eRCMission = RC_EPSILON_4
OR eRCMission = RC_EPSILON_5
OR eRCMission = RC_EPSILON_6
OR eRCMission = RC_EPSILON_7
OR eRCMission = RC_EPSILON_8
SET_STATIC_BLIP_NAME(name, "B_EPS")
ELIF eRCMission = RC_EXTREME_2
OR eRCMission = RC_EXTREME_3
OR eRCMission = RC_EXTREME_4
SET_STATIC_BLIP_NAME(name, "B_EXT")
ELIF eRCMission = RC_FANATIC_1
OR eRCMission = RC_FANATIC_2
OR eRCMission = RC_FANATIC_3
// Mary-Ann is blipped by name if any Fanatic missions have been completed
IF IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[RC_FANATIC_1].rcFlags, ENUM_TO_INT(RC_FLAG_COMPLETED))
OR IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[RC_FANATIC_2].rcFlags, ENUM_TO_INT(RC_FLAG_COMPLETED))
OR IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[RC_FANATIC_3].rcFlags, ENUM_TO_INT(RC_FLAG_COMPLETED))
SET_STATIC_BLIP_NAME(name, "B_FAN")
ELSE
SET_STATIC_BLIP_NAME(name, "BLIP_66")
SET_STATIC_BLIP_ICON(name, RADAR_TRACE_RANDOM_CHARACTER)
ENDIF
ELIF eRCMission = RC_HUNTING_2
SET_STATIC_BLIP_NAME(name, "B_HUN")
ELIF eRCMission = RC_JOSH_2
OR eRCMission = RC_JOSH_3
OR eRCMission = RC_JOSH_4
SET_STATIC_BLIP_NAME(name, "B_JOS")
ELIF eRCMission = RC_MINUTE_2
OR eRCMission = RC_MINUTE_3
SET_STATIC_BLIP_NAME(name, "B_MIN")
ELIF eRCMission = RC_NIGEL_1A
OR eRCMission = RC_NIGEL_1B
OR eRCMission = RC_NIGEL_1C
OR eRCMission = RC_NIGEL_1D
SET_STATIC_BLIP_NAME(name, "B_CEL")
ELIF eRCMission = RC_NIGEL_2
OR eRCMission = RC_NIGEL_3
SET_STATIC_BLIP_NAME(name, "B_NIG")
ELIF eRCMission = RC_OMEGA_2
SET_STATIC_BLIP_NAME(name, "B_OME")
ELIF eRCMission = RC_PAPARAZZO_2
OR eRCMission = RC_PAPARAZZO_3
OR eRCMission = RC_PAPARAZZO_4
SET_STATIC_BLIP_NAME(name, "B_PAP")
ELIF eRCMission = RC_PAPARAZZO_3A
OR eRCMission = RC_PAPARAZZO_3B
SET_STATIC_BLIP_NAME(name, "B_PHO")
ELIF eRCMission = RC_RAMPAGE_1
SET_STATIC_BLIP_NAME(name, "BLIP_66")
ELIF eRCMission = RC_RAMPAGE_2
OR eRCMission = RC_RAMPAGE_3
OR eRCMission = RC_RAMPAGE_4
OR eRCMission = RC_RAMPAGE_5
SET_STATIC_BLIP_NAME(name, "BLIP_84")
ELIF eRCMission = RC_TONYA_2
OR eRCMission = RC_TONYA_3
OR eRCMission = RC_TONYA_4
OR eRCMission = RC_TONYA_5
SET_STATIC_BLIP_NAME(name, "B_TON")
ELSE
SET_STATIC_BLIP_NAME(name, "BLIP_66")
ENDIF
ENDPROC
/// PURPOSE:
/// Checks for the moment at which the player reveals RC mission locations in the FOW,
/// saves these results, and alters RC blip range settings appropriately. Optimised to
/// check 1 RC per frame and save results into an extendable bitset.
PROC UPDATE_RC_REVEALED_IN_FOW_CHECKS()
//Step through one RC mission per frame and precalculate which bit and bitset to query for FOW visiblity.
iRCMission++
iBit++
IF iRCMission >= ENUM_TO_INT(MAX_RC_MISSIONS)
iRCMission = 0
iBit = 0
iBitset = 0
ELIF iBit > 31
iBit = 0
iBitset++
//Check we haven't overrun the number of bitsets we have.
#IF IS_DEBUG_BUILD
IF iBitset >= RC_MAX_FOW_VISIBLE_BITSETS
SCRIPT_ASSERT("UPDATE_RC_VISIBLE_IN_FOW_CHECKS: Not enough bitsets to track FOW state of all RC missions. Increase RC_MAX_FOW_VISIBLE_BITSETS.")
ENDIF
#ENDIF
ENDIF
//CDEBUG3LN(DEBUG_RANDOM_CHAR, "<FOW-VIS> Mission:", iRCMission, " Bit:", iBit, " Bitset:", iBitset)
g_eRC_MissionIDs eRCMission = INT_TO_ENUM(g_eRC_MissionIDs, iRCMission)
// Rampages 2-5 are not affected by the fog of war
IF NOT IS_RAMPAGE_MISSION(eRCMission) OR eRCMission = RC_RAMPAGE_1
// Every frame check one RC mission to see if it has been revealed on the map.
IF NOT IS_BIT_SET(g_savedGlobals.sRandomChars.g_iVisibleInFOWBitset[iBitset], iBit)
CDEBUG3LN(DEBUG_RANDOM_CHAR, "<FOW-VIS> Checking if ", GET_RC_MISSION_DISPLAY_STRING_FROM_ID(eRCMission), " has been revealed in the FOW.")
g_structRCMissionsStatic sRCMissionDetails
Retrieve_Random_Character_Static_Mission_Details(eRCMission, sRCMissionDetails)
// Automatically reveal Tonya 1.
IF eRCMission = RC_TONYA_1
CPRINTLN(DEBUG_RANDOM_CHAR, "<FOW-VIS> Flagging ", GET_RC_MISSION_DISPLAY_STRING_FROM_ID(eRCMission), " as having been revealed in the FOW.")
SET_BIT(g_savedGlobals.sRandomChars.g_iVisibleInFOWBitset[iBitset], iBit)
// Query the FOW state to see if this RC has been revealed yet.
ELIF GET_MINIMAP_FOW_COORDINATE_IS_REVEALED(sRCMissionDetails.rcCoords)
CPRINTLN(DEBUG_RANDOM_CHAR, "<FOW-VIS> Flagging ", GET_RC_MISSION_DISPLAY_STRING_FROM_ID(eRCMission), " as having been revealed in the FOW.")
SET_BIT(g_savedGlobals.sRandomChars.g_iVisibleInFOWBitset[iBitset], iBit)
// If the RC hasn't been revealed and it's blip is long range then make it short range for now.
ELIF g_RandomChars[eRCMission].rcBlipIndex != -1
IF bRCBlipActive[g_RandomChars[eRCMission].rcBlipIndex]
IF IS_BIT_SET(g_GameBlips[eRCBlips[g_RandomChars[eRCMission].rcBlipIndex]].iSetting, STATIC_BLIP_SETTING_RADAR_LONG)
CPRINTLN(DEBUG_RANDOM_CHAR, "<FOW-VIS> Setting blip short range for ", GET_RC_MISSION_DISPLAY_STRING_FROM_ID(eRCMission), ".")
SET_STATIC_BLIP_SHORT_RANGE(eRCBlips[g_RandomChars[eRCMission].rcBlipIndex])
//SET_STATIC_BLIP_VISIBLE_STATE(eRCBlips[g_RandomChars[eRCMission].rcBlipIndex], FALSE)
ENDIF
ENDIF
ENDIF
//If the RC has been revealed and it's blip is short range then make it long range from now on.
ELIF g_RandomChars[eRCMission].rcBlipIndex != -1
IF bRCBlipActive[g_RandomChars[eRCMission].rcBlipIndex]
IF NOT IS_BIT_SET(g_GameBlips[eRCBlips[g_RandomChars[eRCMission].rcBlipIndex]].iSetting, STATIC_BLIP_SETTING_RADAR_LONG)
CPRINTLN(DEBUG_RANDOM_CHAR, "<FOW-VIS> Setting blip long range for ", GET_RC_MISSION_DISPLAY_STRING_FROM_ID(eRCMission), ".")
SET_STATIC_BLIP_LONG_RANGE(eRCBlips[g_RandomChars[eRCMission].rcBlipIndex])
//SET_STATIC_BLIP_VISIBLE_STATE(eRCBlips[g_RandomChars[eRCMission].rcBlipIndex], TRUE)
ENDIF
ENDIF
ENDIF
ENDIF
ENDPROC
PROC UPDATE_BEVERLY_UNLOCK_WILDLIFE_PHOTOGRAPHY()
IF IS_LAST_GEN_PLAYER()
IF NOT GET_MISSION_FLOW_FLAG_STATE(FLOWFLAG_BEVERLY_SENT_WILDLIFE_TEXT)
IF IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[RC_PAPARAZZO_1].rcFlags, ENUM_TO_INT(RC_FLAG_COMPLETED))
CPRINTLN(DEBUG_RANDOM_CHAR, "Paparazzo 1 completed and the player is from last-gen. Queueing Beverly text for Wildlife Photography.")
#IF IS_FINAL_BUILD
REGISTER_TEXT_MESSAGE_FROM_CHARACTER_TO_PLAYER(TEXT_PAP1_WILDLIFE_UNLOCK, CT_AMBIENT, BIT_FRANKLIN, CHAR_BEVERLY, 60000, 10000, VID_BLANK, CID_PAPARAZZO1_SEND_WILDLIFE_EMAIL)
#ENDIF
#IF IS_DEBUG_BUILD
IF NOT g_flowUnsaved.bUpdatingGameflow
IF NOT IS_BIT_SET(g_iDebugLaunchBlockCommunication, BIT_DBG_LNCH_COMM_BLOCK_WILDLIFE_PHOTO)
REGISTER_TEXT_MESSAGE_FROM_CHARACTER_TO_PLAYER(TEXT_PAP1_WILDLIFE_UNLOCK, CT_AMBIENT, BIT_FRANKLIN, CHAR_BEVERLY, 60000, 10000, VID_BLANK, CID_PAPARAZZO1_SEND_WILDLIFE_EMAIL)
ENDIF
ENDIF
CLEAR_BIT(g_iDebugLaunchBlockCommunication, BIT_DBG_LNCH_COMM_BLOCK_WILDLIFE_PHOTO)
#ENDIF
SET_MISSION_FLOW_FLAG_STATE(FLOWFLAG_BEVERLY_SENT_WILDLIFE_TEXT, TRUE)
ENDIF
ELSE
// Check for Beverly being dead. If he is remove his text and jump straight to the Tourist Board email.
IF NOT GET_MISSION_FLOW_FLAG_STATE(FLOWFLAG_WILDLIFE_PHOTOGRAPHY_UNLOCKED)
IF IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[RC_PAPARAZZO_4].rcFlags, ENUM_TO_INT(RC_FLAG_COMPLETED))
IF IS_COMMUNICATION_REGISTERED(TEXT_PAP1_WILDLIFE_UNLOCK)
CPRINTLN(DEBUG_RANDOM_CHAR, "Beverly text for Wildlife Photography queued after Beverly has died.")
CPRINTLN(DEBUG_RANDOM_CHAR, "Skipping directly to the Los Santos Tourist Board email.")
CANCEL_COMMUNICATION(TEXT_PAP1_WILDLIFE_UNLOCK)
Execute_Code_ID(CID_PAPARAZZO1_SEND_WILDLIFE_EMAIL, 0)
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Handles making sure the help displays after passing Josh 1.
PROC Do_Josh1_For_Sale_Signs_Help()
IF NOT HAS_ONE_TIME_HELP_DISPLAYED(FHM_JOSH1_FOR_SALE)
IF IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[RC_JOSH_1].rcFlags, ENUM_TO_INT(RC_FLAG_COMPLETED))
IF GET_FLOW_HELP_MESSAGE_STATUS("FS_HELP1") = FHS_EXPIRED
ADD_HELP_TO_FLOW_QUEUE("FS_HELP1", FHP_MEDIUM, 0, 2000)
ELIF GET_FLOW_HELP_MESSAGE_STATUS("FS_HELP1") = FHS_DISPLAYED
SET_ONE_TIME_HELP_MESSAGE_DISPLAYED(FHM_JOSH1_FOR_SALE)
ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Reminder help text when first entering an area blip for one of the Barry stash missions
PROC Do_Barry3_Reminder_Help()
// Reminder help text
IF IS_PLAYER_PLAYING(PLAYER_ID())
IF NOT HAS_ONE_TIME_HELP_DISPLAYED(FHM_BARRY3_REMINDER_HELP)
IF IS_RC_MISSION_AVAILABLE(RC_BARRY_3A) AND GET_DISTANCE_BETWEEN_ENTITY_AND_COORD(PLAYER_PED_ID(), GET_ORIGIN_FOR_AREA_MISSION(RC_BARRY_3A)) < GET_RADIUS_FOR_AREA_MISSION(RC_BARRY_3A)
OR IS_RC_MISSION_AVAILABLE(RC_BARRY_3C) AND GET_DISTANCE_BETWEEN_ENTITY_AND_COORD(PLAYER_PED_ID(), GET_ORIGIN_FOR_AREA_MISSION(RC_BARRY_3C)) < GET_RADIUS_FOR_AREA_MISSION(RC_BARRY_3C)
// Areas where you can find vehicles with a hidden stash are marked on the map. Search them to find vehicles for Barry.
IF GET_FLOW_HELP_MESSAGE_STATUS("BARSTASH2") = FHS_EXPIRED
ADD_HELP_TO_FLOW_QUEUE("BARSTASH2", FHP_MEDIUM, 0, 2000, DEFAULT_HELP_TEXT_TIME, BIT_FRANKLIN)
ELIF GET_FLOW_HELP_MESSAGE_STATUS("BARSTASH2") = FHS_DISPLAYED
SET_ONE_TIME_HELP_MESSAGE_DISPLAYED(FHM_BARRY3_REMINDER_HELP)
ENDIF
ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Reminder the player that they need to do Tonya1 to progress the gameflow. Keeps displaying
/// if Tonya1 isn't completed after Family1.
PROC Do_Tonya1_Reminder_Help()
IF NOT HAS_ONE_TIME_HELP_DISPLAYED(FHM_RC_TONYA1_REMINDER)
IF IS_PLAYER_PLAYING(PLAYER_ID())
IF GET_MISSION_COMPLETE_STATE(SP_MISSION_FAMILY_1)
IF NOT GET_MISSION_FLOW_FLAG_STATE(FLOWFLAG_RES_AND_RCS_UNLOCKED)
IF eTonya1HelpTime = INVALID_TIMEOFDAY
eTonya1HelpTime = GET_CURRENT_TIMEOFDAY()
ADD_TIME_TO_TIMEOFDAY(eTonya1HelpTime, 0, 5)
ELIF IS_NOW_AFTER_TIMEOFDAY(eTonya1HelpTime)
CPRINTLN(DEBUG_RANDOM_CHAR, "Attempting to display Tonya1 blip reminder this frame.")
BOOL bFoundBlip = FALSE
INT iBlipIndex
g_structRCMissionsStatic sRCMissionDetails
FOR iBlipIndex = ENUM_TO_INT(STATIC_BLIP_RANDOMCHAR_00) TO ENUM_TO_INT(STATIC_BLIP_RANDOMCHAR_19)
STATIC_BLIP_NAME_ENUM eRCBlip = INT_TO_ENUM(STATIC_BLIP_NAME_ENUM, iBlipIndex)
Retrieve_Random_Character_Static_Mission_Details(RC_TONYA_1, sRCMissionDetails)
IF ARE_VECTORS_EQUAL(sRCMissionDetails.rcCoords, GET_STATIC_BLIP_POSITION(eRCBlip))
IF DOES_BLIP_EXIST(g_GameBlips[eRCBlip].biBlip)
CPRINTLN(DEBUG_RANDOM_CHAR, "Found Tonya1 blip. Displaying reminder.")
//Found Tonya blip. Display help and flash blip.
SET_BLIP_FLASHES(g_GameBlips[eRCBlip].biBlip, TRUE)
SET_BLIP_FLASH_TIMER(g_GameBlips[eRCBlip].biBlip, 10000)
SWITCH GET_CURRENT_PLAYER_PED_ENUM()
CASE CHAR_FRANKLIN ADD_HELP_TO_FLOW_QUEUE("AM_H_RCFS", FHP_HIGH, 0, 1000) BREAK
CASE CHAR_MICHAEL ADD_HELP_TO_FLOW_QUEUE("AM_H_RCFS_M", FHP_HIGH, 0, 1000) BREAK
ENDSWITCH
//Display help again in 5 game hours.
eTonya1HelpTime = GET_CURRENT_TIMEOFDAY()
ADD_TIME_TO_TIMEOFDAY(eTonya1HelpTime, 0, 0, 8)
bFoundBlip = TRUE
ENDIF
ENDIF
ENDFOR
//Blip not found this pass. Try again in 5 game minutes.
IF NOT bFoundBlip
CPRINTLN(DEBUG_RANDOM_CHAR, "Couldn't find Tonya1 blip. Delaying next attempt for 5 in-game minutes.")
eTonya1HelpTime = GET_CURRENT_TIMEOFDAY()
ADD_TIME_TO_TIMEOFDAY(eTonya1HelpTime, 0, 5)
ENDIF
ENDIF
ELSE
SET_ONE_TIME_HELP_MESSAGE_DISPLAYED(FHM_RC_TONYA1_REMINDER)
ENDIF
ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Updates the flags and blip state for the specified Random Character mission
/// PARAMS:
/// eRCMission - the RC mission we are processing
PROC Process_Random_Character_Mission_State(g_eRC_MissionIDs eRCMission)
IF (eRCMission = NO_RC_MISSION)
SCRIPT_ASSERT("ERROR: Process_Random_Character_Mission_State has been passed an illegal Random Character mission ID")
EXIT
ENDIF
// Grab the mission details
g_structRCMissionsStatic sRCMissionDetails
Retrieve_Random_Character_Static_Mission_Details(eRCMission, sRCMissionDetails)
//----- -----Mission flags--------------------------------------
// Clear the global force update flag seeing as we are now processing the mission
g_RandomChars[eRCMission].rcForceStateUpdate = FALSE
IF NOT IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_ACTIVATED_IN_FLOW))
IF NOT sRCMissionDetails.rcMustBeActivatedInFlow
SET_BIT(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_ACTIVATED_IN_FLOW))
ENDIF
ENDIF
IF IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_ACTIVATED))
AND NOT IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_COMPLETED))
// Set time delay when first activated
IF NOT g_savedGlobals.sRandomChars.savedRC[eRCMission].rcTimeReqSet
// Set the time
TIMEOFDAY sStartTime = GET_CURRENT_TIMEOFDAY()
ADD_TIME_TO_TIMEOFDAY(sStartTime, 0, 0, sRCMissionDetails.rcHoursToWaitReq, 0, 0, 0)
// Last One mission unlocks a random time after 100% complete
IF eRCMission = RC_THELASTONE
// Unlocks between 3 and 10 hours
INT iRandomHours = GET_RANDOM_INT_IN_RANGE(3, 11)
iRandomHours *= 30 //B*1589468 get real hours
CPRINTLN(DEBUG_RANDOM_CHAR, "The Last One RCM unlocking after ", iRandomHours, " hours")
ADD_TIME_TO_TIMEOFDAY(sStartTime, 0, 0, iRandomHours, 0, 0, 0)
ENDIF
#IF IS_DEBUG_BUILD
TEXT_LABEL_63 tlTime = TIMEOFDAY_TO_TEXT_LABEL(sStartTime)
TEXT_LABEL_7 tlName = GET_RC_MISSION_NAME_LABEL(eRCMission)
CPRINTLN(DEBUG_RANDOM_CHAR,"RC Mission ",tlName," set to start on ",tlTime)
#ENDIF
g_savedGlobals.sRandomChars.savedRC[eRCMission].rcTimeReq = sStartTime
g_savedGlobals.sRandomChars.savedRC[eRCMission].rcTimeReqSet = TRUE
ENDIF
IF IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_ACTIVATED_IN_FLOW))
IF NOT IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_READY_TO_PLAY))
// Set ready to play and then check for any fail conditions
BOOL bReadyToPlay = TRUE
// Check flow flag state
IF sRCMissionDetails.rcFlowFlagReq <> FLOWFLAG_NONE
IF NOT Get_Mission_Flow_Flag_State(sRCMissionDetails.rcFlowFlagReq)
#IF IS_DEBUG_BUILD RC_CONTROLLER_DEBUG_PRINT(eRCMission, "Get_Mission_Flow_Flag_State = FALSE, set bReadyToPlay as FALSE") #ENDIF
bReadyToPlay = FALSE
ENDIF
ENDIF
// Check whether mission is blocked due to a specific story mission
IF Random_Character_Blocked_Due_To_Mission(eRCMission)
#IF IS_DEBUG_BUILD RC_CONTROLLER_DEBUG_PRINT(eRCMission, "Random_Character_Blocked_Due_To_Mission, set bReadyToPlay as FALSE") #ENDIF
bReadyToPlay = FALSE
ENDIF
// Check whether mission is blocked due to being wanted
IF Random_Character_Blocked_Due_To_Wanted_Level(eRCMission)
#IF IS_DEBUG_BUILD RC_CONTROLLER_DEBUG_PRINT(eRCMission, "Random_Character_Blocked_Due_To_Wanted_Level, set bReadyToPlay as FALSE") #ENDIF
bReadyToPlay = FALSE
ENDIF
// Check whether mission is blocked due to Michael and Trevor
IF IS_MISSION_BLOCKED_DUE_TO_EXILE(sRCMissionDetails.bBlockedInExile)
#IF IS_DEBUG_BUILD RC_CONTROLLER_DEBUG_PRINT(eRCMission, "IS_MISSION_BLOCKED_DUE_TO_EXILE, set bReadyToPlay as FALSE") #ENDIF
bReadyToPlay = FALSE
ENDIF
// Check the time delay requirement
IF sRCMissionDetails.rcHoursToWaitReq > 0 AND bReadyToPlay
IF NOT IS_NOW_AFTER_TIMEOFDAY(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcTimeReq)
//Check if the time left to wait is not absurdly large (> 2 weeks in-game)
INT iSec, iMinute, iHour, iDays,iMonths, iYears
GET_DIFFERENCE_BETWEEN_NOW_AND_TIMEOFDAY(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcTimeReq,iSec, iMinute,iHour,iDays,iMonths,iYears)
IF (iYears>0) OR (iMonths > 0) OR (iDays > 10)
TEXT_LABEL_63 strTod
TEXT_LABEL_7 strRCM = GET_RC_MISSION_NAME_LABEL(INT_TO_ENUM(g_eRC_MissionIDs,eRCMission))
#IF IS_DEBUG_BUILD
GET_TIMEOFDAY_STRING(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcTimeReq,strTod,TRUE,TRUE)
PRINT_TIMEOFDAY(GET_CURRENT_TIMEOFDAY(),debug_random_char)
#ENDIF
CERRORLN(DEBUG_RANDOM_CHAR,"See bug 2135359: Time till random char mission '",
strRCM ,"' is very long (over 10 days), supposed to start on ",
strTod," resetting to current + wait time (",sRCMissionDetails.rcHoursToWaitReq,")")
g_savedGlobals.sRandomChars.savedRC[eRCMission].rcTimeReq = GET_CURRENT_TIMEOFDAY()
ADD_TIME_TO_TIMEOFDAY(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcTimeReq, 0, 0, sRCMissionDetails.rcHoursToWaitReq, 0, 0, 0)
ENDIF
#IF IS_DEBUG_BUILD RC_CONTROLLER_DEBUG_PRINT(eRCMission, "IS_NOW_AFTER_TIMEOFDAY, set bReadyToPlay as FALSE") #ENDIF
bReadyToPlay = FALSE
ENDIF
ENDIF
// Check the current time is valid
IF NOT IS_CURRENT_TIME_IN_RANGE(sRCMissionDetails.rcStartTime, sRCMissionDetails.rcEndTime)
#IF IS_DEBUG_BUILD RC_CONTROLLER_DEBUG_PRINT(eRCMission, "Time of day outside restrictions, set bReadyToPlay as FALSE") #ENDIF
bReadyToPlay = FALSE
ENDIF
// Mark as Ready To Play if no issues found
IF bReadyToPlay
CPRINTLN(DEBUG_RANDOM_CHAR, "RC mission ", sRCMissionDetails.rcScriptName, " is ready to launch.")
SET_BIT(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_READY_TO_PLAY))
g_RandomChars[eRCMission].rcIsRunning = FALSE
g_RandomChars[eRCMission].rcFailed = FALSE
g_RandomChars[eRCMission].rcPassed = FALSE
#IF IS_DEBUG_BUILD
IF g_eDebugLaunchingToRCMission = eRCMission
CPRINTLN(DEBUG_RANDOM_CHAR, "Leave area check for RC mission ", sRCMissionDetails.rcScriptName, " was skipped due to debug launch.")
ELSE
#ENDIF
// Special cases
IF eRCMission = RC_MRS_PHILIPS_1
// We want it to appear when player gets control at the end of the game.
g_RandomChars[eRCMission].rcLeaveAreaCheck = FALSE
REACTIVATE_NAMED_WORLD_BRAINS_WAITING_TILL_OUT_OF_RANGE("launcher_MrsPhilips")
ELIF eRCMission = RC_TONYA_1
// We want it to appear as soon as we finish Armenian 2 to encourage the player to trigger it immediately.
g_RandomChars[eRCMission].rcLeaveAreaCheck = FALSE
REACTIVATE_NAMED_WORLD_BRAINS_WAITING_TILL_OUT_OF_RANGE("launcher_Tonya")
ELSE
// Fix for bug #324493 - Force player to leave area before activating the blip
CPRINTLN(DEBUG_RANDOM_CHAR, "Leave area flag set for RC mission ", sRCMissionDetails.rcScriptName, ".")
g_RandomChars[eRCMission].rcLeaveAreaCheck = TRUE
ENDIF
#IF IS_DEBUG_BUILD
ENDIF
#ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
// Check some runtime flags
IF IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_READY_TO_PLAY))
AND NOT IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_COMPLETED))
BOOL bOkToLaunch = TRUE
// Check whether mission is blocked due to a specific story mission
IF Random_Character_Blocked_Due_To_Mission(eRCMission)
#IF IS_DEBUG_BUILD RC_CONTROLLER_DEBUG_PRINT(eRCMission, "Random_Character_Blocked_Due_To_Mission, set bReadyToPlay as FALSE") #ENDIF
bOkToLaunch = FALSE
ENDIF
// Check for wanted update
IF Random_Character_Blocked_Due_To_Wanted_Level(eRCMission)
bOkToLaunch = FALSE
ENDIF
// Check for Exile flag update...
IF IS_MISSION_BLOCKED_DUE_TO_EXILE(sRCMissionDetails.bBlockedInExile)
bOkToLaunch = FALSE
ENDIF
// Still ok?
IF bOkToLaunch
// Are we still within the required time of day?
IF NOT IS_CURRENT_TIME_IN_RANGE(sRCMissionDetails.rcStartTime, sRCMissionDetails.rcEndTime)
#IF IS_DEBUG_BUILD RC_CONTROLLER_DEBUG_PRINT(eRCMission, "Time of day outside restrictions, set bOkToLaunch as FALSE") #ENDIF
bOkToLaunch = FALSE
ENDIF
ENDIF
// Mission no longer ready as either blocked by exile or gone past TOD restriction...
IF NOT bOkToLaunch
CPRINTLN(DEBUG_RANDOM_CHAR, "RC mission ", sRCMissionDetails.rcScriptName, " is no longer ready to play.")
CLEAR_BIT(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_READY_TO_PLAY))
ENDIF
ENDIF
// RC Missions set the rcIsAwaitingTrigger flag to TRUE when waiting for the player
// to trigger the created ped.
// To keep things clean we have made this a reset flag, which means that it will only
// stay TRUE for the frame that it was set. With this in mind, we can set the appropriate
// blip state and force a mission update the following frame.
BOOL bReadyToTrigger = TRUE // Should always display unless still in range and no longer triggerable
IF g_RandomChars[eRCMission].rcIsAwaitingTrigger
// We reach this point every frame that the character is ready to trigger,
// as well as the first frame after the mission has cleaned up.
g_RandomChars[eRCMission].rcForceStateUpdate = TRUE
g_RandomChars[eRCMission].rcIsAwaitingTrigger = FALSE
g_RandomChars[eRCMission].rcLeaveAreaCheck = TRUE
ELSE
IF g_RandomChars[eRCMission].rcLeaveAreaCheck
// RCM strands can now have different brain activation ranges
FLOAT fRCActivationRange
IF sRCMissionDetails.rcStrandID = RCS_ABIGAIL
OR sRCMissionDetails.rcStrandID = RCS_DREYFUSS
OR sRCMissionDetails.rcStrandID = RCS_EPSILON
OR sRCMissionDetails.rcStrandID = RCS_MRS_PHILIPS
OR sRCMissionDetails.rcStrandID = RCS_THELASTONE
OR sRCMissionDetails.rcStrandID = RCS_TONYA
fRCActivationRange = RC_BRAIN_ACTIVATION_RANGE_NORMAL
ELSE
fRCActivationRange = RC_BRAIN_ACTIVATION_RANGE_EXTRA
ENDIF
// Don't do this check if a replay is being processed- to prevent this being reset whilst things for replay load (player warp etc)
IF NOT IS_REPLAY_BEING_PROCESSED()
AND (GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(PLAYER_PED_ID(), FALSE), sRCMissionDetails.rcCoords, FALSE) > fRCActivationRange)
// If the blips are appearing too soon then we need to add some sort of timer here.
// We would also need to tie the timer into the 'is rc mission safe to run' checks.
CPRINTLN(DEBUG_RANDOM_CHAR, "Player left area for RC mission ", sRCMissionDetails.rcScriptName)
g_RandomChars[eRCMission].rcLeaveAreaCheck = FALSE
ELSE
// Still waiting for player to leave so not safe to trigger
CPRINTLN(DEBUG_RANDOM_CHAR, "RC mission ", sRCMissionDetails.rcScriptName, " is waiting for player to leave the area.")
// Below IF statement - fixes B*2204739 - Random Char controller was waiting for the player to leave, but the door was unlocked and quicksaves allowed
IF eRCMission = RC_EPSILON_2
AND g_savedGlobals.sBuildingData.eDoorState[DOORNAME_EPSILON2_STORAGE_ROOM] <> DOORSTATE_LOCKED
CPRINTLN(DEBUG_RANDOM_CHAR, "Locking Epsilon 2 storeroom door...")
SET_DOOR_STATE(DOORNAME_EPSILON2_STORAGE_ROOM, DOORSTATE_LOCKED)
ENDIF
bReadyToTrigger = FALSE
ENDIF
ENDIF
ENDIF
// ----------------Mission blips--------------------------------------------------------
g_eRC_BlipState eBlipState = RC_BLIP_TURNED_OFF
IF bReadyToTrigger
AND NOT (IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_COMPLETED)))
AND (IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_ACTIVATED)))
AND (IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_ACTIVATED_IN_FLOW)))
AND (IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_READY_TO_PLAY)))
// B*1306406 - Don't display first Tonya blip until Armenian 2 results screen has finished
IF eRCMission = RC_TONYA_1 AND IS_RESULT_SCREEN_DISPLAYING()
ELSE
eBlipState = RC_BLIP_READY_TO_TRIGGER
ENDIF
ENDIF
// Only process if the state has changed
IF g_RandomChars[eRCMission].rcBlipState <> eBlipState
// Make sure we have a valid blip index from the pool
IF Setup_Random_Character_Mission_Blip_Index(g_RandomChars[eRCMission].rcBlipIndex)
// Ignore in the event that the RC mission doesn't have a defined blip (e.g. Tonya 3&4)
IF ARE_VECTORS_EQUAL(sRCMissionDetails.rcCoords, <<0,0,0>>)
ELSE
STATIC_BLIP_NAME_ENUM eBlip = eRCBlips[g_RandomChars[eRCMission].rcBlipIndex]
IF eBlipState = RC_BLIP_TURNED_OFF
// Turn off the blip and free up the blip index
CPRINTLN(DEBUG_RANDOM_CHAR, "RC_BLIP_TURNED_OFF ", GET_RC_MISSION_DISPLAY_STRING_FROM_ID(eRCMission))
SET_STATIC_BLIP_ACTIVE_STATE(eBlip, FALSE)
SET_STATIC_BLIP_HAS_CHECKMARK(eBlip, FALSE)
Clear_Random_Character_Mission_Blip_Index(g_RandomChars[eRCMission].rcBlipIndex)
ELIF eBlipState = RC_BLIP_READY_TO_TRIGGER
CPRINTLN(DEBUG_RANDOM_CHAR, "RC_BLIP_READY_TO_TRIGGER ", GET_RC_MISSION_DISPLAY_STRING_FROM_ID(eRCMission))
// Area blips for Barry weed stash and Nigel theft missions
IF IS_AREA_MISSION(eRCMission)
SET_STATIC_BLIP_AS_AREA_BLIP(eBlip, TRUE)
SET_STATIC_BLIP_POSITION(eBlip, GET_ORIGIN_FOR_AREA_MISSION(eRCMission))
SET_STATIC_BLIP_RADIUS(eBlip, GET_RADIUS_FOR_AREA_MISSION(eRCMission))
ELSE
SET_STATIC_BLIP_AS_AREA_BLIP(eBlip, FALSE)
SET_STATIC_BLIP_ICON(eBlip, sRCMissionDetails.rcBlipSprite)
SET_STATIC_BLIP_POSITION(eBlip, sRCMissionDetails.rcCoords)
ENDIF
// Set blip name based on character name
SET_BLIP_NAME_AND_COLOUR_FOR_RC_MISSION(eBlip, eRCMission)
// Set up the blip states that are specific to this mission, this includes
// character visibility, icon, colour, position etc.
RESET_STATIC_BLIP_CHARACTER_VISIBILITY(eBlip)
IF IS_MISSION_TRIGGERABLE_BY_CHARACTER(sRCMissionDetails.rcPlayableChars, CHAR_FRANKLIN)
SET_STATIC_BLIP_CHARACTER_VISIBILITY(eBlip, TRUE, CHAR_FRANKLIN)
ENDIF
IF IS_MISSION_TRIGGERABLE_BY_CHARACTER(sRCMissionDetails.rcPlayableChars, CHAR_MICHAEL)
SET_STATIC_BLIP_CHARACTER_VISIBILITY(eBlip, TRUE, CHAR_MICHAEL)
ENDIF
IF IS_MISSION_TRIGGERABLE_BY_CHARACTER(sRCMissionDetails.rcPlayableChars, CHAR_TREVOR)
SET_STATIC_BLIP_CHARACTER_VISIBILITY(eBlip, TRUE, CHAR_TREVOR)
ENDIF
// B*1566342: Rampage 2-5 are short range
IF IS_RAMPAGE_MISSION(eRCMission) AND eRCMission <> RC_RAMPAGE_1
SET_STATIC_BLIP_SHORT_RANGE(eBlip)
ENDIF
// B*1565201: Rampage Blips Should have a tick mark if completed
IF IS_RAMPAGE_MISSION(eRCMission)
IF DOES_RAMPAGE_MISSION_NEED_A_CHECKMARK(eRCMission)
SET_STATIC_BLIP_HAS_CHECKMARK(eBlip, TRUE)
ENDIF
ENDIF
// Set category and activate.
IF eRCMission = RC_TONYA_1
SET_STATIC_BLIP_CATEGORY(eBlip, STATIC_BLIP_CATEGORY_MISSION) //Tonya 1 blip should behave like a mission blip.
ELSE
SET_STATIC_BLIP_CATEGORY(eBlip, STATIC_BLIP_CATEGORY_RANDOMCHAR)
ENDIF
SET_STATIC_BLIP_ACTIVE_STATE(eBlip, TRUE)
ENDIF
ENDIF
// Update the stored state so we only perform blip updates once
g_RandomChars[eRCMission].rcBlipState = eBlipState
ENDIF
ENDIF
// Display blip help text if we haven't done so already
IF NOT IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_BLIP_HELP_DISPLAYED))
// No help text for this mission
IF GET_HASH_KEY(sRCMissionDetails.rcBlipHelp) = GET_HASH_KEY("")
SET_BIT(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_BLIP_HELP_DISPLAYED))
ELSE
// RCM ready to trigger...
IF eBlipState = RC_BLIP_READY_TO_TRIGGER
IF Is_Mission_Triggerable_By_Character(sRCMissionDetails.rcPlayableChars, GET_CURRENT_PLAYER_PED_ENUM())
// Prevent from triggering if timetable scene or player switch is in progress
IF NOT Is_Player_Timetable_Scene_In_Progress()
AND NOT IS_PLAYER_SWITCH_IN_PROGRESS()
// Help message isn't already onscreen
IF NOT IS_HELP_MESSAGE_BEING_DISPLAYED()
// Flash Tonya 1 blip
IF eRCMission = RC_TONYA_1
IF IS_STATIC_BLIP_CURRENTLY_VISIBLE_TO_SYSTEM(STATIC_BLIP_RANDOMCHAR_00)
IF IS_STATIC_BLIP_CURRENTLY_VISIBLE(STATIC_BLIP_RANDOMCHAR_00)
ADD_HELP_TO_FLOW_QUEUE(sRCMissionDetails.rcBlipHelp, FHP_MEDIUM, 0, FLOW_HELP_NEVER_EXPIRES, DEFAULT_HELP_TEXT_TIME, sRCMissionDetails.rcPlayableChars)
EXECUTE_CODE_ID(CID_FLASH_RANDOM_CHAR_BLIPS, 1000)
SET_BIT(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_BLIP_HELP_DISPLAYED))
ENDIF
ENDIF
// Area sub-mission help text delayed by 5 seconds (afeter text/email is received)
ELIF (eRCMission = RC_BARRY_3A OR eRCMission = RC_NIGEL_1A OR eRCMission = RC_PAPARAZZO_3A)
ADD_HELP_TO_FLOW_QUEUE(sRCMissionDetails.rcBlipHelp, FHP_MEDIUM, 5000, FLOW_HELP_NEVER_EXPIRES, DEFAULT_HELP_TEXT_TIME, sRCMissionDetails.rcPlayableChars)
SET_BIT(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_BLIP_HELP_DISPLAYED))
// Otherwise display help text as soon as possible
ELSE
ADD_HELP_TO_FLOW_QUEUE(sRCMissionDetails.rcBlipHelp, FHP_MEDIUM, 0, FLOW_HELP_NEVER_EXPIRES, DEFAULT_HELP_TEXT_TIME, sRCMissionDetails.rcPlayableChars)
SET_BIT(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_BLIP_HELP_DISPLAYED))
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
// Check for being near RCM blip as an incorrect character
IF IS_PLAYER_PLAYING(PLAYER_ID())
IF NOT HAS_ONE_TIME_HELP_DISPLAYED(FHM_NEAR_FIRST_RCM_AS_WRONG_CHAR)
// Make sure this mission hasn't already been completed
IF IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_READY_TO_PLAY))
AND NOT IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[eRCMission].rcFlags, ENUM_TO_INT(RC_FLAG_COMPLETED))
IF NOT IS_MISSION_TRIGGERABLE_BY_CHARACTER(sRCMissionDetails.rcPlayableChars, GET_CURRENT_PLAYER_PED_ENUM())
AND NOT IS_AREA_MISSION(eRCMission)
AND NOT IS_RAMPAGE_MISSION(eRCMission)
AND NOT ARE_MULTIPLE_MISSIONS_AVAILABLE_AT_SAME_LOCATE(eRCMission)
AND NOT HAS_MISSION_JUST_COMPLETED_AT_SAME_LOCATE(eRCMission)
AND NOT g_RandomChars[eRCMission].rcLeaveAreaCheck
IF VDIST2(sRCMissionDetails.rcCoords, GET_ENTITY_COORDS(PLAYER_PED_ID())) < 81
STRING strHelp = ""
IF IS_BIT_SET(sRCMissionDetails.rcPlayableChars, ENUM_TO_INT(CHAR_FRANKLIN))
strHelp = "TRIG_RC_F"
ELIF IS_BIT_SET(sRCMissionDetails.rcPlayableChars, ENUM_TO_INT(CHAR_MICHAEL))
strHelp = "TRIG_RC_M"
ELSE
strHelp = "TRIG_RC_T"
ENDIF
IF NOT IS_STRING_NULL_OR_EMPTY(strHelp)
SWITCH GET_FLOW_HELP_MESSAGE_STATUS(strHelp)
CASE FHS_EXPIRED
ADD_HELP_TO_FLOW_QUEUE(strHelp, FHP_MEDIUM, 0, 1000, DEFAULT_HELP_TEXT_TIME, GET_CURRENT_PLAYER_PED_BIT())
BREAK
CASE FHS_DISPLAYED
SET_ONE_TIME_HELP_MESSAGE_DISPLAYED(FHM_NEAR_FIRST_RCM_AS_WRONG_CHAR)
g_txtFlowHelpLastDisplayed = ""
BREAK
ENDSWITCH
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Turns off all Random Character mission blips
PROC Turn_Off_Random_Character_Mission_Blips()
g_eRC_MissionIDs eRCMission
INT iBlip
REPEAT MAX_RC_BLIPS iBlip
SET_STATIC_BLIP_ACTIVE_STATE(eRCBlips[iBlip], FALSE)
bRCBlipActive[iBlip] = FALSE
ENDREPEAT
INT iMission
REPEAT MAX_RC_MISSIONS iMission
eRCMission = INT_TO_ENUM(g_eRC_MissionIDs, iMission)
IF g_RandomChars[eRCMission].rcBlipIndex <> -1
SET_STATIC_BLIP_ACTIVE_STATE(eRCBlips[g_RandomChars[eRCMission].rcBlipIndex], FALSE)
Clear_Random_Character_Mission_Blip_Index(g_RandomChars[eRCMission].rcBlipIndex)
ENDIF
g_RandomChars[eRCMission].rcBlipState = RC_BLIP_TURNED_OFF
ENDREPEAT
bRCBlipsTurnedOff = TRUE
ENDPROC
// ===========================================================================================================
// Cleanup
// ===========================================================================================================
/// PURPOSE:
/// Ensures script gets a chance to cleanup under specific circumstances (ie: moving from SP to MP)
PROC Script_Cleanup()
Turn_Off_Random_Character_Mission_Blips()
CPRINTLN(DEBUG_RANDOM_CHAR, "RandomChar_controller cleaned up.")
TERMINATE_THIS_THREAD()
ENDPROC
// ===========================================================================================================
// Script Loop
// ===========================================================================================================
SCRIPT
// This script needs to cleanup only when the game moves from SP to MP
IF (HAS_FORCE_CLEANUP_OCCURRED(FORCE_CLEANUP_FLAG_SP_TO_MP|FORCE_CLEANUP_FLAG_MAGDEMO))
CPRINTLN(DEBUG_RANDOM_CHAR, "RandomChar_controller.sc has been forced to cleanup (SP to MP)")
Script_Cleanup()
ENDIF
#IF IS_DEBUG_BUILD Setup_RC_Debug_Widgets() #ENDIF
// Initialise RC missions + blips
Initialise_Random_Character_Dynamic_Mission_Details()
Initialise_Random_Character_Mission_Blips()
WHILE (TRUE)
WAIT(0)
// We have gone on mission
IF IS_CURRENTLY_ON_MISSION_TO_TYPE(MISSION_TYPE_RANDOM_CHAR)
OR g_bPlayerLockedInToTrigger
CPRINTLN(DEBUG_RANDOM_CHAR, "RandomChar_controller cleaning up as we have gone on mission...")
Script_Cleanup()
ENDIF
#IF IS_DEBUG_BUILD
Maintain_RC_Debug_Widgets()
#ENDIF
IF Is_Controller_Safe_To_Run()
// Allow the blips when controller is safe to run
bRCBlipsTurnedOff = FALSE
// Update the frame that we are processing
iCurrentMissionFrame = (iCurrentMissionFrame+1) % MAX_FRAMES_TO_PROCESS_MISSIONS
INT iMission
REPEAT MAX_RC_MISSIONS iMission
// Only perform an update if this mission matches the current frame or has global update flag set
IF (iMission % MAX_FRAMES_TO_PROCESS_MISSIONS) = iCurrentMissionFrame
OR g_RandomChars[iMission].rcForceStateUpdate
Process_Random_Character_Mission_State(INT_TO_ENUM(g_eRC_MissionIDs, iMission))
ENDIF
ENDREPEAT
UPDATE_RC_REVEALED_IN_FOW_CHECKS()
UPDATE_BEVERLY_UNLOCK_WILDLIFE_PHOTOGRAPHY()
// Mission help text
Do_Barry3_Reminder_Help()
Do_Josh1_For_Sale_Signs_Help()
Do_Tonya1_Reminder_Help()
ELSE
// Controller not safe to run so turn off all blips
IF NOT bRCBlipsTurnedOff
Turn_Off_Random_Character_Mission_Blips()
ENDIF
ENDIF
ENDWHILE
// Script should never reach here. Always terminate with cleanup function.
ENDSCRIPT