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

1901 lines
62 KiB
Scheme
Executable File

// This section holds most/all of the USINGS for native commands and commonly used script helpers
USING "rage_builtins.sch"
USING "globals.sch"
USING "commands_audio.sch"
USING "commands_camera.sch"
USING "commands_event.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_player.sch"
USING "commands_script.sch"
USING "commands_streaming.sch"
USING "commands_task.sch"
USING "commands_vehicle.sch"
USING "commands_interiors.sch"
USING "commands_entity.sch"
USING "commands_physics.sch"
USING "script_maths.sch"
USING "script_blips.sch"
USING "script_ped.sch"
USING "script_player.sch"
USING "script_misc.sch"
USING "script_drawing.sch"
USING "locates_public.sch"
USING "timer_public.sch"
USING "mission_control_public.sch"
USING "brains.sch"
USING "ambience_run_checks.sch"
USING "flow_public_core_override.sch"
USING "model_enums.sch"
USING "help_at_location.sch"
USING "load_queue_public.sch"
/// PURPOSE: Enumeration for control types for minigame players so minigames can be used in both SP and MP
ENUM PLAYER_CONTROL_TYPE
CONTROL_NONE,
HUMAN_LOCAL,
HUMAN_LOCAL_MP,
HUMAN_NETWORK,
COMPUTER
ENDENUM
/// PURPOSE:
/// Helper function to get a color that matches hud
/// PARAMS:
/// hudColor - The hud color you want to match
/// RETURNS:
/// The INT_COLOR of the hud color you pass in
FUNC INT GetTextColor(HUD_COLOURS hudColor, INT iAlpha = -1)
INT iR,iG,iB,iA
GET_HUD_COLOUR(hudColor, iR, iG, iB, iA)
IF iAlpha = -1
RETURN INT_COLOUR(iR, iG, iB, iA)
ELSE
RETURN INT_COLOUR(iR, iG, iB, iAlpha)
ENDIF
ENDFUNC
/// PURPOSE:
/// Procedure that prints an integer as bits to the console (NOT the game screen)
/// Goes from most significant bit to least.
/// You'll need to follow it with PRINTNL() if you want to
PROC PRINTBITS(INT bits, INT spacing = 4)
INT i
FOR i=31 TO 0 STEP -1
IF IS_BIT_SET( bits, i)
PRINTINT(1)
ELSE
PRINTINT(0)
ENDIF
IF i%spacing = 0 AND i > 0
PRINTSTRING(" ")
ENDIF
ENDFOR
ENDPROC
//FUNC CONTROL_ACTION TRANSLATE_INSTR_BUTTON_TO_CONTROL_ACTION(TEXT_LABEL_63 instrButton)
// SWITCH(instrButton)
// CASE ICON_LSTICK_LEFTRIGHT
// RETURN INPUT_FRONTEND_AXIS_X //LEFTSTICKX
// BREAK
// CASE ICON_LSTICK_UPDOWN
// RETURN INPUT_FRONTEND_AXIS_Y //LEFTSTICKY
// BREAK
// CASE ICON_RSTICK_LEFTRIGHT
// RETURN INPUT_FRONTEND_RIGHT_AXIS_X //RIGHTSTICKX
// BREAK
// CASE ICON_RSTICK_UPDOWN
// RETURN INPUT_FRONTEND_RIGHT_AXIS_Y //RIGHTSTICKY
// BREAK
// CASE ICON_UP
// CASE ICON_DPAD_UP
// RETURN INPUT_FRONTEND_UP
// BREAK
// CASE ICON_DOWN
// CASE ICON_DPAD_DOWN
// RETURN INPUT_FRONTEND_DOWN
// BREAK
// CASE ICON_LEFT
// CASE ICON_DPAD_LEFT
// RETURN INPUT_FRONTEND_LEFT
// BREAK
// CASE ICON_RIGHT
// CASE ICON_DPAD_RIGHT
// RETURN INPUT_FRONTEND_RIGHT
// BREAK
// CASE ICON_BUTTON_START
// RETURN INPUT_FRONTEND_PAUSE
// BREAK
// CASE ICON_BUTTON_BACK
// RETURN INPUT_FRONTEND_SELECT
// BREAK
// CASE ICON_BUTTON_X
// RETURN INPUT_FRONTEND_X
// BREAK
// CASE ICON_BUTTON_Y
// RETURN INPUT_FRONTEND_Y
// BREAK
// CASE ICON_BUTTON_A
// RETURN INPUT_FRONTEND_ACCEPT
// BREAK
// CASE ICON_BUTTON_B
// RETURN INPUT_FRONTEND_CANCEL
// BREAK
// //CASE ICON_LSTICK_NONE
// // //RETURN ? //LEFTSHOCK
// //BREAK
// //CASE ICON_RSTICK_NONE
// // //RETURN ? //RIGHTSHOCK
// //BREAK
// CASE ICON_BUTTON_LB
// RETURN INPUT_FRONTEND_LB
// BREAK
// CASE ICON_BUTTON_LT
// RETURN INPUT_FRONTEND_LT
// BREAK
// CASE ICON_BUTTON_RB
// RETURN INPUT_FRONTEND_RB
// BREAK
// CASE ICON_BUTTON_RT
// RETURN INPUT_FRONTEND_RT
// BREAK
// ENDSWITCH
//
// RETURN INT_TO_ENUM(CONTROL_ACTION, -1)
//ENDFUNC
/// PURPOSE:
/// Gets a random float in [mean-deviation, mean+deviation]. Uses trig functions to cluster these around mean and spreads out toward +-deviation.
/// PARAMS:
/// mean - Value to cluster the randoms around
/// deviation - maximum deviator away from mean
/// RETURNS:
/// A random value in in [mean-deviation, mean+deviation], tending to cluster around mean.
FUNC FLOAT GET_RANDOM_FLOAT_POLAR(FLOAT mean = 0.0, FLOAT deviation = 1.0)
FLOAT randTri = SIN(GET_RANDOM_FLOAT_IN_RANGE(-180.0, 180))
IF randTri < 0
randTri++
ELSE
randTri--
ENDIF
RETURN mean + randTri * deviation
ENDFUNC
/// PURPOSE:
/// Fades down and lets us know when it's complete.
/// RETURNS:
/// TRUE when fully faded.
FUNC BOOL FADE_DOWN(INT iFadeTime = DEFAULT_FADE_TIME)
DO_SCREEN_FADE_OUT(iFadeTime)
WHILE NOT IS_SCREEN_FADED_OUT()
WAIT(0)
ENDWHILE
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Fades up and lets us know when it's complete.
/// RETURNS:
/// TRUE when fully faded.
FUNC BOOL FADE_UP(INT iFadeTime = DEFAULT_FADE_TIME)
DO_SCREEN_FADE_IN(iFadeTime)
WHILE NOT IS_SCREEN_FADED_IN()
WAIT(0)
ENDWHILE
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Make an array of strings point at an array of text labels.
PROC POINT_STRINGS_AT_TEXT_LABEL_3(STRING& sStrings[], TEXT_LABEL_3& sLabels[], INT iStrings)
INT iCounter
REPEAT iStrings iCounter
sStrings[iCounter] = TEXT_LABEL_TO_STRING(sLabels[iCounter])
ENDREPEAT
ENDPROC
/// PURPOSE:
/// Make an array of strings point at an array of text labels.
PROC POINT_STRINGS_AT_TEXT_LABEL_7(STRING& sStrings[], TEXT_LABEL_7& sLabels[], INT iStrings)
INT iCounter
REPEAT iStrings iCounter
sStrings[iCounter] = TEXT_LABEL_TO_STRING(sLabels[iCounter])
ENDREPEAT
ENDPROC
/// PURPOSE:
/// Make an array of strings point at an array of text labels.
PROC POINT_STRINGS_AT_TEXT_LABEL_15(STRING& sStrings[], TEXT_LABEL_15& sLabels[], INT iStrings)
INT iCounter
REPEAT iStrings iCounter
sStrings[iCounter] = TEXT_LABEL_TO_STRING(sLabels[iCounter])
ENDREPEAT
ENDPROC
/// PURPOSE:
/// Make an array of strings point at an array of text labels.
PROC POINT_STRINGS_AT_TEXT_LABEL_23(STRING& sStrings[], TEXT_LABEL_23& sLabels[], INT iStrings)
INT iCounter
REPEAT iStrings iCounter
sStrings[iCounter] = TEXT_LABEL_TO_STRING(sLabels[iCounter])
ENDREPEAT
ENDPROC
/// PURPOSE:
/// Make an array of strings point at an array of text labels.
PROC POINT_STRINGS_AT_TEXT_LABEL_31(STRING& sStrings[], TEXT_LABEL_31& sLabels[], INT iStrings)
INT iCounter
REPEAT iStrings iCounter
sStrings[iCounter] = TEXT_LABEL_TO_STRING(sLabels[iCounter])
ENDREPEAT
ENDPROC
/// PURPOSE:
/// Make an array of strings point at an array of text labels.
PROC POINT_STRINGS_AT_TEXT_LABEL_63(STRING& sStrings[], TEXT_LABEL_63& sLabels[], INT iStrings)
INT iCounter
REPEAT iStrings iCounter
sStrings[iCounter] = TEXT_LABEL_TO_STRING(sLabels[iCounter])
ENDREPEAT
ENDPROC
/// PURPOSE:
/// Prints the name of the script, and then the printed message
/// PARAMS:
/// strMessage - Message to print
/// strMessage[2-6] - Optional messages to print
/// separator - optional separator, will go between messages 2-6.
DEBUGONLY PROC DEBUG_MESSAGE(STRING strMessage, STRING strMessage2 = NULL, STRING strMessage3 = NULL, STRING strMessage4 = NULL, STRING strMessage5 = NULL, STRING strMessage6 = NULL, STRING separator = NULL )
PRINTSTRING(GET_THIS_SCRIPT_NAME())
PRINTSTRING(": ")
PRINTINT(GET_GAME_TIMER())
PRINTSTRING(": ")
IF NOT IS_STRING_NULL( strMessage ) PRINTSTRING(strMessage) ENDIF
IF NOT IS_STRING_NULL( strMessage2 ) PRINTSTRING( strMessage2 ) ENDIF
IF NOT IS_STRING_NULL( separator ) PRINTSTRING( separator ) ENDIF
IF NOT IS_STRING_NULL( strMessage3 ) PRINTSTRING( strMessage3 ) ENDIF
IF NOT IS_STRING_NULL( separator ) PRINTSTRING( separator ) ENDIF
IF NOT IS_STRING_NULL( strMessage4 ) PRINTSTRING( strMessage4 ) ENDIF
IF NOT IS_STRING_NULL( separator ) PRINTSTRING( separator ) ENDIF
IF NOT IS_STRING_NULL( strMessage5 ) PRINTSTRING( strMessage5 ) ENDIF
IF NOT IS_STRING_NULL( separator ) PRINTSTRING( separator ) ENDIF
IF NOT IS_STRING_NULL( strMessage6 ) PRINTSTRING( strMessage6 ) ENDIF
PRINTNL()
ENDPROC
/// PURPOSE:
/// Convert a given vector into a printable string format. Similar (but clunkier) usage to V2STR from RDR2.
/// PARAMS:
/// v - valid vector to be converted to prinatable format
/// RETURNS:
/// text label containing
FUNC TEXT_LABEL_31 V2STR(VECTOR v)
TEXT_LABEL_31 retVal = "<< "
retVal += CEIL(v.x)
retVal += ", "
retVal += CEIL(v.y)
retVal += ", "
retVal += CEIL(v.z)
retVal += " >>"
RETURN retVal
ENDFUNC
/// PURPOSE:
/// Prints the name of the script, and then the printed message. Throttles the given print to once a second
/// PARAMS:
/// strMessage - Message to print
/// messageThrottle
/// strMessage[2-6] - Optional messages to print
/// separator - optional separator, will go between messages 2-6.
/// throttleFrequency - sets how often it'll print (1 second or so)
PROC DEBUG_MESSAGE_PERIODIC(STRING strMessage, INT &fMessageThrottle, STRING strMessage2 = NULL, STRING strMessage3 = NULL, STRING strMessage4 = NULL, STRING strMessage5 = NULL, STRING strMessage6 = NULL, STRING separator = NULL, INT throttleFrequency = 1000 )
IF GET_GAME_TIMER() < fMessageThrottle + throttleFrequency
EXIT
ENDIF
fMessageThrottle = GET_GAME_TIMER()
DEBUG_MESSAGE( strMessage, strMessage2, strMessage3, strMessage4, strMessage5, strMessage6, separator )
ENDPROC
/// PURPOSE:
/// Prints the name of the script, and then the printed message. Throttles the given print to once a second
/// PARAMS:
/// strMessage - Message to print
/// messageThrottle
/// throttleFrequency - sets how often it'll print (1 second or so)
PROC CDEBUG_MESSAGE_OJ_TAXI_PERIODIC(STRING strMessage, INT &fMessageThrottle,INT throttleFrequency = 1000)
IF GET_GAME_TIMER() < fMessageThrottle + throttleFrequency
EXIT
ENDIF
fMessageThrottle = GET_GAME_TIMER()
CDEBUG1LN(DEBUG_OJ_TAXI,strMessage)
ENDPROC
/// PURPOSE:
/// Checks if a string is null/empty.
/// PARAMS:
/// sString - String to check if null/empty
/// RETURNS:
/// TRUE if string is null/empty, FALSE otherwise.
FUNC BOOL IS_STRING_EMPTY(STRING sString)
IF IS_STRING_NULL(sString)
//DEBUG_MESSAGE("IS_STRING_EMPTY: String is null!")
RETURN TRUE
ENDIF
IF (GET_LENGTH_OF_LITERAL_STRING(sString) <= 0)
//DEBUG_MESSAGE("IS_STRING_EMPTY: String is empty!")
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
//FUNC VECTOR CONVERT_ROTATION_TO_DIRECTION_VECTOR(VECTOR v_rot)
// RETURN <<-SIN(v_rot.z) * COS(v_rot.x), COS(v_rot.z) * COS(v_rot.x), SIN(v_rot.x)>>
//ENDFUNC
/// PURPOSE: Struct for building streaming helpers (if needed)
STRUCT STREAMED_MODEL
MODEL_NAMES thisModel = DUMMY_MODEL_FOR_SCRIPT
ENDSTRUCT
/// PURPOSE:
/// Finds the first uninitialized element in a streaming array. Returns -1 if the array is full.
/// PARAMS:
/// streamedModel - streaming array to search in
/// RETURNS:
/// The index of the first uninitialized element in a streaming array. -1 if the array is full. The return value must be tested before being used!
FUNC INT GET_EMPTY_STREAMED_MODEL(STREAMED_MODEL &streamedModel[])
INT modelIndex
REPEAT COUNT_OF(streamedModel) modelIndex
IF streamedModel[modelIndex].thisModel = DUMMY_MODEL_FOR_SCRIPT // IS THIS THE CORRECT NULL CHECK??
RETURN modelIndex
ENDIF
ENDREPEAT
RETURN -1
ENDFUNC
/// PURPOSE:
/// Adds a model to the first uninitialized element of a streaming array.
/// PARAMS:
/// streamedModel - Streaming array to add to
/// newModel - MODEL_NAMES to add
/// RETURNS:
/// TRUE if the model was successfully added, FALSE otherwise (the array was full)
FUNC BOOL ADD_STREAMED_MODEL(STREAMED_MODEL &streamedModel[], MODEL_NAMES newModel)
INT iModelIndex
REPEAT COUNT_OF(streamedModel) iModelIndex
IF streamedModel[iModelIndex].thisModel != DUMMY_MODEL_FOR_SCRIPT
IF streamedModel[iModelIndex].thisModel = newModel
DEBUG_MESSAGE("ADD_STREAMED_MODEL returning FALSE, model already in the index")
RETURN FALSE
ENDIF
ENDIF
ENDREPEAT
INT modelIndex = GET_EMPTY_STREAMED_MODEL(streamedModel)
IF modelIndex < 0 OR modelIndex >= COUNT_OF(streamedModel)
RETURN FALSE
ENDIF
streamedModel[modelIndex].thisModel = newModel
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Requests all initialized models in a streaming array
/// PARAMS:
/// streamedModel - Streaming array to request
PROC REQUEST_ALL_MODELS(STREAMED_MODEL &streamedModel[])
INT modelIndex
REPEAT COUNT_OF(streamedModel) modelIndex
IF streamedModel[modelIndex].thisModel != DUMMY_MODEL_FOR_SCRIPT // IS THIS THE CORRECT NULL CHECK??
REQUEST_MODEL(streamedModel[modelIndex].thisModel)
// REQUEST_COLLISION_FOR_MODEL(streamedModel[modelIndex].thisModel)
ENDIF
ENDREPEAT
ENDPROC
/// PURPOSE:
/// Checks to see if all models in a streaming array are in memory
/// PARAMS:
/// streamedModel - Streaming array to check
/// RETURNS:
/// TRUE if every initialized element in a streaming array is in memory, FALSE if at least one is not in memory
FUNC BOOL ARE_MODELS_STREAMED(STREAMED_MODEL &streamedModel[])
INT modelIndex
REPEAT COUNT_OF(streamedModel) modelIndex
IF streamedModel[modelIndex].thisModel != DUMMY_MODEL_FOR_SCRIPT // IS THIS THE CORRECT NULL CHECK??
IF NOT HAS_MODEL_LOADED(streamedModel[modelIndex].thisModel) //OR NOT HAS_COLLISION_FOR_MODEL_LOADED(streamedModel[modelIndex].thisModel)
IF NOT HAS_MODEL_LOADED(streamedModel[modelIndex].thisModel)
#IF IS_DEBUG_BUILD
DEBUG_MESSAGE("Waiting to stream model ", GET_MODEL_NAME_FOR_DEBUG(streamedModel[modelIndex].thisModel))
#ENDIF // IS_DEBUG_BUILD
ENDIF
// IF NOT HAS_COLLISION_FOR_MODEL_LOADED(streamedModel[modelIndex].thisModel)
// DEBUG_MESSAGE("Waiting to stream collision for ", GET_MODEL_NAME_FOR_DEBUG(streamedModel[modelIndex].thisModel))
// ENDIF
RETURN FALSE
ELSE
#IF IS_DEBUG_BUILD
DEBUG_MESSAGE("STREAMED IN ", GET_MODEL_NAME_FOR_DEBUG(streamedModel[modelIndex].thisModel))
#ENDIF // IS_DEBUG_BUILD
ENDIF
ENDIF
ENDREPEAT
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Sets all of the models in the streaming array as no longer needed
/// Only necessary for streaming in and out during missions...scripts auto-do this when they clean up
/// PARAMS:
/// streamedModel - Streaming array to request
PROC SET_ALL_MODELS_AS_NO_LONGER_NEEDED(STREAMED_MODEL &streamedModel[])
INT modelIndex
REPEAT COUNT_OF(streamedModel) modelIndex
IF streamedModel[modelIndex].thisModel != DUMMY_MODEL_FOR_SCRIPT // IS THIS THE CORRECT NULL CHECK??
SET_MODEL_AS_NO_LONGER_NEEDED(streamedModel[modelIndex].thisModel)
ENDIF
ENDREPEAT
ENDPROC
/// PURPOSE: Struct for building streaming helpers
STRUCT STREAMED_ANIM
STRING thisAnim
ENDSTRUCT
/// PURPOSE:
/// Finds the first uninitialized element in a streaming array. Returns -1 if the array is full.
/// PARAMS:
/// streamedAnim - streaming array to search in
/// RETURNS:
/// The index of the first uninitialized element in a streaming array. -1 if the array is full. The return value must be tested before being used!
FUNC INT GET_EMPTY_STREAMED_ANIM(STREAMED_ANIM &streamedAnim[])
INT animIndex
REPEAT COUNT_OF(streamedAnim) animIndex
IF IS_STRING_NULL(streamedAnim[animIndex].thisAnim)
RETURN animIndex
ENDIF
ENDREPEAT
RETURN -1
ENDFUNC
/// PURPOSE:
/// Adds an anim dict to the first uninitialized element of a streaming array.
/// PARAMS:
/// streamedAnim - Streaming array to add to
/// newAnim- STRING of anim name to add
/// RETURNS:
/// TRUE if the anim string was successfully added, FALSE otherwise (the array was full)
FUNC BOOL ADD_STREAMED_ANIM(STREAMED_ANIM &streamedAnim[], STRING newAnim)
INT iAnimIndex
REPEAT COUNT_OF(streamedAnim) iAnimIndex
IF NOT IS_STRING_NULL(streamedAnim[iAnimIndex].thisAnim)
IF ARE_STRINGS_EQUAL(streamedAnim[iAnimIndex].thisAnim, newAnim)
DEBUG_MESSAGE("ADD_STREAMED_ANIM returning FALSE, anim already in the index")
RETURN FALSE
ENDIF
ENDIF
ENDREPEAT
INT animIndex = GET_EMPTY_STREAMED_ANIM(streamedAnim)
IF animIndex < 0 OR animIndex >= COUNT_OF(streamedAnim)
DEBUG_MESSAGE("ADD_STREAMED_ANIM returning FALSE, array is full")
RETURN FALSE
ENDIF
streamedAnim[animIndex].thisAnim = newAnim
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Requests all initialized anims in a streaming array
/// PARAMS:
/// streamedAnim - Streaming array to request
PROC REQUEST_ALL_ANIMS(STREAMED_ANIM &streamedAnim[], LoadQueueLarge &sLoadQueue)
INT animIndex
REPEAT COUNT_OF(streamedAnim) animIndex
IF NOT IS_STRING_NULL(streamedAnim[animIndex].thisAnim)
LOAD_QUEUE_LARGE_ADD_ANIM_DICT(sLoadQueue, streamedAnim[animIndex].thisAnim)
DEBUG_MESSAGE("ADDING ANIM DICT TO QUEUE: ", streamedAnim[animIndex].thisAnim)
ENDIF
ENDREPEAT
ENDPROC
/// PURPOSE:
/// Checks to see if all anims in a streaming array are in memory
/// PARAMS:
/// streamedAnim - Streaming array to check
/// RETURNS:
/// TRUE if every initialized element in a streaming array is in memory, FALSE if at least one is not in memory
FUNC BOOL ARE_ANIMS_STREAMED(LoadQueueLarge &sLoadQueue)
RETURN HAS_LOAD_QUEUE_LARGE_LOADED(sLoadQueue)
ENDFUNC
/// PURPOSE:
/// Sets all of the models in the streaming array as no longer needed
/// Only necessary for streaming in and out during missions...scripts auto-do this when they clean up
/// PARAMS:
/// streamedModel - Streaming array to request
PROC SET_ALL_ANIMS_AS_NO_LONGER_NEEDED(STREAMED_ANIM &streamedAnim[])
INT animIndex
REPEAT COUNT_OF(streamedAnim) animIndex
IF NOT IS_STRING_NULL_OR_EMPTY(streamedAnim[animIndex].thisAnim)
REMOVE_ANIM_DICT(streamedAnim[animIndex].thisAnim)
ENDIF
ENDREPEAT
ENDPROC
/*
/// PURPOSE: Struct for building streaming helpers
STRUCT STREAMED_VEHICLE_RECORDING
INT thisRecordingIndex
STRING thisRecording
ENDSTRUCT
/// PURPOSE:
/// Finds the first uninitialized element in a streaming array. Returns -1 if the array is full.
/// PARAMS:
/// streamedRecordings - streaming array to search in
/// RETURNS:
/// The index of the first uninitialized element in a streaming array. -1 if the array is full. The return value must be tested before being used!
FUNC INT GET_EMPTY_STREAMED_VEHICLE_RECORDING(STREAMED_VEHICLE_RECORDING &streamedRecordings[])
INT recordingIndex
REPEAT COUNT_OF(streamedRecordings) recordingIndex
IF IS_STRING_NULL(streamedRecordings[recordingIndex].thisRecording)
RETURN recordingIndex
ENDIF
ENDREPEAT
RETURN -1
ENDFUNC
/// PURPOSE:
/// Adds an anim dict to the first uninitialized element of a streaming array.
/// PARAMS:
/// streamedRecordings - Streaming array to add to
/// newAnim- STRING of anim name to add
/// RETURNS:
/// TRUE if the anim string was successfully added, FALSE otherwise (the array was full)
FUNC BOOL ADD_STREAMED_VEHICLE_RECORDING(STREAMED_VEHICLE_RECORDING &streamedRecordings[], INT iNewRecording, STRING newRecording)
IF NOT DOES_VEHICLE_RECORDING_FILE_EXIST_IN_CDIMAGE(iNewRecording, newRecording)
TEXT_LABEL_63 tlTempAssert = "\n\nVehicle recording doesn't exist: "
tlTempAssert += iNewRecording
tlTempAssert += " in "
tlTempAssert += newRecording
SCRIPT_ASSERT(tlTempAssert)
RETURN FALSE
ENDIF
INT iRecordingIndex
REPEAT COUNT_OF(streamedRecordings) iRecordingIndex
IF NOT IS_STRING_NULL(streamedRecordings[iRecordingIndex].thisRecording)
IF ARE_STRINGS_EQUAL(streamedRecordings[iRecordingIndex].thisRecording, newRecording)
AND streamedRecordings[iRecordingIndex].thisRecordingIndex = iNewRecording
DEBUG_MESSAGE("ADD_STREAMED_VEHICLE_RECORDING returning FALSE, recording already in the index")
RETURN FALSE
ENDIF
ENDIF
ENDREPEAT
INT recordingIndex = GET_EMPTY_STREAMED_VEHICLE_RECORDING(streamedRecordings)
IF recordingIndex < 0 OR recordingIndex >= COUNT_OF(streamedRecordings)
DEBUG_MESSAGE("ADD_STREAMED_VEHICLE_RECORDING returning FALSE, couldn't find empty array index")
RETURN FALSE
ENDIF
IF iNewRecording < 0
DEBUG_MESSAGE("ADD_STREAMED_VEHICLE_RECORDING returning FALSE, iNewRecording is less than 0")
RETURN FALSE
ENDIF
streamedRecordings[recordingIndex].thisRecording = newRecording
streamedRecordings[recordingIndex].thisRecordingIndex = iNewRecording
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Requests all initialized anims in a streaming array
/// PARAMS:
/// streamedRecordings - Streaming array to request
PROC REQUEST_ALL_VEHICLE_RECORDINGS(STREAMED_VEHICLE_RECORDING &streamedRecordings[])
INT recordingIndex
REPEAT COUNT_OF(streamedRecordings) recordingIndex
IF NOT IS_STRING_NULL(streamedRecordings[recordingIndex].thisRecording)
REQUEST_VEHICLE_RECORDING(streamedRecordings[recordingIndex].thisRecordingIndex, streamedRecordings[recordingIndex].thisRecording)
#IF IS_DEBUG_BUILD
DEBUG_MESSAGE("REQUESTING RECORDING ", streamedRecordings[recordingIndex].thisRecording, " index ", GET_STRING_FROM_INT(streamedRecordings[recordingIndex].thisRecordingIndex))
#ENDIF // IS_DEBUG_BUILD
ENDIF
ENDREPEAT
ENDPROC
/// PURPOSE:
/// Checks to see if all anims in a streaming array are in memory
/// PARAMS:
/// streamedRecordings - Streaming array to check
/// RETURNS:
/// TRUE if every initialized element in a streaming array is in memory, FALSE if at least one is not in memory
FUNC BOOL ARE_VEHICLE_RECORDINGS_STREAMED(STREAMED_VEHICLE_RECORDING &streamedRecordings[])
INT recordingIndex
REPEAT COUNT_OF(streamedRecordings) recordingIndex
IF NOT IS_STRING_NULL(streamedRecordings[recordingIndex].thisRecording)
IF NOT HAS_VEHICLE_RECORDING_BEEN_LOADED(streamedRecordings[recordingIndex].thisRecordingIndex, streamedRecordings[recordingIndex].thisRecording)
#IF IS_DEBUG_BUILD
IF NOT DOES_VEHICLE_RECORDING_FILE_EXIST(streamedRecordings[recordingIndex].thisRecordingIndex, streamedRecordings[recordingIndex].thisRecording)
DEBUG_MESSAGE("Vehicle recording doesn't exist ", streamedRecordings[recordingIndex].thisRecording, " index ", GET_STRING_FROM_INT(streamedRecordings[recordingIndex].thisRecordingIndex))
ELSE
DEBUG_MESSAGE("Waiting to stream recording ", streamedRecordings[recordingIndex].thisRecording, " index ", GET_STRING_FROM_INT(streamedRecordings[recordingIndex].thisRecordingIndex))
ENDIF
#ENDIF // IS_DEBUG_BUILD
RETURN FALSE
ELSE
DEBUG_MESSAGE("STREAMED IN RECORDING ", streamedRecordings[recordingIndex].thisRecording)
ENDIF
ENDIF
ENDREPEAT
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Removes a streamed vehicle recording
/// PARAMS:
/// streamedRecordings - Streaming array to check
PROC REMOVE_STREAMED_VEHICLE_RECORDING(STREAMED_VEHICLE_RECORDING& streamedRecording)
IF NOT IS_STRING_NULL(streamedRecording.thisRecording)
REMOVE_VEHICLE_RECORDING(streamedRecording.thisRecordingIndex, streamedRecording.thisRecording)
streamedRecording.thisRecording = NULL
streamedRecording.thisRecordingIndex = -1
#IF IS_DEBUG_BUILD
DEBUG_MESSAGE("Removed vehicle recording ", streamedRecording.thisRecording, " index ", GET_STRING_FROM_INT(streamedRecording.thisRecordingIndex))
#ENDIF // IS_DEBUG_BUILD
ENDIF
ENDPROC
/// PURPOSE:
/// Removes a streamed vehicle recording from memory and clears all recoridng references from the array
/// PARAMS:
/// streamedRecordings - Streaming array to check
PROC SET_STREAMED_VEHICLE_RECORDING_AS_NO_LONGER_NEEDED(STREAMED_VEHICLE_RECORDING &streamedRecordings[], INT iRecordingIndex, STRING sRecording)
INT recordingIndex
REPEAT COUNT_OF(streamedRecordings) recordingIndex
IF NOT IS_STRING_NULL(streamedRecordings[recordingIndex].thisRecording)
IF streamedRecordings[recordingIndex].thisRecordingIndex = iRecordingIndex
AND ARE_STRINGS_EQUAL(streamedRecordings[recordingIndex].thisRecording, sRecording)
REMOVE_STREAMED_VEHICLE_RECORDING(streamedRecordings[recordingIndex])
ENDIF
ENDIF
ENDREPEAT
ENDPROC
*/
/// PURPOSE:
/// Draws a vector from V1 to V2, and takes the flat heading of that vector.
/// PARAMS:
/// V1 -
/// V2 -
/// RETURNS:
///
FUNC FLOAT GET_HEADING_BETWEEN_VECTORS(VECTOR V1, VECTOR V2)
RETURN GET_HEADING_FROM_VECTOR_2D(V2.x-V1.x,V2.y-V1.y)
ENDFUNC
/// PURPOSE:
/// Prints a help message once if the bit in the bitfieldis not set, and sets the passed in bit so I won't be set again
/// PARAMS:
/// msg - message to print
/// bitField - bitfield that stores the permanent print status
/// bit - bit to check
PROC PRINT_HELP_ONCE(STRING msg, INT &bitField, ENUM_TO_INT bit, BOOL bForever = FALSE)
IF IS_BITMASK_SET(ENUM_TO_INT(bitField), ENUM_TO_INT(bit))
EXIT
ENDIF
IF NOT bForever
PRINT_HELP(msg)
ELSE
PRINT_HELP_FOREVER(msg)
ENDIF
SET_BITMASK(bitField, ENUM_TO_INT(bit))
ENDPROC
/// bit - bit to check
PROC PRINT_HELP_ONCE_AT_WORLD_POSITION(STRING msg, INT &bitField, ENUM_TO_INT bit, VECTOR vWorldPosition, eARROW_DIRECTION iArrowPosition = 0, BOOL bForever = FALSE)
BOOL bUpdatePosition
IF IS_BITMASK_SET(ENUM_TO_INT(bitField), ENUM_TO_INT(bit))
IF NOT IS_THIS_HELP_MESSAGE_BEING_DISPLAYED(msg)
EXIT
ENDIF
bUpdatePosition = TRUE
ENDIF
IF NOT bUpdatePosition
HELP_AT_LOCATION(msg, vWorldPosition, iArrowPosition, PICK_INT(bForever, FLOATING_HELP_PRINT_FOREVER, DEFAULT_GOD_TEXT_TIME), FLOATING_HELP_FIND_NEXT_FREE_SLOT)
SET_BITMASK(bitField, ENUM_TO_INT(bit))
ENDIF
UPDATE_FLOATING_HELP_WORLD_POSITION(msg, vWorldPosition)
ENDPROC
/// bit - bit to check
PROC PRINT_HELP_ONCE_AT_ENTITY(STRING msg, INT &bitField, ENUM_TO_INT bit, ENTITY_INDEX eEntityPrint, eARROW_DIRECTION iArrowPosition = 0, BOOL bForever = FALSE)
BOOL bUpdatePosition
IF IS_BITMASK_SET(ENUM_TO_INT(bitField), ENUM_TO_INT(bit))
IF NOT IS_THIS_HELP_MESSAGE_BEING_DISPLAYED(msg)
EXIT
ENDIF
bUpdatePosition = TRUE
ENDIF
IF NOT bUpdatePosition
HELP_AT_ENTITY(msg, eEntityPrint, iArrowPosition, PICK_INT(bForever, FLOATING_HELP_PRINT_FOREVER, DEFAULT_GOD_TEXT_TIME), FLOATING_HELP_FIND_NEXT_FREE_SLOT)
SET_BITMASK(bitField, ENUM_TO_INT(bit))
ENDIF
ENDPROC
/// bit - bit to check
PROC PRINT_HELP_ONCE_AT_ENTITY_OFFSET(STRING msg, INT &bitField, ENUM_TO_INT bit, ENTITY_INDEX eEntityPrint, VECTOR vOffsetPosition, eARROW_DIRECTION iArrowPosition = 0, BOOL bForever = FALSE)
BOOL bUpdatePosition
IF IS_BITMASK_SET(ENUM_TO_INT(bitField), ENUM_TO_INT(bit))
IF NOT IS_THIS_HELP_MESSAGE_BEING_DISPLAYED(msg)
EXIT
ENDIF
bUpdatePosition = TRUE
ENDIF
IF NOT bUpdatePosition
HELP_AT_ENTITY_OFFSET(msg, eEntityPrint, vOffsetPosition, iArrowPosition, PICK_INT(bForever, FLOATING_HELP_PRINT_FOREVER, DEFAULT_GOD_TEXT_TIME), FLOATING_HELP_FIND_NEXT_FREE_SLOT)
SET_BITMASK(bitField, ENUM_TO_INT(bit))
ENDIF
UPDATE_FLOATING_HELP_ENTITY_OFFSET(msg, eEntityPrint, vOffsetPosition)
ENDPROC
/// bit - bit to check
PROC PRINT_HELP_ONCE_AT_SCREEN_POSITION(STRING msg, INT &bitField, ENUM_TO_INT bit, FLOAT fScreenX, FLOAT fScreenY, eARROW_DIRECTION iArrowPosition = 0, BOOL bForever = FALSE)
BOOL bUpdatePosition
IF IS_BITMASK_SET(ENUM_TO_INT(bitField), ENUM_TO_INT(bit))
IF NOT IS_THIS_HELP_MESSAGE_BEING_DISPLAYED(msg)
EXIT
ENDIF
bUpdatePosition = TRUE
ENDIF
IF NOT bUpdatePosition
HELP_AT_SCREEN_LOCATION(msg, fScreenX, fScreenY, iArrowPosition, PICK_INT(bForever, FLOATING_HELP_PRINT_FOREVER, DEFAULT_GOD_TEXT_TIME), FLOATING_HELP_FIND_NEXT_FREE_SLOT)
SET_BITMASK(bitField, ENUM_TO_INT(bit))
ENDIF
UPDATE_FLOATING_HELP_SCREEN_POSITION(msg, fScreenX, fScreenY)
ENDPROC
/// PURPOSE:
/// Prints an objective once if the bit in the bitfield is not set, and sets the passed in bit so I won't be set again
/// PARAMS:
/// msg - message to print
// Duration is an INT in milliseconds (1000 = 1 sec).
// Colour is an INT and should correspond to the person that speaks.
//PARAM NOTES: Colour 0 = White
// 1 = Red
// 2 = Green
// 3 = Blue
// 4 = Purple
// 5 = Yellow
// 6 = Black
/// bitField - bitfield that stores the permanent print status
/// bit - bit to check
PROC PRINT_NOW_ONCE(STRING msg, INT Duration, INT Colour, INT &bitField, ENUM_TO_INT bit)
IF IS_BITMASK_SET(ENUM_TO_INT(bitField), ENUM_TO_INT(bit))
EXIT
ENDIF
PRINT_NOW(msg, Duration, Colour)
SET_BITMASK(bitField, ENUM_TO_INT(bit))
ENDPROC
/// PURPOSE:
/// Extract the minutes and seconds from a struct timer
PROC GET_TIMER_TIME(structTimer myTimer, INT& hours, INT& minutes, INT& seconds)
FLOAT totalSeconds
hours = 0
totalSeconds = GET_TIMER_IN_SECONDS(myTimer)
minutes = FLOOR(totalSeconds / 60)
seconds = FLOOR(totalSeconds - (minutes * 60))
ENDPROC
/// PURPOSE:
/// To check if there are alive entities in an array
/// PARAMS:
/// myEntities - an array of entities
/// RETURNS:
/// BOOL - True if any entities are alive, False if all entities are dead
FUNC BOOL IS_ANY_ENTITY_ALIVE(ENTITY_INDEX& myEntities[])
INT idx
REPEAT COUNT_OF(myEntities) idx
IF NOT IS_ENTITY_DEAD(myEntities[idx])
RETURN TRUE
ENDIF
ENDREPEAT
RETURN FALSE
ENDFUNC
/* duplicate function - already in script_ped.sch
/// PURPOSE:
/// To get the distance between two entities
/// PARAMS:
/// ENTITY_INDEX ent1 - First ped
/// ENTITY_INDEX ent2 - Second ped
/// RETURNS:
/// FLOAT - Distance between entities, -1 if any entity is invalid
FUNC FLOAT GET_DISTANCE_BETWEEN_ENTITIES(ENTITY_INDEX ent1, ENTITY_INDEX ent2, BOOL bCheck3d = TRUE)
IF IS_ENTITY_DEAD(ent1) OR IS_ENTITY_DEAD(ent2)
SCRIPT_ASSERT("GET_DISTANCE_BETWEEN_ENTITIES: Invalid entity passed in")
RETURN -1.0
ELSE
RETURN GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(ent1), GET_ENTITY_COORDS(ent2), bCheck3d)
ENDIF
ENDFUNC
*/
/// PURPOSE:
/// To find the closest location to the current vector
/// PARAMS:
/// VECTOR vArray[] - Array of locations
/// VECTOR myCurLocation - The current location we're comparing against
/// RETURNS:
/// INT - the index of the closest location
FUNC INT FIND_CLOSEST_LOCATION_INDEX(VECTOR& vArray[], VECTOR myCurLocation, FLOAT minDistance = 0.0)
INT idx
INT closestIndex = 0
FLOAT fClosestDist = 1000000000 //VDIST2(vArray[0], myCurLocation) //assumes first locaiton is valid
FLOAT fDist
REPEAT COUNT_OF(vArray) idx
IF NOT IS_VECTOR_ZERO(vArray[idx])
fDist = VDIST2(vArray[idx], myCurLocation)
IF fDist < fClosestDist AND fDist > minDistance
fClosestDist = fDist
closestIndex = idx
ENDIF
ENDIF
ENDREPEAT
IF closestIndex = -1
SCRIPT_ASSERT("FIND_CLOSEST_LOCATION_INDEX returning -1")
ENDIF
RETURN closestIndex
ENDFUNC
/// PURPOSE:
/// To get the distance between an entity and a vector
/// PARAMS:
/// ENTITY_INDEX ent1 - First entity
/// VECTOR - vLocation
/// RETURNS:
/// FLOAT - Distance between an entity and vector, -1 if the entity is invalid
FUNC FLOAT GET_ENTITY_DISTANCE_FROM_LOCATION(ENTITY_INDEX ped1, VECTOR vLocation, BOOL bCheck3d = TRUE)
IF IS_ENTITY_DEAD(ped1)
PRINTLN("GET_ENTITY_DISTANCE_FROM_LOCATION: Invalid ped passed in")
RETURN -1.0
ELSE
RETURN GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(ped1),vLocation, bCheck3d)
ENDIF
ENDFUNC
/// RETURNS:
/// INT - the index of the closest location, -1 if no valid location found
FUNC INT FIND_CLOSEST_ENTITY_INDEX(ENTITY_INDEX& eArray[], VECTOR myCurLocation)
INT idx
INT closestIndex = -1
FLOAT fClosestDist = -1.0
VECTOR vTempCoord
FLOAT fCurDist
REPEAT COUNT_OF(eArray) idx
IF NOT IS_ENTITY_DEAD(eArray[idx])
vTempCoord = GET_ENTITY_COORDS(eArray[idx])
fCurDist = VDIST2(vTempCoord, myCurLocation)
IF closestIndex = -1
fClosestDist = fCurDist
closestIndex = idx
ELIF fCurDist < fClosestDist
fClosestDist = fCurDist
closestIndex = idx
ENDIF
ENDIF
ENDREPEAT
// IF closestIndex = -1
// SCRIPT_ASSERT("FIND_CLOSEST_ENTITY_INDEX returning -1")
// ENDIF
RETURN closestIndex
ENDFUNC
FUNC FLOAT GET_PLAYER_DISTANCE_FROM_ENTITY(ENTITY_INDEX myObject, BOOL bCheck3d = TRUE)
RETURN GET_DISTANCE_BETWEEN_ENTITIES(GET_PLAYER_PED(GET_PLAYER_INDEX()), myObject, bCheck3d)
ENDFUNC
FUNC FLOAT GET_PLAYER_DISTANCE_FROM_LOCATION(VECTOR vLocation, BOOL bCheck3d = TRUE)
RETURN GET_ENTITY_DISTANCE_FROM_LOCATION(GET_PLAYER_PED(GET_PLAYER_INDEX()), vLocation, bCheck3d)
ENDFUNC
/// PURPOSE:
/// Fly a plane level at desired speed indefinately
/// PARAMS:
/// Plane - The plane to fly in auto-pilot
/// fSpeed - The speed to fly the plane in auto-pilot
/// bRotInterp - Whether or not to interpolate the plane rotating
PROC PLANE_AUTOPILOT(VEHICLE_INDEX Plane, FLOAT fSpeed, BOOL bRotInterp, FLOAT xRot = 0.0, FLOAT yRot = 0.0)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_VEH_EXIT)
// Error checking.
IF IS_ENTITY_DEAD(Plane)
DEBUG_MESSAGE("PLANE_AUTOPILOT: Plane not alive, exiting!")
EXIT
ENDIF
// TODO: May want to add a check to see if vehicle is a plane.
// If Plane speed is below desired speed change it.
IF (GET_ENTITY_SPEED(Plane) < fSpeed)
SET_VEHICLE_FORWARD_SPEED(Plane, fSpeed)
ENDIF
// If Plane isn't level, rotate it (interp if desired).
VECTOR vRot = GET_ENTITY_ROTATION(Plane)
BOOL bPlaneRotated = FALSE
IF bRotInterp
FLOAT fRotInterp = GET_FRAME_TIME() * 45.0
IF (vRot.x < -fRotInterp)
bPlaneRotated = TRUE
vRot.x += fRotInterp
ELIF (vRot.x < xRot)
bPlaneRotated = TRUE
vRot.x = xRot
ELIF (vRot.x > fRotInterp)
bPlaneRotated = TRUE
vRot.x -= fRotInterp
ELIF (vRot.x > xRot)
bPlaneRotated = TRUE
vRot.x = xRot
ENDIF
IF (vRot.y < -fRotInterp)
bPlaneRotated = TRUE
vRot.y += fRotInterp
ELIF (vRot.y < yRot)
bPlaneRotated = TRUE
vRot.y = yRot
ELIF (vRot.y > fRotInterp)
bPlaneRotated = TRUE
vRot.y -= fRotInterp
ELIF (vRot.y > yRot)
bPlaneRotated = TRUE
vRot.y = yRot
ENDIF
ELSE
IF (vRot.x <> xRot)
bPlaneRotated = TRUE
vRot.x = xRot
ENDIF
IF (vRot.y <> yRot)
bPlaneRotated = TRUE
vRot.y = yRot
ENDIF
ENDIF
IF bPlaneRotated
SET_ENTITY_ROTATION(Plane, vRot)
ENDIF
ENDPROC
/// PURPOSE:
/// Fly a plane circling at desired speed indefinately
/// PARAMS:
/// Plane - The plane to fly in auto-circle
/// fSpeed - The speed to fly the plane in auto-circle
/// bRotInterp - Whether or not to interpolate the plane rotating
PROC PLANE_AUTOCIRCLE(VEHICLE_INDEX Plane, FLOAT fSpeed, BOOL bRotInterp)
// Error checking.
IF IS_ENTITY_DEAD(Plane)
DEBUG_MESSAGE("PLANE_AUTOCIRCLE: Plane not alive, exiting!")
EXIT
ENDIF
// TODO: May want to add a check to see if vehicle is a plane.
// If Plane speed is below desired speed change it.
IF (GET_ENTITY_SPEED(Plane) < fSpeed)
SET_VEHICLE_FORWARD_SPEED(Plane, fSpeed)
ENDIF
// If Plane isn't circling, rotate it (interp if desired).
VECTOR vRot = GET_ENTITY_ROTATION(Plane)
BOOL bPlaneRotated = FALSE
FLOAT fPitch = 5.0
FLOAT fRoll = 45.0
IF bRotInterp
FLOAT fRotInterp = GET_FRAME_TIME() * 45.0
IF (vRot.x < (fPitch - fRotInterp))
bPlaneRotated = TRUE
vRot.x += fRotInterp
ELIF (vRot.x < fPitch)
bPlaneRotated = TRUE
vRot.x = fPitch
ELIF (vRot.x > (fPitch + fRotInterp))
bPlaneRotated = TRUE
vRot.x -= fRotInterp
ELIF (vRot.x > fPitch)
bPlaneRotated = TRUE
vRot.x = fPitch
ENDIF
IF (vRot.y < (fRoll - fRotInterp))
bPlaneRotated = TRUE
vRot.y += fRotInterp
ELIF (vRot.y < fRoll)
bPlaneRotated = TRUE
vRot.y = fRoll
ELIF (vRot.y > (fRoll + fRotInterp))
bPlaneRotated = TRUE
vRot.y -= fRotInterp
ELIF (vRot.y > fRoll)
bPlaneRotated = TRUE
vRot.y = fRoll
ENDIF
ELSE
IF (vRot.x <> fPitch)
bPlaneRotated = TRUE
vRot.x = fPitch
ENDIF
IF (vRot.y <> fRoll)
bPlaneRotated = TRUE
vRot.y = fRoll
ENDIF
ENDIF
IF bPlaneRotated
SET_ENTITY_ROTATION(Plane, vRot)
ENDIF
ENDPROC
/// PURPOSE:
/// Snaps a position to the ground
/// PARAMS:
/// vSnappedPos - position to snap to the ground
/// fAddHeight - additional height to add after the snap, defaults to 0.0
/// RETURNS:
/// TRUE if the ground z was found, FALSE if it wasn't found and vector didn't change
FUNC BOOL SNAP_3D_COORD_TO_GROUND(VECTOR& vSnappedPos, FLOAT fAddHeight = 0.0)
FLOAT fGroundZ = 0.0
IF GET_GROUND_Z_FOR_3D_COORD(vSnappedPos, fGroundZ)
vSnappedPos.z = fGroundZ
vSnappedPos.z += fAddHeight
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Given an array of numbers, gets a random number inside a range that's not already inside of the array.
/// RETURNS:
/// A number if found, -1 otherwise.
FUNC INT GET_RANDOM_INT_IN_RANGE_NOT_IN_ARRAY(INT & iArray[], INT iArraySize, INT intMin, INT intMax)
BOOL bAlreadyPresent = TRUE
INT index, iRuns = 0, iResult
WHILE bAlreadyPresent AND iRuns < 50
bAlreadyPresent = FALSE
iResult = (GET_RANDOM_INT_IN_RANGE(0, 50000) % (intMax - intMin)) + intMin
PRINTLN("Trying number ", iResult)
FOR index = 0 TO iArraySize - 1
IF (iArray[index] = iResult)
bAlreadyPresent = TRUE
index = 999999
ENDIF
ENDFOR
iRuns += 1
ENDWHILE
IF iRuns = 50
RETURN -1
ENDIF
RETURN iResult
ENDFUNC
ENUM COUNTDOWN_UI_FLAGS
CNTDWN_UI_Played_3 = BIT0,
CNTDWN_UI_Played_2 = BIT1,
CNTDWN_UI_Played_1 = BIT2,
CNTDWN_UI_Played_Go = BIT3,
CNTDWN_UI_Played_Race_Go = BIT4
ENDENUM
STRUCT COUNTDOWN_UI
SCALEFORM_INDEX uiCountdown
INT iBitFlags
structTimer CountdownTimer
ENDSTRUCT
/// PURPOSE:
/// Provides the scripter with a handle to the countdown UI.
PROC REQUEST_MINIGAME_COUNTDOWN_UI(COUNTDOWN_UI & uiToRequest)
uiToRequest.uiCountdown = REQUEST_SCALEFORM_MOVIE("COUNTDOWN")
REQUEST_SCRIPT_AUDIO_BANK("HUD_321_GO")
ENDPROC
/// PURPOSE:
/// Lets the scripter know if the countdown UI is ready for use. Should be done in streaming checks.
FUNC BOOL HAS_MINIGAME_COUNTDOWN_UI_LOADED(COUNTDOWN_UI & uiToCheck)
RETURN (HAS_SCALEFORM_MOVIE_LOADED(uiToCheck.uiCountdown) AND REQUEST_SCRIPT_AUDIO_BANK("HUD_321_GO"))
ENDFUNC
/// PURPOSE:
/// Clears the scritpers handle to the countdown UI.
PROC RELEASE_MINIGAME_COUNTDOWN_UI(COUNTDOWN_UI & uiToRelease)
SET_SCALEFORM_MOVIE_AS_NO_LONGER_NEEDED(uiToRelease.uiCountdown)
RELEASE_SCRIPT_AUDIO_BANK()
ENDPROC
/// PURPOSE:
/// Private minigame countdown UI func.
PROC SET_MINIGAME_COUNTDOWN_UI_NUMBER(COUNTDOWN_UI & uiToUpdate, INT iNum)
INT iR, iG, iB, iA
GET_HUD_COLOUR(HUD_COLOUR_YELLOW, iR, iG, iB, iA)
BEGIN_SCALEFORM_MOVIE_METHOD(uiToUpdate.uiCountdown, "SET_MESSAGE")
SET_COLOUR_OF_NEXT_TEXT_COMPONENT(INT_TO_ENUM(HUD_COLOURS, -1))
BEGIN_TEXT_COMMAND_SCALEFORM_STRING("NUMBER")
ADD_TEXT_COMPONENT_INTEGER(ABSI(iNum))
END_TEXT_COMMAND_SCALEFORM_STRING()
SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(iR)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(iG)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(iB)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_BOOL(TRUE)
END_SCALEFORM_MOVIE_METHOD()
ENDPROC
/// PURPOSE:
/// Display an on-screen countdown UI
/// PARAMS:
/// uiToUpdate - Your countdown UI struct.
/// iCountdownLength - Between 0 and 3
/// bLoudVersion - Use the sounds in the HUD_MINIGAME_SOUNDSET or the ...
/// RETURNS:
/// TRUE one second after the countdown is done.
FUNC BOOL UPDATE_MINIGAME_COUNTDOWN_UI(COUNTDOWN_UI & uiToUpdate, BOOL bWaitExtraSec = TRUE, BOOL bSkipOnButtonPress = FALSE, BOOL bCancelTimerAtEnd = TRUE, INT iCountdownLength = 3, BOOL bLoudVersionSounds = TRUE, BOOL bTriggerGoQuick = FALSE, BOOL bIsRaceCreator = FALSE)
// Make sure we have a timer.
IF bIsRaceCreator
FORCE_TIMER_TO_USE_GAME_TIME(uiToUpdate.CountdownTimer, TRUE)
ENDIF
IF NOT IS_TIMER_STARTED(uiToUpdate.CountdownTimer)
PRINTLN("[TimerBroken] RESTARTING TIMER")
RESTART_TIMER_NOW(uiToUpdate.CountdownTimer)
ENDIF
// Always draw the movie.
SET_SCRIPT_GFX_DRAW_ORDER(GFX_ORDER_BEFORE_HUD)
DRAW_SCALEFORM_MOVIE(uiToUpdate.uiCountdown, 0.5, 0.5, 1.0, 1.0, 255, 255, 255, 100)
//Make sure Countdown Length is within limits
IF iCountdownLength > 3
iCountdownLength = 3
ELIF iCountdownLength < 0
iCountdownLength = 0
ENDIF
//select sfx
STRING sCountSound, sGoSound, sSoundSet
IF bLoudVersionSounds
sCountSound = "3_2_1"
sGoSound = "GO"
sSoundSet = "HUD_MINI_GAME_SOUNDSET"
ELSE
sCountSound = "3_2_1"
sGoSound = "GO"
sSoundSet = "321_GO_LOW_VOL_SOUNDSET"
ENDIF
// Get the timer values.
FLOAT fScratchTime
INT iTimerVal
IF bisRaceCreator
IF IS_TIMER_STARTED(uiToUpdate.CountdownTimer)
IF IS_TIMER_PAUSED(uiToUpdate.CountdownTimer)
PRINTLN("[TimerBroken] IS_TIMER_PAUSED : YES" )
fScratchTime = uiToUpdate.CountdownTimer.PauseTime
ELSE
fScratchTime = (GetGameTimerSeconds(TRUE) - uiToUpdate.CountdownTimer.StartTime)
IF fScratchTime < 0
PRINTLN("[TimerBroken] Timer is fucked, rebooting it")
fScratchTime = 0
CANCEL_TIMER(uiToUpdate.CountdownTimer)
IF IS_BITMASK_AS_ENUM_SET(uiToUpdate.iBitFlags, CNTDWN_UI_Played_3)
SET_BITMASK_AS_ENUM(uiToUpdate.iBitFlags, CNTDWN_UI_Played_Go)
INT iR, iG, iB, iA
GET_HUD_COLOUR(HUD_COLOUR_GREEN, iR, iG, iB, iA)
BEGIN_SCALEFORM_MOVIE_METHOD(uiToUpdate.uiCountdown, "SET_MESSAGE")
SCALEFORM_MOVIE_METHOD_ADD_PARAM_STRING("CNTDWN_GO")
SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(iR)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(iG)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(iB)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_BOOL(TRUE)
END_SCALEFORM_MOVIE_METHOD()
PRINTLN("[TimerBroken] Failsafe Bail" )
RETURN TRUE
ENDIF
RESTART_TIMER_NOW(uiToUpdate.CountdownTimer)
ENDIF
ENDIF
ELSE
PRINTLN("[TimerBroken] IS_TIMER_STARTED : NO" )
fScratchTime = uiToUpdate.CountdownTimer.StartTime
ENDIF
PRINTLN("[TimerBroken] fScratchTime ", fScratchTime)
iTimerVal = FLOOR(fScratchTime)
ELSE
iTimerVal = FLOOR(GET_TIMER_IN_SECONDS(uiToUpdate.CountdownTimer))
ENDIF
INT iSeconds = iTimerVal - iCountdownLength
BOOL bForceEnd = FALSE
PRINTLN("[TimerBroken] UPDATE_MINIGAME_COUNTDOWN_UI iTimerVal = ", iTimerVal, ", iSeconds = ", iSeconds, " uiToUpdate.CountdownTimer.StartTime = ", uiToUpdate.CountdownTimer.StartTime )
IF NOT IS_BITMASK_AS_ENUM_SET(uiToUpdate.iBitFlags, CNTDWN_UI_Played_Go)
// See if we're due to update.
IF (iSeconds >= -3) AND NOT IS_BITMASK_AS_ENUM_SET(uiToUpdate.iBitFlags, CNTDWN_UI_Played_3)
SET_BITMASK_AS_ENUM(uiToUpdate.iBitFlags, CNTDWN_UI_Played_3)
PRINTLN("Playing SFX for NUMBER 3")
PLAY_SOUND_FRONTEND(-1, sCountSound, sSoundSet)
SET_MINIGAME_COUNTDOWN_UI_NUMBER(uiToUpdate, iSeconds)
ELIF (iSeconds >= -2) AND NOT IS_BITMASK_AS_ENUM_SET(uiToUpdate.iBitFlags, CNTDWN_UI_Played_2)
SET_BITMASK_AS_ENUM(uiToUpdate.iBitFlags, CNTDWN_UI_Played_2)
PRINTLN("Playing SFX for NUMBER 2")
PLAY_SOUND_FRONTEND(-1, sCountSound, sSoundSet)
SET_MINIGAME_COUNTDOWN_UI_NUMBER(uiToUpdate, iSeconds)
ELIF (iSeconds >= -1) AND NOT IS_BITMASK_AS_ENUM_SET(uiToUpdate.iBitFlags, CNTDWN_UI_Played_1)
SET_BITMASK_AS_ENUM(uiToUpdate.iBitFlags, CNTDWN_UI_Played_1)
PRINTLN("Playing SFX for NUMBER 1")
PLAY_SOUND_FRONTEND(-1, sCountSound, sSoundSet)
SET_MINIGAME_COUNTDOWN_UI_NUMBER(uiToUpdate, iSeconds)
ELIF (iSeconds >= -1) AND NOT IS_BITMASK_AS_ENUM_SET(uiToUpdate.iBitFlags, CNTDWN_UI_Played_Race_Go)
IF ABSF(GET_TIMER_IN_SECONDS(uiToUpdate.CountdownTimer) - iCountdownLength) < 0.65 //countdown timer is going BACKWARDS!
IF bLoudVersionSounds OR bTriggerGoQuick
PRINTLN("Playing SFX for RACE GO")
SET_BITMASK_AS_ENUM(uiToUpdate.iBitFlags, CNTDWN_UI_Played_Race_Go)
PLAY_SOUND_FRONTEND(-1, sGoSound, sSoundSet)
ENDIF
ENDIF
ELIF (iSeconds >= 0) AND NOT IS_BITMASK_AS_ENUM_SET(uiToUpdate.iBitFlags, CNTDWN_UI_Played_Go)
IF NOT bLoudVersionSounds AND NOT bTriggerGoQuick
// These get played a but later, for some reason... Not going to change the architecture...
PRINTLN("Playing SFX for GO")
PLAY_SOUND_FRONTEND(-1, sGoSound, sSoundSet)
ENDIF
SET_BITMASK_AS_ENUM(uiToUpdate.iBitFlags, CNTDWN_UI_Played_Go)
INT iR, iG, iB, iA
GET_HUD_COLOUR(HUD_COLOUR_GREEN, iR, iG, iB, iA)
BEGIN_SCALEFORM_MOVIE_METHOD(uiToUpdate.uiCountdown, "SET_MESSAGE")
SCALEFORM_MOVIE_METHOD_ADD_PARAM_STRING("CNTDWN_GO")
SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(iR)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(iG)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(iB)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_BOOL(TRUE)
END_SCALEFORM_MOVIE_METHOD()
IF NOT bWaitExtraSec
bForceEnd = TRUE
ENDIF
ENDIF
ELSE
IF (iSeconds = 1)
bForceEnd = TRUE
ENDIF
ENDIF
//check for a button press to skip the countdown
IF (bSkipOnButtonPress AND IS_CUTSCENE_SKIP_BUTTON_PRESSED()) OR iTimerVal > 5
bForceEnd = TRUE
ENDIF
// If we've been ordered to leave, do so.
IF bForceEnd
IF bCancelTimerAtEnd
// Reset our vars.
uiToUpdate.iBitFlags = 0
CANCEL_TIMER(uiToUpdate.CountdownTimer)
ENDIF
// We're done.
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Packs a vector into a single INT
/// Bits 0-14 are the X value, and Bits 15-29 are the Y value.
/// Z value is dropped
/// Bit 30 is sign X and Bit 31 is SignY
/// All values are currently rounded, giving you ~1m accuracy
/// Does not work for values outside of (-32767, 32767)
/// PARAMS:
/// vPos - Vector to pack
/// RETURNS:
/// The packed int
FUNC INT VECTOR_FLAT_PACK(VECTOR vPacked)
//Truncate the x value to the first fifteen bits
// Or is with the Truncated 15 bits of Z, shifted up to the next 15 bits
INT retVal = (ROUND(ABSF(vPacked.x)) & 32767) | SHIFT_LEFT((ROUND(ABSF(vPacked.y)) & 32767), 15)
IF vPacked.x > 0
retVal |= BIT30
ENDIF
IF vPacked.y > 0
retVal |= (BIT30 * 2)
ENDIF
RETURN retVal
ENDFUNC
/// PURPOSE:
/// Unpacks an int that was packed with PACK_POSITION_FLAT
/// PARAMS:
/// packedPosition -
/// RETURNS:
///
FUNC VECTOR VECTOR_FLAT_UNPACK(INT packedPosition)
VECTOR retVal
INT unpackedX = packedPosition & 32767
INT unpackedZ = SHIFT_RIGHT(packedPosition, 15) & 32767
retVal.x = TO_FLOAT(unpackedX) // Mask off all but the first 15 bits
retVal.y = TO_FLOAT(unpackedZ) // Shift the next 15 bits down to first 15, then mask them off
IF (packedPosition & BIT30) = 0 // Reset the sign of the X value
retVal.x *= -1
ENDIF
IF (packedPosition & (BIT30 * 2)) = 0 // Reset the sign of the Z value
retVal.y *= -1
ENDIF
RETURN retVal
ENDFUNC
/// PURPOSE:
/// To convert the INT packed by VECTOR_FLAT_PACK to a world position, using the ground check
/// RETURNS:
///
FUNC VECTOR GET_WORLD_POSITION_FROM_PACKED_INT(INT packedPosition)
VECTOR retVal = VECTOR_FLAT_UNPACK(packedPosition)
IF NOT GET_GROUND_Z_FOR_3D_COORD(retVal, retVal.z)
SCRIPT_ASSERT("GET_GROUND_Z_FOR_3D_COORD failed in the designer defined func: GET_WORLD_POSITION_FROM_PACKED_INT")
ENDIF
RETURN retVal
ENDFUNC
/// PURPOSE:
/// Stops peds from spawning, walking, and activating scenarios in an area. Rectangle created is axis aligned.
/// Does cause some awkward standing around from peds
/// PARAMS:
/// vMin -
/// vMax -
/// RETURNS:
/// A scenario blocking index, store this and use it to remove the blocking area when the script cleans up
FUNC SCENARIO_BLOCKING_INDEX PREVENT_PEDS_FROM_BEING_IN_AREA(VECTOR vMin, VECTOR vMax)
SET_PED_NON_CREATION_AREA(vMin, vMax)
DISABLE_NAVMESH_IN_AREA(vMin, vMax, TRUE)
RETURN ADD_SCENARIO_BLOCKING_AREA(vMin, vMax)
ENDFUNC
/// PURPOSE:
/// Clean up all the work done by PREVENT_PEDS_FROM_BEING_IN_AREA
/// PARAMS:
/// sbi -
/// vMin -
/// vMax -
PROC ALLOW_PEDS_TO_BE_IN_AREA(SCENARIO_BLOCKING_INDEX sbi, VECTOR vMin, VECTOR vMax)
REMOVE_SCENARIO_BLOCKING_AREA(sbi)
CLEAR_PED_NON_CREATION_AREA()
DISABLE_NAVMESH_IN_AREA(vMin, vMax, FALSE)
ENDPROC
STRUCT SCRIPT_SCALEFORM_MEDAL_TOAST
SCALEFORM_INDEX siMovie
INT iDuration
structTimer movieTimer
ENDSTRUCT
FUNC SCALEFORM_INDEX REQUEST_MG_MEDAL_TOAST()
RETURN REQUEST_SCALEFORM_MOVIE("MP_MEDAL_FREEMODE")
ENDFUNC
PROC CLEAR_MG_MEDAL_TOAST(SCRIPT_SCALEFORM_MEDAL_TOAST& medalToast)
IF HAS_SCALEFORM_MOVIE_LOADED(medalToast.siMovie)
BEGIN_SCALEFORM_MOVIE_METHOD(medalToast.siMovie, "RESET_AWARDS_MOVIE")
END_SCALEFORM_MOVIE_METHOD()
ENDIF
ENDPROC
PROC CLEANUP_MG_MEDAL_TOAST(SCRIPT_SCALEFORM_MEDAL_TOAST& medalToast)
SET_SCALEFORM_MOVIE_AS_NO_LONGER_NEEDED(medalToast.siMovie)
medalToast.siMovie = NULL
ENDPROC
PROC SET_MG_MEDAL_TOAST(SCRIPT_SCALEFORM_MEDAL_TOAST &medalToast, STRING TexDict, STRING TexName, STRING sAward, STRING sTitle, STRING sSubTitle, INT iDuration)
medalToast.iDuration = iDuration
BEGIN_SCALEFORM_MOVIE_METHOD(medalToast.siMovie, "SHOW_AWARD_AND_MESSAGE")
SCALEFORM_MOVIE_METHOD_ADD_PARAM_STRING(sAward)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_STRING(sTitle)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_PLAYER_NAME(TexDict)//DisplayStruct.TextureDictionary
SCALEFORM_MOVIE_METHOD_ADD_PARAM_PLAYER_NAME(TexName)//DisplayStruct.TextureName)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_STRING(sSubTitle)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(0) // 107 = Bronze, 108 = Silver, 109 = gold, 110 = Plat
// SCALEFORM_MOVIE_METHOD_ADD_PARAM_STRING("FAKESTRING")
END_SCALEFORM_MOVIE_METHOD()
ENDPROC
FUNC BOOL UPDATE_MG_MEDAL_TOAST(SCRIPT_SCALEFORM_MEDAL_TOAST &medalToast)
IF NOT IS_TIMER_STARTED(medalToast.movieTimer)
RESTART_TIMER_NOW(medalToast.movieTimer)
ENDIF
// Hide the reticle.
HIDE_HUD_COMPONENT_THIS_FRAME(NEW_HUD_RETICLE)
DRAW_SCALEFORM_MOVIE_FULLSCREEN(medalToast.siMovie, 255, 255, 255, 255)
//always return true if the duration is -1
IF (medalToast.iDuration = -1)
RETURN TRUE
ENDIF
// Is it time to leave?
IF (GET_TIMER_IN_SECONDS(medalToast.movieTimer) * 1000.0 > TO_FLOAT(medalToast.iDuration))
CANCEL_TIMER(medalToast.movieTimer)
RETURN FALSE
ENDIF
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// prepares a music event
/// PARAMS: eBitMask is for a flag to make sure that we dont prepare/trigger more than once.
/// RETURNS:
/// Whether we were successful
///
FUNC BOOL MG_PREPARE_MUSIC_EVENT(STRING sMusicEvent, INT &iBitField, ENUM_TO_INT eBitMask)
IF NOT IS_BIT_SET(iBitField, ENUM_TO_INT(eBitmask))
IF PREPARE_MUSIC_EVENT(sMusicEvent)
PRINTLN("PREPARED MUSIC EVENT: ", sMusicEvent)
SET_BIT(iBitField, ENUM_TO_INT(eBitmask))
RETURN TRUE
ENDIF
ELSE
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL MG_TRIGGER_MUSIC_EVENT(STRING sMusicEvent, INT &iBitField, ENUM_TO_INT eBitMask)
IF NOT IS_BIT_SET(iBitField, ENUM_TO_INT(eBitmask))
IF PREPARE_MUSIC_EVENT(sMusicEvent)
IF TRIGGER_MUSIC_EVENT(sMusicEvent)
PRINTLN("TRIGGERED MUSIC EVENT: ", sMusicEvent)
SET_BIT(iBitField, ENUM_TO_INT(eBitmask))
RETURN TRUE
ELSE
PRINTLN("Couldn't trigger music event this frame: ", sMusicEvent)
ENDIF
ELSE
PRINTLN("music event not prepared")
ENDIF
ELSE
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// starts audio scene
/// PARAMS: eBitMask is for a flag to make sure that we dont prepare/trigger more than once.
/// RETURNS:
/// Whether we were successful
///
FUNC BOOL MG_START_AUDIO_SCENE(STRING sAudioSceneToStart, STRING sAudioSceneToStop, INT &iBitField, ENUM_TO_INT eBitMask)
IF NOT IS_STRING_NULL_OR_EMPTY(sAudioSceneToStop)
IF IS_AUDIO_SCENE_ACTIVE(sAudioSceneToStop)
STOP_AUDIO_SCENE(sAudioSceneToStop)
PRINTLN("STOPPED AUDIO SCENE: ", sAudioSceneToStop)
ELSE
PRINTLN("CANT STOP AN INACTIVE AUDIO SCENE: ", sAudioSceneToStop)
ENDIF
ELSE
PRINTLN("NULL STRING. NO AUDIO SCENE TO STOP ")
ENDIF
IF NOT IS_BIT_SET(iBitField, ENUM_TO_INT(eBitmask))
IF START_AUDIO_SCENE(sAudioSceneToStart)
PRINTLN("STARTED AUDIO SCENE: ", sAudioSceneToStart)
SET_BIT(iBitField, ENUM_TO_INT(eBitmask))
RETURN TRUE
ENDIF
PRINTLN("ATTEMPTING TO START AUDIO SCENE: ", sAudioSceneToStart)
ELSE
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
PROC MG_STOP_AUDIO_SCENE(STRING sAudioScene, INT &iBitField, ENUM_TO_INT eBitMask)
IF NOT IS_BIT_SET(iBitField, ENUM_TO_INT(eBitmask))
STOP_AUDIO_SCENE(sAudioScene)
PRINTLN("STOPPED AUDIO SCENE: ", sAudioScene)
SET_BIT(iBitField, ENUM_TO_INT(eBitmask))
ENDIF
ENDPROC
CONST_INT MG_FAIL_BLUR_EFFECT_TIME 3000
CONST_FLOAT MG_FAIL_BLUR_FAIL_EFFECT_SLO_MO 0.02
CONST_FLOAT MG_FAIL_BLUR_FAIL_OUT_EFFECT_SLO_MO 0.05
INT iFailScreenBits = 0
INT iDelayTimer = 0
ENUM MG_FAIL_BLUR_EFFECT_FLAGS
MG_FAIL_BLUR_HAS_BEEN_TRIGGERED,
MG_FAIL_BLUR_FAIL_OUT_HAS_BEEN_TRIGGERED,
MG_FAIL_BLUR_SLOW_MO_HAS_BEEN_TRIGGERED,
MG_FAIL_BLUR_PAUSED_GAME,
MG_FAIL_BLUR_DELAY_TIMER_SET
ENDENUM
PROC MG_RESET_FAIL_EFFECT_VARS()
iFailScreenBits = 0
iDelayTimer = 0
SET_SCRIPTS_SAFE_FOR_CUTSCENE(FALSE)
DISABLE_CELLPHONE(TRUE)
ENDPROC
PROC MG_DO_PAUSE_EFFECT(BOOL bPause)
BOOL bIgnore = FALSE
#IF IS_DEBUG_BUILD
bIgnore = g_bFlowAutoplayInProgress
#ENDIF
IF bIgnore = FALSE
// don't pause the game if we are doing an auto playthrough
IF bPause = TRUE
// pause the game
IF NOT IS_BIT_SET(iFailScreenBits, ENUM_TO_INT(MG_FAIL_BLUR_PAUSED_GAME))
PRINTLN("MG_DO_PAUSE_EFFECT() is pausing the game.")
SET_GAME_PAUSED(TRUE)
SET_BIT(iFailScreenBits, ENUM_TO_INT(MG_FAIL_BLUR_PAUSED_GAME))
ENDIF
ELSE
// unpause the game
IF IS_BIT_SET(iFailScreenBits, ENUM_TO_INT(MG_FAIL_BLUR_PAUSED_GAME))
PRINTLN( "MG_DO_PAUSE_EFFECT() is unpausing the game.")
SET_GAME_PAUSED(FALSE)
CLEAR_BIT(iFailScreenBits, ENUM_TO_INT(MG_FAIL_BLUR_PAUSED_GAME))
ENDIF
ENDIF
ENDIF
ENDPROC
PROC MG_DO_SLOW_MO_EFFECT(BOOL bStart, INT iFailEffect = 1, BOOL bIgnoreTriggeredFlags = FALSE)
IF bStart = TRUE
PRINTLN("MG_DO_SLOW_MO_EFFECT() is starting slo-mo now")
SET_BIT(iFailScreenBits, ENUM_TO_INT(MG_FAIL_BLUR_SLOW_MO_HAS_BEEN_TRIGGERED))
// start slow motion
#IF USE_TU_CHANGES
IF NOT NETWORK_IS_GAME_IN_PROGRESS()
IF iFailEffect = 1
SET_TIME_SCALE(FAIL_EFFECT_SLO_MO)
ELSE
SET_TIME_SCALE(FAIL_OUT_EFFECT_SLO_MO)
ENDIF
ENDIF
#ENDIF // USE_TU_CHANGES
#IF NOT USE_TU_CHANGES
IF iFailEffect = 1
SET_TIME_SCALE(FAIL_EFFECT_SLO_MO)
ELSE
SET_TIME_SCALE(FAIL_OUT_EFFECT_SLO_MO)
ENDIF
#ENDIF // NOT USE_TU_CHANGES
ELSE
IF IS_BIT_SET(iFailScreenBits, ENUM_TO_INT(MG_FAIL_BLUR_SLOW_MO_HAS_BEEN_TRIGGERED)) OR bIgnoreTriggeredFlags
PRINTLN( "MG_DO_SLOW_MO_EFFECT() is disabling slo-mo now")
#IF USE_TU_CHANGES
IF NOT NETWORK_IS_GAME_IN_PROGRESS()
SET_TIME_SCALE(1.0)
ENDIF
#ENDIF // USE_TU_CHANGES
#IF NOT USE_TU_CHANGES
SET_TIME_SCALE(1.0)
#ENDIF // NOT USE_TU_CHANGES
ENDIF
CLEAR_BIT(iFailScreenBits, ENUM_TO_INT(MG_FAIL_BLUR_SLOW_MO_HAS_BEEN_TRIGGERED))
ENDIF
ENDPROC
PROC MG_DO_FAIL_EFFECT(BOOL bStart, BOOL bIgnoreTriggeredFlags = FALSE)
STRING sEffect
SWITCH GET_CURRENT_PLAYER_PED_ENUM()
CASE CHAR_MICHAEL
sEffect = "DeathFailMichaelIn"
BREAK
CASE CHAR_FRANKLIN
sEffect = "DeathFailFranklinIn"
BREAK
CASE CHAR_TREVOR
sEffect = "DeathFailTrevorIn"
BREAK
ENDSWITCH
IF bStart = TRUE
IF NOT IS_BIT_SET(iFailScreenBits, ENUM_TO_INT(MG_FAIL_BLUR_HAS_BEEN_TRIGGERED)) OR bIgnoreTriggeredFlags
PRINTLN("MG_DO_FAIL_OUT_EFFECT() is starting effect now: ", sEffect)
ANIMPOSTFX_PLAY(sEffect, 0, FALSE)
SET_BIT(iFailScreenBits, ENUM_TO_INT(MG_FAIL_BLUR_HAS_BEEN_TRIGGERED))
MG_DO_SLOW_MO_EFFECT(TRUE, 1)
// trigger slow motion camera effect
SET_CAM_DEATH_FAIL_EFFECT_STATE(CAM_DEATH_FAIL_EFFECT_INTRO)
ENDIF
ELSE
IF IS_BIT_SET(iFailScreenBits, ENUM_TO_INT(MG_FAIL_BLUR_HAS_BEEN_TRIGGERED)) OR bIgnoreTriggeredFlags
PRINTLN("MG_DO_FAIL_OUT_EFFECT() is disabling fail effect now: ", sEffect)
ANIMPOSTFX_STOP(sEffect)
MG_DO_SLOW_MO_EFFECT(FALSE, 1, TRUE)
SET_CAM_DEATH_FAIL_EFFECT_STATE(CAM_DEATH_FAIL_EFFECT_INACTIVE)
ENDIF
CLEAR_BIT(iFailScreenBits, ENUM_TO_INT(MG_FAIL_BLUR_HAS_BEEN_TRIGGERED))
ENDIF
ENDPROC
PROC MG_DO_FAIL_OUT_EFFECT(BOOL bStart, BOOL bIgnoreTriggeredFlags = FALSE)
IF bStart = TRUE
MG_DO_FAIL_EFFECT(FALSE)
PRINTLN("MG_DO_FAIL_OUT_EFFECT() is starting fail out effect now")
ANIMPOSTFX_PLAY("DeathFailOut", 0, FALSE)
SET_BIT(iFailScreenBits, ENUM_TO_INT(MG_FAIL_BLUR_FAIL_OUT_HAS_BEEN_TRIGGERED))
// super slow motion
MG_DO_SLOW_MO_EFFECT(TRUE, 2)
// trigger camera effect
SET_CAM_DEATH_FAIL_EFFECT_STATE(CAM_DEATH_FAIL_EFFECT_OUTRO)
ELSE
IF IS_BIT_SET(iFailScreenBits, ENUM_TO_INT(MG_FAIL_BLUR_FAIL_OUT_HAS_BEEN_TRIGGERED)) OR bIgnoreTriggeredFlags
PRINTLN("MG_DO_FAIL_OUT_EFFECT() is disabling fail out effect now")
ANIMPOSTFX_STOP("DeathFailOut")
MG_DO_SLOW_MO_EFFECT(FALSE, 2, TRUE)
SET_CAM_DEATH_FAIL_EFFECT_STATE(CAM_DEATH_FAIL_EFFECT_INACTIVE)
ENDIF
CLEAR_BIT(iFailScreenBits, ENUM_TO_INT(MG_FAIL_BLUR_FAIL_OUT_HAS_BEEN_TRIGGERED))
ENDIF
ENDPROC
//PROC MG_START_FAIL_BLUR_EFFECT(BOOL bStart)
//
//
//ENDPROC
FUNC BOOL MG_DO_FAIL_BLUR_EFFECT()
//trigger blur effect and reset timer
IF NOT IS_BIT_SET(iFailScreenBits, ENUM_TO_INT(MG_FAIL_BLUR_HAS_BEEN_TRIGGERED))
AND NOT IS_BIT_SET(iFailScreenBits, ENUM_TO_INT(MG_FAIL_BLUR_FAIL_OUT_HAS_BEEN_TRIGGERED))
SETTIMERA(0)
MG_DO_FAIL_EFFECT(TRUE)
ENDIF
// things we need to block every frame
// remove any god text / subtitles
IF NOT IS_CUTSCENE_PLAYING()
CLEAR_PRINTS()
ENDIF
// disable the pause menu during fail screen
DISABLE_CONTROL_ACTION(FRONTEND_CONTROL, INPUT_FRONTEND_PAUSE)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_VEH_MOVE_LR)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_VEH_MOVE_UD)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_SELECT_WEAPON)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_AIM)
SUPPRESS_AMBIENT_PED_AGGRESSIVE_CLEANUP_THIS_FRAME()
IF TIMERA() > 1500 * FAIL_EFFECT_SLO_MO
IF NOT IS_BIT_SET(iFailScreenBits, ENUM_TO_INT(MG_FAIL_BLUR_FAIL_OUT_HAS_BEEN_TRIGGERED))
// not started fail out effect, do it now + pause the game
MG_DO_FAIL_OUT_EFFECT(TRUE)
SETTIMERB(0)
ELSE
IF TIMERB() > 1500 * FAIL_OUT_EFFECT_SLO_MO
OR IS_SCREEN_FADED_OUT() // used for skipping fail cut-scenes
// --------finished fail out effect ------------------------------
// start fading out
IF NOT IS_SCREEN_FADED_OUT()
IF NOT IS_SCREEN_FADING_OUT()
DO_SCREEN_FADE_OUT(1500)
ENDIF
ELSE
IF iDelayTimer = 0
iDelayTimer = GET_GAME_TIMER() + 1000
// -------------screen has faded out--------------------------
// Do necessary setup, and then move on to showing the replay screen
PRINTLN("Fail effect for script [", GET_THIS_SCRIPT_NAME(), "] completed.")
// disable player control
IF IS_PLAYER_PLAYING(PLAYER_ID())
SET_PLAYER_CONTROL(PLAYER_ID(), FALSE)
ENDIF
MG_DO_SLOW_MO_EFFECT(FALSE, 2, TRUE)
SET_SCRIPTS_SAFE_FOR_CUTSCENE(TRUE)
DISABLE_CELLPHONE(TRUE)
SET_FRONTEND_ACTIVE(FALSE)
SET_PAUSE_MENU_ACTIVE(FALSE)
CLEAR_HELP()
CLEAR_PRINTS()
// disable player control
IF IS_PLAYER_PLAYING(PLAYER_ID())
SET_PLAYER_CONTROL(PLAYER_ID(), FALSE)
ENDIF
MG_DO_FAIL_EFFECT(FALSE)
MG_DO_PAUSE_EFFECT(FALSE)
ELSE
IF GET_GAME_TIMER() < iDelayTimer
RETURN TRUE
ENDIF
ENDIF
ENDIF
ELSE
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Gets appropriate checkpoint alpha based on the player's distance from the checkpoint
FUNC INT MG_GET_CHECKPOINT_ALPHA(VECTOR vCheckpointPos, INT iAMin = 25, INT iAMax = 200)
FLOAT fDistMaxAlpha = 100.0
INT iA = 50
//add special min/max alpha distances depending on type of MG?
FLOAT fDist
IF NOT IS_ENTITY_DEAD(PLAYER_PED_ID())
fDist = GET_DISTANCE_BETWEEN_COORDS(vCheckpointPos, GET_ENTITY_COORDS(PLAYER_PED_ID()))
IF fDist > fDistMaxAlpha
iA = iAMax
ELSE
FLOAT fMod = ( (fDistMaxAlpha-fDist) / (fDistMaxAlpha) )
iA = iAMax - CEIL((iAMax-iAMin) * fMod)
//PRINTLN("MG_GET_CHECKPOINT_ALPHA: ", iA, " and fDis: ", fDist, "and fMod: ", fMod)
ENDIF
ENDIF
IF iA < iAMin
iA = iAMin
ENDIF
RETURN iA
ENDFUNC
/// PURPOSE: Gets appropriate checkpoint colour based on chevron type
FUNC HUD_COLOURS MG_GET_RACE_CHECKPOINT_DEFAULT_COLOUR()
RETURN HUD_COLOUR_YELLOW
ENDFUNC
/// PURPOSE:
/// Checks if the vehicle is very large and needs its own respawn space
/// Checks for blimps and helicopters. And some Planes.
/// Please add vehicles as needed.
/// PARAMS:
/// vehPlayer - player's saved vehicle index
/// RETURNS:
/// TRUE if the vehicle should be moved to more open space for respawn.
FUNC BOOL MINIGAME_DOES_VEHICLE_NEED_LARGE_RESPAWN_SPACE( VEHICLE_INDEX vehPlayer )
MODEL_NAMES eModel = GET_ENTITY_MODEL( vehPlayer )
SWITCH eModel
CASE BLIMP RETURN TRUE
CASE BLIMP2 RETURN TRUE
CASE CARGOBOB RETURN TRUE
CASE CARGOBOB2 RETURN TRUE
CASE CARGOBOB3 RETURN TRUE
CASE BUZZARD RETURN TRUE
CASE BUZZARD2 RETURN TRUE
CASE MAVERICK RETURN TRUE
CASE POLMAV RETURN TRUE
CASE SWIFT RETURN TRUE
CASE VALKYRIE RETURN TRUE
CASE ANNIHILATOR RETURN TRUE
CASE FROGGER RETURN TRUE
CASE FROGGER2 RETURN TRUE
CASE DODO RETURN TRUE
ENDSWITCH
RETURN FALSE
ENDFUNC
// END OF FILE! DO NOT ADD ANYTHING BELOW THIS LINE!