Merge pull request #136 from bui/parsetja-add-branches

ParseTja: Add branches
This commit is contained in:
Bui 2019-02-20 21:38:40 +00:00 committed by GitHub
commit 13ca5e368b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 673 additions and 206 deletions

View File

@ -44,7 +44,8 @@
box-sizing: border-box; box-sizing: border-box;
} }
#debug .input-slider{ #debug .input-slider,
#debug .select{
display: flex; display: flex;
width: 100%; width: 100%;
height: 30px; height: 30px;
@ -59,7 +60,8 @@
padding: 2px 4px; padding: 2px 4px;
text-align: center; text-align: center;
} }
#debug .input-slider>span{ #debug .input-slider>span,
#debug .select>span{
display: block; display: block;
width: 10%; width: 10%;
height: 100%; height: 100%;
@ -70,10 +72,19 @@
line-height: 2em; line-height: 2em;
cursor: pointer; cursor: pointer;
} }
#debug .input-slider>span:hover{ #debug .input-slider>span:hover,
#debug .select>span:hover{
opacity: 1; opacity: 1;
background: #333; 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{ #debug label{
display: block; display: block;
@ -111,6 +122,7 @@
margin-left: 3px; margin-left: 3px;
} }
#debug .autoplay-label{ #debug .autoplay-label,
#debug .branch-hide{
display: none; display: none;
} }

View File

@ -268,6 +268,9 @@
easeOut(pos){ easeOut(pos){
return Math.sin(Math.PI / 2 * pos) return Math.sin(Math.PI / 2 * pos)
} }
easeOutBack(pos){
return Math.sin(Math.PI / 1.74 * pos) * 1.03
}
easeInOut(pos){ easeInOut(pos){
return (Math.cos(Math.PI * pos) - 1) / -2 return (Math.cos(Math.PI * pos) - 1) / -2
} }
@ -572,7 +575,7 @@
} }
if(symbol.ura){ if(symbol.ura){
ctx.font = (30 * mul) + "px Meiryo, sans-serif" ctx.font = (30 * mul) + "px Meiryo, sans-serif"
ctx.textBaseline = "center" ctx.textBaseline = "middle"
ctx.beginPath() ctx.beginPath()
ctx.arc(currentX, currentY + (17 * mul), (18 * mul), 0, Math.PI * 2) ctx.arc(currentX, currentY + (17 * mul), (18 * mul), 0, Math.PI * 2)
if(action === "stroke"){ if(action === "stroke"){
@ -581,7 +584,7 @@
}else if(action === "fill"){ }else if(action === "fill"){
ctx.strokeStyle = config.fill ctx.strokeStyle = config.fill
ctx.lineWidth = 2.5 * mul ctx.lineWidth = 2.5 * mul
ctx.fillText(symbol.text, currentX, currentY) ctx.fillText(symbol.text, currentX, currentY + (17 * mul))
} }
ctx.stroke() ctx.stroke()
}else{ }else{
@ -788,7 +791,7 @@
} }
if(symbol.ura){ if(symbol.ura){
ctx.font = (30 * mul) + "px Meiryo, sans-serif" ctx.font = (30 * mul) + "px Meiryo, sans-serif"
ctx.textBaseline = "center" ctx.textBaseline = "middle"
ctx.beginPath() ctx.beginPath()
ctx.arc(currentX, currentY + (17 * mul), (18 * mul), 0, Math.PI * 2) ctx.arc(currentX, currentY + (17 * mul), (18 * mul), 0, Math.PI * 2)
if(action === "strokeText"){ if(action === "strokeText"){
@ -797,7 +800,7 @@
}else if(action === "fillText"){ }else if(action === "fillText"){
ctx.strokeStyle = layer.fill ctx.strokeStyle = layer.fill
ctx.lineWidth = 2.5 * mul ctx.lineWidth = 2.5 * mul
ctx.fillText(symbol.text, currentX, currentY) ctx.fillText(symbol.text, currentX, currentY + (17 * mul))
} }
ctx.stroke() ctx.stroke()
}else{ }else{
@ -1167,6 +1170,7 @@
var firstTop = config.multiplayer ? 0 : 30 var firstTop = config.multiplayer ? 0 : 30
var secondTop = config.multiplayer ? 0 : 8 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 cleared = config.percentage - 1 / 50 >= config.clear
var gaugeW = 14 * 50 var gaugeW = 14 * 50

View File

@ -1,6 +1,5 @@
class Circle{ class Circle{
constructor(config){ constructor(config){
// id, ms, type, text, speed, endTime, requiredHits
this.id = config.id this.id = config.id
this.ms = config.start this.ms = config.start
this.originalMS = this.ms this.originalMS = this.ms
@ -23,38 +22,13 @@ class Circle{
this.gogoChecked = false this.gogoChecked = false
this.beatMS = config.beatMS this.beatMS = config.beatMS
this.fixedPos = config.fixedPos this.fixedPos = config.fixedPos
} this.branch = config.branch
getMS(){ this.section = config.section
return this.ms
}
getEndTime(){
return this.endTime
}
getType(){
return this.type
}
getLastFrame(){
return this.lastFrame
} }
animate(ms){ animate(ms){
this.animating = true this.animating = true
this.animT = ms 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){ played(score, big){
this.score = score this.score = score
this.isPlayed = score <= 0 ? score - 1 : (big ? 2 : 1) this.isPlayed = score <= 0 ? score - 1 : (big ? 2 : 1)
@ -65,16 +39,4 @@ class Circle{
this.timesKa++ this.timesKa++
} }
} }
getScore(){
return this.score
}
getID(){
return this.id
}
getText(){
return this.text
}
getSpeed(){
return this.speed
}
} }

View File

@ -8,15 +8,19 @@ class Debug{
this.debugDiv.innerHTML = assets.pages["debug"] this.debugDiv.innerHTML = assets.pages["debug"]
document.body.appendChild(this.debugDiv) document.body.appendChild(this.debugDiv)
this.titleDiv = this.debugDiv.getElementsByClassName("title")[0] this.titleDiv = this.byClass("title")
this.minimiseDiv = this.debugDiv.getElementsByClassName("minimise")[0] this.minimiseDiv = this.byClass("minimise")
this.offsetDiv = this.debugDiv.getElementsByClassName("offset")[0] this.offsetDiv = this.byClass("offset")
this.measureNumDiv = this.debugDiv.getElementsByClassName("measure-num")[0] this.measureNumDiv = this.byClass("measure-num")
this.restartCheckbox = this.debugDiv.getElementsByClassName("change-restart")[0] this.branchHideDiv = this.byClass("branch-hide")
this.autoplayLabel = this.debugDiv.getElementsByClassName("autoplay-label")[0] this.branchSelectDiv = this.byClass("branch-select")
this.autoplayCheckbox = this.debugDiv.getElementsByClassName("autoplay")[0] this.branchSelect = this.branchSelectDiv.getElementsByTagName("select")[0]
this.restartBtn = this.debugDiv.getElementsByClassName("restart-btn")[0] this.branchResetBtn = this.branchSelectDiv.getElementsByClassName("reset")[0]
this.exitBtn = this.debugDiv.getElementsByClassName("exit-btn")[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 this.moving = false
pageEvents.add(window, ["mousedown", "mouseup", "blur"], this.stopMove.bind(this)) 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.restartBtn, "click", this.restartSong.bind(this))
pageEvents.add(this.exitBtn, "click", this.clean.bind(this)) pageEvents.add(this.exitBtn, "click", this.clean.bind(this))
pageEvents.add(this.autoplayCheckbox, "change", this.toggleAutoplay.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 = new InputSlider(this.offsetDiv, -60, 60, 3)
this.offsetSlider.onchange(this.offsetChange.bind(this)) this.offsetSlider.onchange(this.offsetChange.bind(this))
@ -39,6 +45,9 @@ class Debug{
this.updateStatus() this.updateStatus()
pageEvents.send("debug") pageEvents.send("debug")
} }
byClass(name){
return this.debugDiv.getElementsByClassName(name)[0]
}
startMove(event){ startMove(event){
if(event.which === 1){ if(event.which === 1){
event.stopPropagation() event.stopPropagation()
@ -88,20 +97,28 @@ class Debug{
} }
updateStatus(){ updateStatus(){
if(debugObj.controller && !this.controller){ if(debugObj.controller && !this.controller){
this.controller = debugObj.controller
this.restartBtn.style.display = "block" this.restartBtn.style.display = "block"
this.autoplayLabel.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 var selectedSong = this.controller.selectedSong
this.defaultOffset = selectedSong.offset || 0 this.defaultOffset = selectedSong.offset || 0
if(this.songFolder === selectedSong.folder){ if(this.songFolder === selectedSong.folder){
this.offsetChange(this.offsetSlider.get(), true) this.offsetChange(this.offsetSlider.get(), true)
this.branchChange(null, true)
}else{ }else{
this.songFolder = selectedSong.folder this.songFolder = selectedSong.folder
this.offsetSlider.set(this.defaultOffset) 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) this.measureNumSlider.setMinMax(0, measures.length - 1)
if(this.measureNum && measures.length > this.measureNum){ if(this.measureNum && measures.length > this.measureNum){
var measureMS = measures[this.measureNum].ms var measureMS = measures[this.measureNum].ms
@ -128,6 +145,7 @@ class Debug{
if(this.controller && !debugObj.controller){ if(this.controller && !debugObj.controller){
this.restartBtn.style.display = "" this.restartBtn.style.display = ""
this.autoplayLabel.style.display = "" this.autoplayLabel.style.display = ""
this.branchHideDiv.style.display = ""
this.controller = null this.controller = null
} }
} }
@ -142,6 +160,11 @@ class Debug{
songData.measures.forEach(measure => { songData.measures.forEach(measure => {
measure.ms = measure.originalMS + offset measure.ms = measure.originalMS + offset
}) })
if(songData.branches){
songData.branches.forEach(branch => {
branch.ms = branch.originalMS + offset
})
}
if(this.restartCheckbox.checked && !noRestart){ if(this.restartCheckbox.checked && !noRestart){
this.restartSong() 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(){ clean(){
this.offsetSlider.clean() this.offsetSlider.clean()
this.measureNumSlider.clean()
pageEvents.remove(window, ["mousedown", "mouseup", "blur"]) pageEvents.remove(window, ["mousedown", "mouseup", "blur"])
pageEvents.mouseRemove(this) pageEvents.mouseRemove(this)
pageEvents.remove(this.titleDiv, "mousedown")
pageEvents.remove(this.title, "mousedown") pageEvents.remove(this.title, "mousedown")
pageEvents.remove(this.minimiseDiv, "click") pageEvents.remove(this.minimiseDiv, "click")
pageEvents.remove(this.restartBtn, "click") pageEvents.remove(this.restartBtn, "click")
pageEvents.remove(this.exitBtn, "click") pageEvents.remove(this.exitBtn, "click")
pageEvents.remove(this.autoplayCheckbox, "change") pageEvents.remove(this.autoplayCheckbox, "change")
pageEvents.remove(this.branchSelect, "change")
pageEvents.remove(this.branchResetBtn, "click")
delete this.titleDiv delete this.titleDiv
delete this.minimiseDiv delete this.minimiseDiv
delete this.offsetDiv delete this.offsetDiv
delete this.measureNumDiv delete this.measureNumDiv
delete this.branchHideDiv
delete this.branchSelectDiv
delete this.branchSelect
delete this.branchResetBtn
delete this.restartCheckbox delete this.restartCheckbox
delete this.autoplayLabel delete this.autoplayLabel
delete this.autoplayCheckbox delete this.autoplayCheckbox

View File

@ -19,8 +19,8 @@ class Game{
difficulty: this.rules.difficulty difficulty: this.rules.difficulty
} }
this.HPGain = 100 / this.songData.circles.filter(circle => { this.HPGain = 100 / this.songData.circles.filter(circle => {
var type = circle.getType() var type = circle.type
return type === "don" || type === "ka" || type === "daiDon" || type === "daiKa" return (type === "don" || type === "ka" || type === "daiDon" || type === "daiKa") && (!circle.branch || circle.branch.active)
}).length }).length
this.paused = false this.paused = false
this.started = false this.started = false
@ -28,6 +28,8 @@ class Game{
this.musicFadeOut = 0 this.musicFadeOut = 0
this.fadeOutStarted = false this.fadeOutStarted = false
this.currentTimingPoint = 0 this.currentTimingPoint = 0
this.branchNames = ["normal", "advanced", "master"]
this.resetSection()
assets.songs.forEach(song => { assets.songs.forEach(song => {
if(song.id == selectedSong.folder){ if(song.id == selectedSong.folder){
@ -69,17 +71,18 @@ class Game{
} }
updateCirclesStatus(){ updateCirclesStatus(){
var nextSet = false var nextSet = false
var ms = this.elapsedTime
var circles = this.songData.circles var circles = this.songData.circles
var startIndex = this.currentCircle === 0 ? 0 : this.currentCircle - 1 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] var circle = circles[i]
if(!circle.getPlayed()){ if(circle && (!circle.branch || circle.branch.active) && !circle.isPlayed){
var ms = this.elapsedTime var type = circle.type
var type = circle.getType()
var drumrollNotes = type === "balloon" || type === "drumroll" || type === "daiDrumroll" 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){ if(drumrollNotes && !circle.rendaPlayed && ms < endTime){
circle.rendaPlayed = true circle.rendaPlayed = true
if(this.rules.difficulty === "easy"){ if(this.rules.difficulty === "easy"){
@ -97,11 +100,14 @@ class Game{
if(ms > endTime){ if(ms > endTime){
if(!this.controller.autoPlayEnabled){ if(!this.controller.autoPlayEnabled){
if(drumrollNotes){ if(drumrollNotes){
if(circle.section && circle.timesHit === 0){
this.resetSection()
}
circle.played(-1, false) circle.played(-1, false)
this.updateCurrentCircle() this.updateCurrentCircle()
if(this.controller.multiplayer === 1){ if(this.controller.multiplayer === 1){
var value = { var value = {
pace: (ms - circle.getMS()) / circle.timesHit pace: (ms - circle.ms) / circle.timesHit
} }
if(type === "drumroll" || type === "daiDrumroll"){ if(type === "drumroll" || type === "daiDrumroll"){
value.kaAmount = circle.timesKa / circle.timesHit value.kaAmount = circle.timesKa / circle.timesHit
@ -109,8 +115,12 @@ class Game{
p2.send("drumroll", value) p2.send("drumroll", value)
} }
}else{ }else{
if(circle.section){
this.resetSection()
}
var currentScore = 0 var currentScore = 0
circle.played(-1, type === "daiDon" || type === "daiKa") circle.played(-1, type === "daiDon" || type === "daiKa")
this.sectionNotes.push(0)
this.controller.displayScore(currentScore, true) this.controller.displayScore(currentScore, true)
this.updateCurrentCircle() this.updateCurrentCircle()
this.updateCombo(currentScore) this.updateCombo(currentScore)
@ -126,6 +136,71 @@ class Game{
nextSet = true nextSet = true
this.currentCircle = i 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){ checkKey(keyCodes, circle, check){
if(circle && !circle.getPlayed()){ if(circle && !circle.isPlayed){
if(!this.checkScore(circle, check)){ if(!this.checkScore(circle, check)){
return return
} }
@ -171,7 +246,7 @@ class Game{
} }
checkScore(circle, check){ checkScore(circle, check){
var ms = this.elapsedTime var ms = this.elapsedTime
var type = circle.getType() var type = circle.type
var keysDon = check === "don" || check === "daiDon" var keysDon = check === "don" || check === "daiDon"
var keysKa = check === "ka" || check === "daiKa" var keysKa = check === "ka" || check === "daiKa"
@ -182,7 +257,7 @@ class Game{
var keyTime = this.controller.getKeyTime() var keyTime = this.controller.getKeyTime()
var currentTime = keysDon ? keyTime["don"] : keyTime["ka"] var currentTime = keysDon ? keyTime["don"] : keyTime["ka"]
var relative = currentTime - circle.getMS() var relative = currentTime - circle.ms
if(typeDon || typeKa){ if(typeDon || typeKa){
if(-this.rules.bad >= relative || relative >= this.rules.bad){ if(-this.rules.bad >= relative || relative >= this.rules.bad){
@ -219,10 +294,14 @@ class Game{
this.updateCombo(score) this.updateCombo(score)
this.updateGlobalScore(score, typeDai && keyDai ? 2 : 1, circle.gogoTime) this.updateGlobalScore(score, typeDai && keyDai ? 2 : 1, circle.gogoTime)
this.updateCurrentCircle() 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 = { var value = {
score: score, score: score,
ms: circle.getMS() - currentTime, ms: circle.ms - currentTime,
dai: typeDai ? keyDai ? 2 : 1 : 0 dai: typeDai ? keyDai ? 2 : 1 : 0
} }
if((!keysDon || !typeDon) && (!keysKa || !typeKa)){ if((!keysDon || !typeDon) && (!keysKa || !typeKa)){
@ -231,12 +310,12 @@ class Game{
p2.send("note", value) p2.send("note", value)
} }
}else{ }else{
if(circle.getMS() > currentTime || currentTime > circle.getEndTime()){ if(circle.ms > currentTime || currentTime > circle.endTime){
return true return true
} }
if(keysDon && type === "balloon"){ if(keysDon && type === "balloon"){
this.checkBalloon(circle) this.checkBalloon(circle)
if(check === "daiDon" && !circle.getPlayed()){ if(check === "daiDon" && !circle.isPlayed){
this.checkBalloon(circle) this.checkBalloon(circle)
} }
}else if((keysDon || keysKa) && (type === "drumroll" || type === "daiDrumroll")){ }else if((keysDon || keysKa) && (type === "drumroll" || type === "daiDrumroll")){
@ -256,7 +335,7 @@ class Game{
circle.played(score) circle.played(score)
if(this.controller.multiplayer == 1){ if(this.controller.multiplayer == 1){
p2.send("drumroll", { p2.send("drumroll", {
pace: (this.elapsedTime - circle.getMS()) / circle.timesHit pace: (this.elapsedTime - circle.ms) / circle.timesHit
}) })
} }
}else{ }else{
@ -264,16 +343,20 @@ class Game{
circle.hit() circle.hit()
} }
this.globalScore.drumroll++ this.globalScore.drumroll++
this.sectionDrumroll++
this.globalScore.points += score this.globalScore.points += score
this.view.setDarkBg(false) this.view.setDarkBg(false)
} }
checkDrumroll(circle, keysKa){ checkDrumroll(circle, keysKa){
var ms = this.elapsedTime var ms = this.elapsedTime
var dai = circle.getType() === "daiDrumroll" var dai = circle.type === "daiDrumroll"
var score = 100 var score = 100
if(circle.section && circle.timesHit === 0){
this.resetSection()
}
circle.hit(keysKa) circle.hit(keysKa)
var keyTime = this.controller.getKeyTime() var keyTime = this.controller.getKeyTime()
if(circle.getType() === "drumroll"){ if(circle.type === "drumroll"){
var sound = keyTime["don"] > keyTime["ka"] ? "don" : "ka" var sound = keyTime["don"] > keyTime["ka"] ? "don" : "ka"
}else{ }else{
var sound = keyTime["don"] > keyTime["ka"] ? "daiDon" : "daiKa" var sound = keyTime["don"] > keyTime["ka"] ? "daiDon" : "daiKa"
@ -291,6 +374,7 @@ class Game{
circleAnim.animate(ms) circleAnim.animate(ms)
this.view.drumroll.push(circleAnim) this.view.drumroll.push(circleAnim)
this.globalScore.drumroll++ this.globalScore.drumroll++
this.sectionDrumroll++
this.globalScore.points += score * (dai ? 2 : 1) this.globalScore.points += score * (dai ? 2 : 1)
this.view.setDarkBg(false) this.view.setDarkBg(false)
} }
@ -298,11 +382,11 @@ class Game{
var ms = this.elapsedTime var ms = this.elapsedTime
if(!this.lastCircle){ if(!this.lastCircle){
var circles = this.songData.circles var circles = this.songData.circles
this.lastCircle = circles[circles.length - 1].getEndTime() this.lastCircle = circles[circles.length - 1].endTime
if(this.controller.multiplayer){ if(this.controller.multiplayer){
var syncWith = this.controller.syncWith var syncWith = this.controller.syncWith
var syncCircles = syncWith.game.songData.circles var syncCircles = syncWith.game.songData.circles
var syncLastCircle = syncCircles[syncCircles.length - 1].getEndTime() var syncLastCircle = syncCircles[syncCircles.length - 1].endTime
if(syncLastCircle > this.lastCircle){ if(syncLastCircle > this.lastCircle){
this.lastCircle = syncLastCircle this.lastCircle = syncLastCircle
} }
@ -410,7 +494,10 @@ class Game{
return this.songData.circles return this.songData.circles
} }
updateCurrentCircle(){ updateCurrentCircle(){
this.currentCircle++ var circles = this.songData.circles
do{
var circle = circles[++this.currentCircle]
}while(circle && circle.branch && !circle.branch.active)
} }
getCurrentCircle(){ getCurrentCircle(){
return this.currentCircle return this.currentCircle
@ -464,4 +551,59 @@ class Game{
} }
this.globalScore.points += Math.floor(score * multiplier / 10) * 10 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
}
} }

View File

@ -199,7 +199,7 @@
songObj.subtitle = songObj.subtitle_en = subtitle songObj.subtitle = songObj.subtitle_en = subtitle
songObj.preview = meta.demostart ? Math.floor(meta.demostart * 1000) : 0 songObj.preview = meta.demostart ? Math.floor(meta.demostart * 1000) : 0
if(meta.level){ if(meta.level){
songObj.stars[this.courseTypes[diff]] = meta.level songObj.stars[this.courseTypes[diff]] = meta.level + (meta.branch ? " B" : "")
} }
if(meta.wave){ if(meta.wave){
songObj.music = this.otherFiles[dir + meta.wave.toLowerCase()] songObj.music = this.otherFiles[dir + meta.wave.toLowerCase()]

View File

@ -199,8 +199,8 @@ class Keyboard{
if( if(
sound === "don" sound === "don"
&& circle && circle
&& !circle.getPlayed() && !circle.isPlayed
&& circle.getType() === "balloon" && circle.type === "balloon"
&& circle.requiredHits - circle.timesHit <= 1 && circle.requiredHits - circle.timesHit <= 1
){ ){
this.controller.playSound("se_balloon") this.controller.playSound("se_balloon")

View File

@ -106,7 +106,9 @@ pageEvents.keyAdd(debugObj, "all", "down", event => {
}else if(debugObj.state === "minimised"){ }else if(debugObj.state === "minimised"){
debugObj.debug.restore() debugObj.debug.restore()
}else{ }else{
try{
debugObj.debug = new Debug() debugObj.debug = new Debug()
}catch(e){}
} }
} }
if(event.keyCode === 82 && debugObj.debug && debugObj.controller){ if(event.keyCode === 82 && debugObj.debug && debugObj.controller){

View File

@ -6,12 +6,15 @@ class Mekadon{
this.lastHit = -Infinity this.lastHit = -Infinity
} }
play(circle){ play(circle){
var type = circle.getType() var type = circle.type
if((type === "balloon" || type === "drumroll" || type === "daiDrumroll") && this.getMS() > circle.getEndTime()){ if((type === "balloon" || type === "drumroll" || type === "daiDrumroll") && this.getMS() > circle.endTime){
if(circle.section && circle.timesHit === 0){
this.game.resetSection()
}
circle.played(-1, false) circle.played(-1, false)
this.game.updateCurrentCircle() this.game.updateCurrentCircle()
} }
type = circle.getType() type = circle.type
if(type === "balloon"){ if(type === "balloon"){
this.playDrumrollAt(circle, 0, 30) this.playDrumrollAt(circle, 0, 30)
}else if(type === "drumroll" || type === "daiDrumroll"){ }else if(type === "drumroll" || type === "daiDrumroll"){
@ -21,7 +24,7 @@ class Mekadon{
} }
} }
playAt(circle, ms, score, dai, reverse){ playAt(circle, ms, score, dai, reverse){
var currentMs = circle.getMS() - this.getMS() var currentMs = circle.ms - this.getMS()
if(ms > currentMs - 10){ if(ms > currentMs - 10){
return this.playNow(circle, score, dai, reverse) return this.playNow(circle, score, dai, reverse)
} }
@ -36,18 +39,19 @@ class Mekadon{
} }
} }
miss(circle){ miss(circle){
var currentMs = circle.getMS() - this.getMS() var currentMs = circle.ms - this.getMS()
if(0 >= currentMs - 10){ if(0 >= currentMs - 10){
this.controller.displayScore(0, true) this.controller.displayScore(0, true)
this.game.updateCurrentCircle() this.game.updateCurrentCircle()
this.game.updateCombo(0) this.game.updateCombo(0)
this.game.updateGlobalScore(0, 1, circle.gogoTime) this.game.updateGlobalScore(0, 1, circle.gogoTime)
this.game.sectionNotes.push(0)
return true return true
} }
} }
playNow(circle, score, dai, reverse){ playNow(circle, score, dai, reverse){
var kbd = this.controller.getBindings() var kbd = this.controller.getBindings()
var type = circle.getType() var type = circle.type
var keyDai = false var keyDai = false
var playDai = !dai || dai === 2 var playDai = !dai || dai === 2
var drumrollNotes = type === "balloon" || type === "drumroll" || type === "daiDrumroll" var drumrollNotes = type === "balloon" || type === "drumroll" || type === "daiDrumroll"
@ -55,7 +59,7 @@ class Mekadon{
if(drumrollNotes){ if(drumrollNotes){
var ms = this.getMS() var ms = this.getMS()
}else{ }else{
var ms = circle.getMS() var ms = circle.ms
} }
if(reverse){ if(reverse){
@ -95,6 +99,10 @@ class Mekadon{
this.game.updateGlobalScore(score, keyDai ? 2 : 1, circle.gogoTime) this.game.updateGlobalScore(score, keyDai ? 2 : 1, circle.gogoTime)
this.game.updateCurrentCircle() this.game.updateCurrentCircle()
circle.played(score, keyDai) 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 this.lastHit = ms
return true return true

View File

@ -109,6 +109,7 @@ class P2Connection{
this.dai = 2 this.dai = 2
this.kaAmount = 0 this.kaAmount = 0
this.results = false this.results = false
this.branch = "normal"
break break
case "gameend": case "gameend":
this.otherConnected = false this.otherConnected = false
@ -141,6 +142,10 @@ class P2Connection{
this.kaAmount = response.value.kaAmount this.kaAmount = response.value.kaAmount
} }
break break
case "branch":
this.branch = response.value
this.branchSet = false
break
case "session": case "session":
this.clearMessage("users") this.clearMessage("users")
this.otherConnected = true this.otherConnected = true
@ -161,10 +166,10 @@ class P2Connection{
} }
play(circle, mekadon){ play(circle, mekadon){
if(this.otherConnected || this.notes.length > 0){ if(this.otherConnected || this.notes.length > 0){
var type = circle.getType() var type = circle.type
var drumrollNotes = type === "balloon" || type === "drumroll" || type === "daiDrumroll" var drumrollNotes = type === "balloon" || type === "drumroll" || type === "daiDrumroll"
if(drumrollNotes && mekadon.getMS() > circle.getEndTime()){ if(drumrollNotes && mekadon.getMS() > circle.endTime){
circle.played(-1, false) circle.played(-1, false)
mekadon.game.updateCurrentCircle() mekadon.game.updateCurrentCircle()
} }
@ -177,7 +182,7 @@ class P2Connection{
var note = this.notes[0] var note = this.notes[0]
if(note.score >= 0){ if(note.score >= 0){
var dai = 1 var dai = 1
if(circle.getType() === "daiDon" || circle.getType() === "daiKa"){ if(circle.type === "daiDon" || circle.type === "daiKa"){
dai = this.dai dai = this.dai
} }
if(mekadon.playAt(circle, note.ms, note.score, dai, note.reverse)){ if(mekadon.playAt(circle, note.ms, note.score, dai, note.reverse)){

View File

@ -59,8 +59,10 @@
if(!(courseName in courses)){ if(!(courseName in courses)){
courses[courseName] = {} courses[courseName] = {}
} }
if(name !== "branch"){
courses[courseName][name] = currentCourse[name] courses[courseName][name] = currentCourse[name]
} }
}
courses[courseName].start = lineNum + 1 courses[courseName].start = lineNum + 1
courses[courseName].end = this.data.length courses[courseName].end = this.data.length
} }
@ -70,6 +72,8 @@
hasSong = true hasSong = true
courses[courseName].end = lineNum courses[courseName].end = lineNum
} }
}else if(name.startsWith("branchstart") && inSong){
courses[courseName].branch = true
} }
}else if(!inSong){ }else if(!inSong){
@ -128,9 +132,13 @@
var balloons = meta.balloon || [] var balloons = meta.balloon || []
var lastDrumroll = false var lastDrumroll = false
var branch = false var branch = false
var branchType var branchObj = {}
var branchPreference = "m" var currentBranch = false
var branchSettings = {}
var branchFirstMeasure = false
var sectionBegin = true
var currentMeasure = [] var currentMeasure = []
var firstNote = true var firstNote = true
@ -138,7 +146,6 @@
var circleID = 0 var circleID = 0
var pushMeasure = () => { var pushMeasure = () => {
if(barLine){
var note = currentMeasure[0] var note = currentMeasure[0]
if(note){ if(note){
var speed = note.bpm * note.scroll / 60 var speed = note.bpm * note.scroll / 60
@ -148,9 +155,12 @@
this.measures.push({ this.measures.push({
ms: ms, ms: ms,
originalMS: ms, originalMS: ms,
speed: speed speed: speed,
visible: barLine,
branch: currentBranch,
branchFirst: branchFirstMeasure
}) })
} branchFirstMeasure = false
if(currentMeasure.length){ if(currentMeasure.length){
for(var i = 0; i < currentMeasure.length; i++){ for(var i = 0; i < currentMeasure.length; i++){
var note = currentMeasure[i] var note = currentMeasure[i]
@ -182,7 +192,9 @@
gogoTime: note.gogo, gogoTime: note.gogo,
endTime: note.endTime, endTime: note.endTime,
requiredHits: note.requiredHits, requiredHits: note.requiredHits,
beatMS: 60000 / note.bpm beatMS: 60000 / note.bpm,
branch: currentBranch,
section: note.section
}) })
if(lastDrumroll === note){ if(lastDrumroll === note){
lastDrumroll = circleObj lastDrumroll = circleObj
@ -204,7 +216,6 @@
var line = line.slice(1).toLowerCase() var line = line.slice(1).toLowerCase()
var [name, value] = this.split(line, " ") var [name, value] = this.split(line, " ")
if(!branch || branch && branchType === branchPreference){
switch(name){ switch(name){
case "gogostart": case "gogostart":
gogo = true gogo = true
@ -213,14 +224,14 @@
gogo = false gogo = false
break break
case "bpmchange": case "bpmchange":
bpm = parseFloat(value) bpm = parseFloat(value) || bpm
break break
case "scroll": case "scroll":
scroll = parseFloat(value) scroll = parseFloat(value) || scroll
break break
case "measure": case "measure":
var [numerator, denominator] = value.split("/") var [numerator, denominator] = value.split("/")
measure = numerator / denominator * 4 measure = numerator / denominator * 4 || measure
break break
case "delay": case "delay":
ms += (parseFloat(value) || 0) * 1000 ms += (parseFloat(value) || 0) * 1000
@ -231,32 +242,87 @@
case "barlineoff": case "barlineoff":
barLine = false barLine = false
break break
}
}
switch(name){
case "branchstart": case "branchstart":
branch = true branch = true
branchType = "" currentBranch = false
branchFirstMeasure = true
branchSettings = {
ms: ms,
gogo: gogo,
bpm: bpm,
scroll: scroll,
sectionBegin: sectionBegin
}
value = value.split(",") value = value.split(",")
var forkType = value[0].toLowerCase() if(!this.branches){
if(forkType === "r" || parseFloat(value[2]) <= 100){ this.branches = []
branchPreference = "m" }
}else if(parseFloat(value[1]) <= 100){ var req = {
branchPreference = "e" advanced: parseFloat(value[1]) || 0,
master: parseFloat(value[2]) || 0
}
if(req.advanced > 0){
var active = req.master > 0 ? "normal" : "master"
}else{ }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 break
case "branchend": case "branchend":
case "section":
branch = false branch = false
currentBranch = false
break
case "section":
sectionBegin = true
if(branch && !currentBranch){
branchSettings.sectionBegin = true
}
break break
case "n": case "e": case "m": 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 break
} }
}else if(!branch || branch && branchType === branchPreference){ }else{
var string = line.split("") var string = line.split("")
@ -278,8 +344,10 @@
txt: type.txt, txt: type.txt,
gogo: gogo, gogo: gogo,
bpm: bpm, bpm: bpm,
scroll: scroll scroll: scroll,
section: sectionBegin
} }
sectionBegin = false
if(lastDrumroll){ if(lastDrumroll){
circleObj.endDrumroll = lastDrumroll circleObj.endDrumroll = lastDrumroll
lastDrumroll = false lastDrumroll = false
@ -293,15 +361,19 @@
txt: type.txt, txt: type.txt,
gogo: gogo, gogo: gogo,
bpm: bpm, bpm: bpm,
scroll: scroll scroll: scroll,
section: sectionBegin
} }
sectionBegin = false
if(lastDrumroll){ if(lastDrumroll){
if(symbol === "9"){ if(symbol === "9"){
currentMeasure.push({ currentMeasure.push({
endDrumroll: lastDrumroll, endDrumroll: lastDrumroll,
bpm: bpm, bpm: bpm,
scroll: scroll scroll: scroll,
section: sectionBegin
}) })
sectionBegin = false
lastDrumroll = false lastDrumroll = false
}else{ }else{
currentMeasure.push({ currentMeasure.push({
@ -327,8 +399,10 @@
currentMeasure.push({ currentMeasure.push({
endDrumroll: lastDrumroll, endDrumroll: lastDrumroll,
bpm: bpm, bpm: bpm,
scroll: scroll scroll: scroll,
section: sectionBegin
}) })
sectionBegin = false
lastDrumroll = false lastDrumroll = false
}else{ }else{
currentMeasure.push({ currentMeasure.push({
@ -359,6 +433,11 @@
lastDrumroll.originalEndTime = ms 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 return circles
} }
} }

View File

@ -351,7 +351,7 @@ class SongSelect{
down: code == 40 down: code == 40
// Down // Down
} }
if(key.cancel && event){ if(event && (code == 27 || code == 8)){
event.preventDefault() event.preventDefault()
} }
if(this.state.screen === "song"){ if(this.state.screen === "song"){
@ -1322,7 +1322,26 @@ class SongSelect{
outlineSize: currentUra ? this.songAsset.letterBorder : 0 outlineSize: currentUra ? this.songAsset.letterBorder : 0
}) })
}) })
var songStars = currentUra ? currentSong.stars[4] : currentSong.stars[i] 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++){ for(var j = 0; j < 10; j++){
if(songSel){ if(songSel){
var yPos = _y + 113 + j * 17 var yPos = _y + 113 + j * 17
@ -1345,6 +1364,7 @@ class SongSelect{
}) })
} }
} }
}
var currentDiff = this.selectedDiff - this.diffOptions.length var currentDiff = this.selectedDiff - this.diffOptions.length
if(this.selectedDiff === 4 + this.diffOptions.length){ if(this.selectedDiff === 4 + this.diffOptions.length){
currentDiff = 3 currentDiff = 3

View File

@ -86,7 +86,7 @@ class SoundGain{
this.volume = amount this.volume = amount
} }
setCrossfade(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){ fadeIn(duration, time, absolute){
this.fadeVolume(0, this.volume * this.volume, duration, time, absolute) this.fadeVolume(0, this.volume * this.volume, duration, time, absolute)

View File

@ -34,6 +34,7 @@
this.normal = "ふつう" this.normal = "ふつう"
this.hard = "むずかしい" this.hard = "むずかしい"
this.oni = "おに" this.oni = "おに"
this.songBranch = "譜面分岐あり"
this.sessionStart = "オンラインセッションを開始する!" this.sessionStart = "オンラインセッションを開始する!"
this.sessionEnd = "オンラインセッションを終了する" this.sessionEnd = "オンラインセッションを終了する"
this.loading = "ロード中..." this.loading = "ロード中..."
@ -53,6 +54,11 @@
this.good = "良" this.good = "良"
this.ok = "可" this.ok = "可"
this.bad = "不可" this.bad = "不可"
this.branch = {
"normal": "普通譜面",
"advanced": "玄人譜面",
"master": "達人譜面"
}
this.pauseOptions = [ this.pauseOptions = [
"演奏をつづける", "演奏をつづける",
"はじめからやりなおす", "はじめからやりなおす",
@ -135,6 +141,7 @@ function StringsEn(){
this.normal = "Normal" this.normal = "Normal"
this.hard = "Hard" this.hard = "Hard"
this.oni = "Extreme" this.oni = "Extreme"
this.songBranch = "Diverge Notes"
this.sessionStart = "Begin an Online Session!" this.sessionStart = "Begin an Online Session!"
this.sessionEnd = "End Online Session" this.sessionEnd = "End Online Session"
this.loading = "Loading..." this.loading = "Loading..."
@ -154,6 +161,11 @@ function StringsEn(){
this.good = "GOOD" this.good = "GOOD"
this.ok = "OK" this.ok = "OK"
this.bad = "BAD" this.bad = "BAD"
this.branch = {
"normal": "Normal",
"advanced": "Professional",
"master": "Master"
}
this.pauseOptions = [ this.pauseOptions = [
"Continue", "Continue",
"Retry", "Retry",
@ -236,6 +248,7 @@ function StringsCn(){
this.normal = "普通" this.normal = "普通"
this.hard = "困难" this.hard = "困难"
this.oni = "魔王" this.oni = "魔王"
this.songBranch = "有谱面分歧"
this.sessionStart = "开始在线会话!" this.sessionStart = "开始在线会话!"
this.sessionEnd = "结束在线会话" this.sessionEnd = "结束在线会话"
this.loading = "加载中..." this.loading = "加载中..."
@ -255,6 +268,11 @@ function StringsCn(){
this.good = "良" this.good = "良"
this.ok = "可" this.ok = "可"
this.bad = "不可" this.bad = "不可"
this.branch = {
"normal": "一般谱面",
"advanced": "进阶谱面",
"master": "达人谱面"
}
this.pauseOptions = [ this.pauseOptions = [
"继续演奏", "继续演奏",
"从头开始", "从头开始",
@ -337,6 +355,7 @@ function StringsTw(){
this.normal = "普通" this.normal = "普通"
this.hard = "困難" this.hard = "困難"
this.oni = "魔王" this.oni = "魔王"
this.songBranch = "有譜面分歧"
this.sessionStart = "開始多人模式!" this.sessionStart = "開始多人模式!"
this.sessionEnd = "結束多人模式" this.sessionEnd = "結束多人模式"
this.loading = "讀取中..." this.loading = "讀取中..."
@ -356,6 +375,11 @@ function StringsTw(){
this.good = "良" this.good = "良"
this.ok = "可" this.ok = "可"
this.bad = "不可" this.bad = "不可"
this.branch = {
"normal": "一般譜面",
"advanced": "進階譜面",
"master": "達人譜面"
}
this.pauseOptions = [ this.pauseOptions = [
"繼續演奏", "繼續演奏",
"從頭開始", "從頭開始",
@ -438,6 +462,7 @@ function StringsKo(){
this.normal = "보통" this.normal = "보통"
this.hard = "어려움" this.hard = "어려움"
this.oni = "귀신" this.oni = "귀신"
this.songBranch = "악보 분기 있습니다"
this.sessionStart = "온라인 세션 시작!" this.sessionStart = "온라인 세션 시작!"
this.sessionEnd = "온라인 세션 끝내기" this.sessionEnd = "온라인 세션 끝내기"
this.loading = "로딩 중..." this.loading = "로딩 중..."
@ -457,6 +482,11 @@ function StringsKo(){
this.good = "얼쑤" this.good = "얼쑤"
this.ok = "좋다" this.ok = "좋다"
this.bad = "에구" this.bad = "에구"
this.branch = {
"normal": "보통 악보",
"advanced": "현인 악보",
"master": "달인 악보"
}
this.pauseOptions = [ this.pauseOptions = [
"연주 계속하기", "연주 계속하기",
"처음부터 다시", "처음부터 다시",

View File

@ -18,7 +18,11 @@ class Titlescreen{
this.setLang(allStrings[this.lang], true) this.setLang(allStrings[this.lang], true)
if(songId){ if(songId){
this.goNext() if(localStorage.getItem("tutorial") === "true"){
new SongSelect(false, false, this.touched, this.songId)
}else{
new Tutorial(false, this.songId)
}
}else{ }else{
this.addLangs() this.addLangs()

View File

@ -75,6 +75,33 @@
this.gogoTime = 0 this.gogoTime = 0
this.drumroll = [] this.drumroll = []
this.touchEvents = 0 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.beatInterval = this.controller.parsedSongData.beatInfo.beatInterval
this.font = strings.font this.font = strings.font
@ -85,6 +112,7 @@
this.titleCache = new CanvasCache() this.titleCache = new CanvasCache()
this.comboCache = new CanvasCache() this.comboCache = new CanvasCache()
this.pauseCache = new CanvasCache() this.pauseCache = new CanvasCache()
this.branchCache = new CanvasCache()
this.multiplayer = this.controller.multiplayer this.multiplayer = this.controller.multiplayer
@ -132,7 +160,8 @@
} }
this.setDonBg() 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)) pageEvents.mouseAdd(this, this.onmousemove.bind(this))
this.refresh() this.refresh()
@ -162,6 +191,7 @@
} }
var ratio = (ratioX < ratioY ? ratioX : ratioY) var ratio = (ratioX < ratioY ? ratioX : ratioY)
var resized = false
if(this.winW !== winW || this.winH !== winH){ if(this.winW !== winW || this.winH !== winH){
this.winW = winW this.winW = winW
this.winH = winH this.winH = winH
@ -181,6 +211,7 @@
} }
this.fillComboCache() this.fillComboCache()
this.setDonBgHeight() this.setDonBgHeight()
resized = true
}else if(this.controller.game.paused && !document.hasFocus()){ }else if(this.controller.game.paused && !document.hasFocus()){
return return
}else if(this.multiplayer !== 2){ }else if(this.multiplayer !== 2){
@ -669,7 +700,7 @@
} }
ctx.restore() ctx.restore()
// Bar pressed keys // Branch background
var keyTime = this.controller.getKeyTime() var keyTime = this.controller.getKeyTime()
var sound = keyTime["don"] > keyTime["ka"] ? "don" : "ka" var sound = keyTime["don"] > keyTime["ka"] ? "don" : "ka"
var padding = this.slotPos.paddingLeft var padding = this.slotPos.paddingLeft
@ -677,12 +708,78 @@
var barY = this.slotPos.y - 65 * mul var barY = this.slotPos.y - 65 * mul
var barH = 130 * 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){ if(this.gogoTime || ms <= this.gogoTimeStarted + 100){
var grd = ctx.createLinearGradient(padding, 0, winW, 0) var grd = ctx.createLinearGradient(padding, 0, winW, 0)
grd.addColorStop(0, "#512a2c") grd.addColorStop(0, "rgba(255, 0, 0, 0.16)")
grd.addColorStop(0.46, "#6f2a2d") grd.addColorStop(0.45, "rgba(255, 0, 0, 0.28)")
grd.addColorStop(0.76, "#8a4763") grd.addColorStop(0.77, "rgba(255, 83, 157, 0.4)")
grd.addColorStop(1, "#2c2a2c") grd.addColorStop(1, "rgba(255, 83, 157, 0)")
ctx.fillStyle = grd ctx.fillStyle = grd
if(!this.touchEnabled){ if(!this.touchEnabled){
var alpha = Math.min(100, ms - this.gogoTimeStarted) / 100 var alpha = Math.min(100, ms - this.gogoTimeStarted) / 100
@ -693,6 +790,8 @@
} }
ctx.fillRect(padding, barY, winW - padding, barH) ctx.fillRect(padding, barY, winW - padding, barH)
} }
// Bar pressed keys
if(keyTime[sound] > ms - 130){ if(keyTime[sound] > ms - 130){
var gradients = { var gradients = {
"don": "255, 0, 0", "don": "255, 0, 0",
@ -790,6 +889,7 @@
ctx.lineWidth = 7 * mul ctx.lineWidth = 7 * mul
ctx.textAlign = "center" ctx.textAlign = "center"
ctx.miterLimit = 1 ctx.miterLimit = 1
ctx.strokeStyle = "#000"
ctx.strokeText(strings.combo, comboX, comboTextY) ctx.strokeText(strings.combo, comboX, comboTextY)
ctx.miterLimit = 10 ctx.miterLimit = 10
ctx.fillText(strings.combo, comboX, comboTextY) ctx.fillText(strings.combo, comboX, comboTextY)
@ -1102,15 +1202,23 @@
var timeForDistance = this.posToMs(distanceForCircle, measure.speed) var timeForDistance = this.posToMs(distanceForCircle, measure.speed)
var startingTime = measure.ms - timeForDistance var startingTime = measure.ms - timeForDistance
var finishTime = measure.ms + this.posToMs(this.slotPos.x - this.slotPos.paddingLeft + 3, measure.speed) 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) 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.lineWidth = 3
this.ctx.beginPath() this.ctx.beginPath()
this.ctx.moveTo(measureX, measureY) this.ctx.moveTo(measureX, measureY)
this.ctx.lineTo(measureX, measureY + measureH) this.ctx.lineTo(measureX, measureY + measureH)
this.ctx.stroke() 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(){ updateNoteFaces(){
@ -1137,21 +1245,21 @@
for(var i = circles.length; i--;){ for(var i = circles.length; i--;){
var circle = circles[i] var circle = circles[i]
var speed = circle.getSpeed() var speed = circle.speed
var timeForDistance = this.posToMs(distanceForCircle + this.slotPos.size / 2, speed) var timeForDistance = this.posToMs(distanceForCircle + this.slotPos.size / 2, speed)
var startingTime = circle.getMS() - timeForDistance var startingTime = circle.ms - timeForDistance
var finishTime = circle.getEndTime() + this.posToMs(this.slotPos.x - this.slotPos.paddingLeft + this.slotPos.size * 2, speed) 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(circle.isPlayed <= 0 || circle.score === 0){
if(ms >= startingTime && ms <= finishTime && circle.getPlayed() !== -1){ if((!circle.branch || circle.branch.active) && ms >= startingTime && ms <= finishTime && circle.isPlayed !== -1){
this.drawCircle(circle) this.drawCircle(circle)
} }
}else if(!circle.isAnimated()){ }else if(!circle.animating){
// Start animation to gauge // Start animation to gauge
circle.animate(ms) 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){ if(this.gogoTime != circle.gogoTime){
this.toggleGogoTime(circle) this.toggleGogoTime(circle)
} }
@ -1165,9 +1273,9 @@
for(var i = 0; i < circles.length; i++){ for(var i = 0; i < circles.length; i++){
var circle = circles[i] var circle = circles[i]
if(circle.isAnimated()){ if(circle.animating){
var animT = circle.getAnimT() var animT = circle.animT
if(ms < animT + 490){ if(ms < animT + 490){
if(circle.fixedPos){ if(circle.fixedPos){
@ -1183,7 +1291,7 @@
var pos = this.animateBezier[3] var pos = this.animateBezier[3]
this.drawCircle(circle, pos, (ms - animT - 490) / 160) this.drawCircle(circle, pos, (ms - animT - 490) / 160)
}else{ }else{
circle.endAnimation() circle.animationEnded = true
} }
} }
} }
@ -1211,13 +1319,13 @@
var lyricsSize = 20 * mul var lyricsSize = 20 * mul
var fill, size, faceID var fill, size, faceID
var type = circle.getType() var type = circle.type
var ms = this.getMS() var ms = this.getMS()
var circleMs = circle.getMS() var circleMs = circle.ms
var endTime = circle.getEndTime() var endTime = circle.endTime
var animated = circle.isAnimated() var animated = circle.animating
var speed = circle.getSpeed() var speed = circle.speed
var played = circle.getPlayed() var played = circle.isPlayed
var drumroll = 0 var drumroll = 0
var endX = 0 var endX = 0
@ -1323,9 +1431,9 @@
ctx.fill() ctx.fill()
ctx.globalAlpha = 1 ctx.globalAlpha = 1
} }
if(!circle.isAnimated()){ if(!circle.animating){
// Text // Text
var text = circle.getText() var text = circle.text
var textX = circlePos.x var textX = circlePos.x
var textY = circlePos.y + 83 * mul var textY = circlePos.y + 83 * mul
ctx.font = lyricsSize + "px Kozuka, Microsoft YaHei, sans-serif" ctx.font = lyricsSize + "px Kozuka, Microsoft YaHei, sans-serif"
@ -1436,6 +1544,37 @@
}) })
this.globalAlpha = 1 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){ toggleGogoTime(circle){
this.gogoTime = circle.gogoTime this.gogoTime = circle.gogoTime
this.gogoTimeStarted = circle.ms this.gogoTimeStarted = circle.ms
@ -1466,7 +1605,7 @@
if(this.gogoTime){ if(this.gogoTime){
var circles = this.controller.parsedSongData.circles var circles = this.controller.parsedSongData.circles
var lastCircle = circles[circles.length - 1] var lastCircle = circles[circles.length - 1]
var endTime = lastCircle.getEndTime() + 3000 var endTime = lastCircle.endTime + 3000
if(ms >= endTime){ if(ms >= endTime){
this.toggleGogoTime({ this.toggleGogoTime({
gogoTime: 0, gogoTime: 0,

View File

@ -9,6 +9,17 @@
<div class="measure-num input-slider"> <div class="measure-num input-slider">
<span class="reset">x</span><input type="text" value="" readonly><span class="minus">-</span><span class="plus">+</span> <span class="reset">x</span><input type="text" value="" readonly><span class="minus">-</span><span class="plus">+</span>
</div> </div>
<div class="branch-hide">
<div>Branch:</div>
<div class="branch-select select">
<span class="reset">x</span><select>
<option value="auto" selected style="background:#fff">Auto</option>
<option value="normal" style="background:#ccc">Normal</option>
<option value="advanced" style="background:#bdf">Professional</option>
<option value="master" style="background:#ebf">Master</option>
</select>
</div>
</div>
<label><input class="change-restart" type="checkbox">Restart on change</label> <label><input class="change-restart" type="checkbox">Restart on change</label>
<label class="autoplay-label"><input class="autoplay" type="checkbox">Auto play</label> <label class="autoplay-label"><input class="autoplay" type="checkbox">Auto play</label>
<div class="bottom-btns"> <div class="bottom-btns">

View File

@ -185,6 +185,7 @@ async def connection(ws, path):
if "other_user" in user and "ws" in user["other_user"]: if "other_user" in user and "ws" in user["other_user"]:
if type == "note"\ if type == "note"\
or type == "drumroll"\ or type == "drumroll"\
or type == "branch"\
or type == "gameresults": or type == "gameresults":
await user["other_user"]["ws"].send(msgobj(type, value)) await user["other_user"]["ws"].send(msgobj(type, value))
elif type == "songsel" and user["session"]: elif type == "songsel" and user["session"]: