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

1736 lines
57 KiB
Python
Executable File

///// ob_jukebox.sc
///// Mark Richardson - 13/07/16
///// Network Jukebox functionailty
USING "rage_builtins.sch"
USING "globals.sch"
USING "cellphone_public.sch"
USING "commands_zone.sch"
USING "menu_public.sch"
USING "net_clubhouse.sch"
//***********Script CONSTS***********
CONST_INT ciJukeboxUseCharge 1
//Broadcast data BS consts (Player)
CONST_INT ciRequestingUseOfJukebox 0
CONST_INT ciUsingJukebox 1
CONST_INT ciRequestedPlaylistChange 2
CONST_INT ciRequestedVolumeChange 3
//Local BS consts
CONST_INT ciDelayTimerActive 0
CONST_INT ciRebuildMenu 1
CONST_INT cinetworkInitDone 2
CONST_INT ciMenuActive 3
CONST_INT ciPlaySelectTrackAnim 4
CONST_INT ciPlayInsertCoinAnim 5
CONST_INT ciJukeboxEnabled 6
ENUM JUKEBOX_MENU_STAGE
eSelectPlaylist,
eTurnJBOffNoMoney, //Seperate stage for when the player has no cash
eConfirm
ENDENUM
ENUM JUKEBOX_ACTIVITY_STAGE
eJAInactive,
eJAPlayingEntryAnim,
eJAAwaitingAproval,
eJAMenuActive,
eJAExit
ENDENUM
ENUM JUKEBOX_SERVER_STAGE
JBServerStageOff,
JBServerStageOn
ENDENUM
//***********Script variables***********
OBJECT_INDEX obJukebox
PLAYER_INDEX pPropertyOwnerID
INT iLocalActivePlaylistID = 0
INT iSelectedPlaylist = -1
INT iCurrentSelection = 0
INT iCurrentProperty = -1
INT iStaggeredPlayerLoop = 0
INT iJukeboxContextIntention = NEW_CONTEXT_INTENTION
INT iJukeBoxSyncSceneStage = 0
INT iJukeBoxSyncSceneID = 0
INT iJukeBoxBS = 0
INT iJBSelectedPlistPlayTime = 0
INT iMostPlayedPlayistTime = 0
INT iTelemSelectedPlaylist = 0
INT iMenuOnOffOption
STRING sJukeboxAnimDict = "anim@amb@clubhouse@jukebox@"
JUKEBOX_MENU_STAGE eMenuStage = eSelectPlaylist
JUKEBOX_ACTIVITY_STAGE eActivityStage = eJAInactive
SCRIPT_TIMER stAnalogueMenuDelay
SCRIPT_TIMER stJBWalkToTimer
SCRIPT_TIMER stStatAutosaveTimer
VECTOR vJukeboxCoords
VECTOR vtrigger
VECTOR vSynchSceneOrigin
//***********Script Structs***********
// The server broadcast data.
STRUCT ServerBroadcastData
// Game State
JUKEBOX_SERVER_STAGE iJukeboxServerState = JBServerStageOn
FLOAT fVolume = 0.5
INT iJukeboxUserID = -1
INT iActivePlaylistID = -1
BOOL bNewPlaylistSelectedByOwner
ENDSTRUCT
ServerBroadcastData serverBD
// The client broadcast data.
STRUCT PlayerBroadcastData
INT iBitSet
INT iNewPlaylistRequestID
FLOAT fVolume
ENDSTRUCT
PlayerBroadcastData playerBD[NUM_NETWORK_PLAYERS]
//***********Script functions***********
PROC CLEANUP_ANY_PLAYING_ANIMATIONS()
IF DOES_ENTITY_EXIST(PLAYER_PED_ID())
AND NOT IS_ENTITY_DEAD(PLAYER_PED_ID())
IF IS_ENTITY_PLAYING_ANIM(PLAYER_PED_ID(), sJukeboxAnimDict, "IDLE")
OR IS_ENTITY_PLAYING_ANIM(PLAYER_PED_ID(), sJukeboxAnimDict, "EXIT")
OR IS_ENTITY_PLAYING_ANIM(PLAYER_PED_ID(), sJukeboxAnimDict, "INSERT_COINS")
OR IS_ENTITY_PLAYING_ANIM(PLAYER_PED_ID(), sJukeboxAnimDict, "SELECT_TRACK")
CLEAR_PED_TASKS_IMMEDIATELY(PLAYER_PED_ID())
ENDIF
ENDIF
ENDPROC
PROC INIT_TIME_STAMP_FOR_CURRENT_PLAYLIST()
#IF FEATURE_CASINO_HEIST
IF iCurrentProperty = PROPERTY_ARCADE_GARAGE
INT iFavouritePList = GET_LOCAL_PLAYERS_FAVOURITE_ARCADE_JUKEBOX_PLAYLIST_ID()
iJBSelectedPlistPlayTime = GET_CLOUD_TIME_AS_INT()
iMostPlayedPlayistTime = GET_MP_INT_CHARACTER_STAT(GET_FAV_ARCADE_PLAYLIST_STAT_FROM_ID(iFavouritePList))
ELSE
#ENDIF
INT iFavouritePList = GET_LOCAL_PLAYERS_FAVOURITE_JUKEBOX_PLAYLIST_ID()
iJBSelectedPlistPlayTime = GET_CLOUD_TIME_AS_INT()
iMostPlayedPlayistTime = GET_MP_INT_CHARACTER_STAT(GET_FAV_PLAYLIST_STAT_FROM_ID(iFavouritePList))
#IF FEATURE_CASINO_HEIST
ENDIF
#ENDIF
ENDPROC
/// PURPOSE:
/// Adds the total time the given playlist has been played to the relevant stat
PROC SAVE_JB_PLAYLIST_PLAY_TIME(INT iOldPlaylist)
IF iOldPlaylist = iMenuOnOffOption
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> SAVE_JB_PLAYLIST_PLAY_TIME not setting stat as the JB has been turned off")
EXIT
ENDIF
#IF FEATURE_CASINO_HEIST
IF iCurrentProperty = PROPERTY_ARCADE_GARAGE
INT iCurrentSavedTime = GET_MP_INT_CHARACTER_STAT(GET_FAV_ARCADE_PLAYLIST_STAT_FROM_ID(iOldPlaylist))
INT iTimeToAdd = (GET_CLOUD_TIME_AS_INT() - iJBSelectedPlistPlayTime)
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> SAVE_JB_PLAYLIST_PLAY_TIME saving time for playlist: ", iOldPlaylist, " Previous total time: ", iCurrentSavedTime, " seconds. Time to add: ", iTimeToAdd, " seconds.")
//Set the stat
SET_MP_INT_CHARACTER_STAT(GET_FAV_ARCADE_PLAYLIST_STAT_FROM_ID(iOldPlaylist), (iCurrentSavedTime + iTimeToAdd))
//Set the BD
IF iMostPlayedPlayistTime < (iCurrentSavedTime + iTimeToAdd)
SET_PLAYERS_FAVOURITE_PLAYLIST(GET_LOCAL_PLAYERS_FAVOURITE_ARCADE_JUKEBOX_PLAYLIST_ID())
ENDIF
ELSE
#ENDIF
INT iCurrentSavedTime = GET_MP_INT_CHARACTER_STAT(GET_FAV_PLAYLIST_STAT_FROM_ID(iOldPlaylist))
INT iTimeToAdd = (GET_CLOUD_TIME_AS_INT() - iJBSelectedPlistPlayTime)
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> SAVE_JB_PLAYLIST_PLAY_TIME saving time for playlist: ", iOldPlaylist, " Previous total time: ", iCurrentSavedTime, " seconds. Time to add: ", iTimeToAdd, " seconds.")
//Set the stat
SET_MP_INT_CHARACTER_STAT(GET_FAV_PLAYLIST_STAT_FROM_ID(iOldPlaylist), (iCurrentSavedTime + iTimeToAdd))
//Set the BD
IF iMostPlayedPlayistTime < (iCurrentSavedTime + iTimeToAdd)
SET_PLAYERS_FAVOURITE_PLAYLIST(GET_LOCAL_PLAYERS_FAVOURITE_JUKEBOX_PLAYLIST_ID())
ENDIF
#IF FEATURE_CASINO_HEIST
ENDIF
#ENDIF
iJBSelectedPlistPlayTime = GET_CLOUD_TIME_AS_INT()
ENDPROC
PROC CLEANUP_JUKEBOX(BOOL bCleanupPlayerControl = FALSE)
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> CLEANUP_JUKEBOX")
DISABLE_CELLPHONE(FALSE)
IF IS_BIT_SET(iJukeBoxBS, ciMenuActive)
CLEANUP_MENU_ASSETS()
ENDIF
SET_BIT(g_JukeboxData.iBitSet, ciJBJustRunCleanup)
IF iJukeboxContextIntention != NEW_CONTEXT_INTENTION
RELEASE_CONTEXT_INTENTION(iJukeboxContextIntention)
ENDIF
IF pPropertyOwnerID = PLAYER_ID()
SAVE_JB_PLAYLIST_PLAY_TIME(iLocalActivePlaylistID)
ENDIF
IF IS_HELP_MESSAGE_BEING_DISPLAYED()
AND IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("JBOX_NO_MONEY")
CLEAR_HELP(TRUE)
ENDIF
THEFEED_RESUME()
CLEANUP_ANY_PLAYING_ANIMATIONS()
IF bCleanupPlayerControl
NET_SET_PLAYER_CONTROL(PLAYER_ID(), TRUE)
ENDIF
#IF FEATURE_CASINO_HEIST
IF iCurrentProperty = PROPERTY_ARCADE_GARAGE
STOP_AUDIO_SCENE("dlc_ch_arcade_music_volume")
ENDIF
#ENDIF
TERMINATE_THIS_THREAD()
ENDPROC
FUNC BOOL IS_JUKEBOX_ON()
RETURN serverBD.iJukeboxServerState = JBServerStageOn
ENDFUNC
FUNC BOOL IS_JUKEBOX_IN_USE()
IF serverBD.iJukeboxUserID != -1
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
PROC CHARGE_PLAYER_FOR_JB(INT iPlaylist)
INT iPlaylistHash
IF IS_PROPERTY_CLUBHOUSE(iCurrentProperty)
iPlaylistHash = GET_HASH_KEY(GET_CLUBHOUSE_PLAYLIST_NAME_FROM_ID(iPlaylist))
#IF FEATURE_CASINO_HEIST
ELIF iCurrentProperty = PROPERTY_ARCADE_GARAGE
iPlaylistHash = GET_HASH_KEY(GET_ARCADE_PLAYLIST_NAME_FROM_ID(iPlaylist))
#ENDIF
ENDIF
IF NETWORK_CAN_SPEND_MONEY(ciJukeboxUseCharge, FALSE, FALSE, TRUE)
IF USE_SERVER_TRANSACTIONS()
INT iTransactionID
CDEBUG1LN(DEBUG_SHOPS, "<JUKEBOX> [$$$] Charging MP player for jukebox - PC: $", ciJukeboxUseCharge)
TRIGGER_FIRE_AND_FORGET_SERVICE_TRANSACTION(SERVICE_SPEND_JUKEBOX, ciJukeboxUseCharge, iTransactionID, FALSE, FALSE, TRUE)
g_cashTransactionData[iTransactionID].cashInfo.iItemHash = iPlaylistHash
ELSE
CDEBUG1LN(DEBUG_SHOPS, "<JUKEBOX> [$$$] Charging MP player for jukebox - CONSOLE: $", ciJukeboxUseCharge)
NETWORK_SPENT_JUKEBOX(ciJukeboxUseCharge, iPlaylistHash, FALSE, FALSE)
ENDIF
ENDIF
ENDPROC
#IF IS_DEBUG_BUILD
FUNC STRING GET_JUKEBOX_ACTIVITY_STAGE_AS_STRING(JUKEBOX_ACTIVITY_STAGE eStage)
SWITCH eStage
CASE eJAInactive RETURN "eJAInactive"
CASE eJAPlayingEntryAnim RETURN "eJAPlayingEntryAnim"
CASE eJAAwaitingAproval RETURN "eJAAwaitingAproval"
CASE eJAMenuActive RETURN "eJAMenuActive"
CASE eJAExit RETURN "eJAExit"
ENDSWITCH
RETURN "Jukebox stage unknown"
ENDFUNC
#ENDIF
PROC SET_JUKEBOX_ACTIVITY_STAGE(JUKEBOX_ACTIVITY_STAGE eNewStage)
#IF IS_DEBUG_BUILD
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> SET_JUKEBOX_ACTIVITY_STAGE old stage: ", GET_JUKEBOX_ACTIVITY_STAGE_AS_STRING(eActivityStage), " New stage: ", GET_JUKEBOX_ACTIVITY_STAGE_AS_STRING(eNewStage))
DEBUG_PRINTCALLSTACK()
#ENDIF
eActivityStage = eNewStage
ENDPROC
PROC REQUEST_VOLUME_CHANGE(FLOAT fNewVolume)
playerBD[NATIVE_TO_INT(PLAYER_ID())].fVolume = fNewVolume
SET_BIT(playerBD[NATIVE_TO_INT(PLAYER_ID())].iBitSet, ciRequestedVolumeChange)
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> RUN_JUKEBOX_ACTIVITY - Requested volume change to ", fNewVolume)
ENDPROC
/// PURPOSE:
/// Disable all controls necessary whilst the menu is active
PROC JUKEBOX_MENU_CONTROL_DISABLE()
SET_CLEAR_ON_CALL_HUD_THIS_FRAME()
HANG_UP_AND_PUT_AWAY_PHONE(TRUE)
IF (NOT IS_SYSTEM_UI_BEING_DISPLAYED())
AND (NOT IS_PAUSE_MENU_ACTIVE())
DISABLE_ALL_CONTROL_ACTIONS(PLAYER_CONTROL)
ENABLE_ALL_CONTROL_ACTIONS(CAMERA_CONTROL)
DISABLE_CONTROL_ACTION(FRONTEND_CONTROL, INPUT_FRONTEND_PAUSE_ALTERNATE)
ENDIF
ENDPROC
FUNC INT GET_MAX_AVAILABLE_PLAYLISTS()
IF iCurrentProperty = PROPERTY_ARCADE_GARAGE
#IF FEATURE_TUNER
IF pPropertyOwnerID = PLAYER_ID()
AND HAS_LOCAL_PLAYER_COLLECTED_ANY_USB_MIXES()
RETURN 10 // url:bugstar:6593351
ENDIF
#ENDIF
RETURN 9
ENDIF
#IF FEATURE_TUNER
IF pPropertyOwnerID = PLAYER_ID()
AND HAS_LOCAL_PLAYER_COLLECTED_ANY_USB_MIXES()
RETURN 5 // url:bugstar:6593351
ENDIF
#ENDIF
RETURN 4
ENDFUNC
FUNC INT GET_MENU_OFF_OPTION()
RETURN GET_MAX_AVAILABLE_PLAYLISTS()
ENDFUNC
FUNC INT GET_JUKEBOX_MENU_LIMIT()
INT iNumberOfPlaylists = GET_MAX_AVAILABLE_PLAYLISTS()
SWITCH eMenuStage
CASE eConfirm RETURN 1
CASE eSelectPlaylist
IF IS_JUKEBOX_ON()
RETURN iNumberOfPlaylists //Not (ciMaxAvailablePlaylists -1) to allow for the on/off option
ELSE
RETURN (iNumberOfPlaylists -1)
ENDIF
BREAK
CASE eTurnJBOffNoMoney RETURN 0
ENDSWITCH
SCRIPT_ASSERT("<JUKEBOX> GET_JUKEBOX_MENU_LIMIT: Invalid stage!")
RETURN 0
ENDFUNC
PROC HANDLE_PC_CONTROL(BOOL &bPrevious, BOOL &bNext, BOOL &bAccept, BOOL &bBack, BOOL &bLeft, BOOL &bRight)
// Mouse control support
IF IS_PC_VERSION()
AND NOT NETWORK_TEXT_CHAT_IS_TYPING()
IF IS_USING_CURSOR(FRONTEND_CONTROL)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_LOOK_LR)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_LOOK_UD)
DISABLE_CONTROL_ACTION(FRONTEND_CONTROL, INPUT_FRONTEND_PAUSE_ALTERNATE)
ENABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_CURSOR_ACCEPT)
ENABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_CURSOR_CANCEL)
ENABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_CURSOR_SCROLL_UP)
ENABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_CURSOR_SCROLL_DOWN)
IF ((g_iMenuCursorItem = MENU_CURSOR_NO_ITEM) OR (g_iMenuCursorItem = MENU_CURSOR_NO_CAMERA_MOVE) OR (g_iMenuCursorItem = MENU_CURSOR_DRAG_CAM))
ENABLE_CONTROL_ACTION(CAMERA_CONTROL, INPUT_LOOK_LR)
ENABLE_CONTROL_ACTION(CAMERA_CONTROL, INPUT_LOOK_UD)
ELSE
DISABLE_CONTROL_ACTION(CAMERA_CONTROL, INPUT_LOOK_LR)
DISABLE_CONTROL_ACTION(CAMERA_CONTROL, INPUT_LOOK_UD)
ENDIF
HANDLE_CURSOR_HIGHLIGHTED_MENU_ITEMS()
HANDLE_MENU_CURSOR(FALSE)
ENDIF
// Mouse select
IF IS_MENU_CURSOR_ACCEPT_PRESSED()
IF g_iMenuCursorItem = iCurrentSelection
bAccept = TRUE
ELSE
iCurrentSelection = g_iMenuCursorItem
SET_CURRENT_MENU_ITEM(iCurrentSelection)
ENDIF
ENDIF
// Menu cursor back
IF IS_MENU_CURSOR_CANCEL_PRESSED()
bBack = TRUE
ENDIF
// Mouse scroll up
IF IS_MENU_CURSOR_SCROLL_UP_PRESSED()
OR IS_CONTROL_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_UP)
bPrevious = TRUE
ENDIF
// Mouse scroll down
IF IS_MENU_CURSOR_SCROLL_DOWN_PRESSED()
OR IS_CONTROL_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_DOWN)
bNext = TRUE
ENDIF
IF IS_CONTROL_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_LEFT)
bLeft = TRUE
ENDIF
IF IS_CONTROL_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_RIGHT)
bRight = TRUE
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Control the response to any input whilst the menu is active
PROC HANDLE_JUKEBOX_MENU_INPUT()
INT iLeftY
INT iMenuInputDelay = 150
BOOL bPrevious, bNext, bLeft, bRight
IF IS_USING_CURSOR(FRONTEND_CONTROL)
iMenuInputDelay = 110
ENDIF
JUKEBOX_MENU_CONTROL_DISABLE()
// Set the input flag states
ENABLE_CONTROL_ACTION(FRONTEND_CONTROL, INPUT_FRONTEND_AXIS_X)
ENABLE_CONTROL_ACTION(FRONTEND_CONTROL, INPUT_FRONTEND_AXIS_Y)
ENABLE_CONTROL_ACTION(FRONTEND_CONTROL, INPUT_FRONTEND_ACCEPT)
ENABLE_CONTROL_ACTION(FRONTEND_CONTROL, INPUT_FRONTEND_UP)
ENABLE_CONTROL_ACTION(FRONTEND_CONTROL, INPUT_FRONTEND_DOWN)
BOOL bAccept = (IS_CONTROL_JUST_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_ACCEPT))
BOOL bBack = (IS_CONTROL_JUST_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_CANCEL) OR IS_DISABLED_CONTROL_JUST_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_CANCEL))
iLeftY = GET_CONTROL_VALUE(FRONTEND_CONTROL, INPUT_FRONTEND_AXIS_Y)
IF NOT IS_BIT_SET(iJukeBoxBS, cidelayTimerActive)
IF IS_USING_CURSOR(FRONTEND_CONTROL)
HANDLE_PC_CONTROL(bPrevious, bNext, bAccept, bBack, bLeft, bRight)
ELSE
bPrevious = ((iLeftY < 100) OR IS_CONTROL_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_UP))
bNext = ((iLeftY > 150) OR IS_CONTROL_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_DOWN))
bLeft = IS_CONTROL_PRESSED(FRONTEND_CONTROL, INPUT_CONTEXT_SECONDARY)
bRight = IS_CONTROL_PRESSED(FRONTEND_CONTROL, INPUT_CONTEXT)
ENDIF
IF bPrevious OR bNext OR bLeft OR bRight
START_NET_TIMER(stAnalogueMenuDelay)
SET_BIT(iJukeBoxBS, ciDelayTimerActive)
ENDIF
ELIF HAS_NET_TIMER_EXPIRED(stAnalogueMenuDelay, iMenuInputDelay)
RESET_NET_TIMER(stAnalogueMenuDelay)
CLEAR_BIT(iJukeBoxBS, ciDelayTimerActive)
ENDIF
//Menu navigation
IF bNext
iCurrentSelection ++
IF iCurrentSelection > GET_JUKEBOX_MENU_LIMIT()
iCurrentSelection = 0
ENDIF
PLAY_SOUND_FRONTEND(-1, "NAV_UP_DOWN", "HUD_FRONTEND_DEFAULT_SOUNDSET")
ENDIF
IF bPrevious
iCurrentSelection --
IF iCurrentSelection < 0
iCurrentSelection = GET_JUKEBOX_MENU_LIMIT()
ENDIF
PLAY_SOUND_FRONTEND(-1, "NAV_UP_DOWN", "HUD_FRONTEND_DEFAULT_SOUNDSET")
ENDIF
#IF FEATURE_CASINO_HEIST
IF iCurrentProperty = PROPERTY_ARCADE_GARAGE
IF bLeft
IF serverBD.fVolume < 1
REQUEST_VOLUME_CHANGE(serverBD.fVolume + 0.5)
ENDIF
ENDIF
IF bRight
IF serverBD.fVolume > 0
REQUEST_VOLUME_CHANGE(serverBD.fVolume - 0.5)
ENDIF
ENDIF
ENDIF
#ENDIF
//Menu - Select an option
IF bAccept
BOOL bPlayErrorSound = FALSE
SWITCH eMenuStage
CASE eTurnJBOffNoMoney
eMenuStage = eSelectPlaylist
iCurrentSelection = 0
//Set the selection to off and exit the activity
iSelectedPlaylist = iMenuOnOffOption
SET_JUKEBOX_ACTIVITY_STAGE(eJAExit)
CLEAR_BIT(iJukeBoxBS ,ciPlayInsertCoinAnim)
BREAK
CASE eSelectPlaylist
IF iCurrentSelection != ServerBD.iActivePlaylistID
OR NOT IS_JUKEBOX_ON()
//Move to the next stage if we've selected a valid playlist
iSelectedPlaylist = iCurrentSelection
eMenuStage = eConfirm
iCurrentSelection = 0
SET_BIT(iJukeBoxBS, ciRebuildMenu)
SET_BIT(iJukeBoxBS ,ciPlaySelectTrackAnim)
ELSE
bPlayErrorSound = TRUE
ENDIF
BREAK
CASE eConfirm
IF iCurrentSelection = 0
//This will allow us to interupt the select playlist anim to exit
iJukeBoxSyncSceneStage = 0
SET_JUKEBOX_ACTIVITY_STAGE(eJAExit)
//If we have not selected the 'Turn off' option charge the player and switch the playlist
IF iSelectedPlaylist != iMenuOnOffOption
SET_BIT(iJukeBoxBS ,ciPlayInsertCoinAnim)
CHARGE_PLAYER_FOR_JB(iSelectedPlaylist)
ENDIF
ELSE
//Go back to the select playlist stage as we selected the 'No' option
eMenuStage = eSelectPlaylist
iCurrentSelection = 0
SET_BIT(iJukeBoxBS, ciRebuildMenu)
ENDIF
BREAK
ENDSWITCH
//Play the relevant sound effect
IF bPlayErrorSound
PLAY_SOUND_FRONTEND(-1, "ERROR", "HUD_FRONTEND_DEFAULT_SOUNDSET")
ELSE
PLAY_SOUND_FRONTEND(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET")
ENDIF
ENDIF
//Menu - Back button pressed
IF bBack
PLAY_SOUND_FRONTEND(-1, "BACK", "HUD_FRONTEND_DEFAULT_SOUNDSET")
SWITCH eMenuStage
CASE eSelectPlaylist
eMenuStage = eSelectPlaylist
iSelectedPlaylist = -1
SET_JUKEBOX_ACTIVITY_STAGE(eJAExit)
CLEAR_BIT(iJukeBoxBS ,ciPlayInsertCoinAnim)
BREAK
CASE eTurnJBOffNoMoney
iCurrentSelection = 0
iSelectedPlaylist = -1
SET_JUKEBOX_ACTIVITY_STAGE(eJAExit)
CLEAR_BIT(iJukeBoxBS ,ciPlayInsertCoinAnim)
BREAK
CASE eConfirm
eMenuStage = eSelectPlaylist
iCurrentSelection = 0
iSelectedPlaylist = -1
SET_BIT(iJukeBoxBS, ciRebuildMenu)
BREAK
ENDSWITCH
ENDIF
ENDPROC
PROC BUILD_JUKEBOX_OFF_MENU()
SETUP_MENU_WITH_TITLE("JBOX_MENU_T")
ADD_MENU_ITEM_TEXT(0, "JBOX_M_OFF", 0, TRUE)
SET_CURRENT_MENU_ITEM(iCurrentSelection)
ADD_MENU_HELP_KEY_CLICKABLE(INPUT_FRONTEND_ACCEPT, "ITEM_SELECT")
ADD_MENU_HELP_KEY_CLICKABLE(INPUT_FRONTEND_CANCEL, "ITEM_BACK")
ENDPROC
PROC BUILD_JUKEBOX_MAIN_MENU()
SETUP_MENU_WITH_TITLE("JBOX_MENU_T")
INT iNumberOfPlaylists = GET_MAX_AVAILABLE_PLAYLISTS()
INT iIter
//Build a list of available playlists
REPEAT iNumberOfPlaylists iIter
TEXT_LABEL_15 tlPlaylistName = "JBOX_PLIST_"
//if we're playing this playlist now mark it as unavailable
BOOL bItemAvailable = iIter != serverBD.iActivePlaylistID
IF NOT IS_JUKEBOX_ON()
bItemAvailable = TRUE
ENDIF
tlPlaylistName += iIter
// Custom mix
#IF FEATURE_TUNER
IF iCurrentProperty != PROPERTY_ARCADE_GARAGE
AND pPropertyOwnerID = PLAYER_ID()
AND HAS_LOCAL_PLAYER_COLLECTED_ANY_USB_MIXES()
AND (iIter = (GET_MAX_AVAILABLE_PLAYLISTS() - 1))
tlPlaylistName += "a"
ENDIF
#ENDIF
ADD_MENU_ITEM_TEXT(iIter, tlPlaylistName, 0, bItemAvailable)
IF bItemAvailable
ADD_MENU_ITEM_TEXT(iIter, "JBOX_CHARGE", 0, bItemAvailable)
ENDIF
ENDREPEAT
IF IS_JUKEBOX_ON()
//Add an option to switch off the JB
ADD_MENU_ITEM_TEXT(iIter, "JBOX_M_OFF", 0, TRUE)
ENDIF
SET_CURRENT_MENU_ITEM(iCurrentSelection)
ADD_MENU_HELP_KEY_CLICKABLE(INPUT_FRONTEND_ACCEPT, "ITEM_SELECT")
ADD_MENU_HELP_KEY_CLICKABLE(INPUT_FRONTEND_CANCEL, "ITEM_BACK")
IF iCurrentProperty = PROPERTY_ARCADE_GARAGE
AND IS_JUKEBOX_ON()
ADD_MENU_HELP_KEY_GROUP(INPUTGROUP_FRONTEND_DPAD_LR, "HUD_INPUT77")
ENDIF
ENDPROC
PROC BUILD_FINAL_MENU()
SETUP_MENU_WITH_TITLE("JBOX_CONF_T")
ADD_MENU_ITEM_TEXT(0, "JBOX_MENU_0",0, TRUE)
ADD_MENU_ITEM_TEXT(1, "JBOX_MENU_1",0, TRUE)
SET_CURRENT_MENU_ITEM(iCurrentSelection)
ADD_MENU_HELP_KEY_CLICKABLE(INPUT_FRONTEND_ACCEPT, "ITEM_SELECT")
ADD_MENU_HELP_KEY_CLICKABLE(INPUT_FRONTEND_CANCEL, "ITEM_BACK")
ENDPROC
PROC BUILD_JUKEBOX_MENU()
SWITCH eMenuStage
CASE eTurnJBOffNoMoney
BUILD_JUKEBOX_OFF_MENU()
BREAK
CASE eSelectPlaylist
BUILD_JUKEBOX_MAIN_MENU()
BREAK
CASE eConfirm
BUILD_FINAL_MENU()
BREAK
ENDSWITCH
ENDPROC
PROC SETUP_JUKEBOX_OFF_DESCRIPTION()
SET_CURRENT_MENU_ITEM_DESCRIPTION("JBOX_TOFF_NM")
ENDPROC
PROC SETUP_JUKEBOX_MAIN_MENU_DESCRIPTION()
IF iCurrentSelection = ServerBD.iActivePlaylistID
AND IS_JUKEBOX_ON()
SET_CURRENT_MENU_ITEM_DESCRIPTION("JBOX_PLIST_PLY")
ELIF iCurrentSelection = iMenuOnOffOption
SET_CURRENT_MENU_ITEM_DESCRIPTION("JBOX_TOFF")
ELSE
TEXT_LABEL_15 tlPlaylistDesc = "JBOX_PLIST_D_"
tlPlaylistDesc += iCurrentSelection
// Custom mix
#IF FEATURE_TUNER
IF iCurrentProperty != PROPERTY_ARCADE_GARAGE
AND pPropertyOwnerID = PLAYER_ID()
AND HAS_LOCAL_PLAYER_COLLECTED_ANY_USB_MIXES()
AND (iCurrentSelection = (GET_MAX_AVAILABLE_PLAYLISTS() - 1))
tlPlaylistDesc += "a"
ENDIF
#ENDIF
SET_CURRENT_MENU_ITEM_DESCRIPTION(tlPlaylistDesc)
ENDIF
ENDPROC
PROC SETUP_FINAL_MENU_DESCRIPTION()
IF NOT IS_JUKEBOX_ON()
IF iCurrentSelection = 0
SET_CURRENT_MENU_ITEM_DESCRIPTION("JBOX_CONF_ON")
ELSE
SET_CURRENT_MENU_ITEM_DESCRIPTION("JBOX_CANC_PUR")
ENDIF
ELIF iSelectedPlaylist = iMenuOnOffOption
IF iCurrentSelection = 0
SET_CURRENT_MENU_ITEM_DESCRIPTION("JBOX_CONF_OFF")
ELSE
SET_CURRENT_MENU_ITEM_DESCRIPTION("JBOX_CANC_PUR")
ENDIF
ELIF iCurrentSelection = 0
SET_CURRENT_MENU_ITEM_DESCRIPTION("JBOX_CONF_PUR")
ELSE
SET_CURRENT_MENU_ITEM_DESCRIPTION("JBOX_CANC_PUR")
ENDIF
ENDPROC
PROC RUN_MENU_DESCRIPTION()
SWITCH eMenuStage
CASE eTurnJBOffNoMoney
SETUP_JUKEBOX_OFF_DESCRIPTION()
BREAK
CASE eSelectPlaylist
SETUP_JUKEBOX_MAIN_MENU_DESCRIPTION()
BREAK
CASE eConfirm
SETUP_FINAL_MENU_DESCRIPTION()
BREAK
ENDSWITCH
ENDPROC
PROC DEAL_WITH_HUD_ELEMENTS()
HIDE_HUD_AND_RADAR_THIS_FRAME()
HIDE_SCRIPTED_HUD_COMPONENT_THIS_FRAME(HUD_MP_RANK_BAR)
ENDPROC
/// PURPOSE:
/// Loads them menu assets and builds the current menu
PROC RUN_JUKEBOX_MENU()
IF NOT IS_PAUSE_MENU_ACTIVE_EX()
HANDLE_JUKEBOX_MENU_INPUT()
IF LOAD_MENU_ASSETS("SNK_MNU", DEFAULT, TRUE)
IF IS_BIT_SET(iJukeBoxBS, ciRebuildMenu)
BUILD_JUKEBOX_MENU()
CLEAR_BIT(iJukeBoxBS, ciRebuildMenu)
ELSE
SET_CURRENT_MENU_ITEM(iCurrentSelection)
ENDIF
RUN_MENU_DESCRIPTION()
DRAW_MENU()
DEAL_WITH_HUD_ELEMENTS()
SET_BIT(iJukeBoxBS, ciMenuActive)
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Plays a sync scene on the local player
/// PARAMS:
/// sAnimation - The anim to play
/// bInterupt - Interupt the currently playing animation
/// bLooped - Determines if the anim should play on a loop
/// RETURNS:
/// True when the animation has been started
FUNC BOOL PLAY_INDIVIDUAL_JUKEBOX_ANIMATION(STRING sAnimation, BOOl bInterupt = FALSE, BOOL bLooped = FALSE)
INT iLocaJukeboxSynchSceneID = NETWORK_GET_LOCAL_SCENE_FROM_NETWORK_ID(iJukeBoxSyncSceneID)
REQUEST_ANIM_DICT(sJukeboxAnimDict)
IF HAS_ANIM_DICT_LOADED(sJukeboxAnimDict)
IF (NOT IS_SYNCHRONIZED_SCENE_RUNNING(iLocaJukeboxSynchSceneID)
OR GET_SYNCHRONIZED_SCENE_PHASE(iLocaJukeboxSynchSceneID) >= 0.96)
OR bInterupt
IF IS_SYNCHRONIZED_SCENE_RUNNING(iJukeBoxSyncSceneID)
DETACH_SYNCHRONIZED_SCENE(iJukeBoxSyncSceneID)
iJukeBoxSyncSceneID = -1
ENDIF
iJukeBoxSyncSceneID = NETWORK_CREATE_SYNCHRONISED_SCENE(vSynchSceneOrigin,
GET_ENTITY_ROTATION(obJukebox),DEFAULT,FALSE, bLooped)
NETWORK_ADD_PED_TO_SYNCHRONISED_SCENE(PLAYER_PED_ID(), iJukeBoxSyncSceneID,
sJukeboxAnimDict, sAnimation,
SLOW_BLEND_IN, REALLY_SLOW_BLEND_OUT, SYNCED_SCENE_USE_PHYSICS|SYNCED_SCENE_DONT_INTERRUPT, DEFAULT, REALLY_SLOW_BLEND_IN)
NETWORK_START_SYNCHRONISED_SCENE(iJukeBoxSyncSceneID)
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> PLAY_INDIVIDUAL_JUKEBOX_ANIMATION started anim: ", sAnimation)
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
//// PURPOSE:
/// Runs the enter to idle sync scene animations on the player using the jukebox
/// RETURNS:
/// True wehn the idle sync scene has started
FUNC BOOL RUN_ENTER_TO_IDLE_SYNC_SCENE()
JUKEBOX_MENU_CONTROL_DISABLE()
CONST_INT ciWalkToPosition 0
CONST_INT ciBeginEnterAnim 1
CONST_INT ciBeginIdleAnim 2
SWITCH iJukeBoxSyncSceneStage
CASE ciWalkToPosition
DISABLE_CELLPHONE_THIS_FRAME_ONLY()
VECTOR vTaregtPos
vTaregtPos = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(obJukebox, <<0.06, -0.8958, 0.2>>)
IF NOT HAS_NET_TIMER_STARTED(stJBWalkToTimer)
START_NET_TIMER(stJBWalkToTimer)
ELIF HAS_NET_TIMER_EXPIRED(stJBWalkToTimer, 2000)
iJukeBoxSyncSceneStage = ciBeginEnterAnim
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> JB in position timer expierd! Moving on")
ENDIF
IF NOT ARE_VECTORS_ALMOST_EQUAL(GET_ENTITY_COORDS(PLAYER_PED_ID()), vTaregtPos, 0.8)
IF NOT IsPedPerformingTask(PLAYER_PED_ID(), SCRIPT_TASK_GO_STRAIGHT_TO_COORD)
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> Tasking player to go to:", vTaregtPos)
TASK_GO_STRAIGHT_TO_COORD(PLAYER_PED_ID(), vTaregtPos, PEDMOVEBLENDRATIO_WALK, 4000, GET_ENTITY_HEADING(obJukebox), 0.125)
ENDIF
ELSE
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> JB in position! Moving on")
#IF IS_DEBUG_BUILD
VECTOR vPlayerPos
vPlayerPos = GET_ENTITY_COORDS(PLAYER_PED_ID())
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX>[ENTER] player pos: ", vPlayerPos, " Target pos: ", vTaregtPos)
#ENDIF
iJukeBoxSyncSceneStage = ciBeginEnterAnim
ENDIF
BREAK
CASE ciBeginEnterAnim
IF PLAY_INDIVIDUAL_JUKEBOX_ANIMATION("ENTER", TRUE)
RESET_NET_TIMER(stJBWalkToTimer)
iJukeBoxSyncSceneStage = ciBeginIdleAnim
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> ciBeginEnterAnim starting enter anim. Walk to timer reset")
ENDIF
BREAK
CASE ciBeginIdleAnim
IF PLAY_INDIVIDUAL_JUKEBOX_ANIMATION("IDLE", DEFAULT, TRUE)
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX>: RUN_ENTER_TO_IDLE_SYNC_SCENE complete. Start menu")
//Reset the sync scene stage and return true
iJukeBoxSyncSceneStage = 0
RETURN TRUE
ENDIF
BREAK
ENDSWITCH
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Starts the eixt anim on the player currently using the jukebox
/// Pass true for bPlayInsertCoins to play the insert coin anim first, folowed by the exit
/// RETURNS:
/// True when the sync scene has begun
FUNC BOOL RUN_EXIT_SYNC_SCENE(BOOL bPlayInsertCoins)
CONST_INT ciPlayInsertCoins 0
CONST_INT ciPlayExit 1
IF NOT bPlayInsertCoins
iJukeBoxSyncSceneStage = ciPlayExit
ENDIF
IF NOT IS_ENTITY_DEAD(PLAYER_PED_ID())
SWITCH iJukeBoxSyncSceneStage
CASE ciPlayInsertCoins
IF PLAY_INDIVIDUAL_JUKEBOX_ANIMATION("INSERT_COINS", TRUE)
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX>: <RUN_EXIT_SYNC_SCENE> Coins inserted. moving onto exit anim")
iJukeBoxSyncSceneStage = ciPlayExit
ENDIF
BREAK
CASE ciPlayExit
IF PLAY_INDIVIDUAL_JUKEBOX_ANIMATION("EXIT", NOT bPlayInsertCoins)
REMOVE_ANIM_DICT(sJukeboxAnimDict)
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX>: <RUN_EXIT_SYNC_SCENE> Started exit sync scene")
//Reset the sync scene stage and return true
iJukeBoxSyncSceneStage = 0
RETURN TRUE
ENDIF
BREAK
ENDSWITCH
ELSE
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX>: <RUN_EXIT_SYNC_SCENE> player is dead")
ENDIF
DISABLE_CELLPHONE_THIS_FRAME_ONLY()
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Runs the slect track animation as a sync scene, then returns back to the idle animation
/// RETURNS:
/// True once the idle animation has started
FUNC BOOL PLAY_SELECT_TRACK_SYNC_SCENE()
CONST_INT ciPlaySelectTrack 0
CONST_INT ciPlayIdle 1
IF NOT IS_ENTITY_DEAD(PLAYER_PED_ID())
SWITCH iJukeBoxSyncSceneStage
CASE ciPlaySelectTrack
IF PLAY_INDIVIDUAL_JUKEBOX_ANIMATION("SELECT_TRACK", TRUE)
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX>: <PLAY_SELECT_TRACK_SYNC_SCENE> Track selected. moving back to idle")
iJukeBoxSyncSceneStage = ciPlayIdle
ENDIF
BREAK
CASE ciPlayIdle
IF PLAY_INDIVIDUAL_JUKEBOX_ANIMATION("IDLE", DEFAULT, TRUE)
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX>: <PLAY_SELECT_TRACK_SYNC_SCENE> Started idle scene")
//Reset the sync scene stage and return true
iJukeBoxSyncSceneStage = 0
RETURN TRUE
ENDIF
BREAK
ENDSWITCH
ELSE
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX>: <RUN_EXIT_SYNC_SCENE> player is dead")
ENDIF
DISABLE_CELLPHONE_THIS_FRAME_ONLY()
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Checks if the local player is alone in the locate
/// RETURNS:
/// True if alone in the locate
FUNC BOOL IS_PLAYER_ALONE_IN_AREA()
PED_INDEX mPed[2]
INT iPeds = GET_PED_NEARBY_PEDS(PLAYER_PED_ID(), mPed)
IF iPeds > 0
INT i
FOR i = 0 TO 1
IF NOT IS_ENTITY_DEAD(mPed[i])
IF IS_ENTITY_AT_COORD(mPed[i], vtrigger, <<0.8, 0.8, 1.5>>, FALSE, TRUE, TM_ON_FOOT)
RETURN FALSE
ENDIF
ENDIF
ENDFOR
ENDIF
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Checks if the player can use the jukebox
/// RETURNS:
/// True if the player is able to use the jukebox
FUNC BOOL IS_PLAYER_IN_POSITION_AND_ABLE_TO_USE_JB()
BOOL bPropertyAllowsJukebox = TRUE
IF IS_PLAYER_IN_ARCADE_PROPERTY(PLAYER_ID())
AND NOT HAS_PLAYER_COMPLETED_FULL_ARCADE_SETUP(pPropertyOwnerID)
bPropertyAllowsJukebox = FALSE
ENDIF
IF NOT IS_BIT_SET(g_JukeboxData.iBitSet, ciJBBlockPlayerUsingJB)
AND NOT IS_PLAYER_USING_OFFICE_SEATID_VALID()
AND NOT IS_PHONE_ONSCREEN()
AND NOT IS_BROWSER_OPEN()
AND NOT IS_PED_IN_COVER(PLAYER_PED_ID())
AND (GET_ROOM_KEY_FROM_ENTITY(PLAYER_PED_ID()) = GET_ROOM_KEY_FROM_ENTITY(obJukebox))
AND IS_ENTITY_AT_COORD(PLAYER_PED_ID(), vtrigger, <<0.55, 0.8, 1.5>>, FALSE, TRUE, TM_ON_FOOT)
//AND IS_PLAYER_ALONE_IN_AREA()
AND NOT IS_ENTITY_PLAYING_ANIM(PLAYER_PED_ID(), sJukeboxAnimDict, "EXIT")
AND NOT IS_PLAYER_ENTERING_OR_EXITING_PROPERTY(PLAYER_ID())
AND NOT IS_JUKEBOX_IN_USE()
AND bPropertyAllowsJukebox
AND NOT IS_BIT_SET(g_simpleInteriorData.iFifthBS, BS5_SIMPLE_INTERIOR_INSTANT_PASS_OUT)
AND Get_Peds_Drunk_Alcohol_Hit_Count(PLAYER_PED_ID()) <= 9
AND NOT g_SpawnData.bPassedOutDrunk
AND NOT IS_PED_RAGDOLL(PLAYER_PED_ID())
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Runs a check to see if the player can afford to use the JB
/// If the JB is on we should still be able to turn it off
FUNC BOOL CAN_PLAYER_USE_JB_WITH_NO_MONEY()
IF IS_JUKEBOX_ON()
OR NETWORK_CAN_SPEND_MONEY(1, FALSE, FALSE, FALSE)
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
PROC REQUEST_PLAYLIST_CHANGE(INT iNewPlaylistID)
playerBD[NATIVE_TO_INT(PLAYER_ID())].iNewPlaylistRequestID = iNewPlaylistID
SET_BIT(playerBD[NATIVE_TO_INT(PLAYER_ID())].iBitSet, ciRequestedPlaylistChange)
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> RUN_JUKEBOX_ACTIVITY stage exit. Requested playlist change to ", iNewPlaylistID)
ENDPROC
PROC CLEAR_NO_MONEY_HELP()
IF IS_HELP_MESSAGE_BEING_DISPLAYED()
AND IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("JBOX_NO_MONEY")
CLEAR_HELP(TRUE)
ENDIF
ENDPROC
/// PURPOSE:
/// Runs the jukebox activity stages for the local player
/// This will request to use the jukebox when the player triggers the context intention
PROC RUN_JUKEBOX_ACTIVITY()
SWITCH eActivityStage
CASE eJAInactive
IF IS_PLAYER_IN_POSITION_AND_ABLE_TO_USE_JB()
AND CAN_PLAYER_USE_JB_WITH_NO_MONEY()
CLEAR_NO_MONEY_HELP()
IF iJukeboxContextIntention = NEW_CONTEXT_INTENTION
REGISTER_CONTEXT_INTENTION(iJukeboxContextIntention, CP_HIGH_PRIORITY, "JBOX_INTERACT")
ELIF (iJukeboxContextIntention != NEW_CONTEXT_INTENTION AND HAS_CONTEXT_BUTTON_TRIGGERED(iJukeboxContextIntention))
AND NOT IS_PED_RUNNING(PLAYER_PED_ID())
SET_JUKEBOX_ACTIVITY_STAGE(eJAAwaitingAproval)
RELEASE_CONTEXT_INTENTION(iJukeboxContextIntention)
//Disable player controls
NET_SET_PLAYER_CONTROL(PLAYER_ID(), FALSE, NSPC_LEAVE_CAMERA_CONTROL_ON)
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX>: RUN_JB_ACTIVITY player is requesting use of JB. Player ID: ", NATIVE_TO_INT(PLAYER_ID()))
SET_BIT(playerBD[NATIVE_TO_INT(PLAYER_ID())].iBitSet, ciRequestingUseOfJukebox)
SET_BIT(g_JukeboxData.iBitSet, ciJBTriggeredContextInt)
ENDIF
SET_BIT(g_JukeboxData.iBitSet, ciJBInLocate)
ELIF iJukeboxContextIntention != NEW_CONTEXT_INTENTION
RELEASE_CONTEXT_INTENTION(iJukeboxContextIntention)
CLEAR_BIT(g_JukeboxData.iBitSet, ciJBInLocate)
ELIF NOT CAN_PLAYER_USE_JB_WITH_NO_MONEY()
AND IS_PLAYER_IN_POSITION_AND_ABLE_TO_USE_JB()
PRINT_HELP_FOREVER("JBOX_NO_MONEY")
CLEAR_BIT(g_JukeboxData.iBitSet, ciJBInLocate)
ELSE
CLEAR_NO_MONEY_HELP()
CLEAR_BIT(g_JukeboxData.iBitSet, ciJBInLocate)
ENDIF
BREAK
CASE eJAAwaitingAproval
IF serverBD.iJukeboxUserID = NATIVE_TO_INT(PLAYER_ID())
AND IS_SKYSWOOP_AT_GROUND()
//Start the entry sync scene from the begining
iJukeBoxSyncSceneStage = 0
//Move onto the next main stage
SET_JUKEBOX_ACTIVITY_STAGE(eJAPlayingEntryAnim)
//Clear our request
CLEAR_BIT(playerBD[NATIVE_TO_INT(PLAYER_ID())].iBitSet, ciRequestingUseOfJukebox)
//Inform the server we're using the Jukebox
SET_BIT(playerBD[NATIVE_TO_INT(PLAYER_ID())].iBitSet, ciUsingJukebox)
//Clear the walk to timer
RESET_NET_TIMER(stJBWalkToTimer)
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX>: RUN_JB_ACTIVITY JB request approved. Player ID: ", NATIVE_TO_INT(PLAYER_ID()), " Calling NET_SET_PLAYER_CONTROL with FALSE & NSPC_LEAVE_CAMERA_CONTROL_ON")
ELIF serverBD.iJukeboxUserID != -1
OR NOT IS_SKYSWOOP_AT_GROUND()
SET_JUKEBOX_ACTIVITY_STAGE(eJAInactive)
CLEAR_BIT(playerBD[NATIVE_TO_INT(PLAYER_ID())].iBitSet, ciRequestingUseOfJukebox)
CLEAR_BIT(g_JukeboxData.iBitSet, ciJBTriggeredContextInt)
NET_SET_PLAYER_CONTROL(PLAYER_ID(), TRUE)
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX>: RUN_JB_ACTIVITY JB request denied. Player ID: ", NATIVE_TO_INT(PLAYER_ID()), " Player already using is: ", serverBD.iJukeboxUserID)
ENDIF
BREAK
CASE eJAPlayingEntryAnim
IF RUN_ENTER_TO_IDLE_SYNC_SCENE()
iCurrentSelection = 0
SET_BIT(iJukeBoxBS, ciRebuildMenu)
SET_JUKEBOX_ACTIVITY_STAGE(eJAMenuActive)
IF NOT NETWORK_CAN_SPEND_MONEY(1, FALSE, FALSE, FALSE)
eMenuStage = eTurnJBOffNoMoney
ENDIF
ENDIF
BREAK
CASE eJAMenuActive
IF IS_BIT_SET(iJukeBoxBS ,ciPlaySelectTrackAnim)
IF PLAY_SELECT_TRACK_SYNC_SCENE()
CLEAR_BIT(iJukeBoxBS, ciPlaySelectTrackAnim)
ENDIF
ENDIF
RUN_JUKEBOX_MENU()
BREAK
CASE eJAExit
IF IS_BIT_SET(iJukeBoxBS, ciMenuActive)
CLEAR_BIT(iJukeBoxBS, ciMenuActive)
CLEANUP_MENU_ASSETS()
ENDIF
INT iLocaJukeboxSynchSceneID
iLocaJukeboxSynchSceneID = NETWORK_GET_LOCAL_SCENE_FROM_NETWORK_ID(iJukeBoxSyncSceneID)
IF NOT IS_ENTITY_PLAYING_ANIM(PLAYER_PED_ID(), sJukeboxAnimDict, "EXIT")
RUN_EXIT_SYNC_SCENE(IS_BIT_SET(iJukeBoxBS ,ciPlayInsertCoinAnim))
//Select then new playlist whilst the insert coins anim is playing
//Or when the exit anim has begun (For turning the JB off)
IF iSelectedPlaylist != -1
IF IS_ENTITY_PLAYING_ANIM(PLAYER_PED_ID(), sJukeboxAnimDict, "INSERT_COINS")
AND GET_SYNCHRONIZED_SCENE_PHASE(iLocaJukeboxSynchSceneID) >= 0.82
OR NOT IS_BIT_SET(iJukeBoxBS ,ciPlayInsertCoinAnim)
REQUEST_PLAYLIST_CHANGE(iSelectedPlaylist)
ENDIF
ENDIF
ELSE
IF GET_SYNCHRONIZED_SCENE_PHASE(iLocaJukeboxSynchSceneID) >= 0.96
CLEAR_BIT(iJukeBoxBS ,ciPlayInsertCoinAnim)
CLEAR_BIT(iJukeBoxBS, ciPlaySelectTrackAnim)
CLEAR_BIT(playerBD[NATIVE_TO_INT(PLAYER_ID())].iBitSet, ciUsingJukebox)
CLEAR_BIT(playerBD[NATIVE_TO_INT(PLAYER_ID())].iBitSet, ciRequestedPlaylistChange)
CLEAR_BIT(playerBD[NATIVE_TO_INT(PLAYER_ID())].iBitSet, ciRequestedVolumeChange)
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> RUN_JUKEBOX_ACTIVITY calling NET_SET_PLAYER_CONTROL with TRUE")
NET_SET_PLAYER_CONTROL(PLAYER_ID(), TRUE)
CLEAR_BIT(g_JukeboxData.iBitSet, ciJBTriggeredContextInt)
eMenuStage = eSelectPlaylist
#IF FEATURE_CASINO_HEIST
IF iCurrentProperty = PROPERTY_ARCADE_GARAGE
iTelemSelectedPlaylist = GlobalplayerBD_FM[NATIVE_TO_INT(pPropertyOwnerID)].propertyDetails.iClubhouseJBPlaylistID
ELSE
#ENDIF
iTelemSelectedPlaylist = GlobalplayerBD_FM[NATIVE_TO_INT(pPropertyOwnerID)].propertyDetails.iArcadeJBPlaylistID
#IF FEATURE_CASINO_HEIST
ENDIF
#ENDIF
// Send telemetry data on this activity.
PLAYSTATS_MC_CLUBHOUSE_ACTIVITY(
GB_GET_GANG_BOSS_UUID_INT_A(GB_GET_LOCAL_PLAYER_GANG_BOSS()),
GB_GET_GANG_BOSS_UUID_INT_B(GB_GET_LOCAL_PLAYER_GANG_BOSS()),
GB_GET_MATCH_ID_1_FOR_TELEMETRY(),
GB_GET_MATCH_ID_2_FOR_TELEMETRY(),
4,
iTelemSelectedPlaylist,
0)
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> PLAYSTATS_MC_CLUBHOUSE_ACTIVITY: Setting Jukebox station to ", iTelemSelectedPlaylist)
SET_JUKEBOX_ACTIVITY_STAGE(eJAInactive)
ENDIF
ENDIF
BREAK
ENDSWITCH
ENDPROC
PROC RUN_NETWORK_SCRIPT_INIT()
IF NETWORK_IS_GAME_IN_PROGRESS()
AND NOT IS_BIT_SET(iJukeBoxBS, cinetworkInitDone)
//Get the interior instance we are in
INT iInteriorInstance = mpPropMaintain.iMostRecentPropertyID
#IF FEATURE_CASINO_HEIST
IF IS_PLAYER_IN_ANY_SIMPLE_INTERIOR(PLAYER_ID())
iInteriorInstance = GET_SIMPLE_INTERIOR_INT_SCRIPT_INSTANCE() + 32
ENDIF
#ENDIF
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> RUN_NETWORK_SCRIPT_INIT atempting to create a network script with an instance ID of: ", iInteriorInstance)
NETWORK_SET_SCRIPT_IS_SAFE_FOR_NETWORK_GAME()
NETWORK_SET_THIS_SCRIPT_IS_NETWORK_SCRIPT(NUM_NETWORK_PLAYERS, FALSE, iInteriorInstance)
// This makes sure the net script is active, waits until it is.
HANDLE_NET_SCRIPT_INITIALISATION()
SET_THIS_SCRIPT_CAN_BE_PAUSED(FALSE)
NETWORK_REGISTER_HOST_BROADCAST_VARIABLES(serverBD, SIZE_OF(serverBD))
NETWORK_REGISTER_PLAYER_BROADCAST_VARIABLES(playerBD, SIZE_OF(playerBD))
// KGM: Wait for the first network broadcast before moving on
// (after Initialisation and Broadcast Variable Registration - Hosts can do this too)
IF NOT Wait_For_First_Network_Broadcast()
PRINTLN("[MP safehouse]<JUKEBOX> - MISSION END - FAILED TO RECEIVE INITIAL NETWORK BROADCAST - SCRIPT CLEANUP")
CLEANUP_JUKEBOX()
ENDIF
SET_BIT(iJukeBoxBS, cinetworkInitDone)
CDEBUG1LN(DEBUG_SAFEHOUSE, "[MP safehouse]<JUKEBOX> JUKEBOX script: Ready for network launch...")
ENDIF
ENDPROC
/// PURPOSE:
/// Check for any flags that mean this script should terminate
PROC RUN_CLEANUP_CHECKS()
#IF FEATURE_CASINO_HEIST
IF iCurrentProperty = PROPERTY_ARCADE_GARAGE
IF IS_PLAYER_WALKING_OUT_OF_SIMPLE_INTERIOR(PLAYER_ID())
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> Shutting down! IS_PLAYER_WALKING_OUT_OF_SIMPLE_INTERIOR")
CLEANUP_JUKEBOX()
ELIF NOT NETWORK_IS_GAME_IN_PROGRESS()
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> Shutting down! No network game is currently active")
CLEANUP_JUKEBOX()
ELIF IS_ENTITY_DEAD(PLAYER_PED_ID())
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> Shutting down! Player is injured or dead")
CLEANUP_JUKEBOX()
ELIF IS_BIT_SET(g_JukeboxData.iBitSet, ciJBInteriorGeneralCleanup)
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> Shutting down! ciJBInteriorGeneralCleanup is set")
CLEAR_BIT(g_JukeboxData.iBitSet, ciJBInteriorGeneralCleanup)
CLEANUP_JUKEBOX()
ELIF IS_BIT_SET(g_SimpleInteriorData.iSeventhBS, BS7_SIMPLE_INTERIOR_RENOVATION_TRIGGERED)
IF eActivityStage != eJAInactive
IF IS_BIT_SET(iJukeBoxBS, ciMenuActive)
CLEAR_BIT(iJukeBoxBS, ciMenuActive)
CLEANUP_MENU_ASSETS()
ENDIF
CLEAR_BIT(iJukeBoxBS ,ciPlayInsertCoinAnim)
CLEAR_BIT(iJukeBoxBS, ciPlaySelectTrackAnim)
CLEAR_BIT(playerBD[NATIVE_TO_INT(PLAYER_ID())].iBitSet, ciUsingJukebox)
CLEAR_BIT(playerBD[NATIVE_TO_INT(PLAYER_ID())].iBitSet, ciRequestedPlaylistChange)
CLEAR_BIT(playerBD[NATIVE_TO_INT(PLAYER_ID())].iBitSet, ciRequestedVolumeChange)
CLEAR_BIT(playerBD[NATIVE_TO_INT(PLAYER_ID())].iBitSet, ciRequestingUseOfJukebox)
CLEAR_BIT(g_JukeboxData.iBitSet, ciJBTriggeredContextInt)
eMenuStage = eSelectPlaylist
SET_JUKEBOX_ACTIVITY_STAGE(eJAInactive)
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> Cleaning up JB! Player is renovating the arcade whilst using the JB")
ENDIF
ELIF NOT IS_PLAYER_IN_ARCADE_PROPERTY(PLAYER_ID())
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> Shutting down! Player has left the building")
CLEANUP_JUKEBOX()
ENDIF
ELSE
#ENDIF
IF IS_BIT_SET(GlobalplayerBD_FM[NATIVE_TO_INT(PLAYER_ID())].propertyDetails.iBS,PROPERTY_BROADCAST_BS_PLAYER_EXITING_PROPERTY)
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> Shutting down! PROPERTY_BROADCAST_BS_PLAYER_EXITING_PROPERTY is set")
CLEANUP_JUKEBOX()
ELIF NOT NETWORK_IS_GAME_IN_PROGRESS()
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> Shutting down! No network game is currently active")
CLEANUP_JUKEBOX()
ELIF IS_ENTITY_DEAD(PLAYER_PED_ID())
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> Shutting down! Player is injured or dead")
CLEANUP_JUKEBOX()
ELIF IS_BIT_SET(g_JukeboxData.iBitSet, ciJBInteriorGeneralCleanup)
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> Shutting down! ciJBInteriorGeneralCleanup is set")
CLEAR_BIT(g_JukeboxData.iBitSet, ciJBInteriorGeneralCleanup)
CLEANUP_JUKEBOX()
ELIF GET_UPDATE_CUSTOM_APT()
IF eActivityStage != eJAInactive
IF IS_BIT_SET(iJukeBoxBS, ciMenuActive)
CLEAR_BIT(iJukeBoxBS, ciMenuActive)
CLEANUP_MENU_ASSETS()
ENDIF
CLEAR_BIT(iJukeBoxBS ,ciPlayInsertCoinAnim)
CLEAR_BIT(iJukeBoxBS, ciPlaySelectTrackAnim)
CLEAR_BIT(playerBD[NATIVE_TO_INT(PLAYER_ID())].iBitSet, ciUsingJukebox)
CLEAR_BIT(playerBD[NATIVE_TO_INT(PLAYER_ID())].iBitSet, ciRequestedPlaylistChange)
CLEAR_BIT(playerBD[NATIVE_TO_INT(PLAYER_ID())].iBitSet, ciRequestedVolumeChange)
CLEAR_BIT(playerBD[NATIVE_TO_INT(PLAYER_ID())].iBitSet, ciRequestingUseOfJukebox)
CLEAR_BIT(g_JukeboxData.iBitSet, ciJBTriggeredContextInt)
eMenuStage = eSelectPlaylist
SET_JUKEBOX_ACTIVITY_STAGE(eJAInactive)
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> Cleaning up JB! Player is renovating the clubhouse whilst using the JB")
ENDIF
ELIF iCurrentProperty != GlobalplayerBD_FM[NATIVE_TO_INT(PLAYER_ID())].propertyDetails.iCurrentlyInsideProperty
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> Shutting down! Player has left the building")
CLEANUP_JUKEBOX()
ENDIF
#IF FEATURE_CASINO_HEIST
ENDIF
#ENDIF
ENDPROC
/// PURPOSE:
/// Returns the location of the jukebox interior. Used to grab a handle to the object
FUNC VECTOR GET_JUKEBOX_COORDS
IF IS_PROPERTY_CLUBHOUSE(iCurrentProperty)
RETURN GET_CLUBHOUSE_JUKEBOX_COORDS(iCurrentProperty)
#IF FEATURE_CASINO_HEIST
ELIF iCurrentProperty = PROPERTY_ARCADE_GARAGE
RETURN GET_ARCADE_JUKEBOX_COORDS()
#ENDIF
ENDIF
RETURN <<0.0, 0.0, 0.0>>
ENDFUNC
/// PURPOSE:
/// Sets the station for all emitters in the current interior
PROC CHANGE_PLAYLIST(INT iPlaylistID)
IF IS_PROPERTY_CLUBHOUSE(iCurrentProperty)
//We want to save the current playlist but not try to set the static emiters to an invalid playlist
IF iPlaylistID != iMenuOnOffOption
AND iPlaylistID != -1
IF pPropertyOwnerID = PLAYER_ID()
AND iJBSelectedPlistPlayTime != 0
SAVE_JB_PLAYLIST_PLAY_TIME(iLocalActivePlaylistID)
ENDIF
CHANGE_CLUBHOUSE_JUKEBOX_PLAYLIST(iCurrentProperty, iPlaylistID)
ENDIF
IF iPlaylistID != -1
iLocalActivePlaylistID = iPlaylistID
ENDIF
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> CHANGE_PLAYLIST Server says Playlist changed to ", iPlaylistID)
#IF FEATURE_CASINO_HEIST
ELIF iCurrentProperty = PROPERTY_ARCADE_GARAGE
//We want to save the current playlist but not try to set the static emiters to an invalid playlist
IF iPlaylistID != iMenuOnOffOption
AND iPlaylistID != -1
IF pPropertyOwnerID = PLAYER_ID()
AND iJBSelectedPlistPlayTime != 0
SAVE_JB_PLAYLIST_PLAY_TIME(iLocalActivePlaylistID)
ENDIF
CHANGE_ARCADE_JUKEBOX_PLAYLIST(iPlaylistID)
ENDIF
IF iPlaylistID != -1
iLocalActivePlaylistID = iPlaylistID
ENDIF
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> CHANGE_PLAYLIST Server says Playlist changed to ", iPlaylistID)
#ENDIF
ELSE
CASSERTLN(DEBUG_SAFEHOUSE, "<JUKEBOX> CHANGE_PLAYLIST invalid property ID: ", iCurrentProperty)
ENDIF
ENDPROC
/// PURPOSE:
/// Turn on or off the static emitters
PROC SET_ALL_JUKEBOX_STATIC_EMITTERS_ON_OFF_STATE(BOOL bEnabled)
IF bEnabled
SET_BIT(iJukeBoxBS, ciJukeboxEnabled)
ELSE
CLEAR_BIT(iJukeBoxBS, ciJukeboxEnabled)
ENDIF
IF IS_PROPERTY_CLUBHOUSE(iCurrentProperty)
SET_CLUBHOUSE_JUKEBOX_STATIC_EMITTERS_ON_OFF_STATE(iCurrentProperty, bEnabled)
#IF FEATURE_CASINO_HEIST
ELIF iCurrentProperty = PROPERTY_ARCADE_GARAGE
SET_ARCADE_JUKEBOX_STATIC_EMITTERS_ON_OFF_STATE(bEnabled)
#ENDIF
ELSE
CASSERTLN(DEBUG_SAFEHOUSE, "<JUKEBOX> SET_ALL_JUKEBOX_STATIC_EMITTERS_ON_OFF_STATE invalid property ID: ", iCurrentProperty)
ENDIF
ENDPROC
/// PURPOSE:
/// Run checks for any requests to use the jukebox or change the current playlist
PROC RUN_SERVER_CHECKS()
IF IS_JUKEBOX_IN_USE()
//Check for any new playlist requests
IF IS_BIT_SET(playerBD[serverBD.iJukeboxUserID].iBitSet, ciRequestedPlaylistChange)
IF playerBD[serverBD.iJukeboxUserID].iNewPlaylistRequestID = iMenuOnOffOption
IF IS_JUKEBOX_ON()
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> RUN_SERVER_CHECKS jukebox no longer in use. Requesting Jukebox turn OFF by player: ", serverBD.iJukeboxUserID)
serverBD.iJukeboxServerState = JBServerStageOff
serverBD.iActivePlaylistID = iMenuOnOffOption
IF NATIVE_TO_INT(pPropertyOwnerID) = serverBD.iJukeboxUserID
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> RUN_SERVER_CHECKS jukebox no longer in use. Property Owner requested turn off")
serverBD.bNewPlaylistSelectedByOwner = TRUE
ENDIF
ENDIF
ELIF serverBD.iActivePlaylistID != playerBD[serverBD.iJukeboxUserID].iNewPlaylistRequestID
serverBD.iActivePlaylistID = playerBD[serverBD.iJukeboxUserID].iNewPlaylistRequestID
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> RUN_SERVER_CHECKS jukebox no longer in use. Requesting PList change to ", serverBD.iActivePlaylistID, " by player: ", serverBD.iJukeboxUserID)
IF NATIVE_TO_INT(pPropertyOwnerID) = serverBD.iJukeboxUserID
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> RUN_SERVER_CHECKS jukebox no longer in use. Property Owner requested state change")
serverBD.bNewPlaylistSelectedByOwner = TRUE
ENDIF
serverBD.iJukeboxServerState = JBServerStageOn
ENDIF
ENDIF
//Check for any new playlist requests
IF IS_BIT_SET(playerBD[serverBD.iJukeboxUserID].iBitSet, ciRequestedVolumeChange)
IF serverBD.fVolume != playerBD[serverBD.iJukeboxUserID].fVolume
serverBD.fVolume = playerBD[serverBD.iJukeboxUserID].fVolume
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> RUN_SERVER_CHECKS - Requesting volume change to ", serverBD.fVolume, " by player: ", serverBD.iJukeboxUserID)
ENDIF
ENDIF
//Monitor the player using the jukebox
IF NOT IS_BIT_SET(playerBD[serverBD.iJukeboxUserID].iBitSet, ciUsingJukebox)
AND NOT IS_BIT_SET(playerBD[serverBD.iJukeboxUserID].iBitSet, ciRequestingUseOfJukebox)
serverBD.iJukeboxUserID = -1
ENDIF
//Look for any new requests to use the JB
ELIF IS_BIT_SET(playerBD[iStaggeredPlayerLoop].iBitSet, ciRequestingUseOfJukebox)
IF NOT IS_JUKEBOX_IN_USE()
serverBD.iJukeboxUserID = iStaggeredPlayerLoop
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> RUN_SERVER_CHECKS jukebox now in use by player ", iStaggeredPlayerLoop)
ENDIF
ENDIF
//Make sure we reset this flag once the oner has saved the new playlist
//We do this because the owner of the clubhouse may not be the script host
IF NOT IS_JUKEBOX_IN_USE()
#IF FEATURE_CASINO_HEIST
IF iCurrentProperty = PROPERTY_ARCADE_GARAGE
IF GlobalplayerBD_FM[NATIVE_TO_INT(pPropertyOwnerID)].propertyDetails.iArcadeJBPlaylistID = serverBD.iActivePlaylistID
AND (NOT IS_JUKEBOX_ON() AND GlobalplayerBD_FM[NATIVE_TO_INT(pPropertyOwnerID)].propertyDetails.iArcadeJBPlaylistID = iMenuOnOffOption)
serverBD.bNewPlaylistSelectedByOwner = FALSE
ENDIF
ELSE
#ENDIF
IF GlobalplayerBD_FM[NATIVE_TO_INT(pPropertyOwnerID)].propertyDetails.iClubhouseJBPlaylistID = serverBD.iActivePlaylistID
AND (NOT IS_JUKEBOX_ON() AND GlobalplayerBD_FM[NATIVE_TO_INT(pPropertyOwnerID)].propertyDetails.iClubhouseJBPlaylistID = iMenuOnOffOption)
serverBD.bNewPlaylistSelectedByOwner = FALSE
ENDIF
#IF FEATURE_CASINO_HEIST
ENDIF
#ENDIF
ENDIF
//Move to the next player in the loop
iStaggeredPlayerLoop ++
IF iStaggeredPlayerLoop = NUM_NETWORK_PLAYERS
iStaggeredPlayerLoop = 0
ENDIF
ENDPROC
/// PURPOSE:
/// Run client side checks. Currently only checks for playlist changes
PROC RUN_CLIENT_CHECKS()
BOOL bServerJBEnabled = IS_JUKEBOX_ON()
IF serverBD.iActivePlaylistID != iLocalActivePlaylistID
CHANGE_PLAYLIST(serverBD.iActivePlaylistID)
//Set our stat and BD if we're the owner
//We do this because the owner of the clubhouse may not be the script host
IF pPropertyOwnerID = PLAYER_ID()
AND serverBD.bNewPlaylistSelectedByOwner
#IF FEATURE_CASINO_HEIST
IF iCurrentProperty = PROPERTY_ARCADE_GARAGE
SET_PACKED_STAT_INT(PACKED_MP_INT_ARCADE_JB_SAVED_PLAYLIST, iLocalActivePlaylistID)
GlobalplayerBD_FM[NATIVE_TO_INT(pPropertyOwnerID)].propertyDetails.iArcadeJBPlaylistID = iLocalActivePlaylistID
ELSE
#ENDIF
SET_PACKED_STAT_INT(PACKED_MP_INT_CLUBHOUSE_JB_SAVED_PLAYLIST, iLocalActivePlaylistID)
GlobalplayerBD_FM[NATIVE_TO_INT(pPropertyOwnerID)].propertyDetails.iClubhouseJBPlaylistID = iLocalActivePlaylistID
#IF FEATURE_CASINO_HEIST
ENDIF
#ENDIF
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> RUN_CLIENT_CHECKS property owner setting PL stats")
ENDIF
ENDIF
IF (IS_BIT_SET(iJukeBoxBS, ciJukeboxEnabled) != bServerJBEnabled)
SET_ALL_JUKEBOX_STATIC_EMITTERS_ON_OFF_STATE(bServerJBEnabled)
//Set our stat and BD if we're the owner
//We do this because the owner of the clubhouse may not be the script host
IF pPropertyOwnerID = PLAYER_ID()
AND serverBD.bNewPlaylistSelectedByOwner
AND NOT bServerJBEnabled
#IF FEATURE_CASINO_HEIST
IF iCurrentProperty = PROPERTY_ARCADE_GARAGE
SET_PACKED_STAT_INT(PACKED_MP_INT_ARCADE_JB_SAVED_PLAYLIST, iMenuOnOffOption)
GlobalplayerBD_FM[NATIVE_TO_INT(pPropertyOwnerID)].propertyDetails.iArcadeJBPlaylistID = iMenuOnOffOption
ELSE
#ENDIF
SET_PACKED_STAT_INT(PACKED_MP_INT_CLUBHOUSE_JB_SAVED_PLAYLIST, iMenuOnOffOption)
GlobalplayerBD_FM[NATIVE_TO_INT(pPropertyOwnerID)].propertyDetails.iClubhouseJBPlaylistID = iMenuOnOffOption
#IF FEATURE_CASINO_HEIST
ENDIF
#ENDIF
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> RUN_CLIENT_CHECKS property owner setting PL stat to OFF")
ENDIF
ENDIF
ENDPROC
PROC RUN_AUTOSAVE_STAT_TIMER()
IF pPropertyOwnerID = PLAYER_ID()
AND IS_JUKEBOX_ON()
IF NOT HAS_NET_TIMER_STARTED(stStatAutosaveTimer)
START_NET_TIMER(stStatAutosaveTimer)
ELIF HAS_NET_TIMER_EXPIRED(stStatAutosaveTimer, 180000)
SAVE_JB_PLAYLIST_PLAY_TIME(iLocalActivePlaylistID)
REINIT_NET_TIMER(stStatAutosaveTimer)
ENDIF
ENDIF
ENDPROC
//FEATURE_BIKER
//***********SCRIPT functions***********
SCRIPT
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> started \"", GET_THIS_SCRIPT_NAME(), "\"")
IF GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH(GET_HASH_KEY("ob_jukebox")) > 1
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> JUKEBOX script already running! cleaning up.")
CLEANUP_JUKEBOX()
ENDIF
iCurrentProperty = GlobalplayerBD_FM[NATIVE_TO_INT(PLAYER_ID())].propertyDetails.iCurrentlyInsideProperty
#IF FEATURE_CASINO_HEIST
IF IS_PLAYER_IN_ARCADE_PROPERTY(PLAYER_ID())
iCurrentProperty = PROPERTY_ARCADE_GARAGE
ENDIF
#ENDIF
iMenuOnOffOption = GET_MENU_OFF_OPTION()
RUN_CLEANUP_CHECKS()
RUN_NETWORK_SCRIPT_INIT()
// Default cleanup
IF HAS_FORCE_CLEANUP_OCCURRED()
OR MPGlobalsAmbience.bRunningFmIntroCut
IF MPGlobalsAmbience.bRunningFmIntroCut
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> bRunningFmIntroCut is true so cleaning up!")
ENDIF
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> DEFAULT CLEANUP")
CLEANUP_JUKEBOX()
ENDIF
//Get the ID of the owner so we can check their BD
GAMER_HANDLE sPropertyOwnerGH
GET_GAMER_HANDLE_OF_PROPERTY_OWNER(PLAYER_ID(), sPropertyOwnerGH)
IF IS_GAMER_HANDLE_VALID(sPropertyOwnerGH)
IF NETWORK_IS_GAMER_IN_MY_SESSION(sPropertyOwnerGH)
pPropertyOwnerID = NETWORK_GET_PLAYER_FROM_GAMER_HANDLE(sPropertyOwnerGH)
ENDIF
ENDIF
#IF FEATURE_CASINO_HEIST
IF iCurrentProperty = PROPERTY_ARCADE_GARAGE
pPropertyOwnerID = GET_OWNER_OF_SIMPLE_INTERIOR_PLAYER_IS_IN(PLAYER_ID())
START_AUDIO_SCENE("dlc_ch_arcade_music_volume")
SET_AUDIO_SCENE_VARIABLE("dlc_ch_arcade_music_volume", "ArcadeRadioVolumeDucking", serverBD.fVolume)
ENDIF
#ENDIF
IF pPropertyOwnerID = INVALID_PLAYER_INDEX()
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> CLEANUP pPropertyOwnerID = INVALID_PLAYER_INDEX")
CLEANUP_JUKEBOX()
ENDIF
IF NETWORK_IS_HOST_OF_THIS_SCRIPT()
//Select the saved playlist or turn off the jukebox
#IF FEATURE_CASINO_HEIST
IF iCurrentProperty = PROPERTY_ARCADE_GARAGE
IF GlobalplayerBD_FM[NATIVE_TO_INT(pPropertyOwnerID)].propertyDetails.iArcadeJBPlaylistID != iMenuOnOffOption
AND HAS_PLAYER_COMPLETED_FULL_ARCADE_SETUP(pPropertyOwnerID)
CHANGE_PLAYLIST(GlobalplayerBD_FM[NATIVE_TO_INT(pPropertyOwnerID)].propertyDetails.iArcadeJBPlaylistID)
SET_ALL_JUKEBOX_STATIC_EMITTERS_ON_OFF_STATE(TRUE)
serverBD.fVolume = 0.5
serverBD.iJukeboxServerState = JBServerStageOn
serverBD.iActivePlaylistID = iLocalActivePlaylistID
INIT_TIME_STAMP_FOR_CURRENT_PLAYLIST()
ELSE
SET_ALL_JUKEBOX_STATIC_EMITTERS_ON_OFF_STATE(FALSE)
serverBD.fVolume = 0.5
serverBD.iJukeboxServerState = JBServerStageOff
ENDIF
ELSE
#ENDIF
IF GlobalplayerBD_FM[NATIVE_TO_INT(pPropertyOwnerID)].propertyDetails.iClubhouseJBPlaylistID != iMenuOnOffOption
CHANGE_PLAYLIST(GlobalplayerBD_FM[NATIVE_TO_INT(pPropertyOwnerID)].propertyDetails.iClubhouseJBPlaylistID)
SET_ALL_JUKEBOX_STATIC_EMITTERS_ON_OFF_STATE(TRUE)
serverBD.iJukeboxServerState = JBServerStageOn
serverBD.iActivePlaylistID = iLocalActivePlaylistID
INIT_TIME_STAMP_FOR_CURRENT_PLAYLIST()
ELSE
SET_ALL_JUKEBOX_STATIC_EMITTERS_ON_OFF_STATE(FALSE)
serverBD.iJukeboxServerState = JBServerStageOff
ENDIF
#IF FEATURE_CASINO_HEIST
ENDIF
#ENDIF
ELSE
//Get the current playlist and activate the static emitters
CHANGE_PLAYLIST(serverBD.iActivePlaylistID)
SET_ALL_JUKEBOX_STATIC_EMITTERS_ON_OFF_STATE(IS_JUKEBOX_ON())
ENDIF
/// main update loop
WHILE TRUE
RUN_CLEANUP_CHECKS()
iMenuOnOffOption = GET_MENU_OFF_OPTION()
#IF FEATURE_CASINO_HEIST
IF iCurrentProperty = PROPERTY_ARCADE_GARAGE
SET_AUDIO_SCENE_VARIABLE("dlc_ch_arcade_music_volume", "ArcadeRadioVolumeDucking", serverBD.fVolume)
ENDIF
#ENDIF
IF obJukebox != NULL
AND NOT ARE_VECTORS_EQUAL(vSynchSceneOrigin, <<0.0, 0.0, 0.0>>)
IF NETWORK_IS_HOST_OF_THIS_SCRIPT()
RUN_SERVER_CHECKS()
ENDIF
//Runs the sync scene and controls moving through local jukebox stages
RUN_JUKEBOX_ACTIVITY()
//Responds to changes from the server
RUN_CLIENT_CHECKS()
//Run auto save timer
RUN_AUTOSAVE_STAT_TIMER()
ELSE
//Find the object for the property we are inside
//We don't do this to start with as we need to setup the static emitters before the jukebox model is ready
IF iCurrentProperty != -1
AND iCurrentProperty <= MAX_MP_PROPERTIES
vJukeboxCoords = GET_JUKEBOX_COORDS()
IF GET_FRAME_COUNT() % 4 = 0
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> Looking for Jukebox at coords: ", vJukeboxCoords, " In property: ", iCurrentProperty)
ENDIF
#IF FEATURE_CASINO_HEIST
IF iCurrentProperty = PROPERTY_ARCADE_GARAGE
obJukebox = GET_CLOSEST_OBJECT_OF_TYPE(vJukeboxCoords, 2.0, INT_TO_ENUM(MODEL_NAMES, HASH("ch_prop_arcade_jukebox_01a")), TRUE, FALSE, FALSE)
ELSE
#ENDIF
obJukebox = GET_CLOSEST_OBJECT_OF_TYPE(vJukeboxCoords, 2.0, INT_TO_ENUM(MODEL_NAMES, HASH("bkr_prop_clubhouse_jukebox_01a")), TRUE, FALSE, FALSE)
#IF FEATURE_CASINO_HEIST
ENDIF
#ENDIF
//Calculate the trigger vector
IF DOES_ENTITY_EXIST(obJukebox)
vtrigger = GET_ENTITY_COORDS(obJukebox,TRUE) - (GET_ENTITY_FORWARD_VECTOR(obJukebox)* 0.6)
vtrigger.z += 0.5
vSynchSceneOrigin = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(obJukebox, <<0.0, -0.12, -0.0315>>)
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX>: Found Jukebox! vSyncSceneOrigin: ", vSynchSceneOrigin)
ENDIF
ELSE
CDEBUG1LN(DEBUG_SAFEHOUSE, "<JUKEBOX> JUKEBOX script not in a property so cleaning up!")
CLEANUP_JUKEBOX()
ENDIF
ENDIF
#IF IS_DEBUG_BUILD
IF IS_DEBUG_KEY_JUST_PRESSED(KEY_Q, KEYBOARD_MODIFIER_CTRL_SHIFT, "Kill Jukebox Script")
CLEANUP_JUKEBOX(TRUE)
ENDIF
#ENDIF
WAIT(0)
ENDWHILE
// Script should never reach here. Always terminate with cleanup function.
CASSERTLN(DEBUG_SAFEHOUSE, "<JUKEBOX> \"", GET_THIS_SCRIPT_NAME(), "\" should never reach here. Always terminate with cleanup function.")
ENDSCRIPT