diff --git a/code/__DEFINES/dcs/signals/signals_music.dm b/code/__DEFINES/dcs/signals/signals_music.dm index 3bb023378cd..e41207713e2 100644 --- a/code/__DEFINES/dcs/signals/signals_music.dm +++ b/code/__DEFINES/dcs/signals/signals_music.dm @@ -4,3 +4,5 @@ #define COMSIG_SONG_START "song_start" ///sent to the instrument when a song stops playing #define COMSIG_SONG_END "song_end" +///sent to the instrument on /should_stop_playing(): (atom/player). Return values can be found in DEFINES/song.dm +#define COMSIG_SONG_SHOULD_STOP_PLAYING "song_should_stop_playing" diff --git a/code/__DEFINES/song.dm b/code/__DEFINES/song.dm index 26121707f7d..7af269fbe21 100644 --- a/code/__DEFINES/song.dm +++ b/code/__DEFINES/song.dm @@ -2,5 +2,14 @@ #define MUSIC_MAXLINES 1000 #define MUSIC_MAXLINECHARS 300 +#define BPM_TO_TEMPO_SETTING(value) (600 / round(value, 1)) + +//Return values of song/should_stop_playing() + +///When the song should stop being played +#define STOP_PLAYING 1 +///Will ignore the instrument checks and play the song anyway. +#define IGNORE_INSTRUMENT_CHECKS 2 + ///it's what monkeys play! #define MONKEY_SONG "BPM: 200\nC4/0,14,C,A4-F2,F3,A3,F-F2,A-F,F4,G4,F,D4-Bb2-G2\nD3,G3,D-G2,G3-G2,D,D4-G3,D,B4-B2,G,B3,G-B2,B3-B2\nG4,A4,G,E4-C3,E3,G3,E-C,G-C,E,E4-G,E,C5-E-A3,C4\nA-E3,C,E4-C3,A4-C4,B4-A3-A2,C5-C4,D5-F-B3,D4,B-F3\nD,F4-D3,D4,F-B-B2,G4-D,A4-C-F3,F,C/2,B3/2,A3-C3/2\nB/2,C4,E-C3,F4,G-C,F-F3,F-C,C4/2,B/2,A-A2/2,G3/2\nF/I" diff --git a/code/__DEFINES/wiremod.dm b/code/__DEFINES/wiremod.dm index 124d9f4da47..4277380c055 100644 --- a/code/__DEFINES/wiremod.dm +++ b/code/__DEFINES/wiremod.dm @@ -94,6 +94,7 @@ #define SHELL_FLAG_ALLOW_FAILURE_ACTION (1<<3) // Shell capacities. These can be converted to configs very easily later +#define SHELL_CAPACITY_TINY 12 #define SHELL_CAPACITY_SMALL 25 #define SHELL_CAPACITY_MEDIUM 50 #define SHELL_CAPACITY_LARGE 100 diff --git a/code/controllers/subsystem/processing/instruments.dm b/code/controllers/subsystem/processing/instruments.dm index ee0fd1ea009..0d14c19c2bb 100644 --- a/code/controllers/subsystem/processing/instruments.dm +++ b/code/controllers/subsystem/processing/instruments.dm @@ -20,6 +20,7 @@ PROCESSING_SUBSYSTEM_DEF(instruments) var/static/current_instrument_channels = 0 /// Single cached list for synthesizer instrument ids, so you don't have to have a new list with every synthesizer. var/static/list/synthesizer_instrument_ids + var/static/list/note_sustain_modes = list("Linear" = SUSTAIN_LINEAR, "Exponential" = SUSTAIN_EXPONENTIAL) /datum/controller/subsystem/processing/instruments/Initialize() initialize_instrument_data() diff --git a/code/modules/instruments/items.dm b/code/modules/instruments/items.dm index 9575ddb1451..1b2d64ed1d0 100644 --- a/code/modules/instruments/items.dm +++ b/code/modules/instruments/items.dm @@ -23,8 +23,12 @@ QDEL_NULL(song) return ..() -/obj/item/instrument/proc/should_stop_playing(mob/user) - return user.incapacitated() || !((loc == user) || (isturf(loc) && Adjacent(user))) // sorry, no more TK playing. +/obj/item/instrument/proc/should_stop_playing(atom/music_player) + if(!ismob(music_player)) + return STOP_PLAYING + var/mob/user = music_player + if(user.incapacitated() || !((loc == user) || (isturf(loc) && Adjacent(user)))) // sorry, no more TK playing. + return STOP_PLAYING /obj/item/instrument/suicide_act(mob/user) user.visible_message(span_suicide("[user] begins to play 'Gloomy Sunday'! It looks like [user.p_theyre()] trying to commit suicide!")) @@ -61,63 +65,6 @@ inhand_icon_state = "golden_violin" resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF -/obj/item/instrument/piano_synth - name = "synthesizer" - desc = "An advanced electronic synthesizer that can be used as various instruments." - icon_state = "synth" - inhand_icon_state = "synth" - allowed_instrument_ids = "piano" - -/obj/item/instrument/piano_synth/Initialize(mapload) - . = ..() - song.allowed_instrument_ids = SSinstruments.synthesizer_instrument_ids - -/obj/item/instrument/piano_synth/headphones - name = "headphones" - desc = "Unce unce unce unce. Boop!" - icon = 'icons/obj/clothing/accessories.dmi' - lefthand_file = 'icons/mob/inhands/clothing_lefthand.dmi' - righthand_file = 'icons/mob/inhands/clothing_righthand.dmi' - icon_state = "headphones" - inhand_icon_state = "headphones" - slot_flags = ITEM_SLOT_EARS | ITEM_SLOT_HEAD - force = 0 - w_class = WEIGHT_CLASS_SMALL - custom_price = PAYCHECK_ASSISTANT * 2.5 - instrument_range = 1 - -/obj/item/instrument/piano_synth/headphones/ComponentInitialize() - . = ..() - AddElement(/datum/element/update_icon_updates_onmob) - RegisterSignal(src, COMSIG_SONG_START, .proc/start_playing) - RegisterSignal(src, COMSIG_SONG_END, .proc/stop_playing) - -/** - * Called by a component signal when our song starts playing. - */ -/obj/item/instrument/piano_synth/headphones/proc/start_playing() - SIGNAL_HANDLER - icon_state = "[initial(icon_state)]_on" - update_appearance() - -/** - * Called by a component signal when our song stops playing. - */ -/obj/item/instrument/piano_synth/headphones/proc/stop_playing() - SIGNAL_HANDLER - icon_state = "[initial(icon_state)]" - update_appearance() - -/obj/item/instrument/piano_synth/headphones/spacepods - name = "\improper Nanotrasen space pods" - desc = "Flex your money, AND ignore what everyone else says, all at once!" - icon_state = "spacepods" - inhand_icon_state = "spacepods" - slot_flags = ITEM_SLOT_EARS - strip_delay = 100 //air pods don't fall out - instrument_range = 0 //you're paying for quality here - custom_premium_price = PAYCHECK_ASSISTANT * 36 //Save up 5 shifts worth of pay just to lose it down a drainpipe on the sidewalk - /obj/item/instrument/banjo name = "banjo" desc = "A 'Mura' brand banjo. It's pretty much just a drum with a neck and strings." diff --git a/code/modules/instruments/piano_synth.dm b/code/modules/instruments/piano_synth.dm new file mode 100644 index 00000000000..90748d0f18f --- /dev/null +++ b/code/modules/instruments/piano_synth.dm @@ -0,0 +1,191 @@ + +/obj/item/instrument/piano_synth + name = "synthesizer" + desc = "An advanced electronic synthesizer that can be used as various instruments." + icon_state = "synth" + inhand_icon_state = "synth" + allowed_instrument_ids = "piano" + var/circuit_type = /obj/item/circuit_component/synth + var/shell_capacity = SHELL_CAPACITY_SMALL + +/obj/item/instrument/piano_synth/Initialize(mapload) + . = ..() + song.allowed_instrument_ids = SSinstruments.synthesizer_instrument_ids + AddComponent(/datum/component/shell, list(new circuit_type), shell_capacity) + +/obj/item/instrument/piano_synth/headphones + name = "headphones" + desc = "Unce unce unce unce. Boop!" + icon = 'icons/obj/clothing/accessories.dmi' + lefthand_file = 'icons/mob/inhands/clothing_lefthand.dmi' + righthand_file = 'icons/mob/inhands/clothing_righthand.dmi' + icon_state = "headphones" + inhand_icon_state = "headphones" + slot_flags = ITEM_SLOT_EARS | ITEM_SLOT_HEAD + force = 0 + w_class = WEIGHT_CLASS_SMALL + custom_price = PAYCHECK_ASSISTANT * 2.5 + instrument_range = 1 + circuit_type = /obj/item/circuit_component/synth/headphones + shell_capacity = SHELL_CAPACITY_TINY + +/obj/item/instrument/piano_synth/headphones/ComponentInitialize() + . = ..() + AddElement(/datum/element/update_icon_updates_onmob) + RegisterSignal(src, COMSIG_SONG_START, .proc/start_playing) + RegisterSignal(src, COMSIG_SONG_END, .proc/stop_playing) + +/** + * Called by a component signal when our song starts playing. + */ +/obj/item/instrument/piano_synth/headphones/proc/start_playing() + SIGNAL_HANDLER + icon_state = "[initial(icon_state)]_on" + update_appearance() + +/** + * Called by a component signal when our song stops playing. + */ +/obj/item/instrument/piano_synth/headphones/proc/stop_playing() + SIGNAL_HANDLER + icon_state = "[initial(icon_state)]" + update_appearance() + +/obj/item/instrument/piano_synth/headphones/spacepods + name = "\improper Nanotrasen space pods" + desc = "Flex your money, AND ignore what everyone else says, all at once!" + icon_state = "spacepods" + inhand_icon_state = "spacepods" + slot_flags = ITEM_SLOT_EARS + strip_delay = 100 //air pods don't fall out + instrument_range = 0 //you're paying for quality here + custom_premium_price = PAYCHECK_ASSISTANT * 36 //Save up 5 shifts worth of pay just to lose it down a drainpipe on the sidewalk + +/obj/item/circuit_component/synth + display_name = "Synthesizer" + desc = "An advanced electronic synthesizer that can be used as various instruments." + + /// The song, represented in latin alphabet A to G, that'll be played when play is triggered. + var/datum/port/input/song + /// Starts playing the song. + var/datum/port/input/play + /// Stop playing the song. + var/datum/port/input/stop + /// How many times the song will be played. + var/datum/port/input/repetitions + /// The beats per minute of the song + var/datum/port/input/beats_per_min + /// The volume of the song + var/datum/port/input/volume + /// Notes with volume below this threshold will be dead + var/datum/port/input/volume_dropoff + /// Note shift + var/datum/port/input/note_shift + /// Sustain Mode + var/datum/port/input/sustain_mode + /// The value of the above + var/datum/port/input/sustain_value + /// If set the last held note will decay + var/datum/port/input/note_decay + /// The list of instruments which sound can be synthesized. + var/datum/port/input/option/selected_instrument + /// Whether a song is currently playing + var/datum/port/output/is_playing + /// Sent when a new song has started playing + var/datum/port/output/started_playing + /// Sent when a song has finished playing + var/datum/port/output/stopped_playing + + /// The synthesizer this circut is attached to. + var/obj/item/instrument/piano_synth/synth + +/obj/item/circuit_component/synth/populate_ports() + song = add_input_port("Song", PORT_TYPE_LIST(PORT_TYPE_STRING), trigger = .proc/import_song) + play = add_input_port("Play", PORT_TYPE_SIGNAL, trigger = .proc/start_playing) + stop = add_input_port("Stop", PORT_TYPE_SIGNAL, trigger = .proc/stop_playing) + repetitions = add_input_port("Repetitions", PORT_TYPE_NUMBER, trigger = .proc/set_repetitions) + beats_per_min = add_input_port("BPM", PORT_TYPE_NUMBER, trigger = .proc/set_bpm) + selected_instrument = add_option_port("Selected Instrument", SSinstruments.synthesizer_instrument_ids, trigger = .proc/set_instrument) + volume = add_input_port("Volume", PORT_TYPE_NUMBER, trigger = .proc/set_volume) + volume_dropoff = add_input_port("Volume Dropoff Threshold", PORT_TYPE_NUMBER, trigger = .proc/set_dropoff) + note_shift = add_input_port("Note Shift", PORT_TYPE_NUMBER, trigger = .proc/set_note_shift) + sustain_mode = add_option_port("Note Sustain Mode", SSinstruments.note_sustain_modes, trigger = .proc/set_sustain_mode) + sustain_value = add_input_port("Note Sustain Value", PORT_TYPE_NUMBER, trigger = .proc/set_sustain_value) + note_decay = add_input_port("Held Note Decay", PORT_TYPE_NUMBER, trigger = .proc/set_sustain_decay) + + is_playing = add_output_port("Currently Playing", PORT_TYPE_NUMBER) + started_playing = add_output_port("Started Playing", PORT_TYPE_SIGNAL) + stopped_playing = add_output_port("Stopped Playing", PORT_TYPE_SIGNAL) + +/obj/item/circuit_component/synth/register_shell(atom/movable/shell) + . = ..() + synth = shell + RegisterSignal(synth, COMSIG_SONG_START, .proc/on_song_start) + RegisterSignal(synth, COMSIG_SONG_END, .proc/on_song_end) + RegisterSignal(synth, COMSIG_SONG_SHOULD_STOP_PLAYING, .proc/continue_if_autoplaying) + +/obj/item/circuit_component/synth/unregister_shell(atom/movable/shell) + if(synth.song.music_player == src) + synth.song.stop_playing() + synth = null + UnregisterSignal(synth, list(COMSIG_SONG_START, COMSIG_SONG_END, COMSIG_SONG_SHOULD_STOP_PLAYING)) + return ..() + +/obj/item/circuit_component/synth/proc/start_playing(datum/port/input/port) + synth.song.start_playing(src) + +/obj/item/circuit_component/synth/proc/on_song_start() + SIGNAL_HANDLER + is_playing.set_output(TRUE) + started_playing.set_output(COMPONENT_SIGNAL) + +/obj/item/circuit_component/synth/proc/continue_if_autoplaying(datum/source, atom/music_player) + SIGNAL_HANDLER + if(music_player == src) + return IGNORE_INSTRUMENT_CHECKS + +/obj/item/circuit_component/synth/proc/stop_playing(datum/port/input/port) + synth.song.stop_playing() + +/obj/item/circuit_component/synth/proc/on_song_end() + SIGNAL_HANDLER + is_playing.set_output(FALSE) + stopped_playing.set_output(COMPONENT_SIGNAL) + +/obj/item/circuit_component/synth/proc/import_song() + synth.song.ParseSong(song.value) + +/obj/item/circuit_component/synth/proc/set_repetitions() + synth.song.set_repeats(repetitions.value) + +/obj/item/circuit_component/synth/proc/set_bpm() + synth.song.sanitize_tempo(BPM_TO_TEMPO_SETTING(beats_per_min.value)) + +/obj/item/circuit_component/synth/proc/set_instrument() + synth.song.set_instrument(selected_instrument.value) + +/obj/item/circuit_component/synth/proc/set_volume() + synth.song.set_volume(volume.value) + +/obj/item/circuit_component/synth/proc/set_dropoff() + synth.song.set_dropoff_volume(volume_dropoff.value) + +/obj/item/circuit_component/synth/proc/set_note_shift() + synth.song.note_shift = clamp(note_shift.value, synth.song.note_shift_min, synth.song.note_shift_max) + +/obj/item/circuit_component/synth/proc/set_sustain_mode() + synth.song.sustain_mode = SSinstruments.note_sustain_modes[sustain_mode.value] + +/obj/item/circuit_component/synth/proc/set_sustain_value() + switch(synth.song.sustain_mode) + if(SUSTAIN_LINEAR) + synth.song.set_linear_falloff_duration(sustain_value.value) + if(SUSTAIN_EXPONENTIAL) + synth.song.set_exponential_drop_rate(sustain_value.value) + +/obj/item/circuit_component/synth/proc/set_sustain_decay() + synth.song.full_sustain_held_note = !!synth.song.full_sustain_held_note + +/obj/item/circuit_component/synth/headphones + display_name = "Headphones" + desc = "An advanced electronic device that plays music into your ears." diff --git a/code/modules/instruments/songs/_song.dm b/code/modules/instruments/songs/_song.dm index afe19090ff4..35408eaa35d 100644 --- a/code/modules/instruments/songs/_song.dm +++ b/code/modules/instruments/songs/_song.dm @@ -74,8 +74,8 @@ var/list/channels_playing = list() /// List of channels that aren't being used, as text. This is to prevent unnecessary freeing and reallocations from SSsounds/SSinstruments. var/list/channels_idle = list() - /// Person playing us - var/mob/user_playing + /// Who or what's playing us + var/atom/music_player ////////////////////////////////////////////////////// /// Last world.time we checked for who can hear us @@ -197,7 +197,7 @@ /** * Attempts to start playing our song. */ -/datum/song/proc/start_playing(mob/user) +/datum/song/proc/start_playing(atom/user) if(playing) return if(!using_instrument?.ready()) @@ -208,7 +208,6 @@ to_chat(user, span_warning("Song is empty.")) return playing = TRUE - updateDialog(user_playing) //we can not afford to runtime, since we are going to be doing sound channel reservations and if we runtime it means we have a channel allocation leak. //wrap the rest of the stuff to ensure stop_playing() is called. do_hearcheck() @@ -216,7 +215,9 @@ elapsed_delay = 0 delay_by = 0 current_chord = 1 - user_playing = user + music_player = user + if(ismob(music_player)) + updateDialog(music_player) START_PROCESSING(SSinstruments, src) /** @@ -232,13 +233,13 @@ SEND_SIGNAL(parent, COMSIG_SONG_END) terminate_all_sounds(TRUE) hearing_mobs.len = 0 - user_playing = null + music_player = null /** * Processes our song. */ /datum/song/proc/process_song(wait) - if(!length(compiled_chords) || should_stop_playing(user_playing)) + if(!length(compiled_chords) || should_stop_playing(music_player) == STOP_PLAYING) stop_playing() return var/list/chord = compiled_chords[current_chord] @@ -276,13 +277,26 @@ /datum/song/proc/play_chord(list/chord) // last value is timing information for(var/i in 1 to (length(chord) - 1)) - legacy? playkey_legacy(chord[i][1], chord[i][2], chord[i][3], user_playing) : playkey_synth(chord[i], user_playing) + legacy? playkey_legacy(chord[i][1], chord[i][2], chord[i][3], music_player) : playkey_synth(chord[i], music_player) /** * Checks if we should halt playback. */ -/datum/song/proc/should_stop_playing(mob/user) - return QDELETED(parent) || !using_instrument || !playing +/datum/song/proc/should_stop_playing(atom/player) + if(QDELETED(player) || !using_instrument || !playing) + return STOP_PLAYING + return SEND_SIGNAL(parent, COMSIG_SONG_SHOULD_STOP_PLAYING, player) + +/// Sets and sanitizes the repeats variable. +/datum/song/proc/set_repeats(new_repeats_value) + if(playing) + return //So that people cant keep adding to repeat. If the do it intentionally, it could result in the server crashing. + repeat = round(new_repeats_value) + if(repeat < 0) + repeat = 0 + if(repeat > max_repeats) + repeat = max_repeats + /** * Sanitizes tempo to a value that makes sense and fits the current world.tick_lag. @@ -332,7 +346,7 @@ * Setter for setting output volume. */ /datum/song/proc/set_volume(volume) - src.volume = clamp(volume, max(0, min_volume), min(100, max_volume)) + src.volume = clamp(round(volume, 1), max(0, min_volume), min(100, max_volume)) update_sustain() updateDialog() @@ -340,7 +354,7 @@ * Setter for setting how low the volume has to get before a note is considered "dead" and dropped */ /datum/song/proc/set_dropoff_volume(volume) - sustain_dropoff_volume = clamp(volume, INSTRUMENT_MIN_SUSTAIN_DROPOFF, 100) + sustain_dropoff_volume = clamp(round(volume, 0.01), INSTRUMENT_MIN_SUSTAIN_DROPOFF, 100) update_sustain() updateDialog() @@ -348,7 +362,7 @@ * Setter for setting exponential falloff factor. */ /datum/song/proc/set_exponential_drop_rate(drop) - sustain_exponential_dropoff = clamp(drop, INSTRUMENT_EXP_FALLOFF_MIN, INSTRUMENT_EXP_FALLOFF_MAX) + sustain_exponential_dropoff = clamp(round(drop, 0.00001), INSTRUMENT_EXP_FALLOFF_MIN, INSTRUMENT_EXP_FALLOFF_MAX) update_sustain() updateDialog() @@ -356,7 +370,7 @@ * Setter for setting linear falloff duration. */ /datum/song/proc/set_linear_falloff_duration(duration) - sustain_linear_duration = clamp(duration, 0.1, INSTRUMENT_MAX_TOTAL_SUSTAIN) + sustain_linear_duration = clamp(round(duration * 10, world.tick_lag), 0.1, INSTRUMENT_MAX_TOTAL_SUSTAIN) update_sustain() updateDialog() @@ -379,12 +393,12 @@ /datum/song/handheld/updateDialog(mob/user) parent.ui_interact(user || usr) -/datum/song/handheld/should_stop_playing(mob/user) +/datum/song/handheld/should_stop_playing(atom/player) . = ..() - if(.) - return TRUE + if(. == STOP_PLAYING || . == IGNORE_INSTRUMENT_CHECKS) + return var/obj/item/instrument/I = parent - return I.should_stop_playing(user) + return I.should_stop_playing(player) // subtype for stationary structures, like pianos /datum/song/stationary @@ -392,9 +406,9 @@ /datum/song/stationary/updateDialog(mob/user) parent.ui_interact(user || usr) -/datum/song/stationary/should_stop_playing(mob/user) +/datum/song/stationary/should_stop_playing(atom/player) . = ..() - if(.) + if(. == STOP_PLAYING || . == IGNORE_INSTRUMENT_CHECKS) return TRUE var/obj/structure/musician/M = parent - return M.should_stop_playing(user) + return M.should_stop_playing(player) diff --git a/code/modules/instruments/songs/editor.dm b/code/modules/instruments/songs/editor.dm index c2ff449c614..78c8ab5df00 100644 --- a/code/modules/instruments/songs/editor.dm +++ b/code/modules/instruments/songs/editor.dm @@ -90,15 +90,15 @@ /** * Parses a song the user has input into lines and stores them. */ -/datum/song/proc/ParseSong(text) +/datum/song/proc/ParseSong(new_song) set waitfor = FALSE //split into lines - lines = splittext(text, "\n") + lines = islist(new_song) ? new_song : splittext(new_song, "\n") if(lines.len) var/bpm_string = "BPM: " if(findtext(lines[1], bpm_string, 1, length(bpm_string) + 1)) var/divisor = text2num(copytext(lines[1], length(bpm_string) + 1)) || 120 // default - tempo = sanitize_tempo(600 / round(divisor, 1)) + tempo = sanitize_tempo(BPM_TO_TEMPO_SETTING(divisor)) lines.Cut(1, 2) else tempo = sanitize_tempo(5) // default 120 BPM @@ -148,13 +148,7 @@ editing = text2num(href_list["edit"]) - 1 if(href_list["repeat"]) //Changing this from a toggle to a number of repeats to avoid infinite loops. - if(playing) - return //So that people cant keep adding to repeat. If the do it intentionally, it could result in the server crashing. - repeat += round(text2num(href_list["repeat"])) - if(repeat < 0) - repeat = 0 - if(repeat > max_repeats) - repeat = max_repeats + set_repeats(repeat + text2num(href_list["repeat"])) else if(href_list["tempo"]) tempo = sanitize_tempo(tempo + text2num(href_list["tempo"])) @@ -193,22 +187,22 @@ else if(href_list["setlinearfalloff"]) var/amount = input(usr, "Set linear sustain duration in seconds", "Linear Sustain Duration") as null|num if(!isnull(amount)) - set_linear_falloff_duration(round(amount * 10, world.tick_lag)) + set_linear_falloff_duration(amount) else if(href_list["setexpfalloff"]) var/amount = input(usr, "Set exponential sustain factor", "Exponential sustain factor") as null|num if(!isnull(amount)) - set_exponential_drop_rate(round(amount, 0.00001)) + set_exponential_drop_rate(amount) else if(href_list["setvolume"]) var/amount = input(usr, "Set volume", "Volume") as null|num if(!isnull(amount)) - set_volume(round(amount, 1)) + set_volume(amount) else if(href_list["setdropoffvolume"]) var/amount = input(usr, "Set dropoff threshold", "Dropoff Threshold Volume") as null|num if(!isnull(amount)) - set_dropoff_volume(round(amount, 0.01)) + set_dropoff_volume(amount) else if(href_list["switchinstrument"]) if(!length(allowed_instrument_ids)) @@ -238,12 +232,9 @@ note_shift = clamp(amount, note_shift_min, note_shift_max) else if(href_list["setsustainmode"]) - var/choice = input(usr, "Choose a sustain mode", "Sustain Mode") as null|anything in list("Linear", "Exponential") - switch(choice) - if("Linear") - sustain_mode = SUSTAIN_LINEAR - if("Exponential") - sustain_mode = SUSTAIN_EXPONENTIAL + var/choice = input(usr, "Choose a sustain mode", "Sustain Mode") as null|anything in SSinstruments.note_sustain_modes + if(choice) + sustain_mode = SSinstruments.note_sustain_modes[choice] else if(href_list["togglesustainhold"]) full_sustain_held_note = !full_sustain_held_note diff --git a/code/modules/instruments/songs/play_legacy.dm b/code/modules/instruments/songs/play_legacy.dm index bcd9935ab4c..c3664aca3be 100644 --- a/code/modules/instruments/songs/play_legacy.dm +++ b/code/modules/instruments/songs/play_legacy.dm @@ -46,7 +46,7 @@ * * acc is either "b", "n", or "#" * * oct is 1-8 (or 9 for C) */ -/datum/song/proc/playkey_legacy(note, acc as text, oct, mob/user) +/datum/song/proc/playkey_legacy(note, acc as text, oct, atom/player) // handle accidental -> B<>C of E<>F if(acc == "b" && (note == 3 || note == 6)) // C or F if(note == 3) @@ -82,7 +82,7 @@ var/sound/music_played = sound(soundfile) for(var/i in hearing_mobs) var/mob/M = i - if(user && HAS_TRAIT(user, TRAIT_MUSICIAN) && isliving(M)) + if(player && HAS_TRAIT(player, TRAIT_MUSICIAN) && isliving(M)) var/mob/living/L = M L.apply_status_effect(STATUS_EFFECT_GOOD_MUSIC) if(!(M?.client?.prefs?.toggles & SOUND_INSTRUMENTS)) diff --git a/code/modules/instruments/songs/play_synthesized.dm b/code/modules/instruments/songs/play_synthesized.dm index 649368b61ba..12062fc5db3 100644 --- a/code/modules/instruments/songs/play_synthesized.dm +++ b/code/modules/instruments/songs/play_synthesized.dm @@ -42,7 +42,7 @@ * Plays a specific numerical key from our instrument to anyone who can hear us. * Does a hearing check if enough time has passed. */ -/datum/song/proc/playkey_synth(key, mob/user) +/datum/song/proc/playkey_synth(key, atom/player) if(can_noteshift) key = clamp(key + note_shift, key_min, key_max) if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck) @@ -62,7 +62,7 @@ last_channel_played = channel_text for(var/i in hearing_mobs) var/mob/M = i - if(user && HAS_TRAIT(user, TRAIT_MUSICIAN) && isliving(M)) + if(player && HAS_TRAIT(player, TRAIT_MUSICIAN) && isliving(M)) var/mob/living/L = M L.apply_status_effect(STATUS_EFFECT_GOOD_MUSIC) if(!(M?.client?.prefs?.toggles & SOUND_INSTRUMENTS)) diff --git a/code/modules/instruments/stationary.dm b/code/modules/instruments/stationary.dm index 41d2c0e7284..97fe44d1dfd 100644 --- a/code/modules/instruments/stationary.dm +++ b/code/modules/instruments/stationary.dm @@ -15,12 +15,12 @@ QDEL_NULL(song) return ..() -/obj/structure/musician/proc/should_stop_playing(mob/user) - if(!(anchored || can_play_unanchored)) - return TRUE - if(!user) - return FALSE - return !user.canUseTopic(src, FALSE, TRUE, FALSE, FALSE) //can play with TK and while resting because fun. +/obj/structure/musician/proc/should_stop_playing(atom/music_player) + if(!(anchored || can_play_unanchored) || !ismob(music_player)) + return STOP_PLAYING + var/mob/user = music_player + if(!user.canUseTopic(src, FALSE, TRUE, FALSE, FALSE)) //can play with TK and while resting because fun. + return STOP_PLAYING /obj/structure/musician/ui_interact(mob/user) . = ..() diff --git a/tgstation.dme b/tgstation.dme index f3fae2101be..ceda498dbce 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -2649,6 +2649,7 @@ #include "code\modules\hydroponics\grown\weeds\nettle.dm" #include "code\modules\hydroponics\grown\weeds\starthistle.dm" #include "code\modules\instruments\items.dm" +#include "code\modules\instruments\piano_synth.dm" #include "code\modules\instruments\stationary.dm" #include "code\modules\instruments\instrument_data\_instrument_data.dm" #include "code\modules\instruments\instrument_data\_instrument_key.dm"