diff --git a/code/game/objects/items/devices/guitar.dm b/code/game/objects/items/devices/guitar.dm index 546989c3c6e..9a3b4dd6901 100644 --- a/code/game/objects/items/devices/guitar.dm +++ b/code/game/objects/items/devices/guitar.dm @@ -20,17 +20,19 @@ return ..() /obj/item/device/guitar/attack_self(mob/user as mob) - interact(user) + ui_interact(user) -/obj/item/device/guitar/interact(mob/user as mob) +/obj/item/device/guitar/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) if(!user) return if(!isliving(user) || user.stat || user.restrained() || user.lying) return - user.set_machine(src) - song.interact(user) + song.ui_interact(user, ui_key, ui, force_open) + +/obj/item/device/guitar/Topic(href, href_list) + song.Topic(href, href_list) /datum/table_recipe/guitar name = "Guitar" diff --git a/code/game/objects/items/devices/violin.dm b/code/game/objects/items/devices/violin.dm index 02038717aff..9c4d5e9ddb4 100644 --- a/code/game/objects/items/devices/violin.dm +++ b/code/game/objects/items/devices/violin.dm @@ -24,14 +24,16 @@ ..() /obj/item/device/violin/attack_self(mob/user as mob) - interact(user) + ui_interact(user) -/obj/item/device/violin/interact(mob/user as mob) +/obj/item/device/violin/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) if(!user) return if(!isliving(user) || user.stat || user.restrained() || user.lying) return - user.set_machine(src) - song.interact(user) + song.ui_interact(user, ui_key, ui, force_open) + +/obj/item/device/violin/Topic(href, href_list) + song.Topic(href, href_list) \ No newline at end of file diff --git a/code/game/objects/structures/musician.dm b/code/game/objects/structures/musician.dm index b173ece5b0b..367208bbfe4 100644 --- a/code/game/objects/structures/musician.dm +++ b/code/game/objects/structures/musician.dm @@ -3,13 +3,12 @@ /datum/song var/name = "Untitled" var/list/lines = new() - var/tempo = 5 // delay between notes + var/tempo = 5 // delay between notes - var/playing = 0 // if we're playing - var/help = 0 // if help is open - var/edit = 1 // if we're in editing mode - var/repeat = 0 // number of times remaining to repeat - var/max_repeats = 10 // maximum times we can repeat + var/playing = 0 // if we're playing + var/help = 0 // if help is open + var/repeat = 0 // number of times remaining to repeat + var/max_repeat = 10 // maximum times we can repeat var/instrumentDir = "piano" // the folder with the sounds var/instrumentExt = "ogg" // the file extension @@ -63,9 +62,6 @@ continue M.playsound_local(source, soundfile, 100, falloff = 5) -/datum/song/proc/updateDialog(mob/user as mob) - instrumentObj.updateDialog() // assumes it's an object in world, override if otherwise - /datum/song/proc/shouldStopPlaying(mob/user) if(instrumentObj) //if(!user.canUseTopic(instrumentObj)) @@ -113,68 +109,31 @@ else sleep(tempo) repeat-- - if(repeat >= 0) // don't show the last -1 repeat - updateDialog(user) playing = 0 repeat = 0 - updateDialog(user) -/datum/song/proc/interact(mob/user as mob) - var/dat = "" +/datum/song/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + var/data[0] - if(lines.len > 0) - dat += "

Playback

" - if(!playing) - dat += "Play Stop

" - dat += "Repeat Song: " - dat += repeat > 0 ? "--" : "--" - dat += " [repeat] times " - dat += repeat < max_repeats ? "++" : "++" - dat += "
" - else - dat += "Play Stop
" - dat += "Repeats left: [repeat]
" - if(!edit) - dat += "
Show Editor
" - else - dat += "

Editing

" - dat += "Hide Editor" - dat += " Start a New Song" - dat += " Import a Song

" - var/bpm = round(600 / tempo) - dat += "Tempo: - [bpm] BPM +

" - var/linecount = 0 - for(var/line in lines) - linecount += 1 - dat += "Line [linecount]: Edit X [line]
" - dat += "Add Line

" - if(help) - dat += "Hide Help
" - dat += {" - Lines are a series of chords, separated by commas (,), each with notes seperated by hyphens (-).
- Every note in a chord will play together, with chord timed by the tempo.
-
- Notes are played by the names of the note, and optionally, the accidental, and/or the octave number.
- By default, every note is natural and in octave 3. Defining otherwise is remembered for each note.
- Example: C,D,E,F,G,A,B will play a C major scale.
- After a note has an accidental placed, it will be remembered: C,C4,C,C3 is C3,C4,C4,C3
- Chords can be played simply by seperating each note with a hyphon: A-C#,Cn-E,E-G#,Gn-B
- A pause may be denoted by an empty chord: C,E,,C,G
- To make a chord be a different time, end it with /x, where the chord length will be length
- defined by tempo / x: C,G/2,E/4
- Combined, an example is: E-E4/4,F#/2,G#/8,B/8,E3-E4/4 -
- Lines may be up to 50 characters.
- A song may only contain up to 50 lines.
- "} - else - dat += "Show Help
" + data["lines"] = lines + data["tempo"] = tempo - var/datum/browser/popup = new(user, "instrument", instrumentObj.name, 700, 500) - popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(instrumentObj.icon, instrumentObj.icon_state)) - popup.open() + data["playing"] = playing + data["help"] = help + data["repeat"] = repeat + data["maxRepeat"] = max_repeat + data["minTempo"] = world.tick_lag + data["maxTempo"] = 600 + if(!instrumentObj) + return + + ui = nanomanager.try_update_ui(user, instrumentObj, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, instrumentObj, ui_key, "song.tmpl", instrumentObj.name, 700, 500) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) /datum/song/Topic(href, href_list) if(!in_range(instrumentObj, usr) || (issilicon(usr) && instrumentObj.loc != usr) || !isliving(usr) || !usr.canmove || usr.restrained()) @@ -185,14 +144,16 @@ instrumentObj.add_fingerprint(usr) if(href_list["newsong"]) + playing = 0 lines = new() tempo = sanitize_tempo(5) // default 120 BPM name = "" else if(href_list["import"]) + playing = 0 var/t = "" do - t = html_encode(input(usr, "Please paste the entire song, formatted:", text("[]", name), t) as message) + t = html_encode(input(usr, "Please paste the entire song, formatted:", text("[]", name), t) as message) if(!in_range(instrumentObj, usr)) return @@ -220,13 +181,9 @@ lines.Remove(l) else linenum++ - updateDialog(usr) // make sure updates when complete else if(href_list["help"]) - help = text2num(href_list["help"]) - 1 - - else if(href_list["edit"]) - edit = text2num(href_list["edit"]) - 1 + help = !help if(href_list["repeat"]) //Changing this from a toggle to a number of repeats to avoid infinite loops. if(playing) @@ -234,18 +191,24 @@ repeat += round(text2num(href_list["repeat"])) if(repeat < 0) repeat = 0 - if(repeat > max_repeats) - repeat = max_repeats + if(repeat > max_repeat) + repeat = max_repeat else if(href_list["tempo"]) - tempo = sanitize_tempo(tempo + text2num(href_list["tempo"])) + tempo = sanitize_tempo(tempo + text2num(href_list["tempo"]) * world.tick_lag) else if(href_list["play"]) + if(playing) + return playing = 1 spawn() playsong(usr) - else if(href_list["newline"]) + else if(href_list["insertline"]) + var/num = round(text2num(href_list["insertline"])) + if(num < 1 || num > lines.len + 1) + return + var/newline = html_encode(input("Enter your line: ", instrumentObj.name) as text|null) if(!newline || !in_range(instrumentObj, usr)) return @@ -253,16 +216,17 @@ return if(lentext(newline) > 50) newline = copytext(newline, 1, 50) - lines.Add(newline) + + lines.Insert(num, 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) + lines.Cut(num, num + 1) else if(href_list["modifyline"]) - var/num = round(text2num(href_list["modifyline"]),1) + var/num = round(text2num(href_list["modifyline"])) var/content = html_encode(input("Enter your line: ", instrumentObj.name, lines[num]) as text|null) if(!content || !in_range(instrumentObj, usr)) return @@ -275,19 +239,12 @@ else if(href_list["stop"]) playing = 0 - updateDialog(usr) - return - /datum/song/proc/sanitize_tempo(new_tempo) - new_tempo = abs(new_tempo) return max(round(new_tempo, world.tick_lag), world.tick_lag) // subclass for handheld instruments, like violin /datum/song/handheld -/datum/song/handheld/updateDialog(mob/user as mob) - instrumentObj.interact(user) - /datum/song/handheld/shouldStopPlaying() if(instrumentObj) return !isliving(instrumentObj.loc) @@ -329,14 +286,16 @@ ..() /obj/structure/piano/attack_hand(mob/user as mob) - interact(user) + ui_interact(user) -/obj/structure/piano/interact(mob/user as mob) +/obj/structure/piano/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) if(!user || !anchored) return - user.set_machine(src) - song.interact(user) + song.ui_interact(user, ui_key, ui, force_open) + +/obj/structure/piano/Topic(href, href_list) + song.Topic(href, href_list) /obj/structure/piano/attackby(obj/item/O as obj, mob/user as mob, params) if (istype(O, /obj/item/weapon/wrench)) diff --git a/nano/templates/song.tmpl b/nano/templates/song.tmpl new file mode 100644 index 00000000000..66a2832f332 --- /dev/null +++ b/nano/templates/song.tmpl @@ -0,0 +1,87 @@ + + +
+
+
Playback
+
+ {{:helper.link('New', 'document', {'newsong': 1})}} + {{:helper.link('Import', 'folder-open', {'import': 1})}} + {{:helper.link('Play', 'play', {'play': 1}, data.lines.length > 0 ? (data.playing ? 'selected' : null) : 'disabled')}} + {{:helper.link('Stop', 'stop', {'stop': 1}, !data.playing ? 'selected' : null)}} +
+
+
+
Repeat Song:
+ {{:helper.link('-', null, {'repeat' : -10}, data.repeat > 0 && !data.playing ? null : 'disabled')}} + {{:helper.link('-', null, {'repeat' : -1}, data.repeat > 0 && !data.playing ? null : 'disabled')}} + {{:data.repeat}} + {{:helper.link('+', null, {'repeat' : 1}, data.repeat < data.maxRepeat && !data.playing ? null : 'disabled')}} + {{:helper.link('+', null, {'repeat' : 10}, data.repeat < data.maxRepeat && !data.playing ? null : 'disabled')}} +
+
+
Tempo:
+ {{:helper.link('-', null, {'tempo' : 10}, data.tempo < data.maxTempo ? null : 'disabled')}} + {{:helper.link('-', null, {'tempo' : 1}, data.tempo < data.maxTempo ? null : 'disabled')}} + {{:helper.round(600 / data.tempo)}} BPM + {{:helper.link('+', null, {'tempo' : -1}, data.tempo > data.minTempo ? null : 'disabled')}} + {{:helper.link('+', null, {'tempo' : -10}, data.tempo > data.minTempo ? null : 'disabled')}} +
+
+ +

Editor

+{{for data.lines}} +
+
{{:index + 1}}: 
+
+ {{:helper.link('Edit', 'pencil', {'modifyline': index+1}, data.playing ? 'disabled' : null)}} + {{:helper.link('Insert', 'arrowreturn-1-n', {'insertline': index+1}, data.playing ? 'disabled' : null)}} + {{:helper.link('Delete', 'trash', {'deleteline': index+1}, data.playing ? 'disabled' : null)}} +
+
{{:value}}
+
+{{/for}} +
+ {{:helper.link('Add Line', 'arrowreturn-1-s', {'insertline': data.lines.length + 1}, data.playing ? 'disabled' : null)}} +
+
+
+ +
+
+ {{:helper.link('Help', data.help ? 'close' : 'help', {'help': 1})}} +
+
+ {{if data.help}} +

+ Lines are a series of chords, separated by commas (,), each with notes seperated by hyphens (-). +
+ Every note in a chord will play together, with the chord timed by the tempo as defined above. +

+

+ Notes are played by the names of the note, and optionally, the accidental, and/or the octave number. +
+ By default, every note is natural and in octave 3. Defining a different state for either is remembered for each note. +

+

+

+ Chords can be played simply by seperating each note with a hyphon: A-C#,Cn-E,E-G#,Gn-B.
+ A pause may be denoted by an empty chord: C,E,,C,G. +
+ To make a chord be a different time, end it with /x, where the chord length will be length defined by tempo / x, eg: C,G/2,E/4. +

+

+ Combined, an example line is: E-E4/4,F#/2,G#/8,B/8,E3-E4/4. +

+

+ {{/if}} +
+
\ No newline at end of file