From e43c4afcebce1281df0c24bada0509ab3e510e6e Mon Sep 17 00:00:00 2001 From: KatieFrogs <23621460+KatieFrogs@users.noreply.github.com> Date: Fri, 15 Jul 2022 16:00:43 +0200 Subject: [PATCH] Lyrics, search, and other fixes - #LYRIC - Parse #LYRIC commands and apply them to all difficulties that do not have them - #LYRIC command now supports branches - Fix last #LYRIC at the end of the chart getting ignored - Fix the glitchy dragging and dropping of files on the custom song importing page - Fix Ctrl and Shift keys getting stuck on song select when switching tabs with Ctrl(+Shift)+Tab - Search - Fix the search box "random:yes" query to randomize the entire results and not just the first 50 - Add "all:yes" query to the search box to remove the result limit and display all of the results - Fix searching for an invalid query (like "cleared:yes" or ":") unexpectedly returning all the songs - Fix pressing Q then jumping to a song through search not unmuting the sound - Pressing the search key on mobile will hide the keyboard - Fix search tips changing rapidly when the window is resized - Use comments instead of `######` in the issue template so that the warning does not appear in the issue - Fix TJA MAKER: url between angle brackets not working - Add a check for Class field declarations in the browser support warning - Fix gpicker getting stuck if a network error occurs - Fix not being able to replace some assets using a "taiko-web assets" folder - Fix selectable song title not being aligned with the game if the game window is too wide - Allow plugin developers to use the "select" type for the settings options - It uses "options" array and "options_lang" object - Fix plugins not getting removed from the plugin list on syntax error - Fix error messages not working if a default plugin is broken - Fix the start of default plugins not stopping the page from loading on error - Fix not being able to scroll the plugins screen on mobile --- .github/ISSUE_TEMPLATE.md | 4 +- public/src/js/browsersupport.js | 4 + public/src/js/canvasdraw.js | 4 +- public/src/js/customsongs.js | 15 +- public/src/js/game.js | 57 +++++--- public/src/js/gpicker.js | 22 ++- public/src/js/importsongs.js | 37 +++-- public/src/js/loader.js | 31 +++- public/src/js/lyrics.js | 6 +- public/src/js/parsetja.js | 251 +++++++++++++++++++++----------- public/src/js/plugins.js | 12 +- public/src/js/search.js | 61 +++++--- public/src/js/settings.js | 16 +- public/src/js/songselect.js | 8 +- public/src/js/titlescreen.js | 2 +- public/src/js/view.js | 4 - 16 files changed, 354 insertions(+), 180 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 91c5032..0b1ad1d 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,3 +1,3 @@ -###### 下記の問題を説明してください。 スクリーンショットと診断情報を含めてください。 -###### Describe the problem you are having below. Please include a screenshot and the diagnostic information. + + diff --git a/public/src/js/browsersupport.js b/public/src/js/browsersupport.js index 5555826..12af3c0 100644 --- a/public/src/js/browsersupport.js +++ b/public/src/js/browsersupport.js @@ -14,6 +14,10 @@ function browserSupport(){ eval("class a{}") return true }, + "Class field declarations": function(){ + eval("class a{a=1}") + return true + }, "Array.find": function(){ return "find" in Array.prototype && "findIndex" in Array.prototype }, diff --git a/public/src/js/canvasdraw.js b/public/src/js/canvasdraw.js index ee021f0..72f6a91 100644 --- a/public/src/js/canvasdraw.js +++ b/public/src/js/canvasdraw.js @@ -462,8 +462,8 @@ config.selectable.innerHTML = "" var scale = config.selectableScale var style = config.selectable.style - style.left = (config.x - config.width / 2) * scale + "px" - style.top = config.y * scale + "px" + style.left = ((config.x - config.width / 2) * scale + (config.selectableX || 0)) + "px" + style.top = (config.y * scale + (config.selectableY || 0)) + "px" style.width = config.width * scale + "px" style.height = (drawnHeight+15) * scale + "px" style.fontSize = 40 * mul * scale + "px" diff --git a/public/src/js/customsongs.js b/public/src/js/customsongs.js index bcf7640..a20146c 100644 --- a/public/src/js/customsongs.js +++ b/public/src/js/customsongs.js @@ -89,6 +89,11 @@ class CustomSongs{ var dropContent = this.dropzone.getElementsByClassName("view-content")[0] dropContent.innerText = strings.customSongs.dropzone this.dragging = false + this.dragTarget = null + pageEvents.add(document, "dragenter", event => { + event.preventDefault() + this.dragTarget = event.target + }) pageEvents.add(document, "dragover", event => { event.preventDefault() if(!this.locked){ @@ -100,8 +105,11 @@ class CustomSongs{ } }) pageEvents.add(document, "dragleave", () => { - this.dropzone.classList.remove("dragover") - this.dragging = false + if(this.dragTarget === event.target){ + event.preventDefault() + this.dropzone.classList.remove("dragover") + this.dragging = false + } }) pageEvents.add(document, "drop", this.filesDropped.bind(this)) } @@ -522,8 +530,9 @@ class CustomSongs{ pageEvents.remove(this.errorDiv, ["mousedown", "touchstart"]) pageEvents.remove(this.errorEnd, ["mousedown", "touchstart"]) if(DataTransferItem.prototype.webkitGetAsEntry){ - pageEvents.remove(document, ["dragover", "dragleave", "drop"]) + pageEvents.remove(document, ["dragenter", "dragover", "dragleave", "drop"]) delete this.dropzone + delete this.dragTarget } if(gpicker){ gpicker.tokenResolve = null diff --git a/public/src/js/game.js b/public/src/js/game.js index 27eefb2..ba2c3f3 100644 --- a/public/src/js/game.js +++ b/public/src/js/game.js @@ -172,37 +172,48 @@ class Game{ var measure = measures[i] if(measure.ms > ms){ break - }else if(measure.nextBranch && !measure.gameChecked){ - measure.gameChecked = true - var branch = measure.nextBranch - if(branch.type){ - var accuracy = 0 - if(branch.type === "drumroll"){ - if(force.branch){ - var accuracy = Math.max(0, branch.requirement[force.branch]) - }else{ - var accuracy = this.sectionDrumroll + }else{ + if(measure.nextBranch && !measure.gameChecked){ + measure.gameChecked = true + var branch = measure.nextBranch + if(branch.type){ + var accuracy = 0 + if(branch.type === "drumroll"){ + if(force.branch){ + var accuracy = Math.max(0, branch.requirement[force.branch]) + }else{ + var accuracy = this.sectionDrumroll + } + }else if(this.sectionNotes.length !== 0){ + if(force.branch){ + var accuracy = Math.max(0, Math.min(100, branch.requirement[force.branch])) + }else{ + var accuracy = this.sectionNotes.reduce((a, b) => a + b) / this.sectionNotes.length * 100 + } } - }else if(this.sectionNotes.length !== 0){ - if(force.branch){ - var accuracy = Math.max(0, Math.min(100, branch.requirement[force.branch])) + if(accuracy >= branch.requirement.master){ + this.setBranch(branch, "master") + }else if(accuracy >= branch.requirement.advanced){ + this.setBranch(branch, "advanced") }else{ - var accuracy = this.sectionNotes.reduce((a, b) => a + b) / this.sectionNotes.length * 100 + this.setBranch(branch, "normal") } + }else if(this.controller.multiplayer === 1){ + p2.send("branch", "normal") } - if(accuracy >= branch.requirement.master){ - this.setBranch(branch, "master") - }else if(accuracy >= branch.requirement.advanced){ - this.setBranch(branch, "advanced") - }else{ - this.setBranch(branch, "normal") - } - }else if(this.controller.multiplayer === 1){ - p2.send("branch", "normal") + } + if(!measure.branch){ + this.controller.lyrics.branch = null + }else if(measure.branch.active){ + this.controller.lyrics.branch = measure.branch.name } } } } + + if(this.controller.lyrics){ + this.controller.lyrics.update(ms) + } } fixNoteStream(keysDon){ var circleIsNote = circle => { diff --git a/public/src/js/gpicker.js b/public/src/js/gpicker.js index f46f457..869ef31 100644 --- a/public/src/js/gpicker.js +++ b/public/src/js/gpicker.js @@ -14,7 +14,7 @@ class Gpicker{ this.clientCallbackBind = this.clientCallback.bind(this) } browse(lockedCallback, errorCallback){ - return this.loadApi() + return this.loadApi(lockedCallback, errorCallback) .then(() => this.getToken(lockedCallback, errorCallback)) .then(() => new Promise((resolve, reject) => { this.displayPicker(data => { @@ -120,7 +120,7 @@ class Gpicker{ }) })) } - loadApi(){ + loadApi(lockedCallback=()=>{}, errorCallback=()=>{}){ if(window.gapi && gapi.client && gapi.client.drive){ return Promise.resolve() } @@ -128,15 +128,27 @@ class Gpicker{ loader.loadScript("https://apis.google.com/js/api.js"), loader.loadScript("https://accounts.google.com/gsi/client") ] + var apiLoaded = false return Promise.all(promises).then(() => new Promise((resolve, reject) => gapi.load("picker:client", { callback: resolve, onerror: reject }) )) - .then(() => new Promise((resolve, reject) => - gapi.client.load("drive", "v3").then(resolve, reject) - )) + .then(() => new Promise((resolve, reject) => { + setTimeout(() => { + if(!apiLoaded){ + lockedCallback(false) + } + }, 3000) + return gapi.client.load("drive", "v3").then(resolve, reject) + })).then(() => { + apiLoaded = true + lockedCallback(true) + }).catch(e => { + errorCallback(Array.isArray(e) ? e[0] : e) + return Promise.reject("cancel") + }) } getClient(errorCallback=()=>{}, force){ var obj = { diff --git a/public/src/js/importsongs.js b/public/src/js/importsongs.js index 40c29b7..5fd673b 100644 --- a/public/src/js/importsongs.js +++ b/public/src/js/importsongs.js @@ -37,17 +37,14 @@ } } }) - this.assetSelectors = { - "bg-pattern-1": ".pattern-bg", - "bg_genre_0": "#song-select", - "title-screen": "#title-screen", - "dancing-don": "#loading-don", - "touch_drum": "#touch-drum-img", - "touch_fullscreen": "#touch-full-btn", - "touch_pause": "#touch-pause-btn", - "bg_stage_1": ".song-stage-1", - "bg_stage_2": ".song-stage-2", - "bg_stage_3": ".song-stage-3" + this.assetSelectors = {} + for(var selector in assets.cssBackground){ + var filename = assets.cssBackground[selector] + var index = filename.lastIndexOf(".") + if(index !== -1){ + filename = filename.slice(0, index) + } + this.assetSelectors[filename] = selector } this.comboVoices = ["v_combo_50"].concat(Array.from(Array(50), (d, i) => "v_combo_" + ((i + 1) * 100))) } @@ -64,7 +61,7 @@ var file = files[i] var name = file.name.toLowerCase() var path = file.path.toLowerCase() - if(name.endsWith(".tja")){ + if(name.endsWith(".tja") || name.endsWith(".tjf")){ this.tjaFiles.push({ file: file, index: i @@ -292,9 +289,9 @@ if(gt === maker.length - 1){ var lt = maker.lastIndexOf("<") if(lt !== -1 && lt !== gt - 2){ - url = maker.slice(lt + 2, gt) + url = maker.slice(lt + 1, gt).trim() if(url.startsWith("http://") || url.startsWith("https://")){ - maker = maker.slice(0, lt).trim() + maker = maker.slice(0, lt) }else{ url = null } @@ -452,12 +449,20 @@ vectors = JSON.parse(response) })) } - if(name.endsWith(".png")){ + if(name.endsWith(".png") || name.endsWith(".gif")){ let image = document.createElement("img") promises.push(pageEvents.load(image).then(() => { if(id in this.assetSelectors){ var selector = this.assetSelectors[id] - this.stylesheet.push(selector + '{background-image:url("' + image.src + '")}') + var gradient = "" + if(selector === "#song-search"){ + gradient = loader.songSearchGradient + } + this.stylesheet.push(loader.cssRuleset({ + [selector]: { + "background-image": gradient + "url(\"" + image.src + "\")" + } + })) } })) image.id = name diff --git a/public/src/js/loader.js b/public/src/js/loader.js index 2caeb8e..bc42d90 100644 --- a/public/src/js/loader.js +++ b/public/src/js/loader.js @@ -9,6 +9,7 @@ class Loader{ this.screen = document.getElementById("screen") this.startTime = Date.now() this.errorMessages = [] + this.songSearchGradient = "linear-gradient(to top, rgba(245, 246, 252, 0.08), #ff5963), " var promises = [] @@ -105,7 +106,7 @@ class Loader{ if(selector === ".pattern-bg"){ loader.screen.style.backgroundImage = "url(\"" + blobUrl + "\")" }else if(selector === "#song-search"){ - gradient = "linear-gradient(to top, rgba(245, 246, 252, 0.08), #ff5963), " + gradient = this.songSearchGradient } css.push(this.cssRuleset({ [selector]: { @@ -377,9 +378,9 @@ class Loader{ plugin.loadErrors = true promises.push(plugin.load(true).then(() => { if(obj.start){ - plugin.start() + return plugin.start(false, true) } - }, response => { + }).catch(response => { return this.errorMsg(response, obj.url) })) } @@ -394,14 +395,14 @@ class Loader{ this.callback(songId) this.ready = true pageEvents.send("ready", readyEvent) - }, () => this.errorMsg()) - }, () => this.errorMsg()) + }, e => this.errorMsg(e)) + }, e => this.errorMsg(e)) }) } addPromise(promise, url){ this.promises.push(promise) promise.then(this.assetLoaded.bind(this), response => { - this.errorMsg(response, url) + return this.errorMsg(response, url) }) } soundUrl(name){ @@ -417,9 +418,20 @@ class Loader{ return name.slice(0, name.lastIndexOf(".")) } errorMsg(error, url){ + var rethrow if(url || error){ + if(typeof error === "object" && error.constructor === Error){ + rethrow = error + error = error.stack || "" + var index = error.indexOf("\n ") + if(index !== -1){ + error = error.slice(0, index) + } + }else if(Array.isArray(error)){ + error = error[0] + } if(url){ - error = (Array.isArray(error) ? error[0] + ": " : (error ? error + ": " : "")) + url + error = (error ? error + ": " : "") + url } this.errorMessages.push(error) pageEvents.send("loader-error", url || error) @@ -495,7 +507,10 @@ class Loader{ } var percentage = Math.floor(this.loadedAssets * 100 / (this.promises.length + this.afterJSCount)) this.errorTxt.element[this.errorTxt.method] = "```\n" + this.errorMessages.join("\n") + "\nPercentage: " + percentage + "%\n```" - return Promise.reject(error) + if(rethrow || error){ + console.error(rethrow || error) + } + return Promise.reject() } assetLoaded(){ if(!this.error){ diff --git a/public/src/js/lyrics.js b/public/src/js/lyrics.js index 6cd6bd6..2e0bbdc 100644 --- a/public/src/js/lyrics.js +++ b/public/src/js/lyrics.js @@ -131,12 +131,14 @@ class Lyrics{ } ms += this.songOffset + this.vttOffset var currentLine = this.lines[this.current] - while(currentLine && ms > currentLine.end){ + while(currentLine && (ms > currentLine.end || currentLine.branch && this.branch && currentLine.branch !== this.branch)){ currentLine = this.lines[++this.current] } if(this.shown !== this.current){ if(currentLine && ms >= currentLine.start){ - this.setText(this.lines[this.current].text) + if(!currentLine.copy){ + this.setText(currentLine.text) + } this.shown = this.current }else if(this.shown !== -1){ this.setText("") diff --git a/public/src/js/parsetja.js b/public/src/js/parsetja.js index ab20e9c..de69a5e 100644 --- a/public/src/js/parsetja.js +++ b/public/src/js/parsetja.js @@ -48,7 +48,7 @@ this.beatInfo = {} this.events = [] if(!metaOnly){ - this.circles = this.parseCircles() + this.circles = this.parseCircles(difficulty) } } parseMetadata(){ @@ -135,13 +135,15 @@ } return [string.slice(0, index), string.slice(index + delimiter.length)] } - parseCircles(){ - var meta = this.metadata[this.difficulty] || {} + parseCircles(difficulty, lyricsOnly){ + var meta = this.metadata[difficulty] || {} var ms = (meta.offset || 0) * -1000 + this.offset var bpm = Math.abs(meta.bpm) || 120 var scroll = 1 var measure = 4 - this.beatInfo.beatInterval = 60000 / bpm + if(!lyricsOnly){ + this.beatInfo.beatInterval = 60000 / bpm + } var gogo = false var barLine = true @@ -150,6 +152,7 @@ var lastDrumroll = false + var branches var branch = false var branchObj = {} var currentBranch = false @@ -158,12 +161,17 @@ var sectionBegin = true var lastBpm = bpm var lastGogo = gogo + var lyrics + var lyricsIndex = null var lyricsLine = null + var lyricsCopy = false + var measures = [] var currentMeasure = [] var firstNote = true var circles = [] var circleID = 0 + var events = [] var regexAZ = /[A-Z]/ var regexSpace = /\s/ var regexLinebreak = /\\n/g @@ -200,14 +208,16 @@ }else{ var speed = bpm * scroll / 60 } - this.measures.push({ - ms: ms, - originalMS: ms, - speed: speed, - visible: barLine, - branch: currentBranch, - branchFirst: branchFirstMeasure - }) + if(!lyricsOnly){ + measures.push({ + ms: ms, + originalMS: ms, + speed: speed, + visible: barLine, + branch: currentBranch, + branchFirst: branchFirstMeasure + }) + } branchFirstMeasure = false if(currentMeasure.length){ for(var i = 0; i < currentMeasure.length; i++){ @@ -229,63 +239,79 @@ } var note_chain = []; for (var i = 0; i < currentMeasure.length; i++){ - //console.log(note_chain.length); var note = currentMeasure[i] - circleID++ - var circleObj = new Circle({ - id: circleID, - start: note.start, - type: note.type, - txt: note.txt, - speed: note.bpm * note.scroll / 60, - gogoTime: note.gogo, - endTime: note.endTime, - requiredHits: note.requiredHits, - beatMS: 60000 / note.bpm, - branch: currentBranch, - section: note.section - }) - if (note.type) { - if (note.type === "don" || note.type === "ka" || note.type === "daiDon" || note.type === "daiKa") { - note_chain.push(circleObj); - } else { - if (note_chain.length > 1 && currentMeasure.length >= 8) { - checkChain(note_chain, currentMeasure.length, false); + if(!lyricsOnly){ + circleID++ + var circleObj = new Circle({ + id: circleID, + start: note.start, + type: note.type, + txt: note.txt, + speed: note.bpm * note.scroll / 60, + gogoTime: note.gogo, + endTime: note.endTime, + requiredHits: note.requiredHits, + beatMS: 60000 / note.bpm, + branch: currentBranch, + section: note.section + }) + if(note.type){ + if(note.type === "don" || note.type === "ka" || note.type === "daiDon" || note.type === "daiKa"){ + note_chain.push(circleObj) + }else{ + if(note_chain.length > 1 && currentMeasure.length >= 8){ + checkChain(note_chain, currentMeasure.length, false) + } + note_chain = [] } - note_chain = []; - } - if (lastDrumroll === note) { - lastDrumroll = circleObj + if (lastDrumroll === note) { + lastDrumroll = circleObj + } + + if(note.type !== "event"){ + circles.push(circleObj) + } + }else if( + (currentMeasure.length < 24 || + currentMeasure[i + 1] + && !currentMeasure[i + 1].type + ) && (currentMeasure.length < 48 || + currentMeasure[i + 2] + && !currentMeasure[i + 2].type + && currentMeasure[i + 3] + && !currentMeasure[i + 3].type + ) + ){ + if(note_chain.length > 1 && currentMeasure.length >= 8){ + checkChain(note_chain, currentMeasure.length, true) + } + note_chain = [] } - - if(note.type !== "event"){ - circles.push(circleObj) + if(note.event){ + events.push(circleObj) } - } else if (!(currentMeasure.length >= 24 && (!currentMeasure[i + 1] || currentMeasure[i + 1].type)) - && !(currentMeasure.length >= 48 && (!currentMeasure[i + 2] || currentMeasure[i + 2].type || !currentMeasure[i + 3] || currentMeasure[i + 3].type))) { - if (note_chain.length > 1 && currentMeasure.length >= 8) { - checkChain(note_chain, currentMeasure.length, true); - } - note_chain = []; - } - if(note.event){ - this.events.push(circleObj) } + var lyricsObj = null if("lyricsLine" in note){ - if(!this.lyrics){ - this.lyrics = [] - } - if(this.lyrics.length !== 0){ - this.lyrics[this.lyrics.length - 1].end = note.start - } - this.lyrics.push({ + lyricsObj = { start: note.start, text: note.lyricsLine - }) + } + }else if(note.lyricsCopy){ + lyricsObj = { + start: note.start, + copy: true + } + } + if(lyricsObj){ + if(currentBranch){ + lyricsObj.branch = currentBranch.name + } + insertLyrics(lyricsObj) } } - if (note_chain.length > 1 && currentMeasure.length >= 8) { - checkChain(note_chain, currentMeasure.length, false); + if(!lyricsOnly && note_chain.length > 1 && currentMeasure.length >= 8){ + checkChain(note_chain, currentMeasure.length, false) } }else{ var msPerMeasure = 60000 * measure / bpm @@ -302,7 +328,10 @@ if(lyricsLine !== null){ circleObj.lyricsLine = lyricsLine lyricsLine = null + }else if(lyricsCopy){ + circleObj.lyricsCopy = true } + lyricsCopy = false currentMeasure.push(circleObj) } } @@ -322,17 +351,32 @@ if(lyricsLine !== null){ circleObj2.lyricsLine = lyricsLine lyricsLine = null + }else if(lyricsCopy){ + circleObj2.lyricsCopy = true } + lyricsCopy = false currentMeasure.push(circleObj2) } if(circleObj){ if(lyricsLine !== null){ circleObj.lyricsLine = lyricsLine lyricsLine = null + }else if(lyricsCopy){ + circleObj.lyricsCopy = true } + lyricsCopy = false currentMeasure.push(circleObj) } } + var insertLyrics = obj => { + if(!lyrics){ + lyrics = [] + }else if(lyricsIndex !== null){ + lyrics[lyricsIndex].end = obj.start + } + lyricsIndex = lyrics.length + lyrics.push(obj) + } for(var lineNum = meta.start; lineNum < meta.end; lineNum++){ var line = this.data[lineNum] @@ -377,11 +421,18 @@ gogo: gogo, bpm: bpm, scroll: scroll, - sectionBegin: sectionBegin + sectionBegin: sectionBegin, + lyricsCopy: !!lyrics } + if(lyrics && lyricsIndex !== null){ + var line = lyrics[lyricsIndex] + line.end = ms + } + lyricsIndex = null + value = value.split(",") - if(!this.branches){ - this.branches = [] + if(!branches){ + branches = [] } var req = { advanced: parseFloat(value[1]) || 0, @@ -399,12 +450,12 @@ type: value[0].trim().toLowerCase() === "r" ? "drumroll" : "accuracy", requirement: req } - this.branches.push(branchObj) - if(this.measures.length === 1 && branchObj.type === "drumroll"){ + branches.push(branchObj) + if(measures.length === 1 && branchObj.type === "drumroll"){ for(var i = circles.length; i--;){ var circle = circles[i] - if(circle.endTime && circle.type === "drumroll" || circle.type === "daiDrumroll" || circle.type === "balloon"){ - this.measures.push({ + if(circle.endTime && (circle.type === "drumroll" || circle.type === "daiDrumroll" || circle.type === "balloon")){ + measures.push({ ms: circle.endTime, originalMS: circle.endTime, speed: circle.bpm * circle.scroll / 60, @@ -415,13 +466,14 @@ } } } - if(this.measures.length !== 0){ - this.measures[this.measures.length - 1].nextBranch = branchObj + if(measures.length !== 0){ + measures[measures.length - 1].nextBranch = branchObj } break case "branchend": branch = false currentBranch = false + lyricsCopy = lyricsCopy || !!lyrics break case "section": sectionBegin = true @@ -433,11 +485,19 @@ if(!branch){ break } + if(lyrics){ + if(lyricsIndex !== null){ + var line = lyrics[lyricsIndex] + line.end = ms + } + lyricsIndex = null + } ms = branchSettings.ms gogo = branchSettings.gogo bpm = branchSettings.bpm scroll = branchSettings.scroll sectionBegin = branchSettings.sectionBegin + lyricsCopy = branchSettings.lyricsCopy branchFirstMeasure = true var branchName = name === "m" ? "master" : (name === "e" ? "advanced" : "normal") currentBranch = { @@ -556,31 +616,52 @@ } } - pushMeasure() if(lastDrumroll){ lastDrumroll.endTime = ms lastDrumroll.originalEndTime = ms } + if(lyricsLine !== null){ + insertLyrics({ + start: ms, + text: lyricsLine + }) + } + pushMeasure() - if(this.branches){ - circles.sort((a, b) => a.ms > b.ms ? 1 : -1) - this.measures.sort((a, b) => a.ms > b.ms ? 1 : -1) - circles.forEach((circle, i) => circle.id = i + 1) + if(!lyricsOnly){ + if(branches){ + circles.sort((a, b) => a.ms > b.ms ? 1 : -1) + measures.sort((a, b) => a.ms > b.ms ? 1 : -1) + circles.forEach((circle, i) => circle.id = i + 1) + } + this.measures = measures + this.events = events + this.branches = branches + this.scoreinit = meta.scoreinit + this.scorediff = meta.scorediff + if(this.scoreinit && this.scorediff){ + this.scoremode = meta.scoremode || 1 + }else{ + this.scoremode = meta.scoremode || 2 + var autoscore = new AutoScore(difficulty, this.stars, this.scoremode, circles) + this.scoreinit = autoscore.ScoreInit + this.scorediff = autoscore.ScoreDiff + } } - this.scoreinit = meta.scoreinit; - this.scorediff = meta.scorediff; - if (this.scoreinit && this.scorediff) { - this.scoremode = meta.scoremode || 1; - } else { - this.scoremode = meta.scoremode || 2; - var autoscore = new AutoScore(this.difficulty, this.stars, this.scoremode, circles); - this.scoreinit = autoscore.ScoreInit; - this.scorediff = autoscore.ScoreDiff; - } - if(this.lyrics){ - var line = this.lyrics[this.lyrics.length - 1] + if(lyrics && lyricsIndex !== null){ + var line = lyrics[lyricsIndex] line.end = Math.max(ms, line.start) + 5000 } + if(lyrics){ + this.lyrics = lyrics + }else if(!lyricsOnly){ + for(var courseName in this.metadata){ + if(this.metadata[courseName].inlineLyrics){ + this.parseCircles(courseName, true) + break + } + } + } return circles } } diff --git a/public/src/js/plugins.js b/public/src/js/plugins.js index c9ca1e5..46fd35a 100644 --- a/public/src/js/plugins.js +++ b/public/src/js/plugins.js @@ -373,6 +373,7 @@ class PluginLoader{ } }, e => { this.error() + plugins.remove(this.name) if(e.name === "SyntaxError"){ var error = new SyntaxError() error.stack = "Error in plugin syntax: " + this.name + "\n" + e.stack @@ -388,7 +389,7 @@ class PluginLoader{ }) } } - start(orderChange){ + start(orderChange, startErrors){ if(!orderChange){ plugins.startOrder.push(this.name) } @@ -403,10 +404,15 @@ class PluginLoader{ this.module.start() } }catch(e){ + this.error() var error = new Error() error.stack = "Error in plugin start: " + this.name + "\n" + e.stack - console.error(error) - this.error() + if(startErrors){ + return Promise.reject(error) + }else{ + console.error(error) + return Promise.resolve() + } } } }) diff --git a/public/src/js/search.js b/public/src/js/search.js index ae00331..c3a8fc0 100644 --- a/public/src/js/search.js +++ b/public/src/js/search.js @@ -49,9 +49,7 @@ class Search{ var results = [] var filters = {} - var querySplit = query.split(" ") - var editedSplit = query.split(" ") - querySplit.forEach(word => { + var querySplit = query.split(" ").filter(word => { if(word.length > 0){ var parts = word.toLowerCase().split(":") if(parts.length > 1){ @@ -82,19 +80,23 @@ class Search{ case "maker": case "diverge": case "random": + case "all": filters[parts[0]] = parts[1] break + default: + return true } - - editedSplit.splice(editedSplit.indexOf(word), 1) + return false } } + return true }) - query = this.normalizeString(editedSplit.join(" ").trim()) + query = this.normalizeString(querySplit.join(" ").trim()) var totalFilters = Object.keys(filters).length var random = false + var allResults = false for(var i = 0; i < assets.songs.length; i++){ var song = assets.songs[i] var passedFilters = 0 @@ -169,6 +171,12 @@ class Search{ passedFilters++ } break + case "all": + if(value === "yes" || value === "no"){ + allResults = value === "yes" + passedFilters++ + } + break } }) @@ -177,7 +185,7 @@ class Search{ } } - var maxResults = totalFilters > 0 && !query ? 100 : 50 + var maxResults = allResults ? Infinity : (totalFilters > 0 && !query ? 100 : 50) if(query){ results = fuzzysort.go(query, results, { @@ -233,6 +241,15 @@ class Search{ } } } + if(random){ + var rand = Math.random() * -9000 + if(score0 !== -Infinity){ + score0 = rand + } + if(score1 !== -Infinity){ + score1 = rand + } + } if(a[0]){ return a[1] ? Math.max(score0, score1) : score0 }else{ @@ -241,18 +258,18 @@ class Search{ } }) }else{ + if(random){ + for(var i = results.length - 1; i > 0; i--){ + var j = Math.floor(Math.random() * (i + 1)) + var temp = results[i] + results[i] = results[j] + results[j] = temp + } + } results = results.slice(0, maxResults).map(result => { return {obj: result} }) } - if(random){ - for(var i = results.length - 1; i > 0; i--){ - var j = Math.floor(Math.random() * (i + 1)) - var temp = results[i] - results[i] = results[j] - results[j] = temp - } - } return results } @@ -402,7 +419,7 @@ class Search{ this.input = this.div.querySelector(":scope #song-search-input") this.input.setAttribute("placeholder", strings.search.searchInput) - pageEvents.add(this.input, ["input"], this.onInput.bind(this)) + pageEvents.add(this.input, ["input"], () => this.onInput()) this.songSelect.playSound("se_pause") loader.screen.appendChild(this.div) @@ -484,6 +501,9 @@ class Search{ var song = this.songSelect.songs.find(song => song.id === songId) this.remove() this.songSelect.playBgm(false) + if(this.songSelect.previewing === "muted"){ + this.songSelect.previewing = null + } var songIndex = this.songSelect.songs.findIndex(song => song.id === songId) this.songSelect.setSelectedSong(songIndex) @@ -532,13 +552,15 @@ class Search{ return ranges } - onInput(){ + onInput(resize){ var text = this.input.value localStorage.setItem("lastSearchQuery", text) text = text.toLowerCase() if(text.length === 0){ - this.setTip() + if(!resize){ + this.setTip() + } return } @@ -623,6 +645,9 @@ class Search{ this.proceed(parseInt(this.results[this.active].dataset.songId)) }else{ this.onInput() + if(event.keyCode === 13 && this.songSelect.touchEnabled){ + this.input.blur() + } } } } diff --git a/public/src/js/settings.js b/public/src/js/settings.js index b171920..46d4fee 100644 --- a/public/src/js/settings.js +++ b/public/src/js/settings.js @@ -367,7 +367,7 @@ class SettingsView{ if(event.target.tagName !== "SPAN"){ this.setValue(i) } - }) + }, true) }else{ this.addTouchEnd(settingBox, event => this.setValue(i)) } @@ -494,9 +494,9 @@ class SettingsView{ } this.addTouch(settingBox, event => { if(event.target.tagName !== "SPAN"){ - this.latencySetValue(current, event.type === "touchstart") + this.latencySetValue(current, event.type === "touchend") } - }) + }, true) if(current !== "calibration"){ outputObject.valueDiv = valueDiv outputObject.valueText = valueText @@ -543,7 +543,9 @@ class SettingsView{ var touchEvent = end ? "touchend" : "touchstart" pageEvents.add(element, ["mousedown", touchEvent], event => { if(event.type === touchEvent){ - event.preventDefault() + if(event.cancelable){ + event.preventDefault() + } this.touched = true }else if(event.which !== 1){ return @@ -594,7 +596,11 @@ class SettingsView{ if(current.type === "language"){ value = allStrings[value].name + " (" + value + ")" }else if(current.type === "select" || current.type === "gamepad"){ - value = strings.settings[name][value] + if(current.options_lang && current.options_lang[value]){ + value = this.getLocalTitle(value, current.options_lang[value]) + }else if(!current.getItem){ + value = strings.settings[name][value] + } }else if(current.type === "toggle"){ value = value ? strings.settings.on : strings.settings.off }else if(current.type === "keyboard"){ diff --git a/public/src/js/songselect.js b/public/src/js/songselect.js index 210eb87..705a0d0 100644 --- a/public/src/js/songselect.js +++ b/public/src/js/songselect.js @@ -412,7 +412,7 @@ class SongSelect{ if(name === "ctrl" || name === "shift" || !this.redrawRunning){ return } - var ctrl = this.pressedKeys["ctrl"] || this.pressedKeys["ctrlGamepad"] + var ctrl = event ? event.ctrlKey : (this.pressedKeys["ctrl"] || this.pressedKeys["ctrlGamepad"]) var shift = event ? event.shiftKey : this.pressedKeys["shift"] if(this.state.showWarning){ if(name === "confirm"){ @@ -1076,12 +1076,13 @@ class SongSelect{ this.selectableText = "" if(this.search.opened && this.search.container){ - this.search.onInput() + this.search.onInput(true) } }else if(!document.hasFocus() && !p2.session){ if(this.state.focused){ this.state.focused = false this.songSelect.classList.add("unfocused") + this.pressedKeys = {} } return }else{ @@ -2046,7 +2047,8 @@ class SongSelect{ fontSize: 40, fontFamily: this.font, selectable: this.selectable, - selectableScale: this.ratio / this.pixelRatio + selectableScale: this.ratio / this.pixelRatio, + selectableX: Math.max(0, innerWidth / 2 - lastHeight * 16 / 9) }) this.selectable.style.display = "" this.selectableText = currentSong.title diff --git a/public/src/js/titlescreen.js b/public/src/js/titlescreen.js index a1238d7..459121e 100644 --- a/public/src/js/titlescreen.js +++ b/public/src/js/titlescreen.js @@ -101,7 +101,7 @@ class Titlescreen{ }).then(() => { if(noError){ setTimeout(() => { - new SongSelect("customSongs", false, this.touchEnabled) + new SongSelect("customSongs", false, this.touched) }, 500) } }) diff --git a/public/src/js/view.js b/public/src/js/view.js index 2005924..f67ee89 100644 --- a/public/src/js/view.js +++ b/public/src/js/view.js @@ -255,10 +255,6 @@ this.setDonBgHeight() } - if(this.controller.lyrics){ - this.controller.lyrics.update(ms) - } - ctx.save() ctx.translate(0, frameTop)