mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2026-08-27 23:29:10 +01:00
Merge pull request #2110 from CHOMPStationBot/upstream-merge-10521
[MIRROR] Media tracks subsystem, jukebox UI genres
This commit is contained in:
@@ -55,6 +55,7 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G
|
||||
#define INIT_ORDER_WEBHOOKS 50
|
||||
#define INIT_ORDER_DBCORE 41 //CHOMPEdit
|
||||
#define INIT_ORDER_SQLITE 40
|
||||
#define INIT_ORDER_MEDIA_TRACKS 38 // Gotta get that lobby music up, yo
|
||||
#define INIT_ORDER_CHEMISTRY 35
|
||||
#define INIT_ORDER_SKYBOX 30
|
||||
#define INIT_ORDER_MAPPING 25
|
||||
|
||||
@@ -84,3 +84,7 @@
|
||||
|
||||
/proc/cmp_text_asc(a,b)
|
||||
return sorttext(b,a)
|
||||
|
||||
/proc/cmp_media_track_asc(datum/track/A, datum/track/B)
|
||||
var/genre_sort = sorttext(B.genre || "Uncategorized", A.genre || "Uncategorized")
|
||||
return genre_sort || sorttext(B.title, A.title)
|
||||
|
||||
@@ -294,6 +294,9 @@ var/list/gamemode_cache = list()
|
||||
var/static/vgs_server_port = null // VOREStation Edit - VGS
|
||||
|
||||
var/disable_webhook_embeds = FALSE
|
||||
|
||||
|
||||
var/static/list/jukebox_track_files
|
||||
|
||||
/datum/configuration/New()
|
||||
var/list/L = typesof(/datum/game_mode) - /datum/game_mode
|
||||
@@ -946,6 +949,9 @@ var/list/gamemode_cache = list()
|
||||
|
||||
if("enable_night_shifts")
|
||||
config.enable_night_shifts = TRUE
|
||||
|
||||
if("jukebox_track_files")
|
||||
config.jukebox_track_files = splittext(value, ";")
|
||||
|
||||
// VOREStation Edit Start - Can't be in _vr file because it is loaded too late.
|
||||
if("vgs_access_identifier")
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
SUBSYSTEM_DEF(media_tracks)
|
||||
name = "Media Tracks"
|
||||
flags = SS_NO_FIRE
|
||||
init_order = INIT_ORDER_MEDIA_TRACKS
|
||||
|
||||
/// Every track, including secret
|
||||
var/list/all_tracks = list()
|
||||
/// Non-secret jukebox tracks
|
||||
var/list/jukebox_tracks = list()
|
||||
/// Lobby music tracks
|
||||
var/list/lobby_tracks = list()
|
||||
|
||||
/datum/controller/subsystem/media_tracks/Initialize(timeofday)
|
||||
load_tracks()
|
||||
sort_tracks()
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/media_tracks/proc/load_tracks()
|
||||
for(var/filename in config.jukebox_track_files)
|
||||
report_progress("Loading jukebox track: [filename]")
|
||||
|
||||
if(!fexists(filename))
|
||||
error("File not found: [filename]")
|
||||
continue
|
||||
|
||||
var/list/jsonData = json_decode(file2text(filename))
|
||||
|
||||
if(!istype(jsonData))
|
||||
error("Failed to read tracks from [filename], json_decode failed.")
|
||||
continue
|
||||
|
||||
for(var/entry in jsonData)
|
||||
|
||||
// Critical problems that will prevent the track from working
|
||||
if(!istext(entry["url"]))
|
||||
error("Jukebox entry in [filename]: bad or missing 'url'. Tracks must have a URL.")
|
||||
continue
|
||||
if(!istext(entry["title"]))
|
||||
error("Jukebox entry in [filename]: bad or missing 'title'. Tracks must have a title.")
|
||||
continue
|
||||
if(!isnum(entry["duration"]))
|
||||
error("Jukebox entry in [filename]: bad or missing 'duration'. Tracks must have a duration (in deciseconds).")
|
||||
continue
|
||||
|
||||
// Noncritical problems, we can keep going anyway, but warn so it can be fixed
|
||||
if(!istext(entry["artist"]))
|
||||
warning("Jukebox entry in [filename], [entry["title"]]: bad or missing 'artist'. Please consider crediting the artist.")
|
||||
if(!istext(entry["genre"]))
|
||||
warning("Jukebox entry in [filename], [entry["title"]]: bad or missing 'genre'. Please consider adding a genre.")
|
||||
|
||||
var/datum/track/T = new(entry["url"], entry["title"], entry["duration"], entry["artist"], entry["genre"])
|
||||
|
||||
T.secret = entry["secret"] ? 1 : 0
|
||||
T.lobby = entry["lobby"] ? 1 : 0
|
||||
|
||||
all_tracks += T
|
||||
|
||||
/datum/controller/subsystem/media_tracks/proc/sort_tracks()
|
||||
report_progress("Sorting media tracks...")
|
||||
sortTim(all_tracks, /proc/cmp_media_track_asc)
|
||||
|
||||
jukebox_tracks.Cut()
|
||||
lobby_tracks.Cut()
|
||||
|
||||
for(var/datum/track/T in all_tracks)
|
||||
if(!T.secret)
|
||||
jukebox_tracks += T
|
||||
if(T.lobby)
|
||||
lobby_tracks += T
|
||||
|
||||
/datum/controller/subsystem/media_tracks/proc/manual_track_add()
|
||||
var/client/C = usr.client
|
||||
if(!check_rights(R_DEBUG|R_FUN))
|
||||
return
|
||||
|
||||
// Required
|
||||
var/url = input(C, "REQUIRED: Provide URL for track", "Track URL") as text|null
|
||||
if(!url)
|
||||
return
|
||||
|
||||
var/title = input(C, "REQUIRED: Provide title for track", "Track Title") as text|null
|
||||
if(!title)
|
||||
return
|
||||
|
||||
var/duration = input(C, "REQUIRED: Provide duration for track (in deciseconds, aka seconds*10)", "Track Duration") as num|null
|
||||
if(!duration)
|
||||
return
|
||||
|
||||
// Optional
|
||||
var/artist = input(C, "Optional: Provide artist for track", "Track Artist") as text|null
|
||||
if(isnull(artist)) // Cancel rather than empty string
|
||||
return
|
||||
|
||||
var/genre = input(C, "Optional: Provide genre for track (try to match an existing one)", "Track Genre") as text|null
|
||||
if(isnull(genre)) // Cancel rather than empty string
|
||||
return
|
||||
|
||||
var/secret = alert(C, "Optional: Mark track as secret?", "Track Secret", "Yes", "Cancel", "No")
|
||||
if(secret == "Cancel")
|
||||
return
|
||||
else if(secret == "Yes")
|
||||
secret = TRUE
|
||||
else
|
||||
secret = FALSE
|
||||
|
||||
var/lobby = alert(C, "Optional: Mark track as lobby music?", "Track Lobby", "Yes", "Cancel", "No")
|
||||
if(lobby == "Cancel")
|
||||
return
|
||||
else if(secret == "Yes")
|
||||
secret = TRUE
|
||||
else
|
||||
secret = FALSE
|
||||
|
||||
var/datum/track/T = new(url, title, duration, artist, genre)
|
||||
|
||||
T.secret = secret
|
||||
T.lobby = lobby
|
||||
|
||||
all_tracks += T
|
||||
|
||||
report_progress("New media track added by [C]: [title]")
|
||||
sort_tracks()
|
||||
|
||||
/datum/controller/subsystem/media_tracks/proc/manual_track_remove()
|
||||
var/client/C = usr.client
|
||||
if(!check_rights(R_DEBUG|R_FUN))
|
||||
return
|
||||
|
||||
var/track = input(C, "Input track title or URL to remove (must be exact)", "Remove Track") as text|null
|
||||
if(!track)
|
||||
return
|
||||
|
||||
for(var/datum/track/T in all_tracks)
|
||||
if(T.title == track || T.url == track)
|
||||
all_tracks -= T
|
||||
qdel(T)
|
||||
report_progress("Media track removed by [C]: [track]")
|
||||
sort_tracks()
|
||||
return
|
||||
|
||||
to_chat(C, "<span class='warning>Couldn't find a track matching the specified parameters.</span>")
|
||||
|
||||
/datum/controller/subsystem/media_tracks/vv_get_dropdown()
|
||||
. = ..()
|
||||
VV_DROPDOWN_OPTION("", "---")
|
||||
VV_DROPDOWN_OPTION("add_track", "Add New Track")
|
||||
VV_DROPDOWN_OPTION("remove_track", "Remove Track")
|
||||
|
||||
/datum/controller/subsystem/media_tracks/vv_do_topic(list/href_list)
|
||||
. = ..()
|
||||
IF_VV_OPTION("add_track")
|
||||
manual_track_add()
|
||||
href_list["datumrefresh"] = "\ref[src]"
|
||||
IF_VV_OPTION("remove_track")
|
||||
manual_track_remove()
|
||||
href_list["datumrefresh"] = "\ref[src]"
|
||||
+138
-45
@@ -8,7 +8,7 @@
|
||||
#define JUKEMODE_REPEAT_SONG 3 // Play the same song over and over
|
||||
#define JUKEMODE_PLAY_ONCE 4 // Play, then stop.
|
||||
|
||||
/obj/machinery/media/jukebox/
|
||||
/obj/machinery/media/jukebox
|
||||
name = "space jukebox"
|
||||
desc = "Filled with songs both past and present!"
|
||||
icon = 'icons/obj/jukebox.dmi'
|
||||
@@ -33,54 +33,22 @@
|
||||
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/Initialize()
|
||||
. = ..()
|
||||
default_apply_parts()
|
||||
wires = new/datum/wires/jukebox(src)
|
||||
update_icon()
|
||||
if(!LAZYLEN(getTracksList()))
|
||||
stat |= BROKEN
|
||||
|
||||
/obj/machinery/media/jukebox/Destroy()
|
||||
qdel(wires)
|
||||
wires = null
|
||||
return ..()
|
||||
|
||||
// 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
|
||||
if(T.casino) //CHOMPEDIT: preventing casion tracks from being added to other jukeboxes
|
||||
tracks -= T
|
||||
|
||||
else if(!LAZYLEN(tracks)) //We don't even have default tracks
|
||||
stat |= BROKEN // No tracks configured this round!
|
||||
/obj/machinery/media/jukebox/proc/getTracksList()
|
||||
return hacked ? SSmedia_tracks.all_tracks : SSmedia_tracks.jukebox_tracks
|
||||
|
||||
/obj/machinery/media/jukebox/process()
|
||||
if(!playing)
|
||||
@@ -98,6 +66,7 @@
|
||||
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
|
||||
var/list/tracks = getTracksList()
|
||||
switch(loop_mode)
|
||||
if(JUKEMODE_NEXT)
|
||||
var/curTrackIndex = max(1, tracks.Find(current_track))
|
||||
@@ -122,19 +91,16 @@
|
||||
if(current_track && playing)
|
||||
media_url = current_track.url
|
||||
media_start_time = world.time
|
||||
visible_message("<span class='notice'>\The [src] begins to play [current_track.display()].</span>")
|
||||
audible_message("<span class='notice'>\The [src] begins to play [current_track.display()].</span>", runemessage = "[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
|
||||
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)
|
||||
@@ -220,13 +186,15 @@
|
||||
data["volume"] = volume
|
||||
data["current_track_ref"] = null
|
||||
data["current_track"] = null
|
||||
data["current_genre"] = null
|
||||
if(current_track)
|
||||
data["current_track_ref"] = "\ref[current_track]" // Convenient shortcut
|
||||
data["current_track"] = current_track.toTguiList()
|
||||
data["current_genre"] = current_track.genre
|
||||
data["percent"] = playing ? min(100, round(world.time - media_start_time) / current_track.duration) : 0;
|
||||
|
||||
var/list/tgui_tracks = list()
|
||||
for(var/datum/track/T in tracks)
|
||||
for(var/datum/track/T in getTracksList())
|
||||
tgui_tracks.Add(list(T.toTguiList()))
|
||||
data["tracks"] = tgui_tracks
|
||||
|
||||
@@ -238,7 +206,7 @@
|
||||
|
||||
switch(action)
|
||||
if("change_track")
|
||||
var/datum/track/T = locate(params["change_track"]) in tracks
|
||||
var/datum/track/T = locate(params["change_track"]) in getTracksList()
|
||||
if(istype(T))
|
||||
current_track = T
|
||||
StartPlaying()
|
||||
@@ -339,6 +307,7 @@
|
||||
|
||||
// Advance to the next track - Don't start playing it unless we were already playing
|
||||
/obj/machinery/media/jukebox/proc/NextTrack()
|
||||
var/list/tracks = getTracksList()
|
||||
if(!tracks.len) return
|
||||
var/curTrackIndex = max(1, tracks.Find(current_track))
|
||||
var/newTrackIndex = (curTrackIndex % tracks.len) + 1 // Loop back around if past end
|
||||
@@ -349,6 +318,7 @@
|
||||
|
||||
// Advance to the next track - Don't start playing it unless we were already playing
|
||||
/obj/machinery/media/jukebox/proc/PrevTrack()
|
||||
var/list/tracks = getTracksList()
|
||||
if(!tracks.len) return
|
||||
var/curTrackIndex = max(1, tracks.Find(current_track))
|
||||
var/newTrackIndex = curTrackIndex == 1 ? tracks.len : curTrackIndex - 1
|
||||
@@ -356,3 +326,126 @@
|
||||
if(playing)
|
||||
start_stop_song()
|
||||
updateDialog()
|
||||
|
||||
// Ghostly jukebox for adminbuse
|
||||
/obj/machinery/media/jukebox/ghost
|
||||
name = "ghost jukebox"
|
||||
desc = "A jukebox from the nether-realms! Spooky."
|
||||
|
||||
plane = PLANE_GHOSTS
|
||||
invisibility = INVISIBILITY_OBSERVER
|
||||
alpha = 127
|
||||
|
||||
icon_state = "jukebox2-virtual"
|
||||
|
||||
density = FALSE
|
||||
hacked = TRUE
|
||||
|
||||
use_power = USE_POWER_OFF
|
||||
circuit = null
|
||||
|
||||
var/list/custom_tracks = list()
|
||||
|
||||
// Just junk to make it sneaky - I wish a lot more stuff was on /obj/machinery/media instead of /jukebox so I could use that.
|
||||
/obj/machinery/media/jukebox/ghost/is_incorporeal()
|
||||
return TRUE
|
||||
/obj/machinery/media/jukebox/ghost/audible_message(message, deaf_message, hearing_distance, radio_message, runemessage)
|
||||
return
|
||||
/obj/machinery/media/jukebox/ghost/visible_message(message, blind_message, list/exclude_mobs, range, runemessage)
|
||||
return
|
||||
/obj/machinery/media/jukebox/ghost/attackby(obj/item/W as obj, mob/user as mob)
|
||||
return
|
||||
/obj/machinery/media/jukebox/ghost/attack_ai(mob/user as mob)
|
||||
return
|
||||
/obj/machinery/media/jukebox/ghost/attack_hand(var/mob/user as mob)
|
||||
return
|
||||
/obj/machinery/media/jukebox/ghost/update_use_power(new_use_power)
|
||||
return
|
||||
/obj/machinery/media/jukebox/ghost/power_change()
|
||||
return
|
||||
/obj/machinery/media/jukebox/ghost/emp_act(severity)
|
||||
return
|
||||
/obj/machinery/media/jukebox/ghost/emag_act(remaining_charges, mob/user)
|
||||
return
|
||||
/obj/machinery/media/jukebox/ghost/explode()
|
||||
return
|
||||
/obj/machinery/media/jukebox/ghost/update_icon()
|
||||
if(playing)
|
||||
animate(src, alpha = 200, time = 5, loop = -1)
|
||||
else
|
||||
animate(src, alpha = initial(alpha), time = 10)
|
||||
// End junk
|
||||
|
||||
/obj/machinery/media/jukebox/ghost/attack_ghost(var/mob/observer/dead/M)
|
||||
if(!istype(M))
|
||||
return
|
||||
|
||||
if(check_rights(R_FUN|R_ADMIN, show_msg=0))
|
||||
interact(M)
|
||||
else if(current_track)
|
||||
to_chat(M, "\The [src] is playing [current_track.display()].")
|
||||
else
|
||||
to_chat(M, "\The [src] is not playing any music.")
|
||||
|
||||
/obj/machinery/media/jukebox/ghost/getTracksList()
|
||||
return (custom_tracks + ..())
|
||||
|
||||
/obj/machinery/media/jukebox/ghost/proc/manual_track_add()
|
||||
var/client/C = usr.client
|
||||
if(!check_rights(R_FUN|R_ADMIN))
|
||||
return
|
||||
|
||||
// Required
|
||||
var/url = input(C, "REQUIRED: Provide URL for track", "Track URL") as text|null
|
||||
if(!url)
|
||||
return
|
||||
|
||||
var/title = input(C, "REQUIRED: Provide title for track", "Track Title") as text|null
|
||||
if(!title)
|
||||
return
|
||||
|
||||
var/duration = input(C, "REQUIRED: Provide duration for track (in deciseconds, aka seconds*10)", "Track Duration") as num|null
|
||||
if(!duration)
|
||||
return
|
||||
|
||||
// Optional
|
||||
var/artist = input(C, "Optional: Provide artist for track", "Track Artist") as text|null
|
||||
if(isnull(artist)) // Cancel rather than empty string
|
||||
return
|
||||
|
||||
// So they're obvious and grouped
|
||||
var/genre = "! Admin Loaded !"
|
||||
|
||||
custom_tracks += new /datum/track(url, title, duration, artist, genre)
|
||||
|
||||
/obj/machinery/media/jukebox/ghost/proc/manual_track_remove()
|
||||
var/client/C = usr.client
|
||||
if(!check_rights(R_FUN|R_ADMIN))
|
||||
return
|
||||
|
||||
var/track = input(C, "Input track title or URL to remove (must be exact)", "Remove Track") as text|null
|
||||
if(!track)
|
||||
return
|
||||
|
||||
for(var/datum/track/T in custom_tracks)
|
||||
if(T.title == track || T.url == track)
|
||||
custom_tracks -= T
|
||||
qdel(T)
|
||||
return
|
||||
|
||||
to_chat(C, "<span class='warning>Couldn't find a track matching the specified parameters.</span>")
|
||||
|
||||
/obj/machinery/media/jukebox/ghost/vv_get_dropdown()
|
||||
. = ..()
|
||||
VV_DROPDOWN_OPTION("", "---")
|
||||
VV_DROPDOWN_OPTION("add_track", "Add New Track")
|
||||
VV_DROPDOWN_OPTION("remove_track", "Remove Track")
|
||||
|
||||
/obj/machinery/media/jukebox/ghost/vv_do_topic(list/href_list)
|
||||
. = ..()
|
||||
IF_VV_OPTION("add_track")
|
||||
manual_track_add()
|
||||
href_list["datumrefresh"] = "\ref[src]"
|
||||
IF_VV_OPTION("remove_track")
|
||||
manual_track_remove()
|
||||
href_list["datumrefresh"] = "\ref[src]"
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
icon_state = "casinojukebox-nopower"
|
||||
state_base = "casinojukebox"
|
||||
|
||||
/* Commenting out for now due to conflicts with upstream jukebox changes.
|
||||
// On initialization, copy our tracks from the global list
|
||||
/obj/machinery/media/jukebox/casinojukebox/Initialize()
|
||||
. = ..()
|
||||
@@ -18,4 +19,5 @@
|
||||
if(T.casino)
|
||||
tracks |= T
|
||||
else if(!LAZYLEN(tracks)) //We don't even have default tracks
|
||||
stat |= BROKEN // No tracks configured this round!
|
||||
stat |= BROKEN // No tracks configured this round!
|
||||
*/
|
||||
|
||||
+2
-2
@@ -121,9 +121,9 @@
|
||||
return rand(32000, 55000) //Frequency stuff only works with 45kbps oggs.
|
||||
|
||||
/client/proc/playtitlemusic()
|
||||
if(!ticker || !all_lobby_tracks.len || !media) return
|
||||
if(!ticker || !SSmedia_tracks.lobby_tracks.len || !media) return
|
||||
if(is_preference_enabled(/datum/client_preference/play_lobby_music))
|
||||
var/datum/track/T = pick(all_lobby_tracks)
|
||||
var/datum/track/T = pick(SSmedia_tracks.lobby_tracks)
|
||||
media.push_music(T.url, world.time, 0.85)
|
||||
to_chat(src,"<span class='notice'>Lobby music: <b>[T.title]</b> by <b>[T.artist]</b>.</span>")
|
||||
|
||||
|
||||
@@ -6,16 +6,18 @@
|
||||
/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/artist // Song's creator
|
||||
var/genre // Musical genre
|
||||
var/secret // Show up in regular playlist or secret playlist?
|
||||
var/lobby // Be one of the choices for lobby music?
|
||||
var/casino // CHOMP Music for casino jukebox - Jack
|
||||
|
||||
/datum/track/New(var/url, var/title, var/duration, var/artist = "", var/secret = 0, var/lobby = 0)
|
||||
/datum/track/New(var/url, var/title, var/duration, var/artist = "", var/genre = "", var/secret = 0, var/lobby = 0)
|
||||
src.url = url
|
||||
src.title = title
|
||||
src.artist = artist
|
||||
src.genre = genre
|
||||
src.duration = duration
|
||||
src.secret = secret
|
||||
src.lobby = lobby
|
||||
@@ -24,58 +26,8 @@
|
||||
/datum/track/proc/display()
|
||||
var str = "\"[title]\""
|
||||
if(artist)
|
||||
str += " by [artist]"
|
||||
str += " by [artist || "Unknown"]"
|
||||
return str
|
||||
|
||||
/datum/track/proc/toTguiList()
|
||||
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"
|
||||
var/jukebox_track_file_private = "config/jukebox_private.json" // Uncommitted
|
||||
|
||||
if(!fexists(jukebox_track_file))
|
||||
warning("File not found: [jukebox_track_file]")
|
||||
return 1
|
||||
|
||||
var/list/jsonData = json_decode(file2text(jukebox_track_file))
|
||||
|
||||
if(!istype(jsonData))
|
||||
warning("Failed to read tracks from [jukebox_track_file], json_decode failed.")
|
||||
return 1
|
||||
|
||||
// Optional
|
||||
if(fexists(jukebox_track_file_private))
|
||||
var/list/jsonData_private = json_decode(file2text(jukebox_track_file_private))
|
||||
if(istype(jsonData_private))
|
||||
jsonData += jsonData_private // Pack more tracks in
|
||||
else
|
||||
warning("Failed to read tracks from [jukebox_track_file_private], json_decode failed.")
|
||||
//return 1 // Not worth failing over, as this file is optional
|
||||
|
||||
for(var/entry in jsonData)
|
||||
if(!istext(entry["url"]))
|
||||
warning("Jukebox entry [entry]: bad or missing 'url'")
|
||||
continue
|
||||
if(!istext(entry["title"]))
|
||||
warning("Jukebox entry [entry]: bad or missing 'title'")
|
||||
continue
|
||||
if(!isnum(entry["duration"]))
|
||||
warning("Jukebox 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.casino = entry["casino"] ? 1 : 0 //CHOMP Casino music
|
||||
T.lobby = entry["lobby"] ? 1 : 0
|
||||
all_jukebox_tracks += T
|
||||
if(T.lobby)
|
||||
all_lobby_tracks += T
|
||||
return 1
|
||||
return list("ref" = "\ref[src]", "title" = title, "artist" = artist, "genre" = genre, "duration" = duration)
|
||||
|
||||
@@ -559,3 +559,13 @@ ENGINE_MAP Supermatter Engine,Edison's Bane
|
||||
# Controls how strictly the species whitelists on loadout entries are enforced
|
||||
# Possible values: 0 (Off), 1 (Lax, user must be whitelisted for the species), 2 (Strict, user must be the species)
|
||||
LOADOUT_WHITELIST 1
|
||||
|
||||
# Allowed time from death to defib in minutes
|
||||
DEFIB_TIMER 60
|
||||
|
||||
# Jukebox track files to load, JSON format. Keys include:
|
||||
# REQUIRED: url (str), title (str), duration (num, in DS)
|
||||
# SUGGESTED: artist (str), genre (str)
|
||||
# OPTIONAL: secret (bool), lobby (bool)
|
||||
#JUKEBOX_TRACK_FILES config/jukebox.json;config/jukebox_private.json
|
||||
JUKEBOX_TRACK_FILES config/jukebox.json
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 8.0 KiB |
@@ -17,6 +17,7 @@ export class Collapsible extends Component {
|
||||
const {
|
||||
children,
|
||||
color = 'default',
|
||||
child_mt = 1,
|
||||
title,
|
||||
buttons,
|
||||
...rest
|
||||
@@ -41,7 +42,7 @@ export class Collapsible extends Component {
|
||||
)}
|
||||
</div>
|
||||
{open && (
|
||||
<Box mt={1}>
|
||||
<Box mt={child_mt}>
|
||||
{children}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { round } from 'common/math';
|
||||
import { sortBy } from 'common/collections';
|
||||
import { useBackend } from "../backend";
|
||||
import { Box, Button, LabeledList, ProgressBar, Section, Slider } from "../components";
|
||||
import { Box, Button, Collapsible, LabeledList, ProgressBar, Section, Slider } from "../components";
|
||||
import { Window } from "../layouts";
|
||||
|
||||
export const Jukebox = (props, context) => {
|
||||
@@ -13,10 +12,22 @@ export const Jukebox = (props, context) => {
|
||||
volume,
|
||||
current_track_ref,
|
||||
current_track,
|
||||
current_genre,
|
||||
percent,
|
||||
tracks,
|
||||
} = data;
|
||||
|
||||
let genre_songs = tracks.length && tracks.reduce((acc, obj) => {
|
||||
let key = obj.genre || "Uncategorized";
|
||||
if (!acc[key]) {
|
||||
acc[key] = [];
|
||||
}
|
||||
acc[key].push(obj);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
let true_genre = playing && (current_genre || "Uncategorized");
|
||||
|
||||
return (
|
||||
<Window width={450} height={600} resizable>
|
||||
<Window.Content scrollable>
|
||||
@@ -96,15 +107,25 @@ export const Jukebox = (props, context) => {
|
||||
</LabeledList>
|
||||
</Section>
|
||||
<Section title="Available Tracks">
|
||||
{tracks.length && sortBy(a => a.title)(tracks).map(track => (
|
||||
<Button
|
||||
fluid
|
||||
icon="play"
|
||||
key={track.ref}
|
||||
selected={current_track_ref === track.ref}
|
||||
onClick={() => act("change_track", { change_track: track.ref })}>
|
||||
{track.title}
|
||||
</Button>
|
||||
{tracks.length && Object.keys(genre_songs).sort().map(genre => (
|
||||
<Collapsible
|
||||
title={genre}
|
||||
key={genre}
|
||||
color={true_genre === genre ? "green" : "default"}
|
||||
child_mt={0}>
|
||||
<div style={{ "margin-left": "1em" }}>
|
||||
{genre_songs[genre].map(track => (
|
||||
<Button
|
||||
fluid
|
||||
icon="play"
|
||||
key={track.ref}
|
||||
selected={current_track_ref === track.ref}
|
||||
onClick={() => act("change_track", { change_track: track.ref })}>
|
||||
{track.title}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</Collapsible>
|
||||
)) || (
|
||||
<Box color="bad">
|
||||
Error: No songs loaded.
|
||||
@@ -114,4 +135,4 @@ export const Jukebox = (props, context) => {
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -287,6 +287,7 @@
|
||||
#include "code\controllers\subsystems\lighting.dm"
|
||||
#include "code\controllers\subsystems\machines.dm"
|
||||
#include "code\controllers\subsystems\mapping.dm"
|
||||
#include "code\controllers\subsystems\media_tracks.dm"
|
||||
#include "code\controllers\subsystems\mobs.dm"
|
||||
#include "code\controllers\subsystems\nanoui.dm"
|
||||
#include "code\controllers\subsystems\nightshift.dm"
|
||||
|
||||
Reference in New Issue
Block a user