//╒═════════════════════════════════════════════════════════════════════════════╕ //╞══════════════════╡ 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