2018-09-15 16:34:53 +02:00
|
|
|
class Game{
|
|
|
|
constructor(controller, selectedSong, songData){
|
|
|
|
this.controller = controller
|
|
|
|
this.selectedSong = selectedSong
|
|
|
|
this.songData = songData
|
2018-10-11 22:24:18 +02:00
|
|
|
this.elapsedTime = 0
|
2018-09-15 16:34:53 +02:00
|
|
|
this.currentCircle = 0
|
|
|
|
this.combo = 0
|
2018-10-01 09:33:43 +02:00
|
|
|
this.rules = new GameRules(this)
|
2018-09-15 16:34:53 +02:00
|
|
|
this.globalScore = {
|
|
|
|
points: 0,
|
|
|
|
good: 0,
|
2018-10-01 09:33:43 +02:00
|
|
|
ok: 0,
|
|
|
|
bad: 0,
|
2018-09-15 16:34:53 +02:00
|
|
|
maxCombo: 0,
|
2018-09-15 19:27:53 +02:00
|
|
|
drumroll: 0,
|
2018-10-01 09:33:43 +02:00
|
|
|
gauge: 0,
|
|
|
|
title: selectedSong.title,
|
|
|
|
difficulty: this.rules.difficulty
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
|
|
|
this.HPGain = 100 / this.songData.circles.filter(circle => {
|
2019-02-17 17:26:46 +01:00
|
|
|
var type = circle.type
|
|
|
|
return (type === "don" || type === "ka" || type === "daiDon" || type === "daiKa") && (!circle.branch || circle.branch.active)
|
2018-09-15 16:34:53 +02:00
|
|
|
}).length
|
|
|
|
this.paused = false
|
|
|
|
this.started = false
|
|
|
|
this.mainMusicPlaying = false
|
|
|
|
this.musicFadeOut = 0
|
|
|
|
this.fadeOutStarted = false
|
|
|
|
this.currentTimingPoint = 0
|
2019-02-20 21:48:21 +01:00
|
|
|
this.branchNames = ["normal", "advanced", "master"]
|
|
|
|
this.resetSection()
|
2019-03-15 22:34:48 +01:00
|
|
|
this.gameLagSync = !this.controller.touchEnabled && !(/Firefox/.test(navigator.userAgent))
|
2018-09-21 22:31:35 +02:00
|
|
|
|
2018-09-15 16:34:53 +02:00
|
|
|
assets.songs.forEach(song => {
|
|
|
|
if(song.id == selectedSong.folder){
|
|
|
|
this.mainAsset = song.sound
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
run(){
|
|
|
|
this.timeForDistanceCircle = 2500
|
|
|
|
this.initTiming()
|
2018-10-11 22:24:18 +02:00
|
|
|
this.view = this.controller.view
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
|
|
|
initTiming(){
|
|
|
|
// Date when the chrono is started (before the game begins)
|
2018-10-03 11:48:18 +02:00
|
|
|
var offsetTime = Math.max(0, this.timeForDistanceCircle - this.songData.circles[0].ms) |0
|
2019-01-25 02:42:05 +01:00
|
|
|
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)
|
|
|
|
}
|
2018-10-03 11:48:18 +02:00
|
|
|
this.elapsedTime = -offsetTime
|
2018-09-15 16:34:53 +02:00
|
|
|
// The real start for the game will start when chrono will reach 0
|
2018-12-13 10:18:52 +01:00
|
|
|
this.startDate = Date.now() + offsetTime
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
|
|
|
update(){
|
|
|
|
// Main operations
|
|
|
|
this.updateTime()
|
|
|
|
this.updateCirclesStatus()
|
|
|
|
this.checkPlays()
|
|
|
|
// Event operations
|
|
|
|
this.whenFadeoutMusic()
|
2018-11-13 05:36:15 +01:00
|
|
|
if(this.controller.multiplayer !== 2){
|
|
|
|
this.whenLastCirclePlayed()
|
|
|
|
}
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
|
|
|
getCircles(){
|
|
|
|
return this.songData.circles
|
|
|
|
}
|
|
|
|
updateCirclesStatus(){
|
2018-10-15 00:00:40 +02:00
|
|
|
var nextSet = false
|
2019-02-17 17:26:46 +01:00
|
|
|
var ms = this.elapsedTime
|
2018-09-15 16:34:53 +02:00
|
|
|
var circles = this.songData.circles
|
2019-01-16 13:33:42 +01:00
|
|
|
var startIndex = this.currentCircle === 0 ? 0 : this.currentCircle - 1
|
2019-02-17 17:26:46 +01:00
|
|
|
var index = 0
|
2019-02-24 13:04:14 +01:00
|
|
|
|
2019-02-17 17:26:46 +01:00
|
|
|
for(var i = startIndex; i < circles.length; i++){
|
2018-10-15 00:00:40 +02:00
|
|
|
var circle = circles[i]
|
2019-02-20 21:48:21 +01:00
|
|
|
if(circle && (!circle.branch || circle.branch.active) && !circle.isPlayed){
|
2019-02-17 17:26:46 +01:00
|
|
|
var type = circle.type
|
2018-09-18 19:33:18 +02:00
|
|
|
var drumrollNotes = type === "balloon" || type === "drumroll" || type === "daiDrumroll"
|
2019-02-17 17:26:46 +01:00
|
|
|
var endTime = circle.endTime + (drumrollNotes ? 0 : this.rules.bad)
|
2018-09-15 16:34:53 +02:00
|
|
|
|
2019-02-17 17:26:46 +01:00
|
|
|
if(ms >= circle.ms){
|
2018-10-15 01:18:01 +02:00
|
|
|
if(drumrollNotes && !circle.rendaPlayed && ms < endTime){
|
2018-09-21 22:31:35 +02:00
|
|
|
circle.rendaPlayed = true
|
|
|
|
if(this.rules.difficulty === "easy"){
|
2019-02-04 10:14:42 +01:00
|
|
|
assets.sounds["v_renda" + this.controller.snd].stop()
|
|
|
|
this.controller.playSoundMeka("v_renda")
|
2018-09-18 19:33:18 +02:00
|
|
|
}
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
2018-10-11 22:24:18 +02:00
|
|
|
if(!circle.beatMSCopied){
|
|
|
|
if(this.view.beatInterval !== circle.beatMS){
|
|
|
|
this.view.changeBeatInterval(circle.beatMS)
|
|
|
|
}
|
|
|
|
circle.beatMSCopied = true
|
|
|
|
}
|
2018-09-21 22:31:35 +02:00
|
|
|
}
|
|
|
|
if(ms > endTime){
|
2018-09-20 20:30:49 +02:00
|
|
|
if(!this.controller.autoPlayEnabled){
|
|
|
|
if(drumrollNotes){
|
2019-02-20 21:48:21 +01:00
|
|
|
if(circle.section && circle.timesHit === 0){
|
|
|
|
this.resetSection()
|
|
|
|
}
|
2018-09-21 22:31:35 +02:00
|
|
|
circle.played(-1, false)
|
2018-09-20 20:30:49 +02:00
|
|
|
this.updateCurrentCircle()
|
|
|
|
if(this.controller.multiplayer === 1){
|
2018-11-13 05:36:15 +01:00
|
|
|
var value = {
|
2019-02-17 17:26:46 +01:00
|
|
|
pace: (ms - circle.ms) / circle.timesHit
|
2018-11-13 05:36:15 +01:00
|
|
|
}
|
|
|
|
if(type === "drumroll" || type === "daiDrumroll"){
|
|
|
|
value.kaAmount = circle.timesKa / circle.timesHit
|
|
|
|
}
|
|
|
|
p2.send("drumroll", value)
|
2018-09-20 20:30:49 +02:00
|
|
|
}
|
|
|
|
}else{
|
2019-02-24 16:13:41 +01:00
|
|
|
this.skipNote(circle)
|
2018-09-15 16:34:53 +02:00
|
|
|
this.updateCurrentCircle()
|
|
|
|
}
|
|
|
|
}
|
2018-10-15 01:18:01 +02:00
|
|
|
}else if(!this.controller.autoPlayEnabled && !nextSet){
|
|
|
|
nextSet = true
|
|
|
|
this.currentCircle = i
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
2019-02-17 17:26:46 +01:00
|
|
|
if(index++ > 1){
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-02-24 13:04:14 +01:00
|
|
|
|
2019-02-17 17:26:46 +01:00
|
|
|
var branches = this.songData.branches
|
|
|
|
if(branches){
|
2019-02-20 21:48:21 +01:00
|
|
|
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)
|
2019-02-17 17:26:46 +01:00
|
|
|
}
|
2019-02-20 21:48:21 +01:00
|
|
|
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
|
2019-02-17 17:26:46 +01:00
|
|
|
}
|
|
|
|
}
|
2019-02-20 21:48:21 +01:00
|
|
|
}
|
|
|
|
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])
|
2019-02-17 17:26:46 +01:00
|
|
|
}else{
|
2019-02-20 21:48:21 +01:00
|
|
|
var accuracy = this.sectionDrumroll
|
2019-02-17 17:26:46 +01:00
|
|
|
}
|
2019-02-20 21:48:21 +01:00
|
|
|
}else if(this.sectionNotes.length !== 0){
|
|
|
|
if(force.branch){
|
|
|
|
var accuracy = Math.max(0, Math.min(100, branch.requirement[force.branch]))
|
2019-02-17 17:26:46 +01:00
|
|
|
}else{
|
2019-02-20 21:48:21 +01:00
|
|
|
var accuracy = this.sectionNotes.reduce((a, b) => a + b) / this.sectionNotes.length * 100
|
2019-02-17 17:26:46 +01:00
|
|
|
}
|
|
|
|
}
|
2019-02-20 21:48:21 +01:00
|
|
|
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")
|
2019-02-17 17:26:46 +01:00
|
|
|
}
|
|
|
|
}
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
2018-10-15 00:00:40 +02:00
|
|
|
}
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
2019-02-24 16:13:41 +01:00
|
|
|
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
|
|
|
|
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
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2018-09-15 16:34:53 +02:00
|
|
|
checkPlays(){
|
|
|
|
var circles = this.songData.circles
|
|
|
|
var circle = circles[this.currentCircle]
|
|
|
|
|
2019-02-24 13:04:14 +01:00
|
|
|
if(this.controller.autoPlayEnabled){
|
|
|
|
while(circle && this.controller.autoPlay(circle)){
|
|
|
|
circle = circles[this.currentCircle]
|
|
|
|
}
|
|
|
|
return
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
|
|
|
var keys = this.controller.getKeys()
|
|
|
|
var kbd = this.controller.getBindings()
|
2018-09-18 19:33:18 +02:00
|
|
|
|
|
|
|
var don_l = keys[kbd["don_l"]] && !this.controller.isWaiting(kbd["don_l"], "score")
|
|
|
|
var don_r = keys[kbd["don_r"]] && !this.controller.isWaiting(kbd["don_r"], "score")
|
|
|
|
var ka_l = keys[kbd["ka_l"]] && !this.controller.isWaiting(kbd["ka_l"], "score")
|
|
|
|
var ka_r = keys[kbd["ka_r"]] && !this.controller.isWaiting(kbd["ka_r"], "score")
|
|
|
|
|
2019-02-24 13:04:14 +01:00
|
|
|
var checkDon = () => {
|
|
|
|
if(don_l && don_r){
|
|
|
|
this.checkKey([kbd["don_l"], kbd["don_r"]], circle, "daiDon")
|
|
|
|
}else if(don_l){
|
|
|
|
this.checkKey([kbd["don_l"]], circle, "don")
|
|
|
|
}else if(don_r){
|
|
|
|
this.checkKey([kbd["don_r"]], circle, "don")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var checkKa = () => {
|
|
|
|
if(ka_l && ka_r){
|
|
|
|
this.checkKey([kbd["ka_l"], kbd["ka_r"]], circle, "daiKa")
|
|
|
|
}else if(ka_l){
|
|
|
|
this.checkKey([kbd["ka_l"]], circle, "ka")
|
|
|
|
}else if(ka_r){
|
|
|
|
this.checkKey([kbd["ka_r"]], circle, "ka")
|
|
|
|
}
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
2019-02-24 13:04:14 +01:00
|
|
|
var keyTime = this.controller.getKeyTime()
|
|
|
|
if(keyTime["don"] >= keyTime["ka"]){
|
|
|
|
checkDon()
|
|
|
|
checkKa()
|
|
|
|
}else{
|
|
|
|
checkKa()
|
|
|
|
checkDon()
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
|
|
|
}
|
2018-09-18 19:33:18 +02:00
|
|
|
checkKey(keyCodes, circle, check){
|
2019-02-17 17:26:46 +01:00
|
|
|
if(circle && !circle.isPlayed){
|
2018-09-18 19:33:18 +02:00
|
|
|
if(!this.checkScore(circle, check)){
|
|
|
|
return
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
|
|
|
}
|
2018-09-18 19:33:18 +02:00
|
|
|
keyCodes.forEach(keyCode => {
|
|
|
|
this.controller.waitForKeyup(keyCode, "score")
|
|
|
|
})
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
2018-09-18 19:33:18 +02:00
|
|
|
checkScore(circle, check){
|
2018-10-03 11:48:18 +02:00
|
|
|
var ms = this.elapsedTime
|
2019-02-17 17:26:46 +01:00
|
|
|
var type = circle.type
|
2018-09-18 19:33:18 +02:00
|
|
|
|
|
|
|
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"
|
2018-09-15 16:34:53 +02:00
|
|
|
|
2018-09-21 22:31:35 +02:00
|
|
|
var keyTime = this.controller.getKeyTime()
|
|
|
|
var currentTime = keysDon ? keyTime["don"] : keyTime["ka"]
|
2019-02-17 17:26:46 +01:00
|
|
|
var relative = currentTime - circle.ms
|
2018-09-21 22:31:35 +02:00
|
|
|
|
2019-02-24 16:13:41 +01:00
|
|
|
if(relative >= this.rules.ok){
|
|
|
|
var fixedNote = this.fixNoteStream(keysDon)
|
|
|
|
if(fixedNote){
|
|
|
|
return this.checkScore(fixedNote, check)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-15 16:34:53 +02:00
|
|
|
if(typeDon || typeKa){
|
2018-09-21 22:31:35 +02:00
|
|
|
if(-this.rules.bad >= relative || relative >= this.rules.bad){
|
|
|
|
return true
|
|
|
|
}
|
2018-09-15 16:34:53 +02:00
|
|
|
var score = 0
|
|
|
|
if(keysDon && typeDon || keysKa && typeKa){
|
2018-09-18 19:33:18 +02:00
|
|
|
if(typeDai && !keyDai){
|
|
|
|
if(!circle.daiFailed){
|
|
|
|
circle.daiFailed = ms
|
|
|
|
return false
|
2018-09-21 22:31:35 +02:00
|
|
|
}else if(ms < circle.daiFailed + this.rules.daiLeniency){
|
2018-09-18 19:33:18 +02:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
2018-09-21 22:31:35 +02:00
|
|
|
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
|
|
|
|
}
|
2018-09-18 19:33:18 +02:00
|
|
|
if(circleStatus === 230 || circleStatus === 450){
|
2018-09-15 16:34:53 +02:00
|
|
|
score = circleStatus
|
|
|
|
}
|
2018-10-25 16:18:41 +02:00
|
|
|
circle.played(score, score === 0 ? typeDai : keyDai)
|
|
|
|
this.controller.displayScore(score, false, typeDai && keyDai)
|
2018-09-15 16:34:53 +02:00
|
|
|
}else{
|
2019-02-24 13:04:14 +01:00
|
|
|
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
|
|
|
|
}
|
2018-10-25 16:18:41 +02:00
|
|
|
circle.played(-1, typeDai)
|
|
|
|
this.controller.displayScore(score, true, false)
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
|
|
|
this.updateCombo(score)
|
2018-09-20 01:20:26 +02:00
|
|
|
this.updateGlobalScore(score, typeDai && keyDai ? 2 : 1, circle.gogoTime)
|
2018-09-15 16:34:53 +02:00
|
|
|
this.updateCurrentCircle()
|
2019-02-20 21:48:21 +01:00
|
|
|
if(circle.section){
|
|
|
|
this.resetSection()
|
|
|
|
}
|
2019-02-17 17:26:46 +01:00
|
|
|
this.sectionNotes.push(score === 450 ? 1 : (score === 230 ? 0.5 : 0))
|
|
|
|
if(this.controller.multiplayer === 1){
|
2018-11-13 05:36:15 +01:00
|
|
|
var value = {
|
2018-09-15 16:34:53 +02:00
|
|
|
score: score,
|
2019-02-17 17:26:46 +01:00
|
|
|
ms: circle.ms - currentTime,
|
2019-03-15 22:34:48 +01:00
|
|
|
dai: typeDai ? (keyDai ? 2 : 1) : 0
|
2018-11-13 05:36:15 +01:00
|
|
|
}
|
|
|
|
if((!keysDon || !typeDon) && (!keysKa || !typeKa)){
|
|
|
|
value.reverse = true
|
|
|
|
}
|
|
|
|
p2.send("note", value)
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
2018-09-21 22:31:35 +02:00
|
|
|
}else{
|
2019-02-17 17:26:46 +01:00
|
|
|
if(circle.ms > currentTime || currentTime > circle.endTime){
|
2018-09-21 22:31:35 +02:00
|
|
|
return true
|
2018-09-18 19:33:18 +02:00
|
|
|
}
|
2018-09-21 22:31:35 +02:00
|
|
|
if(keysDon && type === "balloon"){
|
|
|
|
this.checkBalloon(circle)
|
2019-02-17 17:26:46 +01:00
|
|
|
if(check === "daiDon" && !circle.isPlayed){
|
2018-09-21 22:31:35 +02:00
|
|
|
this.checkBalloon(circle)
|
|
|
|
}
|
|
|
|
}else if((keysDon || keysKa) && (type === "drumroll" || type === "daiDrumroll")){
|
2018-11-13 05:36:15 +01:00
|
|
|
this.checkDrumroll(circle, keysKa)
|
2018-09-21 22:31:35 +02:00
|
|
|
if(keyDai){
|
2018-11-13 05:36:15 +01:00
|
|
|
this.checkDrumroll(circle, keysKa)
|
2018-09-21 22:31:35 +02:00
|
|
|
}
|
2018-09-18 19:33:18 +02:00
|
|
|
}
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
2018-09-18 19:33:18 +02:00
|
|
|
return true
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
|
|
|
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", {
|
2019-02-17 17:26:46 +01:00
|
|
|
pace: (this.elapsedTime - circle.ms) / circle.timesHit
|
2018-09-15 16:34:53 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
var score = 300
|
|
|
|
circle.hit()
|
|
|
|
}
|
2019-02-17 17:26:46 +01:00
|
|
|
this.globalScore.drumroll++
|
|
|
|
this.sectionDrumroll++
|
2018-09-15 16:34:53 +02:00
|
|
|
this.globalScore.points += score
|
2018-12-02 17:53:12 +01:00
|
|
|
this.view.setDarkBg(false)
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
2018-11-13 05:36:15 +01:00
|
|
|
checkDrumroll(circle, keysKa){
|
2018-10-07 20:58:42 +02:00
|
|
|
var ms = this.elapsedTime
|
2019-02-17 17:26:46 +01:00
|
|
|
var dai = circle.type === "daiDrumroll"
|
2018-09-15 16:34:53 +02:00
|
|
|
var score = 100
|
2019-02-20 21:48:21 +01:00
|
|
|
if(circle.section && circle.timesHit === 0){
|
|
|
|
this.resetSection()
|
|
|
|
}
|
2018-11-13 05:36:15 +01:00
|
|
|
circle.hit(keysKa)
|
2018-09-15 16:34:53 +02:00
|
|
|
var keyTime = this.controller.getKeyTime()
|
2019-02-17 17:26:46 +01:00
|
|
|
if(circle.type === "drumroll"){
|
2018-09-15 16:34:53 +02:00
|
|
|
var sound = keyTime["don"] > keyTime["ka"] ? "don" : "ka"
|
|
|
|
}else{
|
|
|
|
var sound = keyTime["don"] > keyTime["ka"] ? "daiDon" : "daiKa"
|
|
|
|
}
|
2018-09-20 01:20:26 +02:00
|
|
|
var circleAnim = new Circle({
|
|
|
|
id: 0,
|
2018-10-07 20:58:42 +02:00
|
|
|
start: ms,
|
2018-09-20 01:20:26 +02:00
|
|
|
type: sound,
|
|
|
|
txt: "",
|
|
|
|
speed: circle.speed,
|
2019-01-26 19:29:13 +01:00
|
|
|
gogoTime: circle.gogoTime,
|
|
|
|
fixedPos: document.hasFocus()
|
2018-09-20 01:20:26 +02:00
|
|
|
})
|
2018-09-18 19:33:18 +02:00
|
|
|
circleAnim.played(score, dai)
|
2018-10-07 20:58:42 +02:00
|
|
|
circleAnim.animate(ms)
|
2018-10-11 22:24:18 +02:00
|
|
|
this.view.drumroll.push(circleAnim)
|
2018-09-18 19:33:18 +02:00
|
|
|
this.globalScore.drumroll++
|
2019-02-20 21:48:21 +01:00
|
|
|
this.sectionDrumroll++
|
2018-09-18 19:33:18 +02:00
|
|
|
this.globalScore.points += score * (dai ? 2 : 1)
|
2018-12-02 17:53:12 +01:00
|
|
|
this.view.setDarkBg(false)
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
|
|
|
whenLastCirclePlayed(){
|
2018-10-03 11:48:18 +02:00
|
|
|
var ms = this.elapsedTime
|
2018-11-13 05:36:15 +01:00
|
|
|
if(!this.lastCircle){
|
|
|
|
var circles = this.songData.circles
|
2019-02-17 17:26:46 +01:00
|
|
|
this.lastCircle = circles[circles.length - 1].endTime
|
2018-11-13 05:36:15 +01:00
|
|
|
if(this.controller.multiplayer){
|
|
|
|
var syncWith = this.controller.syncWith
|
|
|
|
var syncCircles = syncWith.game.songData.circles
|
2019-02-17 17:26:46 +01:00
|
|
|
var syncLastCircle = syncCircles[syncCircles.length - 1].endTime
|
2018-11-13 05:36:15 +01:00
|
|
|
if(syncLastCircle > this.lastCircle){
|
|
|
|
this.lastCircle = syncLastCircle
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(!this.fadeOutStarted && ms >= this.lastCircle + 2000){
|
2018-09-18 00:37:59 +02:00
|
|
|
this.fadeOutStarted = ms
|
2018-11-13 05:36:15 +01:00
|
|
|
if(this.controller.multiplayer){
|
|
|
|
this.controller.syncWith.game.fadeOutStarted = ms
|
|
|
|
}
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
whenFadeoutMusic(){
|
2018-09-18 00:37:59 +02:00
|
|
|
var started = this.fadeOutStarted
|
|
|
|
if(started){
|
2018-10-03 11:48:18 +02:00
|
|
|
var ms = this.elapsedTime
|
2019-03-05 22:48:30 +01:00
|
|
|
var duration = this.mainAsset ? this.mainAsset.duration : 0
|
|
|
|
var musicDuration = duration * 1000 - this.controller.offset
|
2018-09-18 00:37:59 +02:00
|
|
|
if(this.musicFadeOut === 0){
|
|
|
|
if(this.controller.multiplayer === 1){
|
2018-09-26 02:26:42 +02:00
|
|
|
p2.send("gameresults", this.getGlobalScore())
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
|
|
|
this.musicFadeOut++
|
2018-09-18 00:37:59 +02:00
|
|
|
}else if(this.musicFadeOut === 1 && ms >= started + 1600){
|
|
|
|
this.controller.gameEnded()
|
2019-01-25 02:42:05 +01:00
|
|
|
if(!p2.session && this.controller.multiplayer === 1){
|
2018-11-01 23:05:18 +01:00
|
|
|
p2.send("gameend")
|
|
|
|
}
|
2018-09-18 00:37:59 +02:00
|
|
|
this.musicFadeOut++
|
2018-10-11 00:13:24 +02:00
|
|
|
}else if(this.musicFadeOut === 2 && (ms >= started + 8600 && ms >= musicDuration + 250)){
|
2018-09-18 00:37:59 +02:00
|
|
|
this.controller.displayResults()
|
2018-09-15 16:34:53 +02:00
|
|
|
this.musicFadeOut++
|
2018-10-11 00:13:24 +02:00
|
|
|
}else if(this.musicFadeOut === 3 && (ms >= started + 9600 && ms >= musicDuration + 1250)){
|
2018-10-05 19:03:59 +02:00
|
|
|
this.controller.clean()
|
2018-10-01 09:33:43 +02:00
|
|
|
if(this.controller.scoresheet){
|
|
|
|
this.controller.scoresheet.startRedraw()
|
|
|
|
}
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
playMainMusic(){
|
2018-10-11 00:13:24 +02:00
|
|
|
var ms = this.elapsedTime + this.controller.offset
|
|
|
|
if(!this.mainMusicPlaying && (!this.fadeOutStarted || ms < this.fadeOutStarted + 1600)){
|
2019-03-05 22:48:30 +01:00
|
|
|
if(this.controller.multiplayer !== 2 && this.mainAsset){
|
2018-09-15 16:34:53 +02:00
|
|
|
this.mainAsset.play((ms < 0 ? -ms : 0) / 1000, false, Math.max(0, ms / 1000))
|
|
|
|
}
|
|
|
|
this.mainMusicPlaying = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
togglePause(){
|
|
|
|
if(!this.paused){
|
2019-02-04 10:14:42 +01:00
|
|
|
assets.sounds["se_pause"].play()
|
2018-09-15 16:34:53 +02:00
|
|
|
this.paused = true
|
2018-12-13 10:18:52 +01:00
|
|
|
this.latestDate = Date.now()
|
2019-03-05 22:48:30 +01:00
|
|
|
if(this.mainAsset){
|
|
|
|
this.mainAsset.stop()
|
|
|
|
}
|
2018-09-15 16:34:53 +02:00
|
|
|
this.mainMusicPlaying = false
|
2018-11-12 11:32:02 +01:00
|
|
|
this.view.pauseMove(0, true)
|
2018-11-23 17:53:29 +01:00
|
|
|
this.view.gameDiv.classList.add("game-paused")
|
2018-11-24 15:09:08 +01:00
|
|
|
this.view.lastMousemove = this.view.getMS()
|
|
|
|
this.view.cursorHidden = false
|
Custom scripting, #song=, translations
- A song can be linked directly by adding "#song=<id>" to the url, replace `<id>` with the id in the database, after loading it jumps immediately jumps to the difficulty selection
- Added tutorial translations
- Fixed song preview not playing
- Use text fallback for the logo when there are no vectors
- Increased combo cache by 1 pixel
- A custom javascript file can be loaded from config.json by defining "custom_js" value
- Added lots of events to help writing custom js files: `version-link, title-screen, language-change, song-select, song-select-move, song-select-difficulty, song-select-back, about, about-link, tutorial, import-songs, import-songs-default, session, session-start, session-end, debug, load-song, load-song-player2, load-song-unfocused, load-song-cancel, load-song-error, game-start, key-events, p2-game-end, p2-disconnected, p2-abandoned, pause, unpause, pause-restart, pause-song-select, game-lag, scoresheet, scoresheet-player2`
- Event syntax example:
```js
addEventListener("game-start", event => {
console.log("game-start", event.detail)
})
```
2019-02-14 10:32:45 +01:00
|
|
|
pageEvents.send("pause")
|
2018-09-15 16:34:53 +02:00
|
|
|
}else{
|
2019-02-04 10:14:42 +01:00
|
|
|
assets.sounds["se_cancel"].play()
|
2018-09-15 16:34:53 +02:00
|
|
|
this.paused = false
|
2018-12-13 10:18:52 +01:00
|
|
|
var currentDate = Date.now()
|
2018-10-03 16:22:40 +02:00
|
|
|
this.startDate += currentDate - this.latestDate
|
|
|
|
this.sndTime = currentDate - snd.buffer.getTime() * 1000
|
2018-11-23 17:53:29 +01:00
|
|
|
this.view.gameDiv.classList.remove("game-paused")
|
2018-11-24 15:09:08 +01:00
|
|
|
this.view.pointer()
|
Custom scripting, #song=, translations
- A song can be linked directly by adding "#song=<id>" to the url, replace `<id>` with the id in the database, after loading it jumps immediately jumps to the difficulty selection
- Added tutorial translations
- Fixed song preview not playing
- Use text fallback for the logo when there are no vectors
- Increased combo cache by 1 pixel
- A custom javascript file can be loaded from config.json by defining "custom_js" value
- Added lots of events to help writing custom js files: `version-link, title-screen, language-change, song-select, song-select-move, song-select-difficulty, song-select-back, about, about-link, tutorial, import-songs, import-songs-default, session, session-start, session-end, debug, load-song, load-song-player2, load-song-unfocused, load-song-cancel, load-song-error, game-start, key-events, p2-game-end, p2-disconnected, p2-abandoned, pause, unpause, pause-restart, pause-song-select, game-lag, scoresheet, scoresheet-player2`
- Event syntax example:
```js
addEventListener("game-start", event => {
console.log("game-start", event.detail)
})
```
2019-02-14 10:32:45 +01:00
|
|
|
pageEvents.send("unpause", currentDate - this.latestDate)
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
isPaused(){
|
|
|
|
return this.paused
|
|
|
|
}
|
|
|
|
updateTime(){
|
|
|
|
// Refreshed date
|
2018-10-03 11:48:18 +02:00
|
|
|
var ms = this.elapsedTime
|
2018-09-15 16:34:53 +02:00
|
|
|
if(ms >= 0 && !this.started){
|
2018-12-13 10:18:52 +01:00
|
|
|
this.startDate = Date.now()
|
2018-10-03 11:48:18 +02:00
|
|
|
this.elapsedTime = this.getAccurateTime()
|
2018-09-15 16:34:53 +02:00
|
|
|
this.started = true
|
2018-10-03 16:22:40 +02:00
|
|
|
this.sndTime = this.startDate - snd.buffer.getTime() * 1000
|
2018-09-15 16:34:53 +02:00
|
|
|
}else if(ms < 0 || ms >= 0 && this.started){
|
2018-12-13 10:18:52 +01:00
|
|
|
var currentDate = Date.now()
|
2019-03-15 22:34:48 +01:00
|
|
|
if(this.gameLagSync){
|
2018-10-06 09:23:36 +02:00
|
|
|
var sndTime = currentDate - snd.buffer.getTime() * 1000
|
|
|
|
var lag = sndTime - this.sndTime
|
|
|
|
if(Math.abs(lag) >= 50){
|
|
|
|
this.startDate += lag
|
|
|
|
this.sndTime = sndTime
|
Custom scripting, #song=, translations
- A song can be linked directly by adding "#song=<id>" to the url, replace `<id>` with the id in the database, after loading it jumps immediately jumps to the difficulty selection
- Added tutorial translations
- Fixed song preview not playing
- Use text fallback for the logo when there are no vectors
- Increased combo cache by 1 pixel
- A custom javascript file can be loaded from config.json by defining "custom_js" value
- Added lots of events to help writing custom js files: `version-link, title-screen, language-change, song-select, song-select-move, song-select-difficulty, song-select-back, about, about-link, tutorial, import-songs, import-songs-default, session, session-start, session-end, debug, load-song, load-song-player2, load-song-unfocused, load-song-cancel, load-song-error, game-start, key-events, p2-game-end, p2-disconnected, p2-abandoned, pause, unpause, pause-restart, pause-song-select, game-lag, scoresheet, scoresheet-player2`
- Event syntax example:
```js
addEventListener("game-start", event => {
console.log("game-start", event.detail)
})
```
2019-02-14 10:32:45 +01:00
|
|
|
pageEvents.send("game-lag", lag)
|
2018-10-06 09:23:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
this.elapsedTime = currentDate - this.startDate
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
|
|
|
}
|
2018-09-21 22:31:35 +02:00
|
|
|
getAccurateTime(){
|
2018-09-26 23:12:50 +02:00
|
|
|
if(this.isPaused()){
|
2018-10-03 11:48:18 +02:00
|
|
|
return this.elapsedTime
|
2018-09-26 23:12:50 +02:00
|
|
|
}else{
|
2018-12-13 10:18:52 +01:00
|
|
|
return Date.now() - this.startDate
|
2018-09-26 23:12:50 +02:00
|
|
|
}
|
2018-09-21 22:31:35 +02:00
|
|
|
}
|
2018-09-15 16:34:53 +02:00
|
|
|
getCircles(){
|
|
|
|
return this.songData.circles
|
|
|
|
}
|
|
|
|
updateCurrentCircle(){
|
2019-02-17 17:26:46 +01:00
|
|
|
var circles = this.songData.circles
|
|
|
|
do{
|
|
|
|
var circle = circles[++this.currentCircle]
|
|
|
|
}while(circle && circle.branch && !circle.branch.active)
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
|
|
|
getCurrentCircle(){
|
|
|
|
return this.currentCircle
|
|
|
|
}
|
|
|
|
updateCombo(score){
|
2018-09-18 19:33:18 +02:00
|
|
|
if(score !== 0){
|
2018-09-15 16:34:53 +02:00
|
|
|
this.combo++
|
|
|
|
}else{
|
|
|
|
this.combo = 0
|
|
|
|
}
|
|
|
|
if(this.combo > this.globalScore.maxCombo){
|
|
|
|
this.globalScore.maxCombo = this.combo
|
|
|
|
}
|
2018-12-31 18:03:54 +01:00
|
|
|
if(this.combo === 50 || this.combo > 0 && this.combo % 100 === 0 && this.combo < 1500 || this.combo > 0 && this.combo % 500 === 0){
|
2019-02-04 10:14:42 +01:00
|
|
|
this.controller.playSoundMeka("v_combo_" + (this.combo <= 1400 ? this.combo : "over1500"))
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
2018-10-11 22:24:18 +02:00
|
|
|
this.view.updateCombo(this.combo)
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
|
|
|
getCombo(){
|
|
|
|
return this.combo
|
|
|
|
}
|
|
|
|
getGlobalScore(){
|
|
|
|
return this.globalScore
|
|
|
|
}
|
2018-09-20 01:20:26 +02:00
|
|
|
updateGlobalScore(score, multiplier, gogoTime){
|
2018-09-15 16:34:53 +02:00
|
|
|
// Circle score
|
|
|
|
switch(score){
|
|
|
|
case 450:
|
2018-10-01 09:33:43 +02:00
|
|
|
this.globalScore.good++
|
2018-09-15 16:34:53 +02:00
|
|
|
break
|
|
|
|
case 230:
|
2018-10-01 09:33:43 +02:00
|
|
|
this.globalScore.ok++
|
2018-09-15 16:34:53 +02:00
|
|
|
break
|
|
|
|
case 0:
|
2018-10-01 09:33:43 +02:00
|
|
|
this.globalScore.bad++
|
2018-09-15 16:34:53 +02:00
|
|
|
break
|
|
|
|
}
|
2018-10-01 09:33:43 +02:00
|
|
|
// Gauge update
|
2018-09-18 19:33:18 +02:00
|
|
|
if(score !== 0){
|
2018-10-01 09:33:43 +02:00
|
|
|
this.globalScore.gauge += this.HPGain
|
|
|
|
}else if(this.globalScore.gauge - this.HPGain > 0){
|
|
|
|
this.globalScore.gauge -= this.HPGain
|
2018-09-15 16:34:53 +02:00
|
|
|
}else{
|
2018-10-01 09:33:43 +02:00
|
|
|
this.globalScore.gauge = 0
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
|
|
|
// Points update
|
|
|
|
score += Math.max(0, Math.floor((Math.min(this.combo, 100) - 1) / 10) * 100)
|
|
|
|
|
2018-09-20 01:20:26 +02:00
|
|
|
if(gogoTime){
|
|
|
|
multiplier *= 1.2
|
|
|
|
}
|
|
|
|
this.globalScore.points += Math.floor(score * multiplier / 10) * 10
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|
2019-02-20 21:48:21 +01:00
|
|
|
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
|
2019-02-17 17:26:46 +01:00
|
|
|
}
|
|
|
|
}
|
2019-02-20 21:48:21 +01:00
|
|
|
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
|
2019-02-17 17:26:46 +01:00
|
|
|
if(this.controller.multiplayer === 1){
|
2019-02-20 21:48:21 +01:00
|
|
|
p2.send("branch", activeName)
|
2019-02-17 17:26:46 +01:00
|
|
|
}
|
|
|
|
}
|
2019-02-20 21:48:21 +01:00
|
|
|
resetSection(){
|
|
|
|
this.sectionNotes = []
|
|
|
|
this.sectionDrumroll = 0
|
|
|
|
}
|
2018-09-15 16:34:53 +02:00
|
|
|
}
|