From e81bf9b480118f7f0552a3cc6f33f9ca6e6bd1d7 Mon Sep 17 00:00:00 2001 From: LoveEevee Date: Thu, 12 Mar 2020 07:59:28 +0300 Subject: [PATCH] More bug fixes - BPM and go go time change even when there are no notes after the change, seen in Ego Ego Atakushi and UFO Swingin' - Maker URL can be added to a local tja - Example: `MAKER: Creator name ` - Long list of settings scrolls more naturally with arrow keys - When media engagement is low, gamepad users will not be able to proceed on the title screen until a mouse click or a key press unpauses the audio context - Fix "Bad" note gauge judgement on easy difficulty - In debug, use hash to tell apart songs --- public/src/js/canvasdraw.js | 2 +- public/src/js/circle.js | 2 +- public/src/js/debug.js | 4 +-- public/src/js/game.js | 36 +++++++++++++------ public/src/js/gamerules.js | 2 +- public/src/js/importsongs.js | 20 ++++++++++- public/src/js/parsetja.js | 69 ++++++++++++++++++++++++++---------- public/src/js/settings.js | 17 ++++++++- public/src/js/soundbuffer.js | 2 +- public/src/js/titlescreen.js | 5 ++- public/src/js/view.js | 15 +++++--- 11 files changed, 131 insertions(+), 43 deletions(-) diff --git a/public/src/js/canvasdraw.js b/public/src/js/canvasdraw.js index 0a4ddb4..ca984ad 100644 --- a/public/src/js/canvasdraw.js +++ b/public/src/js/canvasdraw.js @@ -1390,7 +1390,7 @@ } ctx.fill() - if(gaugeFilled < gaugeClear){ + if(!cleared){ ctx.fillStyle = config.blue ? "#184d55" : "#680000" var x = Math.max(0, gaugeFilled - 5) ctx.fillRect(x, firstTop, gaugeClear - x + 2 + (gaugeClear < gaugeW ? 0 : -7), 22) diff --git a/public/src/js/circle.js b/public/src/js/circle.js index 261629e..d971f84 100644 --- a/public/src/js/circle.js +++ b/public/src/js/circle.js @@ -18,7 +18,7 @@ class Circle{ this.timesKa = 0 this.requiredHits = config.requiredHits || 0 this.rendaPlayed = false - this.gogoTime = config.gogoTime + this.gogoTime = config.gogoTime || false this.gogoChecked = false this.beatMS = config.beatMS this.fixedPos = config.fixedPos diff --git a/public/src/js/debug.js b/public/src/js/debug.js index a940730..271d66a 100644 --- a/public/src/js/debug.js +++ b/public/src/js/debug.js @@ -132,12 +132,12 @@ class Debug{ var selectedSong = this.controller.selectedSong this.defaultOffset = selectedSong.offset || 0 - if(this.songFolder === selectedSong.folder){ + if(this.songHash === selectedSong.hash){ this.offsetChange(this.offsetSlider.get(), true) this.branchChange(null, true) this.volumeChange(this.volumeSlider.get(), true) }else{ - this.songFolder = selectedSong.folder + this.songHash = selectedSong.hash this.offsetSlider.set(this.defaultOffset) this.branchReset(null, true) this.volumeSlider.set(this.controller.volume) diff --git a/public/src/js/game.js b/public/src/js/game.js index fd1f94b..8fcdd99 100644 --- a/public/src/js/game.js +++ b/public/src/js/game.js @@ -4,7 +4,8 @@ class Game{ this.selectedSong = selectedSong this.songData = songData this.elapsedTime = 0 - this.currentCircle = 0 + this.currentCircle = -1 + this.updateCurrentCircle() this.combo = 0 this.rules = new GameRules(this) this.globalScore = { @@ -46,7 +47,13 @@ class Game{ } initTiming(){ // Date when the chrono is started (before the game begins) - var firstCircle = this.songData.circles[0] + var firstCircle + for(var i = 0; i < this.songData.circles.length; i++){ + firstCircle = this.songData.circles[i] + if(firstCircle.type !== "event"){ + break + } + } if(this.controller.calibrationMode){ var offsetTime = 0 }else{ @@ -98,12 +105,6 @@ class Game{ this.controller.playSound("v_renda") } } - if(!circle.beatMSCopied){ - if(this.view.beatInterval !== circle.beatMS){ - this.view.changeBeatInterval(circle.beatMS) - } - circle.beatMSCopied = true - } } if(circle.daiFailed && (ms >= circle.daiFailed.ms + this.rules.daiLeniency || ms > endTime)){ this.checkScore(circle, circle.daiFailed.check) @@ -237,6 +238,9 @@ class Game{ } } skipNote(circle){ + if(circle.type === "event"){ + return + } if(circle.section){ this.resetSection() } @@ -254,6 +258,9 @@ class Game{ checkPlays(){ var circles = this.songData.circles var circle = circles[this.currentCircle] + if(circle && circle.type === "event"){ + this.updateCurrentCircle() + } if(this.controller.autoPlayEnabled){ while(circle && this.controller.autoPlay(circle)){ @@ -460,16 +467,23 @@ class Game{ this.globalScore.points += score * (dai ? 2 : 1) this.view.setDarkBg(false) } + getLastCircle(circles){ + for(var i = circles.length; i--;){ + if(circles[i].type !== "event"){ + return circles[i] + } + } + } whenLastCirclePlayed(){ var ms = this.elapsedTime if(!this.lastCircle){ var circles = this.songData.circles - var circle = circles[circles.length - 1] + var circle = this.getLastCircle(circles) this.lastCircle = circle ? circle.endTime : 0 if(this.controller.multiplayer){ var syncWith = this.controller.syncWith var syncCircles = syncWith.game.songData.circles - circle = syncCircles[syncCircles.length - 1] + circle = this.getLastCircle(syncCircles) var syncLastCircle = circle ? circle.endTime : 0 if(syncLastCircle > this.lastCircle){ this.lastCircle = syncLastCircle @@ -607,7 +621,7 @@ class Game{ var circles = this.songData.circles do{ var circle = circles[++this.currentCircle] - }while(circle && circle.branch && !circle.branch.active) + }while(circle && (circle.branch && !circle.branch.active || circle.type === "event")) } getCurrentCircle(){ return this.currentCircle diff --git a/public/src/js/gamerules.js b/public/src/js/gamerules.js index 51cd4fc..c9da2f9 100644 --- a/public/src/js/gamerules.js +++ b/public/src/js/gamerules.js @@ -44,7 +44,7 @@ class GameRules{ case "easy": good = Math.floor(10000 / combo * 1.575) ok = Math.floor(good * 0.75) - bad = Math.ceil(good * -2) + bad = Math.ceil(good / -2) break case "normal": good = Math.floor(10000 / combo / 0.7) diff --git a/public/src/js/importsongs.js b/public/src/js/importsongs.js index 840d0bf..20a431a 100644 --- a/public/src/js/importsongs.js +++ b/public/src/js/importsongs.js @@ -232,7 +232,25 @@ songObj.song_skin = this.getSkin(dir, meta.taikowebskin) } if(meta.maker){ - songObj.maker = {name: meta.maker, id: 1} + var maker = meta.maker + var url = null + var gt = maker.lastIndexOf(">") + if(gt === maker.length - 1){ + var lt = maker.lastIndexOf("<") + if(lt !== -1 && lt !== gt - 2){ + url = maker.slice(lt + 2, gt) + if(url.startsWith("http://") || url.startsWith("https://")){ + maker = maker.slice(0, lt).trim() + }else{ + url = null + } + } + } + songObj.maker = { + name: maker, + url: url, + id: 1 + } } for(var id in allStrings){ var songTitle = songObj.title diff --git a/public/src/js/parsetja.js b/public/src/js/parsetja.js index a6f94e2..515266d 100644 --- a/public/src/js/parsetja.js +++ b/public/src/js/parsetja.js @@ -2,7 +2,12 @@ constructor(file, difficulty, stars, offset, metaOnly){ this.data = [] for(let line of file){ - line = line.replace(/\/\/.*/, "").trim() + var indexComment = line.indexOf("//") + if(indexComment !== -1 && !line.trim().toLowerCase().startsWith("maker:")){ + line = line.slice(0, indexComment).trim() + }else{ + line = line.trim() + } if(line !== ""){ this.data.push(line) } @@ -143,6 +148,8 @@ var branchSettings = {} var branchFirstMeasure = false var sectionBegin = true + var lastBpm = bpm + var lastGogo = gogo var currentMeasure = [] var firstNote = true @@ -195,7 +202,7 @@ if(currentMeasure.length){ for(var i = 0; i < currentMeasure.length; i++){ var note = currentMeasure[i] - if(firstNote && note.type){ + if(firstNote && note.type && note.type !== "event"){ firstNote = false if(ms < 0){ this.soundOffset = ms @@ -258,6 +265,31 @@ ms += msPerMeasure } } + var insertNote = circleObj => { + lastBpm = bpm + lastGogo = gogo + if(circleObj){ + currentMeasure.push(circleObj) + } + } + var insertBlankNote = circleObj => { + if(bpm !== lastBpm || gogo !== lastGogo){ + insertNote({ + type: "event", + bpm: bpm, + scroll: scroll, + gogo: gogo + }) + }else if(!circleObj){ + currentMeasure.push({ + bpm: bpm, + scroll: scroll + }) + } + if(circleObj){ + currentMeasure.push(circleObj) + } + } for(var lineNum = meta.start; lineNum < meta.end; lineNum++){ var line = this.data[lineNum] @@ -382,10 +414,7 @@ switch(symbol){ case "0": - currentMeasure.push({ - bpm: bpm, - scroll: scroll - }) + insertBlankNote() break case "1": case "2": case "3": case "4": case "A": case "B": var type = this.noteTypes[symbol] @@ -402,7 +431,7 @@ circleObj.endDrumroll = lastDrumroll lastDrumroll = false } - currentMeasure.push(circleObj) + insertNote(circleObj) break case "5": case "6": case "7": case "9": var type = this.noteTypes[symbol] @@ -417,7 +446,7 @@ sectionBegin = false if(lastDrumroll){ if(symbol === "9"){ - currentMeasure.push({ + insertBlankNote({ endDrumroll: lastDrumroll, bpm: bpm, scroll: scroll, @@ -426,10 +455,7 @@ sectionBegin = false lastDrumroll = false }else{ - currentMeasure.push({ - bpm: bpm, - scroll: scroll - }) + insertBlankNote() } break } @@ -442,11 +468,11 @@ balloonID++ } lastDrumroll = circleObj - currentMeasure.push(circleObj) + insertNote(circleObj) break case "8": if(lastDrumroll){ - currentMeasure.push({ + insertBlankNote({ endDrumroll: lastDrumroll, bpm: bpm, scroll: scroll, @@ -455,22 +481,27 @@ sectionBegin = false lastDrumroll = false }else{ - currentMeasure.push({ + insertBlankNote({ bpm: bpm, scroll: scroll }) } break case ",": + if(currentMeasure.length === 0 && (bpm !== lastBpm || gogo !== lastGogo)){ + insertNote({ + type: "event", + bpm: bpm, + scroll: scroll, + gogo: gogo + }) + } pushMeasure() currentMeasure = [] break default: if(regexAZ.test(symbol)){ - currentMeasure.push({ - bpm: bpm, - scroll: scroll - }) + insertBlankNote() }else if(!regexSpace.test(symbol)){ error = true } diff --git a/public/src/js/settings.js b/public/src/js/settings.js index c9fed36..e2c2f4d 100644 --- a/public/src/js/settings.js +++ b/public/src/js/settings.js @@ -544,7 +544,7 @@ class SettingsView{ }while(this.items[this.selected].id === "default" && name !== "left") selected = this.items[this.selected] selected.settingBox.classList.add("selected") - selected.settingBox.scrollIntoView() + this.scrollTo(selected.settingBox) this.playSound("se_ka") }else if(name === "back"){ this.onEnd() @@ -606,6 +606,21 @@ class SettingsView{ } } } + scrollTo(element){ + var parentNode = element.parentNode + var selected = element.getBoundingClientRect() + var parent = parentNode.getBoundingClientRect() + var scrollY = parentNode.scrollTop + var selectedPosTop = selected.top - selected.height / 2 + if(Math.floor(selectedPosTop) < Math.floor(parent.top)){ + parentNode.scrollTop += selectedPosTop - parent.top + }else{ + var selectedPosBottom = selected.top + selected.height * 1.5 - parent.top + if(Math.floor(selectedPosBottom) > Math.floor(parent.height)){ + parentNode.scrollTop += selectedPosBottom - parent.height + } + } + } keyboardSet(){ var selected = this.items[this.selected] var current = settings.items[selected.id] diff --git a/public/src/js/soundbuffer.js b/public/src/js/soundbuffer.js index 3e6f45e..3329802 100644 --- a/public/src/js/soundbuffer.js +++ b/public/src/js/soundbuffer.js @@ -2,7 +2,7 @@ constructor(){ var AudioContext = window.AudioContext || window.webkitAudioContext this.context = new AudioContext() - pageEvents.add(window, ["click", "touchend"], this.pageClicked.bind(this)) + pageEvents.add(window, ["click", "touchend", "keypress"], this.pageClicked.bind(this)) this.gainList = [] } load(url, local, gain){ diff --git a/public/src/js/titlescreen.js b/public/src/js/titlescreen.js index 6068a93..aa98e4d 100644 --- a/public/src/js/titlescreen.js +++ b/public/src/js/titlescreen.js @@ -35,7 +35,7 @@ class Titlescreen{ confirm: ["enter", "space", "don_l", "don_r"] }, this.onPressed.bind(this)) this.gamepad = new Gamepad({ - confirm: ["a", "b", "x", "y", "start", "ls", "rs"] + gamepadConfirm: ["a", "b", "x", "y", "start", "ls", "rs"] }, this.onPressed.bind(this)) if(p2.session){ pageEvents.add(p2, "message", response => { @@ -50,6 +50,9 @@ class Titlescreen{ onPressed(pressed, name){ if(pressed){ + if(name === "gamepadConfirm" && snd.buffer.context.state === "suspended"){ + return + } this.titleScreen.style.cursor = "auto" this.clean() assets.sounds["se_don"].play() diff --git a/public/src/js/view.js b/public/src/js/view.js index 20828e6..9964c83 100644 --- a/public/src/js/view.js +++ b/public/src/js/view.js @@ -1525,7 +1525,13 @@ // Start animation to gauge circle.animate(ms) } - if(ms >= circle.ms && !circle.gogoChecked && (!circle.branch || circle.branch.active)){ + if(ms - this.controller.audioLatency >= circle.ms && !circle.beatMSCopied && (!circle.branch || circle.branch.active)){ + if(this.beatInterval !== circle.beatMS){ + this.changeBeatInterval(circle.beatMS) + } + circle.beatMSCopied = true + } + if(ms - this.controller.audioLatency >= circle.ms && !circle.gogoChecked && (!circle.branch || circle.branch.active)){ if(this.gogoTime != circle.gogoTime){ this.toggleGogoTime(circle) } @@ -1842,15 +1848,16 @@ } } toggleGogoTime(circle){ + var startMS = circle.ms + this.controller.audioLatency this.gogoTime = circle.gogoTime if(circle.gogoTime || this.gogoTimeStarted !== -Infinity){ - this.gogoTimeStarted = circle.ms + this.gogoTimeStarted = startMS } if(this.gogoTime){ this.assets.fireworks.forEach(fireworksAsset => { fireworksAsset.setAnimation("normal") - fireworksAsset.setAnimationStart(circle.ms) + fireworksAsset.setAnimationStart(startMS) var length = fireworksAsset.getAnimationLength("normal") fireworksAsset.setAnimationEnd(length, () => { fireworksAsset.setAnimation(false) @@ -1861,7 +1868,7 @@ don.setAnimation("gogostart") var length = don.getAnimationLength("gogo") don.setUpdateSpeed(4 / length) - var start = circle.ms - (circle.ms % this.beatInterval) + var start = startMS - (startMS % this.beatInterval) don.setAnimationStart(start) var length = don.getAnimationLength("gogostart") don.setAnimationEnd(length, don.normalAnimation)