Files
Bubberstation/code/modules/instruments/songs/editor.dm
SkyratBot 6b320f9eb0 [MIRROR] Implements Instrument UI Interaction When Laying Down [MDB IGNORE] (#19015)
Implements Instrument UI Interaction When Laying Down (#72926)

## About The Pull Request
Adds floor_okay = TRUE tag to the song editor dm. Basically a small QoL
to allow you to initiate your song if you were say, slipped by a 100
potency banana and wanted to continue playing your song while waiting to
get back up. The change allows you to actually interact with instrument
UI while laying down.
## Why It's Good For The Game
Took a bit of searching but I find that this QoL feature really made a
big impact to me at least. You can play your instrument on the floor
while laying down. It makes sense to me that you should be physically
able to strum a guitar or press keys when you're on the floor as you
already are able to play anyway by starting the instrument standing up
and then laying down. This just cuts out the need to have to stand up to
re-interact with the UI if you say, got slipped or lost both your legs
in a tragic accident. Also allows you to chill on a bed and play without
having to get up, I guess. This might be a minor gameplay change to some
but for those who play instruments the entire round for others to enjoy,
this is a huge QoL.
## Changelog
🆑
qol: Musicians rejoice! You can now interact with the instrument UI
while laying down.
/🆑

Co-authored-by: ophaq <44999764+ophaq@users.noreply.github.com>
2023-01-31 02:05:23 +00:00

245 lines
9.7 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, be_close = TRUE, no_dexterity = FALSE, no_tk = FALSE, need_hands = FALSE, floor_okay = TRUE))
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 = tgui_alert(usr, "Your message is too long! Would you like to continue editing it?", "Warning", 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.
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_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)
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", 0.1, INSTRUMENT_MAX_TOTAL_SUSTAIN, 0.1, round_value = FALSE)
if(!isnull(amount))
set_linear_falloff_duration(amount)
else if(href_list["setexpfalloff"])
var/amount = tgui_input_number(usr, "Set exponential sustain factor", "Exponential sustain factor", INSTRUMENT_EXP_FALLOFF_MIN, INSTRUMENT_EXP_FALLOFF_MAX, INSTRUMENT_EXP_FALLOFF_MIN, round_value = FALSE)
if(!isnull(amount))
set_exponential_drop_rate(amount)
else if(href_list["setvolume"])
var/amount = tgui_input_number(usr, "Set volume", "Volume", 1, 75, 1)
if(!isnull(amount))
set_volume(amount)
else if(href_list["setdropoffvolume"])
var/amount = tgui_input_number(usr, "Set dropoff threshold", "Dropoff Volume", max_value = 100)
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 = tgui_input_list(usr, "Select Category", "Instrument Category", categories)
if(isnull(cat))
return
var/list/instruments = categories[cat]
var/choice = tgui_input_list(usr, "Select Instrument", "Instrument Selection", instruments)
if(isnull(choice))
return
if(isnull(instruments[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 = tgui_input_list(usr, "Choose a sustain mode", "Sustain Mode", 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(usr)