Merge pull request #448 from bui/fixes

Fixes
This commit is contained in:
Bui 2022-03-16 14:14:16 +00:00 committed by GitHub
commit a7aee747a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 993 additions and 755 deletions

View File

@ -1,39 +0,0 @@
.pattern-bg{
background-image: url("bg-pattern-1.png");
}
#song-select{
background-image: url("bg_genre_0.png");
}
#title-screen{
background-image: url("title-screen.png");
}
#loading-don{
background-image: url("dancing-don.gif");
}
#touch-full-btn{
background-image: url("touch_fullscreen.png");
}
#touch-pause-btn{
background-image: url("touch_pause.png");
}
.settings-outer{
background-image: url("bg_settings.png");
}
#gamepad-bg,
#gamepad-buttons{
background-image: url("settings_gamepad.png");
}
#song-search{
background: linear-gradient(to top, rgb(245 246 252 / 8%), #ff5963), url("bg_search.png");
background-size: auto, 3.12em;
background-position: -1.2em;
}
.song-search-result-course::before{
background-image: url("difficulty.png");
}
.song-search-result-crown{
background-image: url("crown.png");
}
.song-search-tip-error{
background-image: url("miss.png");
}

View File

@ -127,3 +127,15 @@
#song-lyrics rt{ #song-lyrics rt{
line-height: 1; line-height: 1;
} }
.pixelated #canvas,
.pixelated .donbg>div,
.pixelated #songbg>div,
.pixelated #song-stage,
.pixelated #touch-drum-img,
.pixelated #flowers1-in,
.pixelated #flowers2-in,
.pixelated #mikoshi-in,
.pixelated #tetsuo-in,
.pixelated #hana-in{
image-rendering: pixelated;
}

View File

@ -26,6 +26,8 @@
padding: 1em 1em 0 1em; padding: 1em 1em 0 1em;
z-index: 1; z-index: 1;
box-sizing: border-box; box-sizing: border-box;
background-size: auto, 3.12em;
background-position: 0%, -2%;
} }
#song-search-container.touch-enabled{ #song-search-container.touch-enabled{
@ -96,6 +98,7 @@
box-sizing: border-box; box-sizing: border-box;
-moz-box-sizing: border-box; -moz-box-sizing: border-box;
-webkit-box-sizing: border-box; -webkit-box-sizing: border-box;
border: 0.4em solid;
} }
.song-search-result:last-of-type { .song-search-result:last-of-type {
@ -133,6 +136,7 @@
content: attr(alt); content: attr(alt);
position: absolute; position: absolute;
z-index: -1; z-index: -1;
-webkit-text-stroke-width: 0.4em;
} }
.song-search-result-course { .song-search-result-course {

View File

@ -59,7 +59,9 @@ class RemoteFile{
} }
} }
blob(){ blob(){
return this.arrayBuffer().then(response => new Blob([response])) return loader.ajax(this.url, request => {
request.responseType = "blob"
})
} }
} }
class LocalFile{ class LocalFile{
@ -113,7 +115,7 @@ class GdriveFile{
this.url = gpicker.filesUrl + this.id + "?alt=media" this.url = gpicker.filesUrl + this.id + "?alt=media"
} }
arrayBuffer(){ arrayBuffer(){
return gpicker.downloadFile(this.id, true) return gpicker.downloadFile(this.id, "arraybuffer")
} }
read(encoding){ read(encoding){
if(encoding){ if(encoding){
@ -123,7 +125,7 @@ class GdriveFile{
} }
} }
blob(){ blob(){
return this.arrayBuffer().then(response => new Blob([response])) return gpicker.downloadFile(this.id, "blob")
} }
} }
class CachedFile{ class CachedFile{
@ -144,6 +146,6 @@ class CachedFile{
return this.arrayBuffer() return this.arrayBuffer()
} }
blob(){ blob(){
return this.arrayBuffer().then(response => new Blob([response])) return this.arrayBuffer()
} }
} }

View File

@ -38,7 +38,8 @@ var assets = {
"customsongs.js", "customsongs.js",
"abstractfile.js", "abstractfile.js",
"idb.js", "idb.js",
"plugins.js" "plugins.js",
"search.js"
], ],
"css": [ "css": [
"main.css", "main.css",
@ -50,20 +51,13 @@ var assets = {
"view.css", "view.css",
"search.css" "search.css"
], ],
"assetsCss": [
"img/img.css"
],
"img": [ "img": [
"title-screen.png",
"notes.png", "notes.png",
"notes_drumroll.png", "notes_drumroll.png",
"notes_hit.png", "notes_hit.png",
"notes_explosion.png", "notes_explosion.png",
"balloon.png", "balloon.png",
"taiko.png", "taiko.png",
"dancing-don.gif",
"bg-pattern-1.png",
"difficulty.png",
"don_anim_normal_a.png", "don_anim_normal_a.png",
"don_anim_normal_b1.png", "don_anim_normal_b1.png",
"don_anim_normal_b2.png", "don_anim_normal_b2.png",
@ -81,24 +75,26 @@ var assets = {
"don_anim_clear_b2.png", "don_anim_clear_b2.png",
"fire_anim.png", "fire_anim.png",
"fireworks_anim.png", "fireworks_anim.png",
"bg_genre_def.png",
"bg_score_p1.png", "bg_score_p1.png",
"bg_score_p2.png", "bg_score_p2.png",
"bg_settings.png",
"bg_pause.png", "bg_pause.png",
"badge_auto.png", "badge_auto.png",
"touch_pause.png", "mimizu.png"
"touch_fullscreen.png",
"mimizu.png",
"results_flowers.png",
"results_mikoshi.png",
"results_tetsuohana.png",
"results_tetsuohana2.png",
"settings_gamepad.png",
"crown.png",
"miss.png",
"bg_search.png"
], ],
"cssBackground": {
"#title-screen": "title-screen.png",
"#loading-don": "dancing-don.gif",
".pattern-bg": "bg-pattern-1.png",
".song-search-result-course::before": "difficulty.png",
"#song-select": "bg_genre_def.png",
".settings-outer": "bg_settings.png",
"#touch-pause-btn": "touch_pause.png",
"#touch-full-btn": "touch_fullscreen.png",
"#gamepad-bg, #gamepad-buttons": "settings_gamepad.png",
".song-search-result-crown": "crown.png",
".song-search-tip-error": "miss.png",
"#song-search": "bg_search.png"
},
"audioSfx": [ "audioSfx": [
"se_pause.ogg", "se_pause.ogg",
"se_calibration.ogg", "se_calibration.ogg",

View File

@ -2,7 +2,7 @@ class CustomSongs{
constructor(...args){ constructor(...args){
this.init(...args) this.init(...args)
} }
init(touchEnabled, noPage){ init(touchEnabled, noPage, noLoading){
this.loaderDiv = document.createElement("div") this.loaderDiv = document.createElement("div")
this.loaderDiv.innerHTML = assets.pages["loadsong"] this.loaderDiv.innerHTML = assets.pages["loadsong"]
var loadingText = this.loaderDiv.querySelector("#loading-text") var loadingText = this.loaderDiv.querySelector("#loading-text")
@ -13,6 +13,7 @@ class CustomSongs{
if(noPage){ if(noPage){
this.noPage = true this.noPage = true
this.noLoading = noLoading
return return
} }
@ -262,11 +263,13 @@ class CustomSongs{
var importSongs = new ImportSongs() var importSongs = new ImportSongs()
return importSongs.load(files).then(this.songsLoaded.bind(this), e => { return importSongs.load(files).then(this.songsLoaded.bind(this), e => {
this.browse.parentNode.reset() if(!this.noPage){
this.browse.form.reset()
}
this.locked = false this.locked = false
this.loading(false) this.loading(false)
if(e === "nosongs"){ if(e === "nosongs"){
this.showError(strings.customSongs.noSongs) this.showError(strings.customSongs.noSongs, "nosongs")
}else if(e !== "cancel"){ }else if(e !== "cancel"){
return Promise.reject(e) return Promise.reject(e)
} }
@ -308,7 +311,7 @@ class CustomSongs{
this.locked = false this.locked = false
this.loading(false) this.loading(false)
if(e === "nosongs"){ if(e === "nosongs"){
this.showError(strings.customSongs.noSongs) this.showError(strings.customSongs.noSongs, "nosongs")
}else if(e !== "cancel"){ }else if(e !== "cancel"){
return Promise.reject(e) return Promise.reject(e)
} }
@ -371,7 +374,7 @@ class CustomSongs{
open("privacy") open("privacy")
} }
loading(show){ loading(show){
if(this.noPage){ if(this.noLoading){
return return
} }
if(show){ if(show){
@ -387,14 +390,16 @@ class CustomSongs{
assets.customSongs = true assets.customSongs = true
assets.customSelected = this.noPage ? +localStorage.getItem("customSelected") : 0 assets.customSelected = this.noPage ? +localStorage.getItem("customSelected") : 0
} }
if(!this.noPage){ if(this.noPage){
pageEvents.send("import-songs", length)
}else{
assets.sounds["se_don"].play() assets.sounds["se_don"].play()
}
this.clean()
setTimeout(() => { setTimeout(() => {
new SongSelect("customSongs", false, this.touchEnabled) new SongSelect("customSongs", false, this.touchEnabled)
pageEvents.send("import-songs", length) pageEvents.send("import-songs", length)
}, 500) }, 500)
}
this.clean()
return songs && songs.length return songs && songs.length
} }
keyPressed(pressed, name){ keyPressed(pressed, name){
@ -474,10 +479,14 @@ class CustomSongs{
resolve() resolve()
}, 500)) }, 500))
} }
showError(text){ showError(text, errorName){
this.locked = false this.locked = false
this.loading(false) this.loading(false)
if(this.noPage || this.mode === "error"){ if(this.noPage){
var error = new Error(text)
error.name = errorName
throw error
}else if(this.mode === "error"){
return return
} }
this.mode = "error" this.mode = "error"

View File

@ -159,7 +159,7 @@ class Gpicker{
} }
clientCallback(tokenResponse){ clientCallback(tokenResponse){
this.tokenResponse = tokenResponse this.tokenResponse = tokenResponse
this.oauthToken = tokenResponse.access_token this.oauthToken = tokenResponse && tokenResponse.access_token
if(this.oauthToken && this.tokenResolve){ if(this.oauthToken && this.tokenResolve){
this.tokenResolve() this.tokenResolve()
} }
@ -220,12 +220,12 @@ class Gpicker{
.build() .build()
.setVisible(true) .setVisible(true)
} }
downloadFile(id, arrayBuffer, retry){ downloadFile(id, responseType, retry){
var url = this.filesUrl + id + "?alt=media" var url = this.filesUrl + id + "?alt=media"
return this.queue().then(this.getToken.bind(this)).then(() => return this.queue().then(this.getToken.bind(this)).then(() =>
loader.ajax(url, request => { loader.ajax(url, request => {
if(arrayBuffer){ if(responseType){
request.responseType = "arraybuffer" request.responseType = responseType
} }
request.setRequestHeader("Authorization", "Bearer " + this.oauthToken) request.setRequestHeader("Authorization", "Bearer " + this.oauthToken)
}, true).then(event => { }, true).then(event => {
@ -238,7 +238,7 @@ class Gpicker{
var e = response.error var e = response.error
if(e && e.errors[0].reason === "authError"){ if(e && e.errors[0].reason === "authError"){
delete this.oauthToken delete this.oauthToken
return this.downloadFile(id, arrayBuffer, true) return this.downloadFile(id, responseType, true)
}else{ }else{
return reject() return reject()
} }

View File

@ -61,12 +61,6 @@ class Loader{
stylesheet.href = "/src/css/" + name + this.queryString stylesheet.href = "/src/css/" + name + this.queryString
document.head.appendChild(stylesheet) document.head.appendChild(stylesheet)
}) })
assets.assetsCss.forEach(name => {
var stylesheet = document.createElement("link")
stylesheet.rel = "stylesheet"
stylesheet.href = gameConfig.assets_baseurl + name + this.queryString
document.head.appendChild(stylesheet)
})
var checkStyles = () => { var checkStyles = () => {
if(document.styleSheets.length >= cssCount){ if(document.styleSheets.length >= cssCount){
resolve() resolve()
@ -87,6 +81,7 @@ class Loader{
assets.img.forEach(name => { assets.img.forEach(name => {
var id = this.getFilename(name) var id = this.getFilename(name)
var image = document.createElement("img") var image = document.createElement("img")
image.crossOrigin = "anonymous"
var url = gameConfig.assets_baseurl + "img/" + name var url = gameConfig.assets_baseurl + "img/" + name
this.addPromise(pageEvents.load(image), url) this.addPromise(pageEvents.load(image), url)
image.id = name image.id = name
@ -95,6 +90,37 @@ class Loader{
assets.image[id] = image assets.image[id] = image
}) })
var css = []
for(let selector in assets.cssBackground){
let name = assets.cssBackground[selector]
var url = gameConfig.assets_baseurl + "img/" + name
this.addPromise(loader.ajax(url, request => {
request.responseType = "blob"
}).then(blob => {
var id = this.getFilename(name)
var image = document.createElement("img")
let blobUrl = URL.createObjectURL(blob)
var promise = pageEvents.load(image).then(() => {
var gradient = ""
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), "
}
css.push(this.cssRuleset({
[selector]: {
"background-image": gradient + "url(\"" + blobUrl + "\")"
}
}))
})
image.id = name
image.src = blobUrl
this.assetsDiv.appendChild(image)
assets.image[id] = image
return promise
}), url)
}
assets.views.forEach(name => { assets.views.forEach(name => {
var id = this.getFilename(name) var id = this.getFilename(name)
var url = "/src/views/" + name + this.queryString var url = "/src/views/" + name + this.queryString
@ -147,6 +173,10 @@ class Loader{
return return
} }
var style = document.createElement("style")
style.appendChild(document.createTextNode(css.join("\n")))
document.head.appendChild(style)
this.addPromise(this.ajax("/api/songs").then(songs => { this.addPromise(this.ajax("/api/songs").then(songs => {
songs = JSON.parse(songs) songs = JSON.parse(songs)
songs.forEach(song => { songs.forEach(song => {
@ -179,16 +209,22 @@ class Loader{
.filter(cat => cat.songSkin && cat.songSkin.bg_img) .filter(cat => cat.songSkin && cat.songSkin.bg_img)
.forEach(cat => { .forEach(cat => {
let name = cat.songSkin.bg_img let name = cat.songSkin.bg_img
var url = gameConfig.assets_baseurl + "img/" + name
categoryPromises.push(loader.ajax(url, request => {
request.responseType = "blob"
}).then(blob => {
var id = this.getFilename(name) var id = this.getFilename(name)
var image = document.createElement("img") var image = document.createElement("img")
var url = gameConfig.assets_baseurl + "img/" + name let blobUrl = URL.createObjectURL(blob)
categoryPromises.push(pageEvents.load(image).catch(response => { var promise = pageEvents.load(image)
return this.errorMsg(response, url)
}))
image.id = name image.id = name
image.src = url image.src = blobUrl
this.assetsDiv.appendChild(image) this.assetsDiv.appendChild(image)
assets.image[id] = image assets.image[id] = image
return promise
}).catch(response => {
return this.errorMsg(response, url)
}))
}) })
this.addPromise(Promise.all(categoryPromises)) this.addPromise(Promise.all(categoryPromises))
@ -356,6 +392,7 @@ class Loader{
this.canvasTest.clean() this.canvasTest.clean()
this.clean() this.clean()
this.callback(songId) this.callback(songId)
this.ready = true
pageEvents.send("ready", readyEvent) pageEvents.send("ready", readyEvent)
}, () => this.errorMsg()) }, () => this.errorMsg())
}, () => this.errorMsg()) }, () => this.errorMsg())
@ -407,7 +444,7 @@ class Loader{
if(!lang){ if(!lang){
lang = "en" lang = "en"
} }
loader.screen.getElementsByClassName("view-content")[0].innerText = allStrings[lang].errorOccured loader.screen.getElementsByClassName("view-content")[0].innerText = allStrings[lang] && allStrings[lang].errorOccured || allStrings.en.errorOccured
} }
var loaderError = loader.screen.getElementsByClassName("loader-error-div")[0] var loaderError = loader.screen.getElementsByClassName("loader-error-div")[0]
loaderError.style.display = "flex" loaderError.style.display = "flex"
@ -472,6 +509,19 @@ class Loader{
this.screen.innerHTML = assets.pages[name] this.screen.innerHTML = assets.pages[name]
this.screen.classList[patternBg ? "add" : "remove"]("pattern-bg") this.screen.classList[patternBg ? "add" : "remove"]("pattern-bg")
} }
cssRuleset(rulesets){
var css = []
for(var selector in rulesets){
var declarationsObj = rulesets[selector]
var declarations = []
for(var property in declarationsObj){
var value = declarationsObj[property]
declarations.push("\t" + property + ": " + value + ";")
}
css.push(selector + "{\n" + declarations.join("\n") + "\n}")
}
return css.join("\n")
}
ajax(url, customRequest, customResponse){ ajax(url, customRequest, customResponse){
var request = new XMLHttpRequest() var request = new XMLHttpRequest()
request.open("GET", url) request.open("GET", url)

View File

@ -103,8 +103,8 @@ class LoadSong{
} }
let img = document.createElement("img") let img = document.createElement("img")
let force = imgLoad[i].type === "song" && this.touchEnabled let force = imgLoad[i].type === "song" && this.touchEnabled
if(!songObj.custom && (this.imgScale !== 1 || force)){ if(!songObj.custom){
img.crossOrigin = "Anonymous" img.crossOrigin = "anonymous"
} }
let promise = pageEvents.load(img) let promise = pageEvents.load(img)
this.addPromise(promise.then(() => { this.addPromise(promise.then(() => {
@ -147,15 +147,30 @@ class LoadSong{
} }
if(this.touchEnabled && !assets.image["touch_drum"]){ if(this.touchEnabled && !assets.image["touch_drum"]){
let img = document.createElement("img") let img = document.createElement("img")
if(this.imgScale !== 1){ img.crossOrigin = "anonymous"
img.crossOrigin = "Anonymous"
}
var url = gameConfig.assets_baseurl + "img/touch_drum.png" var url = gameConfig.assets_baseurl + "img/touch_drum.png"
this.addPromise(pageEvents.load(img).then(() => { this.addPromise(pageEvents.load(img).then(() => {
return this.scaleImg(img, "touch_drum", "") return this.scaleImg(img, "touch_drum", "")
}), url) }), url)
img.src = url img.src = url
} }
var resultsImg = [
"results_flowers",
"results_mikoshi",
"results_tetsuohana",
"results_tetsuohana2"
]
resultsImg.forEach(id => {
if(!assets.image[id]){
var img = document.createElement("img")
img.crossOrigin = "anonymous"
var url = gameConfig.assets_baseurl + "img/" + id + ".png"
this.addPromise(pageEvents.load(img).then(() => {
return this.scaleImg(img, id, "")
}), url)
img.src = url
}
})
if(songObj.volume && songObj.volume !== 1){ if(songObj.volume && songObj.volume !== 1){
this.promises.push(new Promise(resolve => setTimeout(resolve, 500))) this.promises.push(new Promise(resolve => setTimeout(resolve, 500)))
} }
@ -217,9 +232,7 @@ class LoadSong{
if(!(filenameAb in assets.image)){ if(!(filenameAb in assets.image)){
let img = document.createElement("img") let img = document.createElement("img")
let force = filenameAb.startsWith("bg_song_") && this.touchEnabled let force = filenameAb.startsWith("bg_song_") && this.touchEnabled
if(this.imgScale !== 1 || force){ img.crossOrigin = "anonymous"
img.crossOrigin = "Anonymous"
}
var url = gameConfig.assets_baseurl + "img/" + filenameAb + ".png" var url = gameConfig.assets_baseurl + "img/" + filenameAb + ".png"
this.addPromise(pageEvents.load(img).then(() => { this.addPromise(pageEvents.load(img).then(() => {
return this.scaleImg(img, filenameAb, "", force) return this.scaleImg(img, filenameAb, "", force)
@ -235,7 +248,6 @@ class LoadSong{
if(force && scale > 0.5){ if(force && scale > 0.5){
scale = 0.5 scale = 0.5
} }
if(scale !== 1){
var canvas = document.createElement("canvas") var canvas = document.createElement("canvas")
var w = Math.floor(img.width * scale) var w = Math.floor(img.width * scale)
var h = Math.floor(img.height * scale) var h = Math.floor(img.height * scale)
@ -247,8 +259,10 @@ class LoadSong{
let img2 = document.createElement("img") let img2 = document.createElement("img")
pageEvents.load(img2).then(() => { pageEvents.load(img2).then(() => {
assets.image[prefix + filename] = img2 assets.image[prefix + filename] = img2
loader.assetsDiv.appendChild(img2)
resolve() resolve()
}, reject) }, reject)
img2.id = prefix + filename
img2.src = url img2.src = url
} }
if("toBlob" in canvas){ if("toBlob" in canvas){
@ -258,10 +272,6 @@ class LoadSong{
}else{ }else{
saveScaled(canvas.toDataURL()) saveScaled(canvas.toDataURL())
} }
}else{
assets.image[prefix + filename] = img
resolve()
}
}) })
} }
randInt(min, max){ randInt(min, max){

View File

@ -582,6 +582,12 @@ class EditFunction extends EditValue{
if(this.name){ if(this.name){
this.original = this.name[0][this.name[1]] this.original = this.name[0][this.name[1]]
} }
if(typeof this.original !== "function"){
console.error(this.loadCallback)
var error = new Error()
error.stack = "Error editing the function value of " + this.getName() + ": Original value is not a function"
throw error
}
var args = plugins.argsFromFunc(this.original) var args = plugins.argsFromFunc(this.original)
try{ try{
var output = this.loadCallback(plugins.strFromFunc(this.original), args) var output = this.loadCallback(plugins.strFromFunc(this.original), args)
@ -618,8 +624,13 @@ class EditFunction extends EditValue{
} }
class Patch{ class Patch{
edits = [] constructor(...args){
addedLanguages = [] this.init(...args)
}
init(){
this.edits = []
this.addedLanguages = []
}
addEdits(...args){ addEdits(...args){
args.forEach(arg => this.edits.push(arg)) args.forEach(arg => this.edits.push(arg))
} }

637
public/src/js/search.js Normal file
View File

@ -0,0 +1,637 @@
class Search{
constructor(...args){
this.init(...args)
}
init(songSelect){
this.songSelect = songSelect
this.opened = false
this.enabled = true
this.style = document.createElement("style")
var css = []
for(var i in this.songSelect.songSkin){
var skin = this.songSelect.songSkin[i]
if("id" in skin || i === "default"){
var id = "id" in skin ? ("cat" + skin.id) : i
css.push(loader.cssRuleset({
[".song-search-" + id]: {
"background-color": skin.background
},
[".song-search-" + id + "::before"]: {
"border-color": skin.border[0],
"border-bottom-color": skin.border[1],
"border-right-color": skin.border[1]
},
[".song-search-" + id + " .song-search-result-title::before, .song-search-" + id + " .song-search-result-subtitle::before"]: {
"-webkit-text-stroke-color": skin.outline
}
}))
}
}
this.style.appendChild(document.createTextNode(css.join("\n")))
loader.screen.appendChild(this.style)
}
perform(query){
var results = []
var filters = {}
var querySplit = query.split(" ")
var editedSplit = query.split(" ")
querySplit.forEach(word => {
if(word.length > 0){
var parts = word.toLowerCase().split(":")
if(parts.length > 1){
switch(parts[0]){
case "easy":
case "normal":
case "hard":
case "oni":
case "ura":
var range = this.parseRange(parts[1])
if(range){
filters[parts[0]] = range
}
break
case "extreme":
var range = this.parseRange(parts[1])
if(range){
filters.oni = this.parseRange(parts[1])
}
break
case "clear":
case "silver":
case "gold":
case "genre":
case "lyrics":
case "creative":
case "played":
case "maker":
case "diverge":
case "random":
filters[parts[0]] = parts[1]
break
}
editedSplit.splice(editedSplit.indexOf(word), 1)
}
}
})
query = editedSplit.join(" ").trim().normalize("NFD").replace(/[\u0300-\u036f]/g, "")
var totalFilters = Object.keys(filters).length
var random = false
for(var i = 0; i < assets.songs.length; i++){
var song = assets.songs[i]
var passedFilters = 0
Object.keys(filters).forEach(filter => {
var value = filters[filter]
switch(filter){
case "easy":
case "normal":
case "hard":
case "oni":
case "ura":
if(song.courses[filter] && song.courses[filter].stars >= value.min && song.courses[filter].stars <= value.max){
passedFilters++
}
break
case "clear":
case "silver":
case "gold":
if(value === "any"){
var score = scoreStorage.scores[song.hash]
scoreStorage.difficulty.forEach(difficulty => {
if(score && score[difficulty] && score[difficulty].crown && (filter === "clear" || score[difficulty].crown === filter)){
passedFilters++
}
})
} else {
var score = scoreStorage.scores[song.hash]
if(score && score[value] && score[value].crown && (filter === "clear" || score[value].crown === filter)){
passedFilters++
}
}
break
case "played":
var score = scoreStorage.scores[song.hash]
if((value === "yes" && score) || (value === "no" && !score)){
passedFilters++
}
break
case "lyrics":
if((value === "yes" && song.lyrics) || (value === "no" && !song.lyrics)){
passedFilters++
}
break
case "creative":
if((value === "yes" && song.maker) || (value === "no" && !song.maker)){
passedFilters++
}
break
case "maker":
if(song.maker && song.maker.name.toLowerCase().includes(value.toLowerCase())){
passedFilters++
}
break
case "genre":
var cat = assets.categories.find(cat => cat.id === song.category_id)
var aliases = cat.aliases ? cat.aliases.concat([cat.title]) : [cat.title]
if(aliases.find(alias => alias.toLowerCase() === value.toLowerCase())){
passedFilters++
}
break
case "diverge":
var branch = Object.values(song.courses).find(course => course && course.branch)
if((value === "yes" && branch) || (value === "no" && !branch)){
passedFilters++
}
break
case "random":
if(value === "yes" || value === "no"){
random = value === "yes"
passedFilters++
}
break
}
})
if(passedFilters === totalFilters){
results.push(song)
}
}
var maxResults = totalFilters > 0 && !query ? 100 : 50
if(query){
results = fuzzysort.go(query, results, {
keys: ["titlePrepared", "subtitlePrepared"],
allowTypo: true,
limit: maxResults,
scoreFn: a => {
if(a[0]){
var score0 = a[0].score
a[0].ranges = this.indexesToRanges(a[0].indexes)
if(a[0].indexes.length > 1){
var rangeAmount = a[0].ranges.length
var lastIdx = -3
a[0].ranges.forEach(range => {
if(range[0] - lastIdx <= 2){
rangeAmount--
score0 -= 1000
}
lastIdx = range[1]
})
var index = a[0].target.toLowerCase().indexOf(query)
if(index !== -1){
a[0].ranges = [[index, index + query.length - 1]]
}else if(rangeAmount > a[0].indexes.length / 2){
score0 = -Infinity
a[0].ranges = null
}else if(rangeAmount !== 1){
score0 -= 9000
}
}
}
if(a[1]){
var score1 = a[1].score - 1000
a[1].ranges = this.indexesToRanges(a[1].indexes)
if(a[1].indexes.length > 1){
var rangeAmount = a[1].ranges.length
var lastIdx = -3
a[1].ranges.forEach(range => {
if(range[0] - lastIdx <= 2){
rangeAmount--
score1 -= 1000
}
lastIdx = range[1]
})
var index = a[1].target.indexOf(query)
if(index !== -1){
a[1].ranges = [[index, index + query.length - 1]]
}else if(rangeAmount > a[1].indexes.length / 2){
score1 = -Infinity
a[1].ranges = null
}else if(rangeAmount !== 1){
score1 -= 9000
}
}
}
if(a[0]){
return a[1] ? Math.max(score0, score1) : score0
}else{
return a[1] ? score1 : -Infinity
}
}
})
}else{
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
}
createResult(result, resultWidth, fontSize){
var song = result.obj
var title = this.songSelect.getLocalTitle(song.title, song.title_lang)
var subtitle = this.songSelect.getLocalTitle(title === song.title ? song.subtitle : "", song.subtitle_lang)
var id = "default"
if(song.category_id){
var cat = assets.categories.find(cat => cat.id === song.category_id)
if(cat && "id" in cat){
id = "cat" + cat.id
}
}
var resultDiv = document.createElement("div")
resultDiv.classList.add("song-search-result", "song-search-" + id)
resultDiv.dataset.songId = song.id
var resultInfoDiv = document.createElement("div")
resultInfoDiv.classList.add("song-search-result-info")
var resultInfoTitle = document.createElement("span")
resultInfoTitle.classList.add("song-search-result-title")
resultInfoTitle.appendChild(this.highlightResult(title, result[0]))
resultInfoTitle.setAttribute("alt", title)
resultInfoDiv.appendChild(resultInfoTitle)
if(subtitle){
resultInfoDiv.appendChild(document.createElement("br"))
var resultInfoSubtitle = document.createElement("span")
resultInfoSubtitle.classList.add("song-search-result-subtitle")
resultInfoSubtitle.appendChild(this.highlightResult(subtitle, result[1]))
resultInfoSubtitle.setAttribute("alt", subtitle)
resultInfoDiv.appendChild(resultInfoSubtitle)
}
resultDiv.appendChild(resultInfoDiv)
var courses = ["easy", "normal", "hard", "oni", "ura"]
courses.forEach(course => {
var courseDiv = document.createElement("div")
courseDiv.classList.add("song-search-result-course", "song-search-result-" + course)
if (song.courses[course]) {
var crown = "noclear"
if (scoreStorage.scores[song.hash]) {
if (scoreStorage.scores[song.hash][course]) {
crown = scoreStorage.scores[song.hash][course].crown || "noclear"
}
}
var courseCrown = document.createElement("div")
courseCrown.classList.add("song-search-result-crown", "song-search-result-" + crown)
var courseStars = document.createElement("div")
courseStars.classList.add("song-search-result-stars")
courseStars.innerText = song.courses[course].stars + "\u2605"
courseDiv.appendChild(courseCrown)
courseDiv.appendChild(courseStars)
} else {
courseDiv.classList.add("song-search-result-hidden")
}
resultDiv.appendChild(courseDiv)
})
this.songSelect.ctx.font = (1.2 * fontSize) + "px " + strings.font
var titleWidth = this.songSelect.ctx.measureText(title).width
var titleRatio = resultWidth / titleWidth
if(titleRatio < 1){
resultInfoTitle.style.transform = "scale(" + titleRatio + ", 1)"
}
if(subtitle){
this.songSelect.ctx.font = (0.8 * 1.2 * fontSize) + "px " + strings.font
var subtitleWidth = this.songSelect.ctx.measureText(subtitle).width
var subtitleRatio = resultWidth / subtitleWidth
if(subtitleRatio < 1){
resultInfoSubtitle.style.transform = "scale(" + subtitleRatio + ", 1)"
}
}
return resultDiv
}
highlightResult(text, result){
var fragment = document.createDocumentFragment()
var ranges = (result ? result.ranges : null) || []
var lastIdx = 0
ranges.forEach(range => {
if(lastIdx !== range[0]){
fragment.appendChild(document.createTextNode(text.slice(lastIdx, range[0])))
}
var span = document.createElement("span")
span.classList.add("highlighted-text")
span.innerText = text.slice(range[0], range[1] + 1)
fragment.appendChild(span)
lastIdx = range[1] + 1
})
if(text.length !== lastIdx){
fragment.appendChild(document.createTextNode(text.slice(lastIdx)))
}
return fragment
}
setActive(idx){
this.songSelect.playSound("se_ka")
var active = this.div.querySelector(":scope .song-search-result-active")
if(active){
active.classList.remove("song-search-result-active")
}
if(idx === null){
this.active = null
return
}
var el = this.results[idx]
this.input.blur()
el.classList.add("song-search-result-active")
this.scrollTo(el)
this.active = idx
}
display(fromButton=false){
if(!this.enabled){
return
}
if(this.opened){
return this.remove(true)
}
this.opened = true
this.results = []
this.div = document.createElement("div")
this.div.innerHTML = assets.pages["search"]
this.container = this.div.querySelector(":scope #song-search-container")
if(this.touchEnabled){
this.container.classList.add("touch-enabled")
}
pageEvents.add(this.container, ["mousedown", "touchstart"], this.onClick.bind(this))
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))
this.songSelect.playSound("se_pause")
loader.screen.appendChild(this.div)
this.setTip()
cancelTouch = false
noResizeRoot = true
if(this.songSelect.songs[this.songSelect.selectedSong].courses){
snd.previewGain.setVolumeMul(0.5)
}else if(this.songSelect.bgmEnabled){
snd.musicGain.setVolumeMul(0.5)
}
setTimeout(() => {
this.input.focus()
this.input.setSelectionRange(0, this.input.value.length)
}, 10)
var lastQuery = localStorage.getItem("lastSearchQuery")
if(lastQuery){
this.input.value = lastQuery
this.input.dispatchEvent(new Event("input", {
value: lastQuery
}))
}
}
remove(byUser=false){
if(this.opened){
this.opened = false
if(byUser){
this.songSelect.playSound("se_cancel")
}
pageEvents.remove(this.div.querySelector(":scope #song-search-container"),
["mousedown", "touchstart"])
pageEvents.remove(this.input, ["input"])
this.div.remove()
delete this.results
delete this.div
delete this.input
delete this.tip
delete this.active
cancelTouch = true
noResizeRoot = false
if(this.songSelect.songs[this.songSelect.selectedSong].courses){
snd.previewGain.setVolumeMul(1)
}else if(this.songSelect.bgmEnabled){
snd.musicGain.setVolumeMul(1)
}
}
}
setTip(tip, error=false){
if(this.tip){
this.tip.remove()
delete this.tip
}
if(!tip){
tip = strings.search.tip + " " + strings.search.tips[Math.floor(Math.random() * strings.search.tips.length)]
}
var resultsDiv = this.div.querySelector(":scope #song-search-results")
resultsDiv.innerHTML = ""
this.results = []
this.tip = document.createElement("div")
this.tip.id = "song-search-tip"
this.tip.innerText = tip
this.div.querySelector(":scope #song-search").appendChild(this.tip)
if(error){
this.tip.classList.add("song-search-tip-error")
}
}
proceed(songId){
var song = this.songSelect.songs.find(song => song.id === songId)
this.remove()
this.songSelect.playBgm(false)
var songIndex = this.songSelect.songs.findIndex(song => song.id === songId)
this.songSelect.setSelectedSong(songIndex)
this.songSelect.toSelectDifficulty()
}
scrollTo(element){
var parentNode = element.parentNode
var selected = element.getBoundingClientRect()
var parent = parentNode.getBoundingClientRect()
var scrollY = parentNode.scrollTop
var selectedPosTop = selected.top - selected.height / 2
if(Math.floor(selectedPosTop) < Math.floor(parent.top)){
parentNode.scrollTop += selectedPosTop - parent.top
}else{
var selectedPosBottom = selected.top + selected.height * 1.5 - parent.top
if(Math.floor(selectedPosBottom) > Math.floor(parent.height)){
parentNode.scrollTop += selectedPosBottom - parent.height
}
}
}
parseRange(string){
var range = string.split("-")
if(range.length == 1){
var min = parseInt(range[0]) || 0
return min > 0 ? {min: min, max: min} : false
} else if(range.length == 2){
var min = parseInt(range[0]) || 0
var max = parseInt(range[1]) || 0
return min > 0 && max > 0 ? {min: min, max: max} : false
}
}
indexesToRanges(indexes){
var ranges = []
var range
indexes.forEach(idx => {
if(range && range[1] === idx - 1){
range[1] = idx
}else{
range = [idx, idx]
ranges.push(range)
}
})
return ranges
}
onInput(){
var text = this.input.value
localStorage.setItem("lastSearchQuery", text)
text = text.toLowerCase()
if(text.length === 0){
this.setTip()
return
}
var new_results = this.perform(text)
if(new_results.length === 0){
this.setTip(strings.search.noResults, true)
return
}else if(this.tip){
this.tip.remove()
delete this.tip
}
var resultsDiv = this.div.querySelector(":scope #song-search-results")
resultsDiv.innerHTML = ""
this.results = []
var fontSize = parseFloat(getComputedStyle(this.div.querySelector(":scope #song-search")).fontSize.slice(0, -2))
var resultsWidth = parseFloat(getComputedStyle(resultsDiv).width.slice(0, -2))
var vmin = Math.min(innerWidth, lastHeight) / 100
var courseWidth = Math.min(3 * fontSize * 1.2, 7 * vmin)
var resultWidth = resultsWidth - 1.8 * fontSize - 0.8 * fontSize - (courseWidth + 0.4 * fontSize * 1.2) * 5 - 0.6 * fontSize
this.songSelect.ctx.save()
var fragment = document.createDocumentFragment()
new_results.forEach(result => {
var result = this.createResult(result, resultWidth, fontSize)
fragment.appendChild(result)
this.results.push(result)
})
resultsDiv.appendChild(fragment)
this.songSelect.ctx.restore()
}
onClick(e){
if((e.target.id === "song-search-container" || e.target.id === "song-search-close") && e.which === 1){
this.remove(true)
}else if(e.which === 1){
var songEl = e.target.closest(".song-search-result")
if(songEl){
var songId = parseInt(songEl.dataset.songId)
this.proceed(songId)
}
}
}
keyPress(pressed, name, event, repeat){
if(name === "back" || (event && event.keyCode && event.keyCode === 70 && ctrl)) {
this.remove(true)
if(event){
event.preventDefault()
}
}else if(name === "down" && this.results.length){
if(this.input == document.activeElement && this.results){
this.setActive(0)
}else if(this.active === this.results.length - 1){
this.setActive(null)
this.input.focus()
}else if(Number.isInteger(this.active)){
this.setActive(this.active + 1)
}else{
this.setActive(0)
}
}else if(name === "up" && this.results.length){
if(this.input == document.activeElement && this.results){
this.setActive(this.results.length - 1)
}else if(this.active === 0){
this.setActive(null)
this.input.focus()
setTimeout(() => {
this.input.setSelectionRange(this.input.value.length, this.input.value.length)
}, 0)
}else if(Number.isInteger(this.active)){
this.setActive(this.active - 1)
}else{
this.setActive(this.results.length - 1)
}
}else if(name === "confirm"){
if(Number.isInteger(this.active)){
this.proceed(parseInt(this.results[this.active].dataset.songId))
}else{
this.onInput()
}
}
}
redraw(){
if(this.opened && this.container){
var vmin = Math.min(innerWidth, lastHeight) / 100
if(this.vmin !== vmin){
this.container.style.setProperty("--vmin", vmin + "px")
this.vmin = vmin
}
}else{
this.vmin = null
}
}
clean(){
loader.screen.removeChild(this.style)
fuzzysort.cleanup()
delete this.container
delete this.style
delete this.songSelect
}
}

View File

@ -217,15 +217,18 @@ class SettingsView{
constructor(...args){ constructor(...args){
this.init(...args) this.init(...args)
} }
init(touchEnabled, tutorial, songId, toSetting, settingsItems){ init(touchEnabled, tutorial, songId, toSetting, settingsItems, noSoundStart){
this.touchEnabled = touchEnabled this.touchEnabled = touchEnabled
this.tutorial = tutorial this.tutorial = tutorial
this.songId = songId this.songId = songId
this.customSettings = !!settingsItems this.customSettings = !!settingsItems
this.settingsItems = settingsItems || settings.items this.settingsItems = settingsItems || settings.items
this.locked = false
loader.changePage("settings", tutorial) loader.changePage("settings", tutorial)
if(!noSoundStart){
assets.sounds["bgm_settings"].playLoop(0.1, false, 0, 1.392, 26.992) assets.sounds["bgm_settings"].playLoop(0.1, false, 0, 1.392, 26.992)
}
this.defaultButton = document.getElementById("settings-default") this.defaultButton = document.getElementById("settings-default")
this.viewOuter = this.getElement("view-outer") this.viewOuter = this.getElement("view-outer")
if(touchEnabled){ if(touchEnabled){
@ -377,16 +380,48 @@ class SettingsView{
this.items.push(outputObject) this.items.push(outputObject)
this.getValue(i, valueDiv) this.getValue(i, valueDiv)
} }
var selectBack = this.items.length === 0
if(this.customSettings){
var form = document.createElement("form")
this.browse = document.createElement("input")
this.browse.id = "browse"
this.browse.type = "file"
this.browse.multiple = true
this.browse.accept = ".taikoweb.js"
pageEvents.add(this.browse, "change", this.browseChange.bind(this))
form.appendChild(this.browse)
loader.screen.appendChild(form)
this.browseButton = document.createElement("div")
this.browseButton.classList.add("taibtn", "stroke-sub")
this.defaultButton.parentNode.insertBefore(this.browseButton, this.defaultButton)
this.items.push({
id: "browse",
settingBox: this.browseButton
})
this.addTouch(this.browseButton, () => {
this.playSound("se_don")
this.browse.click()
})
}
this.showDefault = !this.customSettings || plugins.allPlugins.filter(obj => obj.plugin.imported).length
if(this.showDefault){
this.items.push({ this.items.push({
id: "default", id: "default",
settingBox: this.defaultButton settingBox: this.defaultButton
}) })
this.addTouch(this.defaultButton, this.defaultSettings.bind(this)) this.addTouch(this.defaultButton, this.defaultSettings.bind(this))
}else{
this.defaultButton.parentNode.removeChild(this.defaultButton)
}
this.items.push({ this.items.push({
id: "back", id: "back",
settingBox: this.endButton settingBox: this.endButton
}) })
this.addTouch(this.endButton, this.onEnd.bind(this)) this.addTouch(this.endButton, this.onEnd.bind(this))
if(selectBack){
this.selected = this.items.length - 1
this.endButton.classList.add("selected")
}
if(!this.customSettings){ if(!this.customSettings){
this.gamepadSettings = document.getElementById("settings-gamepad") this.gamepadSettings = document.getElementById("settings-gamepad")
@ -606,6 +641,9 @@ class SettingsView{
valueDiv.innerText = value valueDiv.innerText = value
} }
setValue(name){ setValue(name){
if(this.locked){
return
}
var promise var promise
var current = this.settingsItems[name] var current = this.settingsItems[name]
if(current.getItem){ if(current.getItem){
@ -674,6 +712,9 @@ class SettingsView{
}) })
} }
keyPressed(pressed, name, event, repeat){ keyPressed(pressed, name, event, repeat){
if(this.locked){
return
}
if(pressed){ if(pressed){
if(!this.pressedKeys[name]){ if(!this.pressedKeys[name]){
this.pressedKeys[name] = this.getMS() + 300 this.pressedKeys[name] = this.getMS() + 300
@ -693,6 +734,11 @@ class SettingsView{
this.onEnd() this.onEnd()
}else if(selected.id === "default"){ }else if(selected.id === "default"){
this.defaultSettings() this.defaultSettings()
}else if(selected.id === "browse"){
if(event){
this.playSound("se_don")
this.browse.click()
}
}else{ }else{
this.setValue(selected.id) this.setValue(selected.id)
} }
@ -700,7 +746,7 @@ class SettingsView{
selected.settingBox.classList.remove("selected") selected.settingBox.classList.remove("selected")
do{ do{
this.selected = this.mod(this.items.length, this.selected + ((name === "right" || name === "down") ? 1 : -1)) this.selected = this.mod(this.items.length, this.selected + ((name === "right" || name === "down") ? 1 : -1))
}while(this.items[this.selected].id === "default" && name !== "left") }while((this.items[this.selected].id === "default" || this.items[this.selected].id === "browse") && name !== "left")
selected = this.items[this.selected] selected = this.items[this.selected]
selected.settingBox.classList.add("selected") selected.settingBox.classList.add("selected")
this.scrollTo(selected.settingBox) this.scrollTo(selected.settingBox)
@ -1027,7 +1073,9 @@ class SettingsView{
defaultSettings(){ defaultSettings(){
if(this.customSettings){ if(this.customSettings){
plugins.unloadImported() plugins.unloadImported()
return this.onEnd() this.clean(true)
this.playSound("se_don")
return setTimeout(() => this.restart(), 500)
} }
if(this.mode === "keyboard"){ if(this.mode === "keyboard"){
this.keyboardBack(this.items[this.selected]) this.keyboardBack(this.items[this.selected])
@ -1046,6 +1094,31 @@ class SettingsView{
this.drumSounds = settings.getItem("latency").drumSounds this.drumSounds = settings.getItem("latency").drumSounds
this.playSound("se_don") this.playSound("se_don")
} }
browseChange(event){
this.locked = true
var files = []
for(var i = 0; i < event.target.files.length; i++){
files.push(new LocalFile(event.target.files[i]))
}
var customSongs = new CustomSongs(this.touchEnabled, true)
customSongs.importLocal(files).then(() => {
this.clean(true)
return this.restart()
}).catch(e => {
if(e){
var message = e.message
if(e.name === "nosongs"){
message = strings.plugins.noPlugins
}
if(message){
alert(message)
}
}
this.locked = false
this.browse.form.reset()
return Promise.resolve()
})
}
onEnd(){ onEnd(){
if(this.mode === "number"){ if(this.mode === "number"){
this.numberBack(this.items[this.selected]) this.numberBack(this.items[this.selected])
@ -1063,6 +1136,12 @@ class SettingsView{
} }
}, 500) }, 500)
} }
restart(){
if(this.mode === "number"){
this.numberBack(this.items[this.selected])
}
return new SettingsView(this.touchEnabled, this.tutorial, this.songId, undefined, this.customSettings ? plugins.getSettings() : undefined, true)
}
getLocalTitle(title, titleLang){ getLocalTitle(title, titleLang){
if(titleLang){ if(titleLang){
for(var id in titleLang){ for(var id in titleLang){
@ -1109,15 +1188,19 @@ class SettingsView{
setStrings(){ setStrings(){
this.setAltText(this.viewTitle, this.customSettings ? strings.plugins.title : strings.gameSettings) this.setAltText(this.viewTitle, this.customSettings ? strings.plugins.title : strings.gameSettings)
this.setAltText(this.endButton, strings.settings.ok) this.setAltText(this.endButton, strings.settings.ok)
if(!this.customSettings){ if(this.customSettings){
this.setAltText(this.browseButton, strings.plugins.browse)
}else{
this.setAltText(this.gamepadTitle, strings.settings.gamepadLayout.name) this.setAltText(this.gamepadTitle, strings.settings.gamepadLayout.name)
this.setAltText(this.gamepadEndButton, strings.settings.ok) this.setAltText(this.gamepadEndButton, strings.settings.ok)
this.setAltText(this.latencyTitle, strings.settings.latency.name) this.setAltText(this.latencyTitle, strings.settings.latency.name)
this.setAltText(this.latencyDefaultButton, strings.settings.default) this.setAltText(this.latencyDefaultButton, strings.settings.default)
this.setAltText(this.latencyEndButton, strings.settings.ok) this.setAltText(this.latencyEndButton, strings.settings.ok)
} }
if(this.showDefault){
this.setAltText(this.defaultButton, this.customSettings ? strings.plugins.unloadAll : strings.settings.default) this.setAltText(this.defaultButton, this.customSettings ? strings.plugins.unloadAll : strings.settings.default)
} }
}
setAltText(element, text){ setAltText(element, text){
element.innerText = text element.innerText = text
element.setAttribute("alt", text) element.setAttribute("alt", text)
@ -1154,11 +1237,13 @@ class SettingsView{
getMS(){ getMS(){
return Date.now() return Date.now()
} }
clean(){ clean(noSoundStop){
this.redrawRunning = false this.redrawRunning = false
this.keyboard.clean() this.keyboard.clean()
this.gamepad.clean() this.gamepad.clean()
if(!noSoundStop){
assets.sounds["bgm_settings"].stop() assets.sounds["bgm_settings"].stop()
}
pageEvents.remove(window, ["mouseup", "touchstart", "touchmove", "touchend", "blur"], this.windowSymbol) pageEvents.remove(window, ["mouseup", "touchstart", "touchmove", "touchend", "blur"], this.windowSymbol)
if(this.customSettings){ if(this.customSettings){
pageEvents.remove(window, "language-change", this.windowSymbol) pageEvents.remove(window, "language-change", this.windowSymbol)
@ -1176,7 +1261,12 @@ class SettingsView{
if(this.defaultButton){ if(this.defaultButton){
delete this.defaultButton delete this.defaultButton
} }
if(!this.customSettings){ if(this.customSettings){
pageEvents.remove(this.browse, "change")
this.removeTouch(this.browseButton)
delete this.browse
delete this.browseButton
}else{
this.removeTouch(this.gamepadSettings) this.removeTouch(this.gamepadSettings)
this.removeTouch(this.gamepadEndButton) this.removeTouch(this.gamepadEndButton)
this.removeTouch(this.gamepadBox) this.removeTouch(this.gamepadBox)
@ -1204,8 +1294,12 @@ class SettingsView{
delete this.latencyEndButton delete this.latencyEndButton
if(this.resolution !== settings.getItem("resolution")){ if(this.resolution !== settings.getItem("resolution")){
for(var i in assets.image){ for(var i in assets.image){
if(i === "touch_drum" || i.startsWith("bg_song_") || i.startsWith("bg_stage_") || i.startsWith("bg_don_")){ if(i === "touch_drum" || i.startsWith("bg_song_") || i.startsWith("bg_stage_") || i.startsWith("bg_don_") || i.startsWith("results_")){
URL.revokeObjectURL(assets.image[i].src) var img = assets.image[i]
URL.revokeObjectURL(img.src)
if(img.parentNode){
img.parentNode.removeChild(img)
}
delete assets.image[i] delete assets.image[i]
} }
} }

View File

@ -92,22 +92,6 @@ class SongSelect{
} }
this.songSkin["default"].sort = songSkinLength + 1 this.songSkin["default"].sort = songSkinLength + 1
this.searchStyle = document.createElement("style")
var searchCss = []
Object.keys(this.songSkin).forEach(key => {
var skin = this.songSkin[key]
if("id" in skin || key === "default"){
var id = "id" in skin ? ("cat" + skin.id) : key
searchCss.push('.song-search-' + id + ' { background-color: ' + skin.background + ' }')
searchCss.push('.song-search-' + id + '::before { border: 0.4em solid ' + skin.border[0] + ' ; border-bottom-color: ' + skin.border[1] + ' ; border-right-color: ' + skin.border[1] + ' }')
searchCss.push('.song-search-' + id + ' .song-search-result-title::before { -webkit-text-stroke: 0.4em ' + skin.outline + ' }')
searchCss.push('.song-search-' + id + ' .song-search-result-subtitle::before { -webkit-text-stroke: 0.4em ' + skin.outline + ' }')
}
})
this.searchStyle.appendChild(document.createTextNode(searchCss.join("\n")))
loader.screen.appendChild(this.searchStyle)
this.font = strings.font this.font = strings.font
this.songs = [] this.songs = []
@ -194,14 +178,12 @@ class SongSelect{
category: strings.random category: strings.random
}) })
} }
if(plugins.hasSettings()){
this.songs.push({ this.songs.push({
title: strings.plugins.title, title: strings.plugins.title,
skin: this.songSkin.plugins, skin: this.songSkin.plugins,
action: "plugins", action: "plugins",
category: strings.random category: strings.random
}) })
}
this.songs.push({ this.songs.push({
title: strings.back, title: strings.back,
@ -246,6 +228,8 @@ class SongSelect{
this.currentSongCache = new CanvasCache(noSmoothing) this.currentSongCache = new CanvasCache(noSmoothing)
this.nameplateCache = new CanvasCache(noSmoothing) this.nameplateCache = new CanvasCache(noSmoothing)
this.search = new Search(this)
this.difficulty = [strings.easy, strings.normal, strings.hard, strings.oni] this.difficulty = [strings.easy, strings.normal, strings.hard, strings.oni]
this.difficultyId = ["easy", "normal", "hard", "oni", "ura"] this.difficultyId = ["easy", "normal", "hard", "oni", "ura"]
@ -257,7 +241,6 @@ class SongSelect{
this.selectedSong = 0 this.selectedSong = 0
this.selectedDiff = 0 this.selectedDiff = 0
this.lastCurrentSong = {} this.lastCurrentSong = {}
this.searchEnabled = true
this.lastRandom = false this.lastRandom = false
assets.sounds["bgm_songsel"].playLoop(0.1, false, 0, 1.442, 3.506) assets.sounds["bgm_songsel"].playLoop(0.1, false, 0, 1.442, 3.506)
@ -434,44 +417,14 @@ class SongSelect{
this.state.showWarning = false this.state.showWarning = false
this.showWarning = false this.showWarning = false
} }
}else if (this.search){ }else if(this.search.opened){
if(name === "back" || (event && event.keyCode && event.keyCode === 70 && ctrl)) { this.search.keyPress(pressed, name, event, repeat)
this.removeSearch(true)
if(event){ event.preventDefault() }
}else if(name === "down" && this.search.results.length){
if(this.search.input == document.activeElement && this.search.results){
this.searchSetActive(0)
}else if(this.search.active === this.search.results.length-1){
this.searchSetActive(null)
this.search.input.focus()
}else if(Number.isInteger(this.search.active)){
this.searchSetActive(this.search.active+1)
}else{
this.searchSetActive(0)
}
}else if(name === "up" && this.search.results.length){
if(this.search.input == document.activeElement && this.search.results){
this.searchSetActive(this.search.results.length-1)
}else if(this.search.active === 0){
this.searchSetActive(null)
this.search.input.focus()
setTimeout(() => {
this.search.input.setSelectionRange(this.search.input.value.length, this.search.input.value.length)
}, 0)
}else if(Number.isInteger(this.search.active)){
this.searchSetActive(this.search.active-1)
}else{
this.searchSetActive(this.search.results.length-1)
}
}else if(name === "confirm"){
if(Number.isInteger(this.search.active)){
this.searchProceed(parseInt(this.search.results[this.search.active].dataset.songId))
}
}
}else if(this.state.screen === "song"){ }else if(this.state.screen === "song"){
if(event && event.keyCode && event.keyCode === 70 && ctrl){ if(event && event.keyCode && event.keyCode === 70 && ctrl){
this.displaySearch() this.search.display()
if(event){ event.preventDefault() } if(event){
event.preventDefault()
}
}else if(name === "confirm"){ }else if(name === "confirm"){
this.toSelectDifficulty() this.toSelectDifficulty()
}else if(name === "back"){ }else if(name === "back"){
@ -504,8 +457,10 @@ class SongSelect{
} }
}else if(this.state.screen === "difficulty"){ }else if(this.state.screen === "difficulty"){
if(event && event.keyCode && event.keyCode === 70 && ctrl){ if(event && event.keyCode && event.keyCode === 70 && ctrl){
this.displaySearch() this.search.display()
if(event){ event.preventDefault() } if(event){
event.preventDefault()
}
}else if(name === "confirm"){ }else if(name === "confirm"){
if(this.selectedDiff === 0){ if(this.selectedDiff === 0){
this.toSongSelect() this.toSongSelect()
@ -528,8 +483,10 @@ class SongSelect{
} }
}else if(this.state.screen === "title" || this.state.screen === "titleFadeIn"){ }else if(this.state.screen === "title" || this.state.screen === "titleFadeIn"){
if(event && event.keyCode && event.keyCode === 70 && ctrl){ if(event && event.keyCode && event.keyCode === 70 && ctrl){
this.displaySearch() this.search.display()
if(event){ event.preventDefault() } if(event){
event.preventDefault()
}
} }
} }
} }
@ -627,7 +584,7 @@ class SongSelect{
if(408 < mouse.x && mouse.x < 872 && 470 < mouse.y && mouse.y < 550){ if(408 < mouse.x && mouse.x < 872 && 470 < mouse.y && mouse.y < 550){
moveTo = "showWarning" moveTo = "showWarning"
} }
}else if(this.state.screen === "song" && !this.search){ }else if(this.state.screen === "song" && !this.search.opened){
if(20 < mouse.y && mouse.y < 90 && 410 < mouse.x && mouse.x < 880 && (mouse.x < 540 || mouse.x > 750)){ if(20 < mouse.y && mouse.y < 90 && 410 < mouse.x && mouse.x < 880 && (mouse.x < 540 || mouse.x > 750)){
moveTo = mouse.x < 640 ? "categoryPrev" : "categoryNext" moveTo = mouse.x < 640 ? "categoryPrev" : "categoryNext"
}else if(!p2.session && 60 < mouse.x && mouse.x < 332 && 640 < mouse.y && mouse.y < 706 && gameConfig.accounts){ }else if(!p2.session && 60 < mouse.x && mouse.x < 332 && 640 < mouse.y && mouse.y < 706 && gameConfig.accounts){
@ -792,7 +749,7 @@ class SongSelect{
} }
} }
}else if(this.state.locked === 0 || fromP2){ }else if(this.state.locked === 0 || fromP2){
this.removeSearch() this.search.remove()
if(currentSong.courses){ if(currentSong.courses){
if(currentSong.unloaded){ if(currentSong.unloaded){
return return
@ -815,7 +772,6 @@ class SongSelect{
} }
pageEvents.send("song-select-difficulty", currentSong) pageEvents.send("song-select-difficulty", currentSong)
}else if(currentSong.action === "back"){ }else if(currentSong.action === "back"){
this.clean()
this.toTitleScreen() this.toTitleScreen()
}else if(currentSong.action === "random"){ }else if(currentSong.action === "random"){
do{ do{
@ -827,7 +783,7 @@ class SongSelect{
this.toSelectDifficulty(false, playVoice=false) this.toSelectDifficulty(false, playVoice=false)
pageEvents.send("song-select-random") pageEvents.send("song-select-random")
}else if(currentSong.action === "search"){ }else if(currentSong.action === "search"){
this.displaySearch(true) this.search.display(true)
}else if(currentSong.action === "tutorial"){ }else if(currentSong.action === "tutorial"){
this.toTutorial() this.toTutorial()
}else if(currentSong.action === "about"){ }else if(currentSong.action === "about"){
@ -1116,8 +1072,8 @@ class SongSelect{
this.selectableText = "" this.selectableText = ""
if(this.search && this.searchContainer){ if(this.search.opened && this.search.container){
this.searchInput() this.search.onInput()
} }
}else if(!document.hasFocus() && !p2.session){ }else if(!document.hasFocus() && !p2.session){
if(this.state.focused){ if(this.state.focused){
@ -1146,15 +1102,7 @@ class SongSelect{
var screen = this.state.screen var screen = this.state.screen
var selectedWidth = this.songAsset.width var selectedWidth = this.songAsset.width
if(this.search && this.searchContainer){ this.search.redraw()
var vmin = Math.min(innerWidth, lastHeight) / 100
if(this.vmin !== vmin){
this.searchContainer.style.setProperty("--vmin", vmin + "px")
this.vmin = vmin
}
}else{
this.vmin = null
}
if(this.wheelScrolls !== 0 && !this.state.locked && ms >= this.wheelTimer + 20) { if(this.wheelScrolls !== 0 && !this.state.locked && ms >= this.wheelTimer + 20) {
if(p2.session){ if(p2.session){
@ -2727,520 +2675,6 @@ class SongSelect{
return addedSong return addedSong
} }
createSearchResult(result, resultWidth, fontSize){
var song = result.obj
var title = this.getLocalTitle(song.title, song.title_lang)
var subtitle = this.getLocalTitle(title === song.title ? song.subtitle : "", song.subtitle_lang)
var id = "default"
if(song.category_id){
var cat = assets.categories.find(cat => cat.id === song.category_id)
if(cat && "id" in cat){
id = "cat" + cat.id
}
}
var resultDiv = document.createElement("div")
resultDiv.classList.add("song-search-result", "song-search-" + id)
resultDiv.dataset.songId = song.id
var resultInfoDiv = document.createElement("div")
resultInfoDiv.classList.add("song-search-result-info")
var resultInfoTitle = document.createElement("span")
resultInfoTitle.classList.add("song-search-result-title")
resultInfoTitle.appendChild(this.highlightResult(title, result[0]))
resultInfoTitle.setAttribute("alt", title)
resultInfoDiv.appendChild(resultInfoTitle)
if(subtitle){
resultInfoDiv.appendChild(document.createElement("br"))
var resultInfoSubtitle = document.createElement("span")
resultInfoSubtitle.classList.add("song-search-result-subtitle")
resultInfoSubtitle.appendChild(this.highlightResult(subtitle, result[1]))
resultInfoSubtitle.setAttribute("alt", subtitle)
resultInfoDiv.appendChild(resultInfoSubtitle)
}
resultDiv.appendChild(resultInfoDiv)
var courses = ["easy", "normal", "hard", "oni", "ura"]
courses.forEach(course => {
var courseDiv = document.createElement("div")
courseDiv.classList.add("song-search-result-course", "song-search-result-" + course)
if (song.courses[course]) {
var crown = "noclear"
if (scoreStorage.scores[song.hash]) {
if (scoreStorage.scores[song.hash][course]) {
crown = scoreStorage.scores[song.hash][course].crown || "noclear"
}
}
var courseCrown = document.createElement("div")
courseCrown.classList.add("song-search-result-crown", "song-search-result-" + crown)
var courseStars = document.createElement("div")
courseStars.classList.add("song-search-result-stars")
courseStars.innerText = song.courses[course].stars + '★'
courseDiv.appendChild(courseCrown)
courseDiv.appendChild(courseStars)
} else {
courseDiv.classList.add("song-search-result-hidden")
}
resultDiv.appendChild(courseDiv)
})
this.ctx.font = (1.2 * fontSize) + "px " + strings.font
var titleWidth = this.ctx.measureText(title).width
var titleRatio = resultWidth / titleWidth
if(titleRatio < 1){
resultInfoTitle.style.transform = "scale(" + titleRatio + ", 1)"
}
if(subtitle){
this.ctx.font = (0.8 * 1.2 * fontSize) + "px " + strings.font
var subtitleWidth = this.ctx.measureText(subtitle).width
var subtitleRatio = resultWidth / subtitleWidth
if(subtitleRatio < 1){
resultInfoSubtitle.style.transform = "scale(" + subtitleRatio + ", 1)"
}
}
return resultDiv
}
highlightResult(text, result){
var fragment = document.createDocumentFragment()
var ranges = (result ? result.ranges : null) || []
var lastIdx = 0
ranges.forEach(range => {
if(lastIdx !== range[0]){
fragment.appendChild(document.createTextNode(text.slice(lastIdx, range[0])))
}
var span = document.createElement("span")
span.classList.add("highlighted-text")
span.innerText = text.slice(range[0], range[1] + 1)
fragment.appendChild(span)
lastIdx = range[1] + 1
})
if(text.length !== lastIdx){
fragment.appendChild(document.createTextNode(text.slice(lastIdx)))
}
return fragment
}
searchSetActive(idx){
this.playSound("se_ka")
var active = this.search.div.querySelector(":scope .song-search-result-active")
if(active){
active.classList.remove("song-search-result-active")
}
if(idx === null){
this.search.active = null
return
}
var el = this.search.results[idx]
this.search.input.blur()
el.classList.add("song-search-result-active")
this.scrollTo(el)
this.search.active = idx
}
scrollTo(element){
var parentNode = element.parentNode
var selected = element.getBoundingClientRect()
var parent = parentNode.getBoundingClientRect()
var scrollY = parentNode.scrollTop
var selectedPosTop = selected.top - selected.height / 2
if(Math.floor(selectedPosTop) < Math.floor(parent.top)){
parentNode.scrollTop += selectedPosTop - parent.top
}else{
var selectedPosBottom = selected.top + selected.height * 1.5 - parent.top
if(Math.floor(selectedPosBottom) > Math.floor(parent.height)){
parentNode.scrollTop += selectedPosBottom - parent.height
}
}
}
displaySearch(fromButton=false){
if(!this.searchEnabled){
return
}
if(this.search){
return this.removeSearch(true)
}
this.search = {results: []}
this.search.div = document.createElement("div")
this.search.div.innerHTML = assets.pages["search"]
this.searchContainer = this.search.div.querySelector(":scope #song-search-container")
if(this.touchEnabled){
this.searchContainer.classList.add("touch-enabled")
}
pageEvents.add(this.searchContainer, ["mousedown", "touchstart"], this.searchClick.bind(this))
this.search.input = this.search.div.querySelector(":scope #song-search-input")
this.search.input.setAttribute("placeholder", strings.search.searchInput)
pageEvents.add(this.search.input, ["input"], this.searchInput.bind(this))
this.playSound("se_pause")
loader.screen.appendChild(this.search.div)
this.setSearchTip()
cancelTouch = false
noResizeRoot = true
if(this.songs[this.selectedSong].courses){
snd.previewGain.setVolumeMul(0.5)
}else if(this.bgmEnabled){
snd.musicGain.setVolumeMul(0.5)
}
setTimeout(() => {
this.search.input.focus()
this.search.input.setSelectionRange(0, this.search.input.value.length)
}, 10)
var lastQuery = localStorage.getItem("lastSearchQuery")
if(lastQuery){
this.search.input.value = lastQuery
this.search.input.dispatchEvent(new Event('input', {value: lastQuery}))
}
}
removeSearch(byUser=false){
if(this.search){
if(byUser){
this.playSound("se_cancel")
}
pageEvents.remove(this.search.div.querySelector(":scope #song-search-container"),
["mousedown", "touchstart"])
pageEvents.remove(this.search.input, ["input"])
this.search.div.remove()
delete this.search
cancelTouch = true
noResizeRoot = false
if(this.songs[this.selectedSong].courses){
snd.previewGain.setVolumeMul(1)
}else if(this.bgmEnabled){
snd.musicGain.setVolumeMul(1)
}
}
}
setSearchTip(tip, error=false){
if(this.search.tip){
this.search.tip.remove()
delete this.search.tip
}
if(!tip){
tip = strings.search.tip + " " + strings.search.tips[Math.floor(Math.random() * strings.search.tips.length)]
}
var resultsDiv = this.search.div.querySelector(":scope #song-search-results")
resultsDiv.innerHTML = ""
this.search.results = []
this.search.tip = document.createElement("div")
this.search.tip.setAttribute("id", "song-search-tip")
this.search.tip.innerText = tip
this.search.div.querySelector(":scope #song-search").appendChild(this.search.tip)
if(error){
this.search.tip.classList.add("song-search-tip-error")
}
}
parseRange(string){
var range = string.split("-")
if(range.length == 1){
var min = parseInt(range[0]) || 0
return min > 0 ? {min: min, max: min} : false
} else if(range.length == 2){
var min = parseInt(range[0]) || 0
var max = parseInt(range[1]) || 0
return min > 0 && max > 0 ? {min: min, max: max} : false
}
}
performSearch(query){
var results = []
var filters = {}
var querySplit = query.split(" ")
var editedSplit = query.split(" ")
querySplit.forEach(word => {
if(word.length > 0){
var parts = word.toLowerCase().split(":")
if(parts.length > 1){
switch(parts[0]){
case "easy":
case "normal":
case "hard":
case "oni":
case "ura":
var range = this.parseRange(parts[1])
if (range) { filters[parts[0]] = range }
break
case "extreme":
var range = this.parseRange(parts[1])
if (range) { filters.oni = this.parseRange(parts[1]) }
break
case "clear":
case "silver":
case "gold":
case "genre":
case "lyrics":
case "creative":
case "played":
case "maker":
case "diverge":
filters[parts[0]] = parts[1]
break
}
editedSplit.splice(editedSplit.indexOf(word), 1)
}
}
})
query = editedSplit.join(" ").trim().normalize("NFD").replace(/[\u0300-\u036f]/g, "")
var totalFilters = Object.keys(filters).length
for(var i = 0; i < assets.songs.length; i++){
var song = assets.songs[i]
var passedFilters = 0
Object.keys(filters).forEach(filter => {
var value = filters[filter]
switch(filter){
case "easy":
case "normal":
case "hard":
case "oni":
case "ura":
if(song.courses[filter] && song.courses[filter].stars >= value.min && song.courses[filter].stars <= value.max){
passedFilters++
}
break
case "clear":
case "silver":
case "gold":
if(value === "any"){
var score = scoreStorage.scores[song.hash]
scoreStorage.difficulty.forEach(difficulty => {
if(score && score[difficulty] && score[difficulty].crown && (filter === "clear" || score[difficulty].crown === filter)){
passedFilters++
}
})
} else {
var score = scoreStorage.scores[song.hash]
if(score && score[value] && score[value].crown && (filter === "clear" || score[value].crown === filter)){
passedFilters++
}
}
break
case "played":
var score = scoreStorage.scores[song.hash]
if((value === "yes" && score) || (value === "no" && !score)){
passedFilters++
}
break
case "lyrics":
if((value === "yes" && song.lyrics) || (value === "no" && !song.lyrics)){
passedFilters++
}
break
case "creative":
if((value === "yes" && song.maker) || (value === "no" && !song.maker)){
passedFilters++
}
break
case "maker":
if(song.maker && song.maker.name.toLowerCase().includes(value.toLowerCase())){
passedFilters++
}
break
case "genre":
var cat = assets.categories.find(cat => cat.id === song.category_id)
var aliases = cat.aliases ? cat.aliases.concat([cat.title]) : [cat.title]
if(aliases.find(alias => alias.toLowerCase() === value.toLowerCase())){
passedFilters++
}
break
case "diverge":
var branch = Object.values(song.courses).find(course => course && course.branch)
if((value === "yes" && branch) || (value === "no" && !branch)){
passedFilters++
}
break
}
})
if(passedFilters === totalFilters){
results.push(song)
}
}
var maxResults = totalFilters > 0 && !query ? 100 : 50
if(query){
results = fuzzysort.go(query, results, {
keys: ["titlePrepared", "subtitlePrepared"],
allowTypo: true,
limit: maxResults,
scoreFn: a => {
if(a[0]){
var score0 = a[0].score
a[0].ranges = this.indexesToRanges(a[0].indexes)
if(a[0].indexes.length > 1){
var rangeAmount = a[0].ranges.length
var lastIdx = -3
a[0].ranges.forEach(range => {
if(range[0] - lastIdx <= 2){
rangeAmount--
score0 -= 1000
}
lastIdx = range[1]
})
var index = a[0].target.toLowerCase().indexOf(query)
if(index !== -1){
a[0].ranges = [[index, index + query.length - 1]]
}else if(rangeAmount > a[0].indexes.length / 2){
score0 = -Infinity
a[0].ranges = null
}else if(rangeAmount !== 1){
score0 -= 9000
}
}
}
if(a[1]){
var score1 = a[1].score - 1000
a[1].ranges = this.indexesToRanges(a[1].indexes)
if(a[1].indexes.length > 1){
var rangeAmount = a[1].ranges.length
var lastIdx = -3
a[1].ranges.forEach(range => {
if(range[0] - lastIdx <= 2){
rangeAmount--
score1 -= 1000
}
lastIdx = range[1]
})
var index = a[1].target.indexOf(query)
if(index !== -1){
a[1].ranges = [[index, index + query.length - 1]]
}else if(rangeAmount > a[1].indexes.length / 2){
score1 = -Infinity
a[1].ranges = null
}else if(rangeAmount !== 1){
score1 -= 9000
}
}
}
if(a[0]){
return a[1] ? Math.max(score0, score1) : score0
}else{
return a[1] ? score1 : -Infinity
}
}
})
}else{
results = results.map(result => {
return {obj: result}
}).slice(0, maxResults)
}
return results
}
indexesToRanges(indexes){
var ranges = []
var range
indexes.forEach(idx => {
if(range && range[1] === idx - 1){
range[1] = idx
}else{
range = [idx, idx]
ranges.push(range)
}
})
return ranges
}
searchInput(){
var text = this.search.input.value
localStorage.setItem("lastSearchQuery", text)
text = text.toLowerCase()
if(text.length === 0){
this.setSearchTip()
return
}
var new_results = this.performSearch(text)
if (new_results.length === 0) {
this.setSearchTip(strings.search.noResults, true)
return
} else if (this.search.tip) {
this.search.tip.remove()
delete this.search.tip
}
var resultsDiv = this.search.div.querySelector(":scope #song-search-results")
resultsDiv.innerHTML = ""
this.search.results = []
var fontSize = parseFloat(getComputedStyle(this.search.div.querySelector(":scope #song-search")).fontSize.slice(0, -2))
var resultsWidth = parseFloat(getComputedStyle(resultsDiv).width.slice(0, -2))
var vmin = Math.min(innerWidth, lastHeight) / 100
var courseWidth = Math.min(3 * fontSize * 1.2, 7 * vmin)
var resultWidth = resultsWidth - 1.8 * fontSize - 0.8 * fontSize - (courseWidth + 0.4 * fontSize * 1.2) * 5 - 0.6 * fontSize
this.ctx.save()
var fragment = document.createDocumentFragment()
new_results.forEach(result => {
var result = this.createSearchResult(result, resultWidth, fontSize)
fragment.appendChild(result)
this.search.results.push(result)
})
resultsDiv.appendChild(fragment)
this.ctx.restore()
}
searchClick(e){
if((e.target.id === "song-search-container" || e.target.id === "song-search-close") && e.which === 1){
this.removeSearch(true)
}else if(e.which === 1){
var songEl = e.target.closest(".song-search-result")
if(songEl){
var songId = parseInt(songEl.dataset.songId)
this.searchProceed(songId)
}
}
}
searchProceed(songId){
var song = this.songs.find(song => song.id === songId)
this.removeSearch()
this.playBgm(false)
var songIndex = this.songs.findIndex(song => song.id === songId)
this.setSelectedSong(songIndex)
this.toSelectDifficulty()
}
onusers(response){ onusers(response){
var p2InSong = false var p2InSong = false
this.songs.forEach(song => { this.songs.forEach(song => {
@ -3268,17 +2702,17 @@ class SongSelect{
if(this.state.screen !== "difficulty"){ if(this.state.screen !== "difficulty"){
this.toSelectDifficulty({player: response.value.player}) this.toSelectDifficulty({player: response.value.player})
} }
this.searchEnabled = false this.search.enabled = false
p2InSong = true p2InSong = true
this.removeSearch() this.search.remove()
} }
} }
} }
}) })
} }
if(!this.searchEnabled && !p2InSong){ if(!this.search.enabled && !p2InSong){
this.searchEnabled = true this.search.enabled = true
} }
} }
onsongsel(response){ onsongsel(response){
@ -3404,6 +2838,7 @@ class SongSelect{
this.sessionCache.clean() this.sessionCache.clean()
this.currentSongCache.clean() this.currentSongCache.clean()
this.nameplateCache.clean() this.nameplateCache.clean()
this.search.clean()
assets.sounds["bgm_songsel"].stop() assets.sounds["bgm_songsel"].stop()
if(!this.bgmEnabled){ if(!this.bgmEnabled){
snd.musicGain.fadeIn() snd.musicGain.fadeIn()
@ -3425,13 +2860,8 @@ class SongSelect{
pageEvents.remove(this.touchFullBtn, "click") pageEvents.remove(this.touchFullBtn, "click")
delete this.touchFullBtn delete this.touchFullBtn
} }
if(this.searchStyle){
loader.screen.removeChild(this.searchStyle)
}
delete this.selectable delete this.selectable
delete this.ctx delete this.ctx
delete this.canvas delete this.canvas
delete this.searchContainer
delete this.searchStyle
} }
} }

View File

@ -1331,6 +1331,17 @@ var translations = {
version: { version: {
ja: "Ver. %s", ja: "Ver. %s",
en: "Version %s" en: "Version %s"
},
browse: {
ja: "参照する…",
en: "Browse...",
cn: "浏览…",
tw: "開啟檔案…",
ko: "찾아보기…"
},
noPlugins: {
ja: null,
en: "No .taikoweb.js plugin files have been found in the provided file list."
} }
}, },
search: { search: {

View File

@ -8,6 +8,7 @@ class Titlescreen{
if(!songId){ if(!songId){
loader.changePage("titlescreen", false) loader.changePage("titlescreen", false)
loader.screen.style.backgroundImage = ""
this.titleScreen = document.getElementById("title-screen") this.titleScreen = document.getElementById("title-screen")
this.proceed = document.getElementById("title-proceed") this.proceed = document.getElementById("title-proceed")
@ -75,8 +76,9 @@ class Titlescreen{
} }
pageEvents.remove(p2, "message") pageEvents.remove(p2, "message")
if(this.customFolder && !fromP2 && !assets.customSongs){ if(this.customFolder && !fromP2 && !assets.customSongs){
var customSongs = new CustomSongs(this.touched, true) var customSongs = new CustomSongs(this.touched, true, true)
var soundPlayed = false var soundPlayed = false
var noError = true
var promises = [] var promises = []
var allFiles = [] var allFiles = []
this.customFolder.forEach(file => { this.customFolder.forEach(file => {
@ -95,6 +97,13 @@ class Titlescreen{
setTimeout(() => { setTimeout(() => {
new SongSelect(false, false, this.touched, this.songId) new SongSelect(false, false, this.touched, this.songId)
}, 500) }, 500)
noError = false
}).then(() => {
if(noError){
setTimeout(() => {
new SongSelect("customSongs", false, this.touchEnabled)
}, 500)
}
}) })
}else{ }else{
setTimeout(() => { setTimeout(() => {

View File

@ -12,8 +12,9 @@
if(noSmoothing){ if(noSmoothing){
this.ctx.imageSmoothingEnabled = false this.ctx.imageSmoothingEnabled = false
} }
if(resolution === "lowest"){ this.multiplayer = this.controller.multiplayer
this.canvas.style.imageRendering = "pixelated" if(this.multiplayer !== 2 && resolution === "lowest"){
document.getElementById("game").classList.add("pixelated")
} }
this.gameDiv = document.getElementById("game") this.gameDiv = document.getElementById("game")
@ -97,7 +98,6 @@
this.branchCache = new CanvasCache(noSmoothing) this.branchCache = new CanvasCache(noSmoothing)
this.nameplateCache = new CanvasCache(noSmoothing) this.nameplateCache = new CanvasCache(noSmoothing)
this.multiplayer = this.controller.multiplayer
if(this.multiplayer === 2){ if(this.multiplayer === 2){
this.player = p2.player === 2 ? 1 : 2 this.player = p2.player === 2 ? 1 : 2
}else{ }else{

View File

@ -7,6 +7,8 @@
<meta name="viewport" content="width=device-width, user-scalable=no"> <meta name="viewport" content="width=device-width, user-scalable=no">
<meta name="description" content="パソコンとスマホのブラウザ向けの太鼓の達人シミュレータ 🥁 Taiko no Tatsujin rhythm game simulator for desktop and mobile browsers"> <meta name="description" content="パソコンとスマホのブラウザ向けの太鼓の達人シミュレータ 🥁 Taiko no Tatsujin rhythm game simulator for desktop and mobile browsers">
<meta name="keywords" content="taiko no tatsujin, taiko, don chan, online, rhythm, browser, html5, game, for browsers, pc, arcade, emulator, free, download, website, 太鼓の達人, 太鼓ウェブ, 太鼓之達人, 太鼓達人, 太鼓网页, 网页版, 太鼓網頁, 網頁版, 태고의 달인, 태고 웹"> <meta name="keywords" content="taiko no tatsujin, taiko, don chan, online, rhythm, browser, html5, game, for browsers, pc, arcade, emulator, free, download, website, 太鼓の達人, 太鼓ウェブ, 太鼓之達人, 太鼓達人, 太鼓网页, 网页版, 太鼓網頁, 網頁版, 태고의 달인, 태고 웹">
<meta name="robots" content="notranslate">
<meta name="robots" content="noimageindex">
<meta name="color-scheme" content="only light"> <meta name="color-scheme" content="only light">
<link rel="stylesheet" href="/src/css/loader.css?{{version.commit_short}}"> <link rel="stylesheet" href="/src/css/loader.css?{{version.commit_short}}">
@ -22,7 +24,7 @@
<div id="screen" class="pattern-bg"></div> <div id="screen" class="pattern-bg"></div>
<div data-nosnippet id="version"> <div data-nosnippet id="version">
{% if version.version and version.commit_short and version.commit %} {% if version.version and version.commit_short and version.commit %}
<a href="{{version.url}}commit/{{version.commit}}" target="_blank" id="version-link" class="stroke-sub" alt="taiko-web ver.{{version.version}} ({{version.commit_short}})">taiko-web ver.{{version.version}} ({{version.commit_short}})</a> <a href="{{version.url}}commit/{{version.commit}}" target="_blank" rel="noopener" id="version-link" class="stroke-sub" alt="taiko-web ver.{{version.version}} ({{version.commit_short}})">taiko-web ver.{{version.version}} ({{version.commit_short}})</a>
{% else %} {% else %}
<a href="{{version.url}}" target="_blank" rel="noopener" id="version-link" class="stroke-sub" alt="taiko-web (unknown version)">taiko-web (unknown version)</a> <a href="{{version.url}}" target="_blank" rel="noopener" id="version-link" class="stroke-sub" alt="taiko-web (unknown version)">taiko-web (unknown version)</a>
{% endif %} {% endif %}