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

1117 lines
41 KiB
Python
Executable File

//
// Murder Mystery
//
//
// Written by Simon Bramwell Date: 13/08/14
//
// EASTER EGG. The player must find seven clues around the map in
// order to unlock a black and white, film noir filter.
//
//-------------------- INCLUDES --------------------
USING "rage_builtins.sch"
USING "globals.sch"
USING "brains.sch"
USING "ambient_globals.sch"
USING "random_events_public.sch"
USING "rc_helper_functions.sch"
USING "building_control_private.sch"
USING "noirEffects.sch"
//-------------------- ENUMS --------------------
ENUM MURDER_MYSTERY_STAGE // murder mystery clues
STAGE_CARVING_1_WEST_VINEWOOD,
STAGE_CARVING_2_ALTA,
STAGE_CARVING_3_CHINESE_THEATRE,
STAGE_CARVING_4_OBSERVATORY,
STAGE_SEA,
STAGE_LETTER,
STAGE_MINE,
STAGE_UNSET
ENDENUM
ENUM WARP_STAGE // the stages for warping into Solomon's office
WS_NOT_INSIDE,
WS_INSIDE,
WS_WARPING_IN,
WS_WARPING_OUT
ENDENUM
ENUM FLAGS
F_ARE_PC_CONTROLS_SETUP,
F_DISPLAY_HELP_WHEN_LEAVING_MINE,
F_IS_MINE_DOOR_HIDDEN,
F_RESOLVE_TRIGGER,
F_HAS_FADE_BEEN_REQUESTED,
F_HAS_TIME_BEEN_PAUSED,
F_IS_PLAYER_CONTROL_DISABLED
ENDENUM
//-------------------- CONSTS --------------------
CONST_INT DO_DELAYED_CLEANUP 1
CONST_INT NUM_STAGES STAGE_UNSET
CONST_FLOAT CLEANUP_DIST 175.0 // brain activation range is 100 units
CONST_FLOAT CARVING_ACTIVATE_DIST 5.0
CONST_FLOAT SEA_ACTIVATE_DIST 5.0
CONST_FLOAT MINE_ACTIVATE_DIST 2.0
FLOAT CLEANUP_DIST2 = CLEANUP_DIST * CLEANUP_DIST
FLOAT CARVING_ACTIVATE_DIST2 = CARVING_ACTIVATE_DIST * CARVING_ACTIVATE_DIST
FLOAT SEA_ACTIVATE_DIST2 = SEA_ACTIVATE_DIST * SEA_ACTIVATE_DIST
FLOAT MINE_ACTIVATE_DIST2 = MINE_ACTIVATE_DIST * MINE_ACTIVATE_DIST
CONST_FLOAT HIDE_RADIUS 200.0
//-------------------- VECTORS AND HEADINGS --------------------
VECTOR vExtOfficeWarpPos = <<-1011.7365, -480.2540, 39.0734>>
VECTOR vIntOfficeWarpPos = <<-1003.0779, -477.9620, 49.1374>>
VECTOR vExtOfficePlayerPos = <<-1018.3756, -483.9436, 36.0964>>
FLOAT fExtOfficePlayerHeading = 114.7664
VECTOR vIntOfficePlayerPos = <<-1005.1911, -478.2104, 49.0262>>
FLOAT fIntOfficePlayerHeading = 80.6659
VECTOR vOnFootLocate = <<1.0, 1.0, LOCATE_SIZE_HEIGHT>>
VECTOR vMineDoorPos = <<-596.05000, 2088.5600, 130.590>>
//-------------------- MISC VARS --------------------
INTERIOR_INSTANCE_INDEX intOffice
WARP_STAGE eOfficeWarpStage = WS_NOT_INSIDE
INT iUpdateLetterCamStage = 0
CAMERA_INDEX camLetter
INT iDoor
PED_INDEX pedDeadBodyMine
OBJECT_INDEX objFilmCan
PED_INDEX pedDeadBodySea
STRING sNoirFoundClue = "FOUND_CLUE"
STRING sNoirSetName = "NOIR_FILTER_SOUNDS"
INT iNoirSFXSoundID
VECTOR vPlayerPos
MURDER_MYSTERY_STAGE eTriggerStage = STAGE_UNSET
INT iTimer
INT iTrackedPoint = 0
STRING sDeadBodySeaAnimDict = "missmurder"
STRING sDeadBodySeaAnim = "idle"
STRING sHelpTextReadLetter = "MM_LETREAD"
STRING sHelpTextStopReadLetter = "MM_LETCANC"
INT iFlags = 0
//-------------------- DEBUG VARS --------------------
#IF IS_DEBUG_BUILD
CONST_INT TOTAL_FLAGS COUNT_OF( FLAGS )
// Main
WIDGET_GROUP_ID widMain
TEXT_WIDGET_ID twidTriggerStage
TEXT_WIDGET_ID twidUnlockStage
BOOL bBlockCleanup
BOOL bDelayedCleanup = FALSE
INT iDelayTime
BOOL bToggleBlips
BLIP_INDEX blpUnlocks[ NUM_STAGES ]
BOOL bTerminate
BOOL bCycleFilter
BOOL bAddNoirPhoneContact
BOOL bIsPlayerInMine
// Clues
INT iUnlockStage = 0
INT iOldUnlockStage = -1
BOOL bIsUnlocked
BOOL bSetUnlocked
BOOL bSetNotUnlocked
// Flags
BOOL bFlags[ TOTAL_FLAGS ]
#ENDIF
//-------------------- FLAGS PROCS --------------------
PROC SET_FLAG( FLAGS flag, BOOL bEnable )
IF( bEnable )
SET_BIT( iFlags, ENUM_TO_INT( flag ) )
ELSE
CLEAR_BIT( iFlags, ENUM_TO_INT( flag ) )
ENDIF
ENDPROC
FUNC BOOL IS_FLAG_SET( FLAGS flag )
RETURN IS_BIT_SET( iFlags, ENUM_TO_INT( flag ) )
ENDFUNC
//-------------------- PC CONTROL PROCS --------------------
PROC SETUP_PC_CONTROLS()
IF( IS_PC_VERSION() )
IF( NOT IS_FLAG_SET( F_ARE_PC_CONTROLS_SETUP ) )
INIT_PC_SCRIPTED_CONTROLS( "MYSTERY LETTER CAM" )
SET_FLAG( F_ARE_PC_CONTROLS_SETUP, TRUE )
ENDIF
ENDIF
ENDPROC
PROC CLEANUP_PC_CONTROLS()
IF( IS_PC_VERSION() )
IF( IS_FLAG_SET( F_ARE_PC_CONTROLS_SETUP ) )
SHUTDOWN_PC_SCRIPTED_CONTROLS()
SET_FLAG( F_ARE_PC_CONTROLS_SETUP, FALSE )
ENDIF
ENDIF
ENDPROC
//-------------------- CLEANUP PROCS --------------------
PROC CLEANUP_TRIGGER_STAGE()
SWITCH( eTriggerStage )
CASE STAGE_CARVING_1_WEST_VINEWOOD
CASE STAGE_CARVING_2_ALTA
CASE STAGE_CARVING_3_CHINESE_THEATRE
CASE STAGE_CARVING_4_OBSERVATORY
DESTROY_TRACKED_POINT( iTrackedPoint )
iTrackedPoint = 0
BREAK
CASE STAGE_SEA
IF( DOES_ENTITY_EXIST( pedDeadBodySea ) )
SET_PED_AS_NO_LONGER_NEEDED( pedDeadBodySea )
ENDIF
REMOVE_ANIM_DICT( sDeadBodySeaAnimDict )
BREAK
CASE STAGE_LETTER
IF( IS_DOOR_REGISTERED_WITH_SYSTEM( iDoor ) )
REMOVE_DOOR_FROM_SYSTEM( iDoor )
ENDIF
IF( DOES_CAM_EXIST( camLetter ) )
DESTROY_CAM( camLetter )
ENDIF
IF( IS_FLAG_SET( F_HAS_FADE_BEEN_REQUESTED ) )
DO_SCREEN_FADE_IN( 0 )
ENDIF
IF( IS_FLAG_SET( F_HAS_TIME_BEEN_PAUSED ) )
PAUSE_CLOCK( FALSE )
ENDIF
IF( IS_FLAG_SET( F_IS_PLAYER_CONTROL_DISABLED ) )
SET_PLAYER_CONTROL( PLAYER_ID(), TRUE )
ENDIF
IF( IS_THIS_HELP_MESSAGE_BEING_DISPLAYED( sHelpTextReadLetter )
OR IS_THIS_HELP_MESSAGE_BEING_DISPLAYED( sHelpTextStopReadLetter ) )
CLEAR_HELP()
ENDIF
BREAK
CASE STAGE_MINE
IF( DOES_ENTITY_EXIST( pedDeadBodyMine ) )
SET_PED_AS_NO_LONGER_NEEDED( pedDeadBodyMine )
ENDIF
IF( DOES_ENTITY_EXIST( objFilmCan ) )
SET_OBJECT_AS_NO_LONGER_NEEDED( objFilmCan )
ENDIF
// Make sure the mine door reappears
IF( IS_FLAG_SET( F_IS_MINE_DOOR_HIDDEN ) )
REMOVE_MODEL_HIDE( vMineDoorPos, HIDE_RADIUS, PROP_MINESHAFT_DOOR, FALSE )
CPRINTLN( DEBUG_SIMON, "Unhiding mine door model" )
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC SCRIPT_CLEANUP()
CPRINTLN( DEBUG_SIMON, "Cleaning up murderMystery.sc" )
CLEANUP_TRIGGER_STAGE()
CLEANUP_PC_CONTROLS()
TERMINATE_THIS_THREAD()
ENDPROC
//-------------------- UNLOCK PROCS --------------------
#IF IS_DEBUG_BUILD
FUNC STRING DEBUG_GET_MURDER_MYSTERY_STAGE_AS_STRING( MURDER_MYSTERY_STAGE stage )
SWITCH( stage )
CASE STAGE_CARVING_1_WEST_VINEWOOD RETURN "STAGE_CARVING_1_WEST_VINEWOOD" BREAK
CASE STAGE_CARVING_2_ALTA RETURN "STAGE_CARVING_2_ALTA" BREAK
CASE STAGE_CARVING_3_CHINESE_THEATRE RETURN "STAGE_CARVING_3_CHINESE_THEATRE" BREAK
CASE STAGE_CARVING_4_OBSERVATORY RETURN "STAGE_CARVING_4_OBSERVATORY" BREAK
CASE STAGE_SEA RETURN "STAGE_SEA" BREAK
CASE STAGE_LETTER RETURN "STAGE_LETTER" BREAK
CASE STAGE_MINE RETURN "STAGE_MINE" BREAK
ENDSWITCH
RETURN "UNKNOWN"
ENDFUNC
#ENDIF
FUNC BOOL IS_STAGE_UNLOCKED( MURDER_MYSTERY_STAGE stage )
RETURN IS_BIT_SET( g_savedGlobals.sAmbient.iMurderMysteryFlags, ENUM_TO_INT( stage ) )
ENDFUNC
//PURPOSE: Unlock/lock a stage, also play sfx and save
PROC SET_STAGE_UNLOCKED( MURDER_MYSTERY_STAGE stage, BOOL bUnlocked = TRUE, BOOL bSuppressSound = FALSE )
IF( bUnlocked )
SET_BIT( g_savedGlobals.sAmbient.iMurderMysteryFlags, ENUM_TO_INT( stage ) )
IF( NOT bSuppressSound )
PLAY_SOUND_FRONTEND( iNoirSFXSoundID, sNoirFoundClue, sNoirSetName, FALSE )
ENDIF
MAKE_AUTOSAVE_REQUEST()
CPRINTLN( DEBUG_SIMON, "Unlocking ", DEBUG_GET_MURDER_MYSTERY_STAGE_AS_STRING( stage ) )
ELSE
CLEAR_BIT( g_savedGlobals.sAmbient.iMurderMysteryFlags, ENUM_TO_INT( stage ) )
CPRINTLN( DEBUG_SIMON, "Locking ", DEBUG_GET_MURDER_MYSTERY_STAGE_AS_STRING( stage ) )
ENDIF
ENDPROC
FUNC INT IPOW( INT base, INT exponent )
IF( exponent = 0 )
RETURN 1
ELSE
RETURN base * IPOW( base, exponent - 1 )
ENDIF
ENDFUNC
//PURPOSE: Are all the stages before the given stage unlocked
FUNC BOOL ARE_ALL_STAGES_PRECEDING_UNLOCKED( MURDER_MYSTERY_STAGE stage )
INT iStage = ENUM_TO_INT( stage )
IF( iStage = 0 )
RETURN TRUE
ELSE
INT iSelection = GET_BITS_IN_RANGE( g_savedGlobals.sAmbient.iMurderMysteryFlags, 0, iStage - 1 )
RETURN iSelection = IPOW( 2, iStage ) - 1
ENDIF
ENDFUNC
FUNC BOOL ARE_ALL_STAGES_UNLOCKED()
RETURN g_savedGlobals.sAmbient.iMurderMysteryFlags = 127
ENDFUNC
//-------------------- STAGE DATA PROCS --------------------
//PURPOSE: Gets the centre of the stage trigger area, should match the values in //depot/gta5/build/dev_ng/common/data/levels/gta5/ambient.ipl
FUNC VECTOR GET_WORLD_POINT_COORD_FOR_STAGE( MURDER_MYSTERY_STAGE stage )
SWITCH( stage )
CASE STAGE_CARVING_1_WEST_VINEWOOD RETURN <<1.8941, 309.6565, 111.0097>> BREAK
CASE STAGE_CARVING_2_ALTA RETURN <<190.1696, -431.7692, 42.1372>> BREAK
CASE STAGE_CARVING_3_CHINESE_THEATRE RETURN <<283.3024, 212.6587, 104.5986>> BREAK
CASE STAGE_CARVING_4_OBSERVATORY RETURN <<-425.1273, 1064.8011, 323.8097>> BREAK
CASE STAGE_SEA RETURN <<-3174.901, 3034.061, -35.645>> BREAK // not accurate to values in ipl
CASE STAGE_LETTER RETURN <<-1008.69, -476.4341, 49.9544>> BREAK
CASE STAGE_MINE RETURN <<-544.11, 1986.58, 127.03>> BREAK
ENDSWITCH
RETURN <<0.0, 0.0, 0.0>>
ENDFUNC
//PURPOSE: Gets the unlock position for the stage
FUNC VECTOR GET_UNLOCK_COORD_FOR_STAGE( MURDER_MYSTERY_STAGE stage )
SWITCH( stage )
CASE STAGE_CARVING_1_WEST_VINEWOOD RETURN <<1.8941, 309.6565, 111.0097>> BREAK
CASE STAGE_CARVING_2_ALTA RETURN <<190.1696, -431.7692, 42.1372>> BREAK
CASE STAGE_CARVING_3_CHINESE_THEATRE RETURN <<283.3024, 212.6587, 104.5986>> BREAK
CASE STAGE_CARVING_4_OBSERVATORY RETURN <<-425.1273, 1064.8011, 323.8097>> BREAK
CASE STAGE_SEA RETURN <<-3160.9604, 3005.6284, -39.9701>> BREAK
CASE STAGE_LETTER RETURN <<-1008.1416, -476.2818, 49.9633>> BREAK
CASE STAGE_MINE RETURN <<-544.11, 1986.58, 127.03>> BREAK
ENDSWITCH
RETURN <<0.0, 0.0, 0.0>>
ENDFUNC
//PURPOSE: Gets the range at which the stage should unlock from the position in GET_UNLOCK_COORD_FOR_STAGE
FUNC FLOAT GET_UNLOCK_DIST2_FOR_STAGE( MURDER_MYSTERY_STAGE stage )
SWITCH( stage )
CASE STAGE_CARVING_1_WEST_VINEWOOD RETURN CARVING_ACTIVATE_DIST2 BREAK
CASE STAGE_CARVING_2_ALTA RETURN CARVING_ACTIVATE_DIST2 BREAK
CASE STAGE_CARVING_3_CHINESE_THEATRE RETURN CARVING_ACTIVATE_DIST2 BREAK
CASE STAGE_CARVING_4_OBSERVATORY RETURN CARVING_ACTIVATE_DIST2 BREAK
CASE STAGE_SEA RETURN SEA_ACTIVATE_DIST2 BREAK
CASE STAGE_LETTER RETURN MINE_ACTIVATE_DIST2 BREAK
CASE STAGE_MINE RETURN 4.0 BREAK
ENDSWITCH
RETURN 0.0
ENDFUNC
//PURPOSE: Gets the range at which the stage should cleanup from the position in GET_WORLD_POINT_COORD_FOR_STAGE
FUNC FLOAT GET_ACTIVE_DIST2_FOR_STAGE( MURDER_MYSTERY_STAGE stage )
SWITCH( stage )
CASE STAGE_CARVING_1_WEST_VINEWOOD RETURN CLEANUP_DIST2 BREAK
CASE STAGE_CARVING_2_ALTA RETURN CLEANUP_DIST2 BREAK
CASE STAGE_CARVING_3_CHINESE_THEATRE RETURN CLEANUP_DIST2 BREAK
CASE STAGE_CARVING_4_OBSERVATORY RETURN CLEANUP_DIST2 BREAK
CASE STAGE_SEA RETURN CLEANUP_DIST2 BREAK
CASE STAGE_LETTER RETURN CLEANUP_DIST2 BREAK
CASE STAGE_MINE RETURN CLEANUP_DIST2 BREAK
ENDSWITCH
RETURN 0.0
ENDFUNC
FUNC BOOL VECTOR_COMPARE( VECTOR a, VECTOR b, FLOAT fTolerance = 0.5 )
RETURN VDIST( a, b ) <= fTolerance
ENDFUNC
//PURPOSE: Is the player inside the unlock range of the stage
FUNC BOOL IS_TRIGGER_STAGE_IN_UNLOCK_RANGE()
RETURN VDIST2( GET_UNLOCK_COORD_FOR_STAGE( eTriggerStage ), vPlayerPos ) < GET_UNLOCK_DIST2_FOR_STAGE( eTriggerStage )
ENDFUNC
//PURPOSE: Is the player inside the active range of the stage
FUNC BOOL IS_TRIGGER_STAGE_IN_ACTIVE_RANGE()
RETURN VDIST2( GET_WORLD_POINT_COORD_FOR_STAGE( eTriggerStage ), vPlayerPos ) < GET_ACTIVE_DIST2_FOR_STAGE( eTriggerStage )
ENDFUNC
FUNC BOOL SAFE_IS_PLAYER_IN_ANY_VEHICLE()
IF( IS_ENTITY_ALIVE( PLAYER_PED_ID() ) )
RETURN IS_PED_IN_ANY_VEHICLE( PLAYER_PED_ID() )
ENDIF
RETURN FALSE
ENDFUNC
PROC DO_WARP_FADE( BOOL bFadeIn, INT iFadeTime = DEFAULT_FADE_TIME )
SET_FLAG( F_HAS_FADE_BEEN_REQUESTED, NOT bFadeIn )
IF( bFadeIn )
DO_SCREEN_FADE_IN( iFadeTime )
ELSE
DO_SCREEN_FADE_OUT( iFadeTime )
ENDIF
ENDPROC
PROC HALT_TIME( BOOL bHalt )
SET_FLAG( F_HAS_TIME_BEEN_PAUSED, bHalt )
PAUSE_CLOCK( bHalt )
ENDPROC
PROC ENABLE_PLAYER_CONTROL( BOOL bEnable )
SET_FLAG( F_IS_PLAYER_CONTROL_DISABLED, NOT bEnable )
SET_PLAYER_CONTROL( PLAYER_ID(), bEnable )
ENDPROC
FUNC BOOL IS_PLAYER_MICHAEL()
RETURN GET_ENTITY_MODEL( PLAYER_PED_ID() ) = PLAYER_ZERO
ENDFUNC
//-------------------- ASSET LOAD PROCS --------------------
//PURPOSE: Load a model with waits
PROC SCRIPT_LOAD_MODEL( MODEL_NAMES model )
REQUEST_MODEL( model )
#IF IS_DEBUG_BUILD
iTimer = GET_GAME_TIMER() + 8000
#ENDIF
WHILE( NOT HAS_MODEL_LOADED( model )
#IF IS_DEBUG_BUILD AND iTimer >= GET_GAME_TIMER() #ENDIF )
WAIT( 0 )
ENDWHILE
#IF IS_DEBUG_BUILD
IF( iTimer < GET_GAME_TIMER() )
ASSERTLN( "SCRIPT_LOAD_MODEL model took longer than 8s to load!" )
ENDIF
#ENDIF
ENDPROC
//PURPOSE: Load an anim with waits
PROC SCRIPT_LOAD_ANIM( STRING sAnimDict )
REQUEST_ANIM_DICT( sAnimDict )
#IF IS_DEBUG_BUILD
iTimer = GET_GAME_TIMER() + 8000
#ENDIF
WHILE( NOT HAS_ANIM_DICT_LOADED( sAnimDict )
#IF IS_DEBUG_BUILD AND iTimer >= GET_GAME_TIMER() #ENDIF )
WAIT( 0 )
ENDWHILE
#IF IS_DEBUG_BUILD
IF( iTimer < GET_GAME_TIMER() )
ASSERTLN( "SCRIPT_LOAD_ANIM anim:", sAnimDict, " took longer than 8s to load!" )
ENDIF
#ENDIF
ENDPROC
//-------------------- DEBUG --------------------
#IF IS_DEBUG_BUILD
FUNC STRING DEBUG_GET_FLAG_AS_STRING( FLAGS flag )
SWITCH( flag )
CASE F_ARE_PC_CONTROLS_SETUP RETURN "F_ARE_PC_CONTROLS_SETUP" BREAK
CASE F_DISPLAY_HELP_WHEN_LEAVING_MINE RETURN "F_DISPLAY_HELP_WHEN_LEAVING_MINE" BREAK
CASE F_IS_MINE_DOOR_HIDDEN RETURN "F_IS_MINE_DOOR_HIDDEN" BREAK
CASE F_RESOLVE_TRIGGER RETURN "F_RESOLVE_TRIGGER" BREAK
CASE F_HAS_FADE_BEEN_REQUESTED RETURN "F_HAS_FADE_BEEN_REQUESTED" BREAK
CASE F_HAS_TIME_BEEN_PAUSED RETURN "F_HAS_TIME_BEEN_PAUSED" BREAK
CASE F_IS_PLAYER_CONTROL_DISABLED RETURN "F_IS_PLAYER_CONTROL_DISABLED" BREAK
ENDSWITCH
RETURN "UNKNOWN"
ENDFUNC
//PURPOSE: Init the vars for a delayed cleanup
PROC DEBUG_CLEANUP_WITH_DELAY( INT iDelay = 10000 )
IF( NOT bDelayedCleanup )
bDelayedCleanup = TRUE
iDelayTime = GET_GAME_TIMER() + iDelay
ENDIF
ENDPROC
//PURPOSE: Prints the unlock states of all the stages
PROC DEBUG_PRINT_UNLOCK_STATE()
INT i
STRING sStage, sUnlock
CPRINTLN( DEBUG_SIMON, "Unlock status" )
REPEAT NUM_STAGES i
MURDER_MYSTERY_STAGE stage = INT_TO_ENUM( MURDER_MYSTERY_STAGE, i )
sStage = DEBUG_GET_MURDER_MYSTERY_STAGE_AS_STRING( stage )
IF( IS_STAGE_UNLOCKED( stage ) )
sUnlock = "unlocked"
ELSE
sUnlock = "locked"
ENDIF
CPRINTLN( DEBUG_SIMON, " ", sStage, " is ", sUnlock )
ENDREPEAT
ENDPROC
//PURPOSE: Blips/unblips the clue positions
PROC DEBUG_TOGGLE_BLIPS( BOOL bEnable )
INT i
IF( bEnable )
REPEAT NUM_STAGES i
MURDER_MYSTERY_STAGE stage = INT_TO_ENUM( MURDER_MYSTERY_STAGE, i )
IF( NOT DOES_BLIP_EXIST( blpUnlocks[ i ] ) )
blpUnlocks[ i ] = CREATE_BLIP_FOR_COORD( GET_UNLOCK_COORD_FOR_STAGE( stage ) )
ENDIF
IF( IS_STAGE_UNLOCKED( stage ) )
SET_BLIP_COLOUR( blpUnlocks[ i ], BLIP_COLOUR_GREEN )
ELSE
SET_BLIP_COLOUR( blpUnlocks[ i ], BLIP_COLOUR_RED )
ENDIF
ENDREPEAT
ELSE
REPEAT NUM_STAGES i
IF( DOES_BLIP_EXIST( blpUnlocks[ i ] ) )
REMOVE_BLIP( blpUnlocks[ i ] )
ENDIF
ENDREPEAT
ENDIF
ENDPROC
PROC DEBUG_SCRIPT_INIT()
CPRINTLN( DEBUG_SIMON, "Trigger stage is ", DEBUG_GET_MURDER_MYSTERY_STAGE_AS_STRING( eTriggerStage ) )
DEBUG_PRINT_UNLOCK_STATE()
// Widget
IF( NOT DOES_WIDGET_GROUP_EXIST( widMain ) )
widMain = START_WIDGET_GROUP( "Murder Mystery" )
twidTriggerStage = ADD_TEXT_WIDGET( "Trigger stage" )
ADD_WIDGET_BOOL( "Block script clean up", bBlockCleanup )
ADD_WIDGET_BOOL( "Is script cleaning up", bDelayedCleanup )
ADD_WIDGET_BOOL( "Terminate", bTerminate )
ADD_WIDGET_BOOL( "Toggle blips", bToggleBlips )
ADD_WIDGET_BOOL( "Toggle filter", bCycleFilter )
ADD_WIDGET_BOOL( "Add Noir phone contact to all phonebooks", bAddNoirPhoneContact )
ADD_WIDGET_BOOL( "Is player in mine", bIsPlayerInMine )
STOP_WIDGET_GROUP()
SET_CURRENT_WIDGET_GROUP( widMain )
START_WIDGET_GROUP( "Clues" )
twidUnlockStage = ADD_TEXT_WIDGET( "Selected stage" )
ADD_WIDGET_INT_SLIDER( "Select unlock", iUnlockStage, 0, NUM_STAGES - 1, 1 )
ADD_WIDGET_BOOL( "Is unlocked", bIsUnlocked )
ADD_WIDGET_BOOL( "Set unlocked", bSetUnlocked )
ADD_WIDGET_BOOL( "Set locked", bSetNotUnlocked )
STOP_WIDGET_GROUP()
CLEAR_CURRENT_WIDGET_GROUP( widMain )
SET_CURRENT_WIDGET_GROUP( widMain )
START_WIDGET_GROUP( "Flags" )
INT i
REPEAT TOTAL_FLAGS i
ADD_WIDGET_BOOL( DEBUG_GET_FLAG_AS_STRING( INT_TO_ENUM( FLAGS, i ) ), bFlags[ i ] )
ENDREPEAT
STOP_WIDGET_GROUP()
CLEAR_CURRENT_WIDGET_GROUP( widMain )
ENDIF
// Set the trigger stage
SET_CONTENTS_OF_TEXT_WIDGET( twidTriggerStage, DEBUG_GET_MURDER_MYSTERY_STAGE_AS_STRING( eTriggerStage ) )
iUnlockStage = ENUM_TO_INT( eTriggerStage )
ENDPROC
PROC DEBUG_UPDATE()
// Widget update
MURDER_MYSTERY_STAGE stage = INT_TO_ENUM( MURDER_MYSTERY_STAGE, iUnlockStage )
bIsUnlocked = IS_STAGE_UNLOCKED( stage )
IF( iUnlockStage <> iOldUnlockStage ) // update the text displayed to the current selected stage
iOldUnlockStage = iUnlockStage
SET_CONTENTS_OF_TEXT_WIDGET( twidUnlockStage, DEBUG_GET_MURDER_MYSTERY_STAGE_AS_STRING( stage ) )
ENDIF
IF( bSetUnlocked ) // unlock the current selected stage
SET_STAGE_UNLOCKED( stage, TRUE )
iOldUnlockStage = -1
bSetUnlocked = FALSE
ENDIF
IF( bSetNotUnlocked ) // set the current selected stage locked
SET_STAGE_UNLOCKED( stage, FALSE )
iOldUnlockStage = -1
bSetNotUnlocked = FALSE
ENDIF
DEBUG_TOGGLE_BLIPS( bToggleBlips )
IF( bTerminate ) // terminate the script
SCRIPT_CLEANUP()
ENDIF
IF( bDelayedCleanup AND NOT bBlockCleanup ) // update the delayed cleanup of the script
IF( iDelayTime < GET_GAME_TIMER() )
SCRIPT_CLEANUP()
ENDIF
ENDIF
IF( bCycleFilter ) // cycle through the noir filters
NOIR_TOGGLE_EFFECTS()
bCycleFilter = FALSE
ENDIF
IF( bAddNoirPhoneContact ) // add Isaac to all the player character phone books
ADD_CONTACT_TO_PHONEBOOK( CHAR_ISAAC, ALL_OWNERS_BOOKS, TRUE )
bAddNoirPhoneContact = FALSE
ENDIF
IF( IS_ENTITY_ALIVE( PLAYER_PED_ID() ) )
bIsPlayerInMine = IS_ENTITY_IN_ANGLED_AREA( PLAYER_PED_ID(), <<-415.436462, 2068.288574, 113.300171>>,
<<-564.951599, 1884.702515, 134.040344>>, 258.750000 )
OR IS_ENTITY_IN_ANGLED_AREA( PLAYER_PED_ID(), <<-596.470581, 2089.920898, 125.412750>>,
<<-581.213440, 2036.256104, 136.283630>>, 9.500000 )
ENDIF
INT i // flags
REPEAT TOTAL_FLAGS i
bFlags[ i ] = IS_FLAG_SET( INT_TO_ENUM( FLAGS, i ) )
ENDREPEAT
ENDPROC
#ENDIF
//-------------------- MISC PROCS --------------------
PROC SET_ENTITY_COORD_AND_HEADING( ENTITY_INDEX entity, VECTOR vPos, FLOAT fHeading )
SET_ENTITY_COORDS( entity, vPos )
SET_ENTITY_HEADING( entity, fHeading )
ENDPROC
FUNC BOOL IS_PED_PERFORMING_TASK( PED_INDEX ped, SCRIPT_TASK_NAME scriptTask )
SCRIPTTASKSTATUS status = GET_SCRIPT_TASK_STATUS( ped, scriptTask )
RETURN status = PERFORMING_TASK OR status = WAITING_TO_START_TASK
ENDFUNC
FUNC BOOL IS_COORD_ON_SCREEN( VECTOR vec )
FLOAT fx, fy
RETURN GET_SCREEN_COORD_FROM_WORLD_COORD( vec, fx, fy )
ENDFUNC
PROC CREATE_TRACKED_POINT_WITH_INFO( INT &iTracked, VECTOR vPos, FLOAT fRadius )
iTracked = CREATE_TRACKED_POINT()
IF( iTracked = 0 )
ASSERTLN( "CREATE_TRACKED_POINT_WITH_INFO can't get free index from CREATE_TRACKED_POINT!" )
ELSE
SET_TRACKED_POINT_INFO( iTracked, vPos, fRadius )
ENDIF
ENDPROC
//-------------------- WARP PROCS --------------------
//PURPOSE: Checks to see if it's safe to allow the player to warp into Solomon's office interior
FUNC BOOL SHOULD_ALLOW_OFFICE_WARP()
// if player is in the office always allow warp
IF( eOfficeWarpStage = WS_INSIDE )
RETURN TRUE
ENDIF
// if the current player character isn't michael
IF( NOT IS_PLAYER_MICHAEL() )
RETURN FALSE
ENDIF
// if the solomon strand isn't complete then don't allow warp
IF( NOT GET_MISSION_COMPLETE_STATE( SP_MISSION_SOLOMON_3 ) )
RETURN FALSE
ENDIF
// if the player has a wanted level then don't allow warp
IF( GET_PLAYER_WANTED_LEVEL( PLAYER_ID() ) > 0 AND eOfficeWarpStage = WS_NOT_INSIDE ) // rob - only disabling warp on wanted if outside
RETURN FALSE
ENDIF
// if repeat play is active then don't allow warp
IF( IS_REPEAT_PLAY_ACTIVE() )
RETURN FALSE
ENDIF
// if the player is with a prostitute then don't allow warp
IF( IS_PLAYER_WITH_A_PROSTITUTE() )
RETURN FALSE
ENDIF
// if there's a high priority phone call pending then don't allow warp
IF( IS_PLAYER_PED_PLAYABLE( GET_CURRENT_PLAYER_PED_ENUM() ) )
IF( GET_PLAYER_CHAR_COMMUNICATION_PRIORITY_LEVEL( GET_CURRENT_PLAYER_PED_ENUM() ) = CPR_HIGH
OR GET_PLAYER_CHAR_COMMUNICATION_PRIORITY_LEVEL( GET_CURRENT_PLAYER_PED_ENUM() ) = CPR_VERY_HIGH )
RETURN FALSE
ENDIF
ENDIF
// if the player is currently doing a random event then don't allow warp
IF( GET_RANDOM_EVENT_FLAG() )
RETURN FALSE
ENDIF
// if player is interacting with Chop then don't allow warp
IF( g_bPlayerInteractingWithChop )
RETURN FALSE
ENDIF
// otherwise it's ok to warp
RETURN TRUE
ENDFUNC
//PURPOSE: Handles the camera letter logic
PROC UPDATE_LETTER_CAMERA()
VECTOR vVecCoors1 = <<-1007.013000,-478.366241,49.028175>>
VECTOR vVecCoors2 = <<-1007.850037,-476.957275,51.028072>>
FLOAT fWidth = 1.750000
SWITCH( iUpdateLetterCamStage )
CASE 0 // show help text when close to the desk "Press ~INPUT_SCRIPT_PAD_RIGHT~ to read the letter."
IF( IS_ENTITY_IN_ANGLED_AREA( PLAYER_PED_ID(), vVecCoors1, vVecCoors2, fWidth )
AND NOT IS_PHONE_ONSCREEN() )
PRINT_HELP_FOREVER( sHelpTextReadLetter )
iUpdateLetterCamStage++
ENDIF
BREAK
CASE 1 // if player is close to the desk handle player interaction
IF( IS_ENTITY_IN_ANGLED_AREA( PLAYER_PED_ID(), vVecCoors1, vVecCoors2, fWidth )
AND NOT IS_PHONE_ONSCREEN() )
IF( NOT IS_PED_PERFORMING_TASK( PLAYER_PED_ID(), SCRIPT_TASK_LOOK_AT_COORD ) )
TASK_LOOK_AT_COORD( PLAYER_PED_ID(), <<-1007.9945, -476.8016, 49.9117>>, -1 )
ENDIF
// if the player has pressed the interact button
IF( IS_CONTROL_JUST_PRESSED( FRONTEND_CONTROL, INPUT_CONTEXT ) )
// player walks close to letter and camera cuts
ENABLE_PLAYER_CONTROL( FALSE )
TASK_FOLLOW_NAV_MESH_TO_COORD( PLAYER_PED_ID(), <<-1007.3514, -477.5197, 49.0282>>, PEDMOVEBLENDRATIO_WALK,
DEFAULT_TIME_NEVER_WARP, DEFAULT_SEEK_RADIUS, ENAV_DEFAULT, 30.4025 )
PRINT_HELP_FOREVER( sHelpTextStopReadLetter )
DISPLAY_RADAR( FALSE )
SET_CAM_ACTIVE( camLetter, TRUE )
RENDER_SCRIPT_CAMS( TRUE, FALSE )
iUpdateLetterCamStage++
ENDIF
ELSE // clear help if the player moved away from the desk
CLEAR_HELP()
CLEAR_PED_TASKS( PLAYER_PED_ID() )
iUpdateLetterCamStage--
ENDIF
BREAK
CASE 2 // when the player quits the letter cam reset the gameplay cam and tasks etc.
DISABLE_CELLPHONE_THIS_FRAME_ONLY()
ENABLE_PLAYER_CONTROL( FALSE )
DISPLAY_RADAR( FALSE )
IF( NOT IS_THIS_HELP_MESSAGE_BEING_DISPLAYED( sHelpTextStopReadLetter ) )
PRINT_HELP_FOREVER( sHelpTextStopReadLetter )
ENDIF
IF( IS_CONTROL_JUST_PRESSED( FRONTEND_CONTROL, INPUT_CONTEXT ) )
IF( GET_CAM_VIEW_MODE_FOR_CONTEXT( CAM_VIEW_MODE_CONTEXT_ON_FOOT ) = CAM_VIEW_MODE_FIRST_PERSON ) // if first person on foot
iTimer = GET_GAME_TIMER() + 300 // first person cut requires 300 m/s lead in
ANIMPOSTFX_PLAY( "CamPushInNeutral", 0, FALSE ) // flash
PLAY_SOUND_FRONTEND( -1, "1st_Person_Transition", "PLAYER_SWITCH_CUSTOM_SOUNDSET", FALSE )
ELSE
iTimer = 0
ENDIF
iUpdateLetterCamStage++
ENDIF
BREAK
CASE 3
DISABLE_CELLPHONE_THIS_FRAME_ONLY()
IF( iTimer < GET_GAME_TIMER() )
SET_GAMEPLAY_CAM_RELATIVE_HEADING()
SET_GAMEPLAY_CAM_RELATIVE_PITCH()
DISPLAY_RADAR( TRUE )
ENABLE_PLAYER_CONTROL( TRUE )
CLEAR_PED_TASKS( PLAYER_PED_ID() )
RENDER_SCRIPT_CAMS( FALSE, FALSE )
SET_CAM_ACTIVE( camLetter, FALSE )
iUpdateLetterCamStage++
ENDIF
BREAK
CASE 4
iUpdateLetterCamStage = 0
BREAK
ENDSWITCH
ENDPROC
FUNC BOOL IS_CORRECT_TOD()
INT iHour = GET_CLOCK_HOURS()
IF( iHour >= 21 OR iHour < 6 )
RETURN TRUE
ELSE
PRINT_HELP( "MM_SOLBUSY", 5000 )
RETURN FALSE
ENDIF
ENDFUNC
//PURPOSE: Handles the warp logic when entering/exiting Solomon's office interior
PROC UPDATE_SOLOMON_OFFICE_WARP()
SWITCH( eOfficeWarpStage )
CASE WS_NOT_INSIDE // waiting for player to touch the warp in locate
IF( NOT GET_MISSION_FLAG() ) // we check this here and not in ALLOW_SOLOMON_OFFICE_WARP because the player might start
// a mission in the office (via the phone), which would lock them inside (n.b. not checked for WS_INSIDE)
IF( IS_ENTITY_AT_COORD( PLAYER_PED_ID(), vExtOfficeWarpPos, vOnFootLocate, TRUE ) )
IF( IS_CORRECT_TOD() ) // check if the time at which the player touches the locate is late enough
eOfficeWarpStage = WS_WARPING_IN
DO_WARP_FADE( FALSE )
CPRINTLN( DEBUG_SIMON, " >>> Warping player into Solomon's office" )
ENDIF
ENDIF
ENDIF
BREAK
CASE WS_INSIDE // waiting for player to touch the warp out locate
DISABLE_SELECTOR_THIS_FRAME()
IF( NOT IS_CUTSCENE_PLAYING() ) // don't show the locate if a cutscene is playing
IF( IS_ENTITY_AT_COORD( PLAYER_PED_ID(), vIntOfficeWarpPos, vOnFootLocate, TRUE ) )
DISABLE_CELLPHONE_THIS_FRAME_ONLY()
eOfficeWarpStage = WS_WARPING_OUT
DO_WARP_FADE( FALSE )
CPRINTLN( DEBUG_SIMON, " >>> Warping player out of Solomon's office" )
ENDIF
UPDATE_LETTER_CAMERA()
// force player to walk and disable weapon use while in the office
IF( IS_ENTITY_ALIVE( PLAYER_PED_ID() ) )
WEAPON_TYPE weapon
IF( GET_CURRENT_PED_WEAPON( PLAYER_PED_ID(), weapon ) )
IF( weapon <> WEAPONTYPE_UNARMED )
SET_CURRENT_PED_WEAPON( PLAYER_PED_ID(), WEAPONTYPE_UNARMED )
ENDIF
ENDIF
SET_PED_MAX_MOVE_BLEND_RATIO( PLAYER_PED_ID(), PEDMOVEBLENDRATIO_WALK )
DISABLE_CONTROL_ACTION( PLAYER_CONTROL, INPUT_SELECT_WEAPON, TRUE )
DISABLE_CONTROL_ACTION( PLAYER_CONTROL, INPUT_ATTACK )
DISABLE_CONTROL_ACTION( PLAYER_CONTROL, INPUT_JUMP )
DISABLE_CONTROL_ACTION( PLAYER_CONTROL, INPUT_MELEE_ATTACK_HEAVY )
DISABLE_CONTROL_ACTION( PLAYER_CONTROL, INPUT_MELEE_ATTACK_LIGHT )
DISABLE_CONTROL_ACTION( PLAYER_CONTROL, INPUT_MELEE_ATTACK_ALTERNATE )
DISABLE_CONTROL_ACTION( PLAYER_CONTROL, INPUT_ATTACK2 )
DISABLE_CONTROL_ACTION( PLAYER_CONTROL, INPUT_AIM )
DISABLE_CONTROL_ACTION( PLAYER_CONTROL, INPUT_SELECT_WEAPON )
DISABLE_CONTROL_ACTION( PLAYER_CONTROL, INPUT_COVER )
ENDIF
ENDIF
BREAK
CASE WS_WARPING_IN // handle warping the player into the office
DISABLE_SELECTOR_THIS_FRAME()
IF( IS_SCREEN_FADED_OUT() )
SET_INTERIOR_DISABLED( INTERIOR_V_58_SOL_OFFICE, FALSE )
SET_ENTITY_COORD_AND_HEADING( PLAYER_PED_ID(), vIntOfficePlayerPos, fIntOfficePlayerHeading )
SET_GAMEPLAY_CAM_RELATIVE_HEADING()
SET_GAMEPLAY_CAM_RELATIVE_PITCH()
SET_CURRENT_PED_WEAPON( PLAYER_PED_ID(), WEAPONTYPE_UNARMED, TRUE )
SET_PLAYER_WANTED_LEVEL( PLAYER_ID(), 0 )
SET_PLAYER_WANTED_LEVEL_NOW( PLAYER_ID() )
HALT_TIME( TRUE )
// resolve vehicles in the area where the player will end up when warping out of the office
RESOLVE_VEHICLES_INSIDE_ANGLED_AREA_WITH_SIZE_LIMIT( <<-1023.439148, -479.559204, 35.818062>>,
<<-1014.382874, -495.123932, 40.107964>>, 18.500, <<-1024.0964, -485.1923, 35.9779>>, 209.0203,
GET_DEFAULT_ALLOWABLE_VEHICLE_SIZE_VECTOR(), TRUE, FALSE, TRUE, TRUE )
// load the interior
NEW_LOAD_SCENE_START_SPHERE( vIntOfficePlayerPos, 20.0 )
GET_INTERIOR_INSTANCE_INDEX_FOR_INTERIOR( INTERIOR_V_58_SOL_OFFICE, intOffice )
PIN_INTERIOR_IN_MEMORY( intOffice )
iTimer = GET_GAME_TIMER() + 40000
WHILE( ( NOT IS_NEW_LOAD_SCENE_LOADED() OR NOT IS_INTERIOR_READY( intOffice ) ) AND iTimer > GET_GAME_TIMER() )
WAIT( 0 )
ENDWHILE
NEW_LOAD_SCENE_STOP()
IF( iTimer <= GET_GAME_TIMER() )
ASSERTLN( "UPDATE_SOLOMON_OFFICE_WARP took longer than 40s to load interior in!" )
UNPIN_INTERIOR( intOffice )
SET_ENTITY_COORD_AND_HEADING( PLAYER_PED_ID(), vExtOfficePlayerPos, fExtOfficePlayerHeading )
DO_WARP_FADE( TRUE )
eOfficeWarpStage = WS_NOT_INSIDE
ELSE
// load done
DO_WARP_FADE( TRUE )
eOfficeWarpStage = WS_INSIDE
SETUP_PC_CONTROLS()
CPRINTLN( DEBUG_SIMON, " >>> Warp into Solomon's office finished" )
ENDIF
ENDIF
BREAK
CASE WS_WARPING_OUT // handle warping the player out of the office
DISABLE_SELECTOR_THIS_FRAME()
DISABLE_CELLPHONE_THIS_FRAME_ONLY()
IF( IS_SCREEN_FADED_OUT() )
SET_ENTITY_COORD_AND_HEADING( PLAYER_PED_ID(), vExtOfficePlayerPos, fExtOfficePlayerHeading )
SET_INTERIOR_DISABLED( INTERIOR_V_58_SOL_OFFICE, TRUE )
UNPIN_INTERIOR( intOffice )
SET_GAMEPLAY_CAM_RELATIVE_HEADING()
SET_GAMEPLAY_CAM_RELATIVE_PITCH()
HALT_TIME( FALSE )
CLEANUP_PC_CONTROLS()
WAIT_FOR_WORLD_TO_LOAD( vExtOfficePlayerPos, 30.0 )
DO_WARP_FADE( TRUE )
eOfficeWarpStage = WS_NOT_INSIDE
CPRINTLN( DEBUG_SIMON, " >>> Warp out of Solomon's office finished" )
ENDIF
BREAK
ENDSWITCH
ENDPROC
//-------------------- MAIN STAGE PROCS --------------------
FUNC BOOL SHOULD_TRIGGER_STAGE_RUN_THIS_FRAME()
SWITCH( eTriggerStage )
CASE STAGE_CARVING_1_WEST_VINEWOOD
CASE STAGE_CARVING_2_ALTA
CASE STAGE_CARVING_3_CHINESE_THEATRE
CASE STAGE_CARVING_4_OBSERVATORY
RETURN NOT IS_STAGE_UNLOCKED( eTriggerStage ) AND IS_TRIGGER_STAGE_IN_ACTIVE_RANGE()
CASE STAGE_SEA
RETURN IS_TRIGGER_STAGE_IN_ACTIVE_RANGE()
CASE STAGE_LETTER
RETURN ( ARE_ALL_STAGES_PRECEDING_UNLOCKED( eTriggerStage ) AND IS_TRIGGER_STAGE_IN_ACTIVE_RANGE() )
OR eOfficeWarpStage = WS_INSIDE OR eOfficeWarpStage = WS_WARPING_OUT OR eOfficeWarpStage = WS_WARPING_IN
CASE STAGE_MINE
RETURN IS_TRIGGER_STAGE_IN_ACTIVE_RANGE() OR ( IS_FLAG_SET( F_IS_MINE_DOOR_HIDDEN ) AND IS_COORD_ON_SCREEN( vMineDoorPos ) )
DEFAULT
RETURN FALSE
ENDSWITCH
ENDFUNC
PROC INIT_TRIGGER_STAGE()
SWITCH( eTriggerStage )
CASE STAGE_CARVING_1_WEST_VINEWOOD
CASE STAGE_CARVING_2_ALTA
CASE STAGE_CARVING_3_CHINESE_THEATRE
CASE STAGE_CARVING_4_OBSERVATORY
// used for checking occlusion of carving
CREATE_TRACKED_POINT_WITH_INFO( iTrackedPoint, GET_UNLOCK_COORD_FOR_STAGE( eTriggerStage ), 0.2 )
BREAK
CASE STAGE_SEA
SCRIPT_LOAD_MODEL( U_F_M_DROWNED_01 )
SCRIPT_LOAD_ANIM( sDeadBodySeaAnimDict )
// secretary spawn
pedDeadBodySea = CREATE_PED( PEDTYPE_MISSION, U_F_M_DROWNED_01, GET_UNLOCK_COORD_FOR_STAGE( STAGE_SEA ), 184.5548 )
SET_MODEL_AS_NO_LONGER_NEEDED( U_F_M_DROWNED_01 )
TASK_PLAY_ANIM( pedDeadBodySea, sDeadBodySeaAnimDict, sDeadBodySeaAnim, INSTANT_BLEND_IN, INSTANT_BLEND_OUT, -1,
AF_LOOPING | AF_NOT_INTERRUPTABLE )
SET_BLOCKING_OF_NON_TEMPORARY_EVENTS( pedDeadBodySea, TRUE )
SET_PED_KEEP_TASK( pedDeadBodySea, TRUE )
FREEZE_ENTITY_POSITION( pedDeadBodySea, TRUE )
STOP_PED_SPEAKING( pedDeadBodySea, TRUE )
DISABLE_PED_PAIN_AUDIO( pedDeadBodySea, TRUE )
SET_PED_CONFIG_FLAG( pedDeadBodySea, PCF_RunFromFiresAndExplosions, FALSE )
SET_PED_CONFIG_FLAG( pedDeadBodySea, PCF_DisableExplosionReactions, TRUE )
SET_ENTITY_INVINCIBLE( pedDeadBodySea, TRUE )
SET_PED_RELATIONSHIP_GROUP_HASH( pedDeadBodySea, RELGROUPHASH_PLAYER )
BREAK
CASE STAGE_LETTER
// lock office door
iDoor = HASH( "SOLOMON_OFFICE_DOOR" )
ADD_DOOR_TO_SYSTEM( iDoor, V_ILEV_SOL_OFF_DOOR01, << -1002.147,-478.064,50.117>> )
DOOR_SYSTEM_SET_DOOR_STATE( iDoor, DOORSTATE_LOCKED )
// create letter camera
camLetter = CREATE_CAMERA_WITH_PARAMS( CAMTYPE_SCRIPTED, <<-1008.0560, -476.7573, 50.2493>>,
<<-89.5003, -0.0000, 38.7952>>, 50.0, FALSE )
BREAK
CASE STAGE_MINE
SCRIPT_LOAD_MODEL( U_M_O_FILMNOIR )
// mine dead body spawn
pedDeadBodyMine = CREATE_PED( PEDTYPE_MISSION, U_M_O_FILMNOIR, GET_UNLOCK_COORD_FOR_STAGE( STAGE_MINE ), 22.9631 )
SET_PED_PROP_INDEX( pedDeadBodyMine, ANCHOR_HEAD, 0, 0 )
SET_MODEL_AS_NO_LONGER_NEEDED( U_M_O_FILMNOIR )
STOP_PED_SPEAKING( pedDeadBodyMine, TRUE )
DISABLE_PED_PAIN_AUDIO( pedDeadBodyMine, TRUE )
EXPLODE_PED_HEAD( pedDeadBodyMine )
SET_PED_RELATIONSHIP_GROUP_HASH( pedDeadBodyMine, RELGROUPHASH_PLAYER )
// film canister near dead body spawn
objFilmCan = CREATE_OBJECT_NO_OFFSET( PROP_VINTAGE_FILMCAN, <<-543.71, 1986.20, 126.05>> )
BREAK
ENDSWITCH
ENDPROC
PROC UPDATE_TRIGGER_STAGE()
SWITCH( eTriggerStage )
CASE STAGE_CARVING_1_WEST_VINEWOOD
CASE STAGE_CARVING_2_ALTA
CASE STAGE_CARVING_3_CHINESE_THEATRE
CASE STAGE_CARVING_4_OBSERVATORY
// if the player is near the carving, it isn't occluded and the player isn't in a vehicle unlock it
IF( NOT IS_STAGE_UNLOCKED( eTriggerStage ) )
IF( IS_TRIGGER_STAGE_IN_UNLOCK_RANGE() AND NOT SAFE_IS_PLAYER_IN_ANY_VEHICLE() AND IS_PLAYER_MICHAEL()
AND NOT IS_REPEAT_PLAY_ACTIVE() )
IF( iTrackedPoint <> 0 )
IF( IS_TRACKED_POINT_VISIBLE( iTrackedPoint ) )
SET_STAGE_UNLOCKED( eTriggerStage )
ENDIF
ENDIF
ENDIF
ENDIF
BREAK
CASE STAGE_SEA
// increase dead body capsule so that player's limbs don't clip through it
IF( IS_ENTITY_ALIVE( pedDeadBodySea ) )
SET_PED_CAPSULE( pedDeadBodySea, 0.75 )
ENDIF
// if the player is close to the clue then unlock
IF( NOT IS_STAGE_UNLOCKED( STAGE_SEA ) )
IF( IS_TRIGGER_STAGE_IN_UNLOCK_RANGE() AND IS_PLAYER_MICHAEL() AND NOT IS_REPEAT_PLAY_ACTIVE() )
SET_STAGE_UNLOCKED( STAGE_SEA )
ENDIF
ENDIF
BREAK
CASE STAGE_LETTER
// handles warping in and out of the office
IF( SHOULD_ALLOW_OFFICE_WARP() )
UPDATE_SOLOMON_OFFICE_WARP()
ENDIF
// unlock clue when the player backs out of reading the letter
IF( NOT IS_STAGE_UNLOCKED( STAGE_LETTER ) AND iUpdateLetterCamStage = 4 AND IS_PLAYER_MICHAEL()
AND NOT IS_REPEAT_PLAY_ACTIVE() )
SET_STAGE_UNLOCKED( STAGE_LETTER )
ENDIF
BREAK
CASE STAGE_MINE
// trigger a vehicle resolve when the player enters the mine and can't see the mine door
IF( NOT IS_FLAG_SET( F_RESOLVE_TRIGGER ) )
IF( IS_ENTITY_IN_ANGLED_AREA( PLAYER_PED_ID(), <<-580.891357, 2033.120117, 130.511261>>,
<<-578.478333, 2035.028687, 127.561455>>, 0.750000 ) )
SET_FLAG( F_RESOLVE_TRIGGER, TRUE )
ENDIF
ENDIF
IF( IS_ENTITY_IN_ANGLED_AREA( PLAYER_PED_ID(), <<-580.230469, 2032.054077, 130.292755>>,
<<-577.666870, 2033.917480, 127.515732>>, 0.750000 ) )
IF( IS_FLAG_SET( F_RESOLVE_TRIGGER ) )
RESOLVE_VEHICLES_INSIDE_ANGLED_AREA_WITH_SIZE_LIMIT( <<-595.174561, 2085.767822, 136.881271>>,
<<-601.575439, 2106.161621, 125.461884>>, 20.500000, <<-602.8466, 2115.5112, 126.4209>>, 128.0735,
<<13.5, 22.2, 5.7>>, TRUE, FALSE, TRUE, TRUE )
SET_FLAG( F_RESOLVE_TRIGGER, FALSE )
CPRINTLN( DEBUG_SIMON, "Resolving area" )
ENDIF
ENDIF
// hide the mine door when the player's inside the mine and can't see the mine door
IF( NOT IS_FLAG_SET( F_IS_MINE_DOOR_HIDDEN ) )
IF( IS_ENTITY_IN_ANGLED_AREA( PLAYER_PED_ID(), <<-580.230469, 2032.054077, 130.292755>>,
<<-577.666870, 2033.917480, 127.515732>>, 0.750000 )
OR IS_SCREEN_FADED_OUT() )
CREATE_MODEL_HIDE( vMineDoorPos, HIDE_RADIUS, PROP_MINESHAFT_DOOR, FALSE ) // large radius because debris can be scattered by explosion
CPRINTLN( DEBUG_SIMON, "Hiding mine door model" )
SET_FLAG( F_IS_MINE_DOOR_HIDDEN, TRUE )
ENDIF
ENDIF
// handle unlock and filter toggle
IF( IS_TRIGGER_STAGE_IN_UNLOCK_RANGE() AND NOT SAFE_IS_PLAYER_IN_ANY_VEHICLE() )
DISABLE_CONTROL_ACTION( PLAYER_CONTROL, INPUT_WEAPON_SPECIAL_TWO )
IF( ARE_ALL_STAGES_PRECEDING_UNLOCKED( STAGE_MINE ) )
IF( NOT IS_STAGE_UNLOCKED( STAGE_MINE ) AND IS_PLAYER_MICHAEL() AND NOT IS_REPEAT_PLAY_ACTIVE() )
ADD_CONTACT_TO_PHONEBOOK( CHAR_ISAAC, ALL_OWNERS_BOOKS, TRUE )
SET_STAGE_UNLOCKED( STAGE_MINE )
SET_FLAG( F_DISPLAY_HELP_WHEN_LEAVING_MINE, TRUE )
ENDIF
ENDIF
IF( ARE_ALL_STAGES_UNLOCKED() )
IF( NOT IS_PHONE_ONSCREEN() )
IF( IS_CONTROL_JUST_PRESSED( PLAYER_CONTROL, INPUT_CONTEXT ) )
NOIR_TOGGLE_EFFECTS()
ENDIF
PRINT_HELP( "MM_TOGGLE", 10 )
ENDIF
ENDIF
ENDIF
// display help text when the player leaves the mine after unlocking the final clue, only displays once
IF( IS_FLAG_SET( F_DISPLAY_HELP_WHEN_LEAVING_MINE ) )
IF( IS_ENTITY_IN_ANGLED_AREA( PLAYER_PED_ID(), <<-594.997620, 2089.004639, 130.344940>>,
<<-597.112488, 2088.364502, 133.303513>>, 1.000000 ) )
PRINT_HELP( "MM_HELP", DEFAULT_HELP_TEXT_TIME )
SET_FLAG( F_DISPLAY_HELP_WHEN_LEAVING_MINE, FALSE )
ENDIF
ENDIF
BREAK
ENDSWITCH
ENDPROC
//-------------------- BACKEND PROCS --------------------
PROC SCRIPT_INIT( VECTOR vWorldPointCoord )
#IF IS_DEBUG_BUILD
CPRINTLN( DEBUG_SIMON, "" )
CPRINTLN( DEBUG_SIMON, "Starting murderMystery.sc" )
CPRINTLN( DEBUG_SIMON, "Activated world point: <<", vWorldPointCoord.x, ", ",
vWorldPointCoord.y, ", ", vWorldPointCoord.z, ">>" )
#ENDIF
// terminate script if not a CGtoNG player
IF( NOT IS_LAST_GEN_PLAYER() )
CPRINTLN( DEBUG_SIMON, "Terminating script because player isn't CG to NG" )
TERMINATE_THIS_THREAD()
ENDIF
// terminate script if an instance is already running
IF( GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH( HASH( "murderMystery" ) ) > 1 )
CPRINTLN( DEBUG_SIMON, "Terminating script because an instance of the script already exists" )
TERMINATE_THIS_THREAD()
ENDIF
// figure out which stage has triggered the script to start
INT i = 0
WHILE( i < NUM_STAGES AND eTriggerStage = STAGE_UNSET )
MURDER_MYSTERY_STAGE stage = INT_TO_ENUM( MURDER_MYSTERY_STAGE, i )
IF( VECTOR_COMPARE( vWorldPointCoord, GET_WORLD_POINT_COORD_FOR_STAGE( stage ) ) )
eTriggerStage = stage
ENDIF
i++
ENDWHILE
INIT_TRIGGER_STAGE()
ENDPROC
PROC SCRIPT_UPDATE()
// update player position for this frame
IF( IS_ENTITY_ALIVE( PLAYER_PED_ID() ) )
vPlayerPos = GET_ENTITY_COORDS( PLAYER_PED_ID(), FALSE )
ENDIF
ENDPROC
//-------------------- MAIN LOOP --------------------
SCRIPT( coords_struct in_coords )
IF( HAS_FORCE_CLEANUP_OCCURRED( FORCE_CLEANUP_FLAG_SP_TO_MP | FORCE_CLEANUP_FLAG_REPEAT_PLAY | FORCE_CLEANUP_FLAG_DEBUG_MENU | FORCE_CLEANUP_FLAG_PLAYER_KILLED_OR_ARRESTED ) )
SCRIPT_CLEANUP()
ENDIF
SCRIPT_INIT( in_coords.vec_coord[ 0 ] )
#IF IS_DEBUG_BUILD
DEBUG_SCRIPT_INIT()
#ENDIF
WHILE( TRUE )
SCRIPT_UPDATE()
#IF IS_DEBUG_BUILD
DEBUG_UPDATE()
#ENDIF
// should the script run for the trigger stage this frame, if not then clean up
IF( SHOULD_TRIGGER_STAGE_RUN_THIS_FRAME() )
UPDATE_TRIGGER_STAGE()
ELSE
#IF IS_DEBUG_BUILD
#IF DO_DELAYED_CLEANUP
DEBUG_CLEANUP_WITH_DELAY()
#ENDIF
#IF NOT DO_DELAYED_CLEANUP
SCRIPT_CLEANUP()
#ENDIF
#ENDIF
#IF NOT IS_DEBUG_BUILD
SCRIPT_CLEANUP()
#ENDIF
ENDIF
WAIT( 0 )
ENDWHILE
ENDSCRIPT