From d438053ddd4d4d442d786dafdc5c412cc4db5c4c Mon Sep 17 00:00:00 2001 From: smellie Date: Tue, 22 Jul 2025 11:08:40 +0100 Subject: [PATCH] Earphone Soundplayers (#20964) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds soundplayer functionality to earphones, headphones and earbuds. They can now play music, loaded via music cartridges with preset playlists. Heavily based off CMSS13's Walkmans and our Audioconsoles. Current Features: - All earphones have a cartridge slot. Cartridges can be inserted by clicking an earphone, and removed via an eject_music_cartridge() verb. - Inserting a cartridge will load a playlist containing /datum/tracks, where track names and sound files are loaded. - Shift+Clicking or will Start/Stop a playlist, creating or deleting an active sound_player token. - Alt+Clicking with Pause/Unpause the current track, preserving an active sound_player token. - Attack_self will eject the music cartridge. Ejecting a music cartridge also terminates the sound_player token. - Volume controllable via verb. Missing features i am too weak to figure out: - There is no auto—next song, and a user must manually use next_song() or previous_song() verbs to iterate through a playlist. - Part and parcel with no auto-next: Tracks automatically loop due to using the PlayLoopingSound() proc. Ideally, tracks should not loop. - There is no accomodation for user-uploaded sound files. - There are no UI implementations of earphone controls, which could be more user friendly. Music Cartridge CoderIcons: aaa ### Asset Licenses The following assets that **have not** been created by myself are included in this PR: | Path | Original Author | License | | --- | --- | --- | | sound/music/regional/venus/all_night.ogg | Rollergirl (https://rollergirl.bandcamp.com/album/rollergirl) | CC BY-NC-SA 3.0 | | sound/music/regional/venus/billy_ocean.ogg | Rollergirl (https://rollergirl.bandcamp.com/album/rollergirl) | CC BY-NC-SA 3.0 | | sound/music/regional/venus/artificially_sweetened.ogg | Stevia Sphere (https://steviasphere.bandcamp.com/album/artificially-sweetened-single) | CC BY 3.0 | |sound/music/regional/venus/real_love.ogg | Stevia Sphere (https://steviasphere.bandcamp.com/album/artificially-sweetened-single) | CC BY 3.0 | | sound/music/regional/venus/dance.ogg | ウナチャンUNACHAN (https://unachan.bandcamp.com/track/dance) | CC BY 3.0 | | sound/music/regional/venus/love_sensation.ogg | ウナチャンUNACHAN and bedroomtheft (https://unachan.bandcamp.com/track/love-sensation) | CC BY 3.0 | | sound/music/regional/venus/funky_girl.ogg | B~mO9000 (https://bmo9000.bandcamp.com/album/v-i-n-t-a-g-e) | CC BY-NC-SA 3.0 | | sound/music/regional/venus/break_the_system.ogg | (https://bmo9000.bandcamp.com/album/wanted-in-80s [Download Only]) | CC CC BY-SA 3.0 | | sound/music/regional/xanu/xanu_rock_3.ogg | Swara Sutra (https://swarasutra.bandcamp.com/album/indulgence) | CC BY-SA 3.0 | All sound files compressed to 20K sample rate. No transformations/remixing --- aurorastation.dme | 1 + code/datums/sound_player.dm | 11 + code/game/machinery/jukebox.dm | 1 - .../preference_setup/loadout/items/ears.dm | 14 - .../preference_setup/loadout/items/utility.dm | 31 + code/modules/clothing/ears/earmuffs.dm | 52 - code/modules/clothing/ears/earphones.dm | 437 ++ .../kermit-earphone_soundplayers.yml | 7 + icons/obj/item/music_cartridges.dmi | Bin 0 -> 656 bytes .../abandoned_diner/abandoned_diner.dmm | 6 +- .../away_site/pirate_base/pirate_base.dmm | 2 +- .../freebooter_ship/freebooter_ship_.dmm | 2 +- maps/away/ships/golden_deep/golden_deep.dmm | 2 +- maps/away/ships/lone_spacer/lone_spacer.dmm | 2 +- maps/away/ships/scc/scc_scout_ship.dmm | 2 +- .../ships/tramp_freighter/tramp_freighter.dmm | 2 +- maps/away/ships/yacht_civ/yacht_civ.dmm | 2 +- maps/sccv_horizon/sccv_horizon.dmm | 4944 ++++++++--------- sound/attributions.txt | 22 +- sound/music/regional/venus/all_night.ogg | Bin 0 -> 1714165 bytes .../regional/venus/artificially_sweetened.ogg | Bin 0 -> 2293633 bytes sound/music/regional/venus/billy_ocean.ogg | Bin 0 -> 2412225 bytes .../music/regional/venus/break_the_system.ogg | Bin 0 -> 776959 bytes sound/music/regional/venus/dance.ogg | Bin 0 -> 1825379 bytes sound/music/regional/venus/funky_girl.ogg | Bin 0 -> 1061186 bytes sound/music/regional/venus/love_sensation.ogg | Bin 0 -> 1999834 bytes sound/music/regional/venus/real_love.ogg | Bin 0 -> 1993928 bytes sound/music/regional/xanu/xanu_rock_3.ogg | Bin 0 -> 1808796 bytes 28 files changed, 2986 insertions(+), 2554 deletions(-) create mode 100644 code/modules/clothing/ears/earphones.dm create mode 100644 html/changelogs/kermit-earphone_soundplayers.yml create mode 100644 icons/obj/item/music_cartridges.dmi create mode 100644 sound/music/regional/venus/all_night.ogg create mode 100644 sound/music/regional/venus/artificially_sweetened.ogg create mode 100644 sound/music/regional/venus/billy_ocean.ogg create mode 100644 sound/music/regional/venus/break_the_system.ogg create mode 100644 sound/music/regional/venus/dance.ogg create mode 100644 sound/music/regional/venus/funky_girl.ogg create mode 100644 sound/music/regional/venus/love_sensation.ogg create mode 100644 sound/music/regional/venus/real_love.ogg create mode 100644 sound/music/regional/xanu/xanu_rock_3.ogg diff --git a/aurorastation.dme b/aurorastation.dme index 5cb123a4c43..bcc19fb61ae 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -1924,6 +1924,7 @@ #include "code\modules\clothing\ears\antennae.dm" #include "code\modules\clothing\ears\bandanna.dm" #include "code\modules\clothing\ears\earmuffs.dm" +#include "code\modules\clothing\ears\earphones.dm" #include "code\modules\clothing\ears\earrings.dm" #include "code\modules\clothing\ears\xeno\bulwark.dm" #include "code\modules\clothing\ears\xeno\skrell.dm" diff --git a/code/datums/sound_player.dm b/code/datums/sound_player.dm index ca564d4f5e6..52487a87b40 100644 --- a/code/datums/sound_player.dm +++ b/code/datums/sound_player.dm @@ -36,6 +36,17 @@ GLOBAL_DATUM_INIT(sound_player, /singleton/sound_player, new) return PlaySoundDatum(source, sound_id, S, range, prefer_mute, sound_type) +/singleton/sound_player/proc/PlayNonloopingSound(atom/source, sound_id, sound, volume, range, falloff = 1, echo, frequency, prefer_mute, sound_type = ASFX_AMBIENCE) + var/sound/S = istype(sound, /sound) ? sound : new(sound) + S.environment = 0 // Ensures a 3D effect even if x/y offset happens to be 0 the first time it's played + S.volume = volume + S.falloff = falloff + S.echo = echo + S.frequency = frequency + S.repeat = FALSE + + return PlaySoundDatum(source, sound_id, S, range, prefer_mute, sound_type) + /singleton/sound_player/proc/PrivStopSound(datum/sound_token/sound_token) var/channel = sound_token.sound.channel var/sound_id = sound_token.sound_id diff --git a/code/game/machinery/jukebox.dm b/code/game/machinery/jukebox.dm index 7cd4dcece6a..6455fd2b2d5 100644 --- a/code/game/machinery/jukebox.dm +++ b/code/game/machinery/jukebox.dm @@ -36,7 +36,6 @@ new/datum/track("Thunderdome", 'sound/music/THUNDERDOME.ogg') ) - /obj/machinery/media/jukebox/Destroy() StopPlaying() return ..() diff --git a/code/modules/client/preference_setup/loadout/items/ears.dm b/code/modules/client/preference_setup/loadout/items/ears.dm index 89938c77605..f7383388022 100644 --- a/code/modules/client/preference_setup/loadout/items/ears.dm +++ b/code/modules/client/preference_setup/loadout/items/ears.dm @@ -14,20 +14,6 @@ path = /obj/item/clothing/accessory/ear_warmers flags = GEAR_HAS_NAME_SELECTION | GEAR_HAS_DESC_SELECTION | GEAR_HAS_COLOR_SELECTION -/datum/gear/ears/double/headphones - display_name = "headphone selection" - description = "A selection of headphones." - path = /obj/item/clothing/ears/earmuffs/earphones/headphones - flags = GEAR_HAS_COLOR_SELECTION - -/datum/gear/ears/double/headphones/New() - ..() - var/list/headphones = list() - headphones["headphones"] = /obj/item/clothing/ears/earmuffs/earphones/headphones - headphones["earphones"] = /obj/item/clothing/ears/earmuffs/earphones - headphones["earbuds"] = /obj/item/clothing/ears/earmuffs/earphones/earbuds - gear_tweaks += new /datum/gear_tweak/path(headphones) - /datum/gear/ears/earrings display_name = "earring selection" description = "A selection of eye-catching earrings." diff --git a/code/modules/client/preference_setup/loadout/items/utility.dm b/code/modules/client/preference_setup/loadout/items/utility.dm index 61aec2c0499..b5d870dcc9e 100644 --- a/code/modules/client/preference_setup/loadout/items/utility.dm +++ b/code/modules/client/preference_setup/loadout/items/utility.dm @@ -179,3 +179,34 @@ /datum/gear/utility/buddy_tag/New() ..() gear_tweaks += new /datum/gear_tweak/buddy_tag_config() + +/datum/gear/utility/earphones + display_name = "earphones selection" + flags = GEAR_HAS_NAME_SELECTION | GEAR_HAS_DESC_SELECTION | GEAR_HAS_COLOR_SELECTION + path = /obj/item/clothing/ears/earphones + cost = 1 + +/datum/gear/utility/earphones/New() + ..() + var/list/earphones = list() + earphones["headphones"] = /obj/item/clothing/ears/earphones/headphones + earphones["earphones"] = /obj/item/clothing/ears/earphones + earphones["earbuds"] = /obj/item/clothing/ears/earphones/earbuds + gear_tweaks += new /datum/gear_tweak/path(earphones) + +/datum/gear/utility/earphones_music_cartridge + display_name = "earphones music cartridge selection" + flags = GEAR_HAS_NAME_SELECTION | GEAR_HAS_DESC_SELECTION + path = /obj/item/music_cartridge/audioconsole + cost = 1 + +/datum/gear/utility/earphones_music_cartridge/New() + ..() + var/list/music_cartridges = list() + music_cartridges["Konyang Vibes 2463"] = /obj/item/music_cartridge/konyang_retrowave + music_cartridges["Top of the Charts 66 (Venusian Hits)"] = /obj/item/music_cartridge/venus_funkydisco + music_cartridges["SCC Welcome Package"] = /obj/item/music_cartridge/audioconsole + music_cartridges["Spacer Classics Vol. 1"] = /obj/item/music_cartridge/ + music_cartridges["Indulgence EP (X-Rock)"] = /obj/item/music_cartridge/xanu_rock + music_cartridges["Electro-Swing of Adhomai"] = /obj/item/music_cartridge/adhomai_swing + gear_tweaks += new /datum/gear_tweak/path(music_cartridges) diff --git a/code/modules/clothing/ears/earmuffs.dm b/code/modules/clothing/ears/earmuffs.dm index 1da33c4229c..3845ac7f888 100644 --- a/code/modules/clothing/ears/earmuffs.dm +++ b/code/modules/clothing/ears/earmuffs.dm @@ -7,58 +7,6 @@ item_flags = ITEM_FLAG_SOUND_PROTECTION slot_flags = SLOT_EARS | SLOT_TWOEARS contained_sprite = TRUE - var/on = FALSE - -/obj/item/clothing/ears/earmuffs/earphones - name = "earphones" - desc = "A pair of wired earphones. Good luck trying to find a headphone jack." - desc_extended = "Has a tendency to snag itself on whatever furniture it can find at the most inopportune of times." - icon_state = "earphones" - item_state = "earphones" - build_from_parts = TRUE - -/obj/item/clothing/ears/earmuffs/earphones/attack_self(mob/user) - togglemusic() - -/obj/item/clothing/ears/earmuffs/earphones/verb/togglemusic() - set name = "Toggle Headphone Music" - set category = "Object" - set src in usr - if(!isliving(usr)) - return - if(use_check_and_message(usr)) - return - - playsound(src, /singleton/sound_category/button_sound, 10) - if(on) - ClearOverlays() - worn_overlay = null - to_chat(usr, SPAN_NOTICE("You turn the music off.")) - else - worn_overlay = "music" // this is rather annoying but prevents the music notes on getting colored - to_chat(usr, SPAN_NOTICE("You turn the music on.")) - on = !on - - update_icon() - update_clothing_icon() - -/obj/item/clothing/ears/earmuffs/earphones/headphones - name = "headphones" - desc = "A pair of headphones. Cushioned and sound-cancelling." - desc_extended = "Unce unce unce unce." - icon_state = "headphones" - item_state = "headphones" - -/obj/item/clothing/ears/earmuffs/earphones/headphones/update_icon() - ..() - AddOverlays(overlay_image(icon, "[icon_state]_overlay", flags=RESET_COLOR)) - -/obj/item/clothing/ears/earmuffs/earphones/earbuds - name = "earbuds" - desc = "A pair of wireless earbuds. Don't lose them." - desc_extended = "Oh god, they can't hear us! They've got earbuds on!" - icon_state = "earbuds" - item_state = "earbuds" /obj/item/clothing/accessory/ear_warmers name = "ear warmers" diff --git a/code/modules/clothing/ears/earphones.dm b/code/modules/clothing/ears/earphones.dm new file mode 100644 index 00000000000..a1c9be58907 --- /dev/null +++ b/code/modules/clothing/ears/earphones.dm @@ -0,0 +1,437 @@ +/* + Earphones that use our sound_player system to play sounds from a music cartridge to their wearer. + + Current Features: + - All earphones have a cartridge slot. Cartridges can be inserted by clicking an earphone, and removed via an eject_music_cartridge() verb. + - Inserting a cartridge will load a playlist containing /datum/tracks, where track names and sound files are loaded. + - Shift+Clicking will Start/Stop a playlist, creating or deleting an active sound_player token. + - Alt+Clicking with Pause/Unpause the current track, preserving an active sound_player token. + - attack_self will eject the music cartridge. Ejecting a music cartridge also terminates the sound_player token. + - Volume controllable via verb. + + Missing features i am too weak to figure out: + - There is no auto—next song, and a user must manually use next_song() or previous_song() verbs to iterate through a playlist. + - Part and parcel with no auto-next: Tracks automatically loop due to using the PlayLoopingSound() proc. Ideally, tracks should not loop. + - There is no accomodation for user-uploaded sound files. + - There are no UI implementations of earphone controls, which could be more user friendly. +*/ + +/obj/item/clothing/ears/earphones + name = "earphones" + desc = "A pair of wireless earphones. Includes a little slot for a music cartridge." + desc_mechanics = "Shift+Click to Start/Stop a playlist. Alt+Click to Pause/Resume the current track. Click character to eject cartridge. Alternatively, use verbs." + icon = 'icons/obj/clothing/ears/earmuffs.dmi' + icon_state = "earphones" + item_state = "earphones" + slot_flags = SLOT_EARS + contained_sprite = TRUE + build_from_parts = TRUE + + /// The music cartridge loaded into the earphones. The source of which sound files to play. + var/obj/item/music_cartridge/music_cartridge + + /// The active sound_token for the sound_player. Created per track and deleted when a song is stopped. + var/datum/sound_token/soundplayer_token = null + + /// A list of /datum/tracks the earphones will iterate through to select sound files. /datum/tracks supplied by a music cartridge item. + var/list/datum/track/current_playlist = list() + + /// For iterating through current_player() to select new sounds + var/playlist_index = 1 + + /// Volume. Affected by change_volume() proc/verb so users may control the volume. + var/volume = 25 + + /// Range. No use for this yet, but maybe someone will want to make a boombox or something that has a larger range. + var/range = 0 + + /// Is a track playing? For icon updates. + var/playing = FALSE + +/obj/item/clothing/ears/earphones/Destroy() + StopPlaying() + QDEL_NULL(music_cartridge) + . = ..() + +/* + Music Cartridge Procs: +*/ + +// Cartridge Insertion +/obj/item/clothing/ears/earphones/attackby(obj/item/attacking_item, mob/user) + if(istype(attacking_item, /obj/item/music_cartridge)) + if(attacking_item == user.get_active_hand()) + if(!music_cartridge) // Making sure there is no track in there already + user.drop_from_inventory(attacking_item, src) + music_cartridge = attacking_item + read_music_cartridge(attacking_item, user) // This is where tracks will actually get loaded + to_chat(user, SPAN_NOTICE("You insert \the [attacking_item] into \the [src]")) + else + to_chat(user, SPAN_WARNING("There's already a music cartridge in there.")) + ..() + +/obj/item/clothing/ears/earphones/proc/read_music_cartridge(obj/item/music_cartridge/cartridge, mob/user) + StopPlaying() // New cartridge in, so clean up our sound token if it hasn't already been for some reason + + playlist_index = 1 // Back to the beginning + if(music_cartridge.tracks) + for(var/datum/track/track in music_cartridge.tracks) + current_playlist += track // Move all the tracks stored on the music_cartridge to our current_playlist + + to_chat(user, SPAN_NOTICE("Now listening to: [music_cartridge.name].")) + +/obj/item/clothing/ears/earphones/proc/eject_music_cartridge(mob/user) + if(!music_cartridge) + to_chat(user,SPAN_WARNING("There is no music cartridge in \the [src]!")) + return + + StopPlaying() // Stop & Clean Up + current_playlist.Cut() + user.put_in_hands(music_cartridge) + music_cartridge = null + + to_chat(user,SPAN_NOTICE("You eject the music cartridge from \the [src].")) + +/obj/item/clothing/ears/earphones/attack_self(mob/user) + eject_music_cartridge(user) + ..() + +/* + Starting and Stopping Procs +*/ + +/obj/item/clothing/ears/earphones/proc/StopPlaying() + if(!soundplayer_token) + return + + soundplayer_token.Stop() + QDEL_NULL(soundplayer_token) // A sound token is created per-song, so we'll be liberal with cleaning up old tokens. + + // Icon/Overlay stuff for the music notes + ClearOverlays() + worn_overlay = null + update_icon() + update_clothing_icon() + +/obj/item/clothing/ears/earphones/proc/StartPlaying(mob/user) + if(!music_cartridge) + to_chat(user, SPAN_WARNING("No cartridge loaded.")) + return + + // Make sure we aren't doubling up on soundtokens + if(!soundplayer_token) + if(current_playlist && (current_playlist.len > 0)) + var/sound/sound_to_play = current_playlist[playlist_index].sound + soundplayer_token = GLOB.sound_player.PlayLoopingSound(src, src, sound_to_play, volume, range, 20, prefer_mute = FALSE, sound_type = ASFX_MUSIC) + + //addtimer(CALLBACK(src, PROC_REF(next_song), user), (sound_to_play.len SECOND)) // Queue the next_song() proc when the current song ends. Clean up handled under next_song() which also calls stopplaying() to end this token + // If someone gets ^this^ to work, you can switch out PlayLoopingSound for PlayNonloopingSound + // the sound's len variable doesn't seem to actually store a length :/ + + // Chat Display + var/current_track_name = current_playlist[playlist_index].title + to_chat(user,SPAN_NOTICE("Now Playing: Track [playlist_index] — '[current_track_name]'.")) + + // Icon/Overlay stuff for the music notes + worn_overlay = "music" // this is rather annoying but prevents the music notes on getting colored + update_icon() + update_clothing_icon() + +/* + Song Iteration Procs +*/ + +/obj/item/clothing/ears/earphones/proc/next_song(mob/user) + if(use_check_and_message(user)) + return + + if(!music_cartridge || (current_playlist.len == 0)) + to_chat(user, SPAN_WARNING("No playlist loaded.")) + return + + StopPlaying() // Stop & Cleanup previous sound token + + // Sound iteration + if(playlist_index + 1 <= current_playlist.len) + playlist_index++ + else + playlist_index = 1 + + StartPlaying(user) // New sound token created here + +/obj/item/clothing/ears/earphones/proc/previous_song(mob/user) + if(use_check_and_message(usr)) + return + if(!music_cartridge || (current_playlist.len == 0)) + to_chat(user, SPAN_WARNING("No playlist loaded.")) + return + + StopPlaying() // Stop & Cleanup previous sound token + + // Sound iteration + if(playlist_index - 1 < 1) + playlist_index = 1 + else + playlist_index-- + + StartPlaying(user) // New sound token created here + +/* + Verbs: + + play_stop() + change_volume() + pause_unpause() + next_song() + previous_song() + +*/ + +/obj/item/clothing/ears/earphones/verb/play_stop() + set name = "Earphones - Play/Stop" + set category = "Object" + set src in usr + + if(use_check_and_message(usr)) + return + + if(ismob(src.loc)) + usr.visible_message(SPAN_NOTICE("[usr] clicks a button on [usr.get_pronoun("his")] [src].")) + playsound(usr, /singleton/sound_category/button_sound, 10) + + if(soundplayer_token) + StopPlaying() + else + if(!music_cartridge) + to_chat(usr, SPAN_WARNING("You need to slot a music cartridge in first!")) + return + StartPlaying(usr) + +/obj/item/clothing/ears/earphones/verb/change_volume() + set name = "Earphones - Change Volume" + set category = "Object" + set src in usr + + if(use_check_and_message(usr)) + return + + usr.visible_message(SPAN_NOTICE("[usr] swipes at [usr.get_pronoun("his")] [src].")) + + // Volume Control + var/volume_input = tgui_input_number(usr,"Change the volume (0 - 100)","Volume", volume, 100, 0) + if(volume_input == null) + return + if(volume_input > 100) + volume_input = 100 + if(volume_input < 0) + volume_input = 0 + volume = volume_input + + // If a soundtoken is already active/a song playing, we can adjust the volume mid-token. + if(!soundplayer_token) + return + else + soundplayer_token.SetVolume(volume) + +/obj/item/clothing/ears/earphones/verb/pause_unpause() + set name = "Earphones - Pause/Unpause" + set category = "Object" + set src in usr + + if(use_check_and_message(usr)) + return + + if(ismob(src.loc)) + usr.visible_message(SPAN_NOTICE("[usr] clicks a button on [usr.get_pronoun("his")] [src].")) + playsound(usr, /singleton/sound_category/button_sound, 10) + if(soundplayer_token) + if(soundplayer_token.status != SOUND_PAUSED) + soundplayer_token.Pause() + to_chat(usr, SPAN_NOTICE("Music paused.")) + + // Icon/Overlay stuff for the music notes + ClearOverlays() + worn_overlay = null + update_icon() + update_clothing_icon() + + else if (soundplayer_token.status == SOUND_PAUSED) + soundplayer_token.Unpause() + to_chat(usr, SPAN_NOTICE("Music unpaused.")) + + // Icon/Overlay stuff for the music notes + worn_overlay = "music" // this is rather annoying but prevents the music notes on getting colored + update_icon() + update_clothing_icon() + else + play_stop() //No soundtoken? They probably meant to use the other verb instead. + +/obj/item/clothing/ears/earphones/verb/next_song_verb() + set name = "Earphones - Next Song" + set category = "Object" + set src in usr + + usr.visible_message(SPAN_NOTICE("[usr] clicks a button on [usr.get_pronoun("his")] [src].")) + playsound(usr, /singleton/sound_category/button_sound, 10) + next_song(usr) + +/obj/item/clothing/ears/earphones/verb/previous_song_verb() + set name = "Earphones - Previous Song" + set category = "Object" + set src in usr + + usr.visible_message(SPAN_NOTICE("[usr] clicks a button on [usr.get_pronoun("his")] [src].")) + playsound(usr, /singleton/sound_category/button_sound, 10) + previous_song(usr) + +/obj/item/clothing/ears/earphones/verb/eject_music_cartridge_verb() + set name = "Earphones - Eject Music Cartridge" + set category = "Object" + set src in usr + + eject_music_cartridge(usr) + +/* + Click Controls: + + Shift+Click to Pause/Unpause + Alt+Click to Start/Stop + +*/ + +/obj/item/clothing/ears/earphones/AltClick(mob/user) + pause_unpause() + +/obj/item/clothing/ears/earphones/ShiftClick(mob/user) + play_stop() + +/* + Generic Earwear Procs +*/ + +/obj/item/clothing/ears/earphones/update_clothing_icon() + if (ismob(src.loc)) + var/mob/M = src.loc + M.update_inv_l_ear() + M.update_inv_r_ear() + +/* +Earphone Variants +*/ + +/obj/item/clothing/ears/earphones/headphones + name = "headphones" + desc = "A pair of headphones. Cushioned but not quite sound-cancelling. Includes a little slot for a music cartridge." + desc_extended = "Unce unce unce unce." + icon_state = "headphones" + item_state = "headphones" + + slot_flags = SLOT_EARS | SLOT_TWOEARS + +/obj/item/clothing/ears/earphones/headphones/update_icon() + ..() + AddOverlays(overlay_image(icon, "[icon_state]_overlay", flags=RESET_COLOR)) + +/obj/item/clothing/ears/earphones/earbuds + name = "earbuds" + desc = "A pair of wireless earbuds. Don't lose them. Includes a little slot for a music cartridge." + icon_state = "earbuds" + item_state = "earbuds" + +/* + Music Cartridges +*/ + +/obj/item/music_cartridge + name = "music cartridge" + desc = "A music cartridge." + icon = 'icons/obj/item/music_cartridges.dmi' + icon_state = "generic" + w_class = WEIGHT_CLASS_SMALL + + var/list/datum/track/tracks = list() + +/obj/item/music_cartridge/ss13 + name = "Spacer Classics Vol. 1" + desc = "An old music cartridge with a cheap-looking label." + + tracks = list( + new/datum/track("Title 1", 'sound/music/title1.ogg'), + new/datum/track("Title 2", 'sound/music/title2.ogg'), + new/datum/track("Title 3", 'sound/music/title3.ogg'), + new/datum/track("Traitor", 'sound/music/traitor.ogg'), + new/datum/track("Space", 'sound/music/space.ogg'), + new/datum/track("Title 3 Mk2", 'sound/music/title3mk2.ogg') + ) + +/obj/item/music_cartridge/audioconsole + name = "SCC Welcome Package" + desc = "A music cartridge with some company-selected songs. Nothing special, everyone got one of these in their welcome boxes..." + + tracks = list( + new/datum/track("Amsterdam", 'sound/music/audioconsole/Amsterdam.ogg'), + new/datum/track("Butterflies", 'sound/music/audioconsole/Butterflies.ogg'), + new/datum/track("Childhood", 'sound/music/audioconsole/Childhood.ogg'), + new/datum/track("Don't Rush", 'sound/music/audioconsole/DontRush.ogg'), + new/datum/track("Lips", 'sound/music/audioconsole/Lips.ogg'), + new/datum/track("Number 0", 'sound/music/audioconsole/Number0.ogg'), + new/datum/track("Phoron Will Make Us Rich", 'sound/music/audioconsole/PhoronWillMakeUsRich.ogg'), + new/datum/track("That Ain't Chopin", 'sound/music/audioconsole/ThatAintChopin.ogg'), + new/datum/track("The Pianist", 'sound/music/audioconsole/ThePianist.ogg'), + new/datum/track("When", 'sound/music/audioconsole/When.ogg') + ) + +/* + Regional Music Cartridges +*/ + +/obj/item/music_cartridge/konyang_retrowave + name = "Konyang Vibes 2463" + desc = "A music cartridge with a Konyang flag on the hololabel." + icon_state = "konyang" + + tracks = list( + new/datum/track("Konyang Vibes #1", 'sound/music/lobby/konyang/konyang-1.ogg'), + new/datum/track("Konyang Vibes #2", 'sound/music/lobby/konyang/konyang-2.ogg'), + new/datum/track("Konyang Vibes #3", 'sound/music/lobby/konyang/konyang-3.ogg') + ) + +/obj/item/music_cartridge/venus_funkydisco + name = "Top of the Charts 66 (Venusian Hits)" + desc = "A glitzy, pink music cartridge with more Venusian hits." + icon_state = "venus" + + tracks = list( + new/datum/track("dance させる", 'sound/music/regional/venus/dance.ogg'), + new/datum/track("love sensation", 'sound/music/regional/venus/love_sensation.ogg'), + new/datum/track("All Night", 'sound/music/regional/venus/all_night.ogg'), + new/datum/track("#billyocean", 'sound/music/regional/venus/billy_ocean.ogg'), + new/datum/track("Artificially Sweetened", 'sound/music/regional/venus/artificially_sweetened.ogg'), + new/datum/track("Real Love", 'sound/music/regional/venus/real_love.ogg'), + new/datum/track("F U N K Y G I R L <3", 'sound/music/regional/venus/funky_girl.ogg'), + new/datum/track("Break The System", 'sound/music/regional/venus/break_the_system.ogg') + ) + + +/obj/item/music_cartridge/xanu_rock + name = "Indulgence EP (X-Rock)" //feel free to reflavour as a more varied x-rock mixtape, instead of a single EP, if other x-rock tracks are added + desc = "A music cartridge with a Xanan flag on the hololabel. Some fancy, rainbow text over it reads, 'INDULGENCE'." + icon_state = "xanu" + + tracks = list( + new/datum/track("Shimmer", 'sound/music/regional/xanu/xanu_rock_3.ogg'), + new/datum/track("Rise", 'sound/music/regional/xanu/xanu_rock_1.ogg'), + new/datum/track("Indulgence", 'sound/music/regional/xanu/xanu_rock_2.ogg') + ) + +/obj/item/music_cartridge/adhomai_swing + name = "Electro-Swing of Adhomai" + desc = "A red music cartridge holding the most widely-known Adhomian electro-swing songs." + icon_state = "adhomai" + + tracks = list( + new/datum/track("Boolean Sisters", 'sound/music/phonograph/boolean_sisters.ogg'), + new/datum/track("Electro Swing", 'sound/music/phonograph/electro_swing.ogg'), + new/datum/track("Le Swing", 'sound/music/phonograph/le_swing.ogg'), + new/datum/track("Posin", 'sound/music/phonograph/posin.ogg') + ) diff --git a/html/changelogs/kermit-earphone_soundplayers.yml b/html/changelogs/kermit-earphone_soundplayers.yml new file mode 100644 index 00000000000..50eba83f1cc --- /dev/null +++ b/html/changelogs/kermit-earphone_soundplayers.yml @@ -0,0 +1,7 @@ +author: kermit + +delete-after: True + +changes: + - rscadd: "Makes earphones/headphones/earbuds play music, much like Audioconsoles!" + - rscadd: "Adds music cartridges which are used to load music into earphones." \ No newline at end of file diff --git a/icons/obj/item/music_cartridges.dmi b/icons/obj/item/music_cartridges.dmi new file mode 100644 index 0000000000000000000000000000000000000000..e802ab922a01d86294d7ba41df8604c3dd8e9771 GIT binary patch literal 656 zcmV;B0&o3^P)P)t-sz`(#X zGBW>`%q1*MJv}{HSXlqB%>T^HGCE^ENqzs!Gt8L)|ANf_>(>C0&gGXk0A@2IBO@Ch zHo%4wz`(#rBN|*UBXl|}GZ_*QN8)?{0004WQchCV=-0C=2JR&a84_w-Y6@%7{?OD!tS%+FJ>RWQ*r;NmRLOex6#a*U0*I5Sc+(=$pS zoZ^zil2jm5DLpkWwJ0-LiHkEOv#1y-V#vjrR+N~V3Sq<5m8IsD786uek(gIXP*HY% zUS(olIze@bDH-{>iJ7?6DJ!`8xq$r)0O_Yk7ve0m<^TWzT1iAfR9J=WmeG!cFbGAh z)jGOGS0`=#|1Vv{XD*4&%**!hcnv4en9y~NM&rx43y}90Aomxj|1gf@pd0!yO;gYf z{x#3@3mDwE{4667Oq@?ZFmpa5!N4ED2{?xna1JJr4S^l;hC&GXj$}hD%>LBUGDb%KrX1nXu9Rx$*}iY$RV zG7!ik1A#m;6bL{d0D%AmyA)WFlrA`=RKd%m zC$>Bn825e3iKwKC30SZifY?|beoR0!~WpYkd7SYX}v qXL@I@bUqcB_x*YExPPPZU-<*w#~1BO=qMln0000