diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm
index f2d78f3438..384b857978 100644
--- a/code/__DEFINES/sound.dm
+++ b/code/__DEFINES/sound.dm
@@ -3,21 +3,22 @@
#define CHANNEL_ADMIN 1023
#define CHANNEL_VOX 1022
#define CHANNEL_JUKEBOX 1021
-#define CHANNEL_JUSTICAR_ARK 1020
-#define CHANNEL_HEARTBEAT 1019 //sound channel for heartbeats
-#define CHANNEL_AMBIENCE 1018
-#define CHANNEL_BUZZ 1017
-#define CHANNEL_BICYCLE 1016
+#define CHANNEL_JUKEBOX_START 1016
+#define CHANNEL_JUSTICAR_ARK 1015
+#define CHANNEL_HEARTBEAT 1014 //sound channel for heartbeats
+#define CHANNEL_AMBIENCE 1013
+#define CHANNEL_BUZZ 1012
+#define CHANNEL_BICYCLE 1011
//CIT CHANNELS - TRY NOT TO REGRESS
-#define CHANNEL_PRED 1015
-#define CHANNEL_DIGEST 1014
-#define CHANNEL_PREYLOOP 1013
+#define CHANNEL_PRED 1010
+#define CHANNEL_DIGEST 1009
+#define CHANNEL_PREYLOOP 1008
//THIS SHOULD ALWAYS BE THE LOWEST ONE!
//KEEP IT UPDATED
-#define CHANNEL_HIGHEST_AVAILABLE 1012 //CIT CHANGE - COMPENSATES FOR VORESOUND CHANNELS
+#define CHANNEL_HIGHEST_AVAILABLE 1008 //CIT CHANGE - COMPENSATES FOR VORESOUND CHANNELS
#define SOUND_MINIMUM_PRESSURE 10
diff --git a/code/controllers/subsystem/jukeboxes.dm b/code/controllers/subsystem/jukeboxes.dm
new file mode 100644
index 0000000000..ab510e1efd
--- /dev/null
+++ b/code/controllers/subsystem/jukeboxes.dm
@@ -0,0 +1,100 @@
+SUBSYSTEM_DEF(jukeboxes)
+ name = "Jukeboxes"
+ wait = 5
+ var/list/songs = list()
+ var/list/activejukeboxes = list()
+
+/datum/track
+ var/song_name = "generic"
+ var/song_path = null
+ var/song_length = 0
+ var/song_beat = 0
+ var/song_associated_id = null
+
+/datum/track/New(name, path, length, beat, assocID)
+ song_name = name
+ song_path = path
+ song_length = length
+ song_beat = beat
+ song_associated_id = assocID
+
+/datum/controller/subsystem/jukeboxes/proc/addjukebox(obj/jukebox, datum/track/T)
+ if(!istype(T))
+ CRASH("[src] tried to play a song with a nonexistant track")
+ var/channeltoreserve = CHANNEL_JUKEBOX_START + activejukeboxes.len - 1
+ if(channeltoreserve > CHANNEL_JUKEBOX)
+ return FALSE
+ activejukeboxes.len++
+ activejukeboxes[activejukeboxes.len] = list(T, channeltoreserve, jukebox)
+ return activejukeboxes.len
+
+/datum/controller/subsystem/jukeboxes/proc/removejukebox(IDtoremove)
+ if(islist(activejukeboxes[IDtoremove]))
+ for(var/mob/M in GLOB.player_list)
+ if(!M.client)
+ continue
+ M.stop_sound_channel(activejukeboxes[IDtoremove][2])
+ activejukeboxes.Cut(IDtoremove, IDtoremove+1)
+ return TRUE
+ else
+ to_chat(world, "If you see this, screenshot it and send it to a dev. Tried to remove jukebox with invalid ID")
+
+/datum/controller/subsystem/jukeboxes/proc/findjukeboxindex(obj/jukebox)
+ if(activejukeboxes.len)
+ for(var/list/jukeinfo in activejukeboxes)
+ if(jukebox in jukeinfo)
+ return activejukeboxes.Find(jukeinfo)
+ return FALSE
+
+/datum/controller/subsystem/jukeboxes/Initialize()
+ var/list/tracks = flist("config/jukebox_music/sounds/")
+ for(var/S in tracks)
+ var/datum/track/T = new()
+ T.song_path = file("config/jukebox_music/sounds/[S]")
+ var/list/L = splittext(S,"+")
+ T.song_name = L[1]
+ T.song_length = text2num(L[2])
+ T.song_beat = text2num(L[3])
+ T.song_associated_id = L[4]
+ songs |= T
+ return ..()
+
+/datum/controller/subsystem/jukeboxes/fire()
+ if(!activejukeboxes.len)
+ return
+ for(var/list/jukeinfo in activejukeboxes)
+ if(!jukeinfo.len)
+ to_chat(world, "If you see this, screenshot it and send it to a dev. Active jukebox without any associated metadata")
+ var/datum/track/juketrack = jukeinfo[1]
+ if(!istype(juketrack))
+ to_chat(world, "If you see this, screenshot it and send it to a dev. After jukebox track grabbing")
+ continue
+ var/obj/jukebox = jukeinfo[3]
+ if(!istype(jukebox))
+ to_chat(world, "If you see this, screenshot it and send it to a dev. Nonexistant or invalid jukebox in active jukebox list")
+ continue
+ var/sound/song_played = sound(juketrack.song_path)
+ var/area/currentarea = get_area(jukebox)
+ var/turf/currentturf = get_turf(jukebox)
+ var/list/hearerscache = hearers(7, jukebox)
+
+ for(var/mob/M in GLOB.player_list)
+ if(!M.client)
+ continue
+ if(!(M.client.prefs.toggles & SOUND_INSTRUMENTS))
+ M.stop_sound_channel(jukeinfo[2])
+ continue
+
+ var/inrange = FALSE
+ if(jukebox.z == M.z) //todo - expand this to work with mining planet z-levels when robust jukebox audio gets merged to master
+ song_played.status = SOUND_UPDATE
+ if(get_area(M) == currentarea)
+ inrange = TRUE
+ else if(M in hearerscache)
+ inrange = TRUE
+ else
+ song_played.status = SOUND_MUTE | SOUND_UPDATE //Setting volume = 0 doesn't let the sound properties update at all, which is lame.
+
+ M.playsound_local(currentturf, null, 100, channel = jukeinfo[2], S = song_played, envwet = (inrange ? -250 : 0), envdry = (inrange ? 0 : -10000))
+ CHECK_TICK
+ return
diff --git a/code/game/machinery/dance_machine.dm b/code/game/machinery/dance_machine.dm
index cfdf07acdd..f67600252e 100644
--- a/code/game/machinery/dance_machine.dm
+++ b/code/game/machinery/dance_machine.dm
@@ -9,7 +9,6 @@
var/active = FALSE
var/list/rangers = list()
var/stop = 0
- var/list/songs = list()
var/datum/track/selection = null
/obj/machinery/jukebox/disco
@@ -29,34 +28,6 @@
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
flags_1 = NODECONSTRUCT_1
-/datum/track
- var/song_name = "generic"
- var/song_path = null
- var/song_length = 0
- var/song_beat = 0
-
-/datum/track/New(name, path, length, beat)
- song_name = name
- song_path = path
- song_length = length
- song_beat = beat
-
-/obj/machinery/jukebox/Initialize()
- . = ..()
- var/list/tracks = flist("config/jukebox_music/sounds/")
-
- for(var/S in tracks)
- var/datum/track/T = new()
- T.song_path = file("config/jukebox_music/sounds/[S]")
- var/list/L = splittext(S,"+")
- T.song_name = L[1]
- T.song_length = text2num(L[2])
- T.song_beat = text2num(L[3])
- songs |= T
-
- if(songs.len)
- selection = pick(songs)
-
/obj/machinery/jukebox/Destroy()
dance_over()
return ..()
@@ -91,7 +62,7 @@
to_chat(user,"Error: Access Denied.")
user.playsound_local(src,'sound/misc/compiler-failure.ogg', 25, 1)
return
- if(!songs.len)
+ if(!SSjukeboxes.songs.len)
to_chat(user,"Error: No music tracks have been authorized for your station. Petition Central Command to resolve this issue.")
playsound(src,'sound/misc/compiler-failure.ogg', 25, 1)
return
@@ -100,8 +71,11 @@
dat += "[!active ? "BREAK IT DOWN" : "SHUT IT DOWN"]
"
dat += "
"
dat += " Select Track
"
- dat += "Track Selected: [selection.song_name]
"
- dat += "Track Length: [DisplayTimeText(selection.song_length)]
"
+ if(istype(selection))
+ dat += "Track Selected: [selection.song_name]
"
+ dat += "Track Length: [DisplayTimeText(selection.song_length)]
"
+ else
+ dat += "Track Selected: None!
"
var/datum/browser/popup = new(user, "vending", "[name]", 400, 350)
popup.set_content(dat.Join())
popup.open()
@@ -120,8 +94,14 @@
to_chat(usr, "Error: The device is still resetting from the last activation, it will be ready again in [DisplayTimeText(stop-world.time)].")
playsound(src, 'sound/misc/compiler-failure.ogg', 50, 1)
return
- activate_music()
- START_PROCESSING(SSobj, src)
+ if(!istype(selection))
+ to_chat(usr, "Error: Severe user incompetence detected.")
+ playsound(src, 'sound/misc/compiler-failure.ogg', 50, 1)
+ return
+ if(!activate_music())
+ to_chat(usr, "Error: Generic hardware failure.")
+ playsound(src, 'sound/misc/compiler-failure.ogg', 50, 1)
+ return
updateUsrDialog()
else if(active)
stop = 0
@@ -132,7 +112,7 @@
return
var/list/available = list()
- for(var/datum/track/S in songs)
+ for(var/datum/track/S in SSjukeboxes.songs)
available[S.song_name] = S
var/selected = input(usr, "Choose your song", "Track:") as null|anything in available
if(QDELETED(src) || !selected || !istype(available[selected], /datum/track))
@@ -141,10 +121,15 @@
updateUsrDialog()
/obj/machinery/jukebox/proc/activate_music()
- active = TRUE
- update_icon()
- START_PROCESSING(SSobj, src)
- stop = world.time + selection.song_length
+ var/jukeboxslottotake = SSjukeboxes.addjukebox(src, selection)
+ if(jukeboxslottotake)
+ active = TRUE
+ update_icon()
+ START_PROCESSING(SSobj, src)
+ stop = world.time + selection.song_length
+ return TRUE
+ else
+ return FALSE
/obj/machinery/jukebox/disco/activate_music()
..()
@@ -433,23 +418,9 @@
QDEL_LIST(sparkles)
/obj/machinery/jukebox/process()
- if(world.time < stop && active)
- var/sound/song_played = sound(selection.song_path)
-
- for(var/mob/M in range(10,src))
- if(!M.client || !(M.client.prefs.toggles & SOUND_INSTRUMENTS))
- continue
- if(!(M in rangers))
- rangers[M] = TRUE
- M.playsound_local(get_turf(M), null, 100, channel = CHANNEL_JUKEBOX, S = song_played)
- for(var/mob/L in rangers)
- if(get_dist(src,L) > 10)
- rangers -= L
- if(!L || !L.client)
- continue
- L.stop_sound_channel(CHANNEL_JUKEBOX)
- else if(active)
+ if(active && world.time >= stop)
active = FALSE
+ SSjukeboxes.removejukebox(SSjukeboxes.findjukeboxindex(src))
STOP_PROCESSING(SSobj, src)
dance_over()
playsound(src,'sound/machines/terminal_off.ogg',50,1)
diff --git a/code/game/sound.dm b/code/game/sound.dm
index 48accadb59..5b40e6640a 100644
--- a/code/game/sound.dm
+++ b/code/game/sound.dm
@@ -26,7 +26,7 @@
if(T && T.z == turf_source.z)
M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, channel, pressure_affected, S)
-/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff, channel = 0, pressure_affected = TRUE, sound/S)
+/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff, channel = 0, pressure_affected = TRUE, sound/S, envwet = -10000, envdry = 0)
if(!client || !can_hear())
return
@@ -36,6 +36,7 @@
S.wait = 0 //No queue
S.channel = channel || open_sound_channel()
S.volume = vol
+ S.environment = 7
if(vary)
if(frequency)
@@ -64,6 +65,8 @@
else //space
pressure_factor = 0
+ S.echo = list(envdry, null, envwet, null, null, null, null, null, null, null, null, null, null, 1, 1, 1, null, null)
+
if(distance <= 1)
pressure_factor = max(pressure_factor, 0.15) //touching the source of the sound
diff --git a/tgstation.dme b/tgstation.dme
index 6fb2973b66..157b78614f 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -221,6 +221,7 @@
#include "code\controllers\subsystem\input.dm"
#include "code\controllers\subsystem\ipintel.dm"
#include "code\controllers\subsystem\job.dm"
+#include "code\controllers\subsystem\jukeboxes.dm"
#include "code\controllers\subsystem\language.dm"
#include "code\controllers\subsystem\lighting.dm"
#include "code\controllers\subsystem\machines.dm"