Adds new settings for controlling the note offset while playing. It can be either an actual offset (it is called "Audio Latency" in the settings) or just the visual offset ("Video Latency").
With higher audio latency it means you have to press the button sooner than what you hear, similarly with higher video latency it is sooner than what you see. By offsetting these events the game would play better, however, the sound effect of you hitting the drum would still play at the wrong time, the code cannot anticipate you to hit the drum in the future so to work around this issue a new option that disables drum sounds is also included.
These settings could be set through trial and error but it would be better to get the correct values through the automated latency calibration, where you can hit the drum as you hear sounds or see a blinking animation. I tried making one by measuring latency from user input, adding all the latency up, and dividing, but that gives unreliable results. I hope someone suggests to me what I should be doing during the calibration to get better results, as I cannot figure what to do on my own.
835 lines
24 KiB
JavaScript
835 lines
24 KiB
JavaScript
class Game{
|
|
constructor(controller, selectedSong, songData){
|
|
this.controller = controller
|
|
this.selectedSong = selectedSong
|
|
this.songData = songData
|
|
this.elapsedTime = 0
|
|
this.currentCircle = 0
|
|
this.combo = 0
|
|
this.rules = new GameRules(this)
|
|
this.globalScore = {
|
|
points: 0,
|
|
good: 0,
|
|
ok: 0,
|
|
bad: 0,
|
|
maxCombo: 0,
|
|
drumroll: 0,
|
|
gauge: 0,
|
|
title: selectedSong.title,
|
|
difficulty: this.rules.difficulty
|
|
}
|
|
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
|
|
this.paused = false
|
|
this.started = false
|
|
this.mainMusicPlaying = false
|
|
this.musicFadeOut = 0
|
|
this.fadeOutStarted = false
|
|
this.currentTimingPoint = 0
|
|
this.branchNames = ["normal", "advanced", "master"]
|
|
this.resetSection()
|
|
this.gameLagSync = !this.controller.touchEnabled && !(/Firefox/.test(navigator.userAgent))
|
|
|
|
assets.songs.forEach(song => {
|
|
if(song.id == selectedSong.folder){
|
|
this.mainAsset = song.sound
|
|
}
|
|
})
|
|
}
|
|
run(){
|
|
this.timeForDistanceCircle = 2500
|
|
this.initTiming()
|
|
this.view = this.controller.view
|
|
}
|
|
initTiming(){
|
|
// Date when the chrono is started (before the game begins)
|
|
var firstCircle = this.songData.circles[0]
|
|
if(this.controller.calibrationMode){
|
|
var offsetTime = 0
|
|
}else{
|
|
var offsetTime = Math.max(0, this.timeForDistanceCircle - (firstCircle ? firstCircle.ms : 0)) |0
|
|
}
|
|
if(this.controller.multiplayer){
|
|
var syncWith = this.controller.syncWith
|
|
var syncCircles = syncWith.game.songData.circles
|
|
var syncOffsetTime = Math.max(0, this.timeForDistanceCircle - syncCircles[0].ms) |0
|
|
offsetTime = Math.max(offsetTime, syncOffsetTime)
|
|
}
|
|
this.elapsedTime = -offsetTime
|
|
// The real start for the game will start when chrono will reach 0
|
|
this.startDate = Date.now() + offsetTime
|
|
}
|
|
update(){
|
|
this.updateTime()
|
|
// Main operations
|
|
this.updateCirclesStatus()
|
|
this.checkPlays()
|
|
// Event operations
|
|
this.whenFadeoutMusic()
|
|
if(this.controller.multiplayer !== 2){
|
|
this.whenLastCirclePlayed()
|
|
}
|
|
}
|
|
getCircles(){
|
|
return this.songData.circles
|
|
}
|
|
updateCirclesStatus(){
|
|
var nextSet = false
|
|
var ms = this.elapsedTime
|
|
var circles = this.songData.circles
|
|
var startIndex = this.currentCircle === 0 ? 0 : this.currentCircle - 1
|
|
var index = 0
|
|
|
|
for(var i = startIndex; i < circles.length; i++){
|
|
var circle = circles[i]
|
|
if(circle && (!circle.branch || circle.branch.active) && !circle.isPlayed){
|
|
var type = circle.type
|
|
var drumrollNotes = type === "balloon" || type === "drumroll" || type === "daiDrumroll"
|
|
var endTime = circle.endTime + (drumrollNotes ? 0 : this.rules.bad) + this.controller.audioLatency
|
|
|
|
if(ms >= circle.ms + this.controller.audioLatency){
|
|
if(drumrollNotes && !circle.rendaPlayed && ms < endTime + this.controller.audioLatency){
|
|
circle.rendaPlayed = true
|
|
if(this.rules.difficulty === "easy"){
|
|
assets.sounds["v_renda" + this.controller.snd].stop()
|
|
this.controller.playSoundMeka("v_renda")
|
|
}
|
|
}
|
|
if(!circle.beatMSCopied){
|
|
if(this.view.beatInterval !== circle.beatMS){
|
|
this.view.changeBeatInterval(circle.beatMS)
|
|
}
|
|
circle.beatMSCopied = true
|
|
}
|
|
}
|
|
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.ms - this.controller.audioLatency) / circle.timesHit
|
|
}
|
|
if(type === "drumroll" || type === "daiDrumroll"){
|
|
value.kaAmount = circle.timesKa / circle.timesHit
|
|
}
|
|
p2.send("drumroll", value)
|
|
}
|
|
}else{
|
|
this.skipNote(circle)
|
|
this.updateCurrentCircle()
|
|
}
|
|
}
|
|
}else if(!this.controller.autoPlayEnabled && !nextSet){
|
|
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){
|
|
if(!this.branchStatic){
|
|
view.branchAnimate = {
|
|
ms: ms,
|
|
fromBranch: view.branch
|
|
}
|
|
}
|
|
this.branchStatic = false
|
|
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")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
fixNoteStream(keysDon){
|
|
var circleIsNote = circle => {
|
|
var type = circle.type
|
|
return type === "don" || type === "ka" || type === "daiDon" || type === "daiKa"
|
|
}
|
|
var correctNote = circle => {
|
|
var type = circle.type
|
|
return keysDon ? (type === "don" || type === "daiDon") : (type === "ka" || type === "daiKa")
|
|
}
|
|
var ms = this.elapsedTime
|
|
var circles = this.songData.circles
|
|
|
|
for(var i = this.currentCircle + 1; i < circles.length; i++){
|
|
var circle = circles[i]
|
|
var relative = ms - circle.ms - this.controller.audioLatency
|
|
if(!circle.branch || circle.branch.active){
|
|
if((!circleIsNote(circle) || relative < -this.rules.bad)){
|
|
break
|
|
}else if(Math.abs(relative) < this.rules.ok && correctNote(circle)){
|
|
for(var j = this.currentCircle; j < i; j++){
|
|
var circle = circles[j]
|
|
if(circle && !circle.branch || circle.branch.active){
|
|
this.skipNote(circles[j])
|
|
}
|
|
}
|
|
this.currentCircle = i
|
|
return circles[i]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
skipNote(circle){
|
|
if(circle.section){
|
|
this.resetSection()
|
|
}
|
|
circle.played(-1, circle.type === "daiDon" || circle.type === "daiKa")
|
|
this.sectionNotes.push(0)
|
|
this.controller.displayScore(0, true)
|
|
this.updateCombo(0)
|
|
this.updateGlobalScore(0, 1)
|
|
if(this.controller.multiplayer === 1){
|
|
p2.send("note", {
|
|
score: -1
|
|
})
|
|
}
|
|
}
|
|
checkPlays(){
|
|
var circles = this.songData.circles
|
|
var circle = circles[this.currentCircle]
|
|
|
|
if(this.controller.autoPlayEnabled){
|
|
while(circle && this.controller.autoPlay(circle)){
|
|
circle = circles[this.currentCircle]
|
|
}
|
|
return
|
|
}
|
|
var keys = this.controller.getKeys()
|
|
|
|
var don_l = keys["don_l"] && !this.controller.isWaiting("don_l", "score")
|
|
var don_r = keys["don_r"] && !this.controller.isWaiting("don_r", "score")
|
|
var ka_l = keys["ka_l"] && !this.controller.isWaiting("ka_l", "score")
|
|
var ka_r = keys["ka_r"] && !this.controller.isWaiting("ka_r", "score")
|
|
|
|
var checkDon = () => {
|
|
if(don_l && don_r){
|
|
this.checkKey(["don_l", "don_r"], circle, "daiDon")
|
|
}else if(don_l){
|
|
this.checkKey(["don_l"], circle, "don")
|
|
}else if(don_r){
|
|
this.checkKey(["don_r"], circle, "don")
|
|
}
|
|
}
|
|
var checkKa = () => {
|
|
if(ka_l && ka_r){
|
|
this.checkKey(["ka_l", "ka_r"], circle, "daiKa")
|
|
}else if(ka_l){
|
|
this.checkKey(["ka_l"], circle, "ka")
|
|
}else if(ka_r){
|
|
this.checkKey(["ka_r"], circle, "ka")
|
|
}
|
|
}
|
|
var keyTime = this.controller.getKeyTime()
|
|
if(keyTime["don"] >= keyTime["ka"]){
|
|
checkDon()
|
|
checkKa()
|
|
}else{
|
|
checkKa()
|
|
checkDon()
|
|
}
|
|
}
|
|
checkKey(keyCodes, circle, check){
|
|
if(circle && !circle.isPlayed){
|
|
if(!this.checkScore(circle, check)){
|
|
return
|
|
}
|
|
}
|
|
keyCodes.forEach(keyCode => {
|
|
this.controller.waitForKeyup(keyCode, "score")
|
|
})
|
|
}
|
|
checkScore(circle, check){
|
|
var ms = this.elapsedTime
|
|
var type = circle.type
|
|
|
|
var keysDon = check === "don" || check === "daiDon"
|
|
var keysKa = check === "ka" || check === "daiKa"
|
|
var keyDai = check === "daiDon" || check === "daiKa"
|
|
var typeDon = type === "don" || type === "daiDon"
|
|
var typeKa = type === "ka" || type === "daiKa"
|
|
var typeDai = type === "daiDon" || type === "daiKa"
|
|
|
|
var keyTime = this.controller.getKeyTime()
|
|
var currentTime = keysDon ? keyTime["don"] : keyTime["ka"]
|
|
var relative = currentTime - circle.ms - this.controller.audioLatency
|
|
|
|
if(relative >= this.rules.ok){
|
|
var fixedNote = this.fixNoteStream(keysDon)
|
|
if(fixedNote){
|
|
return this.checkScore(fixedNote, check)
|
|
}
|
|
}
|
|
|
|
if(typeDon || typeKa){
|
|
if(-this.rules.bad >= relative || relative >= this.rules.bad){
|
|
return true
|
|
}
|
|
var score = 0
|
|
if(keysDon && typeDon || keysKa && typeKa){
|
|
if(typeDai && !keyDai){
|
|
if(!circle.daiFailed){
|
|
circle.daiFailed = ms
|
|
return false
|
|
}else if(ms < circle.daiFailed + this.rules.daiLeniency){
|
|
return false
|
|
}
|
|
}
|
|
var circleStatus = -1
|
|
relative = Math.abs(relative)
|
|
if(relative < this.rules.good){
|
|
circleStatus = 450
|
|
}else if(relative < this.rules.ok){
|
|
circleStatus = 230
|
|
}else if(relative < this.rules.bad){
|
|
circleStatus = 0
|
|
}
|
|
if(circleStatus === 230 || circleStatus === 450){
|
|
score = circleStatus
|
|
}
|
|
circle.played(score, score === 0 ? typeDai : keyDai)
|
|
this.controller.displayScore(score, false, typeDai && keyDai)
|
|
}else{
|
|
var keyTime = this.controller.getKeyTime()
|
|
var keyTimeRelative = Math.abs(keyTime.don - keyTime.ka)
|
|
if(Math.abs(relative) >= (keyTimeRelative <= 25 ? this.rules.bad : this.rules.good)){
|
|
return true
|
|
}
|
|
circle.played(-1, typeDai)
|
|
this.controller.displayScore(score, true, false)
|
|
}
|
|
this.updateCombo(score)
|
|
this.updateGlobalScore(score, typeDai && keyDai ? 2 : 1, circle.gogoTime)
|
|
this.updateCurrentCircle()
|
|
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.ms - currentTime - this.controller.audioLatency,
|
|
dai: typeDai ? (keyDai ? 2 : 1) : 0
|
|
}
|
|
if((!keysDon || !typeDon) && (!keysKa || !typeKa)){
|
|
value.reverse = true
|
|
}
|
|
p2.send("note", value)
|
|
}
|
|
}else{
|
|
if(circle.ms + this.controller.audioLatency > currentTime || currentTime > circle.endTime + this.controller.audioLatency){
|
|
return true
|
|
}
|
|
if(keysDon && type === "balloon"){
|
|
this.checkBalloon(circle)
|
|
if(check === "daiDon" && !circle.isPlayed){
|
|
this.checkBalloon(circle)
|
|
}
|
|
}else if((keysDon || keysKa) && (type === "drumroll" || type === "daiDrumroll")){
|
|
this.checkDrumroll(circle, keysKa)
|
|
if(keyDai){
|
|
this.checkDrumroll(circle, keysKa)
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
checkBalloon(circle){
|
|
if(circle.timesHit >= circle.requiredHits - 1){
|
|
var score = 5000
|
|
this.updateCurrentCircle()
|
|
circle.hit()
|
|
circle.played(score)
|
|
if(this.controller.multiplayer == 1){
|
|
p2.send("drumroll", {
|
|
pace: (this.elapsedTime - circle.ms + this.controller.audioLatency) / circle.timesHit
|
|
})
|
|
}
|
|
}else{
|
|
var score = 300
|
|
circle.hit()
|
|
}
|
|
this.globalScore.drumroll++
|
|
this.sectionDrumroll++
|
|
this.globalScore.points += score
|
|
this.view.setDarkBg(false)
|
|
}
|
|
checkDrumroll(circle, keysKa){
|
|
var ms = this.elapsedTime
|
|
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.type === "drumroll"){
|
|
var sound = keyTime["don"] > keyTime["ka"] ? "don" : "ka"
|
|
}else{
|
|
var sound = keyTime["don"] > keyTime["ka"] ? "daiDon" : "daiKa"
|
|
}
|
|
var circleAnim = new Circle({
|
|
id: 0,
|
|
start: ms,
|
|
type: sound,
|
|
txt: "",
|
|
speed: circle.speed,
|
|
gogoTime: circle.gogoTime,
|
|
fixedPos: document.hasFocus()
|
|
})
|
|
circleAnim.played(score, dai)
|
|
circleAnim.animate(ms)
|
|
this.view.drumroll.push(circleAnim)
|
|
this.globalScore.drumroll++
|
|
this.sectionDrumroll++
|
|
this.globalScore.points += score * (dai ? 2 : 1)
|
|
this.view.setDarkBg(false)
|
|
}
|
|
whenLastCirclePlayed(){
|
|
var ms = this.elapsedTime
|
|
if(!this.lastCircle){
|
|
var circles = this.songData.circles
|
|
var circle = circles[circles.length - 1]
|
|
this.lastCircle = circle ? circle.endTime : 0
|
|
if(this.controller.multiplayer){
|
|
var syncWith = this.controller.syncWith
|
|
var syncCircles = syncWith.game.songData.circles
|
|
circle = syncCircles[syncCircles.length - 1]
|
|
var syncLastCircle = circle ? circle.endTime : 0
|
|
if(syncLastCircle > this.lastCircle){
|
|
this.lastCircle = syncLastCircle
|
|
}
|
|
}
|
|
}
|
|
if(!this.fadeOutStarted && ms >= this.lastCircle + 2000 + this.controller.audioLatency){
|
|
this.fadeOutStarted = ms
|
|
if(this.controller.multiplayer){
|
|
this.controller.syncWith.game.fadeOutStarted = ms
|
|
}
|
|
}
|
|
}
|
|
whenFadeoutMusic(){
|
|
var started = this.fadeOutStarted
|
|
if(started){
|
|
var ms = this.elapsedTime
|
|
var duration = this.mainAsset ? this.mainAsset.duration : 0
|
|
var musicDuration = duration * 1000 - this.controller.offset
|
|
if(this.musicFadeOut === 0){
|
|
if(this.controller.multiplayer === 1){
|
|
p2.send("gameresults", this.getGlobalScore())
|
|
}
|
|
this.musicFadeOut++
|
|
}else if(this.musicFadeOut === 1 && ms >= started + 1600){
|
|
this.controller.gameEnded()
|
|
if(!p2.session && this.controller.multiplayer === 1){
|
|
p2.send("gameend")
|
|
}
|
|
this.musicFadeOut++
|
|
}else if(this.musicFadeOut === 2 && (ms >= started + 8600 && ms >= musicDuration + 250)){
|
|
this.controller.displayResults()
|
|
this.musicFadeOut++
|
|
}else if(this.musicFadeOut === 3 && (ms >= started + 9600 && ms >= musicDuration + 1250)){
|
|
this.controller.clean()
|
|
if(this.controller.scoresheet){
|
|
this.controller.scoresheet.startRedraw()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
playMainMusic(){
|
|
var ms = this.elapsedTime + this.controller.offset
|
|
if(!this.mainMusicPlaying && (!this.fadeOutStarted || ms < this.fadeOutStarted + 1600)){
|
|
if(this.calibrationState === "audio"){
|
|
var beatInterval = this.controller.view.beatInterval
|
|
var startAt = ms % beatInterval
|
|
var duration = this.mainAsset.duration * 1000
|
|
if(startAt < duration){
|
|
this.mainAsset.playLoop(0, false, startAt / 1000, 0, beatInterval / 1000)
|
|
}else{
|
|
this.mainAsset.playLoop((startAt - duration) / 1000, false, 0, 0, beatInterval / 1000)
|
|
}
|
|
}else if(this.controller.multiplayer !== 2 && this.mainAsset){
|
|
this.mainAsset.play((ms < 0 ? -ms : 0) / 1000, false, Math.max(0, ms / 1000))
|
|
}
|
|
this.mainMusicPlaying = true
|
|
}
|
|
}
|
|
togglePause(forcePause, pauseMove, noSound){
|
|
if(!this.paused){
|
|
if(forcePause === false){
|
|
return
|
|
}
|
|
if(!noSound){
|
|
this.controller.playSound("se_pause", 0, true)
|
|
}
|
|
this.paused = true
|
|
this.latestDate = Date.now()
|
|
if(this.mainAsset){
|
|
this.mainAsset.stop()
|
|
}
|
|
this.mainMusicPlaying = false
|
|
this.view.pauseMove(pauseMove || 0, true)
|
|
this.view.gameDiv.classList.add("game-paused")
|
|
this.view.lastMousemove = this.view.getMS()
|
|
this.view.cursorHidden = false
|
|
pageEvents.send("pause")
|
|
}else if(!forcePause){
|
|
if(forcePause !== false && this.calibrationState && ["audioHelp", "audioComplete", "videoHelp", "videoComplete", "results"].indexOf(this.calibrationState) !== -1){
|
|
return
|
|
}
|
|
if(this.calibrationState === "audioHelp" || this.calibrationState === "videoHelp"){
|
|
this.calibrationState = this.calibrationState === "audioHelp" ? "audio" : "video"
|
|
this.controller.view.pauseOptions = strings.pauseOptions
|
|
this.controller.playSound("se_don", 0, true)
|
|
}else if(!noSound){
|
|
this.controller.playSound("se_cancel", 0, true)
|
|
}
|
|
this.paused = false
|
|
var currentDate = Date.now()
|
|
this.startDate += currentDate - this.latestDate
|
|
this.sndTime = currentDate - snd.buffer.getTime() * 1000
|
|
this.view.gameDiv.classList.remove("game-paused")
|
|
this.view.pointer()
|
|
pageEvents.send("unpause", currentDate - this.latestDate)
|
|
}
|
|
}
|
|
isPaused(){
|
|
return this.paused
|
|
}
|
|
updateTime(){
|
|
// Refreshed date
|
|
var ms = this.elapsedTime
|
|
if(ms >= 0 && !this.started){
|
|
this.startDate = Date.now()
|
|
this.elapsedTime = this.getAccurateTime()
|
|
this.started = true
|
|
this.sndTime = this.startDate - snd.buffer.getTime() * 1000
|
|
}else if(ms < 0 || ms >= 0 && this.started){
|
|
var currentDate = Date.now()
|
|
if(this.gameLagSync){
|
|
var sndTime = currentDate - snd.buffer.getTime() * 1000
|
|
var lag = sndTime - this.sndTime
|
|
if(Math.abs(lag) >= 50){
|
|
this.startDate += lag
|
|
this.sndTime = sndTime
|
|
pageEvents.send("game-lag", lag)
|
|
}
|
|
}
|
|
this.elapsedTime = currentDate - this.startDate
|
|
}
|
|
}
|
|
getAccurateTime(){
|
|
if(this.isPaused()){
|
|
return this.elapsedTime
|
|
}else{
|
|
return Date.now() - this.startDate
|
|
}
|
|
}
|
|
getCircles(){
|
|
return this.songData.circles
|
|
}
|
|
updateCurrentCircle(){
|
|
var circles = this.songData.circles
|
|
do{
|
|
var circle = circles[++this.currentCircle]
|
|
}while(circle && circle.branch && !circle.branch.active)
|
|
}
|
|
getCurrentCircle(){
|
|
return this.currentCircle
|
|
}
|
|
updateCombo(score){
|
|
if(score !== 0){
|
|
this.combo++
|
|
}else{
|
|
this.combo = 0
|
|
}
|
|
if(this.combo > this.globalScore.maxCombo){
|
|
this.globalScore.maxCombo = this.combo
|
|
}
|
|
if(this.combo === 50 || this.combo > 0 && this.combo % 100 === 0 && this.combo < 1500 || this.combo > 0 && this.combo % 500 === 0){
|
|
this.controller.playSoundMeka("v_combo_" + (this.combo <= 1400 ? this.combo : "over1500"))
|
|
}
|
|
if (this.songData.scoremode == 2 && this.combo > 0 && this.combo % 100 == 0) {
|
|
this.globalScore.points += 10000;
|
|
}
|
|
this.view.updateCombo(this.combo)
|
|
}
|
|
getCombo(){
|
|
return this.combo
|
|
}
|
|
getGlobalScore(){
|
|
return this.globalScore
|
|
}
|
|
updateGlobalScore(score, multiplier, gogoTime){
|
|
// Circle score
|
|
switch(score){
|
|
case 450:
|
|
this.globalScore.good++
|
|
break
|
|
case 230:
|
|
this.globalScore.ok++
|
|
break
|
|
case 0:
|
|
this.globalScore.bad++
|
|
break
|
|
}
|
|
if (this.songData.scoremode) {
|
|
switch (score) {
|
|
case 450:
|
|
score = this.songData.scoreinit;
|
|
break;
|
|
case 230:
|
|
score = Math.floor(this.songData.scoreinit / 2);
|
|
break;
|
|
}
|
|
}
|
|
// Gauge update
|
|
if(score !== 0){
|
|
this.globalScore.gauge += this.HPGain
|
|
}else if(this.globalScore.gauge - this.HPGain > 0){
|
|
this.globalScore.gauge -= this.HPGain
|
|
}else{
|
|
this.globalScore.gauge = 0
|
|
}
|
|
// Points update
|
|
if (this.songData.scoremode == 2) {
|
|
var diff_mul = 0;
|
|
if (this.combo >= 100) {
|
|
diff_mul = 8;
|
|
} else if (this.combo >= 50) {
|
|
diff_mul = 4;
|
|
} else if (this.combo >= 30) {
|
|
diff_mul = 2;
|
|
} else if (this.combo >= 10) {
|
|
diff_mul = 1;
|
|
}
|
|
score += this.songData.scorediff * diff_mul;
|
|
} else {
|
|
score += Math.max(0, Math.floor((Math.min(this.combo, 100) - 1) / 10) * (this.songData.scoremode ? this.songData.scorediff : 100));
|
|
}
|
|
|
|
if(gogoTime){
|
|
multiplier *= 1.2
|
|
}
|
|
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 + this.controller.audioLatency >= 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
|
|
}
|
|
clearKeyTime(){
|
|
var keyboard = this.controller.keyboard
|
|
for(var key in keyboard.keyTime){
|
|
keyboard.keys[key] = null
|
|
keyboard.keyTime[key] = -Infinity
|
|
}
|
|
}
|
|
calibration(){
|
|
var view = this.controller.view
|
|
if(!this.calibrationState){
|
|
this.controller.parsedSongData.measures = []
|
|
this.calibrationProgress = {
|
|
audio: 0,
|
|
video: 0,
|
|
requirement: 40
|
|
}
|
|
this.calibrationReset("audio", true)
|
|
}
|
|
var progress = this.calibrationProgress
|
|
var state = this.calibrationState
|
|
switch(state){
|
|
case "audio":
|
|
case "video":
|
|
if(state === "audio" && !this.mainAsset){
|
|
this.mainAsset = assets.sounds["calibration"]
|
|
this.mainMusicPlaying = false
|
|
}
|
|
if(progress.hit >= progress.requirement){
|
|
var reduced = 0
|
|
for(var i = 2; i < progress.offsets.length; i++){
|
|
reduced += progress.offsets[i]
|
|
}
|
|
progress[state] = Math.max(0, Math.round(reduced / progress.offsets.length - 2))
|
|
this.calibrationState += "Complete"
|
|
view.pauseOptions = []
|
|
this.clearKeyTime()
|
|
this.togglePause(true, 1)
|
|
this.mainAsset = null
|
|
}
|
|
break
|
|
case "audioComplete":
|
|
case "videoComplete":
|
|
if(Date.now() - this.latestDate > 3000){
|
|
var audioComplete = this.calibrationState === "audioComplete"
|
|
this.controller.playSound("se_pause", 0, true)
|
|
if(audioComplete){
|
|
this.calibrationReset("video")
|
|
}else{
|
|
view.pauseOptions = [
|
|
strings.calibration.retryPrevious,
|
|
strings.calibration.finish
|
|
]
|
|
}
|
|
this.calibrationState = audioComplete ? "videoHelp" : "results"
|
|
}
|
|
break
|
|
}
|
|
}
|
|
calibrationHit(ms){
|
|
var progress = this.calibrationProgress
|
|
var beatInterval = this.controller.view.beatInterval
|
|
var current = Math.floor((ms + 100) / beatInterval)
|
|
if(current !== progress.last){
|
|
var offset = ((ms + 100) % beatInterval) - 100
|
|
var offsets = progress.offsets
|
|
if(offsets.length >= progress.requirement){
|
|
offsets.shift()
|
|
}
|
|
offsets.push(offset)
|
|
progress.hit++
|
|
progress.last = current
|
|
this.globalScore.gauge = 100 / (progress.requirement / progress.hit)
|
|
}
|
|
}
|
|
calibrationReset(to, togglePause){
|
|
var view = this.controller.view
|
|
this.songData.circles = []
|
|
view.pauseOptions = [
|
|
to === "audio" ? strings.calibration.back : strings.calibration.retryPrevious,
|
|
strings.calibration.start
|
|
]
|
|
this.calibrationState = to + "Help"
|
|
var progress = this.calibrationProgress
|
|
progress.offsets = []
|
|
progress.hit = 0
|
|
progress.last = null
|
|
this.globalScore.gauge = 0
|
|
if(to === "video"){
|
|
this.clearKeyTime()
|
|
this.initTiming()
|
|
this.latestDate = this.startDate
|
|
this.elapsedTime = 0
|
|
view.ms = 0
|
|
}
|
|
if(togglePause){
|
|
this.togglePause(true, 1, true)
|
|
}else{
|
|
view.pauseMove(1, true)
|
|
}
|
|
}
|
|
}
|