Date: Fri, 23 Aug 2019 06:50:01 -0400
Subject: [PATCH 15/18] jukebox additions + config changes
---
code/game/machinery/jukebox.dm | 717 +++++++++++++++--------------
code/modules/media/media_tracks.dm | 135 +++---
jukebox.json | 50 ++
nano/templates/jukebox.tmpl | 5 +
4 files changed, 491 insertions(+), 416 deletions(-)
create mode 100644 jukebox.json
diff --git a/code/game/machinery/jukebox.dm b/code/game/machinery/jukebox.dm
index 5c94d8a1148..a81859613bb 100644
--- a/code/game/machinery/jukebox.dm
+++ b/code/game/machinery/jukebox.dm
@@ -1,353 +1,364 @@
-//
-// Media Player Jukebox
-// Rewritten by Leshana from existing Polaris code, merging in D2K5 and N3X15 work
-//
-
-#define JUKEMODE_NEXT 1 // Advance to next song in the track list
-#define JUKEMODE_RANDOM 2 // Not shuffle, randomly picks next each time.
-#define JUKEMODE_REPEAT_SONG 3 // Play the same song over and over
-#define JUKEMODE_PLAY_ONCE 4 // Play, then stop.
-
-/obj/machinery/media/jukebox/
- name = "space jukebox"
- icon = 'icons/obj/jukebox.dmi'
- icon_state = "jukebox2-nopower"
- var/state_base = "jukebox2"
- anchored = 1
- density = 1
- power_channel = EQUIP
- use_power = 1
- idle_power_usage = 10
- active_power_usage = 100
- circuit = /obj/item/weapon/circuitboard/jukebox
-
- // Vars for hacking
- var/datum/wires/jukebox/wires = null
- var/hacked = 0 // Whether to show the hidden songs or not
- var/freq = 0 // Currently no effect, will return in phase II of mediamanager.
- //VOREStation Add
- var/loop_mode = JUKEMODE_PLAY_ONCE // Behavior when finished playing a song
- var/max_queue_len = 3 // How many songs are we allowed to queue up?
- var/list/queue = list()
- //VOREStation Add End
- var/datum/track/current_track
- var/list/datum/track/tracks = list(
- new/datum/track("Beyond", 'sound/ambience/ambispace.ogg'),
- new/datum/track("Clouds of Fire", 'sound/music/clouds.s3m'),
- new/datum/track("D`Bert", 'sound/music/title2.ogg'),
- new/datum/track("D`Fort", 'sound/ambience/song_game.ogg'),
- new/datum/track("Floating", 'sound/music/main.ogg'),
- new/datum/track("Endless Space", 'sound/music/space.ogg'),
- new/datum/track("Part A", 'sound/misc/TestLoop1.ogg'),
- new/datum/track("Scratch", 'sound/music/title1.ogg'),
- new/datum/track("Trai`Tor", 'sound/music/traitor.ogg'),
- new/datum/track("Stellar Transit", 'sound/ambience/space/space_serithi.ogg'),
- )
-
- // Only visible if hacked
- var/list/datum/track/secret_tracks = list(
- new/datum/track("Clown", 'sound/music/clown.ogg'),
- new/datum/track("Space Asshole", 'sound/music/space_asshole.ogg'),
- new/datum/track("Thunderdome", 'sound/music/THUNDERDOME.ogg'),
- new/datum/track("Russkiy rep Diskoteka", 'sound/music/russianrapdisco.ogg')
- )
-
-/obj/machinery/media/jukebox/New()
- ..()
- default_apply_parts()
- wires = new/datum/wires/jukebox(src)
- update_icon()
-
-/obj/machinery/media/jukebox/Destroy()
- qdel(wires)
- wires = null
- ..()
-
-// On initialization, copy our tracks from the global list
-/obj/machinery/media/jukebox/initialize()
- . = ..()
- if(LAZYLEN(all_jukebox_tracks)) //Global list has tracks
- tracks.Cut()
- secret_tracks.Cut()
- for(var/datum/track/T in all_jukebox_tracks) //Load them
- if(T.secret)
- secret_tracks |= T
- else
- tracks |= T
- else if(!LAZYLEN(tracks)) //We don't even have default tracks
- stat |= BROKEN // No tracks configured this round!
-
-/obj/machinery/media/jukebox/process()
- if(!playing)
- return
- if(inoperable())
- disconnect_media_source()
- playing = 0
- return
- // If the current track isn't finished playing, let it keep going
- if(current_track && world.time < media_start_time + current_track.duration)
- return
- // Otherwise time to pick a new one!
- if(queue.len > 0)
- current_track = queue[1]
- queue.Cut(1, 2) // Remove the item we just took off the list
- else
- // Oh... nothing in queue? Well then pick next according to our rules
- switch(loop_mode)
- if(JUKEMODE_NEXT)
- var/curTrackIndex = max(1, tracks.Find(current_track))
- var/newTrackIndex = (curTrackIndex % tracks.len) + 1 // Loop back around if past end
- current_track = tracks[newTrackIndex]
- if(JUKEMODE_RANDOM)
- var/previous_track = current_track
- do
- current_track = pick(tracks)
- while(current_track == previous_track && tracks.len > 1)
- if(JUKEMODE_REPEAT_SONG)
- current_track = current_track
- if(JUKEMODE_PLAY_ONCE)
- current_track = null
- playing = 0
- update_icon()
- updateDialog()
- start_stop_song()
-
-// Tells the media manager to start or stop playing based on current settings.
-/obj/machinery/media/jukebox/proc/start_stop_song()
- if(current_track && playing)
- media_url = current_track.url
- media_start_time = world.time
- visible_message("\The [src] begins to play [current_track.display()].")
- else
- media_url = ""
- media_start_time = 0
- update_music()
-
-/obj/machinery/media/jukebox/proc/set_hacked(var/newhacked)
- if (hacked == newhacked) return
- hacked = newhacked
- if (hacked)
- tracks.Add(secret_tracks)
- else
- tracks.Remove(secret_tracks)
- updateDialog()
-
-/obj/machinery/media/jukebox/attackby(obj/item/W as obj, mob/user as mob)
- src.add_fingerprint(user)
-
- if(default_deconstruction_screwdriver(user, W))
- return
- if(default_deconstruction_crowbar(user, W))
- return
- if(W.is_wirecutter())
- return wires.Interact(user)
- if(istype(W, /obj/item/device/multitool))
- return wires.Interact(user)
- if(W.is_wrench())
- if(playing)
- StopPlaying()
- user.visible_message("[user] has [anchored ? "un" : ""]secured \the [src].", "You [anchored ? "un" : ""]secure \the [src].")
- anchored = !anchored
- playsound(src, W.usesound, 50, 1)
- power_change()
- update_icon()
- if(!anchored)
- playing = 0
- disconnect_media_source()
- else
- update_media_source()
- return
- return ..()
-
-/obj/machinery/media/jukebox/power_change()
- if(!powered(power_channel) || !anchored)
- stat |= NOPOWER
- else
- stat &= ~NOPOWER
-
- if(stat & (NOPOWER|BROKEN) && playing)
- StopPlaying()
- update_icon()
-
-/obj/machinery/media/jukebox/update_icon()
- overlays.Cut()
- if(stat & (NOPOWER|BROKEN) || !anchored)
- if(stat & BROKEN)
- icon_state = "[state_base]-broken"
- else
- icon_state = "[state_base]-nopower"
- return
- icon_state = state_base
- if(playing)
- if(emagged)
- overlays += "[state_base]-emagged"
- else
- overlays += "[state_base]-running"
- if (panel_open)
- overlays += "panel_open"
-
-/obj/machinery/media/jukebox/Topic(href, href_list)
- if(..() || !(Adjacent(usr) || istype(usr, /mob/living/silicon)))
- return
-
- if(!anchored)
- to_chat(usr, "You must secure \the [src] first.")
- return
-
- if(inoperable())
- to_chat(usr, "\The [src] doesn't appear to function.")
- return
-
- if(href_list["change_track"])
- var/datum/track/T = locate(href_list["change_track"]) in tracks
- if(istype(T))
- current_track = T
- StartPlaying()
- else if(href_list["loopmode"])
- var/newval = text2num(href_list["loopmode"])
- loop_mode = sanitize_inlist(newval, list(JUKEMODE_NEXT, JUKEMODE_RANDOM, JUKEMODE_REPEAT_SONG, JUKEMODE_PLAY_ONCE), loop_mode)
- else if(href_list["volume"])
- var/newval = input("Choose Jukebox volume (0-100%)", "Jukebox volume", round(volume * 100.0))
- newval = sanitize_integer(text2num(newval), min = 0, max = 100, default = volume * 100.0)
- volume = newval / 100.0
- update_music() // To broadcast volume change without restarting song
- else if(href_list["stop"])
- StopPlaying()
- else if(href_list["play"])
- if(emagged)
- playsound(src.loc, 'sound/items/AirHorn.ogg', 100, 1)
- for(var/mob/living/carbon/M in ohearers(6, src))
- if(M.get_ear_protection() >= 2)
- continue
- M.sleeping = 0
- M.stuttering += 20
- M.ear_deaf += 30
- M.Weaken(3)
- if(prob(30))
- M.Stun(10)
- M.Paralyse(4)
- else
- M.make_jittery(500)
- spawn(15)
- explode()
- else if(current_track == null)
- to_chat(usr, "No track selected.")
- else
- StartPlaying()
-
- return 1
-
-/obj/machinery/media/jukebox/interact(mob/user)
- if(inoperable())
- to_chat(usr, "\The [src] doesn't appear to function.")
- return
- ui_interact(user)
-
-/obj/machinery/media/jukebox/ui_interact(mob/user, ui_key = "jukebox", var/datum/nanoui/ui = null, var/force_open = 1)
- var/title = "RetroBox - Space Style"
- var/data[0]
-
- if(operable())
- data["playing"] = playing
- data["hacked"] = hacked
- data["max_queue_len"] = max_queue_len
- data["media_start_time"] = media_start_time
- data["loop_mode"] = loop_mode
- data["volume"] = volume
- if(current_track)
- data["current_track_ref"] = "\ref[current_track]" // Convenient shortcut
- data["current_track"] = current_track.toNanoList()
- data["percent"] = playing ? min(100, round(world.time - media_start_time) / current_track.duration) : 0;
-
- var/list/nano_tracks = new
- for(var/datum/track/T in tracks)
- nano_tracks[++nano_tracks.len] = T.toNanoList()
- data["tracks"] = nano_tracks
-
- // update the ui if it exists, returns null if no ui is passed/found
- ui = GLOB.nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
- if (!ui)
- ui = new(user, src, ui_key, "jukebox.tmpl", title, 450, 600)
- ui.set_initial_data(data)
- ui.open()
- ui.set_auto_update(playing)
-
-/obj/machinery/media/jukebox/attack_ai(mob/user as mob)
- return src.attack_hand(user)
-
-/obj/machinery/media/jukebox/attack_hand(var/mob/user as mob)
- interact(user)
-
-/obj/machinery/media/jukebox/proc/explode()
- walk_to(src,0)
- src.visible_message("\the [src] blows apart!", 1)
-
- explosion(src.loc, 0, 0, 1, rand(1,2), 1)
-
- var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
- s.set_up(3, 1, src)
- s.start()
-
- new /obj/effect/decal/cleanable/blood/oil(src.loc)
- qdel(src)
-
-/obj/machinery/media/jukebox/attackby(obj/item/W as obj, mob/user as mob)
- src.add_fingerprint(user)
-
- if(default_deconstruction_screwdriver(user, W))
- return
- if(default_deconstruction_crowbar(user, W))
- return
- if(W.is_wrench())
- if(playing)
- StopPlaying()
- user.visible_message("[user] has [anchored ? "un" : ""]secured \the [src].", "You [anchored ? "un" : ""]secure \the [src].")
- anchored = !anchored
- playsound(src, W.usesound, 50, 1)
- power_change()
- update_icon()
- return
- return ..()
-
-/obj/machinery/media/jukebox/emag_act(var/remaining_charges, var/mob/user)
- if(!emagged)
- emagged = 1
- StopPlaying()
- visible_message("\The [src] makes a fizzling sound.")
- update_icon()
- return 1
-
-/obj/machinery/media/jukebox/proc/StopPlaying()
- playing = 0
- update_use_power(1)
- update_icon()
- start_stop_song()
-
-/obj/machinery/media/jukebox/proc/StartPlaying()
- if(!current_track)
- return
- playing = 1
- update_use_power(2)
- update_icon()
- start_stop_song()
- updateDialog()
-
-// Advance to the next track - Don't start playing it unless we were already playing
-/obj/machinery/media/jukebox/proc/NextTrack()
- if(!tracks.len) return
- var/curTrackIndex = max(1, tracks.Find(current_track))
- var/newTrackIndex = (curTrackIndex % tracks.len) + 1 // Loop back around if past end
- current_track = tracks[newTrackIndex]
- if(playing)
- start_stop_song()
- updateDialog()
-
-// Advance to the next track - Don't start playing it unless we were already playing
-/obj/machinery/media/jukebox/proc/PrevTrack()
- if(!tracks.len) return
- var/curTrackIndex = max(1, tracks.Find(current_track))
- var/newTrackIndex = curTrackIndex == 1 ? tracks.len : curTrackIndex - 1
- current_track = tracks[newTrackIndex]
- if(playing)
- start_stop_song()
- updateDialog()
+//
+// Media Player Jukebox
+// Rewritten by Leshana from existing Polaris code, merging in D2K5 and N3X15 work
+//
+
+#define JUKEMODE_NEXT 1 // Advance to next song in the track list
+#define JUKEMODE_RANDOM 2 // Not shuffle, randomly picks next each time.
+#define JUKEMODE_REPEAT_SONG 3 // Play the same song over and over
+#define JUKEMODE_PLAY_ONCE 4 // Play, then stop.
+
+/obj/machinery/media/jukebox/
+ name = "space jukebox"
+ icon = 'icons/obj/jukebox.dmi'
+ icon_state = "jukebox2-nopower"
+ var/state_base = "jukebox2"
+ anchored = 1
+ density = 1
+ power_channel = EQUIP
+ use_power = 1
+ idle_power_usage = 10
+ active_power_usage = 100
+ circuit = /obj/item/weapon/circuitboard/jukebox
+
+ // Vars for hacking
+ var/datum/wires/jukebox/wires = null
+ var/hacked = 0 // Whether to show the hidden songs or not
+ var/freq = 0 // Currently no effect, will return in phase II of mediamanager.
+ //VOREStation Add
+ var/loop_mode = JUKEMODE_PLAY_ONCE // Behavior when finished playing a song
+ var/max_queue_len = 3 // How many songs are we allowed to queue up?
+ var/list/queue = list()
+ //VOREStation Add End
+ var/current_genre = "Electronic" //What is our current genre?
+ var/list/genres = list("Electronic", "Rock", "Orchestral", "Folk", "Jazz", "Western") //Avaliable genres.
+ var/datum/track/current_track
+ var/list/datum/track/tracks = list(
+ new/datum/track("Beyond", 'sound/ambience/ambispace.ogg'),
+ new/datum/track("Clouds of Fire", 'sound/music/clouds.s3m'),
+ new/datum/track("D`Bert", 'sound/music/title2.ogg'),
+ new/datum/track("D`Fort", 'sound/ambience/song_game.ogg'),
+ new/datum/track("Floating", 'sound/music/main.ogg'),
+ new/datum/track("Endless Space", 'sound/music/space.ogg'),
+ new/datum/track("Part A", 'sound/misc/TestLoop1.ogg'),
+ new/datum/track("Scratch", 'sound/music/title1.ogg'),
+ new/datum/track("Trai`Tor", 'sound/music/traitor.ogg'),
+ new/datum/track("Stellar Transit", 'sound/ambience/space/space_serithi.ogg'),
+ )
+
+ // Only visible if hacked
+ var/list/datum/track/secret_tracks = list(
+ new/datum/track("Clown", 'sound/music/clown.ogg'),
+ new/datum/track("Space Asshole", 'sound/music/space_asshole.ogg'),
+ new/datum/track("Thunderdome", 'sound/music/THUNDERDOME.ogg'),
+ new/datum/track("Russkiy rep Diskoteka", 'sound/music/russianrapdisco.ogg')
+ )
+
+/obj/machinery/media/jukebox/New()
+ ..()
+ default_apply_parts()
+ wires = new/datum/wires/jukebox(src)
+ update_icon()
+
+/obj/machinery/media/jukebox/Destroy()
+ qdel(wires)
+ wires = null
+ ..()
+
+// On initialization, copy our tracks from the global list
+/obj/machinery/media/jukebox/initialize()
+ . = ..()
+ if(LAZYLEN(all_jukebox_tracks)) //Global list has tracks
+ tracks.Cut()
+ secret_tracks.Cut()
+ for(var/datum/track/T in all_jukebox_tracks) //Load them
+ if(!T.jukebox)
+ continue
+ if(T.secret)
+ secret_tracks |= T
+ else
+ tracks |= T
+ else if(!LAZYLEN(tracks)) //We don't even have default tracks
+ stat |= BROKEN // No tracks configured this round!
+
+/obj/machinery/media/jukebox/process()
+ if(!playing)
+ return
+ if(inoperable())
+ disconnect_media_source()
+ playing = 0
+ return
+ // If the current track isn't finished playing, let it keep going
+ if(current_track && world.time < media_start_time + current_track.duration)
+ return
+ // Otherwise time to pick a new one!
+ if(queue.len > 0)
+ current_track = queue[1]
+ queue.Cut(1, 2) // Remove the item we just took off the list
+ else
+ // Oh... nothing in queue? Well then pick next according to our rules
+ switch(loop_mode)
+ if(JUKEMODE_NEXT)
+ var/curTrackIndex = max(1, tracks.Find(current_track))
+ var/newTrackIndex = (curTrackIndex % tracks.len) + 1 // Loop back around if past end
+ current_track = tracks[newTrackIndex]
+ if(JUKEMODE_RANDOM)
+ var/previous_track = current_track
+ do
+ current_track = pick(tracks)
+ while(current_track == previous_track && tracks.len > 1)
+ if(JUKEMODE_REPEAT_SONG)
+ current_track = current_track
+ if(JUKEMODE_PLAY_ONCE)
+ current_track = null
+ playing = 0
+ update_icon()
+ updateDialog()
+ start_stop_song()
+
+// Tells the media manager to start or stop playing based on current settings.
+/obj/machinery/media/jukebox/proc/start_stop_song()
+ if(current_track && playing)
+ media_url = current_track.url
+ media_start_time = world.time
+ visible_message("\The [src] begins to play [current_track.display()].")
+ else
+ media_url = ""
+ media_start_time = 0
+ update_music()
+
+/obj/machinery/media/jukebox/proc/set_hacked(var/newhacked)
+ if (hacked == newhacked) return
+ hacked = newhacked
+ if (hacked)
+ tracks.Add(secret_tracks)
+ else
+ tracks.Remove(secret_tracks)
+ updateDialog()
+
+/obj/machinery/media/jukebox/attackby(obj/item/W as obj, mob/user as mob)
+ src.add_fingerprint(user)
+
+ if(default_deconstruction_screwdriver(user, W))
+ return
+ if(default_deconstruction_crowbar(user, W))
+ return
+ if(W.is_wirecutter())
+ return wires.Interact(user)
+ if(istype(W, /obj/item/device/multitool))
+ return wires.Interact(user)
+ if(W.is_wrench())
+ if(playing)
+ StopPlaying()
+ user.visible_message("[user] has [anchored ? "un" : ""]secured \the [src].", "You [anchored ? "un" : ""]secure \the [src].")
+ anchored = !anchored
+ playsound(src, W.usesound, 50, 1)
+ power_change()
+ update_icon()
+ if(!anchored)
+ playing = 0
+ disconnect_media_source()
+ else
+ update_media_source()
+ return
+ return ..()
+
+/obj/machinery/media/jukebox/power_change()
+ if(!powered(power_channel) || !anchored)
+ stat |= NOPOWER
+ else
+ stat &= ~NOPOWER
+
+ if(stat & (NOPOWER|BROKEN) && playing)
+ StopPlaying()
+ update_icon()
+
+/obj/machinery/media/jukebox/update_icon()
+ overlays.Cut()
+ if(stat & (NOPOWER|BROKEN) || !anchored)
+ if(stat & BROKEN)
+ icon_state = "[state_base]-broken"
+ else
+ icon_state = "[state_base]-nopower"
+ return
+ icon_state = state_base
+ if(playing)
+ if(emagged)
+ overlays += "[state_base]-emagged"
+ else
+ overlays += "[state_base]-running"
+ if (panel_open)
+ overlays += "panel_open"
+
+/obj/machinery/media/jukebox/Topic(href, href_list)
+ if(..() || !(Adjacent(usr) || istype(usr, /mob/living/silicon)))
+ return
+
+ if(!anchored)
+ to_chat(usr, "You must secure \the [src] first.")
+ return
+
+ if(inoperable())
+ to_chat(usr, "\The [src] doesn't appear to function.")
+ return
+
+ if(href_list["change_track"])
+ var/datum/track/T = locate(href_list["change_track"]) in tracks
+ if(istype(T))
+ current_track = T
+ StartPlaying()
+ else if(href_list["change_genre"])
+ var/new_genre = input("Choose Genre", "Genre Selection") in genres
+ current_genre = new_genre
+ else if(href_list["loopmode"])
+ var/newval = text2num(href_list["loopmode"])
+ loop_mode = sanitize_inlist(newval, list(JUKEMODE_NEXT, JUKEMODE_RANDOM, JUKEMODE_REPEAT_SONG, JUKEMODE_PLAY_ONCE), loop_mode)
+ else if(href_list["volume"])
+ var/newval = input("Choose Jukebox volume (0-100%)", "Jukebox volume", round(volume * 100.0))
+ newval = sanitize_integer(text2num(newval), min = 0, max = 100, default = volume * 100.0)
+ volume = newval / 100.0
+ update_music() // To broadcast volume change without restarting song
+ else if(href_list["stop"])
+ StopPlaying()
+ else if(href_list["play"])
+ if(emagged)
+ playsound(src.loc, 'sound/items/AirHorn.ogg', 100, 1)
+ for(var/mob/living/carbon/M in ohearers(6, src))
+ if(M.get_ear_protection() >= 2)
+ continue
+ M.sleeping = 0
+ M.stuttering += 20
+ M.ear_deaf += 30
+ M.Weaken(3)
+ if(prob(30))
+ M.Stun(10)
+ M.Paralyse(4)
+ else
+ M.make_jittery(500)
+ spawn(15)
+ explode()
+ else if(current_track == null)
+ to_chat(usr, "No track selected.")
+ else
+ StartPlaying()
+
+ return 1
+
+/obj/machinery/media/jukebox/interact(mob/user)
+ if(inoperable())
+ to_chat(usr, "\The [src] doesn't appear to function.")
+ return
+ ui_interact(user)
+
+/obj/machinery/media/jukebox/ui_interact(mob/user, ui_key = "jukebox", var/datum/nanoui/ui = null, var/force_open = 1)
+ var/title = "RetroBox - Space Style"
+ var/data[0]
+
+ if(operable())
+ data["playing"] = playing
+ data["hacked"] = hacked
+ data["max_queue_len"] = max_queue_len
+ data["media_start_time"] = media_start_time
+ data["loop_mode"] = loop_mode
+ data["volume"] = volume
+ if(current_track)
+ data["current_track_ref"] = "\ref[current_track]" // Convenient shortcut
+ data["current_track"] = current_track.toNanoList()
+ data["percent"] = playing ? min(100, round(world.time - media_start_time) / current_track.duration) : 0;
+
+ data["current_genre"] = current_genre
+
+ var/list/nano_tracks = new
+ for(var/datum/track/T in tracks)
+ if(T.genre != current_genre)
+ continue
+ nano_tracks[++nano_tracks.len] = T.toNanoList()
+ data["tracks"] = nano_tracks
+
+ // update the ui if it exists, returns null if no ui is passed/found
+ ui = GLOB.nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
+ if (!ui)
+ ui = new(user, src, ui_key, "jukebox.tmpl", title, 450, 600)
+ ui.set_initial_data(data)
+ ui.open()
+ ui.set_auto_update(playing)
+
+/obj/machinery/media/jukebox/attack_ai(mob/user as mob)
+ return src.attack_hand(user)
+
+/obj/machinery/media/jukebox/attack_hand(var/mob/user as mob)
+ interact(user)
+
+/obj/machinery/media/jukebox/proc/explode()
+ walk_to(src,0)
+ src.visible_message("\the [src] blows apart!", 1)
+
+ explosion(src.loc, 0, 0, 1, rand(1,2), 1)
+
+ var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
+ s.set_up(3, 1, src)
+ s.start()
+
+ new /obj/effect/decal/cleanable/blood/oil(src.loc)
+ qdel(src)
+
+/obj/machinery/media/jukebox/attackby(obj/item/W as obj, mob/user as mob)
+ src.add_fingerprint(user)
+
+ if(default_deconstruction_screwdriver(user, W))
+ return
+ if(default_deconstruction_crowbar(user, W))
+ return
+ if(W.is_wrench())
+ if(playing)
+ StopPlaying()
+ user.visible_message("[user] has [anchored ? "un" : ""]secured \the [src].", "You [anchored ? "un" : ""]secure \the [src].")
+ anchored = !anchored
+ playsound(src, W.usesound, 50, 1)
+ power_change()
+ update_icon()
+ return
+ return ..()
+
+/obj/machinery/media/jukebox/emag_act(var/remaining_charges, var/mob/user)
+ if(!emagged)
+ emagged = 1
+ StopPlaying()
+ visible_message("\The [src] makes a fizzling sound.")
+ update_icon()
+ return 1
+
+/obj/machinery/media/jukebox/proc/StopPlaying()
+ playing = 0
+ update_use_power(1)
+ update_icon()
+ start_stop_song()
+
+/obj/machinery/media/jukebox/proc/StartPlaying()
+ if(!current_track)
+ return
+ playing = 1
+ update_use_power(2)
+ update_icon()
+ start_stop_song()
+ updateDialog()
+
+// Advance to the next track - Don't start playing it unless we were already playing
+/obj/machinery/media/jukebox/proc/NextTrack()
+ if(!tracks.len) return
+ var/curTrackIndex = max(1, tracks.Find(current_track))
+ var/newTrackIndex = (curTrackIndex % tracks.len) + 1 // Loop back around if past end
+ current_track = tracks[newTrackIndex]
+ if(playing)
+ start_stop_song()
+ updateDialog()
+
+// Advance to the next track - Don't start playing it unless we were already playing
+/obj/machinery/media/jukebox/proc/PrevTrack()
+ if(!tracks.len) return
+ var/curTrackIndex = max(1, tracks.Find(current_track))
+ var/newTrackIndex = curTrackIndex == 1 ? tracks.len : curTrackIndex - 1
+ current_track = tracks[newTrackIndex]
+ if(playing)
+ start_stop_song()
+ updateDialog()
diff --git a/code/modules/media/media_tracks.dm b/code/modules/media/media_tracks.dm
index 708e2a811ef..84eba4cad5d 100644
--- a/code/modules/media/media_tracks.dm
+++ b/code/modules/media/media_tracks.dm
@@ -1,63 +1,72 @@
-//
-// Load the list of available music tracks for the jukebox (or other things that use music)
-//
-
-// Music track available for playing in a media machine.
-/datum/track
- var/url // URL to load song from
- var/title // Song title
- var/artist // Song's creator
- var/duration // Song length in deciseconds
- var/secret // Show up in regular playlist or secret playlist?
- var/lobby // Be one of the choices for lobby music?
-
-/datum/track/New(var/url, var/title, var/duration, var/artist = "", var/secret = 0, var/lobby = 0)
- src.url = url
- src.title = title
- src.artist = artist
- src.duration = duration
- src.secret = secret
- src.lobby = lobby
-
-/datum/track/proc/display()
- var str = "\"[title]\""
- if(artist)
- str += " by [artist]"
- return str
-
-/datum/track/proc/toNanoList()
- return list("ref" = "\ref[src]", "title" = title, "artist" = artist, "duration" = duration)
-
-
-// Global list holding all configured jukebox tracks
-var/global/list/all_jukebox_tracks = list()
-var/global/list/all_lobby_tracks = list()
-
-// Read the jukebox configuration file on system startup.
-/hook/startup/proc/load_jukebox_tracks()
- var/jukebox_track_file = "config/jukebox.json"
- if(!fexists(jukebox_track_file))
- warning("File not found: [jukebox_track_file]")
- return
- var/list/jsonData = json_decode(file2text(jukebox_track_file))
- if(!istype(jsonData))
- warning("Failed to read tracks from [jukebox_track_file], json_decode failed.")
- for(var/entry in jsonData)
- if(!istext(entry["url"]))
- warning("[jukebox_track_file] entry [entry]: bad or missing 'url'")
- continue
- if(!istext(entry["title"]))
- warning("[jukebox_track_file] entry [entry]: bad or missingg 'title'")
- continue
- if(!isnum(entry["duration"]))
- warning("[jukebox_track_file] entry [entry]: bad or missing 'duration'")
- continue
- var/datum/track/T = new(entry["url"], entry["title"], entry["duration"])
- if(istext(entry["artist"]))
- T.artist = entry["artist"]
- T.secret = entry["secret"] ? 1 : 0
- T.lobby = entry["lobby"] ? 1 : 0
- all_jukebox_tracks += T
- if(T.lobby)
- all_lobby_tracks += T
- return 1
+//
+// Load the list of available music tracks for the jukebox (or other things that use music)
+//
+
+// Music track available for playing in a media machine.
+/datum/track
+ var/url // URL to load song from
+ var/title // Song title
+ var/artist // Song's creator
+ var/duration // Song length in deciseconds
+ var/secret // Show up in regular playlist or secret playlist?
+ var/lobby // Be one of the choices for lobby music?
+ var/jukebox // Does it even show up in the jukebox?
+ var/genre // What is the genre of the song?
+
+/datum/track/New(var/url, var/title, var/duration, var/artist = "", var/secret = 0, var/lobby = 0, var/jukebox = 0, var/genre = "")
+ src.url = url
+ src.title = title
+ src.artist = artist
+ src.duration = duration
+ src.secret = secret
+ src.lobby = lobby
+ src.jukebox = jukebox
+ src.genre = genre
+
+/datum/track/proc/display()
+ var str = "\"[title]\""
+ if(artist)
+ str += " by [artist]"
+ return str
+
+/datum/track/proc/toNanoList()
+ return list("ref" = "\ref[src]", "title" = title, "artist" = artist, "duration" = duration)
+
+
+// Global list holding all configured jukebox tracks
+var/global/list/all_jukebox_tracks = list()
+var/global/list/all_lobby_tracks = list()
+
+// Read the jukebox configuration file on system startup.
+/hook/startup/proc/load_jukebox_tracks()
+ var/jukebox_track_file = "config/jukebox.json"
+ if(!fexists(jukebox_track_file))
+ warning("File not found: [jukebox_track_file]")
+ return
+ var/list/jsonData = json_decode(file2text(jukebox_track_file))
+ if(!istype(jsonData))
+ warning("Failed to read tracks from [jukebox_track_file], json_decode failed.")
+ for(var/entry in jsonData)
+ if(!istext(entry["url"]))
+ warning("[jukebox_track_file] entry [entry]: bad or missing 'url'")
+ continue
+ if(!istext(entry["title"]))
+ warning("[jukebox_track_file] entry [entry]: bad or missingg 'title'")
+ continue
+ if(!isnum(entry["duration"]))
+ warning("[jukebox_track_file] entry [entry]: bad or missing 'duration'")
+ continue
+ var/datum/track/T = new(entry["url"], entry["title"], entry["duration"])
+ if(istext(entry["artist"]))
+ T.artist = entry["artist"]
+ if(istext(entry["genre"]))
+ T.genre = entry["genre"]
+ T.secret = entry["secret"] ? 1 : 0
+ T.lobby = entry["lobby"] ? 1 : 0
+ T.jukebox = entry["jukebox"] ? 1 : 0
+ if(istext(entry["genre"]))
+ T.genre = entry["genre"]
+ all_jukebox_tracks += T
+ if(T.lobby)
+ all_lobby_tracks += T
+ return 1
diff --git a/jukebox.json b/jukebox.json
new file mode 100644
index 00000000000..4b0653274ed
--- /dev/null
+++ b/jukebox.json
@@ -0,0 +1,50 @@
+[
+{
+"url": "https://s.put.re/bkZYkYwX.mp3",
+"title": "Flip-Flap (Title One)",
+"duration": 1500,
+"artist": "X-CEED",
+"secret": false,
+"lobby": true
+},
+{
+"url": "https://s.put.re/EzLP21Mp.mp3",
+"title": "Robocop Theme (Title Two)",
+"duration": 1180,
+"artist": "Cboyardee.",
+"secret": false,
+"lobby": true
+},
+{
+"url": "https://s.put.re/uDpZrL1L.mp3",
+"title": "Tin Tin on the Moon (Remix)",
+"duration": 2320,
+"artist": "Jeroen Tel (Remixed by Cuboos)",
+"secret": false,
+"lobby": true
+},
+{
+"url": "https://s.put.re/nNXTd9ko.mp3",
+"title": "Phoron Will Make Us Rich",
+"duration": 1370,
+"artist": "EarthCrusher",
+"secret": false,
+"lobby": true
+},
+{
+"url": "https://s.put.re/vrN9ATH7.mp3",
+"title": "Spaceman's Dilemmia",
+"duration": 2230,
+"artist": "Carmen Miranda",
+"secret": false,
+"lobby": true
+},
+{
+"url": "https://s.put.re/QgemNJPJ.mp3",
+"title": "Banned from Argo",
+"duration": 3200,
+"artist": "Leslie Fish",
+"secret": false,
+"lobby": true
+}
+]
\ No newline at end of file
diff --git a/nano/templates/jukebox.tmpl b/nano/templates/jukebox.tmpl
index 77edb9ed419..ddbc1909a56 100644
--- a/nano/templates/jukebox.tmpl
+++ b/nano/templates/jukebox.tmpl
@@ -39,6 +39,11 @@ Used In File(s): \code\game\machinery\jukebox.dm
+Current Genre: {{:data.current_genre}}
+
+ {{:helper.link('Change Genre' , 'play', {'change_genre' : 1}, data.genre == 1 ? 'disabled' : null, null)}}
+
+
Available tracks:
{{for data.tracks}}
From 455d91ed11901571ef9b02869b42122e4df942af Mon Sep 17 00:00:00 2001
From: Andromeda-K22
Date: Sat, 24 Aug 2019 01:57:37 -0400
Subject: [PATCH 16/18] this isn't right.
---
jukebox.json | 50 --------------------------------------------------
1 file changed, 50 deletions(-)
delete mode 100644 jukebox.json
diff --git a/jukebox.json b/jukebox.json
deleted file mode 100644
index 4b0653274ed..00000000000
--- a/jukebox.json
+++ /dev/null
@@ -1,50 +0,0 @@
-[
-{
-"url": "https://s.put.re/bkZYkYwX.mp3",
-"title": "Flip-Flap (Title One)",
-"duration": 1500,
-"artist": "X-CEED",
-"secret": false,
-"lobby": true
-},
-{
-"url": "https://s.put.re/EzLP21Mp.mp3",
-"title": "Robocop Theme (Title Two)",
-"duration": 1180,
-"artist": "Cboyardee.",
-"secret": false,
-"lobby": true
-},
-{
-"url": "https://s.put.re/uDpZrL1L.mp3",
-"title": "Tin Tin on the Moon (Remix)",
-"duration": 2320,
-"artist": "Jeroen Tel (Remixed by Cuboos)",
-"secret": false,
-"lobby": true
-},
-{
-"url": "https://s.put.re/nNXTd9ko.mp3",
-"title": "Phoron Will Make Us Rich",
-"duration": 1370,
-"artist": "EarthCrusher",
-"secret": false,
-"lobby": true
-},
-{
-"url": "https://s.put.re/vrN9ATH7.mp3",
-"title": "Spaceman's Dilemmia",
-"duration": 2230,
-"artist": "Carmen Miranda",
-"secret": false,
-"lobby": true
-},
-{
-"url": "https://s.put.re/QgemNJPJ.mp3",
-"title": "Banned from Argo",
-"duration": 3200,
-"artist": "Leslie Fish",
-"secret": false,
-"lobby": true
-}
-]
\ No newline at end of file
From 0a885609b96783a9a46e234515772488f2949c87 Mon Sep 17 00:00:00 2001
From: Andromeda-K22
Date: Sat, 24 Aug 2019 01:58:46 -0400
Subject: [PATCH 17/18] config update
---
config/example/jukebox.txt | 93 ++++++++++++++++++++++++++------------
1 file changed, 63 insertions(+), 30 deletions(-)
diff --git a/config/example/jukebox.txt b/config/example/jukebox.txt
index 08ae86b3988..291181c209d 100644
--- a/config/example/jukebox.txt
+++ b/config/example/jukebox.txt
@@ -1,30 +1,63 @@
-#Jukebox configuration file
-#Sound file | Song Title | <0 = normal or 1 = secret>
-
-# Normal songs
-sound/music/jukebox/SongAboutHares.ogg | A Song About Hares | 0
-sound/music/jukebox/BelowTheAsteroids.ogg | Below The Asteroids | 0
-sound/ambience/ambispace.ogg | Beyond | 0
-sound/music/clouds.s3m | Clouds of Fire | 0
-sound/music/title2.ogg | D`Bert | 0
-sound/ambience/song_game.ogg | D`Fort | 0
-sound/music/jukebox/DuckTalesMoon.mid | Duck Tales - Moon | 0
-sound/music/space.ogg | Endless Space | 0
-sound/music/main.ogg | Floating | 0
-sound/music/jukebox/Fly_Me_To_The_Moon.ogg | Fly Me To The Moon | 0
-sound/music/jukebox/Cantina.ogg | Mad About Me | 0
-sound/music/jukebox/MinorTurbulenceFull.ogg | Minor Turbulence | 0
-sound/music/jukebox/OdeToGreed.ogg | Ode to Greed | 0
-sound/misc/TestLoop1.ogg | Part A | 0
-sound/music/jukebox/Ransacked.ogg | Ransacked | 0
-sound/music/jukebox/russianrapdisco.ogg | Russkiy rep Diskoteka | 0
-sound/music/title1.ogg | Scratch | 0
-sound/music/space_oddity.ogg | Space Oddity | 0
-sound/music/THUNDERDOME.ogg | Thunderdome | 0
-sound/music/traitor.ogg | Trai`Tor | 0
-sound/music/jukebox/WelcomeToJurassicPark.mid | Welcome To Jurassic Park | 0
-
-# Secret songs!
-sound/music/jukebox/bandit_radio.ogg | Bandit Radio | 1
-sound/music/space_asshole.ogg | Space Asshole | 1
-sound/music/THUNDERDOME.ogg | THUNDERDOME | 1
+##DELETE THIS LINE AND SAVE FILE AS jukebox.json##
+[
+{
+"url": "https://s.put.re/bkZYkYwX.mp3",
+"title": "Flip-Flap (Title One)",
+"duration": 1500,
+"artist": "X-CEED",
+"secret": false,
+"lobby": true,
+"jukebox": false,
+"genre": "Jazz"
+},
+{
+"url": "https://s.put.re/EzLP21Mp.mp3",
+"title": "Robocop Theme (Title Two)",
+"duration": 1180,
+"artist": "Cboyardee",
+"secret": false,
+"lobby": true,
+"jukebox": false,
+"genre": "Electronic"
+},
+{
+"url": "https://s.put.re/uDpZrL1L.mp3",
+"title": "Tin Tin on the Moon (Remix)",
+"duration": 2320,
+"artist": "Jeroen Tel (Remixed by Cuboos)",
+"secret": false,
+"lobby": true,
+"jukebox": true,
+"genre": "Electronic"
+},
+{
+"url": "https://s.put.re/nNXTd9ko.mp3",
+"title": "Phoron Will Make Us Rich",
+"duration": 1370,
+"artist": "Earthcrusher",
+"secret": false,
+"lobby": true,
+"jukebox": true,
+"genre": "Electronic"
+},
+{
+"url": "https://s.put.re/vrN9ATH7.mp3",
+"title": "Spaceman's Dilemmia",
+"duration": 2230,
+"artist": "Carmen Miranda",
+"secret": false,
+"lobby": true,
+"jukebox": true,
+"genre": "Folk"
+},
+{
+"url": "https://s.put.re/QgemNJPJ.mp3",
+"title": "Banned from Argo",
+"duration": 3200,
+"artist": "Leslie Fish",
+"secret": false,
+"lobby": true,
+"jukebox": true,
+"genre": "Folk"
+}
+]
From b87e2722adf04eabff2aa157328599f52b493328 Mon Sep 17 00:00:00 2001
From: Andromeda-K22
Date: Sat, 24 Aug 2019 02:01:36 -0400
Subject: [PATCH 18/18] Attempt at including proper json file
---
config/example/jukebox.json | 62 +++++++++++++++++++++++++++++++++++++
1 file changed, 62 insertions(+)
create mode 100644 config/example/jukebox.json
diff --git a/config/example/jukebox.json b/config/example/jukebox.json
new file mode 100644
index 00000000000..5845b2b1078
--- /dev/null
+++ b/config/example/jukebox.json
@@ -0,0 +1,62 @@
+[
+{
+"url": "https://s.put.re/bkZYkYwX.mp3",
+"title": "Flip-Flap (Title One)",
+"duration": 1500,
+"artist": "X-CEED",
+"secret": false,
+"lobby": true,
+"jukebox": false,
+"genre": "Jazz"
+},
+{
+"url": "https://s.put.re/EzLP21Mp.mp3",
+"title": "Robocop Theme (Title Two)",
+"duration": 1180,
+"artist": "Cboyardee",
+"secret": false,
+"lobby": true,
+"jukebox": false,
+"genre": "Electronic"
+},
+{
+"url": "https://s.put.re/uDpZrL1L.mp3",
+"title": "Tin Tin on the Moon (Remix)",
+"duration": 2320,
+"artist": "Jeroen Tel (Remixed by Cuboos)",
+"secret": false,
+"lobby": true,
+"jukebox": true,
+"genre": "Electronic"
+},
+{
+"url": "https://s.put.re/nNXTd9ko.mp3",
+"title": "Phoron Will Make Us Rich",
+"duration": 1370,
+"artist": "Earthcrusher",
+"secret": false,
+"lobby": true,
+"jukebox": true,
+"genre": "Electronic"
+},
+{
+"url": "https://s.put.re/vrN9ATH7.mp3",
+"title": "Spaceman's Dilemmia",
+"duration": 2230,
+"artist": "Carmen Miranda",
+"secret": false,
+"lobby": true,
+"jukebox": true,
+"genre": "Folk"
+},
+{
+"url": "https://s.put.re/QgemNJPJ.mp3",
+"title": "Banned from Argo",
+"duration": 3200,
+"artist": "Leslie Fish",
+"secret": false,
+"lobby": true,
+"jukebox": true,
+"genre": "Folk"
+}
+]
\ No newline at end of file