diff --git a/public/src/css/debug.css b/public/src/css/debug.css index 7c838f0..f9e1478 100644 --- a/public/src/css/debug.css +++ b/public/src/css/debug.css @@ -44,7 +44,8 @@ box-sizing: border-box; } -#debug .input-slider{ +#debug .input-slider, +#debug .select{ display: flex; width: 100%; height: 30px; @@ -59,7 +60,8 @@ padding: 2px 4px; text-align: center; } -#debug .input-slider>span{ +#debug .input-slider>span, +#debug .select>span{ display: block; width: 10%; height: 100%; @@ -70,10 +72,19 @@ line-height: 2em; cursor: pointer; } -#debug .input-slider>span:hover{ +#debug .input-slider>span:hover, +#debug .select>span:hover{ opacity: 1; background: #333; } +#debug .select select{ + width: 90%; + height: 100%; + box-sizing: border-box; + font-size: 18px; + font-family: sans-serif; + padding: 2px 4px; +} #debug label{ display: block; @@ -111,6 +122,7 @@ margin-left: 3px; } -#debug .autoplay-label{ +#debug .autoplay-label, +#debug .branch-hide{ display: none; } diff --git a/public/src/js/canvasdraw.js b/public/src/js/canvasdraw.js index 472730c..7733c3d 100644 --- a/public/src/js/canvasdraw.js +++ b/public/src/js/canvasdraw.js @@ -268,6 +268,9 @@ easeOut(pos){ return Math.sin(Math.PI / 2 * pos) } + easeOutBack(pos){ + return Math.sin(Math.PI / 1.74 * pos) * 1.03 + } easeInOut(pos){ return (Math.cos(Math.PI * pos) - 1) / -2 } @@ -572,7 +575,7 @@ } if(symbol.ura){ ctx.font = (30 * mul) + "px Meiryo, sans-serif" - ctx.textBaseline = "center" + ctx.textBaseline = "middle" ctx.beginPath() ctx.arc(currentX, currentY + (17 * mul), (18 * mul), 0, Math.PI * 2) if(action === "stroke"){ @@ -581,7 +584,7 @@ }else if(action === "fill"){ ctx.strokeStyle = config.fill ctx.lineWidth = 2.5 * mul - ctx.fillText(symbol.text, currentX, currentY) + ctx.fillText(symbol.text, currentX, currentY + (17 * mul)) } ctx.stroke() }else{ @@ -788,7 +791,7 @@ } if(symbol.ura){ ctx.font = (30 * mul) + "px Meiryo, sans-serif" - ctx.textBaseline = "center" + ctx.textBaseline = "middle" ctx.beginPath() ctx.arc(currentX, currentY + (17 * mul), (18 * mul), 0, Math.PI * 2) if(action === "strokeText"){ @@ -797,7 +800,7 @@ }else if(action === "fillText"){ ctx.strokeStyle = layer.fill ctx.lineWidth = 2.5 * mul - ctx.fillText(symbol.text, currentX, currentY) + ctx.fillText(symbol.text, currentX, currentY + (17 * mul)) } ctx.stroke() }else{ @@ -1167,6 +1170,7 @@ var firstTop = config.multiplayer ? 0 : 30 var secondTop = config.multiplayer ? 0 : 8 + config.percentage = Math.max(0, Math.min(1, config.percentage)) var cleared = config.percentage - 1 / 50 >= config.clear var gaugeW = 14 * 50 diff --git a/public/src/js/circle.js b/public/src/js/circle.js index 6d3c650..261629e 100644 --- a/public/src/js/circle.js +++ b/public/src/js/circle.js @@ -1,6 +1,5 @@ class Circle{ constructor(config){ - // id, ms, type, text, speed, endTime, requiredHits this.id = config.id this.ms = config.start this.originalMS = this.ms @@ -23,38 +22,13 @@ class Circle{ this.gogoChecked = false this.beatMS = config.beatMS this.fixedPos = config.fixedPos - } - getMS(){ - return this.ms - } - getEndTime(){ - return this.endTime - } - getType(){ - return this.type - } - getLastFrame(){ - return this.lastFrame + this.branch = config.branch + this.section = config.section } animate(ms){ this.animating = true this.animT = ms } - isAnimated(){ - return this.animating - } - getAnimT(){ - return this.animT - } - getPlayed(){ - return this.isPlayed - } - isAnimationFinished(){ - return this.animationEnded - } - endAnimation(){ - this.animationEnded = true - } played(score, big){ this.score = score this.isPlayed = score <= 0 ? score - 1 : (big ? 2 : 1) @@ -65,16 +39,4 @@ class Circle{ this.timesKa++ } } - getScore(){ - return this.score - } - getID(){ - return this.id - } - getText(){ - return this.text - } - getSpeed(){ - return this.speed - } } \ No newline at end of file diff --git a/public/src/js/debug.js b/public/src/js/debug.js index 6b03534..9f2836c 100644 --- a/public/src/js/debug.js +++ b/public/src/js/debug.js @@ -8,15 +8,19 @@ class Debug{ this.debugDiv.innerHTML = assets.pages["debug"] document.body.appendChild(this.debugDiv) - this.titleDiv = this.debugDiv.getElementsByClassName("title")[0] - this.minimiseDiv = this.debugDiv.getElementsByClassName("minimise")[0] - this.offsetDiv = this.debugDiv.getElementsByClassName("offset")[0] - this.measureNumDiv = this.debugDiv.getElementsByClassName("measure-num")[0] - this.restartCheckbox = this.debugDiv.getElementsByClassName("change-restart")[0] - this.autoplayLabel = this.debugDiv.getElementsByClassName("autoplay-label")[0] - this.autoplayCheckbox = this.debugDiv.getElementsByClassName("autoplay")[0] - this.restartBtn = this.debugDiv.getElementsByClassName("restart-btn")[0] - this.exitBtn = this.debugDiv.getElementsByClassName("exit-btn")[0] + this.titleDiv = this.byClass("title") + this.minimiseDiv = this.byClass("minimise") + this.offsetDiv = this.byClass("offset") + this.measureNumDiv = this.byClass("measure-num") + this.branchHideDiv = this.byClass("branch-hide") + this.branchSelectDiv = this.byClass("branch-select") + this.branchSelect = this.branchSelectDiv.getElementsByTagName("select")[0] + this.branchResetBtn = this.branchSelectDiv.getElementsByClassName("reset")[0] + this.restartCheckbox = this.byClass("change-restart") + this.autoplayLabel = this.byClass("autoplay-label") + this.autoplayCheckbox = this.byClass("autoplay") + this.restartBtn = this.byClass("restart-btn") + this.exitBtn = this.byClass("exit-btn") this.moving = false pageEvents.add(window, ["mousedown", "mouseup", "blur"], this.stopMove.bind(this)) @@ -26,6 +30,8 @@ class Debug{ pageEvents.add(this.restartBtn, "click", this.restartSong.bind(this)) pageEvents.add(this.exitBtn, "click", this.clean.bind(this)) pageEvents.add(this.autoplayCheckbox, "change", this.toggleAutoplay.bind(this)) + pageEvents.add(this.branchSelect, "change", this.branchChange.bind(this)) + pageEvents.add(this.branchResetBtn, "click", this.branchReset.bind(this)) this.offsetSlider = new InputSlider(this.offsetDiv, -60, 60, 3) this.offsetSlider.onchange(this.offsetChange.bind(this)) @@ -39,6 +45,9 @@ class Debug{ this.updateStatus() pageEvents.send("debug") } + byClass(name){ + return this.debugDiv.getElementsByClassName(name)[0] + } startMove(event){ if(event.which === 1){ event.stopPropagation() @@ -88,20 +97,28 @@ class Debug{ } updateStatus(){ if(debugObj.controller && !this.controller){ + this.controller = debugObj.controller + this.restartBtn.style.display = "block" this.autoplayLabel.style.display = "block" + if(this.controller.parsedSongData.branches){ + this.branchHideDiv.style.display = "block" + } - this.controller = debugObj.controller var selectedSong = this.controller.selectedSong this.defaultOffset = selectedSong.offset || 0 if(this.songFolder === selectedSong.folder){ this.offsetChange(this.offsetSlider.get(), true) + this.branchChange(null, true) }else{ this.songFolder = selectedSong.folder this.offsetSlider.set(this.defaultOffset) + this.branchReset(null, true) } - var measures = this.controller.parsedSongData.measures + var measures = this.controller.parsedSongData.measures.filter((measure, i, array) => { + return i === 0 || Math.abs(measure.ms - array[i - 1].ms) > 0.01 + }) this.measureNumSlider.setMinMax(0, measures.length - 1) if(this.measureNum && measures.length > this.measureNum){ var measureMS = measures[this.measureNum].ms @@ -128,6 +145,7 @@ class Debug{ if(this.controller && !debugObj.controller){ this.restartBtn.style.display = "" this.autoplayLabel.style.display = "" + this.branchHideDiv.style.display = "" this.controller = null } } @@ -142,6 +160,11 @@ class Debug{ songData.measures.forEach(measure => { measure.ms = measure.originalMS + offset }) + if(songData.branches){ + songData.branches.forEach(branch => { + branch.ms = branch.originalMS + offset + }) + } if(this.restartCheckbox.checked && !noRestart){ this.restartSong() } @@ -171,21 +194,46 @@ class Debug{ } } } + branchChange(event, noRestart){ + if(this.controller){ + var game = this.controller.game + var name = this.branchSelect.value + game.branch = name === "auto" ? false : name + game.branchSet = name === "auto" + var selectedOption = this.branchSelect.selectedOptions[0] + this.branchSelect.style.background = selectedOption.style.background + if(this.restartCheckbox.checked && !noRestart){ + this.restartSong() + } + } + } + branchReset(event, noRestart){ + this.branchSelect.value = "auto" + this.branchChange(null, noRestart) + } clean(){ this.offsetSlider.clean() + this.measureNumSlider.clean() pageEvents.remove(window, ["mousedown", "mouseup", "blur"]) pageEvents.mouseRemove(this) + pageEvents.remove(this.titleDiv, "mousedown") pageEvents.remove(this.title, "mousedown") pageEvents.remove(this.minimiseDiv, "click") pageEvents.remove(this.restartBtn, "click") pageEvents.remove(this.exitBtn, "click") pageEvents.remove(this.autoplayCheckbox, "change") + pageEvents.remove(this.branchSelect, "change") + pageEvents.remove(this.branchResetBtn, "click") delete this.titleDiv delete this.minimiseDiv delete this.offsetDiv delete this.measureNumDiv + delete this.branchHideDiv + delete this.branchSelectDiv + delete this.branchSelect + delete this.branchResetBtn delete this.restartCheckbox delete this.autoplayLabel delete this.autoplayCheckbox diff --git a/public/src/js/game.js b/public/src/js/game.js index 76f7006..3f0845b 100644 --- a/public/src/js/game.js +++ b/public/src/js/game.js @@ -19,8 +19,8 @@ class Game{ difficulty: this.rules.difficulty } this.HPGain = 100 / this.songData.circles.filter(circle => { - var type = circle.getType() - return type === "don" || type === "ka" || type === "daiDon" || type === "daiKa" + var type = circle.type + return (type === "don" || type === "ka" || type === "daiDon" || type === "daiKa") && (!circle.branch || circle.branch.active) }).length this.paused = false this.started = false @@ -28,6 +28,8 @@ class Game{ this.musicFadeOut = 0 this.fadeOutStarted = false this.currentTimingPoint = 0 + this.branchNames = ["normal", "advanced", "master"] + this.resetSection() assets.songs.forEach(song => { if(song.id == selectedSong.folder){ @@ -69,17 +71,18 @@ class Game{ } updateCirclesStatus(){ var nextSet = false + var ms = this.elapsedTime var circles = this.songData.circles var startIndex = this.currentCircle === 0 ? 0 : this.currentCircle - 1 - for(var i = startIndex; i < circles.length && i < this.currentCircle + 2; i++){ + var index = 0 + for(var i = startIndex; i < circles.length; i++){ var circle = circles[i] - if(!circle.getPlayed()){ - var ms = this.elapsedTime - var type = circle.getType() + if(circle && (!circle.branch || circle.branch.active) && !circle.isPlayed){ + var type = circle.type var drumrollNotes = type === "balloon" || type === "drumroll" || type === "daiDrumroll" - var endTime = circle.getEndTime() + (drumrollNotes ? 0 : this.rules.bad) + var endTime = circle.endTime + (drumrollNotes ? 0 : this.rules.bad) - if(ms >= circle.getMS()){ + if(ms >= circle.ms){ if(drumrollNotes && !circle.rendaPlayed && ms < endTime){ circle.rendaPlayed = true if(this.rules.difficulty === "easy"){ @@ -97,11 +100,14 @@ class Game{ if(ms > endTime){ if(!this.controller.autoPlayEnabled){ if(drumrollNotes){ + if(circle.section && circle.timesHit === 0){ + this.resetSection() + } circle.played(-1, false) this.updateCurrentCircle() if(this.controller.multiplayer === 1){ var value = { - pace: (ms - circle.getMS()) / circle.timesHit + pace: (ms - circle.ms) / circle.timesHit } if(type === "drumroll" || type === "daiDrumroll"){ value.kaAmount = circle.timesKa / circle.timesHit @@ -109,8 +115,12 @@ class Game{ p2.send("drumroll", value) } }else{ + if(circle.section){ + this.resetSection() + } var currentScore = 0 circle.played(-1, type === "daiDon" || type === "daiKa") + this.sectionNotes.push(0) this.controller.displayScore(currentScore, true) this.updateCurrentCircle() this.updateCombo(currentScore) @@ -126,6 +136,71 @@ class Game{ nextSet = true this.currentCircle = i } + if(index++ > 1){ + break + } + } + } + var branches = this.songData.branches + if(branches){ + var force = this.controller.multiplayer === 2 ? p2 : this + var measures = this.songData.measures + if(this.controller.multiplayer === 2 || force.branch){ + if(!force.branchSet){ + force.branchSet = true + if(branches.length){ + this.setBranch(branches[0], force.branch) + } + var view = this.controller.view + var currentMeasure = view.branch + for(var i = measures.length; i--;){ + var measure = measures[i] + if(measure.nextBranch && measure.ms <= ms){ + currentMeasure = measure.nextBranch.active + } + } + if(view.branch !== currentMeasure){ + view.branchAnimate = { + ms: ms, + fromBranch: view.branch + } + view.branch = currentMeasure + } + } + } + for(var i = 0; i < measures.length; i++){ + var measure = measures[i] + if(measure.ms > ms){ + break + }else if(measure.nextBranch && !measure.gameChecked){ + measure.gameChecked = true + var branch = measure.nextBranch + if(branch.type){ + var accuracy = 0 + if(branch.type === "drumroll"){ + if(force.branch){ + var accuracy = Math.max(0, branch.requirement[force.branch]) + }else{ + var accuracy = this.sectionDrumroll + } + }else if(this.sectionNotes.length !== 0){ + if(force.branch){ + var accuracy = Math.max(0, Math.min(100, branch.requirement[force.branch])) + }else{ + var accuracy = this.sectionNotes.reduce((a, b) => a + b) / this.sectionNotes.length * 100 + } + } + if(accuracy >= branch.requirement.master){ + this.setBranch(branch, "master") + }else if(accuracy >= branch.requirement.advanced){ + this.setBranch(branch, "advanced") + }else{ + this.setBranch(branch, "normal") + } + }else if(this.controller.multiplayer === 1){ + p2.send("branch", "normal") + } + } } } } @@ -160,7 +235,7 @@ class Game{ } } checkKey(keyCodes, circle, check){ - if(circle && !circle.getPlayed()){ + if(circle && !circle.isPlayed){ if(!this.checkScore(circle, check)){ return } @@ -171,7 +246,7 @@ class Game{ } checkScore(circle, check){ var ms = this.elapsedTime - var type = circle.getType() + var type = circle.type var keysDon = check === "don" || check === "daiDon" var keysKa = check === "ka" || check === "daiKa" @@ -182,7 +257,7 @@ class Game{ var keyTime = this.controller.getKeyTime() var currentTime = keysDon ? keyTime["don"] : keyTime["ka"] - var relative = currentTime - circle.getMS() + var relative = currentTime - circle.ms if(typeDon || typeKa){ if(-this.rules.bad >= relative || relative >= this.rules.bad){ @@ -219,10 +294,14 @@ class Game{ this.updateCombo(score) this.updateGlobalScore(score, typeDai && keyDai ? 2 : 1, circle.gogoTime) this.updateCurrentCircle() - if(this.controller.multiplayer == 1){ + if(circle.section){ + this.resetSection() + } + this.sectionNotes.push(score === 450 ? 1 : (score === 230 ? 0.5 : 0)) + if(this.controller.multiplayer === 1){ var value = { score: score, - ms: circle.getMS() - currentTime, + ms: circle.ms - currentTime, dai: typeDai ? keyDai ? 2 : 1 : 0 } if((!keysDon || !typeDon) && (!keysKa || !typeKa)){ @@ -231,12 +310,12 @@ class Game{ p2.send("note", value) } }else{ - if(circle.getMS() > currentTime || currentTime > circle.getEndTime()){ + if(circle.ms > currentTime || currentTime > circle.endTime){ return true } if(keysDon && type === "balloon"){ this.checkBalloon(circle) - if(check === "daiDon" && !circle.getPlayed()){ + if(check === "daiDon" && !circle.isPlayed){ this.checkBalloon(circle) } }else if((keysDon || keysKa) && (type === "drumroll" || type === "daiDrumroll")){ @@ -256,24 +335,28 @@ class Game{ circle.played(score) if(this.controller.multiplayer == 1){ p2.send("drumroll", { - pace: (this.elapsedTime - circle.getMS()) / circle.timesHit + pace: (this.elapsedTime - circle.ms) / circle.timesHit }) } }else{ var score = 300 circle.hit() } - this.globalScore.drumroll ++ + this.globalScore.drumroll++ + this.sectionDrumroll++ this.globalScore.points += score this.view.setDarkBg(false) } checkDrumroll(circle, keysKa){ var ms = this.elapsedTime - var dai = circle.getType() === "daiDrumroll" + var dai = circle.type === "daiDrumroll" var score = 100 + if(circle.section && circle.timesHit === 0){ + this.resetSection() + } circle.hit(keysKa) var keyTime = this.controller.getKeyTime() - if(circle.getType() === "drumroll"){ + if(circle.type === "drumroll"){ var sound = keyTime["don"] > keyTime["ka"] ? "don" : "ka" }else{ var sound = keyTime["don"] > keyTime["ka"] ? "daiDon" : "daiKa" @@ -291,6 +374,7 @@ class Game{ circleAnim.animate(ms) this.view.drumroll.push(circleAnim) this.globalScore.drumroll++ + this.sectionDrumroll++ this.globalScore.points += score * (dai ? 2 : 1) this.view.setDarkBg(false) } @@ -298,11 +382,11 @@ class Game{ var ms = this.elapsedTime if(!this.lastCircle){ var circles = this.songData.circles - this.lastCircle = circles[circles.length - 1].getEndTime() + this.lastCircle = circles[circles.length - 1].endTime if(this.controller.multiplayer){ var syncWith = this.controller.syncWith var syncCircles = syncWith.game.songData.circles - var syncLastCircle = syncCircles[syncCircles.length - 1].getEndTime() + var syncLastCircle = syncCircles[syncCircles.length - 1].endTime if(syncLastCircle > this.lastCircle){ this.lastCircle = syncLastCircle } @@ -410,7 +494,10 @@ class Game{ return this.songData.circles } updateCurrentCircle(){ - this.currentCircle++ + var circles = this.songData.circles + do{ + var circle = circles[++this.currentCircle] + }while(circle && circle.branch && !circle.branch.active) } getCurrentCircle(){ return this.currentCircle @@ -464,4 +551,59 @@ class Game{ } this.globalScore.points += Math.floor(score * multiplier / 10) * 10 } + setBranch(currentBranch, activeName){ + var pastActive = currentBranch.active + var ms = currentBranch.ms + for(var i = 0; i < this.songData.branches.length; i++){ + var branch = this.songData.branches[i] + if(branch.ms >= ms){ + var relevantName = activeName + var req = branch.requirement + var noNormal = req.advanced <= 0 + var noAdvanced = req.master <= 0 || req.advanced >= req.master || branch.type === "accuracy" && req.advanced > 100 + var noMaster = branch.type === "accuracy" && req.master > 100 + if(relevantName === "normal" && noNormal){ + relevantName = noAdvanced ? "master" : "advanced" + } + if(relevantName === "advanced" && noAdvanced){ + relevantName = noMaster ? "normal" : "master" + } + if(relevantName === "master" && noMaster){ + relevantName = noAdvanced ? "normal" : "advanced" + } + for(var j in this.branchNames){ + var name = this.branchNames[j] + if(name in branch){ + branch[name].active = name === relevantName + } + } + if(branch === currentBranch){ + activeName = relevantName + } + branch.active = relevantName + } + } + var circles = this.songData.circles + var circle = circles[this.currentCircle] + if(!circle || circle.branch === currentBranch[pastActive]){ + var ms = this.elapsedTime + var closestCircle = circles.findIndex(circle => { + return (!circle.branch || circle.branch.active) && circle.endTime >= ms + }) + if(closestCircle !== -1){ + this.currentCircle = closestCircle + } + } + this.HPGain = 100 / this.songData.circles.filter(circle => { + var type = circle.type + return (type === "don" || type === "ka" || type === "daiDon" || type === "daiKa") && (!circle.branch || circle.branch.active) + }).length + if(this.controller.multiplayer === 1){ + p2.send("branch", activeName) + } + } + resetSection(){ + this.sectionNotes = [] + this.sectionDrumroll = 0 + } } diff --git a/public/src/js/importsongs.js b/public/src/js/importsongs.js index 400f443..cb907f5 100644 --- a/public/src/js/importsongs.js +++ b/public/src/js/importsongs.js @@ -199,7 +199,7 @@ songObj.subtitle = songObj.subtitle_en = subtitle songObj.preview = meta.demostart ? Math.floor(meta.demostart * 1000) : 0 if(meta.level){ - songObj.stars[this.courseTypes[diff]] = meta.level + songObj.stars[this.courseTypes[diff]] = meta.level + (meta.branch ? " B" : "") } if(meta.wave){ songObj.music = this.otherFiles[dir + meta.wave.toLowerCase()] diff --git a/public/src/js/keyboard.js b/public/src/js/keyboard.js index 186c651..5925658 100644 --- a/public/src/js/keyboard.js +++ b/public/src/js/keyboard.js @@ -199,8 +199,8 @@ class Keyboard{ if( sound === "don" && circle - && !circle.getPlayed() - && circle.getType() === "balloon" + && !circle.isPlayed + && circle.type === "balloon" && circle.requiredHits - circle.timesHit <= 1 ){ this.controller.playSound("se_balloon") diff --git a/public/src/js/main.js b/public/src/js/main.js index a63691e..1e632ca 100644 --- a/public/src/js/main.js +++ b/public/src/js/main.js @@ -106,7 +106,9 @@ pageEvents.keyAdd(debugObj, "all", "down", event => { }else if(debugObj.state === "minimised"){ debugObj.debug.restore() }else{ - debugObj.debug = new Debug() + try{ + debugObj.debug = new Debug() + }catch(e){} } } if(event.keyCode === 82 && debugObj.debug && debugObj.controller){ diff --git a/public/src/js/mekadon.js b/public/src/js/mekadon.js index cca80e2..962b9ff 100644 --- a/public/src/js/mekadon.js +++ b/public/src/js/mekadon.js @@ -6,12 +6,15 @@ class Mekadon{ this.lastHit = -Infinity } play(circle){ - var type = circle.getType() - if((type === "balloon" || type === "drumroll" || type === "daiDrumroll") && this.getMS() > circle.getEndTime()){ + var type = circle.type + if((type === "balloon" || type === "drumroll" || type === "daiDrumroll") && this.getMS() > circle.endTime){ + if(circle.section && circle.timesHit === 0){ + this.game.resetSection() + } circle.played(-1, false) this.game.updateCurrentCircle() } - type = circle.getType() + type = circle.type if(type === "balloon"){ this.playDrumrollAt(circle, 0, 30) }else if(type === "drumroll" || type === "daiDrumroll"){ @@ -21,7 +24,7 @@ class Mekadon{ } } playAt(circle, ms, score, dai, reverse){ - var currentMs = circle.getMS() - this.getMS() + var currentMs = circle.ms - this.getMS() if(ms > currentMs - 10){ return this.playNow(circle, score, dai, reverse) } @@ -36,18 +39,19 @@ class Mekadon{ } } miss(circle){ - var currentMs = circle.getMS() - this.getMS() + var currentMs = circle.ms - this.getMS() if(0 >= currentMs - 10){ this.controller.displayScore(0, true) this.game.updateCurrentCircle() this.game.updateCombo(0) this.game.updateGlobalScore(0, 1, circle.gogoTime) + this.game.sectionNotes.push(0) return true } } playNow(circle, score, dai, reverse){ var kbd = this.controller.getBindings() - var type = circle.getType() + var type = circle.type var keyDai = false var playDai = !dai || dai === 2 var drumrollNotes = type === "balloon" || type === "drumroll" || type === "daiDrumroll" @@ -55,7 +59,7 @@ class Mekadon{ if(drumrollNotes){ var ms = this.getMS() }else{ - var ms = circle.getMS() + var ms = circle.ms } if(reverse){ @@ -95,6 +99,10 @@ class Mekadon{ this.game.updateGlobalScore(score, keyDai ? 2 : 1, circle.gogoTime) this.game.updateCurrentCircle() circle.played(score, keyDai) + if(circle.section){ + this.game.resetSection() + } + this.game.sectionNotes.push(score === 450 ? 1 : (score === 230 ? 0.5 : 0)) } this.lastHit = ms return true diff --git a/public/src/js/p2.js b/public/src/js/p2.js index 1f6f97e..a0c266d 100644 --- a/public/src/js/p2.js +++ b/public/src/js/p2.js @@ -109,6 +109,7 @@ class P2Connection{ this.dai = 2 this.kaAmount = 0 this.results = false + this.branch = "normal" break case "gameend": this.otherConnected = false @@ -141,6 +142,10 @@ class P2Connection{ this.kaAmount = response.value.kaAmount } break + case "branch": + this.branch = response.value + this.branchSet = false + break case "session": this.clearMessage("users") this.otherConnected = true @@ -161,10 +166,10 @@ class P2Connection{ } play(circle, mekadon){ if(this.otherConnected || this.notes.length > 0){ - var type = circle.getType() + var type = circle.type var drumrollNotes = type === "balloon" || type === "drumroll" || type === "daiDrumroll" - if(drumrollNotes && mekadon.getMS() > circle.getEndTime()){ + if(drumrollNotes && mekadon.getMS() > circle.endTime){ circle.played(-1, false) mekadon.game.updateCurrentCircle() } @@ -177,7 +182,7 @@ class P2Connection{ var note = this.notes[0] if(note.score >= 0){ var dai = 1 - if(circle.getType() === "daiDon" || circle.getType() === "daiKa"){ + if(circle.type === "daiDon" || circle.type === "daiKa"){ dai = this.dai } if(mekadon.playAt(circle, note.ms, note.score, dai, note.reverse)){ diff --git a/public/src/js/parsetja.js b/public/src/js/parsetja.js index 0055b99..22e2e9f 100644 --- a/public/src/js/parsetja.js +++ b/public/src/js/parsetja.js @@ -59,7 +59,9 @@ if(!(courseName in courses)){ courses[courseName] = {} } - courses[courseName][name] = currentCourse[name] + if(name !== "branch"){ + courses[courseName][name] = currentCourse[name] + } } courses[courseName].start = lineNum + 1 courses[courseName].end = this.data.length @@ -70,6 +72,8 @@ hasSong = true courses[courseName].end = lineNum } + }else if(name.startsWith("branchstart") && inSong){ + courses[courseName].branch = true } }else if(!inSong){ @@ -128,9 +132,13 @@ var balloons = meta.balloon || [] var lastDrumroll = false + var branch = false - var branchType - var branchPreference = "m" + var branchObj = {} + var currentBranch = false + var branchSettings = {} + var branchFirstMeasure = false + var sectionBegin = true var currentMeasure = [] var firstNote = true @@ -138,19 +146,21 @@ var circleID = 0 var pushMeasure = () => { - if(barLine){ - var note = currentMeasure[0] - if(note){ - var speed = note.bpm * note.scroll / 60 - }else{ - var speed = bpm * scroll / 60 - } - this.measures.push({ - ms: ms, - originalMS: ms, - speed: speed - }) + var note = currentMeasure[0] + if(note){ + var speed = note.bpm * note.scroll / 60 + }else{ + var speed = bpm * scroll / 60 } + this.measures.push({ + ms: ms, + originalMS: ms, + speed: speed, + visible: barLine, + branch: currentBranch, + branchFirst: branchFirstMeasure + }) + branchFirstMeasure = false if(currentMeasure.length){ for(var i = 0; i < currentMeasure.length; i++){ var note = currentMeasure[i] @@ -182,7 +192,9 @@ gogoTime: note.gogo, endTime: note.endTime, requiredHits: note.requiredHits, - beatMS: 60000 / note.bpm + beatMS: 60000 / note.bpm, + branch: currentBranch, + section: note.section }) if(lastDrumroll === note){ lastDrumroll = circleObj @@ -204,59 +216,113 @@ var line = line.slice(1).toLowerCase() var [name, value] = this.split(line, " ") - if(!branch || branch && branchType === branchPreference){ - switch(name){ - case "gogostart": - gogo = true - break - case "gogoend": - gogo = false - break - case "bpmchange": - bpm = parseFloat(value) - break - case "scroll": - scroll = parseFloat(value) - break - case "measure": - var [numerator, denominator] = value.split("/") - measure = numerator / denominator * 4 - break - case "delay": - ms += (parseFloat(value) || 0) * 1000 - break - case "barlineon": - barLine = true - break - case "barlineoff": - barLine = false - break - } - } switch(name){ + case "gogostart": + gogo = true + break + case "gogoend": + gogo = false + break + case "bpmchange": + bpm = parseFloat(value) || bpm + break + case "scroll": + scroll = parseFloat(value) || scroll + break + case "measure": + var [numerator, denominator] = value.split("/") + measure = numerator / denominator * 4 || measure + break + case "delay": + ms += (parseFloat(value) || 0) * 1000 + break + case "barlineon": + barLine = true + break + case "barlineoff": + barLine = false + break case "branchstart": branch = true - branchType = "" + currentBranch = false + branchFirstMeasure = true + branchSettings = { + ms: ms, + gogo: gogo, + bpm: bpm, + scroll: scroll, + sectionBegin: sectionBegin + } value = value.split(",") - var forkType = value[0].toLowerCase() - if(forkType === "r" || parseFloat(value[2]) <= 100){ - branchPreference = "m" - }else if(parseFloat(value[1]) <= 100){ - branchPreference = "e" + if(!this.branches){ + this.branches = [] + } + var req = { + advanced: parseFloat(value[1]) || 0, + master: parseFloat(value[2]) || 0 + } + if(req.advanced > 0){ + var active = req.master > 0 ? "normal" : "master" }else{ - branchPreference = "n" + var active = req.master > 0 ? "advanced" : "master" + } + branchObj = { + ms: ms, + originalMS: ms, + active: active, + type: value[0].trim().toLowerCase() === "r" ? "drumroll" : "accuracy", + requirement: req + } + this.branches.push(branchObj) + if(this.measures.length === 1 && branchObj.type === "drumroll"){ + for(var i = circles.length; i--;){ + var circle = circles[i] + if(circle.endTime && circle.type === "drumroll" || circle.type === "daiDrumroll" || circle.type === "balloon"){ + this.measures.push({ + ms: circle.endTime, + originalMS: circle.endTime, + speed: circle.bpm * circle.scroll / 60, + visible: false, + branch: circle.branch + }) + break + } + } + } + if(this.measures.length !== 0){ + this.measures[this.measures.length - 1].nextBranch = branchObj } break case "branchend": - case "section": branch = false + currentBranch = false + break + case "section": + sectionBegin = true + if(branch && !currentBranch){ + branchSettings.sectionBegin = true + } break case "n": case "e": case "m": - branchType = name + if(!branch){ + break + } + ms = branchSettings.ms + gogo = branchSettings.gogo + bpm = branchSettings.bpm + scroll = branchSettings.scroll + sectionBegin = branchSettings.sectionBegin + branchFirstMeasure = true + var branchName = name === "m" ? "master" : (name === "e" ? "advanced" : "normal") + currentBranch = { + name: branchName, + active: branchName === branchObj.active + } + branchObj[branchName] = currentBranch break } - }else if(!branch || branch && branchType === branchPreference){ + }else{ var string = line.split("") @@ -278,8 +344,10 @@ txt: type.txt, gogo: gogo, bpm: bpm, - scroll: scroll + scroll: scroll, + section: sectionBegin } + sectionBegin = false if(lastDrumroll){ circleObj.endDrumroll = lastDrumroll lastDrumroll = false @@ -293,15 +361,19 @@ txt: type.txt, gogo: gogo, bpm: bpm, - scroll: scroll + scroll: scroll, + section: sectionBegin } + sectionBegin = false if(lastDrumroll){ if(symbol === "9"){ currentMeasure.push({ endDrumroll: lastDrumroll, bpm: bpm, - scroll: scroll + scroll: scroll, + section: sectionBegin }) + sectionBegin = false lastDrumroll = false }else{ currentMeasure.push({ @@ -327,8 +399,10 @@ currentMeasure.push({ endDrumroll: lastDrumroll, bpm: bpm, - scroll: scroll + scroll: scroll, + section: sectionBegin }) + sectionBegin = false lastDrumroll = false }else{ currentMeasure.push({ @@ -359,6 +433,11 @@ lastDrumroll.originalEndTime = ms } + if(this.branches){ + circles.sort((a, b) => a.ms > b.ms ? 1 : -1) + this.measures.sort((a, b) => a.ms > b.ms ? 1 : -1) + circles.forEach((circle, i) => circle.id = i + 1) + } return circles } } diff --git a/public/src/js/songselect.js b/public/src/js/songselect.js index 6dbe579..c6271f4 100644 --- a/public/src/js/songselect.js +++ b/public/src/js/songselect.js @@ -351,7 +351,7 @@ class SongSelect{ down: code == 40 // Down } - if(key.cancel && event){ + if(event && (code == 27 || code == 8)){ event.preventDefault() } if(this.state.screen === "song"){ @@ -1322,27 +1322,47 @@ class SongSelect{ outlineSize: currentUra ? this.songAsset.letterBorder : 0 }) }) - var songStars = currentUra ? currentSong.stars[4] : currentSong.stars[i] - for(var j = 0; j < 10; j++){ - if(songSel){ - var yPos = _y + 113 + j * 17 - }else{ - var yPos = _y + 178 + j * 19.5 - } - if(10 - j > songStars){ - ctx.fillStyle = currentUra ? "#187085" : (songSel ? "#e97526" : "#e7e7e7") - ctx.beginPath() - ctx.arc(_x, yPos, songSel ? 4.5 : 5, 0, Math.PI * 2) - ctx.fill() - }else{ - this.draw.diffStar({ - ctx: ctx, - songSel: songSel, - ura: currentUra, - x: _x, - y: yPos, - ratio: ratio - }) + var songStarsArray = (currentUra ? currentSong.stars[4] : currentSong.stars[i]).toString().split(" ") + var songStars = songStarsArray[0] + var songBranch = songStarsArray[1] === "B" + var elapsedMS = this.state.screenMS > this.state.moveMS || !songSel ? this.state.screenMS : this.state.moveMS + var fade = ((ms - elapsedMS) % 2000) / 2000 + if(songBranch && fade > 0.25 && fade < 0.75){ + this.draw.verticalText({ + ctx: ctx, + text: strings.songBranch, + x: _x, + y: _y + (songSel ? 110 : 185), + width: songSel ? 44 : 56, + height: songSel ? 160 : 170, + fill: songSel && !currentUra ? "#c85200" : "#fff", + fontSize: songSel ? 25 : 27, + fontFamily: songSel ? "Meiryo, Microsoft YaHei, sans-serif" : this.font, + outline: songSel ? false : "#f22666", + outlineSize: songSel ? 0 : this.songAsset.letterBorder + }) + }else{ + for(var j = 0; j < 10; j++){ + if(songSel){ + var yPos = _y + 113 + j * 17 + }else{ + var yPos = _y + 178 + j * 19.5 + } + if(10 - j > songStars){ + ctx.fillStyle = currentUra ? "#187085" : (songSel ? "#e97526" : "#e7e7e7") + ctx.beginPath() + ctx.arc(_x, yPos, songSel ? 4.5 : 5, 0, Math.PI * 2) + ctx.fill() + }else{ + this.draw.diffStar({ + ctx: ctx, + songSel: songSel, + ura: currentUra, + x: _x, + y: yPos, + ratio: ratio + }) + } } } var currentDiff = this.selectedDiff - this.diffOptions.length diff --git a/public/src/js/soundbuffer.js b/public/src/js/soundbuffer.js index 0262a6b..f220863 100644 --- a/public/src/js/soundbuffer.js +++ b/public/src/js/soundbuffer.js @@ -86,7 +86,7 @@ class SoundGain{ this.volume = amount } setCrossfade(amount){ - this.setVolume(Math.pow(Math.sin(Math.PI / 2 * amount), 1 / 4)) + this.setVolume(Math.sqrt(Math.sin(Math.PI / 2 * amount))) } fadeIn(duration, time, absolute){ this.fadeVolume(0, this.volume * this.volume, duration, time, absolute) diff --git a/public/src/js/strings.js b/public/src/js/strings.js index 967d0a1..7d13389 100644 --- a/public/src/js/strings.js +++ b/public/src/js/strings.js @@ -34,6 +34,7 @@ this.normal = "ふつう" this.hard = "むずかしい" this.oni = "おに" + this.songBranch = "譜面分岐あり" this.sessionStart = "オンラインセッションを開始する!" this.sessionEnd = "オンラインセッションを終了する" this.loading = "ロード中..." @@ -53,6 +54,11 @@ this.good = "良" this.ok = "可" this.bad = "不可" + this.branch = { + "normal": "普通譜面", + "advanced": "玄人譜面", + "master": "達人譜面" + } this.pauseOptions = [ "演奏をつづける", "はじめからやりなおす", @@ -135,6 +141,7 @@ function StringsEn(){ this.normal = "Normal" this.hard = "Hard" this.oni = "Extreme" + this.songBranch = "Diverge Notes" this.sessionStart = "Begin an Online Session!" this.sessionEnd = "End Online Session" this.loading = "Loading..." @@ -154,6 +161,11 @@ function StringsEn(){ this.good = "GOOD" this.ok = "OK" this.bad = "BAD" + this.branch = { + "normal": "Normal", + "advanced": "Professional", + "master": "Master" + } this.pauseOptions = [ "Continue", "Retry", @@ -236,6 +248,7 @@ function StringsCn(){ this.normal = "普通" this.hard = "困难" this.oni = "魔王" + this.songBranch = "有谱面分歧" this.sessionStart = "开始在线会话!" this.sessionEnd = "结束在线会话" this.loading = "加载中..." @@ -255,6 +268,11 @@ function StringsCn(){ this.good = "良" this.ok = "可" this.bad = "不可" + this.branch = { + "normal": "一般谱面", + "advanced": "进阶谱面", + "master": "达人谱面" + } this.pauseOptions = [ "继续演奏", "从头开始", @@ -337,6 +355,7 @@ function StringsTw(){ this.normal = "普通" this.hard = "困難" this.oni = "魔王" + this.songBranch = "有譜面分歧" this.sessionStart = "開始多人模式!" this.sessionEnd = "結束多人模式" this.loading = "讀取中..." @@ -356,6 +375,11 @@ function StringsTw(){ this.good = "良" this.ok = "可" this.bad = "不可" + this.branch = { + "normal": "一般譜面", + "advanced": "進階譜面", + "master": "達人譜面" + } this.pauseOptions = [ "繼續演奏", "從頭開始", @@ -438,6 +462,7 @@ function StringsKo(){ this.normal = "보통" this.hard = "어려움" this.oni = "귀신" + this.songBranch = "악보 분기 있습니다" this.sessionStart = "온라인 세션 시작!" this.sessionEnd = "온라인 세션 끝내기" this.loading = "로딩 중..." @@ -457,6 +482,11 @@ function StringsKo(){ this.good = "얼쑤" this.ok = "좋다" this.bad = "에구" + this.branch = { + "normal": "보통 악보", + "advanced": "현인 악보", + "master": "달인 악보" + } this.pauseOptions = [ "연주 계속하기", "처음부터 다시", diff --git a/public/src/js/titlescreen.js b/public/src/js/titlescreen.js index 642aab9..9f95cdc 100644 --- a/public/src/js/titlescreen.js +++ b/public/src/js/titlescreen.js @@ -18,7 +18,11 @@ class Titlescreen{ this.setLang(allStrings[this.lang], true) if(songId){ - this.goNext() + if(localStorage.getItem("tutorial") === "true"){ + new SongSelect(false, false, this.touched, this.songId) + }else{ + new Tutorial(false, this.songId) + } }else{ this.addLangs() diff --git a/public/src/js/view.js b/public/src/js/view.js index fa0c534..a50bf13 100644 --- a/public/src/js/view.js +++ b/public/src/js/view.js @@ -75,6 +75,33 @@ this.gogoTime = 0 this.drumroll = [] this.touchEvents = 0 + if(this.controller.parsedSongData.branches){ + this.branch = "normal" + this.branchAnimate = { + ms: -Infinity, + fromBranch: "normal" + } + this.branchMap = { + "normal": { + "bg": "rgba(0, 0, 0, 0)", + "text": "#d3d3d3", + "stroke": "#393939", + "shadow": "#000" + }, + "advanced": { + "bg": "rgba(29, 129, 189, 0.4)", + "text": "#94d7e7", + "stroke": "#315973", + "shadow": "#082031" + }, + "master": { + "bg": "rgba(230, 29, 189, 0.4)", + "text": "#f796ef", + "stroke": "#7e2e6e", + "shadow": "#3e0836" + } + } + } this.beatInterval = this.controller.parsedSongData.beatInfo.beatInterval this.font = strings.font @@ -85,6 +112,7 @@ this.titleCache = new CanvasCache() this.comboCache = new CanvasCache() this.pauseCache = new CanvasCache() + this.branchCache = new CanvasCache() this.multiplayer = this.controller.multiplayer @@ -132,7 +160,8 @@ } this.setDonBg() - this.lastMousemove = this.controller.game.getAccurateTime() + this.startTime = this.controller.game.getAccurateTime() + this.lastMousemove = this.startTime pageEvents.mouseAdd(this, this.onmousemove.bind(this)) this.refresh() @@ -162,6 +191,7 @@ } var ratio = (ratioX < ratioY ? ratioX : ratioY) + var resized = false if(this.winW !== winW || this.winH !== winH){ this.winW = winW this.winH = winH @@ -181,6 +211,7 @@ } this.fillComboCache() this.setDonBgHeight() + resized = true }else if(this.controller.game.paused && !document.hasFocus()){ return }else if(this.multiplayer !== 2){ @@ -669,7 +700,7 @@ } ctx.restore() - // Bar pressed keys + // Branch background var keyTime = this.controller.getKeyTime() var sound = keyTime["don"] > keyTime["ka"] ? "don" : "ka" var padding = this.slotPos.paddingLeft @@ -677,12 +708,78 @@ var barY = this.slotPos.y - 65 * mul var barH = 130 * mul + if(this.branchAnimate && ms <= this.branchAnimate.ms + 300){ + var alpha = Math.max(0, (ms - this.branchAnimate.ms) / 300) + ctx.globalAlpha = 1 - alpha + ctx.fillStyle = this.branchMap[this.branchAnimate.fromBranch].bg + ctx.fillRect(padding, barY, winW - padding, barH) + ctx.globalAlpha = alpha + } + if(this.branch){ + ctx.fillStyle = this.branchMap[this.branch].bg + ctx.fillRect(padding, barY, winW - padding, barH) + ctx.globalAlpha = 1 + } + + // Current branch text + if(this.branch){ + if(resized){ + this.fillBranchCache() + } + var textW = Math.floor(260 * mul) + var textH = Math.floor(barH) + var textX = winW - textW + var oldOffset = 0 + var newOffset = 0 + var elapsed = ms - this.startTime + if(elapsed < 250){ + textX = winW + }else if(elapsed < 500){ + textX += (1 - this.draw.easeOutBack((elapsed - 250) / 250)) * textW + } + if(this.branchAnimate && ms - this.branchAnimate.ms < 310 && ms >= this.branchAnimate.ms){ + var fromBranch = this.branchAnimate.fromBranch + var elapsed = ms - this.branchAnimate.ms + var reverse = fromBranch === "master" || fromBranch === "advanced" && this.branch === "normal" ? -1 : 1 + if(elapsed < 65){ + oldOffset = elapsed / 65 * 12 * mul * reverse + ctx.globalAlpha = 1 + var newAlpha = 0 + }else if(elapsed < 215){ + var animPoint = (elapsed - 65) / 150 + oldOffset = (12 - animPoint * 48) * mul * reverse + newOffset = (36 - animPoint * 48) * mul * reverse + ctx.globalAlpha = this.draw.easeIn(1 - animPoint) + var newAlpha = this.draw.easeIn(animPoint) + }else{ + newOffset = (1 - (elapsed - 215) / 95) * -12 * mul * reverse + ctx.globalAlpha = 0 + var newAlpha = 1 + } + this.branchCache.get({ + ctx: ctx, + x: textX, y: barY + oldOffset, + w: textW, h: textH, + id: fromBranch + }) + ctx.globalAlpha = newAlpha + } + this.branchCache.get({ + ctx: ctx, + x: textX, y: barY + newOffset, + w: textW, h: textH, + id: this.branch + }) + ctx.globalAlpha = 1 + } + + // Go go time background if(this.gogoTime || ms <= this.gogoTimeStarted + 100){ var grd = ctx.createLinearGradient(padding, 0, winW, 0) - grd.addColorStop(0, "#512a2c") - grd.addColorStop(0.46, "#6f2a2d") - grd.addColorStop(0.76, "#8a4763") - grd.addColorStop(1, "#2c2a2c") + grd.addColorStop(0, "rgba(255, 0, 0, 0.16)") + grd.addColorStop(0.45, "rgba(255, 0, 0, 0.28)") + grd.addColorStop(0.77, "rgba(255, 83, 157, 0.4)") + grd.addColorStop(1, "rgba(255, 83, 157, 0)") ctx.fillStyle = grd if(!this.touchEnabled){ var alpha = Math.min(100, ms - this.gogoTimeStarted) / 100 @@ -693,6 +790,8 @@ } ctx.fillRect(padding, barY, winW - padding, barH) } + + // Bar pressed keys if(keyTime[sound] > ms - 130){ var gradients = { "don": "255, 0, 0", @@ -790,6 +889,7 @@ ctx.lineWidth = 7 * mul ctx.textAlign = "center" ctx.miterLimit = 1 + ctx.strokeStyle = "#000" ctx.strokeText(strings.combo, comboX, comboTextY) ctx.miterLimit = 10 ctx.fillText(strings.combo, comboX, comboTextY) @@ -1102,15 +1202,23 @@ var timeForDistance = this.posToMs(distanceForCircle, measure.speed) var startingTime = measure.ms - timeForDistance var finishTime = measure.ms + this.posToMs(this.slotPos.x - this.slotPos.paddingLeft + 3, measure.speed) - if(ms >= startingTime && ms <= finishTime){ + if(measure.visible && (!measure.branch || measure.branch.active) && ms >= startingTime && ms <= finishTime){ var measureX = this.slotPos.x + this.msToPos(measure.ms - ms, measure.speed) - this.ctx.strokeStyle = "#bdbdbd" + this.ctx.strokeStyle = measure.branchFirst ? "#ff0" : "#bdbdbd" this.ctx.lineWidth = 3 this.ctx.beginPath() this.ctx.moveTo(measureX, measureY) this.ctx.lineTo(measureX, measureY + measureH) this.ctx.stroke() } + if(this.multiplayer !== 2 && ms >= measure.ms && measure.nextBranch && !measure.viewChecked && measure.gameChecked){ + measure.viewChecked = true + if(measure.nextBranch.active !== this.branch){ + this.branchAnimate.ms = ms + this.branchAnimate.fromBranch = this.branch + } + this.branch = measure.nextBranch.active + } }) } updateNoteFaces(){ @@ -1137,21 +1245,21 @@ for(var i = circles.length; i--;){ var circle = circles[i] - var speed = circle.getSpeed() + var speed = circle.speed var timeForDistance = this.posToMs(distanceForCircle + this.slotPos.size / 2, speed) - var startingTime = circle.getMS() - timeForDistance - var finishTime = circle.getEndTime() + this.posToMs(this.slotPos.x - this.slotPos.paddingLeft + this.slotPos.size * 2, speed) + var startingTime = circle.ms - timeForDistance + var finishTime = circle.endTime + this.posToMs(this.slotPos.x - this.slotPos.paddingLeft + this.slotPos.size * 2, speed) - if(circle.getPlayed() <= 0 || circle.getScore() === 0){ - if(ms >= startingTime && ms <= finishTime && circle.getPlayed() !== -1){ + if(circle.isPlayed <= 0 || circle.score === 0){ + if((!circle.branch || circle.branch.active) && ms >= startingTime && ms <= finishTime && circle.isPlayed !== -1){ this.drawCircle(circle) } - }else if(!circle.isAnimated()){ + }else if(!circle.animating){ // Start animation to gauge circle.animate(ms) } - if(ms >= circle.ms && !circle.gogoChecked){ + if(ms >= circle.ms && !circle.gogoChecked && (!circle.branch || circle.branch.active)){ if(this.gogoTime != circle.gogoTime){ this.toggleGogoTime(circle) } @@ -1165,9 +1273,9 @@ for(var i = 0; i < circles.length; i++){ var circle = circles[i] - if(circle.isAnimated()){ + if(circle.animating){ - var animT = circle.getAnimT() + var animT = circle.animT if(ms < animT + 490){ if(circle.fixedPos){ @@ -1183,7 +1291,7 @@ var pos = this.animateBezier[3] this.drawCircle(circle, pos, (ms - animT - 490) / 160) }else{ - circle.endAnimation() + circle.animationEnded = true } } } @@ -1211,13 +1319,13 @@ var lyricsSize = 20 * mul var fill, size, faceID - var type = circle.getType() + var type = circle.type var ms = this.getMS() - var circleMs = circle.getMS() - var endTime = circle.getEndTime() - var animated = circle.isAnimated() - var speed = circle.getSpeed() - var played = circle.getPlayed() + var circleMs = circle.ms + var endTime = circle.endTime + var animated = circle.animating + var speed = circle.speed + var played = circle.isPlayed var drumroll = 0 var endX = 0 @@ -1323,9 +1431,9 @@ ctx.fill() ctx.globalAlpha = 1 } - if(!circle.isAnimated()){ + if(!circle.animating){ // Text - var text = circle.getText() + var text = circle.text var textX = circlePos.x var textY = circlePos.y + 83 * mul ctx.font = lyricsSize + "px Kozuka, Microsoft YaHei, sans-serif" @@ -1436,6 +1544,37 @@ }) this.globalAlpha = 1 } + fillBranchCache(){ + var mul = this.slotPos.size / 106 + var textW = Math.floor(260 * mul) + var barH = Math.floor(130 * mul) + var branchNames = this.controller.game.branchNames + var textX = textW - 33 * mul + var textY = 63 * mul + var fontSize = (strings.id === "en" ? 33 : (strings.id === "ko" ? 38 : 43)) * mul + this.branchCache.resize((textW + 1), (barH + 1) * 3, this.ratio) + for(var i in branchNames){ + this.branchCache.set({ + w: textW, + h: barH, + id: branchNames[i] + }, ctx => { + var currentMap = this.branchMap[branchNames[i]] + ctx.font = this.draw.bold(this.font) + fontSize + "px " + this.font + ctx.lineJoin = "round" + ctx.miterLimit = 1 + ctx.textAlign = "right" + ctx.textBaseline = "middle" + ctx.lineWidth = 8 * mul + ctx.strokeStyle = currentMap.shadow + ctx.strokeText(strings.branch[branchNames[i]], textX, textY + 4 * mul) + ctx.strokeStyle = currentMap.stroke + ctx.strokeText(strings.branch[branchNames[i]], textX, textY) + ctx.fillStyle = currentMap.text + ctx.fillText(strings.branch[branchNames[i]], textX, textY) + }) + } + } toggleGogoTime(circle){ this.gogoTime = circle.gogoTime this.gogoTimeStarted = circle.ms @@ -1466,7 +1605,7 @@ if(this.gogoTime){ var circles = this.controller.parsedSongData.circles var lastCircle = circles[circles.length - 1] - var endTime = lastCircle.getEndTime() + 3000 + var endTime = lastCircle.endTime + 3000 if(ms >= endTime){ this.toggleGogoTime({ gogoTime: 0, diff --git a/public/src/views/debug.html b/public/src/views/debug.html index eba64ac..dd37b44 100644 --- a/public/src/views/debug.html +++ b/public/src/views/debug.html @@ -9,6 +9,17 @@
x-+
+
+
Branch:
+
+ x +
+
diff --git a/server.py b/server.py index b4b65ca..aafcad4 100644 --- a/server.py +++ b/server.py @@ -185,6 +185,7 @@ async def connection(ws, path): if "other_user" in user and "ws" in user["other_user"]: if type == "note"\ or type == "drumroll"\ + or type == "branch"\ or type == "gameresults": await user["other_user"]["ws"].send(msgobj(type, value)) elif type == "songsel" and user["session"]: