/******************************* * Largely a rewrite of the Jukebox from D2K5 * * By N3X15 *******************************/ #define JUKEMODE_SHUFFLE 1 // Default #define JUKEMODE_REPEAT_SONG 2 #define JUKEMODE_PLAY_ONCE 3 // Play, then stop. #define JUKEMODE_COUNT 3 #define JUKEBOX_SCREEN_MAIN 1 // Default #define JUKEBOX_SCREEN_PAYMENT 2 #define JUKEBOX_SCREEN_SETTINGS 3 #define JUKEBOX_RELOAD_COOLDOWN 600 // 60s // Represents a record returned. /datum/song_info var/title = "" var/artist = "" var/album = "" var/url = "" var/length = 0 // decaseconds var/emagged = 0 New(var/list/json) title = json["title"] artist = json["artist"] album = json["album"] url = json["url"] length = text2num(json["length"]) proc/display() var/str="\"[title]\"" if(artist!="") str += ", by [artist]" if(album!="") str += ", from '[album]'" return str proc/displaytitle() if(artist==""&&title=="") return "\[NO TAGS\]" var/str="" if(artist!="") str += artist+" - " if(title!="") str += "\"[title]\"" else str += "Untitled" // Only show album if we have to. if(album!="" && artist == "") str += " ([album])" return str var/global/loopModeNames=list( JUKEMODE_SHUFFLE = "Shuffle", JUKEMODE_REPEAT_SONG = "Single", JUKEMODE_PLAY_ONCE= "Once", ) /obj/machinery/media/jukebox name = "Jukebox" desc = "A jukebox used for parties and shit." icon = 'icons/obj/jukebox.dmi' icon_state = "jukebox2" density = 1 anchored = 1 luminosity = 4 // Why was this 16 playing=0 var/loop_mode = JUKEMODE_SHUFFLE // Server-side playlist IDs this jukebox can play. var/list/playlists=list() // ID = Label // Playlist to load at startup. var/playlist_id = "" var/list/playlist var/current_song = 0 // 0, or whatever song is currently playing. var/next_song = 0 // 0, or a song someone has purchased. Played after current song completes. var/selected_song = 0 // 0 or the song someone has selected for purchase var/autoplay = 0 // Start playing after spawn? var/last_reload = 0 // Reload cooldown. var/screen = JUKEBOX_SCREEN_MAIN var/credits_held = 0 // Cash currently held var/credits_needed = 0 // Credits needed to complete purchase. var/change_cost = 10 // Current cost to change songs. var/list/change_access = list() // Access required to change songs var/datum/money_account/linked_account var/department // Department that gets the money var/state_base = "jukebox2" /obj/machinery/media/jukebox/New(loc) ..(loc) if(department) linked_account = department_accounts[department] else linked_account = station_account update_icon() /obj/machinery/media/jukebox/attack_ai(var/mob/user) attack_hand(user) /obj/machinery/media/jukebox/attack_paw() return /obj/machinery/media/jukebox/power_change() ..() if(emagged && !(stat & (NOPOWER|BROKEN))) playing = 1 update_icon() /obj/machinery/media/jukebox/update_icon() overlays = 0 if(stat & (NOPOWER|BROKEN) || !anchored) if(stat & BROKEN) icon_state = "[state_base]-broken" else icon_state = "[state_base]-nopower" stop_playing() return icon_state = state_base if(playing) if(emagged) overlays += "[state_base]-emagged" else overlays += "[state_base]-running" /obj/machinery/media/jukebox/proc/check_reload() return world.time > last_reload + JUKEBOX_RELOAD_COOLDOWN /obj/machinery/media/jukebox/attack_hand(var/mob/user) if(stat & NOPOWER) usr << "\red You don't see anything to mess with." return if(stat & BROKEN && playlist!=null) user.visible_message("\red [user.name] smacks the side of \the [src.name].","\red You hammer the side of \the [src.name].") stat &= ~BROKEN playlist=null playing=emagged update_icon() return var/t = "
" t += "Main" if(allowed(user)) t += " | Settings" t += "
" switch(screen) if(JUKEBOX_SCREEN_MAIN) t += ScreenMain(user) if(JUKEBOX_SCREEN_PAYMENT) t += ScreenPayment(user) if(JUKEBOX_SCREEN_SETTINGS) t += ScreenSettings(user) user.set_machine(src) var/datum/browser/popup = new (user,"jukebox",name,420,700) popup.set_content(t) popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) popup.open() /obj/machinery/media/jukebox/proc/ScreenMain(var/mob/user) var/t = "

Jukebox Interface

" t += "Power: [playing?"On":"Off"]
" t += "Play Mode: [loopModeNames[loop_mode]]
" if(playlist == null) t += "\[DOWNLOADING PLAYLIST, PLEASE WAIT\]" else if(req_access.len == 0 || allowed(user)) if(check_reload()) t += "Playlist: " for(var/plid in playlists) t += "[playlists[plid]]" else t += "Please wait before changing playlists." else t += "You cannot change the playlist." t += "
" if(current_song) var/datum/song_info/song=playlist[current_song] t += "Current song: [song.artist] - [song.title]
" if(next_song) var/datum/song_info/song=playlist[next_song] t += "Up next: [song.artist] - [song.title]
" t += "" var/i var/can_change=!next_song if(change_access.len > 0) // Permissions if(can_access(user.GetAccess(),req_access=change_access)) can_change = 1 for(i = 1,i <= playlist.len,i++) var/datum/song_info/song=playlist[i] t += "" t += "
Artist - TitleAlbum
#[i]" if(can_change) t += "" t += song.displaytitle() if(can_change) t += "" t += "[song.album]
" return t /obj/machinery/media/jukebox/proc/ScreenPayment(var/mob/user) var/t = "

Pay for Song

" var/datum/song_info/song=playlist[selected_song] t += {"

You've selected [song.displaytitle()].

Swipe ID card or insert cash to play this song next! ($[num2septext(change_cost)])

\[ Cancel \]
"} return t /obj/machinery/media/jukebox/proc/ScreenSettings(var/mob/user) var/dat={"

Settings

Banking
Payable Account:
Pricing
Change Song: $
Access

Permissions required to change song:

"} return dat /obj/machinery/media/jukebox/attackby(obj/item/W, mob/user) if(istype(W, /obj/item/weapon/card/emag)) current_song = 0 if(!emagged) playlist_id = "emagged" last_reload=world.time playlist=null loop_mode = JUKEMODE_SHUFFLE emagged = 1 playing = 1 user.visible_message("\red [user.name] slides something into the [src.name]'s card-reader.","\red You short out the [src.name].") update_icon() update_music() else if(istype(W,/obj/item/weapon/wrench)) var/un = !anchored ? "" : "un" user.visible_message("\blue [user.name] begins [un]locking \the [src.name]'s casters.","\blue You begin [un]locking \the [src.name]'s casters.") if(do_after(user,30)) playsound(get_turf(src), 'sound/items/Ratchet.ogg', 50, 1) anchored = !anchored user.visible_message("\blue [user.name] [un]locks \the [src.name]'s casters.","\red You [un]lock \the [src.name]'s casters.") playing = emagged update_music() update_icon() else if(istype(W,/obj/item/weapon/card/id)) if(!selected_song || screen!=JUKEBOX_SCREEN_PAYMENT) visible_message("\blue The machine buzzes.","\red You hear a buzz.") return var/obj/item/weapon/card/id/I = W if(!linked_account) visible_message("\red The machine buzzes, and flashes \"NO LINKED ACCOUNT\" on the screen.","You hear a buzz.") return var/datum/money_account/acct = get_card_account(I) if(!acct) visible_message("\red The machine buzzes, and flashes \"NO ACCOUNT\" on the screen.","You hear a buzz.") return if(credits_needed > acct.money) visible_message("\red The machine buzzes, and flashes \"NOT ENOUGH FUNDS\" on the screen.","You hear a buzz.") return visible_message("\blue The machine beeps happily.","You hear a beep.") acct.charge(credits_needed,linked_account,"Song selection at [areaMaster.name]'s [name].") credits_needed = 0 successful_purchase() attack_hand(user) else if(istype(W,/obj/item/weapon/spacecash)) if(!selected_song || screen!=JUKEBOX_SCREEN_PAYMENT) visible_message("\blue The machine buzzes.","\red You hear a buzz.") return if(!linked_account) visible_message("\red The machine buzzes, and flashes \"NO LINKED ACCOUNT\" on the screen.","You hear a buzz.") return var/obj/item/weapon/spacecash/C=W credits_held += C.worth*C.amount if(credits_held >= credits_needed) visible_message("\blue The machine beeps happily.","You hear a beep.") credits_held -= credits_needed credits_needed=0 screen=JUKEBOX_SCREEN_MAIN if(credits_held) var/obj/item/weapon/storage/box/B = new(loc) dispense_cash(credits_held,B) B.name="change" B.desc="A box of change." credits_held=0 successful_purchase() attack_hand(user) /obj/machinery/media/jukebox/proc/successful_purchase() next_song = selected_song selected_song = 0 screen = JUKEBOX_SCREEN_MAIN /obj/machinery/media/jukebox/Topic(href, href_list) if(isobserver(usr) && !isAdminGhost(usr)) usr << "\red You can't push buttons when your fingers go right through them, dummy." return ..() if(emagged) usr << "\red You touch the bluescreened menu. Nothing happens. You feel dumber." return if (href_list["power"]) playing=!playing update_music() update_icon() if("screen" in href_list) screen=text2num(href_list["screen"]) if("act" in href_list) switch(href_list["act"]) if("Save Settings") var/datum/money_account/new_linked_account = get_money_account(text2num(href_list["payableto"]),z) if(!new_linked_account) usr << "\red Unable to link new account. Aborting." return change_cost = max(0,text2num(href_list["set_change_cost"])) linked_account = new_linked_account if("lock" in href_list && href_list["lock"] != "") change_access = list(text2num(href_list["lock"])) else change_access = list() screen=POS_SCREEN_SETTINGS if (href_list["playlist"]) if(!check_reload()) usr << "\red You must wait 60 seconds between playlist reloads." return playlist_id=href_list["playlist"] last_reload=world.time playlist=null current_song = 0 next_song = 0 selected_song = 0 update_music() update_icon() if (href_list["song"]) selected_song=Clamp(text2num(href_list["song"]),1,playlist.len) if(!change_cost) next_song = selected_song selected_song = 0 if(!current_song) update_music() update_icon() else usr << "\red Swipe card or insert $[num2septext(change_cost)] to set this song." screen = JUKEBOX_SCREEN_PAYMENT credits_needed=change_cost if (href_list["cancelbuy"]) selected_song=0 screen = JUKEBOX_SCREEN_MAIN if (href_list["mode"]) loop_mode = (loop_mode % JUKEMODE_COUNT) + 1 return attack_hand(usr) /obj/machinery/media/jukebox/process() if(!playlist) var/url="[config.media_base_url]/index.php?playlist=[playlist_id]" testing("[src] - Updating playlist from [url]...") var/response = world.Export(url) playlist=list() if(response) var/json = file2text(response["CONTENT"]) if("/>" in json) visible_message("\icon[src] \The [src] buzzes, unable to update its playlist.","You hear a buzz.") stat &= BROKEN update_icon() return var/json_reader/reader = new() reader.tokens = reader.ScanJson(json) reader.i = 1 var/songdata = reader.read_value() for(var/list/record in songdata) playlist += new /datum/song_info(record) if(playlist.len==0) visible_message("\icon[src] \The [src] buzzes, unable to update its playlist.","You hear a buzz.") stat &= BROKEN update_icon() return visible_message("\icon[src] \The [src] beeps, and the menu on its front fills with [playlist.len] items.","You hear a beep.") else testing("[src] failed to update playlist: Response null.") stat &= BROKEN update_icon() return if(playing) var/datum/song_info/song if(current_song) song = playlist[current_song] if(!current_song || (song && world.time >= media_start_time + song.length)) current_song=1 if(next_song) current_song = next_song next_song = 0 else switch(loop_mode) if(JUKEMODE_SHUFFLE) current_song=rand(1,playlist.len) if(JUKEMODE_REPEAT_SONG) current_song=current_song if(JUKEMODE_PLAY_ONCE) playing=0 update_icon() return update_music() /obj/machinery/media/jukebox/update_music() if(current_song && playing) var/datum/song_info/song = playlist[current_song] media_url = song.url media_start_time = world.time visible_message("\icon[src] \The [src] begins to play [song.display()].","You hear music.") //visible_message("\icon[src] \The [src] warbles: [song.length/10]s @ [song.url]") else media_url="" media_start_time = 0 ..() /obj/machinery/media/jukebox/proc/stop_playing() //current_song=0 playing=0 update_music() return /obj/machinery/media/jukebox/bar department = "Civilian" req_access = list(access_bar) playlist_id="bar" // Must be defined on your server. playlists=list( "bar" = "Bar Mix", "jazz" = "Jazz", "rock" = "Rock" ) // So I don't have to do all this shit manually every time someone sacrifices pun-pun. // Also for debugging. /obj/machinery/media/jukebox/superjuke name = "Super Juke" desc = "A jukebox used for parties at Mount Olympus and shit." state_base = "superjuke" change_cost = 0 playlist_id="bar" // Must be defined on your server. playlists=list( "bar" = "Bar Mix", "jazz" = "Jazz", "rock" = "Rock", "emagged" = "Syndie Mix", "shuttle" = "Shuttle", "endgame" = "Apocalypse" ) /obj/machinery/media/jukebox/superjuke/attackby(obj/item/W, mob/user) // NO FUN ALLOWED. Emag list is included, anyway. if(istype(W, /obj/item/weapon/card/emag)) user << "\red Your [W] refuses to touch \the [src]!" return ..() /obj/machinery/media/jukebox/shuttle playlist_id="shuttle" // Must be defined on your server. playlists=list( "shuttle" = "Shuttle Mix" ) invisibility=101 // FAK U NO SONG 4 U