Files
Bubberstation/code/modules/instruments/songs/editor.dm
SkyratBot bfd0fed710 [MIRROR] Synthesizers and headphones can now have circuits! [MDB IGNORE] (#9818)
* Synthesizers and headphones can now have circuits! (#62825)

About The Pull Request

A circuit and shell components have been added to Synthesizers (headphones and spacepods included, though with a reduced capacity because of their size), so they can now be used for wiremod. Just like for instant cameras, no shell design here. They are meant to be found in dorms or maybe ordered from cargo.
Why It's Good For The Game

The station outside the sci department has plenty of USB ports stuff but is lacking when it comes to circuits shell. This is another small step toward a better and more applicable wiremod.
Changelog

cl
expansion: Synthesizers and headphones can now have circuits installed.
/cl

* Synthesizers and headphones can now have circuits!

Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
2021-12-01 22:32:33 +00:00

243 lines
9.4 KiB
Plaintext

/**
* Returns the HTML for the status UI for this song datum.
*/
/datum/song/proc/instrument_status_ui()
. = list()
. += "<div class='statusDisplay'>"
. += "<b><a href='?src=[REF(src)];switchinstrument=1'>Current instrument</a>:</b> "
if(!using_instrument)
. += "[span_danger("No instrument loaded!")]<br>"
else
. += "[using_instrument.name]<br>"
. += "Playback Settings:<br>"
if(can_noteshift)
. += "<a href='?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 = ""
switch(sustain_mode)
if(SUSTAIN_LINEAR)
smt = "Linear"
modetext = "<a href='?src=[REF(src)];setlinearfalloff=1'>Linear Sustain Duration</a>: [sustain_linear_duration / 10] seconds<br>"
if(SUSTAIN_EXPONENTIAL)
smt = "Exponential"
modetext = "<a href='?src=[REF(src)];setexpfalloff=1'>Exponential Falloff Factor</a>: [sustain_exponential_dropoff]% per decisecond<br>"
. += "<a href='?src=[REF(src)];setsustainmode=1'>Sustain Mode</a>: [smt]<br>"
. += modetext
. += using_instrument?.ready()? "Status: <span class='good'>Ready</span><br>" : "Status: <span class='bad'>!Instrument Definition Error!</span><br>"
. += "Instrument Type: [legacy? "Legacy" : "Synthesized"]<br>"
. += "<a href='?src=[REF(src)];setvolume=1'>Volume</a>: [volume]<br>"
. += "<a href='?src=[REF(src)];setdropoffvolume=1'>Volume Dropoff Threshold</a>: [sustain_dropoff_volume]<br>"
. += "<a href='?src=[REF(src)];togglesustainhold=1'>Sustain indefinitely last held note</a>: [full_sustain_held_note? "Enabled" : "Disabled"].<br>"
. += "</div>"
/datum/song/ui_interact(mob/user)
var/list/dat = list()
dat += instrument_status_ui()
if(lines.len > 0)
dat += "<H3>Playback</H3>"
if(!playing)
dat += "<A href='?src=[REF(src)];play=1'>Play</A> <span class='linkOn'>Stop</span><BR><BR>"
dat += "Repeat Song: "
dat += repeat > 0 ? "<A href='?src=[REF(src)];repeat=-10'>-</A><A href='?src=[REF(src)];repeat=-1'>-</A>" : "<span class='linkOff'>-</SPAN><SPAN CLASS='linkOff'>-</span>"
dat += " [repeat] times "
dat += repeat < max_repeats ? "<A href='?src=[REF(src)];repeat=1'>+</A><A href='?src=[REF(src)];repeat=10'>+</A>" : "<span class='linkOff'>+</SPAN><SPAN CLASS='linkOff'>+</span>"
dat += "<BR>"
else
dat += "<span class='linkOn'>Play</span> <A href='?src=[REF(src)];stop=1'>Stop</A><BR>"
dat += "Repeats left: <B>[repeat]</B><BR>"
if(!editing)
dat += "<BR><B><A href='?src=[REF(src)];edit=2'>Show Editor</A></B><BR>"
else
dat += "<H3>Editing</H3>"
dat += "<B><A href='?src=[REF(src)];edit=1'>Hide Editor</A></B>"
dat += " <A href='?src=[REF(src)];newsong=1'>Start a New Song</A>"
dat += " <A href='?src=[REF(src)];import=1'>Import a Song</A><BR><BR>"
var/bpm = round(600 / tempo)
dat += "Tempo: <A href='?src=[REF(src)];tempo=[world.tick_lag]'>-</A> [bpm] BPM <A href='?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='?src=[REF(src)];modifyline=[linecount]'>Edit</A> <A href='?src=[REF(src)];deleteline=[linecount]'>X</A> [line]<BR>"
dat += "<A href='?src=[REF(src)];newline=1'>Add Line</A><BR><BR>"
if(help)
dat += "<B><A href='?src=[REF(src)];help=1'>Hide Help</A></B><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 seperating each note with a hyphon: <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 += "<B><A href='?src=[REF(src)];help=2'>Show Help</A></B><BR>"
var/datum/browser/popup = new(user, "instrument", parent?.name || "instrument", 700, 500)
popup.set_content(dat.Join(""))
popup.open()
/**
* Parses a song the user has input into lines and stores them.
*/
/datum/song/proc/ParseSong(new_song)
set waitfor = FALSE
//split into lines
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(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!")
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!")
lines.Remove(l)
else
linenum++
updateDialog(usr) // make sure updates when complete
/datum/song/Topic(href, href_list)
if(!usr.canUseTopic(parent, TRUE, FALSE, FALSE, FALSE))
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 = html_encode(input(usr, "Please paste the entire song, formatted:", text("[]", name), t) as message)
if(!in_range(parent, usr))
return
if(length_char(t) >= MUSIC_MAXLINES * MUSIC_MAXLINECHARS)
var/cont = input(usr, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no")
if(cont == "no")
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.
set_repeats(repeat + text2num(href_list["repeat"]))
else if(href_list["tempo"])
tempo = sanitize_tempo(tempo + text2num(href_list["tempo"]))
else if(href_list["play"])
INVOKE_ASYNC(src, .proc/start_playing, usr)
else if(href_list["newline"])
var/newline = html_encode(input("Enter your line: ", parent.name) as text|null)
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 = stripped_input(usr, "Enter your line: ", parent.name, lines[num], MUSIC_MAXLINECHARS)
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 = input(usr, "Set linear sustain duration in seconds", "Linear Sustain Duration") as null|num
if(!isnull(amount))
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(amount)
else if(href_list["setvolume"])
var/amount = input(usr, "Set volume", "Volume") as null|num
if(!isnull(amount))
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(amount)
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 = input(usr, "Select Category", "Instrument Category") as null|anything in categories
if(!cat)
return
var/list/instruments = categories[cat]
var/choice = input(usr, "Select Instrument", "Instrument Selection") as null|anything in instruments
if(!choice)
return
choice = instruments[choice] //get id
if(choice)
set_instrument(choice)
else if(href_list["setnoteshift"])
var/amount = input(usr, "Set note shift", "Note Shift") as null|num
if(!isnull(amount))
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 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
updateDialog()