SongSelect: Add Settings

- Resolution can be adjusted, as well as touch drum animation on mobile
- A translation text file "songtitle.txt" can be imported
  - Titles and translated titles are each on their own line, if a line begins with a language code, it will translate the song title that is above
  - An example file can be found here: https://gist.github.com/LoveEevee/65fe66f0b54c0536f96fd2f4862984d4
- The page will fail to load if version on the page does not match /api/config
- Disabled Tab key while playing, before hitting it would focus the version link
- Fix forcing branches in debug not working
- Fixed not being able to click on songs that do not have oni but have ura
- Fix unexpected category being used as a fallback
- Fix verticalText and layeredText not accepting anything except strings
This commit is contained in:
LoveEevee 2019-04-04 23:40:11 +03:00
parent 7411022b14
commit cb64777012
20 changed files with 527 additions and 86 deletions

3
app.py
View File

@ -111,7 +111,6 @@ def route_api_songs():
raw_categories = query_db('select * from categories') raw_categories = query_db('select * from categories')
categories = {} categories = {}
def_category = {'title': None, 'title_en': None}
for cat in raw_categories: for cat in raw_categories:
categories[cat[0]] = cat[1] categories[cat[0]] = cat[1]
@ -126,7 +125,7 @@ def route_api_songs():
song_type = song[12] song_type = song[12]
preview = song[15] preview = song[15]
category_out = categories[song[11]] if song[11] in categories else def_category category_out = categories[song[11]] if song[11] in categories else ""
song_skin_out = song_skins[song[14]] if song[14] in song_skins else None song_skin_out = song_skins[song[14]] if song[14] in song_skins else None
songs_out.append({ songs_out.append({

View File

@ -10,24 +10,9 @@
#loading-don{ #loading-don{
background-image: url("dancing-don.gif"); background-image: url("dancing-don.gif");
} }
#touch-drum-img{
background-image: url("touch_drum.png");
}
#touch-full-btn{ #touch-full-btn{
background-image: url("touch_fullscreen.png"); background-image: url("touch_fullscreen.png");
} }
#touch-pause-btn{ #touch-pause-btn{
background-image: url("touch_pause.png"); background-image: url("touch_pause.png");
} }
.song-stage-1{
background-image: url("bg_stage_1.png");
background-size: calc(100vh / 720 * 66);
}
.song-stage-2{
background-image: url("bg_stage_2.png");
background-size: calc(100vh / 720 * 254);
}
.song-stage-3{
background-image: url("bg_stage_3.png");
background-size: calc(100vh / 720 * 458);
}

View File

@ -92,7 +92,8 @@ kbd{
color: #000; color: #000;
} }
.taibtn:hover, .taibtn:hover,
#tutorial-end-button:hover{ #tutorial-end-button:hover,
#tutorial-end-button.selected{
position: relative; position: relative;
z-index: 1; z-index: 1;
color: #fff; color: #fff;
@ -147,6 +148,47 @@ kbd{
cursor: text; cursor: text;
overflow: hidden; overflow: hidden;
} }
.setting-box{
display: flex;
height: 2em;
margin-top: 1.2em;
border: 0.25em solid #000;
border-radius: 0.5em;
padding: 0.3em;
outline: none;
color: #000;
cursor: pointer;
}
.setting-box:first-child{
margin-top: 0;
}
#tutorial-content:not(:hover) .setting-box.selected,
.setting-box:hover{
background: #ffb547;
border-color: #21211a;
}
.setting-name{
position: relative;
width: 50%;
padding: 0.3em;
font-size: 1.3em;
box-sizing: border-box;
}
#tutorial-content:not(:hover) .setting-box.selected .setting-name,
.setting-box:hover .setting-name{
color: #fff;
z-index: 0;
}
.setting-name::before{
padding-left: 0.3em;
}
.setting-value{
background: #fff;
width: 50%;
border-radius: 0.2em;
padding: 0.5em;
box-sizing: border-box;
}
@keyframes bgscroll{ @keyframes bgscroll{
from{ from{
background-position: 0 top; background-position: 0 top;

View File

@ -26,6 +26,7 @@
height: calc(44 / 720 * 100vh); height: calc(44 / 720 * 100vh);
background-position: center bottom; background-position: center bottom;
background-repeat-y: no-repeat; background-repeat-y: no-repeat;
background-size: auto 100%;
bottom: 0; bottom: 0;
} }
.portrait #songbg{ .portrait #songbg{

View File

@ -27,7 +27,8 @@ var assets = {
"debug.js", "debug.js",
"session.js", "session.js",
"importsongs.js", "importsongs.js",
"logo.js" "logo.js",
"settings.js"
], ],
"css": [ "css": [
"main.css", "main.css",
@ -71,11 +72,7 @@ var assets = {
"bg_score_p2.png", "bg_score_p2.png",
"bg_settings.png", "bg_settings.png",
"bg_pause.png", "bg_pause.png",
"bg_stage_1.png",
"bg_stage_2.png",
"bg_stage_3.png",
"badge_auto.png", "badge_auto.png",
"touch_drum.png",
"touch_pause.png", "touch_pause.png",
"touch_fullscreen.png", "touch_fullscreen.png",
"mimizu.png", "mimizu.png",
@ -168,7 +165,8 @@ var assets = {
"tutorial.html", "tutorial.html",
"about.html", "about.html",
"debug.html", "debug.html",
"session.html" "session.html",
"settings.html"
], ],
"songs": [], "songs": [],

View File

@ -106,6 +106,9 @@ class CanvasCache{
this.ctx.clearRect(0, 0, this.w, this.h) this.ctx.clearRect(0, 0, this.w, this.h)
} }
clean(){ clean(){
if(!this.canvas){
return
}
this.resize(1, 1, 1) this.resize(1, 1, 1)
delete this.map delete this.map
delete this.ctx delete this.ctx

View File

@ -277,7 +277,7 @@
verticalText(config){ verticalText(config){
var ctx = config.ctx var ctx = config.ctx
var inputText = config.text var inputText = config.text.toString()
var mul = config.fontSize / 40 var mul = config.fontSize / 40
var ura = false var ura = false
var r = this.regex var r = this.regex
@ -601,7 +601,7 @@
layeredText(config, layers){ layeredText(config, layers){
var ctx = config.ctx var ctx = config.ctx
var inputText = config.text var inputText = config.text.toString()
var mul = config.fontSize / 40 var mul = config.fontSize / 40
var ura = false var ura = false
var r = this.regex var r = this.regex

View File

@ -141,6 +141,7 @@ class Debug{
if(circles[i].endTime >= measureMS){ if(circles[i].endTime >= measureMS){
break break
} }
game.skipNote(circles[i])
} }
if(game.mainMusicPlaying){ if(game.mainMusicPlaying){
game.mainMusicPlaying = false game.mainMusicPlaying = false
@ -215,6 +216,9 @@ class Debug{
var name = this.branchSelect.value var name = this.branchSelect.value
game.branch = name === "auto" ? false : name game.branch = name === "auto" ? false : name
game.branchSet = name === "auto" game.branchSet = name === "auto"
if(noRestart){
game.branchStatic = true
}
var selectedOption = this.branchSelect.selectedOptions[0] var selectedOption = this.branchSelect.selectedOptions[0]
this.branchSelect.style.background = selectedOption.style.background this.branchSelect.style.background = selectedOption.style.background
if(this.restartCheckbox.checked && !noRestart){ if(this.restartCheckbox.checked && !noRestart){

View File

@ -150,10 +150,13 @@ class Game{
} }
} }
if(view.branch !== currentMeasure){ if(view.branch !== currentMeasure){
view.branchAnimate = { if(!this.branchStatic){
ms: ms, view.branchAnimate = {
fromBranch: view.branch ms: ms,
fromBranch: view.branch
}
} }
this.branchStatic = false
view.branch = currentMeasure view.branch = currentMeasure
} }
} }

View File

@ -29,6 +29,8 @@
this.otherFiles = {} this.otherFiles = {}
this.songs = [] this.songs = []
this.stylesheet = [] this.stylesheet = []
this.songTitle = {}
this.uraRegex = /\s*[\(]裏[\)]$/
this.courseTypes = { this.courseTypes = {
"easy": 0, "easy": 0,
"normal": 1, "normal": 1,
@ -82,7 +84,7 @@
file: file, file: file,
index: i index: i
}) })
}else if(name === "genre.ini" || name === "box.def"){ }else if(name === "genre.ini" || name === "box.def" || name === "songtitle.txt"){
var level = (file.webkitRelativePath.match(/\//g) || []).length var level = (file.webkitRelativePath.match(/\//g) || []).length
metaFiles.push({ metaFiles.push({
file: file, file: file,
@ -154,6 +156,20 @@
break break
} }
} }
}else if(name === "songtitle.txt"){
var lastTitle
for(var i = 0; i < data.length; i++){
var line = data[i].trim()
if(line){
var lang = line.slice(0, 2)
if(line.charAt(2) !== " " || !(lang in allStrings)){
this.songTitle[line] = {}
lastTitle = line
}else if(lastTitle){
this.songTitle[lastTitle][lang] = line.slice(3).trim()
}
}
}
} }
if(category){ if(category){
var metaPath = file.webkitRelativePath.toLowerCase().slice(0, file.name.length * -1) var metaPath = file.webkitRelativePath.toLowerCase().slice(0, file.name.length * -1)
@ -168,7 +184,11 @@
this.osuFiles.forEach(filesLoop) this.osuFiles.forEach(filesLoop)
} }
}).catch(() => {}) }).catch(() => {})
reader.readAsText(file, "sjis") if(name === "songtitle.txt"){
reader.readAsText(file)
}else{
reader.readAsText(file, "sjis")
}
return promise return promise
} }
@ -212,8 +232,19 @@
songObj.song_skin = this.getSkin(dir, meta.taikowebskin) songObj.song_skin = this.getSkin(dir, meta.taikowebskin)
} }
for(var id in allStrings){ for(var id in allStrings){
var songTitle = songObj.title
var ura = ""
if(songTitle){
var uraPos = songTitle.search(this.uraRegex)
if(uraPos !== -1){
ura = songTitle.slice(uraPos)
songTitle = songTitle.slice(0, uraPos)
}
}
if(meta["title" + id]){ if(meta["title" + id]){
titleLang[id] = meta["title" + id] titleLang[id] = meta["title" + id]
}else if(songTitle in this.songTitle && this.songTitle[songTitle][id]){
titleLang[id] = this.songTitle[songTitle][id] + ura
} }
if(meta["subtitle" + id]){ if(meta["subtitle" + id]){
subtitleLang[id] = meta["subtitle" + id] subtitleLang[id] = meta["subtitle" + id]

View File

@ -59,8 +59,8 @@ class Keyboard{
} }
pageEvents.keyAdd(this, "all", "both", event => { pageEvents.keyAdd(this, "all", "both", event => {
if(event.keyCode === 8){ if(event.keyCode === 27 || event.keyCode === 8 || event.keyCode === 9){
// Disable back navigation when pressing backspace // Escape, Backspace, Tab
event.preventDefault() event.preventDefault()
} }
var key = this.kbdSearch[event.keyCode] var key = this.kbdSearch[event.keyCode]

View File

@ -38,7 +38,15 @@ class Loader{
document.head.appendChild(script) document.head.appendChild(script)
}) })
this.addPromise(new Promise(resolve => { this.addPromise(new Promise((resolve, reject) => {
if(
versionLink.href !== gameConfig._version.url &&
gameConfig._version.commit &&
versionLink.href.indexOf(gameConfig._version.commit) === -1
){
// Version in the config does not match version on the page
reject()
}
var cssCount = document.styleSheets.length + assets.css.length var cssCount = document.styleSheets.length + assets.css.length
assets.css.forEach(name => { assets.css.forEach(name => {
var stylesheet = document.createElement("link") var stylesheet = document.createElement("link")
@ -195,6 +203,8 @@ class Loader{
p2.hash("") p2.hash("")
} }
settings = new Settings()
Promise.all(this.promises).then(() => { Promise.all(this.promises).then(() => {
this.canvasTest.drawAllImages().then(result => { this.canvasTest.drawAllImages().then(result => {
perf.allImg = result perf.allImg = result
@ -212,7 +222,7 @@ class Loader{
} }
addPromise(promise){ addPromise(promise){
this.promises.push(promise) this.promises.push(promise)
promise.then(this.assetLoaded.bind(this)) promise.then(this.assetLoaded.bind(this), this.errorMsg.bind(this))
} }
loadSound(name, gain){ loadSound(name, gain){
var id = this.getFilename(name) var id = this.getFilename(name)
@ -258,7 +268,9 @@ class Loader{
} }
clean(){ clean(){
var fontDetectDiv = document.getElementById("fontdetectHelper") var fontDetectDiv = document.getElementById("fontdetectHelper")
fontDetectDiv.parentNode.removeChild(fontDetectDiv) if(fontDetectDiv){
fontDetectDiv.parentNode.removeChild(fontDetectDiv)
}
delete this.loaderPercentage delete this.loaderPercentage
delete this.loaderProgress delete this.loaderProgress
delete this.promises delete this.promises

View File

@ -4,6 +4,15 @@ class LoadSong{
this.autoPlayEnabled = autoPlayEnabled this.autoPlayEnabled = autoPlayEnabled
this.multiplayer = multiplayer this.multiplayer = multiplayer
this.touchEnabled = touchEnabled this.touchEnabled = touchEnabled
var resolution = settings.getItem("resolution")
this.imgScale = 1
if(resolution === "medium"){
this.imgScale = 0.75
}else if(resolution === "low"){
this.imgScale = 0.5
}else if(resolution === "lowest"){
this.imgScale = 0.25
}
loader.changePage("loadsong", true) loader.changePage("loadsong", true)
var loadingText = document.getElementById("loading-text") var loadingText = document.getElementById("loading-text")
@ -57,9 +66,10 @@ class LoadSong{
} }
if(type === "don"){ if(type === "don"){
song.donBg = null song.donBg = null
} }else if(type === "song"){
if(type === "song"){
song.songBg = null song.songBg = null
}else if(type === "stage"){
song.songStage = null
} }
} }
} }
@ -71,19 +81,14 @@ class LoadSong{
continue continue
} }
let img = document.createElement("img") let img = document.createElement("img")
if(!songObj.music && this.touchEnabled && imgLoad[i].type === "song"){ let force = imgLoad[i].type === "song" && this.touchEnabled
if(!songObj.music && (this.imgScale !== 1 || force)){
img.crossOrigin = "Anonymous" img.crossOrigin = "Anonymous"
} }
let promise = pageEvents.load(img) let promise = pageEvents.load(img)
if(imgLoad[i].type === "song"){ promises.push(promise.then(() => {
promises.push(promise.then(() => { return this.scaleImg(img, filename, prefix, force)
return this.scaleImg(img, filename, prefix) }))
}))
}else{
promises.push(promise.then(() => {
assets.image[prefix + filename] = img
}))
}
if(songObj.music){ if(songObj.music){
img.src = URL.createObjectURL(song.songSkin[filename + ".png"]) img.src = URL.createObjectURL(song.songSkin[filename + ".png"])
}else{ }else{
@ -126,6 +131,16 @@ class LoadSong{
this.songData = data.replace(/\0/g, "").split("\n") this.songData = data.replace(/\0/g, "").split("\n")
})) }))
} }
if(this.touchEnabled && !assets.image["touch_drum"]){
let img = document.createElement("img")
if(this.imgScale !== 1){
img.crossOrigin = "Anonymous"
}
promises.push(pageEvents.load(img).then(() => {
return this.scaleImg(img, "touch_drum", "")
}))
img.src = gameConfig.assets_baseurl + "img/touch_drum.png"
}
Promise.all(promises).then(() => { Promise.all(promises).then(() => {
this.setupMultiplayer() this.setupMultiplayer()
}, error => { }, error => {
@ -151,23 +166,23 @@ class LoadSong{
filenames.push("bg_don2_" + this.selectedSong.donBg) filenames.push("bg_don2_" + this.selectedSong.donBg)
} }
} }
if(this.selectedSong.songStage !== null){
filenames.push("bg_stage_" + this.selectedSong.songStage)
}
for(var i = 0; i < filenames.length; i++){ for(var i = 0; i < filenames.length; i++){
for(var letter = 0; letter < 2; letter++){ var filename = filenames[i]
let filenameAb = filenames[i] + (letter === 0 ? "a" : "b") var stage = filename.startsWith("bg_stage_")
for(var letter = 0; letter < (stage ? 1 : 2); letter++){
let filenameAb = filenames[i] + (stage ? "" : (letter === 0 ? "a" : "b"))
if(!(filenameAb in assets.image)){ if(!(filenameAb in assets.image)){
let img = document.createElement("img") let img = document.createElement("img")
if(filenameAb.startsWith("bg_song_")){ let force = filenameAb.startsWith("bg_song_") && this.touchEnabled
if(this.touchEnabled){ if(this.imgScale !== 1 || force){
img.crossOrigin = "Anonymous" img.crossOrigin = "Anonymous"
}
promises.push(pageEvents.load(img).then(() => {
return this.scaleImg(img, filenameAb, "")
}))
}else{
promises.push(pageEvents.load(img).then(() => {
assets.image[filenameAb] = img
}))
} }
promises.push(pageEvents.load(img).then(() => {
return this.scaleImg(img, filenameAb, "", force)
}))
img.src = gameConfig.assets_baseurl + "img/" + filenameAb + ".png" img.src = gameConfig.assets_baseurl + "img/" + filenameAb + ".png"
} }
} }
@ -175,12 +190,16 @@ class LoadSong{
Promise.all(promises).then(resolve, reject) Promise.all(promises).then(resolve, reject)
}) })
} }
scaleImg(img, filename, prefix){ scaleImg(img, filename, prefix, force){
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if(this.touchEnabled){ var scale = this.imgScale
if(force && 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 / 2) var w = Math.floor(img.width * scale)
var h = Math.floor(img.height / 2) var h = Math.floor(img.height * scale)
canvas.width = w canvas.width = w
canvas.height = h canvas.height = h
var ctx = canvas.getContext("2d") var ctx = canvas.getContext("2d")

View File

@ -82,6 +82,7 @@ var perf = {
} }
var strings var strings
var vectors var vectors
var settings
pageEvents.add(root, ["touchstart", "touchmove", "touchend"], event => { pageEvents.add(root, ["touchstart", "touchmove", "touchend"], event => {
if(event.cancelable && cancelTouch && event.target.tagName !== "SELECT"){ if(event.cancelable && cancelTouch && event.target.tagName !== "SELECT"){
@ -90,6 +91,7 @@ pageEvents.add(root, ["touchstart", "touchmove", "touchend"], event => {
}) })
var versionDiv = document.getElementById("version") var versionDiv = document.getElementById("version")
var versionLink = document.getElementById("version-link") var versionLink = document.getElementById("version-link")
versionLink.tabIndex = -1
pageEvents.add(versionDiv, ["click", "touchend"], event => { pageEvents.add(versionDiv, ["click", "touchend"], event => {
if(event.target === versionDiv){ if(event.target === versionDiv){
versionLink.click() versionLink.click()

210
public/src/js/settings.js Normal file
View File

@ -0,0 +1,210 @@
class Settings{
constructor(){
var ios = /iPhone|iPad/.test(navigator.userAgent)
var phone = /Android|iPhone|iPad/.test(navigator.userAgent)
this.items = {
resolution: {
type: "select",
options: ["high", "medium", "low", "lowest"],
default: phone ? "medium" : "high"
},
touchAnimation: {
type: "toggle",
default: !ios,
touch: true
}
}
this.storage = {}
try{
var storage = JSON.parse(localStorage.getItem("settings") || "{}")
for(var i in this.items){
var current = this.items[i]
if(i in storage){
if(current.type === "select" && current.options.indexOf(storage[i]) === -1){
this.storage[i] = null
}else{
this.storage[i] = storage[i]
}
}else{
this.storage[i] = null
}
}
}catch(e){
for(var i in this.items){
this.storage[i] = null
}
}
}
getItem(name){
var value = this.storage[name]
return value === null ? this.items[name].default : value
}
setItem(name, value){
this.storage[name] = value
try{
localStorage.setItem("settings", JSON.stringify(this.storage))
}catch(e){}
}
}
class SettingsView{
constructor(touchEnabled){
this.touchEnabled = touchEnabled
loader.changePage("settings", true)
this.endButton = document.getElementById("tutorial-end-button")
if(touchEnabled){
document.getElementById("tutorial-outer").classList.add("touch-enabled")
}
var tutorialTitle = document.getElementById("tutorial-title")
tutorialTitle.innerText = strings.gameSettings
tutorialTitle.setAttribute("alt", strings.gameSettings)
this.endButton.innerText = strings.settings.ok
this.endButton.setAttribute("alt", strings.settings.ok)
this.resolution = settings.getItem("resolution")
var content = document.getElementById("tutorial-content")
this.items = []
this.selected = 0
for(let i in settings.items){
if(!touchEnabled && settings.items[i].touch){
continue
}
var settingBox = document.createElement("div")
settingBox.classList.add("setting-box")
var nameDiv = document.createElement("div")
nameDiv.classList.add("setting-name", "stroke-sub")
var name = strings.settings[i].name
nameDiv.innerText = name
nameDiv.setAttribute("alt", name)
settingBox.appendChild(nameDiv)
var valueDiv = document.createElement("div")
valueDiv.classList.add("setting-value")
valueDiv.innerText = this.getValue(i)
settingBox.appendChild(valueDiv)
content.appendChild(settingBox)
if(this.items.length === this.selected){
settingBox.classList.add("selected")
}
pageEvents.add(settingBox, ["mousedown", "touchstart"], event => {
event.preventDefault()
this.setValue(i)
})
this.items.push({
id: i,
settingBox: settingBox,
valueDiv: valueDiv
})
}
this.items.push({
id: "back",
settingBox: this.endButton
})
this.kbd = {
"confirm": [13, 32, 70, 74], // Enter, Space, F, J
"previous": [37, 38, 68], // Left, Up, D
"next": [39, 40, 75], // Right, Down, K
"back": [8, 27] // Backspace, Esc
}
pageEvents.once(this.endButton, ["mousedown", "touchstart"]).then(this.onEnd.bind(this))
pageEvents.keyAdd(this, "all", "down", this.keyEvent.bind(this))
this.gamepad = new Gamepad({
"confirm": ["b", "ls", "rs"],
"previous": ["u", "l", "lb", "lt", "lsu", "lsl"],
"next": ["d", "r", "rb", "rt", "lsd", "lsr"],
"back": ["start", "a"]
}, this.keyPressed.bind(this))
pageEvents.send("settings")
}
getValue(name){
var current = settings.items[name]
var value = settings.getItem(name)
if(current.type === "select"){
value = strings.settings[name][value]
}else if(current.type === "toggle"){
value = value ? strings.settings.on : strings.settings.off
}
return value
}
setValue(name){
var current = settings.items[name]
var value = settings.getItem(name)
if(current.type === "select"){
value = current.options[this.mod(current.options.length, current.options.indexOf(value) + 1)]
}else if(current.type === "toggle"){
value = !value
}
settings.setItem(name, value)
this.selected = this.items.findIndex(item => item.id === name)
this.items[this.selected].valueDiv.innerText = this.getValue(name)
}
keyEvent(event){
if(event.keyCode === 27 || event.keyCode === 8 || event.keyCode === 9){
// Escape, Backspace, Tab
event.preventDefault()
}
if(!event.repeat){
for(var i in this.kbd){
if(this.kbd[i].indexOf(event.keyCode) !== -1){
this.keyPressed(true, i)
break
}
}
}
}
keyPressed(pressed, name){
if(!pressed){
return
}
var selected = this.items[this.selected]
if(name === "confirm"){
if(selected.id === "back"){
this.onEnd()
}else{
this.setValue(selected.id)
}
}else if(name === "previous" || name === "next"){
selected.settingBox.classList.remove("selected")
this.selected = this.mod(this.items.length, this.selected + (name === "next" ? 1 : -1))
this.items[this.selected].settingBox.classList.add("selected")
}else if(name === "back"){
this.onEnd()
}
}
onEnd(event){
var touched = false
if(event && event.type === "touchstart"){
event.preventDefault()
touched = true
}
this.clean()
assets.sounds["se_don"].play()
setTimeout(() => {
new SongSelect("settings", false, touched)
}, 500)
}
mod(length, index){
return ((index % length) + length) % length
}
clean(){
this.gamepad.clean()
pageEvents.keyRemove(this, "all")
for(var i in this.items){
pageEvents.remove(this.items[i].settingBox, ["mousedown", "touchstart"])
}
delete this.endButton
delete this.items
if(this.resolution !== settings.getItem("resolution")){
for(var i in assets.image){
if(i.startsWith("bg_song_") || i.startsWith("bg_stage_") || i.startsWith("bg_don_")){
URL.revokeObjectURL(assets.image[i].src)
delete assets.image[i]
}
}
}
}
}

View File

@ -25,21 +25,27 @@ class SongSelect{
}, },
"tutorial": { "tutorial": {
sort: 7, sort: 7,
background: "#9afbe1", background: "#29e8aa",
border: ["#d6ffff", "#6bae9c"], border: ["#86ffbd", "#009a8c"],
outline: "#31ae94" outline: "#08a28c"
}, },
"about": { "about": {
sort: 7, sort: 7,
background: "#91cfff", background: "#a2d0e7",
border: ["#dff0ff", "#6890b2"], border: ["#c6dfff", "#4485d9"],
outline: "#217abb" outline: "#2390d9"
},
"settings": {
sort: 7,
background: "#ce93fa",
border: ["#dec4fd", "#a543ef"],
outline: "#a741ef"
}, },
"browse": { "browse": {
sort: 7, sort: 7,
background: "#9791ff", background: "#fab5d3",
border: ["#e2dfff", "#6d68b2"], border: ["#ffe7ef", "#d36aa2"],
outline: "#5350ba" outline: "#d36aa2"
}, },
"J-POP": { "J-POP": {
sort: 0, sort: 0,
@ -149,6 +155,12 @@ class SongSelect{
action: "about", action: "about",
category: strings.random category: strings.random
}) })
this.songs.push({
title: strings.gameSettings,
skin: this.songSkin.settings,
action: "settings",
category: strings.random
})
if("webkitdirectory" in HTMLInputElement.prototype && !(/Android|iPhone|iPad/.test(navigator.userAgent))){ if("webkitdirectory" in HTMLInputElement.prototype && !(/Android|iPhone|iPad/.test(navigator.userAgent))){
this.browse = document.getElementById("browse") this.browse = document.getElementById("browse")
pageEvents.add(this.browse, "change", this.browseChange.bind(this)) pageEvents.add(this.browse, "change", this.browseChange.bind(this))
@ -352,7 +364,8 @@ class SongSelect{
down: code == 40 down: code == 40
// Down // Down
} }
if(event && (code == 27 || code == 8)){ if(event && (code == 27 || code == 8 || code == 9)){
// Escape, Backspace, Tab
event.preventDefault() event.preventDefault()
} }
if(this.state.screen === "song"){ if(this.state.screen === "song"){
@ -523,7 +536,7 @@ class SongSelect{
}else if(550 < x && x < 1050 && 95 < y && y < 524){ }else if(550 < x && x < 1050 && 95 < y && y < 524){
var moveBy = Math.floor((x - 550) / ((1050 - 550) / 5)) + this.diffOptions.length var moveBy = Math.floor((x - 550) / ((1050 - 550) / 5)) + this.diffOptions.length
var currentSong = this.songs[this.selectedSong] var currentSong = this.songs[this.selectedSong]
if(this.state.ura && moveBy === this.diffOptions + 3 || currentSong.stars[moveBy - this.diffOptions.length]){ if(this.state.ura && moveBy === this.diffOptions.length + 3 || currentSong.stars[moveBy - this.diffOptions.length]){
return moveBy return moveBy
} }
} }
@ -624,6 +637,8 @@ class SongSelect{
this.toTutorial() this.toTutorial()
}else if(currentSong.action === "about"){ }else if(currentSong.action === "about"){
this.toAbout() this.toAbout()
}else if(currentSong.action === "settings"){
this.toSettings()
}else if(currentSong.action === "browse"){ }else if(currentSong.action === "browse"){
this.toBrowse() this.toBrowse()
} }
@ -722,6 +737,13 @@ class SongSelect{
new About(this.touchEnabled) new About(this.touchEnabled)
}, 500) }, 500)
} }
toSettings(){
assets.sounds["se_don"].play()
this.clean()
setTimeout(() => {
new SettingsView(this.touchEnabled)
}, 500)
}
toSession(){ toSession(){
if(p2.socket.readyState !== 1 || assets.customSongs){ if(p2.socket.readyState !== 1 || assets.customSongs){
return return
@ -790,6 +812,14 @@ class SongSelect{
winW = winH / 9 * 32 winW = winH / 9 * 32
} }
this.pixelRatio = window.devicePixelRatio || 1 this.pixelRatio = window.devicePixelRatio || 1
var resolution = settings.getItem("resolution")
if(resolution === "medium"){
this.pixelRatio *= 0.75
}else if(resolution === "low"){
this.pixelRatio *= 0.5
}else if(resolution === "lowest"){
this.pixelRatio *= 0.25
}
winW *= this.pixelRatio winW *= this.pixelRatio
winH *= this.pixelRatio winH *= this.pixelRatio
var ratioX = winW / 1280 var ratioX = winW / 1280

View File

@ -101,7 +101,7 @@ class SoundGain{
this.volume = amount this.volume = amount
} }
setVolumeMul(amount){ setVolumeMul(amount){
this.setVolume(Math.sqrt(amount * amount * this.defaultVol)) this.setVolume(amount * this.defaultVol)
} }
setCrossfade(amount){ setCrossfade(amount){
this.setVolume(Math.sqrt(Math.sin(Math.PI / 2 * amount))) this.setVolume(Math.sqrt(Math.sin(Math.PI / 2 * amount)))

View File

@ -24,6 +24,7 @@
this.randomSong = "ランダムに曲をえらぶ" this.randomSong = "ランダムに曲をえらぶ"
this.howToPlay = "あそびかた説明" this.howToPlay = "あそびかた説明"
this.aboutSimulator = "このシミュレータについて" this.aboutSimulator = "このシミュレータについて"
this.gameSettings = "ゲーム設定"
this.browse = "参照する…" this.browse = "参照する…"
this.defaultSongList = "デフォルト曲リスト" this.defaultSongList = "デフォルト曲リスト"
this.songOptions = "演奏オプション" this.songOptions = "演奏オプション"
@ -98,6 +99,21 @@
linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.", linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.",
cancel: "キャンセル" cancel: "キャンセル"
} }
this.settings = {
resolution: {
name: "ゲームの解像度",
high: "高",
medium: "中",
low: "低",
lowest: "最低"
},
touchAnimation: {
name: "タッチアニメーション"
},
on: "オン",
off: "オフ",
ok: "OK"
}
this.browserSupport = { this.browserSupport = {
browserWarning: "サポートされていないブラウザを実行しています (%s)", browserWarning: "サポートされていないブラウザを実行しています (%s)",
details: "詳しく", details: "詳しく",
@ -131,6 +147,7 @@ function StringsEn(){
this.randomSong = "Random Song" this.randomSong = "Random Song"
this.howToPlay = "How to Play" this.howToPlay = "How to Play"
this.aboutSimulator = "About Simulator" this.aboutSimulator = "About Simulator"
this.gameSettings = "Game Settings"
this.browse = "Browse…" this.browse = "Browse…"
this.defaultSongList = "Default Song List" this.defaultSongList = "Default Song List"
this.songOptions = "Song Options" this.songOptions = "Song Options"
@ -205,6 +222,21 @@ function StringsEn(){
linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.", linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.",
cancel: "Cancel" cancel: "Cancel"
} }
this.settings = {
resolution: {
name: "Game Resolution",
high: "High",
medium: "Medium",
low: "Low",
lowest: "Lowest"
},
touchAnimation: {
name: "Touch Animation"
},
on: "On",
off: "Off",
ok: "OK"
}
this.browserSupport = { this.browserSupport = {
browserWarning: "You are running an unsupported browser (%s)", browserWarning: "You are running an unsupported browser (%s)",
details: "Details...", details: "Details...",
@ -238,6 +270,7 @@ function StringsCn(){
this.randomSong = "随机选曲" this.randomSong = "随机选曲"
this.howToPlay = "操作说明" this.howToPlay = "操作说明"
this.aboutSimulator = "关于模拟器" this.aboutSimulator = "关于模拟器"
this.gameSettings = "游戏设定"
this.browse = "浏览…" this.browse = "浏览…"
this.defaultSongList = "默认歌曲列表" this.defaultSongList = "默认歌曲列表"
this.songOptions = "选项" this.songOptions = "选项"
@ -312,6 +345,21 @@ function StringsCn(){
linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.", linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.",
cancel: "取消" cancel: "取消"
} }
this.settings = {
resolution: {
name: "游戏分辨率",
high: "高",
medium: "中",
low: "低",
lowest: "最低"
},
touchAnimation: {
name: "触摸动画"
},
on: "开",
off: "关",
ok: "确定"
}
this.browserSupport = { this.browserSupport = {
browserWarning: "You are running an unsupported browser (%s)", browserWarning: "You are running an unsupported browser (%s)",
details: "Details...", details: "Details...",
@ -345,6 +393,7 @@ function StringsTw(){
this.randomSong = "隨機選曲" this.randomSong = "隨機選曲"
this.howToPlay = "操作說明" this.howToPlay = "操作說明"
this.aboutSimulator = "關於模擬器" this.aboutSimulator = "關於模擬器"
this.gameSettings = "遊戲設定"
this.browse = "開啟檔案…" this.browse = "開啟檔案…"
this.defaultSongList = "默認歌曲列表" this.defaultSongList = "默認歌曲列表"
this.songOptions = "選項" this.songOptions = "選項"
@ -419,6 +468,21 @@ function StringsTw(){
linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.", linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.",
cancel: "取消" cancel: "取消"
} }
this.settings = {
resolution: {
name: "遊戲分辨率",
high: "高",
medium: "中",
low: "低",
lowest: "最低"
},
touchAnimation: {
name: "觸摸動畫"
},
on: "開",
off: "關",
ok: "確定"
}
this.browserSupport = { this.browserSupport = {
browserWarning: "You are running an unsupported browser (%s)", browserWarning: "You are running an unsupported browser (%s)",
details: "Details...", details: "Details...",
@ -452,6 +516,7 @@ function StringsKo(){
this.randomSong = "랜덤" this.randomSong = "랜덤"
this.howToPlay = "지도 시간" this.howToPlay = "지도 시간"
this.aboutSimulator = "게임 정보" this.aboutSimulator = "게임 정보"
this.gameSettings = "게임 설정"
this.browse = "찾아보기…" this.browse = "찾아보기…"
this.defaultSongList = "기본 노래 목록" this.defaultSongList = "기본 노래 목록"
this.songOptions = "옵션" this.songOptions = "옵션"
@ -526,6 +591,21 @@ function StringsKo(){
linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.", linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.",
cancel: "취소" cancel: "취소"
} }
this.settings = {
resolution: {
name: "게임 해상도",
high: "높은",
medium: "중간",
low: "저",
lowest: "최저"
},
touchAnimation: {
name: "터치 애니메이션"
},
on: "온",
off: "오프",
ok: "확인"
}
this.browserSupport = { this.browserSupport = {
browserWarning: "You are running an unsupported browser (%s)", browserWarning: "You are running an unsupported browser (%s)",
details: "Details...", details: "Details...",

View File

@ -118,6 +118,7 @@
this.touchEnabled = this.controller.touchEnabled this.touchEnabled = this.controller.touchEnabled
this.touch = -Infinity this.touch = -Infinity
this.touchAnimation = settings.getItem("touchAnimation")
if(this.multiplayer !== 2){ if(this.multiplayer !== 2){
@ -125,6 +126,8 @@
this.touchDrumDiv = document.getElementById("touch-drum") this.touchDrumDiv = document.getElementById("touch-drum")
this.touchDrumImg = document.getElementById("touch-drum-img") this.touchDrumImg = document.getElementById("touch-drum-img")
this.setBgImage(this.touchDrumImg, assets.image["touch_drum"].src)
if(this.controller.autoPlayEnabled){ if(this.controller.autoPlayEnabled){
this.touchDrumDiv.style.display = "none" this.touchDrumDiv.style.display = "none"
} }
@ -180,6 +183,14 @@
var touchMultiplayer = this.touchEnabled && this.multiplayer && !this.portrait var touchMultiplayer = this.touchEnabled && this.multiplayer && !this.portrait
this.pixelRatio = window.devicePixelRatio || 1 this.pixelRatio = window.devicePixelRatio || 1
var resolution = settings.getItem("resolution")
if(resolution === "medium"){
this.pixelRatio *= 0.75
}else if(resolution === "low"){
this.pixelRatio *= 0.5
}else if(resolution === "lowest"){
this.pixelRatio *= 0.25
}
winW *= this.pixelRatio winW *= this.pixelRatio
winH *= this.pixelRatio winH *= this.pixelRatio
if(this.portrait){ if(this.portrait){
@ -1123,6 +1134,7 @@
if(!selectedSong.songSkin.stage){ if(!selectedSong.songSkin.stage){
this.songStage.classList.add("song-stage-" + selectedSong.songStage) this.songStage.classList.add("song-stage-" + selectedSong.songStage)
this.setBgImage(this.songStage, assets.image["bg_stage_" + selectedSong.songStage].src)
}else if(selectedSong.songSkin.stage !== "none"){ }else if(selectedSong.songSkin.stage !== "none"){
var prefix = selectedSong.songSkin.prefix || "" var prefix = selectedSong.songSkin.prefix || ""
this.setBgImage(this.songStage, assets.image[prefix + "bg_stage_" + songSkinName].src) this.setBgImage(this.songStage, assets.image[prefix + "bg_stage_" + songSkinName].src)
@ -1180,9 +1192,10 @@
} }
setDonBgHeight(){ setDonBgHeight(){
this.donBg.style.setProperty("--h", getComputedStyle(this.donBg).height) this.donBg.style.setProperty("--h", getComputedStyle(this.donBg).height)
this.gameDiv.classList.add("fix-animations") var gameDiv = this.gameDiv
gameDiv.classList.add("fix-animations")
setTimeout(()=>{ setTimeout(()=>{
this.gameDiv.classList.remove("fix-animations") gameDiv.classList.remove("fix-animations")
}, 50) }, 50)
} }
setLayers(elements, file, ab){ setLayers(elements, file, ab){
@ -1700,14 +1713,16 @@
this.touchDrumDiv.style.width = drumWidth + "px" this.touchDrumDiv.style.width = drumWidth + "px"
this.touchDrumDiv.style.height = drumHeight + "px" this.touchDrumDiv.style.height = drumHeight + "px"
} }
if(this.touch > ms - 100){ if(this.touchAnimation){
if(!this.drumPadding){ if(this.touch > ms - 100){
this.drumPadding = true if(!this.drumPadding){
this.touchDrumImg.style.backgroundPositionY = "7px" this.drumPadding = true
this.touchDrumImg.style.backgroundPositionY = "7px"
}
}else if(this.drumPadding){
this.drumPadding = false
this.touchDrumImg.style.backgroundPositionY = ""
} }
}else if(this.drumPadding){
this.drumPadding = false
this.touchDrumImg.style.backgroundPositionY = ""
} }
} }
} }

View File

@ -0,0 +1,7 @@
<div id="tutorial-outer">
<div id="tutorial">
<div id="tutorial-title" class="stroke-sub"></div>
<div id="tutorial-content"></div>
<div id="tutorial-end-button" class="taibtn stroke-sub"></div>
</div>
</div>