mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-09 16:12:17 +00:00
[MIRROR] Instrument Update (#11645)
Co-authored-by: Selis <12716288+ItsSelis@users.noreply.github.com> Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
19b8044f61
commit
d8408a2c59
@@ -2,5 +2,8 @@
|
||||
// When the signal is called: (signal arguments)
|
||||
// All signals send the source datum of the signal as the first argument
|
||||
|
||||
///when an atom starts playing a song datum (datum/song)
|
||||
#define COMSIG_ATOM_STARTING_INSTRUMENT "atom_starting_instrument"
|
||||
|
||||
///When the transform or an atom is varedited through vv topic.
|
||||
#define COMSIG_ATOM_VV_MODIFY_TRANSFORM "atom_vv_modify_transform"
|
||||
|
||||
12
code/__defines/dcs/signals/signals_music.dm
Normal file
12
code/__defines/dcs/signals/signals_music.dm
Normal file
@@ -0,0 +1,12 @@
|
||||
// /datum/song signals
|
||||
|
||||
///sent to the instrument when a song starts playing: (datum/starting_song, atom/player)
|
||||
#define COMSIG_INSTRUMENT_START "instrument_start"
|
||||
///sent to the instrument when a song stops playing
|
||||
#define COMSIG_INSTRUMENT_END "instrument_end"
|
||||
///sent to the instrument on /should_stop_playing(): (atom/player). Return values can be found in DEFINES/song.dm
|
||||
#define COMSIG_INSTRUMENT_SHOULD_STOP_PLAYING "instrument_should_stop_playing"
|
||||
///sent to the instrument (and player if available) when a song repeats (datum/song)
|
||||
#define COMSIG_INSTRUMENT_REPEAT "instrument_repeat"
|
||||
///sent to the instrument when tempo changes, skipped on new. (datum/song)
|
||||
#define COMSIG_INSTRUMENT_TEMPO_CHANGE "instrument_tempo_change"
|
||||
@@ -6,6 +6,8 @@
|
||||
/// Max number of playing notes per instrument.
|
||||
#define CHANNELS_PER_INSTRUMENT 128
|
||||
|
||||
/// Minimum length a note should ever go for
|
||||
#define INSTRUMENT_MIN_TOTAL_SUSTAIN 0.1
|
||||
/// Maximum length a note should ever go for
|
||||
#define INSTRUMENT_MAX_TOTAL_SUSTAIN (5 SECONDS)
|
||||
|
||||
@@ -16,8 +18,8 @@
|
||||
/// Minimum volume for when the sound is considered dead.
|
||||
#define INSTRUMENT_MIN_SUSTAIN_DROPOFF 0
|
||||
|
||||
#define SUSTAIN_LINEAR 1
|
||||
#define SUSTAIN_EXPONENTIAL 2
|
||||
#define SUSTAIN_LINEAR "Linear"
|
||||
#define SUSTAIN_EXPONENTIAL "Exponential"
|
||||
|
||||
// /datum/instrument instrument_flags
|
||||
#define INSTRUMENT_LEGACY (1<<0) //Legacy instrument. Implies INSTRUMENT_DO_NOT_AUTOSAMPLE
|
||||
|
||||
17
code/__defines/song.dm
Normal file
17
code/__defines/song.dm
Normal file
@@ -0,0 +1,17 @@
|
||||
#define MUSICIAN_HEARCHECK_MINDELAY 4
|
||||
#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"
|
||||
///song played by the mook bard
|
||||
#define MOOK_SONG "BPM: 240\nA5,B5,C#6,D6,E6/0.17,A/0.5,A/0.25,A3/0.25\nA4/0.25,C#5/0.25,E5/0.25,A/0.25,C#/0.25,E/0.12\nC#6/0.25,C#/0.25,E6/0.25,A3/0.25,A4/0.25\nC#5/0.25,E5/0.25,A/0.25,C#/0.25,E/0.25,D/0.25\nG6/0.25,D/0.17,F6/0.17,C#6/0.5,E6/0.5,D4/0.25\nA/0.25,D5/0.25,F5/0.25,A/0.25,D/0.25,F/0.25\nD6/0.08,F6/0.08,D4/0.25,A/0.25,D5/0.25,F5/0.25\nCn4/0.2,B/0.17,D6/0.17,G5/0.5,G/0.25,B3/0.25\nD4/0.25,G4/0.25,B4/0.25,D/0.25,G/0.25,B/0.12\nB5/0.25,B/0.25,D6/0.25,G3/0.25,G4/0.25,B4/0.25\nF/0.25,G/0.25,B/0.25,F/0.25,D/0.25,F6/0.25\nC6/0.17,E/0.17,B5/0.5,D#/0.5,C4/0.25,G/0.25\nC5/0.25,E5/0.25,G/0.25,C/0.25,E/0.25,C6/0.08\nE6/0.08,C4/0.25,Dn4/0.25,E4/0.25,A5/0.17,B/0.5\nC6/0.25,F5/0.08,F4/0.08,C5/0.08,E5/0.12,G5/0.12\nC6/0.25,E6/0.25,E4/0.08,C5/0.08,B/0.17,F6/0.17\nE6/0.5,B/0.25,E4/0.08,G#4/0.08,C6/0.17,D6/0.5\nE6/0.25,A3/0.25,E4/0.25,C5/0.25,Gn3/0.25\nF5/0.12,A5/0.12,A6/0.25,F3/0.25,F4/0.12,A4/0.12\nC/0.12,F6/0.17,A6/0.17,G#6/0.5,A/0.25,F3/0.25\nF4/0.12,A4/0.12,D#5/0.12,B/0.17,G#/0.17,B6/0.5\nB5/0.25,G#/0.25,E3/0.25,E4/0.12,G#4/0.12\nDn/0.12,E6/0.08,E3/0.25,F#3/0.25,G#3/0.25\nE5/0.17,A5/0.17,E/0.5,E/0.25,A3/0.25,C#4/0.25\nE4/0.25,C#/0.25,E/0.12,A5/0.5,B/0.5,C#6/0.5\nD6/0.5,A3/0.25,C#4/0.25,E/0.25,C#/0.25,E/0.25\nE6/0.08,Gn/0.25,E4/0.25,A4/0.25,C#5/0.25,E/0.25\nA/0.25,C#/0.25,E6/0.17,E/0.5,Fn6/0.5,G6/0.5\nG3/0.25,E4/0.25,A/0.25,C#/0.25,E/0.25,A/0.25\nC#/0.25,F/0.08,A6/0.08,F3/0.25,F4/0.25,A4/0.25\nCn/0.25,F/0.25,A/0.25,C/0.25,G6/0.12,A6/0.12\nG A G F6 G3/0.25 D4/0.25 G4/0.25 B4/0.25 D/0.25\nG/0.25 B/0.25 E6/0.12 G6/0.12 F/0.71 G/0.71 F/0.71\nE3/0.25 E4/0.25 G4/0.25 B/0.25 E/0.25 G/0.25 B/0.25\nA5/0.08 E6/0.08 A3/0.25 E4/0.25 A4/0.25 C#/0.25 E/0.25 A/0.25 C#/0.25 D6/0.17 E6/0.5 F/0.25 B3/0.25 D4/0.12 F4/0.12 B4/0.12 F6/0.25 E/0.25 D6/0.25 G#3/0.25 E4/0.12 G#4/0.12 B/0.12 Cn6/0.12 D/0.25 A3/0.25 A4/0.25 C5/0.25 E5/0.25 G#3/0.25 Gn/0.25 C4/0.25 E4/0.25 A/"
|
||||
@@ -15,6 +15,10 @@
|
||||
/// Removes everything enclose in < and > inclusive of the bracket, and limits the length of the message.
|
||||
#define STRIP_HTML_FULL(text, limit) (GLOB.html_tags.Replace(copytext(text, 1, limit), ""))
|
||||
|
||||
/// BYOND's string procs don't support being used on datum references (as in it doesn't look for a name for stringification)
|
||||
/// We just use this macro to ensure that we will only pass strings to this BYOND-level function without developers needing to really worry about it.
|
||||
#define LOWER_TEXT(thing) lowertext(UNLINT("[thing]"))
|
||||
|
||||
/// Removes characters incompatible with file names.
|
||||
#define SANITIZE_FILENAME(text) (GLOB.filename_forbidden_chars.Replace(text, ""))
|
||||
|
||||
|
||||
@@ -601,6 +601,113 @@ GLOBAL_LIST_EMPTY(text_tag_cache)
|
||||
else
|
||||
return trim(html_encode(user_input), max_length)
|
||||
|
||||
#define NO_CHARS_DETECTED 0
|
||||
#define SPACES_DETECTED 1
|
||||
#define SYMBOLS_DETECTED 2
|
||||
#define NUMBERS_DETECTED 3
|
||||
#define LETTERS_DETECTED 4
|
||||
|
||||
/**
|
||||
* Filters out undesirable characters from names.
|
||||
*
|
||||
* * strict - return null immediately instead of filtering out
|
||||
* * allow_numbers - allows numbers and common special characters - used for silicon/other weird things names
|
||||
* * cap_after_symbols - words like Bob's will be capitalized to Bob'S by default. False is good for titles.
|
||||
*/
|
||||
/proc/reject_bad_name(t_in, allow_numbers = FALSE, max_length = MAX_NAME_LEN, ascii_only = TRUE, strict = FALSE, cap_after_symbols = TRUE)
|
||||
if(!t_in)
|
||||
return //Rejects the input if it is null
|
||||
|
||||
var/number_of_alphanumeric = 0
|
||||
var/last_char_group = NO_CHARS_DETECTED
|
||||
var/t_out = ""
|
||||
var/t_len = length(t_in)
|
||||
var/charcount = 0
|
||||
var/char = ""
|
||||
|
||||
// This is a sanity short circuit, if the users name is three times the maximum allowable length of name
|
||||
// We bail out on trying to process the name at all, as it could be a bug or malicious input and we don't
|
||||
// Want to iterate all of it.
|
||||
if(t_len > 3 * MAX_NAME_LEN)
|
||||
return
|
||||
for(var/i = 1, i <= t_len, i += length(char))
|
||||
char = t_in[i]
|
||||
switch(text2ascii(char))
|
||||
|
||||
// A .. Z
|
||||
if(65 to 90) //Uppercase Letters
|
||||
number_of_alphanumeric++
|
||||
last_char_group = LETTERS_DETECTED
|
||||
|
||||
// a .. z
|
||||
if(97 to 122) //Lowercase Letters
|
||||
if(last_char_group == NO_CHARS_DETECTED || last_char_group == SPACES_DETECTED || cap_after_symbols && last_char_group == SYMBOLS_DETECTED) //start of a word
|
||||
char = uppertext(char)
|
||||
number_of_alphanumeric++
|
||||
last_char_group = LETTERS_DETECTED
|
||||
|
||||
// 0 .. 9
|
||||
if(48 to 57) //Numbers
|
||||
if(!allow_numbers) //allow name to start with number if AI/Borg
|
||||
if(strict)
|
||||
return
|
||||
continue
|
||||
number_of_alphanumeric++
|
||||
last_char_group = NUMBERS_DETECTED
|
||||
// ' - .
|
||||
if(39,45,46) //Common name punctuation
|
||||
if(last_char_group == NO_CHARS_DETECTED)
|
||||
if(strict)
|
||||
return
|
||||
continue
|
||||
last_char_group = SYMBOLS_DETECTED
|
||||
|
||||
// ~ | @ : # $ % & * +
|
||||
if(126,124,64,58,35,36,37,38,42,43) //Other symbols that we'll allow (mainly for AI)
|
||||
if(last_char_group == NO_CHARS_DETECTED || !allow_numbers) //suppress at start of string
|
||||
if(strict)
|
||||
return
|
||||
continue
|
||||
last_char_group = SYMBOLS_DETECTED
|
||||
|
||||
//Space
|
||||
if(32)
|
||||
if(last_char_group == NO_CHARS_DETECTED || last_char_group == SPACES_DETECTED) //suppress double-spaces and spaces at start of string
|
||||
if(strict)
|
||||
return
|
||||
continue
|
||||
last_char_group = SPACES_DETECTED
|
||||
|
||||
if(127 to INFINITY)
|
||||
if(ascii_only)
|
||||
if(strict)
|
||||
return
|
||||
continue
|
||||
last_char_group = SYMBOLS_DETECTED //for now, we'll treat all non-ascii characters like symbols even though most are letters
|
||||
|
||||
else
|
||||
continue
|
||||
t_out += char
|
||||
charcount++
|
||||
if(charcount >= max_length)
|
||||
break
|
||||
|
||||
if(number_of_alphanumeric < 2)
|
||||
return //protects against tiny names like "A" and also names like "' ' ' ' ' ' ' '"
|
||||
|
||||
if(last_char_group == SPACES_DETECTED)
|
||||
t_out = copytext_char(t_out, 1, -1) //removes the last character (in this case a space)
|
||||
|
||||
//if(!filter_name_ic(t_out)) FIXME
|
||||
// return
|
||||
|
||||
return t_out
|
||||
|
||||
#undef NO_CHARS_DETECTED
|
||||
#undef SPACES_DETECTED
|
||||
#undef NUMBERS_DETECTED
|
||||
#undef LETTERS_DETECTED
|
||||
|
||||
//Adds 'char' ahead of 'text' until there are 'count' characters total
|
||||
/proc/add_leading(text, count, char = " ")
|
||||
text = "[text]"
|
||||
|
||||
@@ -19,6 +19,10 @@ 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(
|
||||
SUSTAIN_LINEAR,
|
||||
SUSTAIN_EXPONENTIAL,
|
||||
)
|
||||
|
||||
/datum/controller/subsystem/processing/instruments/Initialize()
|
||||
initialize_instrument_data()
|
||||
@@ -32,10 +36,11 @@ PROCESSING_SUBSYSTEM_DEF(instruments)
|
||||
songs -= S
|
||||
|
||||
/datum/controller/subsystem/processing/instruments/proc/initialize_instrument_data()
|
||||
for(var/datum/instrument/I as anything in subtypesof(/datum/instrument))
|
||||
if(initial(I.instrument_type) == I)
|
||||
for(var/path in subtypesof(/datum/instrument))
|
||||
var/datum/instrument/I = path
|
||||
if(initial(I.abstract_type) == path)
|
||||
continue
|
||||
I = new I
|
||||
I = new path
|
||||
I.Initialize()
|
||||
if(!I.id)
|
||||
qdel(I)
|
||||
|
||||
@@ -119,7 +119,7 @@
|
||||
|
||||
if(hack_result && in_hack_mode)
|
||||
to_chat(user, span_notice("Your hacking attempt was succesful!"))
|
||||
user.playsound_local(get_turf(src), 'sound/instruments/piano/An6.ogg', 50)
|
||||
user.playsound_local(get_turf(src), 'sound/runtime/instruments/piano/An6.ogg', 50)
|
||||
else
|
||||
to_chat(user, span_warning("Your hacking attempt failed!"))
|
||||
return 0
|
||||
|
||||
@@ -255,3 +255,17 @@
|
||||
|
||||
/datum/preference/numeric/living/jukebox_volume/apply_to_client_updated(client/client, value)
|
||||
client?.media?.update_volume(value)
|
||||
|
||||
/datum/preference/numeric/volume
|
||||
abstract_type = /datum/preference/numeric/volume
|
||||
minimum = 0
|
||||
maximum = 100
|
||||
|
||||
/datum/preference/numeric/volume/create_default_value()
|
||||
return maximum
|
||||
|
||||
/// Controls hearing instruments
|
||||
/datum/preference/numeric/volume/sound_instruments
|
||||
category = PREFERENCE_CATEGORY_GAME_PREFERENCES
|
||||
savefile_key = "sound_instruments"
|
||||
savefile_identifier = PREFERENCE_PLAYER
|
||||
|
||||
@@ -17,14 +17,14 @@
|
||||
* Since songs cache them while playing, there isn't realistic issues regarding performance from accessing.
|
||||
*/
|
||||
/datum/instrument
|
||||
/// Used for categorization subtypes
|
||||
abstract_type = /datum/instrument
|
||||
/// Name of the instrument
|
||||
var/name = "Generic instrument"
|
||||
/// Uniquely identifies this instrument so runtime changes are possible as opposed to paths. If this is unset, things will use path instead.
|
||||
var/id
|
||||
/// Category
|
||||
var/category = "Unsorted"
|
||||
/// Used for categorization subtypes
|
||||
var/instrument_type = /datum/instrument
|
||||
/// Write here however many samples, follow this syntax: "%note num%"='%sample file%' eg. "27"='synthesizer/e2.ogg'. Key must never be lower than 0 and higher than 127
|
||||
var/list/real_samples
|
||||
/// assoc list key = /datum/instrument_key. do not fill this yourself!
|
||||
@@ -70,7 +70,8 @@
|
||||
|
||||
/datum/instrument/Destroy()
|
||||
SSinstruments.instrument_data -= id
|
||||
for(var/datum/song/S as anything in songs_using)
|
||||
for(var/i in songs_using)
|
||||
var/datum/song/S = i
|
||||
S.set_instrument(null)
|
||||
real_samples = null
|
||||
samples = null
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
/datum/instrument/brass
|
||||
name = "Generic brass instrument"
|
||||
category = "Brass"
|
||||
instrument_type = /datum/instrument/brass
|
||||
abstract_type = /datum/instrument/brass
|
||||
|
||||
/datum/instrument/brass/crisis_section
|
||||
name = "Crisis Brass Section"
|
||||
id = "crbrass"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/brass/crisis_brass/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/brass/crisis_brass/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/brass/crisis_brass/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/brass/crisis_brass/c5.ogg')
|
||||
real_samples = list("36"='sound/runtime/instruments/synthesis_samples/brass/crisis_brass/c2.ogg',
|
||||
"48"='sound/runtime/instruments/synthesis_samples/brass/crisis_brass/c3.ogg',
|
||||
"60"='sound/runtime/instruments/synthesis_samples/brass/crisis_brass/c4.ogg',
|
||||
"72"='sound/runtime/instruments/synthesis_samples/brass/crisis_brass/c5.ogg')
|
||||
|
||||
/datum/instrument/brass/crisis_trombone
|
||||
name = "Crisis Trombone"
|
||||
id = "crtrombone"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/brass/crisis_trombone/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/brass/crisis_trombone/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/brass/crisis_trombone/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/brass/crisis_trombone/c5.ogg')
|
||||
real_samples = list("36"='sound/runtime/instruments/synthesis_samples/brass/crisis_trombone/c2.ogg',
|
||||
"48"='sound/runtime/instruments/synthesis_samples/brass/crisis_trombone/c3.ogg',
|
||||
"60"='sound/runtime/instruments/synthesis_samples/brass/crisis_trombone/c4.ogg',
|
||||
"72"='sound/runtime/instruments/synthesis_samples/brass/crisis_trombone/c5.ogg')
|
||||
|
||||
/datum/instrument/brass/crisis_trumpet
|
||||
name = "Crisis Trumpet"
|
||||
id = "crtrumpet"
|
||||
real_samples = list("60"='sound/instruments/synthesis_samples/brass/crisis_trumpet/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/brass/crisis_trumpet/c5.ogg')
|
||||
real_samples = list("60"='sound/runtime/instruments/synthesis_samples/brass/crisis_trumpet/c4.ogg',
|
||||
"72"='sound/runtime/instruments/synthesis_samples/brass/crisis_trumpet/c5.ogg')
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
/datum/instrument/chromatic
|
||||
name = "Generic chromatic percussion instrument"
|
||||
category = "Chromatic percussion"
|
||||
instrument_type = /datum/instrument/chromatic
|
||||
abstract_type = /datum/instrument/chromatic
|
||||
|
||||
/datum/instrument/chromatic/vibraphone1
|
||||
name = "Crisis Vibraphone"
|
||||
id = "crvibr"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/chromatic/vibraphone1/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/chromatic/vibraphone1/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/chromatic/vibraphone1/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/chromatic/vibraphone1/c5.ogg')
|
||||
real_samples = list("36"='sound/runtime/instruments/synthesis_samples/chromatic/vibraphone1/c2.ogg',
|
||||
"48"='sound/runtime/instruments/synthesis_samples/chromatic/vibraphone1/c3.ogg',
|
||||
"60"='sound/runtime/instruments/synthesis_samples/chromatic/vibraphone1/c4.ogg',
|
||||
"72"='sound/runtime/instruments/synthesis_samples/chromatic/vibraphone1/c5.ogg')
|
||||
|
||||
/datum/instrument/chromatic/musicbox1
|
||||
name = "SGM Music Box"
|
||||
id = "sgmmbox"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/chromatic/sgmbox/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/chromatic/sgmbox/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/chromatic/sgmbox/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/chromatic/sgmbox/c5.ogg')
|
||||
real_samples = list("36"='sound/runtime/instruments/synthesis_samples/chromatic/sgmbox/c2.ogg',
|
||||
"48"='sound/runtime/instruments/synthesis_samples/chromatic/sgmbox/c3.ogg',
|
||||
"60"='sound/runtime/instruments/synthesis_samples/chromatic/sgmbox/c4.ogg',
|
||||
"72"='sound/runtime/instruments/synthesis_samples/chromatic/sgmbox/c5.ogg')
|
||||
|
||||
/datum/instrument/chromatic/fluid_celeste
|
||||
name = "FluidR3 Celeste"
|
||||
id = "r3celeste"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c5.ogg',
|
||||
"84"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c6.ogg',
|
||||
"96"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c7.ogg',
|
||||
"108"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c8.ogg')
|
||||
real_samples = list("36"='sound/runtime/instruments/synthesis_samples/chromatic/fluid_celeste/c2.ogg',
|
||||
"48"='sound/runtime/instruments/synthesis_samples/chromatic/fluid_celeste/c3.ogg',
|
||||
"60"='sound/runtime/instruments/synthesis_samples/chromatic/fluid_celeste/c4.ogg',
|
||||
"72"='sound/runtime/instruments/synthesis_samples/chromatic/fluid_celeste/c5.ogg',
|
||||
"84"='sound/runtime/instruments/synthesis_samples/chromatic/fluid_celeste/c6.ogg',
|
||||
"96"='sound/runtime/instruments/synthesis_samples/chromatic/fluid_celeste/c7.ogg',
|
||||
"108"='sound/runtime/instruments/synthesis_samples/chromatic/fluid_celeste/c8.ogg')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/datum/instrument/fun
|
||||
name = "Generic Fun Instrument"
|
||||
category = "Fun"
|
||||
instrument_type = /datum/instrument/fun
|
||||
abstract_type = /datum/instrument/fun
|
||||
|
||||
/datum/instrument/fun/honk
|
||||
name = "!!HONK!!"
|
||||
@@ -18,6 +18,15 @@
|
||||
id = "chime"
|
||||
real_samples = list("79"='sound/machines/chime.ogg')
|
||||
|
||||
/datum/instrument/fun/meowsynth
|
||||
name = "MeowSynth"
|
||||
id = "meowsynth"
|
||||
real_samples = list("36"='sound/runtime/instruments/synthesis_samples/meowsynth/c2.ogg',
|
||||
"48"='sound/runtime/instruments/synthesis_samples/meowsynth/c3.ogg',
|
||||
"60"='sound/runtime/instruments/synthesis_samples/meowsynth/c4.ogg',
|
||||
"72"='sound/runtime/instruments/synthesis_samples/meowsynth/c5.ogg',
|
||||
"84"='sound/runtime/instruments/synthesis_samples/meowsynth/c6.ogg')
|
||||
|
||||
/datum/instrument/fun/mothscream
|
||||
name = "Moth Scream"
|
||||
id = "mothscream"
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
/datum/instrument/guitar
|
||||
name = "Generic guitar-like instrument"
|
||||
category = "Guitar"
|
||||
instrument_type = /datum/instrument/guitar
|
||||
abstract_type = /datum/instrument/guitar
|
||||
|
||||
/datum/instrument/guitar/steel_crisis
|
||||
name = "Crisis Steel String Guitar"
|
||||
id = "csteelgt"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/guitar/crisis_steel/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/guitar/crisis_steel/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/guitar/crisis_steel/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/guitar/crisis_steel/c5.ogg')
|
||||
real_samples = list("36"='sound/runtime/instruments/synthesis_samples/guitar/crisis_steel/c2.ogg',
|
||||
"48"='sound/runtime/instruments/synthesis_samples/guitar/crisis_steel/c3.ogg',
|
||||
"60"='sound/runtime/instruments/synthesis_samples/guitar/crisis_steel/c4.ogg',
|
||||
"72"='sound/runtime/instruments/synthesis_samples/guitar/crisis_steel/c5.ogg')
|
||||
|
||||
/datum/instrument/guitar/nylon_crisis
|
||||
name = "Crisis Nylon String Guitar"
|
||||
id = "cnylongt"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/guitar/crisis_nylon/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/guitar/crisis_nylon/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/guitar/crisis_nylon/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/guitar/crisis_nylon/c5.ogg')
|
||||
real_samples = list("36"='sound/runtime/instruments/synthesis_samples/guitar/crisis_nylon/c2.ogg',
|
||||
"48"='sound/runtime/instruments/synthesis_samples/guitar/crisis_nylon/c3.ogg',
|
||||
"60"='sound/runtime/instruments/synthesis_samples/guitar/crisis_nylon/c4.ogg',
|
||||
"72"='sound/runtime/instruments/synthesis_samples/guitar/crisis_nylon/c5.ogg')
|
||||
|
||||
/datum/instrument/guitar/clean_crisis
|
||||
name = "Crisis Clean Guitar"
|
||||
id = "ccleangt"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/guitar/crisis_clean/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/guitar/crisis_clean/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/guitar/crisis_clean/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/guitar/crisis_clean/c5.ogg')
|
||||
real_samples = list("36"='sound/runtime/instruments/synthesis_samples/guitar/crisis_clean/c2.ogg',
|
||||
"48"='sound/runtime/instruments/synthesis_samples/guitar/crisis_clean/c3.ogg',
|
||||
"60"='sound/runtime/instruments/synthesis_samples/guitar/crisis_clean/c4.ogg',
|
||||
"72"='sound/runtime/instruments/synthesis_samples/guitar/crisis_clean/c5.ogg')
|
||||
|
||||
/datum/instrument/guitar/muted_crisis
|
||||
name = "Crisis Muted Guitar"
|
||||
id = "cmutedgt"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/guitar/crisis_muted/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/guitar/crisis_muted/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/guitar/crisis_muted/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/guitar/crisis_muted/c5.ogg')
|
||||
real_samples = list("36"='sound/runtime/instruments/synthesis_samples/guitar/crisis_muted/c2.ogg',
|
||||
"48"='sound/runtime/instruments/synthesis_samples/guitar/crisis_muted/c3.ogg',
|
||||
"60"='sound/runtime/instruments/synthesis_samples/guitar/crisis_muted/c4.ogg',
|
||||
"72"='sound/runtime/instruments/synthesis_samples/guitar/crisis_muted/c5.ogg')
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//SONGS WILL BE AUTOMATICALLY SWITCHED TO LEGACY MODE IF THEY USE THIS KIND OF INSTRUMENT!
|
||||
//I'd prefer these stayed. They sound different from the mechanical synthesis of synthed instruments, and I quite like them that way. It's not legacy, it's hardcoded, old style. - kevinz000
|
||||
/datum/instrument/hardcoded
|
||||
instrument_type = /datum/instrument/hardcoded
|
||||
abstract_type = /datum/instrument/hardcoded
|
||||
category = "Non-Synthesized"
|
||||
instrument_flags = INSTRUMENT_LEGACY
|
||||
volume_multiplier = 1 //not as loud as synth'd
|
||||
@@ -23,7 +23,7 @@
|
||||
name = "Electric Guitar"
|
||||
id = "eguitar"
|
||||
legacy_instrument_ext = "ogg"
|
||||
legacy_instrument_path = "eguitar"
|
||||
legacy_instrument_path = "electric_guitar"
|
||||
|
||||
/datum/instrument/hardcoded/glockenspiel
|
||||
name = "Glockenspiel"
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
/datum/instrument/organ
|
||||
name = "Generic organ"
|
||||
category = "Organ"
|
||||
instrument_type = /datum/instrument/organ
|
||||
abstract_type = /datum/instrument/organ
|
||||
|
||||
/datum/instrument/organ/crisis_church
|
||||
name = "Crisis Church Organ"
|
||||
id = "crichugan"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_church/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/organ/crisis_church/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/organ/crisis_church/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/organ/crisis_church/c5.ogg')
|
||||
real_samples = list("36"='sound/runtime/instruments/synthesis_samples/organ/crisis_church/c2.ogg',
|
||||
"48"='sound/runtime/instruments/synthesis_samples/organ/crisis_church/c3.ogg',
|
||||
"60"='sound/runtime/instruments/synthesis_samples/organ/crisis_church/c4.ogg',
|
||||
"72"='sound/runtime/instruments/synthesis_samples/organ/crisis_church/c5.ogg')
|
||||
|
||||
/datum/instrument/organ/crisis_hammond
|
||||
name = "Crisis Hammond Organ"
|
||||
id = "crihamgan"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_hammond/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/organ/crisis_hammond/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/organ/crisis_hammond/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/organ/crisis_hammond/c5.ogg')
|
||||
real_samples = list("36"='sound/runtime/instruments/synthesis_samples/organ/crisis_hammond/c2.ogg',
|
||||
"48"='sound/runtime/instruments/synthesis_samples/organ/crisis_hammond/c3.ogg',
|
||||
"60"='sound/runtime/instruments/synthesis_samples/organ/crisis_hammond/c4.ogg',
|
||||
"72"='sound/runtime/instruments/synthesis_samples/organ/crisis_hammond/c5.ogg')
|
||||
|
||||
/datum/instrument/organ/crisis_accordian
|
||||
name = "Crisis Accordian"
|
||||
id = "crack"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_accordian/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/organ/crisis_accordian/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/organ/crisis_accordian/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/organ/crisis_accordian/c5.ogg')
|
||||
real_samples = list("36"='sound/runtime/instruments/synthesis_samples/organ/crisis_accordian/c2.ogg',
|
||||
"48"='sound/runtime/instruments/synthesis_samples/organ/crisis_accordian/c3.ogg',
|
||||
"60"='sound/runtime/instruments/synthesis_samples/organ/crisis_accordian/c4.ogg',
|
||||
"72"='sound/runtime/instruments/synthesis_samples/organ/crisis_accordian/c5.ogg')
|
||||
|
||||
/datum/instrument/organ/crisis_harmonica
|
||||
name = "Crisis Harmonica"
|
||||
id = "crharmony"
|
||||
real_samples = list("48"='sound/instruments/synthesis_samples/organ/crisis_harmonica/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/organ/crisis_harmonica/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/organ/crisis_harmonica/c5.ogg')
|
||||
real_samples = list("48"='sound/runtime/instruments/synthesis_samples/organ/crisis_harmonica/c3.ogg',
|
||||
"60"='sound/runtime/instruments/synthesis_samples/organ/crisis_harmonica/c4.ogg',
|
||||
"72"='sound/runtime/instruments/synthesis_samples/organ/crisis_harmonica/c5.ogg')
|
||||
|
||||
/datum/instrument/organ/crisis_tango_accordian
|
||||
name = "Crisis Tango Accordian"
|
||||
id = "crtango"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c5.ogg')
|
||||
real_samples = list("36"='sound/runtime/instruments/synthesis_samples/organ/crisis_tangaccordian/c2.ogg',
|
||||
"48"='sound/runtime/instruments/synthesis_samples/organ/crisis_tangaccordian/c3.ogg',
|
||||
"60"='sound/runtime/instruments/synthesis_samples/organ/crisis_tangaccordian/c4.ogg',
|
||||
"72"='sound/runtime/instruments/synthesis_samples/organ/crisis_tangaccordian/c5.ogg')
|
||||
|
||||
@@ -1,56 +1,56 @@
|
||||
/datum/instrument/piano
|
||||
name = "Generic piano"
|
||||
category = "Piano"
|
||||
instrument_type = /datum/instrument/piano
|
||||
abstract_type = /datum/instrument/piano
|
||||
|
||||
/datum/instrument/piano/fluid_piano
|
||||
name = "FluidR3 Grand Piano"
|
||||
id = "r3grand"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/piano/fluid_piano/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/piano/fluid_piano/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/piano/fluid_piano/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/piano/fluid_piano/c5.ogg',
|
||||
"84"='sound/instruments/synthesis_samples/piano/fluid_piano/c6.ogg',
|
||||
"96"='sound/instruments/synthesis_samples/piano/fluid_piano/c7.ogg',
|
||||
"108"='sound/instruments/synthesis_samples/piano/fluid_piano/c8.ogg')
|
||||
real_samples = list("36"='sound/runtime/instruments/synthesis_samples/piano/fluid_piano/c2.ogg',
|
||||
"48"='sound/runtime/instruments/synthesis_samples/piano/fluid_piano/c3.ogg',
|
||||
"60"='sound/runtime/instruments/synthesis_samples/piano/fluid_piano/c4.ogg',
|
||||
"72"='sound/runtime/instruments/synthesis_samples/piano/fluid_piano/c5.ogg',
|
||||
"84"='sound/runtime/instruments/synthesis_samples/piano/fluid_piano/c6.ogg',
|
||||
"96"='sound/runtime/instruments/synthesis_samples/piano/fluid_piano/c7.ogg',
|
||||
"108"='sound/runtime/instruments/synthesis_samples/piano/fluid_piano/c8.ogg')
|
||||
|
||||
/datum/instrument/piano/fluid_harpsichord
|
||||
name = "FluidR3 Harpsichord"
|
||||
id = "r3harpsi"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c5.ogg',
|
||||
"84"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c6.ogg',
|
||||
"96"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c7.ogg',
|
||||
"108"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c8.ogg')
|
||||
real_samples = list("36"='sound/runtime/instruments/synthesis_samples/piano/fluid_harpsi/c2.ogg',
|
||||
"48"='sound/runtime/instruments/synthesis_samples/piano/fluid_harpsi/c3.ogg',
|
||||
"60"='sound/runtime/instruments/synthesis_samples/piano/fluid_harpsi/c4.ogg',
|
||||
"72"='sound/runtime/instruments/synthesis_samples/piano/fluid_harpsi/c5.ogg',
|
||||
"84"='sound/runtime/instruments/synthesis_samples/piano/fluid_harpsi/c6.ogg',
|
||||
"96"='sound/runtime/instruments/synthesis_samples/piano/fluid_harpsi/c7.ogg',
|
||||
"108"='sound/runtime/instruments/synthesis_samples/piano/fluid_harpsi/c8.ogg')
|
||||
|
||||
/datum/instrument/piano/crisis_harpsichord
|
||||
name = "Crisis Harpsichord"
|
||||
id = "crharpsi"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/piano/crisis_harpsichord/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/piano/crisis_harpsichord/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/piano/crisis_harpsichord/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/piano/crisis_harpsichord/c5.ogg')
|
||||
real_samples = list("36"='sound/runtime/instruments/synthesis_samples/piano/crisis_harpsichord/c2.ogg',
|
||||
"48"='sound/runtime/instruments/synthesis_samples/piano/crisis_harpsichord/c3.ogg',
|
||||
"60"='sound/runtime/instruments/synthesis_samples/piano/crisis_harpsichord/c4.ogg',
|
||||
"72"='sound/runtime/instruments/synthesis_samples/piano/crisis_harpsichord/c5.ogg')
|
||||
|
||||
/datum/instrument/piano/crisis_grandpiano_uni
|
||||
name = "Crisis Grand Piano One"
|
||||
id = "crgrand1"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c5.ogg',
|
||||
"84"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c6.ogg',
|
||||
"96"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c7.ogg',
|
||||
"108"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c8.ogg')
|
||||
real_samples = list("36"='sound/runtime/instruments/synthesis_samples/piano/crisis_grand_piano/c2.ogg',
|
||||
"48"='sound/runtime/instruments/synthesis_samples/piano/crisis_grand_piano/c3.ogg',
|
||||
"60"='sound/runtime/instruments/synthesis_samples/piano/crisis_grand_piano/c4.ogg',
|
||||
"72"='sound/runtime/instruments/synthesis_samples/piano/crisis_grand_piano/c5.ogg',
|
||||
"84"='sound/runtime/instruments/synthesis_samples/piano/crisis_grand_piano/c6.ogg',
|
||||
"96"='sound/runtime/instruments/synthesis_samples/piano/crisis_grand_piano/c7.ogg',
|
||||
"108"='sound/runtime/instruments/synthesis_samples/piano/crisis_grand_piano/c8.ogg')
|
||||
|
||||
/datum/instrument/piano/crisis_brightpiano_uni
|
||||
name = "Crisis Bright Piano One"
|
||||
id = "crbright1"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c5.ogg',
|
||||
"84"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c6.ogg',
|
||||
"96"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c7.ogg',
|
||||
"108"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c8.ogg')
|
||||
real_samples = list("36"='sound/runtime/instruments/synthesis_samples/piano/crisis_bright_piano/c2.ogg',
|
||||
"48"='sound/runtime/instruments/synthesis_samples/piano/crisis_bright_piano/c3.ogg',
|
||||
"60"='sound/runtime/instruments/synthesis_samples/piano/crisis_bright_piano/c4.ogg',
|
||||
"72"='sound/runtime/instruments/synthesis_samples/piano/crisis_bright_piano/c5.ogg',
|
||||
"84"='sound/runtime/instruments/synthesis_samples/piano/crisis_bright_piano/c6.ogg',
|
||||
"96"='sound/runtime/instruments/synthesis_samples/piano/crisis_bright_piano/c7.ogg',
|
||||
"108"='sound/runtime/instruments/synthesis_samples/piano/crisis_bright_piano/c8.ogg')
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/datum/instrument/tones
|
||||
name = "Ideal tone"
|
||||
category = "Tones"
|
||||
instrument_type = /datum/instrument/tones
|
||||
abstract_type = /datum/instrument/tones
|
||||
|
||||
/datum/instrument/tones/square_wave
|
||||
name = "Ideal square wave"
|
||||
id = "square"
|
||||
real_samples = list("81"='sound/instruments/synthesis_samples/tones/Square.ogg')
|
||||
real_samples = list("81"='sound/runtime/instruments/synthesis_samples/tones/Square.ogg')
|
||||
|
||||
/datum/instrument/tones/sine_wave
|
||||
name = "Ideal sine wave"
|
||||
id = "sine"
|
||||
real_samples = list("81"='sound/instruments/synthesis_samples/tones/Sine.ogg')
|
||||
real_samples = list("81"='sound/runtime/instruments/synthesis_samples/tones/Sine.ogg')
|
||||
|
||||
/datum/instrument/tones/saw_wave
|
||||
name = "Ideal sawtooth wave"
|
||||
id = "saw"
|
||||
real_samples = list("81"='sound/instruments/synthesis_samples/tones/Sawtooth.ogg')
|
||||
real_samples = list("81"='sound/runtime/instruments/synthesis_samples/tones/Sawtooth.ogg')
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
slot_l_hand_str = 'icons/mob/items/lefthand_instruments.dmi',
|
||||
slot_r_hand_str = 'icons/mob/items/righthand_instruments.dmi',
|
||||
)
|
||||
|
||||
abstract_type = /obj/item/instrument
|
||||
/// Our song datum.
|
||||
var/datum/song/handheld/song
|
||||
/// Our allowed list of instrument ids. This is nulled on initialize.
|
||||
@@ -20,27 +20,30 @@
|
||||
/obj/item/instrument/Initialize(mapload)
|
||||
. = ..()
|
||||
song = new(src, allowed_instrument_ids, instrument_range)
|
||||
allowed_instrument_ids = null //We don't need this clogging memory after it's used.
|
||||
allowed_instrument_ids = null //We don't need this clogging memory after its used.
|
||||
|
||||
/obj/item/instrument/Destroy()
|
||||
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/can_play(atom/music_player)
|
||||
if(!ismob(music_player))
|
||||
return FALSE
|
||||
var/mob/user = music_player
|
||||
if(user.incapacitated())
|
||||
return FALSE
|
||||
if(!Adjacent(user))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/item/instrument/attack_self(mob/user)
|
||||
if(!user.IsAdvancedToolUser())
|
||||
to_chat(user, span_warning("You don't have the dexterity to do this!"))
|
||||
return TRUE
|
||||
interact(user)
|
||||
|
||||
/obj/item/instrument/interact(mob/living/user)
|
||||
if(!isliving(user) || user.incapacitated())
|
||||
/obj/item/instrument/attack_self(mob/M)
|
||||
if(!M.IsAdvancedToolUser())
|
||||
return
|
||||
|
||||
user.set_machine(src)
|
||||
song.interact(user)
|
||||
tgui_interact(M)
|
||||
|
||||
/obj/item/instrument/tgui_interact(mob/user, datum/tgui/ui)
|
||||
return song.tgui_interact(user)
|
||||
|
||||
/obj/item/instrument/violin
|
||||
name = "space violin"
|
||||
@@ -60,67 +63,6 @@
|
||||
icon_state = "xylophone"
|
||||
allowed_instrument_ids = "xylophone"
|
||||
|
||||
/obj/item/instrument/piano_synth
|
||||
name = "synthesizer"
|
||||
desc = "An advanced electronic synthesizer that can be used as various instruments."
|
||||
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_state = "headphones"
|
||||
slot_flags = SLOT_EARS | SLOT_HEAD
|
||||
force = 0
|
||||
w_class = ITEMSIZE_SMALL
|
||||
instrument_range = 1
|
||||
|
||||
/obj/item/instrument/piano_synth/headphones/Initialize(mapload)
|
||||
. = ..()
|
||||
RegisterSignal(src, COMSIG_SONG_START, PROC_REF(start_playing))
|
||||
RegisterSignal(src, COMSIG_SONG_END, PROC_REF(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"
|
||||
if(ishuman(loc))
|
||||
var/mob/living/carbon/human/H = loc
|
||||
if(H.l_ear == src || H.r_ear == src)
|
||||
H.update_inv_ears()
|
||||
else if(H.head == src)
|
||||
H.update_inv_head()
|
||||
|
||||
/**
|
||||
* 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)]"
|
||||
if(ishuman(loc))
|
||||
var/mob/living/carbon/human/H = loc
|
||||
if(H.l_ear == src || H.r_ear == src)
|
||||
H.update_inv_ears()
|
||||
else if(H.head == src)
|
||||
H.update_inv_head()
|
||||
|
||||
|
||||
/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"
|
||||
slot_flags = SLOT_EARS
|
||||
//strip_delay = 100 //air pods don't fall out
|
||||
instrument_range = 0 //you're paying for quality here
|
||||
|
||||
/obj/item/instrument/banjo
|
||||
name = "banjo"
|
||||
desc = "A 'Mura' brand banjo. It's pretty much just a drum with a neck and strings."
|
||||
@@ -177,7 +119,7 @@
|
||||
AddComponent(/datum/component/spooky)
|
||||
*/
|
||||
/obj/item/instrument/trumpet/spectral/attack(mob/living/carbon/C, mob/user)
|
||||
playsound (src, 'sound/instruments/trombone/En4.mid', 100,1,-1)
|
||||
playsound (src, 'sound/runtime/instruments/trombone/En4.mid', 100,1,-1)
|
||||
..()
|
||||
|
||||
/obj/item/instrument/saxophone
|
||||
@@ -200,7 +142,7 @@
|
||||
*/
|
||||
|
||||
/obj/item/instrument/saxophone/spectral/attack(mob/living/carbon/C, mob/user)
|
||||
playsound (src, 'sound/instruments/saxophone/En4.mid', 100,1,-1)
|
||||
playsound (src, 'sound/runtime/instruments/saxophone/En4.mid', 100,1,-1)
|
||||
..()
|
||||
|
||||
/obj/item/instrument/trombone
|
||||
@@ -223,7 +165,7 @@
|
||||
*/
|
||||
|
||||
/obj/item/instrument/trombone/spectral/attack(mob/living/carbon/C, mob/user)
|
||||
playsound (src, 'sound/instruments/trombone/Cn4.mid', 100,1,-1)
|
||||
playsound (src, 'sound/runtime/instruments/trombone/Cn4.mid', 100,1,-1)
|
||||
..()
|
||||
|
||||
/obj/item/instrument/recorder
|
||||
|
||||
60
code/modules/instruments/piano_synth.dm
Normal file
60
code/modules/instruments/piano_synth.dm
Normal file
@@ -0,0 +1,60 @@
|
||||
/obj/item/instrument/piano_synth
|
||||
name = "synthesizer"
|
||||
desc = "An advanced electronic synthesizer that can be used as various instruments."
|
||||
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_state = "headphones"
|
||||
slot_flags = SLOT_EARS | SLOT_HEAD
|
||||
force = 0
|
||||
w_class = ITEMSIZE_SMALL
|
||||
instrument_range = 1
|
||||
|
||||
/obj/item/instrument/piano_synth/headphones/Initialize(mapload)
|
||||
. = ..()
|
||||
RegisterSignal(src, COMSIG_SONG_START, PROC_REF(start_playing))
|
||||
RegisterSignal(src, COMSIG_SONG_END, PROC_REF(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"
|
||||
if(ishuman(loc))
|
||||
var/mob/living/carbon/human/H = loc
|
||||
if(H.l_ear == src || H.r_ear == src)
|
||||
H.update_inv_ears()
|
||||
else if(H.head == src)
|
||||
H.update_inv_head()
|
||||
|
||||
/**
|
||||
* 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)]"
|
||||
if(ishuman(loc))
|
||||
var/mob/living/carbon/human/H = loc
|
||||
if(H.l_ear == src || H.r_ear == src)
|
||||
H.update_inv_ears()
|
||||
else if(H.head == src)
|
||||
H.update_inv_head()
|
||||
|
||||
|
||||
/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"
|
||||
slot_flags = SLOT_EARS
|
||||
//strip_delay = 100 //air pods don't fall out
|
||||
instrument_range = 0 //you're paying for quality here
|
||||
@@ -1,7 +1,3 @@
|
||||
#define MUSICIAN_HEARCHECK_MINDELAY 4
|
||||
#define MUSIC_MAXLINES 1000
|
||||
#define MUSIC_MAXLINECHARS 300
|
||||
|
||||
/**
|
||||
* # Song datum
|
||||
*
|
||||
@@ -12,6 +8,9 @@
|
||||
/// Name of the song
|
||||
var/name = "Untitled"
|
||||
|
||||
/// ID for syncing songs together
|
||||
var/id = ""
|
||||
|
||||
/// The atom we're attached to/playing from
|
||||
var/atom/parent
|
||||
|
||||
@@ -27,11 +26,6 @@
|
||||
/// Are we currently playing?
|
||||
var/playing = FALSE
|
||||
|
||||
/// Are we currently editing?
|
||||
var/editing = TRUE
|
||||
/// Is the help screen open?
|
||||
var/help = FALSE
|
||||
|
||||
/// Repeats left
|
||||
var/repeat = 0
|
||||
/// Maximum times we can repeat
|
||||
@@ -78,8 +72,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
|
||||
@@ -111,7 +105,6 @@
|
||||
var/note_shift = 0
|
||||
var/note_shift_min = -100
|
||||
var/note_shift_max = 100
|
||||
var/can_noteshift = TRUE
|
||||
/// The kind of sustain we're using
|
||||
var/sustain_mode = SUSTAIN_LINEAR
|
||||
/// When a note is considered dead if it is below this in volume
|
||||
@@ -130,10 +123,10 @@
|
||||
/datum/song/New(atom/parent, list/instrument_ids, new_range)
|
||||
SSinstruments.on_song_new(src)
|
||||
lines = list()
|
||||
tempo = sanitize_tempo(tempo)
|
||||
tempo = sanitize_tempo(tempo, TRUE)
|
||||
src.parent = parent
|
||||
if(instrument_ids)
|
||||
allowed_instrument_ids = islist(instrument_ids)? instrument_ids : list(instrument_ids)
|
||||
allowed_instrument_ids = islist(instrument_ids) ? instrument_ids : list(instrument_ids)
|
||||
if(length(allowed_instrument_ids))
|
||||
set_instrument(allowed_instrument_ids[1])
|
||||
hearing_mobs = list()
|
||||
@@ -161,6 +154,8 @@
|
||||
var/list/old = hearing_mobs.Copy()
|
||||
hearing_mobs.len = 0
|
||||
var/turf/source = get_turf(parent)
|
||||
// FIXME
|
||||
// for(var/mob/M in get_hearers_in_view(instrument_range, source))
|
||||
var/list/in_range = get_mobs_and_objs_in_view_fast(source, instrument_range, remote_ghosts = FALSE)
|
||||
for(var/mob/M in in_range["mobs"])
|
||||
hearing_mobs[M] = get_dist(M, source)
|
||||
@@ -202,64 +197,98 @@
|
||||
/**
|
||||
* 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())
|
||||
to_chat(user, span_warning("An error has occurred with [src]. Please reset the instrument."))
|
||||
to_chat(user, span_warning("An error has occured with [src]. Please reset the instrument."))
|
||||
return
|
||||
compile_chords()
|
||||
if(!length(compiled_chords))
|
||||
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()
|
||||
SEND_SIGNAL(parent, COMSIG_SONG_START)
|
||||
SEND_SIGNAL(parent, COMSIG_INSTRUMENT_START, src, user)
|
||||
SEND_SIGNAL(user, COMSIG_ATOM_STARTING_INSTRUMENT, src)
|
||||
elapsed_delay = 0
|
||||
delay_by = 0
|
||||
current_chord = 1
|
||||
user_playing = user
|
||||
music_player = user
|
||||
START_PROCESSING(SSinstruments, src)
|
||||
if(id)
|
||||
sync_play()
|
||||
|
||||
/**
|
||||
* Attempts to find other instruments with the same ID and syncs them to our song.
|
||||
*/
|
||||
/datum/song/proc/sync_play()
|
||||
for(var/datum/song/other_instrument as anything in SSinstruments.songs)
|
||||
if(other_instrument == src || other_instrument.id != id)
|
||||
continue
|
||||
if(other_instrument.playing)
|
||||
continue
|
||||
var/atom/other_player = other_instrument.find_sync_player()
|
||||
if(isnull(other_player) || !(other_player in view(get_turf(parent))))
|
||||
continue
|
||||
// copies the main song info to target songs
|
||||
other_instrument.lines = lines.Copy()
|
||||
other_instrument.max_repeats = max_repeats
|
||||
other_instrument.tempo = tempo
|
||||
other_instrument.start_playing(other_player)
|
||||
|
||||
/**
|
||||
* Finds a player which would reasonably be able to play this song.
|
||||
*/
|
||||
/datum/song/proc/find_sync_player()
|
||||
return null
|
||||
|
||||
/**
|
||||
* Stops playing, terminating all sounds if in synthesized mode. Clears hearing_mobs.
|
||||
*
|
||||
* Arguments:
|
||||
* * finished: boolean, whether the song ended via reaching the end.
|
||||
*/
|
||||
/datum/song/proc/stop_playing()
|
||||
/datum/song/proc/stop_playing(finished = FALSE)
|
||||
if(!playing)
|
||||
return
|
||||
playing = FALSE
|
||||
if(!debug_mode)
|
||||
compiled_chords = null
|
||||
STOP_PROCESSING(SSinstruments, src)
|
||||
SEND_SIGNAL(parent, COMSIG_SONG_END)
|
||||
SEND_SIGNAL(parent, COMSIG_INSTRUMENT_END, finished)
|
||||
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))
|
||||
stop_playing()
|
||||
if(!length(compiled_chords))
|
||||
stop_playing(TRUE)
|
||||
return
|
||||
if(should_stop_playing(music_player) == STOP_PLAYING)
|
||||
stop_playing(FALSE)
|
||||
return
|
||||
var/list/chord = compiled_chords[current_chord]
|
||||
if(++elapsed_delay >= delay_by)
|
||||
play_chord(chord)
|
||||
elapsed_delay = 0
|
||||
delay_by = tempodiv_to_delay(chord[length(chord)])
|
||||
current_chord++
|
||||
if(current_chord > length(compiled_chords))
|
||||
if(repeat)
|
||||
repeat--
|
||||
current_chord = 1
|
||||
return
|
||||
else
|
||||
stop_playing()
|
||||
return
|
||||
elapsed_delay++
|
||||
if(elapsed_delay < delay_by)
|
||||
return
|
||||
play_chord(chord)
|
||||
elapsed_delay = 0
|
||||
delay_by = tempodiv_to_delay(chord[length(chord)])
|
||||
current_chord++
|
||||
if(current_chord <= length(compiled_chords))
|
||||
return
|
||||
if(!repeat)
|
||||
stop_playing(TRUE)
|
||||
return
|
||||
repeat--
|
||||
current_chord = 1
|
||||
SEND_SIGNAL(parent, COMSIG_INSTRUMENT_REPEAT, TRUE)
|
||||
|
||||
/**
|
||||
* Converts a tempodiv to ticks to elapse before playing the next chord, taking into account our tempo.
|
||||
@@ -281,19 +310,34 @@
|
||||
/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_INSTRUMENT_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.
|
||||
*/
|
||||
/datum/song/proc/sanitize_tempo(new_tempo)
|
||||
/datum/song/proc/sanitize_tempo(new_tempo, initializing = FALSE)
|
||||
new_tempo = abs(new_tempo)
|
||||
if(!initializing) // not only is it not helpful while initializing but it will runtime really hard since nothing is set up
|
||||
SEND_SIGNAL(parent, COMSIG_INSTRUMENT_TEMPO_CHANGE, src)
|
||||
return clamp(round(new_tempo, world.tick_lag), world.tick_lag, 5 SECONDS)
|
||||
|
||||
/**
|
||||
@@ -308,12 +352,6 @@
|
||||
/datum/song/proc/set_bpm(bpm)
|
||||
tempo = sanitize_tempo(600 / bpm)
|
||||
|
||||
/**
|
||||
* Updates the window for our users. Override down the line.
|
||||
*/
|
||||
/datum/song/proc/updateDialog(mob/user)
|
||||
interact(user)
|
||||
|
||||
/datum/song/process(wait)
|
||||
if(!playing)
|
||||
return PROCESS_KILL
|
||||
@@ -337,33 +375,29 @@
|
||||
* 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()
|
||||
|
||||
/**
|
||||
* 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()
|
||||
|
||||
/**
|
||||
* 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()
|
||||
|
||||
/**
|
||||
* 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), world.tick_lag, INSTRUMENT_MAX_TOTAL_SUSTAIN)
|
||||
update_sustain()
|
||||
updateDialog()
|
||||
|
||||
/datum/song/vv_edit_var(var_name, var_value)
|
||||
. = ..()
|
||||
@@ -381,25 +415,34 @@
|
||||
// subtype for handheld instruments, like violin
|
||||
/datum/song/handheld
|
||||
|
||||
/datum/song/handheld/updateDialog(mob/user)
|
||||
parent.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.can_play(player) ? NONE : STOP_PLAYING
|
||||
|
||||
/datum/song/handheld/find_sync_player()
|
||||
var/obj/item/instrument/instrument = parent
|
||||
var/mob/living/player = get(parent, /mob/living)
|
||||
if(instrument.can_play(player))
|
||||
return player
|
||||
return null
|
||||
|
||||
// subtype for stationary structures, like pianos
|
||||
/datum/song/stationary
|
||||
|
||||
/datum/song/stationary/updateDialog(mob/user)
|
||||
parent.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.can_play(player) ? NONE : STOP_PLAYING
|
||||
|
||||
/datum/song/stationary/find_sync_player()
|
||||
var/obj/structure/musician/piano = parent
|
||||
for(var/mob/living/player in view(parent, 1))
|
||||
if(piano.can_play(player))
|
||||
return player
|
||||
|
||||
return null
|
||||
|
||||
@@ -1,251 +1,216 @@
|
||||
/**
|
||||
* Returns the HTML for the status UI for this song datum.
|
||||
*/
|
||||
/datum/song/proc/instrument_status_ui()
|
||||
. = list()
|
||||
. += "<div class='statusDisplay'>"
|
||||
. += span_bold("<a href='byond://?src=[REF(src)];switchinstrument=1'>Current instrument</a>:") + " "
|
||||
if(!using_instrument)
|
||||
. += span_danger("No instrument loaded!") + "<br>"
|
||||
else
|
||||
. += "[using_instrument.name]<br>"
|
||||
. += "Playback Settings:<br>"
|
||||
if(can_noteshift)
|
||||
. += "<a href='byond://?src=[REF(src)];setnoteshift=1'>Note Shift/Note Transpose</a>: [note_shift] keys / [round(note_shift / 12, 0.01)] octaves<br>"
|
||||
var/smt
|
||||
var/modetext = ""
|
||||
/datum/song/tgui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if (!ui)
|
||||
ui = new(user, src, "InstrumentEditor", parent.name)
|
||||
ui.open()
|
||||
|
||||
/datum/song/tgui_host(mob/user)
|
||||
return parent
|
||||
|
||||
/datum/song/tgui_data(mob/user)
|
||||
var/list/data = ..()
|
||||
data["id"] = id
|
||||
data["using_instrument"] = using_instrument?.name || "No instrument loaded!"
|
||||
data["note_shift"] = note_shift
|
||||
data["octaves"] = round(note_shift / 12, 0.01)
|
||||
data["sustain_mode"] = sustain_mode
|
||||
switch(sustain_mode)
|
||||
if(SUSTAIN_LINEAR)
|
||||
smt = "Linear"
|
||||
modetext = "<a href='byond://?src=[REF(src)];setlinearfalloff=1'>Linear Sustain Duration</a>: [sustain_linear_duration / 10] seconds<br>"
|
||||
data["sustain_mode_button"] = "Linear Sustain Duration (in seconds)"
|
||||
data["sustain_mode_duration"] = sustain_linear_duration / 10
|
||||
data["sustain_mode_min"] = INSTRUMENT_MIN_TOTAL_SUSTAIN
|
||||
data["sustain_mode_max"] = INSTRUMENT_MAX_TOTAL_SUSTAIN
|
||||
if(SUSTAIN_EXPONENTIAL)
|
||||
smt = "Exponential"
|
||||
modetext = "<a href='byond://?src=[REF(src)];setexpfalloff=1'>Exponential Falloff Factor</a>: [sustain_exponential_dropoff]% per decisecond<br>"
|
||||
. += "<a href='byond://?src=[REF(src)];setsustainmode=1'>Sustain Mode</a>: [smt]<br>"
|
||||
. += modetext
|
||||
. += using_instrument?.ready()? ("Status: " + span_green("Ready") + "<br>") : ("Status: " + span_red("!Instrument Definition Error!") + "<br>")
|
||||
. += "Instrument Type: [legacy? "Legacy" : "Synthesized"]<br>"
|
||||
. += "<a href='byond://?src=[REF(src)];setvolume=1'>Volume</a>: [volume]<br>"
|
||||
. += "<a href='byond://?src=[REF(src)];setdropoffvolume=1'>Volume Dropoff Threshold</a>: [sustain_dropoff_volume]<br>"
|
||||
. += "<a href='byond://?src=[REF(src)];togglesustainhold=1'>Sustain indefinitely last held note</a>: [full_sustain_held_note? "Enabled" : "Disabled"].<br>"
|
||||
. += "</div>"
|
||||
data["sustain_mode_button"] = "Exponential Falloff Factor (% per decisecond)"
|
||||
data["sustain_mode_duration"] = sustain_exponential_dropoff
|
||||
data["sustain_mode_min"] = INSTRUMENT_EXP_FALLOFF_MIN
|
||||
data["sustain_mode_max"] = INSTRUMENT_EXP_FALLOFF_MAX
|
||||
data["instrument_ready"] = using_instrument?.ready()
|
||||
data["volume"] = volume
|
||||
data["volume_dropoff_threshold"] = sustain_dropoff_volume
|
||||
data["sustain_indefinitely"] = full_sustain_held_note
|
||||
data["playing"] = playing
|
||||
data["repeat"] = repeat
|
||||
data["bpm"] = round(60 SECONDS / tempo)
|
||||
data["lines"] = list()
|
||||
var/linecount
|
||||
for(var/line in lines)
|
||||
linecount++
|
||||
data["lines"] += list(list(
|
||||
"line_count" = linecount,
|
||||
"line_text" = line,
|
||||
))
|
||||
return data
|
||||
|
||||
/datum/song/proc/interact(mob/user)
|
||||
var/list/dat = list()
|
||||
/datum/song/tgui_static_data(mob/user)
|
||||
var/list/data = ..()
|
||||
data["can_switch_instrument"] = (length(allowed_instrument_ids) > 1)
|
||||
data["possible_instruments"] = list()
|
||||
for(var/instrument in allowed_instrument_ids)
|
||||
UNTYPED_LIST_ADD(data["possible_instruments"], list("name" = SSinstruments.instrument_data[instrument], "id" = instrument))
|
||||
data["sustain_modes"] = SSinstruments.note_sustain_modes
|
||||
data["max_repeats"] = max_repeats
|
||||
data["min_volume"] = min_volume
|
||||
data["max_volume"] = max_volume
|
||||
data["note_shift_min"] = note_shift_min
|
||||
data["note_shift_max"] = note_shift_max
|
||||
data["max_line_chars"] = MUSIC_MAXLINECHARS
|
||||
data["max_lines"] = MUSIC_MAXLINES
|
||||
return data
|
||||
|
||||
dat += instrument_status_ui()
|
||||
/datum/song/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
. = ..()
|
||||
var/mob/user = ui.user
|
||||
if(!istype(user))
|
||||
return FALSE
|
||||
|
||||
if(lines.len > 0)
|
||||
dat += "<H3>Playback</H3>"
|
||||
if(!playing)
|
||||
dat += "<A href='byond://?src=[REF(src)];play=1'>Play</A> " + span_linkOn("Stop") + "<BR><BR>"
|
||||
dat += "Repeat Song: "
|
||||
dat += repeat > 0 ? "<A href='byond://?src=[REF(src)];repeat=-10'>-</A><A href='byond://?src=[REF(src)];repeat=-1'>-</A>" : (span_linkOff("-") + span_linkOff("-"))
|
||||
dat += " [repeat] times "
|
||||
dat += repeat < max_repeats ? "<A href='byond://?src=[REF(src)];repeat=1'>+</A><A href='byond://?src=[REF(src)];repeat=10'>+</A>" : (span_linkOff("+") + span_linkOff("+"))
|
||||
dat += "<BR>"
|
||||
else
|
||||
dat += span_linkOn("Play") + " <A href='byond://?src=[REF(src)];stop=1'>Stop</A><BR>"
|
||||
dat += "Repeats left: " + span_bold("[repeat]") + "<BR>"
|
||||
if(!editing)
|
||||
dat += "<BR>" + span_bold("<A href='byond://?src=[REF(src)];edit=2'>Show Editor</A>") + "<BR>"
|
||||
else
|
||||
dat += "<H3>Editing</H3>"
|
||||
dat += span_bold("<A href='byond://?src=[REF(src)];edit=1'>Hide Editor</A>")
|
||||
dat += " <A href='byond://?src=[REF(src)];newsong=1'>Start a New Song</A>"
|
||||
dat += " <A href='byond://?src=[REF(src)];import=1'>Import a Song</A><BR><BR>"
|
||||
var/bpm = round(600 / tempo)
|
||||
dat += "Tempo: <A href='byond://?src=[REF(src)];tempo=[world.tick_lag]'>-</A> [bpm] BPM <A href='byond://?src=[REF(src)];tempo=-[world.tick_lag]'>+</A><BR><BR>"
|
||||
var/linecount = 0
|
||||
for(var/line in lines)
|
||||
linecount += 1
|
||||
dat += "Line [linecount]: <A href='byond://?src=[REF(src)];modifyline=[linecount]'>Edit</A> <A href='byond://?src=[REF(src)];deleteline=[linecount]'>X</A> [line]<BR>"
|
||||
dat += "<A href='byond://?src=[REF(src)];newline=1'>Add Line</A><BR><BR>"
|
||||
if(help)
|
||||
dat += span_bold("<A href='byond://?src=[REF(src)];help=1'>Hide Help</A>") + "<BR>"
|
||||
dat += {"
|
||||
Lines are a series of chords, separated by commas (,), each with notes separated by hyphens (-).<br>
|
||||
Every note in a chord will play together, with chord timed by the tempo.<br>
|
||||
<br>
|
||||
Notes are played by the names of the note, and optionally, the accidental, and/or the octave number.<br>
|
||||
By default, every note is natural and in octave 3. Defining otherwise is remembered for each note.<br>
|
||||
Example: <i>C,D,E,F,G,A,B</i> will play a C major scale.<br>
|
||||
After a note has an accidental placed, it will be remembered: <i>C,C4,C,C3</i> is <i>C3,C4,C4,C3</i><br>
|
||||
Chords can be played simply by separating each note with a hyphen: <i>A-C#,Cn-E,E-G#,Gn-B</i><br>
|
||||
A pause may be denoted by an empty chord: <i>C,E,,C,G</i><br>
|
||||
To make a chord be a different time, end it with /x, where the chord length will be length<br>
|
||||
defined by tempo / x: <i>C,G/2,E/4</i><br>
|
||||
Combined, an example is: <i>E-E4/4,F#/2,G#/8,B/8,E3-E4/4</i>
|
||||
<br>
|
||||
Lines may be up to [MUSIC_MAXLINECHARS] characters.<br>
|
||||
A song may only contain up to [MUSIC_MAXLINES] lines.<br>
|
||||
"}
|
||||
else
|
||||
dat += span_bold("<A href='byond://?src=[REF(src)];help=2'>Show Help</A>") + "<BR>"
|
||||
switch(action)
|
||||
//SETTINGS
|
||||
if("play_music")
|
||||
if(!playing)
|
||||
INVOKE_ASYNC(src, PROC_REF(start_playing), user)
|
||||
else
|
||||
stop_playing()
|
||||
return TRUE
|
||||
if("set_instrument_id")
|
||||
var/new_id = reject_bad_name(LOWER_TEXT(params["id"]), max_length = 20, allow_numbers = TRUE, cap_after_symbols = FALSE)
|
||||
if(new_id)
|
||||
id = new_id
|
||||
return TRUE
|
||||
if("change_instrument")
|
||||
var/new_instrument = params["new_instrument"]
|
||||
//only one instrument, so no need to bother changing it.
|
||||
if(!length(allowed_instrument_ids))
|
||||
return FALSE
|
||||
if(!(new_instrument in allowed_instrument_ids))
|
||||
return FALSE
|
||||
set_instrument(new_instrument)
|
||||
return TRUE
|
||||
if("tempo")
|
||||
var/move_direction = params["tempo_change"]
|
||||
var/tempo_diff
|
||||
if(move_direction == "increase_speed")
|
||||
tempo_diff = world.tick_lag
|
||||
else
|
||||
tempo_diff = -world.tick_lag
|
||||
tempo = sanitize_tempo(tempo + tempo_diff)
|
||||
return TRUE
|
||||
|
||||
var/datum/browser/popup = new(user, "instrument", parent?.name || "instrument", 700, 500)
|
||||
popup.set_content(dat.Join(""))
|
||||
popup.open()
|
||||
//SONG MAKING
|
||||
if("import_song")
|
||||
var/song_text = ""
|
||||
do
|
||||
song_text = tgui_input_text(user, "Please paste the entire song, formatted:", name, max_length = (MUSIC_MAXLINES * MUSIC_MAXLINECHARS), multiline = TRUE)
|
||||
if(!in_range(parent, user))
|
||||
return
|
||||
|
||||
if(length_char(song_text) >= MUSIC_MAXLINES * MUSIC_MAXLINECHARS)
|
||||
var/should_continue = tgui_alert(user, "Your message is too long! Would you like to continue editing it?", "Warning", list("Yes", "No"))
|
||||
if(should_continue != "Yes")
|
||||
break
|
||||
while(length_char(song_text) > MUSIC_MAXLINES * MUSIC_MAXLINECHARS)
|
||||
ParseSong(user, song_text)
|
||||
return TRUE
|
||||
if("start_new_song")
|
||||
name = ""
|
||||
lines = new()
|
||||
tempo = sanitize_tempo(5) // default 120 BPM
|
||||
return TRUE
|
||||
if("add_new_line")
|
||||
var/newline = tgui_input_text(user, "Enter your line", parent.name, max_length = MUSIC_MAXLINECHARS)
|
||||
if(!newline || !in_range(parent, user))
|
||||
return
|
||||
if(lines.len > MUSIC_MAXLINES)
|
||||
return
|
||||
if(length(newline) > MUSIC_MAXLINECHARS)
|
||||
newline = copytext(newline, 1, MUSIC_MAXLINECHARS)
|
||||
lines.Add(newline)
|
||||
if("delete_line")
|
||||
var/line_to_delete = params["line_deleted"]
|
||||
if(line_to_delete > lines.len || line_to_delete < 1)
|
||||
return FALSE
|
||||
lines.Cut(line_to_delete, line_to_delete + 1)
|
||||
return TRUE
|
||||
if("modify_line")
|
||||
var/line_to_edit = params["line_editing"]
|
||||
if(line_to_edit > lines.len || line_to_edit < 1)
|
||||
return FALSE
|
||||
var/new_line_text = tgui_input_text(user, "Enter your line ", parent.name, lines[line_to_edit], max_length = MUSIC_MAXLINECHARS)
|
||||
if(isnull(new_line_text) || !in_range(parent, user))
|
||||
return FALSE
|
||||
lines[line_to_edit] = new_line_text
|
||||
return TRUE
|
||||
|
||||
//MODE STUFF
|
||||
if("set_sustain_mode")
|
||||
var/new_mode = params["new_mode"]
|
||||
if(isnull(new_mode) || !(new_mode in SSinstruments.note_sustain_modes))
|
||||
return FALSE
|
||||
sustain_mode = new_mode
|
||||
return TRUE
|
||||
if("set_note_shift")
|
||||
var/amount = params["amount"]
|
||||
if(!isnum(amount))
|
||||
return FALSE
|
||||
note_shift = clamp(amount, note_shift_min, note_shift_max)
|
||||
return TRUE
|
||||
if("set_volume")
|
||||
var/new_volume = params["amount"]
|
||||
if(!isnum(new_volume))
|
||||
return FALSE
|
||||
set_volume(new_volume)
|
||||
return TRUE
|
||||
if("set_dropoff_volume")
|
||||
var/dropoff_threshold = params["amount"]
|
||||
if(!isnum(dropoff_threshold))
|
||||
return FALSE
|
||||
set_dropoff_volume(dropoff_threshold)
|
||||
return TRUE
|
||||
if("toggle_sustain_hold_indefinitely")
|
||||
full_sustain_held_note = !full_sustain_held_note
|
||||
return TRUE
|
||||
if("set_repeat_amount")
|
||||
if(playing)
|
||||
return
|
||||
var/repeat_amount = params["amount"]
|
||||
if(!isnum(repeat_amount))
|
||||
return FALSE
|
||||
set_repeats(repeat_amount)
|
||||
return TRUE
|
||||
if("edit_sustain_mode")
|
||||
var/sustain_amount = params["amount"]
|
||||
if(isnull(sustain_amount) || !isnum(sustain_amount))
|
||||
return
|
||||
switch(sustain_mode)
|
||||
if(SUSTAIN_LINEAR)
|
||||
set_linear_falloff_duration(sustain_amount)
|
||||
if(SUSTAIN_EXPONENTIAL)
|
||||
set_exponential_drop_rate(sustain_amount)
|
||||
|
||||
/**
|
||||
* Parses a song the user has input into lines and stores them.
|
||||
*/
|
||||
/datum/song/proc/ParseSong(text)
|
||||
/datum/song/proc/ParseSong(mob/user, 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
|
||||
if(lines.len > MUSIC_MAXLINES)
|
||||
to_chat(usr, "Too many lines!")
|
||||
if(user)
|
||||
to_chat(user, "Too many lines!")
|
||||
lines.Cut(MUSIC_MAXLINES + 1)
|
||||
var/linenum = 1
|
||||
for(var/l in lines)
|
||||
if(length_char(l) > MUSIC_MAXLINECHARS)
|
||||
to_chat(usr, "Line [linenum] too long!")
|
||||
if(user)
|
||||
to_chat(user, "Line [linenum] too long!")
|
||||
lines.Remove(l)
|
||||
else
|
||||
linenum++
|
||||
updateDialog(usr) // make sure updates when complete
|
||||
|
||||
/datum/song/Topic(href, href_list)
|
||||
if(!parent.CanUseTopic(usr))
|
||||
usr << browse(null, "window=instrument")
|
||||
usr.unset_machine()
|
||||
return
|
||||
|
||||
parent.add_fingerprint(usr)
|
||||
|
||||
if(href_list["newsong"])
|
||||
lines = new()
|
||||
tempo = sanitize_tempo(5) // default 120 BPM
|
||||
name = ""
|
||||
|
||||
else if(href_list["import"])
|
||||
var/t = ""
|
||||
do
|
||||
t = tgui_input_text(usr, "Please paste the entire song, formatted:", text("[]", name), t, multiline = TRUE, prevent_enter = TRUE)
|
||||
if(!in_range(parent, usr))
|
||||
return
|
||||
|
||||
if(length_char(t) >= MUSIC_MAXLINES * MUSIC_MAXLINECHARS)
|
||||
var/cont = tgui_alert(usr, "Your message is too long! Would you like to continue editing it?", "Too long!", list("Yes", "No"))
|
||||
if(cont != "Yes")
|
||||
break
|
||||
while(length_char(t) > MUSIC_MAXLINES * MUSIC_MAXLINECHARS)
|
||||
ParseSong(t)
|
||||
|
||||
else if(href_list["help"])
|
||||
help = text2num(href_list["help"]) - 1
|
||||
|
||||
else if(href_list["edit"])
|
||||
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
|
||||
|
||||
else if(href_list["tempo"])
|
||||
tempo = sanitize_tempo(tempo + text2num(href_list["tempo"]))
|
||||
|
||||
else if(href_list["play"])
|
||||
INVOKE_ASYNC(src, PROC_REF(start_playing), usr)
|
||||
|
||||
else if(href_list["newline"])
|
||||
var/newline = tgui_input_text(usr, "Enter your line: ", parent.name)
|
||||
if(!newline || !in_range(parent, usr))
|
||||
return
|
||||
if(lines.len > MUSIC_MAXLINES)
|
||||
return
|
||||
if(length(newline) > MUSIC_MAXLINECHARS)
|
||||
newline = copytext(newline, 1, MUSIC_MAXLINECHARS)
|
||||
lines.Add(newline)
|
||||
|
||||
else if(href_list["deleteline"])
|
||||
var/num = round(text2num(href_list["deleteline"]))
|
||||
if(num > lines.len || num < 1)
|
||||
return
|
||||
lines.Cut(num, num+1)
|
||||
|
||||
else if(href_list["modifyline"])
|
||||
var/num = round(text2num(href_list["modifyline"]),1)
|
||||
var/content = tgui_input_text(usr, "Enter your line: ", parent.name, lines[num], MUSIC_MAXLINECHARS, encode=TRUE)
|
||||
if(!content || !in_range(parent, usr))
|
||||
return
|
||||
if(num > lines.len || num < 1)
|
||||
return
|
||||
lines[num] = content
|
||||
|
||||
else if(href_list["stop"])
|
||||
stop_playing()
|
||||
|
||||
else if(href_list["setlinearfalloff"])
|
||||
var/amount = tgui_input_number(usr, "Set linear sustain duration in seconds", "Linear Sustain Duration", round_value=FALSE)
|
||||
if(!isnull(amount))
|
||||
set_linear_falloff_duration(round(amount * 10, world.tick_lag))
|
||||
|
||||
else if(href_list["setexpfalloff"])
|
||||
var/amount = tgui_input_number(usr, "Set exponential sustain factor", "Exponential sustain factor", round_value=FALSE)
|
||||
if(!isnull(amount))
|
||||
set_exponential_drop_rate(round(amount, 0.00001))
|
||||
|
||||
else if(href_list["setvolume"])
|
||||
var/amount = tgui_input_number(usr, "Set volume", "Volume")
|
||||
if(!isnull(amount))
|
||||
set_volume(round(amount, 1))
|
||||
|
||||
else if(href_list["setdropoffvolume"])
|
||||
var/amount = tgui_input_number(usr, "Set dropoff threshold", "Dropoff Threshold Volume", round_value=FALSE)
|
||||
if(!isnull(amount))
|
||||
set_dropoff_volume(round(amount, 0.01))
|
||||
|
||||
else if(href_list["switchinstrument"])
|
||||
if(!length(allowed_instrument_ids))
|
||||
return
|
||||
else if(length(allowed_instrument_ids) == 1)
|
||||
set_instrument(allowed_instrument_ids[1])
|
||||
return
|
||||
var/list/categories = list()
|
||||
for(var/i in allowed_instrument_ids)
|
||||
var/datum/instrument/I = SSinstruments.get_instrument(i)
|
||||
if(I)
|
||||
LAZYSET(categories[I.category || "ERROR CATEGORY"], I.name, I.id)
|
||||
var/cat = tgui_input_list(usr, "Select Category", "Instrument Category", categories)
|
||||
if(!cat)
|
||||
return
|
||||
var/list/instruments = categories[cat]
|
||||
var/choice = tgui_input_list(usr, "Select Instrument", "Instrument Selection", instruments)
|
||||
if(!choice)
|
||||
return
|
||||
choice = instruments[choice] //get id
|
||||
if(choice)
|
||||
set_instrument(choice)
|
||||
|
||||
else if(href_list["setnoteshift"])
|
||||
var/amount = tgui_input_number(usr, "Set note shift", "Note Shift", null, note_shift_max, note_shift_min)
|
||||
if(!isnull(amount))
|
||||
note_shift = clamp(amount, note_shift_min, note_shift_max)
|
||||
|
||||
else if(href_list["setsustainmode"])
|
||||
var/choice = tgui_input_list(usr, "Choose a sustain mode", "Sustain Mode", list("Linear", "Exponential"))
|
||||
switch(choice)
|
||||
if("Linear")
|
||||
sustain_mode = SUSTAIN_LINEAR
|
||||
if("Exponential")
|
||||
sustain_mode = SUSTAIN_EXPONENTIAL
|
||||
|
||||
else if(href_list["togglesustainhold"])
|
||||
full_sustain_held_note = !full_sustain_held_note
|
||||
|
||||
updateDialog()
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
var/list/octaves = list(3, 3, 3, 3, 3, 3, 3)
|
||||
var/list/accents = list("n", "n", "n", "n", "n", "n", "n")
|
||||
for(var/line in lines)
|
||||
var/list/chords = splittext(lowertext(line), ",")
|
||||
var/list/chords = splittext(LOWER_TEXT(line), ",")
|
||||
for(var/chord in chords)
|
||||
var/list/compiled_chord = list()
|
||||
var/tempodiv = 1
|
||||
@@ -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)
|
||||
@@ -70,7 +70,7 @@
|
||||
return
|
||||
|
||||
// now generate name
|
||||
var/soundfile = "sound/instruments/[cached_legacy_dir]/[ascii2text(note+64)][acc][oct].[cached_legacy_ext]"
|
||||
var/soundfile = "sound/runtime/instruments/[cached_legacy_dir]/[ascii2text(note+64)][acc][oct].[cached_legacy_ext]"
|
||||
soundfile = file(soundfile)
|
||||
// make sure the note exists
|
||||
if(!fexists(soundfile))
|
||||
@@ -80,14 +80,13 @@
|
||||
if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck)
|
||||
do_hearcheck()
|
||||
var/sound/music_played = sound(soundfile)
|
||||
for(var/mob/M as anything in hearing_mobs)
|
||||
/* Would be nice
|
||||
if(user && HAS_TRAIT(user, TRAIT_MUSICIAN) && isliving(M))
|
||||
var/mob/living/L = M
|
||||
L.apply_status_effect(STATUS_EFFECT_GOOD_MUSIC)
|
||||
*/
|
||||
if(!M)
|
||||
hearing_mobs -= M
|
||||
for(var/i in hearing_mobs)
|
||||
var/mob/M = i
|
||||
//if(player && HAS_TRAIT(player, TRAIT_MUSICIAN) && isliving(M))
|
||||
// var/mob/living/L = M
|
||||
// L.apply_status_effect(/datum/status_effect/good_music)
|
||||
var/pref_volume = M?.client?.prefs.read_preference(/datum/preference/numeric/volume/sound_instruments)
|
||||
if(!pref_volume)
|
||||
continue
|
||||
M.playsound_local(source, null, volume * using_instrument.volume_multiplier, S = music_played, preference = /datum/preference/toggle/instrument_toggle, volume_channel = VOLUME_CHANNEL_INSTRUMENTS)
|
||||
M.playsound_local(source, null, volume * using_instrument.volume_multiplier * (pref_volume/100), S = music_played)
|
||||
// Could do environment and echo later but not for now
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
var/list/octaves = list(3, 3, 3, 3, 3, 3, 3)
|
||||
var/list/accents = list("n", "n", "n", "n", "n", "n", "n")
|
||||
for(var/line in lines)
|
||||
var/list/chords = splittext(lowertext(line), ",")
|
||||
var/list/chords = splittext(LOWER_TEXT(line), ",")
|
||||
for(var/chord in chords)
|
||||
var/list/compiled_chord = list()
|
||||
var/tempodiv = 1
|
||||
@@ -42,9 +42,8 @@
|
||||
* 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)
|
||||
if(can_noteshift)
|
||||
key = clamp(key + note_shift, key_min, key_max)
|
||||
/datum/song/proc/playkey_synth(key, atom/player)
|
||||
key = clamp(key + note_shift, key_min, key_max)
|
||||
if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck)
|
||||
do_hearcheck()
|
||||
var/datum/instrument_key/K = using_instrument.samples[num2text(key)] //See how fucking easy it is to make a number text? You don't need a complicated 9 line proc!
|
||||
@@ -60,27 +59,15 @@
|
||||
var/channel_text = num2text(channel)
|
||||
channels_playing[channel_text] = 100
|
||||
last_channel_played = channel_text
|
||||
var/turf/source = get_turf(parent)
|
||||
for(var/mob/M as anything in hearing_mobs)
|
||||
/* Maybe someday
|
||||
if(user && HAS_TRAIT(user, TRAIT_MUSICIAN) && isliving(M))
|
||||
var/mob/living/L = M
|
||||
L.apply_status_effect(STATUS_EFFECT_GOOD_MUSIC)
|
||||
*/
|
||||
// Jeez
|
||||
M.playsound_local(
|
||||
turf_source = source,
|
||||
soundin = null,
|
||||
vol = volume,
|
||||
vary = FALSE,
|
||||
frequency = K.frequency,
|
||||
falloff = FALLOFF_SOUNDS, //CHOMPEdit
|
||||
is_global = null,
|
||||
channel = channel,
|
||||
pressure_affected = null,
|
||||
S = copy,
|
||||
preference = /datum/preference/toggle/instrument_toggle,
|
||||
volume_channel = VOLUME_CHANNEL_INSTRUMENTS)
|
||||
for(var/i in hearing_mobs)
|
||||
var/mob/M = i
|
||||
//if(player && HAS_TRAIT(player, TRAIT_MUSICIAN) && isliving(M))
|
||||
// var/mob/living/L = M
|
||||
// L.apply_status_effect(/datum/status_effect/good_music)
|
||||
var/pref_volume = M?.client?.prefs.read_preference(/datum/preference/numeric/volume/sound_instruments)
|
||||
if(!pref_volume)
|
||||
continue
|
||||
M.playsound_local(get_turf(parent), null, volume * (pref_volume/100), FALSE, K.frequency, null, channel, null, copy)
|
||||
// Could do environment and echo later but not for now
|
||||
|
||||
/**
|
||||
@@ -141,8 +128,10 @@
|
||||
if(dead)
|
||||
channels_playing -= channel
|
||||
channels_idle += channel
|
||||
for(var/mob/M in hearing_mobs)
|
||||
for(var/i in hearing_mobs)
|
||||
var/mob/M = i
|
||||
M.stop_sound_channel(channelnumber)
|
||||
else
|
||||
for(var/mob/M in hearing_mobs)
|
||||
for(var/i in hearing_mobs)
|
||||
var/mob/M = i
|
||||
M.set_sound_channel_volume(channelnumber, (current_volume * 0.01) * volume * using_instrument.volume_multiplier)
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
/obj/structure/musician
|
||||
name = "Not A Piano"
|
||||
desc = "Something broke, contact coderbus."
|
||||
/// IF FALSE music stops when the piano is unanchored.
|
||||
var/can_play_unanchored = FALSE
|
||||
/// Our allowed list of instrument ids. This is nulled on initialize.
|
||||
var/list/allowed_instrument_ids = list("r3grand","r3harpsi","crharpsi","crgrand1","crbright1", "crichugan", "crihamgan","piano")
|
||||
var/datum/song/song
|
||||
/// Our song datum.
|
||||
var/datum/song/stationary/song
|
||||
|
||||
/obj/structure/musician/Initialize(mapload)
|
||||
. = ..()
|
||||
@@ -14,64 +17,62 @@
|
||||
QDEL_NULL(song)
|
||||
return ..()
|
||||
|
||||
/obj/structure/musician/proc/can_play(atom/music_player)
|
||||
if(!anchored && !can_play_unanchored)
|
||||
return FALSE
|
||||
if(!ismob(music_player))
|
||||
return FALSE
|
||||
var/mob/user = music_player
|
||||
if(!user.IsAdvancedToolUser())
|
||||
return FALSE
|
||||
if(user.incapacitated())
|
||||
return FALSE
|
||||
if(!Adjacent(user))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/structure/musician/attack_hand(mob/M)
|
||||
if(!M.IsAdvancedToolUser())
|
||||
return
|
||||
|
||||
interact(M)
|
||||
tgui_interact(M)
|
||||
|
||||
// CHOMPAdd - Grand piano moving
|
||||
/obj/structure/musician/tgui_interact(mob/user)
|
||||
return song.tgui_interact(user)
|
||||
|
||||
/obj/structure/musician/attackby(obj/item/W as obj, mob/user as mob)
|
||||
if(W.has_tool_quality(TOOL_WRENCH))
|
||||
playsound(src, W.usesound, 100, 1)
|
||||
if(anchored)
|
||||
user.visible_message(span_filter_notice("[user] begins unsecuring \the [src] from the floor."), span_filter_notice("You start unsecuring \the [src] from the floor."))
|
||||
else
|
||||
user.visible_message(span_filter_notice("[user] begins securing \the [src] to the floor."), span_filter_notice("You start securing \the [src] to the floor."))
|
||||
|
||||
if(do_after(user, 20 * W.toolspeed))
|
||||
if(!src) return
|
||||
to_chat(user, span_notice("You [anchored? "un" : ""]secured \the [src]!"))
|
||||
anchored = !anchored
|
||||
return
|
||||
|
||||
// CHOMPEnd
|
||||
|
||||
/obj/structure/musician/proc/should_stop_playing(mob/user)
|
||||
if(!(anchored || can_play_unanchored))
|
||||
return TRUE
|
||||
if(!user)
|
||||
return FALSE
|
||||
return !CanUseTopic(user)
|
||||
|
||||
/obj/structure/musician/interact(mob/user)
|
||||
/* FIXME
|
||||
/obj/structure/musician/wrench_act(mob/living/user, obj/item/tool)
|
||||
. = ..()
|
||||
song.interact(user)
|
||||
|
||||
/*
|
||||
/obj/structure/musician/wrench_act(mob/living/user, obj/item/I)
|
||||
default_unfasten_wrench(user, I, 40)
|
||||
return TRUE
|
||||
default_unfasten_wrench(user, tool, time = 4 SECONDS)
|
||||
return ITEM_INTERACT_SUCCESS
|
||||
*/
|
||||
|
||||
/obj/structure/musician/piano
|
||||
name = "space minimoog"
|
||||
name = "space piano"
|
||||
desc = "This is a space piano, like a regular piano, but always in tune! Even if the musician isn't."
|
||||
icon = 'icons/obj/musician.dmi'
|
||||
icon_state = "minimoog"
|
||||
anchored = TRUE
|
||||
density = TRUE
|
||||
var/broken_icon_state = "pianobroken"
|
||||
|
||||
/obj/structure/musician/piano/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/climbable)
|
||||
|
||||
/** FIXME: We do not have atom_break implemented yet
|
||||
/obj/structure/musician/piano/atom_break(damage_flag)
|
||||
. = ..()
|
||||
if(!broken)
|
||||
broken = TRUE
|
||||
icon_state = broken_icon_state
|
||||
*/
|
||||
|
||||
/obj/structure/musician/piano/unanchored
|
||||
anchored = FALSE
|
||||
|
||||
/obj/structure/musician/piano/Initialize(mapload)
|
||||
. = ..()
|
||||
if(prob(50) && icon_state == initial(icon_state))
|
||||
name = "space minimoog"
|
||||
desc = "This is a minimoog, like a space piano, but more spacey!"
|
||||
icon_state = "minimoog"
|
||||
else
|
||||
name = "space piano"
|
||||
desc = "This is a space piano, like a regular piano, but always in tune! Even if the musician isn't."
|
||||
icon_state = "piano"
|
||||
/obj/structure/musician/piano/minimoog
|
||||
name = "space minimoog"
|
||||
desc = "This is a minimoog, like a space piano, but more spacey!"
|
||||
icon_state = "minimoog"
|
||||
broken_icon_state = "minimoogbroken"
|
||||
|
||||
13
modular_chomp/code/modules/instruments/stationary.dm
Normal file
13
modular_chomp/code/modules/instruments/stationary.dm
Normal file
@@ -0,0 +1,13 @@
|
||||
/obj/structure/musician/attackby(obj/item/W, mob/user)
|
||||
if(W.has_tool_quality(TOOL_WRENCH))
|
||||
playsound(src, W.usesound, 100, 1)
|
||||
if(anchored)
|
||||
user.visible_message(span_filter_notice("[user] begins unsecuring \the [src] from the floor."), span_filter_notice("You start unsecuring \the [src] from the floor."))
|
||||
else
|
||||
user.visible_message(span_filter_notice("[user] begins securing \the [src] to the floor."), span_filter_notice("You start securing \the [src] to the floor."))
|
||||
|
||||
if(do_after(user, 20 * W.toolspeed))
|
||||
if(!src) return
|
||||
to_chat(user, span_notice("You [anchored? "un" : ""]secured \the [src]!"))
|
||||
anchored = !anchored
|
||||
return
|
||||
5
sound/runtime/README.md
Normal file
5
sound/runtime/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Runtime Loaded Sounds
|
||||
|
||||
These sounds are not compiled into the .rsc or are otherwise loaded at runtime.
|
||||
|
||||
Please keep all (non-config) sounds that do this in this folder as it is needed by the [deploy.sh](../../tools/deploy.sh) script to minimize build output.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user