diff --git a/app.py b/app.py index 91f45b0..e03643d 100644 --- a/app.py +++ b/app.py @@ -111,7 +111,6 @@ def route_api_songs(): raw_categories = query_db('select * from categories') categories = {} - def_category = {'title': None, 'title_en': None} for cat in raw_categories: categories[cat[0]] = cat[1] @@ -126,7 +125,7 @@ def route_api_songs(): song_type = song[12] 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 songs_out.append({ diff --git a/public/assets/audio/bgm_settings.mp3 b/public/assets/audio/bgm_settings.mp3 new file mode 100644 index 0000000..e09d492 Binary files /dev/null and b/public/assets/audio/bgm_settings.mp3 differ diff --git a/public/assets/img/img.css b/public/assets/img/img.css index 7a9d682..2a79238 100644 --- a/public/assets/img/img.css +++ b/public/assets/img/img.css @@ -10,24 +10,16 @@ #loading-don{ background-image: url("dancing-don.gif"); } -#touch-drum-img{ - background-image: url("touch_drum.png"); -} #touch-full-btn{ background-image: url("touch_fullscreen.png"); } #touch-pause-btn{ background-image: url("touch_pause.png"); } -.song-stage-1{ - background-image: url("bg_stage_1.png"); - background-size: calc(100vh / 720 * 66); +.settings-outer{ + background-image: url("bg_settings.png"); } -.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); +#gamepad-bg, +#gamepad-buttons{ + background-image: url("settings_gamepad.png"); } diff --git a/public/assets/img/settings_gamepad.png b/public/assets/img/settings_gamepad.png new file mode 100644 index 0000000..c287f06 Binary files /dev/null and b/public/assets/img/settings_gamepad.png differ diff --git a/public/assets/img/vectors.json b/public/assets/img/vectors.json index 0e55a2a..536fb30 100644 --- a/public/assets/img/vectors.json +++ b/public/assets/img/vectors.json @@ -66,8 +66,5 @@ "", "logo5": -"", - -"globe": "" } diff --git a/public/src/css/main.css b/public/src/css/main.css index 2ec0cf7..c6e1f80 100644 --- a/public/src/css/main.css +++ b/public/src/css/main.css @@ -23,115 +23,6 @@ left: 0; z-index: -1; } -#tutorial-outer{ - display: flex; - justify-content: center; - align-items: center; - overflow: hidden; - position: absolute; - width: 100%; - height: 100%; -} -#tutorial{ - background: rgb(246, 234, 212); - color: black; - border: 0.25em black solid; - border-radius: 0.5em; - width: 800px; - padding: 1em; - margin: 1em; - font-size: 21px; - position: relative; -} -.touch-enabled #tutorial{ - font-size: 3vmin; -} -#tutorial-title{ - z-index: 1; - position: absolute; - color: white; - top: -0.7em; - font-size: 1.65em; -} -#tutorial-content{ - margin: 0.7em 0; - overflow-y: auto; - max-height: calc(100vh - 14em); -} -kbd{ - font-family: inherit; - padding: 0.1em 0.6em; - border: 1px solid #ccc; - font-size: 0.6em; - background-color: #f7f7f7; - color: #333; - box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2), 0 0 0 2px #ffffff inset; - border-radius: 3px; - display: inline-block; - text-shadow: 0 1px 0 #fff; - line-height: 1.4; - white-space: nowrap; -} -.taibtn{ - display: inline-block; - background: #f6ead4; - padding: 0.4em 0.4em; - border-radius: 0.5em; - border: 0.1em rgba(218, 205, 178, 1) solid; - cursor: pointer; - font-size: 1.4em; - box-sizing: border-box; - color: #555; - text-align: center; -} -#tutorial-end-button{ - float: right; - padding: 0.4em 1.5em; - font-weight: bold; - border-color: #000; - color: #000; -} -.taibtn:hover, -#tutorial-end-button:hover{ - position: relative; - z-index: 1; - color: #fff; - background: #ffb547; - border-color: #fff; -} -.taibtn::before{ - padding-left: inherit; -} -#about-link-btns{ - float: left; - display: flex; -} -#about-link-btns .taibtn{ - margin-right: 0.4em; -} -#diag-txt textarea, -#diag-txt iframe{ - width: 100%; - height: 5em; - font-size: inherit; - resize: none; - word-break: break-all; - margin-bottom: 1em; - background: #fff; - border: 1px solid #a9a9a9; - user-select: all; -} -.text-warn{ - color: #d00; -} -.link-btn a{ - color: inherit; - text-decoration: none; - pointer-events: none; -} -.nowrap{ - white-space: nowrap; -} #session-invite{ width: 100%; height: 1.9em; @@ -149,10 +40,10 @@ kbd{ } @keyframes bgscroll{ from{ - background-position: 0 top; + background-position: 50% top; } to{ - background-position: calc(-100vh / 720 * 512) top; + background-position: calc(50% - 100vh / 720 * 512) top; } } #song-select{ diff --git a/public/src/css/songbg.css b/public/src/css/songbg.css index 9374f93..5f6f2ef 100644 --- a/public/src/css/songbg.css +++ b/public/src/css/songbg.css @@ -26,6 +26,7 @@ height: calc(44 / 720 * 100vh); background-position: center bottom; background-repeat-y: no-repeat; + background-size: auto 100%; bottom: 0; } .portrait #songbg{ diff --git a/public/src/css/titlescreen.css b/public/src/css/titlescreen.css index 6a280a3..3ca80fc 100644 --- a/public/src/css/titlescreen.css +++ b/public/src/css/titlescreen.css @@ -38,53 +38,6 @@ -webkit-text-stroke: 0.25em #f00; filter: blur(0.3vmin); } -#lang{ - font-size: 3vmin; - position: absolute; - bottom: 0; - left: 0; - width: 7em; - height: 4em; - color: #000; - z-index: 5; -} -#lang:focus-within{ - outline: #4d90fe auto 5px; -} -#lang-dropdown{ - font-size: 1.7em; - font-family: Microsoft YaHei, sans-serif; - opacity: 0; - width: 100%; - height: 100%; - color: #000; - cursor: pointer; -} -#lang-id{ - position: absolute; - top: 0; - bottom: 0; - left: 2.6em; - font-family: TnT, Meiryo, sans-serif; - font-size: 1.5em; - font-weight: normal; - color: #fff; - line-height: 2.75em; - z-index: 0; -} -#lang-icon{ - position: absolute; - width: 2.8em; - height: 2.8em; - padding: 0.6em; - fill: currentColor; -} -#lang:hover #lang-icon{ - color: #f00; -} -#lang:hover #lang-id::before { - -webkit-text-stroke: 0.25em #f00; -} #title-disclaimer { text-align: center; position: absolute; diff --git a/public/src/css/view.css b/public/src/css/view.css new file mode 100644 index 0000000..0340d97 --- /dev/null +++ b/public/src/css/view.css @@ -0,0 +1,261 @@ +.view-outer{ + display: flex; + justify-content: center; + align-items: center; + overflow: hidden; + position: absolute; + width: 100%; + height: 100%; + background-position: center; +} +.view{ + background: rgb(246, 234, 212); + color: black; + border: 0.25em black solid; + border-radius: 0.5em; + width: 800px; + max-width: 40em; + padding: 1em; + margin: 1em; + font-size: 21px; + position: relative; +} +@media (max-width: 950px){ + .view-outer:not(.touch-enabled) .view{ + font-size: 3vmin; + } +} +@media (max-height: 650px){ + .view-outer:not(.touch-enabled) .view{ + font-size: 3vmin; + } +} +.touch-enabled .view{ + font-size: 3vmin; +} +.view-title{ + z-index: 1; + position: absolute; + color: white; + top: -0.7em; + font-size: 1.65em; +} +.view-content{ + margin: 0.7em 0; + overflow-y: auto; + max-height: calc(100vh - 14em); +} +kbd{ + font-family: inherit; + padding: 0.1em 0.6em; + border: 1px solid #ccc; + font-size: 0.6em; + background-color: #f7f7f7; + color: #333; + box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2), 0 0 0 2px #ffffff inset; + border-radius: 3px; + display: inline-block; + text-shadow: 0 1px 0 #fff; + line-height: 1.4; + white-space: nowrap; +} +.taibtn{ + display: inline-block; + background: #f6ead4; + padding: 0.4em 0.4em; + border-radius: 0.5em; + border: 0.1em rgba(218, 205, 178, 1) solid; + cursor: pointer; + font-size: 1.4em; + box-sizing: border-box; + color: #555; + text-align: center; +} +.view-end-button{ + float: right; + padding: 0.4em 1.5em; + font-weight: bold; + border-color: #000; + color: #000; + z-index: 1; +} +.taibtn:hover, +.taibtn.selected, +.view-end-button:hover, +.view-end-button.selected{ + position: relative; + color: #fff; + background: #ffb547; + border-color: #fff; +} +.taibtn::before, +.view-end-button::before{ + display: none; +} +.taibtn:hover::before, +.taibtn.selected::before, +.view-end-button:hover::before, +.view-end-button.selected::before{ + display: block +} +.taibtn::before{ + padding-left: inherit; +} +.left-buttons{ + float: left; + display: flex; +} +.left-buttons .taibtn{ + margin-right: 0.4em; +} +#diag-txt textarea, +#diag-txt iframe{ + width: 100%; + height: 5em; + font-size: inherit; + resize: none; + word-break: break-all; + margin-bottom: 1em; + background: #fff; + border: 1px solid #a9a9a9; + user-select: all; +} +.text-warn{ + color: #d00; +} +.link-btn a{ + color: inherit; + text-decoration: none; + pointer-events: none; +} +.nowrap{ + white-space: nowrap; +} +@keyframes border-pulse{ + 0%{border-color: #ff0} + 50%{border-color: rgba(255, 255, 0, 0)} + 100%{border-color: #ff0} +} +@keyframes border-pulse2{ + 0%{border-color: #e29e06} + 50%{border-color: rgba(226, 158, 6, 0)} + 100%{border-color: #e29e06} +} +.settings-outer{ + background-size: 50vh; +} +.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; +} +.settings-outer .view-content:not(:hover) .setting-box.selected, +.view-outer:not(.settings-outer) .setting-box.selected, +.setting-box:hover{ + background: #ffb547; + animation: 2s linear border-pulse infinite; +} +.bold-fonts .setting-box{ + line-height: 1em; +} +.setting-name{ + position: relative; + width: 50%; + padding: 0.3em; + font-size: 1.3em; + box-sizing: border-box; + white-space: nowrap; + overflow: hidden; +} +.view-content:not(:hover) .setting-box.selected .setting-name, +.view-outer:not(.settings-outer) .setting-box.selected .setting-name, +.setting-box:hover .setting-name, +.setting-box:hover #gamepad-value{ + color: #fff; + z-index: 0; +} +.setting-name::before{ + padding-left: 0.3em; +} +.setting-value{ + display: flex; + background: #fff; + width: 50%; + border-radius: 0.2em; + padding: 0.5em; + box-sizing: border-box; +} +.setting-value.selected{ + width: calc(50% + 0.2em); + margin: -0.1em; + border: 0.2em solid #e29e06; + padding: 0.4em; + animation: 2s linear border-pulse2 infinite; +} +.setting-value>div{ + padding: 0 0.4em; + overflow: hidden; + text-overflow: ellipsis; +} +.shadow-outer{ + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + background: rgba(0, 0, 0, 0.5); + z-index: 1; +} +#settings-gamepad{ + display: none; +} +#settings-gamepad .view{ + position: absolute; + margin: auto; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: 574px; + height: 428px; + max-height: calc(100vh - 14em + 88px); +} +#settings-gamepad .setting-box{ + height: auto; +} +#gamepad-bg{ + position: relative; + width: 550px; + height: 317px; + max-height: none; + background-repeat: no-repeat; + text-align: center; + font-size: 1.4em; + cursor: pointer; +} +#gamepad-buttons{ + position: absolute; + left: 141px; + top: 120px; + width: 282px; + height: 131px; + background-position: 0 -318px; + background-repeat: no-repeat; + pointer-events: none; +} +#gamepad-value{ + position: relative; + margin-top: 1em; +} +#gamepad-value::before{ + left: auto; +} diff --git a/public/src/js/about.js b/public/src/js/about.js index d01c66d..104d8a1 100644 --- a/public/src/js/about.js +++ b/public/src/js/about.js @@ -4,20 +4,20 @@ loader.changePage("about", true) cancelTouch = false - this.endButton = document.getElementById("tutorial-end-button") + this.endButton = this.getElement("view-end-button") this.diagTxt = document.getElementById("diag-txt") this.version = document.getElementById("version-link").href - this.tutorialOuter = document.getElementById("tutorial-outer") + this.tutorialOuter = this.getElement("view-outer") if(touchEnabled){ this.tutorialOuter.classList.add("touch-enabled") } this.linkIssues = document.getElementById("link-issues") this.linkEmail = document.getElementById("link-email") - var tutorialTitle = document.getElementById("tutorial-title") + var tutorialTitle = this.getElement("view-title") tutorialTitle.innerText = strings.aboutSimulator tutorialTitle.setAttribute("alt", strings.aboutSimulator) - var tutorialContent = document.getElementById("tutorial-content") + var tutorialContent = this.getElement("view-content") strings.about.bugReporting.forEach(string => { tutorialContent.appendChild(document.createTextNode(string)) tutorialContent.appendChild(document.createElement("br")) @@ -34,20 +34,62 @@ pageEvents.add(this.linkIssues, ["click", "touchend"], this.linkButton.bind(this)) pageEvents.add(this.linkEmail, ["click", "touchend"], this.linkButton.bind(this)) - pageEvents.once(this.endButton, ["mousedown", "touchstart"]).then(this.onEnd.bind(this)) - pageEvents.keyOnce(this, 13, "down").then(this.onEnd.bind(this)) + pageEvents.add(this.endButton, ["mousedown", "touchstart"], this.onEnd.bind(this)) + this.items = [this.linkIssues, this.linkEmail, this.endButton] + this.selected = 2 + this.keyboard = new Keyboard({ + confirm: ["enter", "space", "don_l", "don_r"], + previous: ["left", "up", "ka_l"], + next: ["right", "down", "ka_r"], + back: ["escape"] + }, this.keyPressed.bind(this)) this.gamepad = new Gamepad({ - "confirm": ["start", "b", "ls", "rs"] - }, this.onEnd.bind(this)) + "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("about", this.addDiag()) } + getElement(name){ + return loader.screen.getElementsByClassName(name)[0] + } + keyPressed(pressed, name){ + if(!pressed){ + return + } + var selected = this.items[this.selected] + if(name === "confirm"){ + if(selected === this.endButton){ + this.onEnd() + }else{ + this.getLink(selected).click() + pageEvents.send("about-link", selected) + assets.sounds["se_don"].play() + } + }else if(name === "previous" || name === "next"){ + selected.classList.remove("selected") + this.selected = this.mod(this.items.length, this.selected + (name === "next" ? 1 : -1)) + this.items[this.selected].classList.add("selected") + assets.sounds["se_ka"].play() + }else if(name === "back"){ + this.onEnd() + } + } + mod(length, index){ + return ((index % length) + length) % length + } onEnd(event){ var touched = false - if(event && event.type === "touchstart"){ - event.preventDefault() - touched = true + if(event){ + if(event.type === "touchstart"){ + event.preventDefault() + touched = true + }else if(event.which !== 1){ + return + } } this.clean() assets.sounds["se_don"].play() @@ -154,11 +196,13 @@ linkButton(event){ if(event.target === event.currentTarget){ this.getLink(event.currentTarget).click() - pageEvents.send("about-link", event) + pageEvents.send("about-link", event.currentTarget) + assets.sounds["se_don"].play() } } clean(){ cancelTouch = true + this.keyboard.clean() this.gamepad.clean() pageEvents.remove(this.linkIssues, ["click", "touchend"]) pageEvents.remove(this.linkEmail, ["click", "touchend"]) @@ -166,7 +210,7 @@ if(this.textarea){ pageEvents.remove(this.textarea, ["focus", "blur"]) } - pageEvents.keyRemove(this, 13) + pageEvents.keyRemove(this, "all") delete this.endButton delete this.diagTxt delete this.version diff --git a/public/src/js/assets.js b/public/src/js/assets.js index 4f938a1..e363842 100644 --- a/public/src/js/assets.js +++ b/public/src/js/assets.js @@ -7,6 +7,7 @@ var assets = { "scoresheet.js", "songselect.js", "keyboard.js", + "gameinput.js", "game.js", "controller.js", "circle.js", @@ -27,7 +28,8 @@ var assets = { "debug.js", "session.js", "importsongs.js", - "logo.js" + "logo.js", + "settings.js" ], "css": [ "main.css", @@ -35,7 +37,8 @@ var assets = { "loadsong.css", "game.css", "debug.css", - "songbg.css" + "songbg.css", + "view.css" ], "assetsCss": [ "fonts/fonts.css", @@ -71,18 +74,15 @@ var assets = { "bg_score_p2.png", "bg_settings.png", "bg_pause.png", - "bg_stage_1.png", - "bg_stage_2.png", - "bg_stage_3.png", "badge_auto.png", - "touch_drum.png", "touch_pause.png", "touch_fullscreen.png", "mimizu.png", "results_flowers.png", "results_mikoshi.png", "results_tetsuohana.png", - "results_tetsuohana2.png" + "results_tetsuohana2.png", + "settings_gamepad.png" ], "audioSfx": [ "se_cancel.wav", @@ -154,7 +154,8 @@ var assets = { "audioMusic": [ "bgm_songsel.mp3", "bgm_result.mp3", - "bgm_setsume.mp3" + "bgm_setsume.mp3", + "bgm_settings.mp3" ], "fonts": [ "Kozuka", @@ -168,7 +169,8 @@ var assets = { "tutorial.html", "about.html", "debug.html", - "session.html" + "session.html", + "settings.html" ], "songs": [], diff --git a/public/src/js/canvascache.js b/public/src/js/canvascache.js index b8070a8..a731bcb 100644 --- a/public/src/js/canvascache.js +++ b/public/src/js/canvascache.js @@ -106,6 +106,9 @@ class CanvasCache{ this.ctx.clearRect(0, 0, this.w, this.h) } clean(){ + if(!this.canvas){ + return + } this.resize(1, 1, 1) delete this.map delete this.ctx diff --git a/public/src/js/canvasdraw.js b/public/src/js/canvasdraw.js index 97d3afd..0e77fbb 100644 --- a/public/src/js/canvasdraw.js +++ b/public/src/js/canvasdraw.js @@ -277,7 +277,7 @@ verticalText(config){ var ctx = config.ctx - var inputText = config.text + var inputText = config.text.toString() var mul = config.fontSize / 40 var ura = false var r = this.regex @@ -601,7 +601,7 @@ layeredText(config, layers){ var ctx = config.ctx - var inputText = config.text + var inputText = config.text.toString() var mul = config.fontSize / 40 var ura = false var r = this.regex diff --git a/public/src/js/controller.js b/public/src/js/controller.js index 6e2baae..cf253b4 100644 --- a/public/src/js/controller.js +++ b/public/src/js/controller.js @@ -28,7 +28,7 @@ class Controller{ this.game = new Game(this, this.selectedSong, this.parsedSongData) this.view = new View(this) this.mekadon = new Mekadon(this, this.game) - this.keyboard = new Keyboard(this) + this.keyboard = new GameInput(this) this.playedSounds = {} } @@ -210,11 +210,8 @@ class Controller{ getKeys(){ return this.keyboard.getKeys() } - setKey(keyCode, down, ms){ - return this.keyboard.setKey(keyCode, down, ms) - } - getBindings(){ - return this.keyboard.getBindings() + setKey(pressed, name, ms){ + return this.keyboard.setKey(pressed, name, ms) } getElapsedTime(){ return this.game.elapsedTime diff --git a/public/src/js/debug.js b/public/src/js/debug.js index beeede3..a6fa9a7 100644 --- a/public/src/js/debug.js +++ b/public/src/js/debug.js @@ -141,6 +141,7 @@ class Debug{ if(circles[i].endTime >= measureMS){ break } + game.skipNote(circles[i]) } if(game.mainMusicPlaying){ game.mainMusicPlaying = false @@ -201,11 +202,10 @@ class Debug{ this.controller.autoPlayEnabled = this.autoplayCheckbox.checked if(!this.controller.autoPlayEnabled){ var keyboard = debugObj.controller.keyboard - var kbd = keyboard.getBindings() - keyboard.setKey(kbd.don_l, false) - keyboard.setKey(kbd.don_r, false) - keyboard.setKey(kbd.ka_l, false) - keyboard.setKey(kbd.ka_r, false) + keyboard.setKey(false, "don_l") + keyboard.setKey(false, "don_r") + keyboard.setKey(false, "ka_l") + keyboard.setKey(false, "ka_r") } } } @@ -215,6 +215,9 @@ class Debug{ var name = this.branchSelect.value game.branch = name === "auto" ? false : name game.branchSet = name === "auto" + if(noRestart){ + game.branchStatic = true + } var selectedOption = this.branchSelect.selectedOptions[0] this.branchSelect.style.background = selectedOption.style.background if(this.restartCheckbox.checked && !noRestart){ diff --git a/public/src/js/game.js b/public/src/js/game.js index 19609a4..63a92b7 100644 --- a/public/src/js/game.js +++ b/public/src/js/game.js @@ -150,10 +150,13 @@ class Game{ } } if(view.branch !== currentMeasure){ - view.branchAnimate = { - ms: ms, - fromBranch: view.branch + if(!this.branchStatic){ + view.branchAnimate = { + ms: ms, + fromBranch: view.branch + } } + this.branchStatic = false view.branch = currentMeasure } } @@ -251,29 +254,28 @@ class Game{ return } var keys = this.controller.getKeys() - var kbd = this.controller.getBindings() - var don_l = keys[kbd["don_l"]] && !this.controller.isWaiting(kbd["don_l"], "score") - var don_r = keys[kbd["don_r"]] && !this.controller.isWaiting(kbd["don_r"], "score") - var ka_l = keys[kbd["ka_l"]] && !this.controller.isWaiting(kbd["ka_l"], "score") - var ka_r = keys[kbd["ka_r"]] && !this.controller.isWaiting(kbd["ka_r"], "score") + var don_l = keys["don_l"] && !this.controller.isWaiting("don_l", "score") + var don_r = keys["don_r"] && !this.controller.isWaiting("don_r", "score") + var ka_l = keys["ka_l"] && !this.controller.isWaiting("ka_l", "score") + var ka_r = keys["ka_r"] && !this.controller.isWaiting("ka_r", "score") var checkDon = () => { if(don_l && don_r){ - this.checkKey([kbd["don_l"], kbd["don_r"]], circle, "daiDon") + this.checkKey(["don_l", "don_r"], circle, "daiDon") }else if(don_l){ - this.checkKey([kbd["don_l"]], circle, "don") + this.checkKey(["don_l"], circle, "don") }else if(don_r){ - this.checkKey([kbd["don_r"]], circle, "don") + this.checkKey(["don_r"], circle, "don") } } var checkKa = () => { if(ka_l && ka_r){ - this.checkKey([kbd["ka_l"], kbd["ka_r"]], circle, "daiKa") + this.checkKey(["ka_l", "ka_r"], circle, "daiKa") }else if(ka_l){ - this.checkKey([kbd["ka_l"]], circle, "ka") + this.checkKey(["ka_l"], circle, "ka") }else if(ka_r){ - this.checkKey([kbd["ka_r"]], circle, "ka") + this.checkKey(["ka_r"], circle, "ka") } } var keyTime = this.controller.getKeyTime() diff --git a/public/src/js/gameinput.js b/public/src/js/gameinput.js new file mode 100644 index 0000000..4707652 --- /dev/null +++ b/public/src/js/gameinput.js @@ -0,0 +1,247 @@ +class GameInput{ + constructor(controller){ + this.controller = controller + this.game = this.controller.game + + this.keyboard = new Keyboard({ + ka_l: ["ka_l"], + don_l: ["don_l"], + don_r: ["don_r"], + ka_r: ["ka_r"], + pause: ["q", "esc"], + back: ["backspace"], + previous: ["left", "up"], + next: ["right", "down"], + confirm: ["enter", "space"] + }, this.keyPress.bind(this)) + this.keys = {} + this.waitKeyupScore = {} + this.waitKeyupSound = {} + this.waitKeyupMenu = {} + this.keyTime = { + "don": -Infinity, + "ka": -Infinity + } + this.keyboardEvents = 0 + + var layout = settings.getItem("gamepadLayout") + if(layout === "b"){ + var gameBtn = { + don_l: ["d", "r", "ls"], + don_r: ["a", "x", "rs"], + ka_l: ["u", "l", "lb", "lt"], + ka_r: ["b", "y", "rb", "rt"] + } + }else if(layout === "c"){ + var gameBtn = { + don_l: ["d", "l", "ls"], + don_r: ["a", "b", "rs"], + ka_l: ["u", "r", "lb", "lt"], + ka_r: ["x", "y", "rb", "rt"] + } + }else{ + var gameBtn = { + don_l: ["u", "d", "l", "r", "ls"], + don_r: ["a", "b", "x", "y", "rs"], + ka_l: ["lb", "lt"], + ka_r: ["rb", "rt"] + } + } + this.gamepad = new Gamepad(gameBtn) + this.gamepadInterval = setInterval(this.gamepadKeys.bind(this), 1000 / 60 / 2) + + this.gamepadMenu = new Gamepad({ + cancel: ["a"], + confirm: ["b", "ls", "rs"], + previous: ["u", "l", "lb", "lt", "lsu", "lsl"], + next: ["d", "r", "rb", "rt", "lsd", "lsr"], + pause: ["start"] + }) + + if(controller.multiplayer === 1){ + pageEvents.add(window, "beforeunload", event => { + if(p2.otherConnected){ + pageEvents.send("p2-abandoned", event) + } + }) + } + } + keyPress(pressed, name){ + if(!this.controller.autoPlayEnabled || this.game.isPaused() || name !== "don_l" && name !== "don_r" && name !== "ka_l" && name !== "ka_r"){ + this.setKey(pressed, name, this.game.getAccurateTime()) + } + } + checkGameKeys(){ + if(this.controller.autoPlayEnabled){ + this.checkKeySound("don_l", "don") + this.checkKeySound("don_r", "don") + this.checkKeySound("ka_l", "ka") + this.checkKeySound("ka_r", "ka") + } + } + gamepadKeys(){ + if(!this.game.isPaused() && !this.controller.autoPlayEnabled){ + this.gamepad.play((pressed, name) => { + if(pressed){ + if(this.keys[name]){ + this.setKey(false, name) + } + this.setKey(true, name, this.game.getAccurateTime()) + }else{ + this.setKey(false, name) + } + }) + } + } + checkMenuKeys(){ + if(!this.controller.multiplayer && !this.locked){ + var moveMenu = 0 + var ms = this.game.getAccurateTime() + this.gamepadMenu.play((pressed, name) => { + if(pressed){ + if(this.game.isPaused()){ + if(name === "cancel"){ + this.locked = true + return setTimeout(() => { + this.controller.togglePause() + this.locked = false + }, 200) + } + } + if(this.keys[name]){ + this.setKey(false, name) + } + this.setKey(true, name, ms) + }else{ + this.setKey(false, name) + } + }) + this.checkKey("pause", "menu", () => { + this.controller.togglePause() + for(var key in this.keyTime){ + this.keys[key] = null + this.keyTime[key] = -Infinity + } + }) + var moveMenuMinus = () => { + moveMenu = -1 + } + var moveMenuPlus = () => { + moveMenu = 1 + } + var moveMenuConfirm = () => { + if(this.game.isPaused()){ + this.locked = true + setTimeout(() => { + this.controller.view.pauseConfirm() + this.locked = false + }, 200) + } + } + this.checkKey("previous", "menu", moveMenuMinus) + this.checkKey("ka_l", "menu", moveMenuMinus) + this.checkKey("next", "menu", moveMenuPlus) + this.checkKey("ka_r", "menu", moveMenuPlus) + this.checkKey("confirm", "menu", moveMenuConfirm) + this.checkKey("don_l", "menu", moveMenuConfirm) + this.checkKey("don_r", "menu", moveMenuConfirm) + if(moveMenu && this.game.isPaused()){ + assets.sounds["se_ka"].play() + this.controller.view.pauseMove(moveMenu) + } + } + if(this.controller.multiplayer !== 2){ + this.checkKey("back", "menu", () => { + if(this.controller.multiplayer === 1 && p2.otherConnected){ + p2.send("gameend") + pageEvents.send("p2-abandoned") + } + this.controller.togglePause() + this.controller.songSelection() + }) + } + } + checkKey(name, type, callback){ + if(this.keys[name] && !this.isWaiting(name, type)){ + this.waitForKeyup(name, type) + callback() + } + } + checkKeySound(name, sound){ + this.checkKey(name, "sound", () => { + var circles = this.controller.getCircles() + var circle = circles[this.controller.getCurrentCircle()] + var currentTime = this.keyTime[name] + this.keyTime[sound] = currentTime + if(circle && !circle.isPlayed){ + if(circle.type === "balloon"){ + if(sound === "don" && circle.requiredHits - circle.timesHit <= 1){ + this.controller.playSound("se_balloon") + return + } + } + } + this.controller.playSound("neiro_1_" + sound) + }) + } + getKeys(){ + return this.keys + } + setKey(pressed, name, ms){ + if(pressed){ + this.keys[name] = true + this.waitKeyupScore[name] = false + this.waitKeyupSound[name] = false + this.waitKeyupMenu[name] = false + if(this.game.isPaused()){ + return + } + this.keyTime[name] = ms + if(name == "don_l" || name == "don_r"){ + this.checkKeySound(name, "don") + this.keyboardEvents++ + }else if(name == "ka_l" || name == "ka_r"){ + this.checkKeySound(name, "ka") + this.keyboardEvents++ + } + }else{ + this.keys[name] = false + this.waitKeyupScore[name] = false + this.waitKeyupSound[name] = false + this.waitKeyupMenu[name] = false + } + } + isWaiting(name, type){ + if(type === "score"){ + return this.waitKeyupScore[name] + }else if(type === "sound"){ + return this.waitKeyupSound[name] + }else if(type === "menu"){ + return this.waitKeyupMenu[name] + } + } + waitForKeyup(name, type){ + if(!this.keys[name]){ + return + } + if(type === "score"){ + this.waitKeyupScore[name] = true + }else if(type === "sound"){ + this.waitKeyupSound[name] = true + }else if(type === "menu"){ + this.waitKeyupMenu[name] = true + } + } + getKeyTime(){ + return this.keyTime + } + clean(){ + this.keyboard.clean() + this.gamepad.clean() + this.gamepadMenu.clean() + clearInterval(this.gamepadInterval) + if(this.controller.multiplayer === 1){ + pageEvents.remove(window, "beforeunload") + } + } +} diff --git a/public/src/js/gamepad.js b/public/src/js/gamepad.js index 2956c1a..a4b3327 100644 --- a/public/src/js/gamepad.js +++ b/public/src/js/gamepad.js @@ -1,6 +1,7 @@ class Gamepad{ constructor(bindings, callback){ this.bindings = bindings + this.callback = !!callback this.b = { "a": 0, "b": 1, @@ -87,6 +88,9 @@ class Gamepad{ for(var name in bindings[bind]){ var bindName = bindings[bind][name] this.checkButton(gamepads, this.b[bindName], bind, callback, force[bindName]) + if(!this.b){ + return + } } } break @@ -136,6 +140,11 @@ class Gamepad{ } } clean(){ - clearInterval(this.interval) + if(this.callback){ + clearInterval(this.interval) + } + delete this.bindings + delete this.b + delete this.btn } } diff --git a/public/src/js/importsongs.js b/public/src/js/importsongs.js index 93bd9ec..112b675 100644 --- a/public/src/js/importsongs.js +++ b/public/src/js/importsongs.js @@ -29,6 +29,8 @@ this.otherFiles = {} this.songs = [] this.stylesheet = [] + this.songTitle = {} + this.uraRegex = /\s*[\((]裏[\))]$/ this.courseTypes = { "easy": 0, "normal": 1, @@ -82,7 +84,7 @@ file: file, 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 metaFiles.push({ file: file, @@ -154,6 +156,20 @@ 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){ var metaPath = file.webkitRelativePath.toLowerCase().slice(0, file.name.length * -1) @@ -168,7 +184,11 @@ this.osuFiles.forEach(filesLoop) } }).catch(() => {}) - reader.readAsText(file, "sjis") + if(name === "songtitle.txt"){ + reader.readAsText(file) + }else{ + reader.readAsText(file, "sjis") + } return promise } @@ -212,8 +232,19 @@ songObj.song_skin = this.getSkin(dir, meta.taikowebskin) } 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]){ 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]){ subtitleLang[id] = meta["subtitle" + id] diff --git a/public/src/js/keyboard.js b/public/src/js/keyboard.js index cb9a2ac..949673b 100644 --- a/public/src/js/keyboard.js +++ b/public/src/js/keyboard.js @@ -1,265 +1,105 @@ class Keyboard{ - constructor(controller){ - this.controller = controller - this.game = this.controller.game - - this.kbd = { - "don_l": 70, // F - "don_r": 74, // J - "ka_l": 68, // D - "ka_r": 75, // K - "pause": 81, // Q - "back": 8, // Backspace - "previous": 37, // Left - "next": 39, // Right - "confirm": 13 // Enter + constructor(bindings, callback){ + this.bindings = bindings + this.callback = callback + this.wildcard = false + this.substitute = { + "up": "arrowup", + "right": "arrowright", + "down": "arrowdown", + "left": "arrowleft", + "space": " ", + "esc": "escape", + "ctrl": "control", + "altgr": "altgraph" } - this.kbdAlias = { - "pause": [27], // Esc - "previous": [38], // Up - "next": [40], // Down - "confirm": [32] // Space - } - this.keys = {} - this.waitKeyupScore = {} - this.waitKeyupSound = {} - this.waitKeyupMenu = {} - this.keyTime = { - "don": -Infinity, - "ka": -Infinity - } - this.keyboardEvents = 0 - - var gameBtn = {} - gameBtn[this.kbd["don_l"]] = ["u", "d", "l", "r", "ls"] - gameBtn[this.kbd["don_r"]] = ["a", "b", "x", "y", "rs"] - gameBtn[this.kbd["ka_l"]] = ["lb", "lt"] - gameBtn[this.kbd["ka_r"]] = ["rb", "rt"] - this.gamepad = new Gamepad(gameBtn) - this.gamepadInterval = setInterval(this.gamepadKeys.bind(this), 1000 / 60 / 2) - - var menuBtn = { - "cancel": ["a"], - } - menuBtn[this.kbd["confirm"]] = ["b", "ls", "rs"] - menuBtn[this.kbd["previous"]] = ["u", "l", "lb", "lt", "lsu", "lsl"], - menuBtn[this.kbd["next"]] = ["d", "r", "rb", "rt", "lsd", "lsr"] - menuBtn[this.kbd["pause"]] = ["start"] - this.gamepadMenu = new Gamepad(menuBtn) - - this.kbdSearch = {} - for(var name in this.kbdAlias){ - var list = this.kbdAlias[name] - for(var i in list){ - this.kbdSearch[list[i]] = this.kbd[name] + this.btn = {} + this.update() + pageEvents.keyAdd(this, "all", "both", this.keyEvent.bind(this)) + pageEvents.blurAdd(this, this.blurEvent.bind(this)) + } + update(){ + var kbdSettings = settings.getItem("keyboardSettings") + var drumKeys = {} + for(var name in kbdSettings){ + var keys = kbdSettings[name] + for(var i in keys){ + drumKeys[keys[i]] = name } } - for(var name in this.kbd){ - this.kbdSearch[this.kbd[name]] = this.kbd[name] - } - - pageEvents.keyAdd(this, "all", "both", event => { - if(event.keyCode === 8){ - // Disable back navigation when pressing backspace - event.preventDefault() - } - var key = this.kbdSearch[event.keyCode] - if(key && !event.repeat && this.buttonEnabled(key)){ - var ms = this.game.getAccurateTime() - this.setKey(key, event.type === "keydown", ms) - if(event.type === "keydown"){ - this.keyboardEvents++ + this.kbd = {} + for(var name in this.bindings){ + var keys = this.bindings[name] + for(var i in keys){ + var key = keys[i] + if(key in drumKeys){ + continue } - } - }) - - if(controller.multiplayer === 1){ - pageEvents.add(window, "beforeunload", event => { - if(p2.otherConnected){ - pageEvents.send("p2-abandoned", event) - } - }) - } - } - getBindings(){ - return this.kbd - } - buttonEnabled(keyCode){ - if(this.controller.autoPlayEnabled){ - switch(keyCode){ - case this.kbd["don_l"]: - case this.kbd["don_r"]: - case this.kbd["ka_l"]: - case this.kbd["ka_r"]: - return false - } - } - return true - } - checkGameKeys(){ - if(this.controller.autoPlayEnabled){ - this.checkKeySound(this.kbd["don_l"], "don") - this.checkKeySound(this.kbd["don_r"], "don") - this.checkKeySound(this.kbd["ka_l"], "ka") - this.checkKeySound(this.kbd["ka_r"], "ka") - } - } - gamepadKeys(){ - if(!this.game.isPaused() && !this.controller.autoPlayEnabled){ - this.gamepad.play((pressed, keyCode) => { - if(pressed){ - if(this.keys[keyCode]){ - this.setKey(keyCode, false) - } - this.setKey(keyCode, true, this.game.getAccurateTime()) - }else{ - this.setKey(keyCode, false) - } - }) - } - } - checkMenuKeys(){ - if(!this.controller.multiplayer && !this.locked){ - var moveMenu = 0 - var ms = this.game.getAccurateTime() - this.gamepadMenu.play((pressed, keyCode) => { - if(pressed){ - if(this.game.isPaused()){ - if(keyCode === "cancel"){ - this.locked = true - return setTimeout(() => { - this.controller.togglePause() - this.locked = false - }, 200) + if(key in kbdSettings){ + var keyArray = kbdSettings[key] + for(var j in keyArray){ + key = keyArray[j] + if(!(key in this.kbd)){ + this.kbd[key] = name } } - if(this.keys[keyCode]){ - this.setKey(keyCode, false) - } - this.setKey(keyCode, true, ms) }else{ - this.setKey(keyCode, false) - } - }) - this.checkKey(this.kbd["pause"], "menu", () => { - this.controller.togglePause() - for(var key in this.keyTime){ - this.keys[key] = null - this.keyTime[key] = -Infinity - } - }) - var moveMenuMinus = () => { - moveMenu = -1 - } - var moveMenuPlus = () => { - moveMenu = 1 - } - var moveMenuConfirm = () => { - if(this.game.isPaused()){ - this.locked = true - setTimeout(() => { - this.controller.view.pauseConfirm() - this.locked = false - }, 200) - } - } - this.checkKey(this.kbd["previous"], "menu", moveMenuMinus) - this.checkKey(this.kbd["ka_l"], "menu", moveMenuMinus) - this.checkKey(this.kbd["next"], "menu", moveMenuPlus) - this.checkKey(this.kbd["ka_r"], "menu", moveMenuPlus) - this.checkKey(this.kbd["confirm"], "menu", moveMenuConfirm) - this.checkKey(this.kbd["don_l"], "menu", moveMenuConfirm) - this.checkKey(this.kbd["don_r"], "menu", moveMenuConfirm) - if(moveMenu && this.game.isPaused()){ - assets.sounds["se_ka"].play() - this.controller.view.pauseMove(moveMenu) - } - } - if(this.controller.multiplayer !== 2){ - this.checkKey(this.kbd["back"], "menu", () => { - if(this.controller.multiplayer === 1 && p2.otherConnected){ - p2.send("gameend") - pageEvents.send("p2-abandoned") - } - this.controller.togglePause() - this.controller.songSelection() - }) - } - } - checkKey(keyCode, type, callback){ - if(this.keys[keyCode] && !this.isWaiting(keyCode, type)){ - this.waitForKeyup(keyCode, type) - callback() - } - } - checkKeySound(keyCode, sound){ - this.checkKey(keyCode, "sound", () => { - var circles = this.controller.getCircles() - var circle = circles[this.controller.getCurrentCircle()] - var currentTime = this.keyTime[keyCode] - this.keyTime[sound] = currentTime - if(circle && !circle.isPlayed){ - if(circle.type === "balloon"){ - if(sound === "don" && circle.requiredHits - circle.timesHit <= 1){ - this.controller.playSound("se_balloon") - return + if(key in this.substitute){ + key = this.substitute[key] + } + if(!(key in this.kbd)){ + if(key === "wildcard"){ + this.wildcard = true + } + this.kbd[key] = name } } } - this.controller.playSound("neiro_1_" + sound) - }) + } } - getKeys(){ - return this.keys - } - setKey(keyCode, down, ms){ - if(down){ - this.keys[keyCode] = true - if(this.game.isPaused()){ - return + keyEvent(event){ + var key = event.key.toLowerCase() + if(key === "escape" || key === "backspace" || key === "tab"){ + event.preventDefault() + } + if(!event.repeat){ + var pressed = event.type === "keydown" + if(pressed){ + this.btn[key] = true + }else{ + delete this.btn[key] + if(key in this.kbd){ + for(var i in this.btn){ + if(this.kbd[i] === this.kbd[key]){ + return + } + } + } } - this.keyTime[keyCode] = ms - if(keyCode == this.kbd.don_l || keyCode == this.kbd.don_r){ - this.checkKeySound(keyCode, "don") - }else if(keyCode == this.kbd.ka_l || keyCode == this.kbd.ka_r){ - this.checkKeySound(keyCode, "ka") + if(key in this.kbd){ + this.callback(pressed, this.kbd[key], event) + }else if(this.wildcard){ + this.callback(pressed, this.kbd["wildcard"], event) } - }else{ - this.keys[keyCode] = false - this.waitKeyupScore[keyCode] = false - this.waitKeyupSound[keyCode] = false - this.waitKeyupMenu[keyCode] = false } } - isWaiting(keyCode, type){ - if(type === "score"){ - return this.waitKeyupScore[keyCode] - }else if(type === "sound"){ - return this.waitKeyupSound[keyCode] - }else if(type === "menu"){ - return this.waitKeyupMenu[keyCode] + blurEvent(){ + for(var key in this.btn){ + if(this.btn[key]){ + delete this.btn[key] + var name = this.kbd[key] || (this.wildcard ? "wildcard" : false) + if(name){ + this.callback(false, name) + } + } } } - waitForKeyup(keyCode, type){ - if(!this.keys[keyCode]){ - return - } - if(type === "score"){ - this.waitKeyupScore[keyCode] = true - }else if(type === "sound"){ - this.waitKeyupSound[keyCode] = true - }else if(type === "menu"){ - this.waitKeyupMenu[keyCode] = true - } - } - getKeyTime(){ - return this.keyTime - } clean(){ pageEvents.keyRemove(this, "all") - clearInterval(this.gamepadInterval) - if(this.controller.multiplayer === 1){ - pageEvents.remove(window, "beforeunload") - } + pageEvents.blurRemove(this) + delete this.bindings + delete this.callback + delete this.kbd + delete this.btn } } diff --git a/public/src/js/loader.js b/public/src/js/loader.js index 47d1ecb..a3ba450 100644 --- a/public/src/js/loader.js +++ b/public/src/js/loader.js @@ -38,7 +38,15 @@ class Loader{ 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 assets.css.forEach(name => { var stylesheet = document.createElement("link") @@ -195,6 +203,9 @@ class Loader{ p2.hash("") } + settings = new Settings() + pageEvents.setKbd() + Promise.all(this.promises).then(() => { this.canvasTest.drawAllImages().then(result => { perf.allImg = result @@ -212,7 +223,7 @@ class Loader{ } addPromise(promise){ this.promises.push(promise) - promise.then(this.assetLoaded.bind(this)) + promise.then(this.assetLoaded.bind(this), this.errorMsg.bind(this)) } loadSound(name, gain){ var id = this.getFilename(name) @@ -258,7 +269,9 @@ class Loader{ } clean(){ var fontDetectDiv = document.getElementById("fontdetectHelper") - fontDetectDiv.parentNode.removeChild(fontDetectDiv) + if(fontDetectDiv){ + fontDetectDiv.parentNode.removeChild(fontDetectDiv) + } delete this.loaderPercentage delete this.loaderProgress delete this.promises diff --git a/public/src/js/loadsong.js b/public/src/js/loadsong.js index 0c0b02f..d70b799 100644 --- a/public/src/js/loadsong.js +++ b/public/src/js/loadsong.js @@ -4,6 +4,15 @@ class LoadSong{ this.autoPlayEnabled = autoPlayEnabled this.multiplayer = multiplayer 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) var loadingText = document.getElementById("loading-text") @@ -57,9 +66,10 @@ class LoadSong{ } if(type === "don"){ song.donBg = null - } - if(type === "song"){ + }else if(type === "song"){ song.songBg = null + }else if(type === "stage"){ + song.songStage = null } } } @@ -71,19 +81,14 @@ class LoadSong{ continue } 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" } let promise = pageEvents.load(img) - if(imgLoad[i].type === "song"){ - promises.push(promise.then(() => { - return this.scaleImg(img, filename, prefix) - })) - }else{ - promises.push(promise.then(() => { - assets.image[prefix + filename] = img - })) - } + promises.push(promise.then(() => { + return this.scaleImg(img, filename, prefix, force) + })) if(songObj.music){ img.src = URL.createObjectURL(song.songSkin[filename + ".png"]) }else{ @@ -126,6 +131,16 @@ class LoadSong{ 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(() => { this.setupMultiplayer() }, error => { @@ -151,23 +166,23 @@ class LoadSong{ 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 letter = 0; letter < 2; letter++){ - let filenameAb = filenames[i] + (letter === 0 ? "a" : "b") + var filename = filenames[i] + 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)){ let img = document.createElement("img") - if(filenameAb.startsWith("bg_song_")){ - if(this.touchEnabled){ - 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 - })) + let force = filenameAb.startsWith("bg_song_") && this.touchEnabled + if(this.imgScale !== 1 || force){ + img.crossOrigin = "Anonymous" } + promises.push(pageEvents.load(img).then(() => { + return this.scaleImg(img, filenameAb, "", force) + })) img.src = gameConfig.assets_baseurl + "img/" + filenameAb + ".png" } } @@ -175,12 +190,16 @@ class LoadSong{ Promise.all(promises).then(resolve, reject) }) } - scaleImg(img, filename, prefix){ + scaleImg(img, filename, prefix, force){ 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 w = Math.floor(img.width / 2) - var h = Math.floor(img.height / 2) + var w = Math.floor(img.width * scale) + var h = Math.floor(img.height * scale) canvas.width = w canvas.height = h var ctx = canvas.getContext("2d") diff --git a/public/src/js/main.js b/public/src/js/main.js index 1e632ca..e9067f1 100644 --- a/public/src/js/main.js +++ b/public/src/js/main.js @@ -82,6 +82,7 @@ var perf = { } var strings var vectors +var settings pageEvents.add(root, ["touchstart", "touchmove", "touchend"], event => { 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 versionLink = document.getElementById("version-link") +versionLink.tabIndex = -1 pageEvents.add(versionDiv, ["click", "touchend"], event => { if(event.target === versionDiv){ versionLink.click() diff --git a/public/src/js/mekadon.js b/public/src/js/mekadon.js index eff1537..a58247e 100644 --- a/public/src/js/mekadon.js +++ b/public/src/js/mekadon.js @@ -50,7 +50,6 @@ class Mekadon{ } } playNow(circle, score, dai, reverse){ - var kbd = this.controller.getBindings() var type = circle.type var keyDai = false var playDai = !dai || dai === 2 @@ -70,20 +69,20 @@ class Mekadon{ } } if(type === "daiDon" && playDai){ - this.setKey(kbd["don_l"], ms) - this.setKey(kbd["don_r"], ms) + this.setKey("don_l", ms) + this.setKey("don_r", ms) this.lr = false keyDai = true }else if(type === "don" || type === "daiDon" || drumrollNotes && score !== 2){ - this.setKey(this.lr ? kbd["don_l"] : kbd["don_r"], ms) + this.setKey(this.lr ? "don_l" : "don_r", ms) this.lr = !this.lr }else if(type === "daiKa" && playDai){ - this.setKey(kbd["ka_l"], ms) - this.setKey(kbd["ka_r"], ms) + this.setKey("ka_l", ms) + this.setKey("ka_r", ms) this.lr = false keyDai = true }else if(type === "ka" || type === "daiKa" || drumrollNotes){ - this.setKey(this.lr ? kbd["ka_l"] : kbd["ka_r"], ms) + this.setKey(this.lr ? "ka_l" : "ka_r", ms) this.lr = !this.lr } if(type === "balloon"){ @@ -110,8 +109,7 @@ class Mekadon{ getMS(){ return this.controller.getElapsedTime() } - setKey(keyCode, ms){ - this.controller.setKey(keyCode, false) - this.controller.setKey(keyCode, true, ms) + setKey(name, ms){ + this.controller.setKey(true, name, ms) } } diff --git a/public/src/js/pageevents.js b/public/src/js/pageevents.js index 7ba8d68..c8c0af7 100644 --- a/public/src/js/pageevents.js +++ b/public/src/js/pageevents.js @@ -3,10 +3,13 @@ class PageEvents{ this.allEvents = new Map() this.keyListeners = new Map() this.mouseListeners = new Map() + this.blurListeners = new Map() this.lastKeyEvent = -Infinity this.add(window, "keydown", this.keyEvent.bind(this)) this.add(window, "keyup", this.keyEvent.bind(this)) this.add(window, "mousemove", this.mouseEvent.bind(this)) + this.add(window, "blur", this.blurEvent.bind(this)) + this.kbd = [] } add(target, type, callback){ if(Array.isArray(type)){ @@ -81,8 +84,9 @@ class PageEvents{ }) } keyEvent(event){ - if ([68, 70, 74, 75].indexOf(event.keyCode) > -1) { // D, F, J, K + if(this.kbd.indexOf(event.key.toLowerCase()) !== -1){ this.lastKeyEvent = Date.now() + event.preventDefault() } this.keyListeners.forEach(addedKeyCode => { this.checkListener(addedKeyCode.get("all"), event) @@ -140,10 +144,29 @@ class PageEvents{ mouseRemove(target){ this.mouseListeners.delete(target) } + blurEvent(event){ + this.blurListeners.forEach(callback => callback(event)) + } + blurAdd(target, callback){ + this.blurListeners.set(target, callback) + } + blurRemove(target){ + this.blurListeners.delete(target) + } getMouse(){ return this.lastMouse } send(name, detail){ dispatchEvent(new CustomEvent(name, {detail: detail})) } + setKbd(){ + this.kbd = [] + var kbdSettings = settings.getItem("keyboardSettings") + for(var name in kbdSettings){ + var keys = kbdSettings[name] + for(var i in keys){ + this.kbd.push(keys[i]) + } + } + } } diff --git a/public/src/js/scoresheet.js b/public/src/js/scoresheet.js index 037e7e4..24fde5d 100644 --- a/public/src/js/scoresheet.js +++ b/public/src/js/scoresheet.js @@ -31,9 +31,12 @@ class Scoresheet{ this.draw = new CanvasDraw() this.canvasCache = new CanvasCache() + this.keyboard = new Keyboard({ + confirm: ["enter", "space", "esc", "don_l", "don_r"] + }, this.keyDown.bind(this)) this.gamepad = new Gamepad({ - "13": ["a", "b", "start", "ls", "rs"] - }) + confirm: ["a", "b", "start", "ls", "rs"] + }, this.keyDown.bind(this)) this.difficulty = { "easy": 0, @@ -72,23 +75,8 @@ class Scoresheet{ touchEvents: controller.view.touchEvents }) } - keyDown(event, code){ - if(!code){ - if(event.repeat){ - return - } - code = event.keyCode - } - var key = { - confirm: code == 13 || code == 32 || code == 70 || code == 74, - // Enter, Space, F, J - cancel: code == 27 || code == 8 - // Esc, Backspace - } - if(key.cancel && event){ - event.preventDefault() - } - if(key.confirm || key.cancel){ + keyDown(pressed){ + if(pressed && this.redrawing){ this.toNext() } } @@ -137,7 +125,6 @@ class Scoresheet{ this.winW = null this.winH = null - pageEvents.keyAdd(this, "all", "down", this.keyDown.bind(this)) pageEvents.add(this.canvas, ["mousedown", "touchstart"], this.mouseDown.bind(this)) if(!this.multiplayer){ @@ -177,12 +164,6 @@ class Scoresheet{ } var ms = this.getMS() - this.gamepad.play((pressed, keyCode) => { - if(pressed){ - this.keyDown(false, keyCode) - } - }) - if(!this.redrawRunning){ return } @@ -193,6 +174,14 @@ class Scoresheet{ var winW = innerWidth var winH = lastHeight 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 winH *= this.pixelRatio var ratioX = winW / 1280 @@ -853,12 +842,13 @@ class Scoresheet{ } clean(){ + this.keyboard.clean() + this.gamepad.clean() this.draw.clean() this.canvasCache.clean() assets.sounds["bgm_result"].stop() snd.buffer.loadSettings() this.redrawRunning = false - pageEvents.keyRemove(this, "all") pageEvents.remove(this.canvas, ["mousedown", "touchstart"]) if(this.multiplayer !== 2 && this.touchEnabled){ pageEvents.remove(document.getElementById("touch-full-btn"), "touchend") diff --git a/public/src/js/session.js b/public/src/js/session.js index 8dfe8e6..0f08c67 100644 --- a/public/src/js/session.js +++ b/public/src/js/session.js @@ -2,13 +2,13 @@ class Session{ constructor(touchEnabled){ this.touchEnabled = touchEnabled loader.changePage("session", true) - this.endButton = document.getElementById("tutorial-end-button") + this.endButton = this.getElement("view-end-button") if(touchEnabled){ - document.getElementById("tutorial-outer").classList.add("touch-enabled") + this.getElement("view-outer").classList.add("touch-enabled") } this.sessionInvite = document.getElementById("session-invite") - var tutorialTitle = document.getElementById("tutorial-title") + var tutorialTitle = this.getElement("view-title") tutorialTitle.innerText = strings.session.multiplayerSession tutorialTitle.setAttribute("alt", strings.session.multiplayerSession) this.sessionInvite.parentNode.insertBefore(document.createTextNode(strings.session.linkTutorial), this.sessionInvite) @@ -16,11 +16,12 @@ class Session{ this.endButton.setAttribute("alt", strings.session.cancel) pageEvents.add(window, ["mousedown", "touchstart"], this.mouseDown.bind(this)) - pageEvents.keyOnce(this, 27, "down").then(this.onEnd.bind(this)) - + this.keyboard = new Keyboard({ + confirm: ["esc"] + }, this.keyPress.bind(this)) this.gamepad = new Gamepad({ - "confirm": ["start", "b", "ls", "rs"] - }, this.onEnd.bind(this)) + confirm: ["start", "b", "ls", "rs"] + }, this.keyPress.bind(this)) p2.hashLock = true pageEvents.add(p2, "message", response => { @@ -29,14 +30,20 @@ class Session{ p2.hash(response.value) }else if(response.type === "songsel"){ p2.clearMessage("users") - this.onEnd(false, true) + this.onEnd(true) pageEvents.send("session-start", "host") } }) p2.send("invite") pageEvents.send("session") } + getElement(name){ + return loader.screen.getElementsByClassName(name)[0] + } mouseDown(event){ + if(event.type === "mousedown" && event.which !== 1){ + return + } if(event.target === this.sessionInvite){ this.sessionInvite.focus() }else{ @@ -47,7 +54,12 @@ class Session{ this.onEnd() } } - onEnd(event, fromP2){ + keyPress(pressed){ + if(pressed){ + this.onEnd() + } + } + onEnd(fromP2){ if(!p2.session){ p2.send("leave") p2.hash("") @@ -56,9 +68,6 @@ class Session{ }else if(!fromP2){ return p2.send("songsel") } - if(event && event.type === "keydown"){ - event.preventDefault() - } this.clean() assets.sounds["se_don"].play() setTimeout(() => { @@ -66,9 +75,9 @@ class Session{ }, 500) } clean(){ + this.keyboard.clean() this.gamepad.clean() pageEvents.remove(window, ["mousedown", "touchstart"]) - pageEvents.keyRemove(this, 27) pageEvents.remove(p2, "message") delete this.endButton delete this.sessionInvite diff --git a/public/src/js/settings.js b/public/src/js/settings.js new file mode 100644 index 0000000..35a863e --- /dev/null +++ b/public/src/js/settings.js @@ -0,0 +1,536 @@ +class Settings{ + constructor(){ + var ios = /iPhone|iPad/.test(navigator.userAgent) + var phone = /Android|iPhone|iPad/.test(navigator.userAgent) + + this.items = { + language: { + type: "language", + options: ["ja", "en", "cn", "tw", "ko"], + default: this.getLang() + }, + resolution: { + type: "select", + options: ["high", "medium", "low", "lowest"], + default: phone ? "medium" : "high" + }, + touchAnimation: { + type: "toggle", + default: !ios, + touch: true + }, + keyboardSettings: { + type: "keyboard", + default: { + ka_l: ["d"], + don_l: ["f"], + don_r: ["j"], + ka_r: ["k"] + }, + touch: false + }, + gamepadLayout: { + type: "gamepad", + options: ["a", "b", "c"], + default: "a", + gamepad: true + } + } + + this.storage = {} + try{ + var storage = JSON.parse(localStorage.getItem("settings") || "{}") + for(var i in this.items){ + var current = this.items[i] + if(current.type === "language"){ + this.storage[i] = localStorage.getItem("lang") + if(current.options.indexOf(this.storage[i]) === -1){ + this.storage[i] = null + } + }else if(i in storage){ + if((current.type === "select" || current.type === "gamepad") && current.options.indexOf(storage[i]) === -1){ + this.storage[i] = null + }else if(current.type === "keyboard"){ + var obj = {} + for(var j in current.default){ + if(storage[i] && storage[i][j] && storage[i][j][0]){ + obj[j] = storage[i][j] + }else{ + obj = null + break + } + } + this.storage[i] = obj + }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{ + if(name === "language"){ + if(value){ + localStorage.setItem("lang", value) + }else{ + localStorage.removeItem("lang") + } + }else{ + var language = this.storage.language + delete this.storage.language + localStorage.setItem("settings", JSON.stringify(this.storage)) + this.storage.language = language + } + }catch(e){} + } + getLang(){ + if("languages" in navigator){ + var userLang = navigator.languages.slice() + userLang.unshift(navigator.language) + for(var i in userLang){ + for(var j in allStrings){ + if(allStrings[j].regex.test(userLang[i])){ + return j + } + } + } + } + return "ja" + } + setLang(lang, noEvent){ + strings = lang + var boldFonts = strings.font === "Microsoft YaHei, sans-serif" + loader.screen.style.fontFamily = strings.font + loader.screen.style.fontWeight = boldFonts ? "bold" : "" + loader.screen.classList[boldFonts ? "add" : "remove"]("bold-fonts") + if(!noEvent){ + pageEvents.send("language-change", lang.id) + } + } +} + +class SettingsView{ + constructor(touchEnabled, tutorial, songId){ + this.touchEnabled = touchEnabled + this.tutorial = tutorial + this.songId = songId + + loader.changePage("settings", tutorial) + assets.sounds["bgm_settings"].playLoop(0.1, false, 0, 1.392, 26.992) + this.defaultButton = document.getElementById("settings-default") + if(touchEnabled){ + this.getElement("view-outer").classList.add("touch-enabled") + } + var gamepadEnabled = false + if("getGamepads" in navigator){ + var gamepads = navigator.getGamepads() + for(var i = 0; i < gamepads.length; i++){ + if(gamepads[i]){ + gamepadEnabled = true + break + } + } + } + this.mode = "settings" + + this.keyboard = new Keyboard({ + "confirm": ["enter", "space", "don_l", "don_r"], + "up": ["up"], + "previous": ["left", "ka_l"], + "next": ["right", "down", "ka_r"], + "back": ["esc"], + "other": ["wildcard"] + }, this.keyPressed.bind(this)) + this.gamepad = new Gamepad({ + "confirm": ["b", "ls", "rs"], + "up": ["u", "lsu"], + "previous": ["l", "lb", "lt", "lsl"], + "next": ["d", "r", "rb", "rt", "lsd", "lsr"], + "back": ["start", "a"] + }, this.keyPressed.bind(this)) + + this.viewTitle = this.getElement("view-title") + this.endButton = this.getElement("view-end-button") + this.resolution = settings.getItem("resolution") + + var content = this.getElement("view-content") + this.items = [] + this.selected = tutorial ? 1 : 0 + for(let i in settings.items){ + var current = settings.items[i] + if( + !touchEnabled && current.touch === true || + touchEnabled && current.touch === false || + !gamepadEnabled && current.gamepad === true || + tutorial && current.type !== "language" + ){ + 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") + this.getValue(i, valueDiv) + settingBox.appendChild(valueDiv) + content.appendChild(settingBox) + if(this.items.length === this.selected){ + settingBox.classList.add("selected") + } + this.addTouch(settingBox, event => this.setValue(i)) + this.items.push({ + id: i, + settingBox: settingBox, + nameDiv: nameDiv, + valueDiv: valueDiv + }) + } + if(tutorial){ + this.defaultButton.style.display = "none" + this.endButton.classList.add("selected") + }else{ + this.items.push({ + id: "default", + settingBox: this.defaultButton + }) + this.addTouch(this.defaultButton, this.defaultSettings.bind(this)) + } + this.items.push({ + id: "back", + settingBox: this.endButton + }) + this.addTouch(this.endButton, this.onEnd.bind(this)) + + this.gamepadSettings = document.getElementById("settings-gamepad") + this.addTouch(this.gamepadSettings, event => { + if(event.target === event.currentTarget){ + this.gamepadBack() + } + }) + this.gamepadTitle = this.gamepadSettings.getElementsByClassName("view-title")[0] + this.gamepadEndButton = this.gamepadSettings.getElementsByClassName("view-end-button")[0] + this.addTouch(this.gamepadEndButton, event => this.gamepadBack(true)) + this.gamepadBox = this.gamepadSettings.getElementsByClassName("setting-box")[0] + this.addTouch(this.gamepadBox, event => this.gamepadSet(1)) + this.gamepadButtons = document.getElementById("gamepad-buttons") + this.gamepadValue = document.getElementById("gamepad-value") + + this.setStrings() + + pageEvents.send("settings") + } + getElement(name){ + return loader.screen.getElementsByClassName(name)[0] + } + addTouch(element, callback){ + pageEvents.add(element, ["mousedown", "touchstart"], event => { + if(event.type === "touchstart"){ + event.preventDefault() + this.touched = true + }else if(event.which !== 1){ + return + }else{ + this.touched = false + } + callback(event) + }) + } + removeTouch(element){ + pageEvents.remove(element, ["mousedown", "touchstart"]) + } + getValue(name, valueDiv){ + var current = settings.items[name] + var value = settings.getItem(name) + if(current.type === "language"){ + value = allStrings[value].name + " (" + value + ")" + }else if(current.type === "select" || current.type === "gamepad"){ + value = strings.settings[name][value] + }else if(current.type === "toggle"){ + value = value ? strings.settings.on : strings.settings.off + }else if(current.type === "keyboard"){ + valueDiv.innerHTML = "" + for(var i in value){ + var keyDiv = document.createElement("div") + keyDiv.style.color = i === "ka_l" || i === "ka_r" ? "#009aa5" : "#ef2c10" + var key = value[i][0] + for(var j in this.keyboard.substitute){ + if(this.keyboard.substitute[j] === key){ + key = j + break + } + } + keyDiv.innerText = key.toUpperCase() + valueDiv.appendChild(keyDiv) + } + return + } + valueDiv.innerText = value + } + setValue(name){ + var current = settings.items[name] + var value = settings.getItem(name) + var selectedIndex = this.items.findIndex(item => item.id === name) + var selected = this.items[selectedIndex] + if(this.mode !== "settings"){ + if(this.selected === selectedIndex){ + this.keyboardBack(selected) + } + return + } + if(this.selected !== selectedIndex){ + this.items[this.selected].settingBox.classList.remove("selected") + this.selected = selectedIndex + selected.settingBox.classList.add("selected") + } + if(current.type === "language" || current.type === "select"){ + value = current.options[this.mod(current.options.length, current.options.indexOf(value) + 1)] + }else if(current.type === "toggle"){ + value = !value + }else if(current.type === "keyboard"){ + this.mode = "keyboard" + selected.settingBox.style.animation = "none" + selected.valueDiv.classList.add("selected") + this.keyboardKeys = {} + this.keyboardSet() + assets.sounds["se_don"].play() + return + }else if(current.type === "gamepad"){ + this.mode = "gamepad" + this.gamepadSelected = current.options.indexOf(value) + this.gamepadSet() + assets.sounds["se_don"].play() + return + } + settings.setItem(name, value) + this.getValue(name, this.items[this.selected].valueDiv) + assets.sounds["se_ka"].play() + if(current.type === "language"){ + this.setLang(allStrings[value]) + } + } + keyPressed(pressed, name, event){ + if(!pressed){ + return + } + this.touched = false + var selected = this.items[this.selected] + if(this.mode === "settings"){ + if(name === "confirm"){ + if(selected.id === "back"){ + this.onEnd() + }else if(selected.id === "default"){ + this.defaultSettings() + }else{ + this.setValue(selected.id) + } + }else if(name === "up" || name === "previous" || name === "next"){ + selected.settingBox.classList.remove("selected") + do{ + this.selected = this.mod(this.items.length, this.selected + (name === "next" ? 1 : -1)) + }while(this.items[this.selected].id === "default" && name !== "previous") + selected = this.items[this.selected] + selected.settingBox.classList.add("selected") + selected.settingBox.scrollIntoView() + assets.sounds["se_ka"].play() + }else if(name === "back"){ + this.onEnd() + } + }else if(this.mode === "gamepad"){ + if(name === "confirm"){ + this.gamepadBack(true) + }else if(name === "up" || name === "previous" || name === "next"){ + this.gamepadSet(name === "next" ? 1 : -1) + }else if(name === "back"){ + this.gamepadBack() + } + }else if(this.mode === "keyboard"){ + if(name === "back"){ + this.keyboardBack(selected) + assets.sounds["se_cancel"].play() + }else{ + event.preventDefault() + var currentKey = event.key.toLowerCase() + for(var i in this.keyboardKeys){ + if(this.keyboardKeys[i][0] === currentKey || !currentKey){ + return + } + } + var current = this.keyboardCurrent + assets.sounds[current === "ka_l" || current === "ka_r" ? "se_ka" : "se_don"].play() + this.keyboardKeys[current] = [currentKey] + this.keyboardSet() + } + } + } + keyboardSet(){ + var selected = this.items[this.selected] + var current = settings.items[selected.id] + selected.valueDiv.innerHTML = "" + for(var i in current.default){ + var keyDiv = document.createElement("div") + keyDiv.style.color = i === "ka_l" || i === "ka_r" ? "#009aa5" : "#ef2c10" + if(this.keyboardKeys[i]){ + var key = this.keyboardKeys[i][0] + for(var j in this.keyboard.substitute){ + if(this.keyboard.substitute[j] === key){ + key = j + break + } + } + keyDiv.innerText = key.toUpperCase() + selected.valueDiv.appendChild(keyDiv) + }else{ + keyDiv.innerText = "[" + strings.settings[selected.id][i] + "]" + selected.valueDiv.appendChild(keyDiv) + this.keyboardCurrent = i + return + } + } + settings.setItem(selected.id, this.keyboardKeys) + this.keyboardBack(selected) + this.keyboard.update() + pageEvents.setKbd() + } + keyboardBack(selected){ + this.mode = "settings" + selected.settingBox.style.animation = "" + selected.valueDiv.classList.remove("selected") + this.getValue(selected.id, selected.valueDiv) + } + gamepadSet(diff){ + if(this.mode !== "gamepad"){ + return + } + var selected = this.items[this.selected] + var current = settings.items[selected.id] + if(diff){ + this.gamepadSelected = this.mod(current.options.length, this.gamepadSelected + diff) + assets.sounds["se_ka"].play() + } + var opt = current.options[this.gamepadSelected] + var value = strings.settings[selected.id][opt] + this.gamepadValue.innerText = value + this.gamepadValue.setAttribute("alt", value) + this.gamepadButtons.style.backgroundPosition = "0 " + (-318 - 132 * this.gamepadSelected) + "px" + this.gamepadSettings.style.display = "block" + } + gamepadBack(confirm){ + if(this.mode !== "gamepad"){ + return + } + var selected = this.items[this.selected] + var current = settings.items[selected.id] + settings.setItem(selected.id, current.options[this.gamepadSelected]) + this.getValue(selected.id, selected.valueDiv) + assets.sounds[confirm ? "se_don" : "se_cancel"].play() + this.gamepadSettings.style.display = "" + this.mode = "settings" + } + defaultSettings(){ + if(this.mode === "keyboard"){ + this.keyboardBack(this.items[this.selected]) + } + for(var i in settings.items){ + settings.setItem(i, null) + } + this.setLang(allStrings[settings.getItem("language")]) + this.keyboard.update() + pageEvents.setKbd() + assets.sounds["se_don"].play() + } + onEnd(){ + this.clean() + assets.sounds["se_don"].play() + setTimeout(() => { + if(this.tutorial && !this.touched){ + new Tutorial(false, this.songId) + }else{ + try{ + localStorage.setItem("tutorial", "true") + }catch(e){} + new SongSelect(this.tutorial ? false : "settings", false, this.touched, this.songId) + } + }, 500) + } + setLang(lang){ + settings.setLang(lang) + if(failedTests.length !== 0){ + showUnsupported(strings) + } + for(var i in this.items){ + var item = this.items[i] + if(item.valueDiv){ + var name = strings.settings[item.id].name + item.nameDiv.innerText = name + item.nameDiv.setAttribute("alt", name) + this.getValue(item.id, item.valueDiv) + } + } + this.setStrings() + } + setStrings(){ + this.viewTitle.innerText = strings.gameSettings + this.viewTitle.setAttribute("alt", strings.gameSettings) + this.endButton.innerText = strings.settings.ok + this.endButton.setAttribute("alt", strings.settings.ok) + this.gamepadTitle.innerText = strings.settings.gamepadLayout.name + this.gamepadTitle.setAttribute("alt", strings.settings.gamepadLayout.name) + this.gamepadEndButton.innerText = strings.settings.ok + this.gamepadEndButton.setAttribute("alt", strings.settings.ok) + if(!this.tutorial){ + this.defaultButton.innerText = strings.settings.default + this.defaultButton.setAttribute("alt", strings.settings.default) + } + } + mod(length, index){ + return ((index % length) + length) % length + } + clean(){ + this.keyboard.clean() + this.gamepad.clean() + assets.sounds["bgm_settings"].stop() + for(var i in this.items){ + this.removeTouch(this.items[i].settingBox) + } + if(this.defaultButton){ + delete this.defaultButton + } + this.removeTouch(this.gamepadSettings) + this.removeTouch(this.gamepadEndButton) + this.removeTouch(this.gamepadBox) + delete this.tutorialTitle + delete this.endButton + delete this.items + delete this.gamepadSettings + delete this.gamepadTitle + delete this.gamepadEndButton + delete this.gamepadBox + delete this.gamepadButtons + delete this.gamepadValue + if(this.resolution !== settings.getItem("resolution")){ + for(var i in assets.image){ + if(i === "touch_drum" || i.startsWith("bg_song_") || i.startsWith("bg_stage_") || i.startsWith("bg_don_")){ + URL.revokeObjectURL(assets.image[i].src) + delete assets.image[i] + } + } + } + } +} diff --git a/public/src/js/songselect.js b/public/src/js/songselect.js index ed8d08b..f50dbc1 100644 --- a/public/src/js/songselect.js +++ b/public/src/js/songselect.js @@ -25,21 +25,27 @@ class SongSelect{ }, "tutorial": { sort: 7, - background: "#9afbe1", - border: ["#d6ffff", "#6bae9c"], - outline: "#31ae94" + background: "#29e8aa", + border: ["#86ffbd", "#009a8c"], + outline: "#08a28c" }, "about": { sort: 7, - background: "#91cfff", - border: ["#dff0ff", "#6890b2"], - outline: "#217abb" + background: "#a2d0e7", + border: ["#c6dfff", "#4485d9"], + outline: "#2390d9" + }, + "settings": { + sort: 7, + background: "#ce93fa", + border: ["#dec4fd", "#a543ef"], + outline: "#a741ef" }, "browse": { sort: 7, - background: "#9791ff", - border: ["#e2dfff", "#6d68b2"], - outline: "#5350ba" + background: "#fab5d3", + border: ["#ffe7ef", "#d36aa2"], + outline: "#d36aa2" }, "J-POP": { sort: 0, @@ -149,6 +155,12 @@ class SongSelect{ action: "about", 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))){ this.browse = document.getElementById("browse") pageEvents.add(this.browse, "change", this.browseChange.bind(this)) @@ -277,23 +289,33 @@ class SongSelect{ this.startPreview(true) this.pressedKeys = {} + this.keyboard = new Keyboard({ + confirm: ["enter", "space", "don_l", "don_r"], + back: ["escape"], + left: ["left", "ka_l"], + right: ["right", "ka_r"], + up: ["up"], + down: ["down"], + session: ["backspace"], + ctrl: ["ctrl"], + shift: ["shift"] + }, this.keyPress.bind(this)) this.gamepad = new Gamepad({ - "13": ["b", "start", "ls", "rs"], - "27": ["a"], - "37": ["l", "lb", "lt", "lsl"], - "39": ["r", "rb", "rt", "lsr"], - "38": ["u", "lsu"], - "40": ["d", "lsd"], - "8": ["back"], - "ctrl": ["y"], - "shift": ["x"] - }) + confirm: ["b", "start", "ls", "rs"], + back: ["a"], + left: ["l", "lb", "lt", "lsl"], + right: ["r", "rb", "rt", "lsr"], + up: ["u", "lsu"], + down: ["d", "lsd"], + session: ["back"], + ctrl: ["y"], + shift: ["x"] + }, this.keyPress.bind(this)) if(!assets.customSongs){ this.startP2() } - pageEvents.keyAdd(this, "all", "down", this.keyDown.bind(this)) pageEvents.add(loader.screen, "mousemove", this.mouseMove.bind(this)) pageEvents.add(loader.screen, "mouseleave", () => { this.state.moveHover = null @@ -319,71 +341,47 @@ class SongSelect{ } } - keyDown(event, code){ - if(code){ - var modifiers = { - shift: this.pressedKeys["shift"], - ctrl: this.pressedKeys["ctrl"] + keyPress(pressed, name, event){ + if(pressed){ + if(!this.pressedKeys[name]){ + this.pressedKeys[name] = this.getMS() + 300 } }else{ - code = event.keyCode - var modifiers = { - shift: event.shiftKey, - ctrl: event.ctrlKey - } - } - if(code === "ctrl" || code === "shift" || !this.redrawRunning){ + this.pressedKeys[name] = 0 return } - - var key = { - confirm: code == 13 || code == 32 || code == 70 || code == 74, - // Enter, Space, F, J - cancel: code == 27, - // Esc - session: code == 8, - // Backspace - left: code == 37 || code == 68, - // Left, D - right: code == 39 || code == 75, - // Right, K - up: code == 38, - // Up - down: code == 40 - // Down - } - if(event && (code == 27 || code == 8)){ - event.preventDefault() + if(name === "ctrl" || name === "shift" || !this.redrawRunning){ + return } if(this.state.screen === "song"){ - if(key.confirm){ + if(name === "confirm"){ this.toSelectDifficulty() - }else if(key.cancel){ + }else if(name === "back"){ this.toTitleScreen() - }else if(key.session){ + }else if(name === "session"){ this.toSession() - }else if(key.left){ + }else if(name === "left"){ this.moveToSong(-1) - }else if(key.right){ + }else if(name === "right"){ this.moveToSong(1) } }else if(this.state.screen === "difficulty"){ - if(key.confirm){ + if(name === "confirm"){ if(this.selectedDiff === 0){ this.toSongSelect() }else if(this.selectedDiff === 1){ this.toOptions(1) }else{ - this.toLoadSong(this.selectedDiff - this.diffOptions.length, modifiers.shift, modifiers.ctrl) + this.toLoadSong(this.selectedDiff - this.diffOptions.length, this.pressedKeys["shift"], this.pressedKeys["ctrl"]) } - }else if(key.cancel || key.session){ + }else if(name === "back" || name === "session"){ this.toSongSelect() - }else if(key.left){ + }else if(name === "left"){ this.moveToDiff(-1) - }else if(key.right){ + }else if(name === "right"){ this.moveToDiff(1) - }else if(this.selectedDiff === 1 && (key.up || key.down)){ - this.toOptions(key.up ? -1 : 1) + }else if(this.selectedDiff === 1 && (name === "up" || name === "down")){ + this.toOptions(name === "up" ? -1 : 1) } } } @@ -523,7 +521,7 @@ class SongSelect{ }else if(550 < x && x < 1050 && 95 < y && y < 524){ var moveBy = Math.floor((x - 550) / ((1050 - 550) / 5)) + this.diffOptions.length 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 } } @@ -624,6 +622,8 @@ class SongSelect{ this.toTutorial() }else if(currentSong.action === "about"){ this.toAbout() + }else if(currentSong.action === "settings"){ + this.toSettings() }else if(currentSong.action === "browse"){ this.toBrowse() } @@ -722,6 +722,13 @@ class SongSelect{ new About(this.touchEnabled) }, 500) } + toSettings(){ + assets.sounds["se_don"].play() + this.clean() + setTimeout(() => { + new SettingsView(this.touchEnabled) + }, 500) + } toSession(){ if(p2.socket.readyState !== 1 || assets.customSongs){ return @@ -760,20 +767,10 @@ class SongSelect{ requestAnimationFrame(this.redrawBind) var ms = this.getMS() - this.gamepad.play((pressed, keyCode) => { - if(pressed){ - if(!this.pressedKeys[keyCode]){ - this.pressedKeys[keyCode] = ms + 300 - this.keyDown(false, keyCode) - } - }else{ - this.pressedKeys[keyCode] = 0 - } - }) for(var key in this.pressedKeys){ if(this.pressedKeys[key]){ if(ms >= this.pressedKeys[key] + 50){ - this.keyDown(false, key) + this.keyPress(true, key) this.pressedKeys[key] = ms } } @@ -790,6 +787,14 @@ class SongSelect{ winW = winH / 9 * 32 } 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 winH *= this.pixelRatio var ratioX = winW / 1280 @@ -1928,6 +1933,8 @@ class SongSelect{ } clean(){ + this.keyboard.clean() + this.gamepad.clean() this.clearHash() this.draw.clean() this.songTitleCache.clean() @@ -1950,7 +1957,6 @@ class SongSelect{ song.preview_sound.clean() } }) - pageEvents.keyRemove(this, "all") pageEvents.remove(loader.screen, ["mousemove", "mouseleave", "mousedown", "touchstart"]) pageEvents.remove(this.canvas, "touchend") pageEvents.remove(p2, "message") diff --git a/public/src/js/soundbuffer.js b/public/src/js/soundbuffer.js index cd179f5..3e6f45e 100644 --- a/public/src/js/soundbuffer.js +++ b/public/src/js/soundbuffer.js @@ -101,7 +101,7 @@ class SoundGain{ this.volume = amount } setVolumeMul(amount){ - this.setVolume(Math.sqrt(amount * amount * this.defaultVol)) + this.setVolume(amount * this.defaultVol) } setCrossfade(amount){ this.setVolume(Math.sqrt(Math.sin(Math.PI / 2 * amount))) diff --git a/public/src/js/strings.js b/public/src/js/strings.js index 16ddaa8..eb77015 100644 --- a/public/src/js/strings.js +++ b/public/src/js/strings.js @@ -24,6 +24,7 @@ this.randomSong = "ランダムに曲をえらぶ" this.howToPlay = "あそびかた説明" this.aboutSimulator = "このシミュレータについて" + this.gameSettings = "ゲーム設定" this.browse = "参照する…" this.defaultSongList = "デフォルト曲リスト" this.songOptions = "演奏オプション" @@ -98,6 +99,38 @@ linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.", cancel: "キャンセル" } + this.settings = { + language: { + name: "言語" + }, + resolution: { + name: "ゲームの解像度", + high: "高", + medium: "中", + low: "低", + lowest: "最低" + }, + touchAnimation: { + name: "タッチアニメーション" + }, + keyboardSettings: { + name: "キーボード設定", + ka_l: "ふち(左)", + don_l: "面(左)", + don_r: "面(右)", + ka_r: "ふち(右)" + }, + gamepadLayout: { + name: "そうさタイプ設定", + a: "タイプA", + b: "タイプB", + c: "タイプC" + }, + on: "オン", + off: "オフ", + default: "既定値にリセット", + ok: "OK" + } this.browserSupport = { browserWarning: "サポートされていないブラウザを実行しています (%s)", details: "詳しく", @@ -131,6 +164,7 @@ function StringsEn(){ this.randomSong = "Random Song" this.howToPlay = "How to Play" this.aboutSimulator = "About Simulator" + this.gameSettings = "Game Settings" this.browse = "Browse…" this.defaultSongList = "Default Song List" this.songOptions = "Song Options" @@ -205,6 +239,38 @@ function StringsEn(){ linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.", cancel: "Cancel" } + this.settings = { + language: { + name: "Language" + }, + resolution: { + name: "Game Resolution", + high: "High", + medium: "Medium", + low: "Low", + lowest: "Lowest" + }, + touchAnimation: { + name: "Touch Animation" + }, + keyboardSettings: { + name: "Keyboard Settings", + ka_l: "Left Rim", + don_l: "Left Surface", + don_r: "Right Surface", + ka_r: "Right Rim" + }, + gamepadLayout: { + name: "Gamepad Layout", + a: "Type A", + b: "Type B", + c: "Type C" + }, + on: "On", + off: "Off", + default: "Reset to Defaults", + ok: "OK" + } this.browserSupport = { browserWarning: "You are running an unsupported browser (%s)", details: "Details...", @@ -238,6 +304,7 @@ function StringsCn(){ this.randomSong = "随机选曲" this.howToPlay = "操作说明" this.aboutSimulator = "关于模拟器" + this.gameSettings = "游戏设定" this.browse = "浏览…" this.defaultSongList = "默认歌曲列表" this.songOptions = "选项" @@ -312,6 +379,38 @@ function StringsCn(){ linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.", cancel: "取消" } + this.settings = { + language: { + name: "语言" + }, + resolution: { + name: "游戏分辨率", + high: "高", + medium: "中", + low: "低", + lowest: "最低" + }, + touchAnimation: { + name: "触摸动画" + }, + keyboardSettings: { + name: "键盘设置", + ka_l: "边缘(左)", + don_l: "表面(左)", + don_r: "表面(右)", + ka_r: "边缘(右)" + }, + gamepadLayout: { + name: "操作类型设定", + a: "类型A", + b: "类型B", + c: "类型C" + }, + on: "开", + off: "关", + default: "重置为默认值", + ok: "确定" + } this.browserSupport = { browserWarning: "You are running an unsupported browser (%s)", details: "Details...", @@ -345,6 +444,7 @@ function StringsTw(){ this.randomSong = "隨機選曲" this.howToPlay = "操作說明" this.aboutSimulator = "關於模擬器" + this.gameSettings = "遊戲設定" this.browse = "開啟檔案…" this.defaultSongList = "默認歌曲列表" this.songOptions = "選項" @@ -419,6 +519,38 @@ function StringsTw(){ linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.", cancel: "取消" } + this.settings = { + language: { + name: "語系" + }, + resolution: { + name: "遊戲分辨率", + high: "高", + medium: "中", + low: "低", + lowest: "最低" + }, + touchAnimation: { + name: "觸摸動畫" + }, + keyboardSettings: { + name: "鍵盤設置", + ka_l: "邊緣(左)", + don_l: "表面(左)", + don_r: "表面(右)", + ka_r: "邊緣(右)" + }, + gamepadLayout: { + name: "操作類型設定", + a: "類型A", + b: "類型B", + c: "類型C" + }, + on: "開", + off: "關", + default: "重置為默認值", + ok: "確定" + } this.browserSupport = { browserWarning: "You are running an unsupported browser (%s)", details: "Details...", @@ -452,6 +584,7 @@ function StringsKo(){ this.randomSong = "랜덤" this.howToPlay = "지도 시간" this.aboutSimulator = "게임 정보" + this.gameSettings = "게임 설정" this.browse = "찾아보기…" this.defaultSongList = "기본 노래 목록" this.songOptions = "옵션" @@ -526,6 +659,38 @@ function StringsKo(){ linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.", cancel: "취소" } + this.settings = { + language: { + name: "언어" + }, + resolution: { + name: "게임 해상도", + high: "높은", + medium: "중간", + low: "저", + lowest: "최저" + }, + touchAnimation: { + name: "터치 애니메이션" + }, + keyboardSettings: { + name: "키보드 설정", + ka_l: "가장자리 (왼쪽)", + don_l: "표면 (왼쪽)", + don_r: "표면 (오른쪽)", + ka_r: "가장자리 (오른쪽)" + }, + gamepadLayout: { + name: "조작 타입 설정", + a: "타입 A", + b: "타입 B", + c: "타입 C" + }, + on: "온", + off: "오프", + default: "기본값으로 재설정", + ok: "확인" + } this.browserSupport = { browserWarning: "You are running an unsupported browser (%s)", details: "Details...", diff --git a/public/src/js/titlescreen.js b/public/src/js/titlescreen.js index 9f95cdc..6068a93 100644 --- a/public/src/js/titlescreen.js +++ b/public/src/js/titlescreen.js @@ -7,37 +7,36 @@ class Titlescreen{ this.titleScreen = document.getElementById("title-screen") this.proceed = document.getElementById("title-proceed") - this.langDropdown = document.getElementById("lang-dropdown") - this.langId = document.getElementById("lang-id") this.disclaimerText = document.getElementById("title-disclaimer-text") this.disclaimerCopyright = document.getElementById("title-disclaimer-copyright") - document.getElementById("globe-path").setAttribute("d", vectors.globe) this.logo = new Logo() } - this.lang = this.getLang() - this.setLang(allStrings[this.lang], true) + this.setLang(allStrings[settings.getItem("language")]) if(songId){ if(localStorage.getItem("tutorial") === "true"){ new SongSelect(false, false, this.touched, this.songId) }else{ - new Tutorial(false, this.songId) + new SettingsView(false, true, this.songId) } }else{ - this.addLangs() - - pageEvents.keyAdd(this, "all", "down", this.keyDown.bind(this)) - pageEvents.add(this.titleScreen, ["mousedown", "touchstart"], this.onPressed.bind(this)) - pageEvents.add(this.langDropdown, "change", this.langChange.bind(this)) + pageEvents.add(this.titleScreen, ["mousedown", "touchstart"], event => { + if(event.type === "touchstart"){ + event.preventDefault() + this.touched = true + }else if(event.type === "mousedown" && event.which !== 1){ + return + } + this.onPressed(true) + }) assets.sounds["v_title"].play() + this.keyboard = new Keyboard({ + confirm: ["enter", "space", "don_l", "don_r"] + }, this.onPressed.bind(this)) this.gamepad = new Gamepad({ - "13": ["a", "b", "x", "y", "start", "ls", "rs"] - }, pressed => { - if(pressed){ - this.onPressed() - } - }) + confirm: ["a", "b", "x", "y", "start", "ls", "rs"] + }, this.onPressed.bind(this)) if(p2.session){ pageEvents.add(p2, "message", response => { if(response.type === "songsel"){ @@ -49,36 +48,18 @@ class Titlescreen{ } } - keyDown(event, code){ - if(event && event.target === this.langDropdown){ - return + onPressed(pressed, name){ + if(pressed){ + this.titleScreen.style.cursor = "auto" + this.clean() + assets.sounds["se_don"].play() + this.goNext() } - if(!code){ - code = event.keyCode - } - if(code == 13 || code == 32 || code == 70 || code == 74){ - // Enter, Space, F, J - this.onPressed() - } - } - onPressed(event){ - if(event){ - if(event.type === "touchstart"){ - event.preventDefault() - this.touched = true - }else if(event.type === "mousedown" && event.which !== 1){ - return - } - } - this.titleScreen.style.cursor = "auto" - this.clean() - assets.sounds["se_don"].play() - this.goNext() } goNext(fromP2){ if(p2.session && !fromP2){ p2.send("songsel") - }else if(fromP2 || this.touched || localStorage.getItem("tutorial") === "true"){ + }else if(fromP2 || localStorage.getItem("tutorial") === "true"){ if(this.touched){ localStorage.setItem("tutorial", "true") } @@ -88,44 +69,17 @@ class Titlescreen{ }, 500) }else{ setTimeout(() => { - new Tutorial(false, this.songId) + new SettingsView(this.touched, true, this.songId) }, 500) } } - - getLang(){ - if(localStorage.lang && localStorage.lang in allStrings){ - return localStorage.lang - } - if("languages" in navigator){ - var userLang = navigator.languages.slice() - userLang.unshift(navigator.language) - for(var i in userLang){ - for(var j in allStrings){ - if(allStrings[j].regex.test(userLang[i])){ - return j - } - } - } - } - return "ja" - } - setLang(lang, initial){ - strings = lang - - loader.screen.style.fontFamily = strings.font - loader.screen.style.fontWeight = strings.font === "Microsoft YaHei, sans-serif" ? "bold" : "" - - if(failedTests.length !== 0){ - showUnsupported(strings) - } + setLang(lang, noEvent){ + settings.setLang(lang, true) if(this.songId){ return } this.proceed.innerText = strings.titleProceed this.proceed.setAttribute("alt", strings.titleProceed) - this.langId.innerText = strings.id.toUpperCase() - this.langId.setAttribute("alt", strings.id.toUpperCase()) this.disclaimerText.innerText = strings.titleDisclaimer this.disclaimerText.setAttribute("alt", strings.titleDisclaimer) @@ -133,38 +87,16 @@ class Titlescreen{ this.disclaimerCopyright.setAttribute("alt", strings.titleCopyright) this.logo.updateSubtitle() - if(!initial){ - pageEvents.send("language-change", lang.id) - } } - addLangs(){ - for(var i in allStrings){ - var option = document.createElement("option") - option.value = i - if(i === this.lang){ - option.selected = true - } - option.appendChild(document.createTextNode(allStrings[i].name)) - this.langDropdown.appendChild(option) - } - } - langChange(){ - this.lang = this.langDropdown.value - localStorage.lang = this.lang - this.setLang(allStrings[this.lang]) - } - clean(){ + this.keyboard.clean() this.gamepad.clean() this.logo.clean() assets.sounds["v_title"].stop() - pageEvents.keyRemove(this, "all") pageEvents.remove(this.titleScreen, ["mousedown", "touchstart"]) - pageEvents.remove(this.langDropdown, "change") delete this.titleScreen delete this.proceed delete this.titleDisclaimer delete this.titleCopyright - delete this.langDropdown } } diff --git a/public/src/js/tutorial.js b/public/src/js/tutorial.js index d2999cd..2d1302c 100644 --- a/public/src/js/tutorial.js +++ b/public/src/js/tutorial.js @@ -4,13 +4,69 @@ class Tutorial{ this.songId = songId loader.changePage("tutorial", true) assets.sounds["bgm_setsume"].playLoop(0.1, false, 0, 1.054, 16.054) - this.endButton = document.getElementById("tutorial-end-button") + this.endButton = this.getElement("view-end-button") - var tutorialTitle = document.getElementById("tutorial-title") - tutorialTitle.innerText = strings.howToPlay - tutorialTitle.setAttribute("alt", strings.howToPlay) - var tutorialContent = document.getElementById("tutorial-content") - var keys = ["F", "J", "D", "K", "Q", "SHIFT", "CTRL"] + this.tutorialTitle = this.getElement("view-title") + this.tutorialDiv = document.createElement("div") + this.getElement("view-content").appendChild(this.tutorialDiv) + this.setStrings() + + pageEvents.add(this.endButton, ["mousedown", "touchstart"], event => { + if(event.type === "touchstart"){ + event.preventDefault() + this.touched = true + }else if(event.type === "mousedown" && event.which !== 1){ + return + } + this.onEnd(true) + }) + this.keyboard = new Keyboard({ + confirm: ["enter", "space", "esc", "don_l", "don_r"] + }, this.onEnd.bind(this)) + this.gamepad = new Gamepad({ + confirm: ["start", "b", "ls", "rs"] + }, this.onEnd.bind(this)) + + pageEvents.send("tutorial") + } + getElement(name){ + return loader.screen.getElementsByClassName(name)[0] + } + insertText(text, parent){ + parent.appendChild(document.createTextNode(text)) + } + insertKey(key, parent){ + var kbd = document.createElement("kbd") + kbd.innerText = key + parent.appendChild(kbd) + } + onEnd(pressed, name){ + if(pressed){ + this.clean() + assets.sounds["se_don"].play() + try{ + localStorage.setItem("tutorial", "true") + }catch(e){} + setTimeout(() => { + new SongSelect(this.fromSongSel ? "tutorial" : false, false, this.touched, this.songId) + }, 500) + } + } + setStrings(){ + this.tutorialTitle.innerText = strings.howToPlay + this.tutorialTitle.setAttribute("alt", strings.howToPlay) + this.endButton.innerText = strings.tutorial.ok + this.endButton.setAttribute("alt", strings.tutorial.ok) + this.tutorialDiv.innerHTML = "" + var kbdSettings = settings.getItem("keyboardSettings") + var pauseKey = pageEvents.kbd.indexOf("q") === -1 ? "Q" : "ESC" + var keys = [ + kbdSettings.don_l[0].toUpperCase(), + kbdSettings.don_r[0].toUpperCase(), + kbdSettings.ka_l[0].toUpperCase(), + kbdSettings.ka_r[0].toUpperCase(), + pauseKey, "SHIFT", "CTRL" + ] var keyIndex = 0 strings.tutorial.basics.forEach(string => { var par = document.createElement("p") @@ -21,7 +77,7 @@ class Tutorial{ } this.insertText(stringKey, par) }) - tutorialContent.appendChild(par) + this.tutorialDiv.appendChild(par) }) var par = document.createElement("p") var span = document.createElement("span") @@ -38,44 +94,15 @@ class Tutorial{ this.insertText(stringKey, par) }) }) - tutorialContent.appendChild(par) - this.endButton.innerText = strings.tutorial.ok - this.endButton.setAttribute("alt", strings.tutorial.ok) - - pageEvents.once(this.endButton, ["mousedown", "touchstart"]).then(this.onEnd.bind(this)) - pageEvents.keyOnce(this, 13, "down").then(this.onEnd.bind(this)) - - this.gamepad = new Gamepad({ - "confirm": ["start", "b", "ls", "rs"] - }, this.onEnd.bind(this)) - pageEvents.send("tutorial") - } - insertText(text, parent){ - parent.appendChild(document.createTextNode(text)) - } - insertKey(key, parent){ - var kbd = document.createElement("kbd") - kbd.innerText = key - parent.appendChild(kbd) - } - onEnd(event){ - var touched = false - if(event && event.type === "touchstart"){ - event.preventDefault() - touched = true - } - this.clean() - assets.sounds["se_don"].play() - localStorage.setItem("tutorial", "true") - setTimeout(() => { - new SongSelect(this.fromSongSel ? "tutorial" : false, false, touched, this.songId) - }, 500) + this.tutorialDiv.appendChild(par) } clean(){ + this.keyboard.clean() this.gamepad.clean() - assets.sounds["bgm_setsume"].stop() pageEvents.remove(this.endButton, ["mousedown", "touchstart"]) - pageEvents.keyRemove(this, 13) + assets.sounds["bgm_setsume"].stop() + delete this.tutorialTitle delete this.endButton + delete this.tutorialDiv } } diff --git a/public/src/js/view.js b/public/src/js/view.js index b81cd29..06c30c7 100644 --- a/public/src/js/view.js +++ b/public/src/js/view.js @@ -118,6 +118,7 @@ this.touchEnabled = this.controller.touchEnabled this.touch = -Infinity + this.touchAnimation = settings.getItem("touchAnimation") if(this.multiplayer !== 2){ @@ -125,6 +126,8 @@ this.touchDrumDiv = document.getElementById("touch-drum") this.touchDrumImg = document.getElementById("touch-drum-img") + this.setBgImage(this.touchDrumImg, assets.image["touch_drum"].src) + if(this.controller.autoPlayEnabled){ this.touchDrumDiv.style.display = "none" } @@ -180,6 +183,14 @@ var touchMultiplayer = this.touchEnabled && this.multiplayer && !this.portrait 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 winH *= this.pixelRatio if(this.portrait){ @@ -819,11 +830,10 @@ ) // Taiko pressed keys - var kbd = this.controller.getBindings() var keys = ["ka_l", "ka_r", "don_l", "don_r"] for(var i = 0; i < keys.length; i++){ - var keyMS = ms - keyTime[kbd[keys[i]]] + var keyMS = ms - keyTime[keys[i]] if(keyMS < 130){ if(keyMS > 70 && !this.touchEnabled){ ctx.globalAlpha = this.draw.easeOut(1 - (keyMS - 70) / 60) @@ -1123,6 +1133,7 @@ if(!selectedSong.songSkin.stage){ 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"){ var prefix = selectedSong.songSkin.prefix || "" this.setBgImage(this.songStage, assets.image[prefix + "bg_stage_" + songSkinName].src) @@ -1180,9 +1191,10 @@ } setDonBgHeight(){ 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(()=>{ - this.gameDiv.classList.remove("fix-animations") + gameDiv.classList.remove("fix-animations") }, 50) } setLayers(elements, file, ab){ @@ -1700,14 +1712,16 @@ this.touchDrumDiv.style.width = drumWidth + "px" this.touchDrumDiv.style.height = drumHeight + "px" } - if(this.touch > ms - 100){ - if(!this.drumPadding){ - this.drumPadding = true - this.touchDrumImg.style.backgroundPositionY = "7px" + if(this.touchAnimation){ + if(this.touch > ms - 100){ + if(!this.drumPadding){ + 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 = "" } } } @@ -1754,11 +1768,10 @@ } touchNote(note){ var keyboard = this.controller.keyboard - var kbd = keyboard.getBindings() var ms = this.controller.game.getAccurateTime() this.touch = ms - keyboard.setKey(kbd[note], false) - keyboard.setKey(kbd[note], true, ms) + keyboard.setKey(false, note) + keyboard.setKey(true, note, ms) } mod(length, index){ return ((index % length) + length) % length diff --git a/public/src/views/about.html b/public/src/views/about.html index 7ec8436..3506eb3 100644 --- a/public/src/views/about.html +++ b/public/src/views/about.html @@ -1,9 +1,9 @@ -