"
dat += ""
+ if(9)
+ if(R)
+ stopMusic(user)
+ radio_holder = null
+ R.forceMove(get_turf(src))
+ playsound(src, 'sound/effects/plastic_click.ogg', 100, 0)
+ R = null
+ else
+ to_chat(src.loc, "No record disk inserted!")
+ mode = 0
+
+ if(10)
+ playMusiclocal(user)
+ mode = 0
+
+ if(11)
+ stopMusic(user)
+ radio_holder = null
+ mode = 0
else//Else it links to the cart menu proc. Although, it really uses menu hub 4--menu 4 doesn't really exist as it simply redirects to hub.
dat += cartridge.generate_menu()
@@ -751,7 +783,10 @@ GLOBAL_LIST_EMPTY(PDAs)
if(href_list["rspktoggle"])
radio.listening = !radio.listening
Boop()
-
+ if(href_list["allowmmusictoggle"])
+ allow_music = !allow_music
+ stopMusic(radio_holder)
+ Boop()
if(href_list["rfreq"])
var/new_frequency = (radio.frequency + text2num(href_list["rfreq"]))
if (!radio.freerange || (radio.frequency < MIN_FREE_FREQ || radio.frequency > MAX_FREE_FREQ))
@@ -897,7 +932,7 @@ GLOBAL_LIST_EMPTY(PDAs)
if (!signal.data["done"])
to_chat(user, span_notice("ERROR: Server isn't responding."))
if (!silent)
- playsound(src, 'sound/machines/terminal_error.ogg', 15, 1)
+ playsound(src, 'sound/machines/terminal_error.ogg', 35, channel = music_channel)
return
var/target_text = signal.format_target()
@@ -1090,6 +1125,15 @@ GLOBAL_LIST_EMPTY(PDAs)
to_chat(user, span_notice("You insert [cartridge] into [src]."))
update_icon()
playsound(src, 'sound/machines/button.ogg', 50, 1)
+ if(istype(C, /obj/item/record_disk))
+ if(R)
+ to_chat(user, "A record disk is already inserted!")
+ return
+ else
+ R = C
+ C.forceMove(src)
+ playsound(src, 'sound/effects/plastic_click.ogg', 100, 0)
+ playMusiclocal(user)
else if(istype(C, /obj/item/card/id))
var/obj/item/card/id/idcard = C
@@ -1216,6 +1260,9 @@ GLOBAL_LIST_EMPTY(PDAs)
/obj/item/pda/Destroy()
GLOB.PDAs -= src
+ GLOB.radio_list -= src //Big iron. Removes from global radio list
+ stopMusic(radio_holder)
+ radio_holder = null
if(istype(id))
QDEL_NULL(id)
if(istype(cartridge))
@@ -1315,6 +1362,45 @@ GLOBAL_LIST_EMPTY(PDAs)
continue
. += P
+/obj/item/pda/proc/playMusiclocal(mob/living/user)
+ if(istype(src.loc, /mob/living))
+ user = src.loc
+ if(item_flags & IN_INVENTORY)
+ if(allow_music)
+ playsound(user, R.R.song_path, 100, channel = music_channel) //plays the music to the user
+ radio_holder = user
+ to_chat(user, "You play the [R] on your PDA.")
+ else
+ to_chat(user, "The [src] is not allowed to play music!")
+ else
+ to_chat(user, "The [src] must be in your inventory to play music!")
+
+/obj/item/pda/proc/playmusic(music_filepath, name_of_music, music_volume) //Plays music at src using the filepath to the audio file. This proc is directly working with the bluespace radio station at radio_station.dm
+ var/atom/loc_layer = loc
+ while(istype(loc_layer, /atom/movable))
+ if(!istype(loc_layer, /mob/living))
+ loc_layer = loc_layer.loc
+ else
+ radio_holder = loc_layer
+ break
+ if(!loc_layer) //if loc is null then this proc doesn't need to continue
+ return
+ if(!istype(loc_layer, /mob/living)) //doesn't need to continue if not on a mob
+ return
+
+ if(allow_music) //Music player is on
+ var/mob/living/M = loc_layer
+ if(istype(M) && M.client)
+ var/client/C = M.client
+ if(!(C.prefs.toggles & MUSIC_RADIO))
+ return
+ stopMusic(radio_holder) //stop the previously playing song to make way for the new one
+ playsound(radio_holder, music_filepath, music_volume, channel = music_channel) //plays the music to the user
+ to_chat(radio_holder, "[src] beeps into your ears, 'Now playing: [name_of_music].' ")
+
+/obj/item/pda/proc/stopMusic(mob/user)
+ playsound(user, 'sound/machines/button.ogg', 50, channel = music_channel)
+ playsound(user, null, channel = music_channel)
#undef PDA_SCANNER_NONE
#undef PDA_SCANNER_MEDICAL
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index 2c92bb4033..54198cd604 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -47,6 +47,12 @@
var/linked_faction = FALSE // Which faction the radio is linked to.
var/mob/living/carbon/linked_mob = null // Which mob the radio is checked out to.
//fortuna addition end. radio management.
+ var/music_channel = null //The sound channel the music is playing on.
+ var/radio_music_file = "" //The file path to the music's audio file
+ var/music_toggle = TRUE //Toggles whether music will play or not.
+ var/music_name = "" //Used to display the name of currently playing music.
+ var/music_playing = FALSE
+ var/mob/living/radio_holder //stopmusic() will apply to this person
/obj/item/radio/suicide_act(mob/living/user)
user.visible_message("[user] starts bouncing [src] off [user.p_their()] head! It looks like [user.p_theyre()] trying to commit suicide!")
@@ -97,6 +103,7 @@
LAZYREMOVE(GLOB.bos_radios, src)
if(FACTION_ENCLAVE)
LAZYREMOVE(GLOB.enclave_radios, src)
+ GLOB.radio_list -= src //Big-Iron, removes radio from radio list lmao
remove_radio_all(src) //Just to be sure
QDEL_NULL(wires)
QDEL_NULL(keyslot)
@@ -104,6 +111,18 @@
/obj/item/radio/Initialize()
wires = new /datum/wires/radio(src)
+ var/obj/item/pda/pdloc = loc
+ if(!istype(src, /obj/item/radio/intercom) && !istype(pdloc)) //Intercoms playing music is useless
+ GLOB.radio_list += src //Big iron. Adds the radio to the global radio list for usage in radio_station.dm
+ var/i
+ for(i = 1; i <= GLOB.radio_list.len; i++)
+ if(GLOB.radio_list[i] == src)
+ music_channel = i //I hope that over 1,000 radios are never initialized.
+ /* Allow me to explain why. There are 1,024 usable channels. The top ~10
+ are preserved for ambience, admin music, etc. The other 1,000 are unused (to my knowledge)
+ and so to allow radios to play their own music without inferefering with other sounds, I give
+ each radio their own channel to play music on. This way the user can also stop the music
+ playing from their radio (headsets, etc.) without stopping the music of someone else's radio. */
if(prison_radio)
wires.cut(WIRE_TX) // OH GOD WHY
secure_radio_connections = new
@@ -161,6 +180,7 @@
data["broadcasting"] = broadcasting
data["listening"] = listening
+ data["music_toggle"] = music_toggle
data["frequency"] = frequency
data["minFrequency"] = freerange ? MIN_FREE_FREQ : MIN_FREQ
data["maxFrequency"] = freerange ? MAX_FREE_FREQ : MAX_FREQ
@@ -228,6 +248,10 @@
else
recalculateChannels()
. = TRUE
+ if("streammusic")
+ music_toggle = !music_toggle
+ stopmusic(radio_holder)
+ . = TRUE
/obj/item/radio/talk_into(atom/movable/M, message, channel, list/spans, datum/language/language)
if(HAS_TRAIT(M, TRAIT_SIGN_LANG)) //Forces Sign Language users to wear the translation gloves to speak over radios
@@ -410,6 +434,102 @@
to_chat(user, "The radio can no longer be modified or attached!")
else
return ..()
+
+/obj/item/radio/AltClick(mob/user) //Big Iron
+ ..()
+ if(!istype(user) || !Adjacent(user) || user.incapacitated())
+ return
+ if(user.a_intent == INTENT_HARM)
+ if(music_toggle)
+ music_toggle = 0
+ stopmusic(radio_holder)
+ to_chat(user, "[src]'s music player is now OFF. ")
+ else
+ music_toggle = 1
+ to_chat(user, "[src]'s music player is now ON. ")
+ return
+
+/obj/item/radio/proc/avoiding_a_sleep(mob/living/user, music_filepath, name_of_music, music_volume)
+ music_name = name_of_music
+ to_chat(user, "[src] beeps into your ears, 'Now playing: [music_name].' ")
+ if(user.client)
+ var/mob/M = user
+ var/client/C = M.client
+ if(!(C.prefs.toggles & MUSIC_RADIO))
+ to_chat(user, "[src] your preferences stopped [music_name] from playing!.' ")
+ return
+ music_playing = TRUE
+ playsound(user, music_filepath, music_volume, channel = music_channel) //plays the music to the user
+ update_icon()
+
+/obj/item/radio/proc/playmusic(music_filepath, name_of_music, music_volume) //Plays music at src using the filepath to the audio file. This proc is directly working with the bluespace radio station at radio_station.dm
+ radio_music_file = music_filepath
+
+ var/atom/loc_layer = loc
+ while(istype(loc_layer, /atom/movable))
+ if(!istype(loc_layer, /mob/living))
+ loc_layer = loc_layer.loc
+ else
+ radio_holder = loc_layer
+ break
+ if(!loc_layer) //if loc is null then this proc doesn't need to continue
+ return
+ if(!istype(loc_layer, /mob/living)) //doesn't need to continue if not on a mob
+ return
+
+ if(music_toggle == 1) //Music player is on
+ if(istype(src, /obj/item/radio/headset))
+ var/mob/living/carbon/wearer = radio_holder
+ if(!(wearer.ears == src)) //only want headsets to play music if they're equipped
+ return
+ stopmusic(radio_holder) //stop the previously playing song to make way for the new one
+ addtimer(CALLBACK(src, .proc/avoiding_a_sleep, radio_holder, music_filepath, name_of_music, music_volume), 10)
+
+/obj/item/radio/proc/stopmusic(mob/living/user, music_turnoff_message_type)
+ if(music_playing)
+ music_playing = FALSE
+ update_icon()
+ playsound(user, null, channel = music_channel)
+ playsound(user, 'sound/machines/buzz-sigh.ogg', 50, channel = music_channel)
+ music_name = ""
+ switch(music_turnoff_message_type)
+ if(1)
+ audible_message("[src] beeps, '[src] removed, turning off music.' ")
+ if(2)
+ src.audible_message("[src] beeps, 'Music toggled off.' ") //Unused message
+ if(3)
+ src.audible_message("[src] beeps, 'Signal interrupted.' ")
+ music_playing = FALSE
+
+/obj/item/radio/dropped(mob/user)
+ ..()
+ addtimer(CALLBACK(src, .proc/droppedStopMusic, user), 3)
+
+/obj/item/radio/proc/droppedStopMusic(mob/user)
+ var/i
+ for(i = 1, i <= user.contents.len, i++)
+ if(user.contents[i] == src)
+ return
+ if(item_flags & IN_INVENTORY)
+ return
+ if(determineIfInMob(user) == TRUE)
+ return
+ stopmusic(user, 1)
+
+/obj/item/radio/proc/determineIfInMob(mob/user)
+ var/obj/itemholder = src
+ var/mob/M = src.loc
+ while(M && !istype(M, /mob/living))
+ M = itemholder
+ if(!M)
+ return FALSE
+ itemholder = itemholder.loc
+ if(M == user)
+ return TRUE
+ else
+ return FALSE
+
+//Big-iron end
/*
/obj/item/radio/emp_act(severity)
. = ..()
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index f349b32d10..53ae6bf4ff 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -664,6 +664,7 @@ Records disabled until a use for them is found
dat += " "
dat += "Play Admin MIDIs:[(toggles & SOUND_MIDI) ? "Enabled":"Disabled"] "
dat += "Play Lobby Music:[(toggles & SOUND_LOBBY) ? "Enabled":"Disabled"] "
+ dat += "Stream radio music:[(toggles & MUSIC_RADIO) ? "Enabled":"Disabled"] "
dat += "See Pull Requests:[(chat_toggles & CHAT_PULLR) ? "Enabled":"Disabled"] "
dat += " "
if(user.client)
@@ -2542,6 +2543,9 @@ Records disabled until a use for them is found
if("ghost_ears")
chat_toggles ^= CHAT_GHOSTEARS
+ if("music_streaming")
+ toggles ^= MUSIC_RADIO
+
if("ghost_sight")
chat_toggles ^= CHAT_GHOSTSIGHT
diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm
index 8c637f36c9..6e164c4708 100644
--- a/code/modules/client/preferences_toggles.dm
+++ b/code/modules/client/preferences_toggles.dm
@@ -236,6 +236,19 @@ TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, toggleprayersounds)()
C?.tgui_panel?.stop_music()
SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Stop Self Sounds")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, toggleradiomusic)()
+ set name = "Hear/Silence Radio Music"
+ set category = "Preferences"
+ set desc = "Hear music streamed by radio stations"
+ usr.client.prefs.toggles ^= MUSIC_RADIO
+ usr.client.prefs.save_preferences()
+ if(usr.client.prefs.toggles & MUSIC_RADIO)
+ to_chat(usr, "You will now hear songs through radios.")
+ else
+ to_chat(usr, "You will no longer hear sounds played in radios")
+ SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Radio Music", "[usr.client.prefs.toggles & MUSIC_RADIO ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+/datum/verbs/menu/Settings/Sound/toggleradiomusic/Get_checked(client/C)
+ return C.prefs.toggles & MUSIC_RADIO
TOGGLE_CHECKBOX(/datum/verbs/menu/Settings, listen_ooc)()
set name = "Show/Hide OOC"
diff --git a/code/modules/jobs/job_types/kebab.dm b/code/modules/jobs/job_types/kebab.dm
index 001b096b7d..4ba01ee01b 100644
--- a/code/modules/jobs/job_types/kebab.dm
+++ b/code/modules/jobs/job_types/kebab.dm
@@ -977,6 +977,7 @@
ears = /obj/item/radio/headset/headset_town
glasses = /obj/item/clothing/glasses/sunglasses/big
r_pocket = /obj/item/gun/ballistic/revolver/police
+ l_pocket = /obj/item/circuitboard/machine/radio_station
shoes = /obj/item/clothing/shoes/laceup
backpack = /obj/item/storage/backpack/satchel/leather
backpack_contents = list(
diff --git a/fortune13.dme b/fortune13.dme
index 749e2ecace..ae360861f4 100644
--- a/fortune13.dme
+++ b/fortune13.dme
@@ -3701,6 +3701,8 @@
#include "modular_BD2\kitchen_50s\code\kitchen_50s.dm"
#include "modular_BD2\legio_invicta\code\legio_invicta.dm"
#include "modular_BD2\mortar\code\mortar.dm"
+#include "modular_big-iron\code\game\machinery\radio_station.dm"
+#include "modular_big-iron\code\game\objects\circuitboards\radio_station.dm"
#include "modular_citadel\code\datums\components\souldeath.dm"
#include "modular_citadel\code\datums\status_effects\chems.dm"
#include "modular_citadel\code\game\objects\cit_screenshake.dm"
diff --git a/modular_big-iron/code/game/machinery/radio_station.dm b/modular_big-iron/code/game/machinery/radio_station.dm
new file mode 100644
index 0000000000..96f3122283
--- /dev/null
+++ b/modular_big-iron/code/game/machinery/radio_station.dm
@@ -0,0 +1,156 @@
+//******************************************
+//WHEN ADDING NEW MUSIC, ADD THE MUSIC'S NAME TO _globalvars/lists/objects.dm global_music_list and then scroll down to the switch(music_name) part
+//Make sure you put it in alphabetical order and also update the switch(music_file) part at the disk burner's attack_hand proc with whatever music you added
+//******************************************
+
+/obj/machinery/radio_station
+ name = "radio station"
+ desc = "A specially equipped radio station to broadcast music to every radio in a the waste. Comes pre-coded with over 30 songs!"
+ icon = 'icons/fallout/machines/radio.dmi'
+ icon_state = "gannets_machine21"
+ max_integrity = 150
+ anchored = TRUE
+ density = TRUE
+ use_power = IDLE_POWER_USE
+ idle_power_usage = 2
+ armor = list("melee" = 20, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 20)
+ circuit = /obj/item/circuitboard/machine/radio_station
+ light_color = LIGHT_COLOR_GREEN
+ var/datum/track/selectedtrack = "None" //File path to the music's audio file
+ var/selectedtrackname = "none"
+// var/datum/track = null //This is the name of the song, not the file name. This is what shows up at the top of the music selection.
+ var/list/music_list //List of music that can be played. Only the most based songs may be added.
+ var/cooldowntime
+ var/obj/item/record_disk/R //The record disk that's currently playing music
+ var/can_eject_disk = 1
+ var/ready_to_delete = 0 //For Destroy()
+ var/brightness_on = 1
+ var/volume = 35 //the volume that music plays at
+
+/obj/machinery/radio_station/Initialize()
+ . = ..()
+ music_list = SSjukeboxes.songs
+ update_icon()
+
+/obj/machinery/radio_station/attack_hand(mob/living/user)
+ ..()
+ var/machinesound = 'sound/machines/doorclick.ogg'
+ playsound(src, machinesound, 100, 1)
+ if(R)
+ if(can_eject_disk)
+ update_icon()
+ playsound(src, 'sound/effects/plastic_click.ogg', 100, 0)
+ R.forceMove(get_turf(src))
+ R = null
+ stopRadioMusic()
+ return
+ else
+ to_chat(user, "You must wait to eject the record disk!")
+ return
+
+ if(stat & (NOPOWER|BROKEN) || . & EMP_PROTECT_SELF)
+ update_icon()
+ return
+ if(cooldowntime > world.time)
+ to_chat(user, "The broadcasting antenna needs time to recharge! It will be ready in [DisplayTimeText(cooldowntime - world.time)].")
+ return
+ var/list/songnames = list()
+ if(music_list.len)
+ for(var/i = 1, i <= music_list.len, i++)
+ var/datum/track/CT = music_list[i]
+ var/ctsn = "[CT.song_name]"
+ songnames += ctsn
+ var/datum/track/cooming = input(user, "Last played music: [selectedtrackname]", "Available Music") as null|anything in songnames
+ if(!user.Adjacent(src))
+ to_chat(user, "You are too far away!")
+ return
+ if(!cooming)
+ return
+ if(cooldowntime > world.time)
+ to_chat(user, "The broadcasting antenna needs time to recharge! It will be ready in [DisplayTimeText(cooldowntime - world.time)].")
+ return
+ for(var/datum/track/S in music_list)
+ if(S.song_name == cooming)//agghggagaghaghah
+ selectedtrack = S.song_path
+ selectedtrackname = S.song_name
+ continue
+ playsound(src, machinesound, 100, 1)
+ playMusicToRadios(user)
+
+/obj/machinery/radio_station/attackby(obj/item/I, mob/living/user)
+ if(istype(I, /obj/item/record_disk))
+ if(cooldowntime > world.time)
+ to_chat(user, "The broadcasting antenna needs time to recharge! It will be ready in [DisplayTimeText(cooldowntime - world.time)].")
+ return
+ if(!R)
+ R = I
+ I.forceMove(src)
+ playsound(src, 'sound/effects/plastic_click.ogg', 100, 0)
+ selectedtrack = R.R.song_path
+ if(R.R.song_name != "CUSTOM")
+ selectedtrack = R.R.song_path
+ selectedtrackname = R.R.song_name
+ else
+ selectedtrack = R.R.song_path
+ selectedtrackname = "A custom song!f"
+ playMusicToRadios(user)
+ return
+ else
+ to_chat(user, "There is already a record disk in the [src]!")
+ return
+ ..()
+
+/obj/machinery/radio_station/proc/playMusicToRadios(mob/living/user)
+ if(stat & (NOPOWER|BROKEN) || . & EMP_PROTECT_SELF)
+ update_icon()
+ return
+ if(cooldowntime > world.time)
+ to_chat(user, "The broadcasting antenna needs time to recharge! It will be ready in [DisplayTimeText(cooldowntime - world.time)].")
+ return
+
+ var/store_resistance_flags = resistance_flags
+ resistance_flags = INDESTRUCTIBLE //Need to do this so that it can't be destroyed before the music starts playing.
+
+ can_eject_disk = 0
+ cooldowntime = world.time + 500
+ for(var/obj/item/radio/R in GLOB.radio_list) //Calls the playmusic() proc for every radio in radio_list (everyone)
+ R.playmusic(selectedtrack, selectedtrackname, volume)
+ for(var/obj/item/pda/R in GLOB.radio_list) //Calls the playmusic() proc for every radio in radio_list (everyone)
+ R.playmusic(selectedtrack, selectedtrackname, volume)
+
+ src.audible_message("[src] beeps, 'Now broadcasting: [selectedtrackname]' ")
+
+ resistance_flags = store_resistance_flags
+
+ can_eject_disk = 1
+ if(stat & (NOPOWER|BROKEN) || . & EMP_PROTECT_SELF) //Need to check again in case the radio station is destroyed while this proc is in progress
+ update_icon()
+ stopRadioMusic()
+ return
+
+
+/obj/machinery/radio_station/proc/stopRadioMusic()
+ var/i
+ for(i = 1; i <= GLOB.radio_list.len; i++) //This time it will stop the music for every radio listening to this radio station.
+ if(!istype(GLOB.radio_list[i], /obj/item/pda))
+ GLOB.radio_list[i].stopmusic(GLOB.radio_list[i].radio_holder, 3)
+
+/obj/machinery/radio_station/Destroy()
+ stopRadioMusic()
+ ..()
+
+/obj/machinery/radio_station/update_icon()
+ ..()
+ if(!(stat & (NOPOWER|BROKEN) || . & EMP_PROTECT_SELF))
+ luminosity = 1
+ set_light(brightness_on)
+ return
+ luminosity = 0
+ set_light(0)
+
+/obj/machinery/radio_station/power_change()
+ . = ..()
+ if(stat & (NOPOWER|BROKEN) || . & EMP_PROTECT_SELF)
+ set_light(0)
+ else
+ set_light(brightness_on)
diff --git a/modular_big-iron/code/game/objects/circuitboards/radio_station.dm b/modular_big-iron/code/game/objects/circuitboards/radio_station.dm
new file mode 100644
index 0000000000..76af946c42
--- /dev/null
+++ b/modular_big-iron/code/game/objects/circuitboards/radio_station.dm
@@ -0,0 +1,6 @@
+/obj/item/circuitboard/machine/radio_station
+ name = "circuit board (radio station)"
+ build_path = /obj/machinery/radio_station
+ req_components = list(
+ /obj/item/stock_parts/cell = 1,
+ /obj/item/stock_parts/capacitor = 2)
diff --git a/tgui/packages/tgui/interfaces/Radio.js b/tgui/packages/tgui/interfaces/Radio.js
index 738826ba63..06c9ad0d52 100644
--- a/tgui/packages/tgui/interfaces/Radio.js
+++ b/tgui/packages/tgui/interfaces/Radio.js
@@ -18,6 +18,7 @@ export const Radio = (props, context) => {
useCommand,
subspace,
subspaceSwitchable,
+ music_toggle,
} = data;
const tunedChannel = RADIO_CHANNELS
.find(channel => channel.freq === frequency);
@@ -80,6 +81,12 @@ export const Radio = (props, context) => {
icon={broadcasting ? 'microphone' : 'microphone-slash'}
selected={broadcasting}
onClick={() => act('broadcast')} />
+