taiko-web/public/src/js/controller.js

392 lines
10 KiB
JavaScript
Raw Normal View History

2018-09-12 19:10:00 +02:00
class Controller{
constructor(...args){
this.init(...args)
}
init(selectedSong, songData, autoPlayEnabled, multiplayer, touchEnabled){
2018-09-12 19:10:00 +02:00
this.selectedSong = selectedSong
this.songData = songData
this.autoPlayEnabled = autoPlayEnabled
this.saveScore = !autoPlayEnabled
2018-09-12 19:10:00 +02:00
this.multiplayer = multiplayer
2018-10-05 19:03:59 +02:00
this.touchEnabled = touchEnabled
if(multiplayer === 2){
this.snd = p2.player === 2 ? "_p1" : "_p2"
this.don = p2.don || defaultDon
}else{
this.snd = multiplayer ? "_p" + p2.player : ""
this.don = account.loggedIn ? account.don : defaultDon
}
if(this.snd === "_p2" && this.objEqual(defaultDon, this.don)){
this.don = {
body_fill: defaultDon.face_fill,
face_fill: defaultDon.body_fill
}
}
2018-09-12 19:10:00 +02:00
this.calibrationMode = selectedSong.folder === "calibration"
this.audioLatency = 0
this.videoLatency = 0
if(!this.calibrationMode){
var latency = settings.getItem("latency")
if(!autoPlayEnabled || this.multiplayer){
this.audioLatency = Math.round(latency.audio) || 0
}
this.videoLatency = Math.round(latency.video) || 0 + this.audioLatency
}
if(this.multiplayer !== 2){
loader.changePage("game", false)
}
2018-10-11 00:13:24 +02:00
if(selectedSong.type === "tja"){
this.parsedSongData = new ParseTja(songData, selectedSong.difficulty, selectedSong.stars, selectedSong.offset)
2018-10-11 00:13:24 +02:00
}else{
this.parsedSongData = new ParseOsu(songData, selectedSong.difficulty, selectedSong.stars, selectedSong.offset)
2018-10-11 00:13:24 +02:00
}
this.offset = this.parsedSongData.soundOffset
2019-12-24 20:37:10 +01:00
var maxCombo = this.parsedSongData.circles.filter(circle => ["don", "ka", "daiDon", "daiKa"].indexOf(circle.type) > -1 && (!circle.branch || circle.branch.name == "master")).length
if (maxCombo >= 50) {
var comboVoices = ["v_combo_50"].concat(Array.from(Array(Math.min(50, Math.floor(maxCombo / 100))), (d, i) => "v_combo_" + ((i + 1) * 100)))
2019-12-24 20:37:10 +01:00
var promises = []
2019-12-24 20:37:10 +01:00
comboVoices.forEach(name => {
if (!assets.sounds[name + "_p1"]) {
promises.push(loader.loadSound(name + ".ogg", snd.sfxGain).then(sound => {
2019-12-24 20:37:10 +01:00
assets.sounds[name + "_p1"] = assets.sounds[name].copy(snd.sfxGainL)
assets.sounds[name + "_p2"] = assets.sounds[name].copy(snd.sfxGainR)
}))
}
})
2020-02-22 14:13:04 +01:00
Promise.all(promises)
2019-12-24 20:37:10 +01:00
}
2018-09-12 19:10:00 +02:00
if(this.calibrationMode){
this.volume = 1
}else{
assets.songs.forEach(song => {
if(song.id == this.selectedSong.folder){
this.mainAsset = song.sound
this.volume = song.volume || 1
2020-03-31 15:44:54 +02:00
if(!multiplayer && (!this.touchEnabled || this.autoPlayEnabled) && settings.getItem("showLyrics")){
if(song.lyricsData){
var lyricsDiv = document.getElementById("song-lyrics")
this.lyrics = new Lyrics(song.lyricsData, selectedSong.offset, lyricsDiv)
}else if(this.parsedSongData.lyrics){
var lyricsDiv = document.getElementById("song-lyrics")
this.lyrics = new Lyrics(this.parsedSongData.lyrics, selectedSong.offset, lyricsDiv, true)
}
2020-03-30 08:50:34 +02:00
}
}
})
}
2015-07-17 10:22:46 +02:00
2018-09-12 19:10:00 +02:00
this.game = new Game(this, this.selectedSong, this.parsedSongData)
2018-11-23 17:53:29 +01:00
this.view = new View(this)
2018-09-12 19:10:00 +02:00
this.mekadon = new Mekadon(this, this.game)
this.keyboard = new GameInput(this)
2020-02-29 16:56:59 +01:00
if(!autoPlayEnabled && this.multiplayer !== 2){
this.easierBigNotes = settings.getItem("easierBigNotes") || this.keyboard.keyboard.TaikoForceLv5
}else{
this.easierBigNotes = false
}
this.drumSounds = settings.getItem("latency").drumSounds
this.playedSounds = {}
2018-09-12 19:10:00 +02:00
}
run(syncWith){
2019-01-25 02:42:05 +01:00
if(syncWith){
this.syncWith = syncWith
}
if(this.multiplayer !== 2){
snd.musicGain.setVolumeMul(this.volume)
}
2018-09-12 19:10:00 +02:00
this.game.run()
this.view.run()
2019-01-25 02:42:05 +01:00
if(this.multiplayer === 1){
syncWith.run(this)
syncWith.game.elapsedTime = this.game.elapsedTime
syncWith.game.startDate = this.game.startDate
2018-09-12 19:10:00 +02:00
}
requestAnimationFrame(() => {
this.startMainLoop()
if(!this.multiplayer){
debugObj.controller = this
if(debugObj.debug){
debugObj.debug.updateStatus()
}
2018-10-14 20:08:05 +02:00
}
})
2018-09-12 19:10:00 +02:00
}
startMainLoop(){
this.mainLoopRunning = true
this.gameLoop()
this.viewLoop()
2019-01-25 02:42:05 +01:00
if(this.multiplayer !== 2){
this.gameInterval = setInterval(this.gameLoop.bind(this), 1000 / 60)
pageEvents.send("game-start", {
selectedSong: this.selectedSong,
autoPlayEnabled: this.autoPlayEnabled,
multiplayer: this.multiplayer,
touchEnabled: this.touchEnabled
})
2019-01-25 02:42:05 +01:00
}
2018-09-12 19:10:00 +02:00
}
2018-09-15 16:34:53 +02:00
stopMainLoop(){
this.mainLoopRunning = false
if(this.game.mainAsset){
this.game.mainAsset.stop()
}
2019-01-25 02:42:05 +01:00
if(this.multiplayer !== 2){
clearInterval(this.gameInterval)
}
2018-09-15 16:34:53 +02:00
}
gameLoop(){
if(this.mainLoopRunning){
2019-01-25 02:42:05 +01:00
if(this.multiplayer === 1){
this.syncWith.game.elapsedTime = this.game.elapsedTime
this.syncWith.game.startDate = this.game.startDate
}
var ms = this.game.elapsedTime
2019-02-03 13:04:25 +01:00
if(this.game.musicFadeOut < 3){
this.keyboard.checkMenuKeys()
}
if(this.calibrationMode){
this.game.calibration()
}
if(!this.game.isPaused()){
this.keyboard.checkGameKeys()
if(ms < 0){
this.game.updateTime()
}else{
if(!this.calibrationMode){
this.game.update()
}
if(!this.mainLoopRunning){
return
}
this.game.playMainMusic()
}
}
2019-01-25 02:42:05 +01:00
if(this.multiplayer === 1){
this.syncWith.gameLoop()
}
}
}
viewLoop(){
2018-09-12 19:10:00 +02:00
if(this.mainLoopRunning){
2018-09-18 00:37:59 +02:00
if(this.multiplayer !== 2){
2018-09-12 19:10:00 +02:00
requestAnimationFrame(() => {
var player = this.multiplayer ? p2.player : 1
if(player === 1){
this.viewLoop()
}
2019-01-25 02:42:05 +01:00
if(this.multiplayer === 1){
this.syncWith.viewLoop()
2018-09-12 19:10:00 +02:00
}
if(player === 2){
this.viewLoop()
}
2018-10-25 16:18:41 +02:00
if(this.scoresheet){
if(this.view.ctx){
this.view.ctx.save()
this.view.ctx.setTransform(1, 0, 0, 1, 0, 0)
}
this.scoresheet.redraw()
if(this.view.ctx){
this.view.ctx.restore()
}
}
2018-09-12 19:10:00 +02:00
})
}
2018-11-12 11:32:02 +01:00
this.view.refresh()
2018-09-12 19:10:00 +02:00
}
}
2018-09-18 00:37:59 +02:00
gameEnded(){
2018-09-12 19:10:00 +02:00
var score = this.getGlobalScore()
var vp
2020-03-05 16:58:49 +01:00
if(this.game.rules.clearReached(score.gauge)){
2018-10-25 16:18:41 +02:00
if(score.bad === 0){
vp = "fullcombo"
2019-12-24 19:10:40 +01:00
this.playSound("v_fullcombo", 1.350)
2018-10-25 16:18:41 +02:00
}else{
vp = "clear"
}
2018-09-15 16:34:53 +02:00
}else{
2018-09-12 19:10:00 +02:00
vp = "fail"
}
2019-02-04 10:14:42 +01:00
this.playSound("se_game" + vp)
2018-09-18 00:37:59 +02:00
}
displayResults(){
if(this.multiplayer !== 2){
if(this.view.cursorHidden){
this.view.canvas.style.cursor = ""
}
this.scoresheet = new Scoresheet(this, this.getGlobalScore(), this.multiplayer, this.touchEnabled)
2018-09-18 00:37:59 +02:00
}
2018-09-12 19:10:00 +02:00
}
2018-10-25 16:18:41 +02:00
displayScore(score, notPlayed, bigNote){
this.view.displayScore(score, notPlayed, bigNote)
2018-09-12 19:10:00 +02:00
}
songSelection(fadeIn, showWarning){
Bug fixes - Change song select mouse wheel song scrolling to be instant - Clicking on don chan in account settings toggles the animation - If the music is too long for the chart, the results screen is shown earlier - Fix weird BPM values freezing the browser (zero, negative, and very large) - Add a warning to the page when JavaScript is disabled in the browser - Fix Chrome auto dark mode by forcing light mode on the page - Add a meta keywords tag to the page - Fix plugin names getting cut off in the menu - Delay the function editing of the EditFunction class in plugins to the start() function instead of load() - When stopping one of the plugins, all the plugins have to be stopped in reverse order and started again so that patched code of a stopped plugin does not linger around - Fix importing plugins that have a SyntaxError - Fix plugins getting the same internal name when added without one, causing them to not appear in the plugin settings - Support editing args in EditFunction for plugins - Prevent multiple websockets from being opened - Fix page freezing after selecting Random song with no songs - Fix the back button being repeated twice when there are no songs - Fix /admin/users not accepting case insensitive usernames - Pressing enter on the Delete Account field does the expected action instead of refreshing the page - Better error message when custom folder access is denied - Fix being able to start netplay in custom songs after refreshing the page (#383) - Fix an error when importing songs from previous session and clicking on the white spot where you normally start multiplayer session - Fix canvas elements becoming smaller than 1x1 resolution and crashing the game (#390) - Fix song frame shadow cache on song select not being cleared when resizing the browser window, causing it to become blurry - Fix a pause-restart error when you hit both confirm keys on the restart button
2022-02-17 21:50:07 +01:00
if(this.cleaned){
return
}
2018-10-01 12:02:28 +02:00
if(!fadeIn){
this.clean()
}
if(this.calibrationMode){
new SettingsView(this.touchEnabled, false, null, "latency")
}else{
new SongSelect(false, fadeIn, this.touchEnabled, null, showWarning)
}
2018-09-12 19:10:00 +02:00
}
restartSong(){
Bug fixes - Change song select mouse wheel song scrolling to be instant - Clicking on don chan in account settings toggles the animation - If the music is too long for the chart, the results screen is shown earlier - Fix weird BPM values freezing the browser (zero, negative, and very large) - Add a warning to the page when JavaScript is disabled in the browser - Fix Chrome auto dark mode by forcing light mode on the page - Add a meta keywords tag to the page - Fix plugin names getting cut off in the menu - Delay the function editing of the EditFunction class in plugins to the start() function instead of load() - When stopping one of the plugins, all the plugins have to be stopped in reverse order and started again so that patched code of a stopped plugin does not linger around - Fix importing plugins that have a SyntaxError - Fix plugins getting the same internal name when added without one, causing them to not appear in the plugin settings - Support editing args in EditFunction for plugins - Prevent multiple websockets from being opened - Fix page freezing after selecting Random song with no songs - Fix the back button being repeated twice when there are no songs - Fix /admin/users not accepting case insensitive usernames - Pressing enter on the Delete Account field does the expected action instead of refreshing the page - Better error message when custom folder access is denied - Fix being able to start netplay in custom songs after refreshing the page (#383) - Fix an error when importing songs from previous session and clicking on the white spot where you normally start multiplayer session - Fix canvas elements becoming smaller than 1x1 resolution and crashing the game (#390) - Fix song frame shadow cache on song select not being cleared when resizing the browser window, causing it to become blurry - Fix a pause-restart error when you hit both confirm keys on the restart button
2022-02-17 21:50:07 +01:00
if(this.cleaned){
return
}
2018-09-18 00:37:59 +02:00
this.clean()
if(this.multiplayer){
2018-12-13 10:18:52 +01:00
new LoadSong(this.selectedSong, false, true, this.touchEnabled)
2018-09-18 00:37:59 +02:00
}else{
new Promise(resolve => {
if(this.calibrationMode){
resolve()
}else{
var songObj = assets.songs.find(song => song.id === this.selectedSong.folder)
var promises = []
if(songObj.chart && songObj.chart !== "blank"){
var chart = songObj.chart
if(chart.separateDiff){
var chartDiff = this.selectedSong.difficulty
chart = chart[chartDiff]
}
this.addPromise(promises, chart.read(this.selectedSong.type === "tja" ? "sjis" : undefined).then(data => {
this.songData = data.replace(/\0/g, "").split("\n")
return Promise.resolve()
}), chart.url)
}
if(songObj.lyricsFile){
this.addPromise(promises, songObj.lyricsFile.read().then(result => {
songObj.lyricsData = result
}, () => Promise.resolve()), songObj.lyricsFile.url)
}
Promise.all(promises).then(resolve)
}
}).then(() => {
var taikoGame = new Controller(this.selectedSong, this.songData, this.autoPlayEnabled, false, this.touchEnabled)
taikoGame.run()
})
2018-09-18 00:37:59 +02:00
}
2018-09-12 19:10:00 +02:00
}
addPromise(promises, promise, url){
promises.push(promise.catch(error => {
if(this.restartSongError){
return
}
this.restartSongError = true
if(url){
error = (Array.isArray(error) ? error[0] + ": " : (error ? error + ": " : "")) + url
}
pageEvents.send("load-song-error", error)
errorMessage(new Error(error).stack)
var title = this.selectedSong.title
if(title !== this.selectedSong.originalTitle){
title += " (" + this.selectedSong.originalTitle + ")"
}
setTimeout(() => {
new SongSelect(false, false, this.touchEnabled, null, {
name: "loadSongError",
title: title,
id: this.selectedSong.folder,
error: error
})
}, 500)
return Promise.reject(error)
}))
}
playSound(id, time, noSnd){
if(!this.drumSounds && (id === "neiro_1_don" || id === "neiro_1_ka" || id === "se_don" || id === "se_ka")){
return
}
2018-12-13 10:18:52 +01:00
var ms = Date.now() + (time || 0) * 1000
if(!(id in this.playedSounds) || ms > this.playedSounds[id] + 30){
assets.sounds[id + (noSnd ? "" : this.snd)].play(time)
this.playedSounds[id] = ms
}
2018-10-03 11:48:18 +02:00
}
togglePause(forcePause, pauseMove, noSound){
2019-01-25 02:42:05 +01:00
if(this.multiplayer === 1){
this.syncWith.game.togglePause(forcePause, pauseMove, noSound)
2018-09-12 19:10:00 +02:00
}
this.game.togglePause(forcePause, pauseMove, noSound)
2018-09-12 19:10:00 +02:00
}
getKeys(){
return this.keyboard.getKeys()
}
setKey(pressed, name, ms){
return this.keyboard.setKey(pressed, name, ms)
2018-09-12 19:10:00 +02:00
}
getElapsedTime(){
2018-10-03 11:48:18 +02:00
return this.game.elapsedTime
2018-09-12 19:10:00 +02:00
}
getCircles(){
return this.game.getCircles()
}
getCurrentCircle(){
return this.game.getCurrentCircle()
}
isWaiting(key, type){
return this.keyboard.isWaiting(key, type)
2018-09-12 19:10:00 +02:00
}
waitForKeyup(key, type){
this.keyboard.waitForKeyup(key, type)
}
getKeyTime(){
return this.keyboard.getKeyTime()
}
getCombo(){
return this.game.getCombo()
}
getGlobalScore(){
return this.game.getGlobalScore()
}
autoPlay(circle){
if(this.multiplayer){
p2.play(circle, this.mekadon)
}else{
return this.mekadon.play(circle)
2018-09-12 19:10:00 +02:00
}
}
objEqual(a, b){
for(var i in a){
if(a[i] !== b[i]){
return false
}
}
return true
}
2018-09-18 00:37:59 +02:00
clean(){
Bug fixes - Change song select mouse wheel song scrolling to be instant - Clicking on don chan in account settings toggles the animation - If the music is too long for the chart, the results screen is shown earlier - Fix weird BPM values freezing the browser (zero, negative, and very large) - Add a warning to the page when JavaScript is disabled in the browser - Fix Chrome auto dark mode by forcing light mode on the page - Add a meta keywords tag to the page - Fix plugin names getting cut off in the menu - Delay the function editing of the EditFunction class in plugins to the start() function instead of load() - When stopping one of the plugins, all the plugins have to be stopped in reverse order and started again so that patched code of a stopped plugin does not linger around - Fix importing plugins that have a SyntaxError - Fix plugins getting the same internal name when added without one, causing them to not appear in the plugin settings - Support editing args in EditFunction for plugins - Prevent multiple websockets from being opened - Fix page freezing after selecting Random song with no songs - Fix the back button being repeated twice when there are no songs - Fix /admin/users not accepting case insensitive usernames - Pressing enter on the Delete Account field does the expected action instead of refreshing the page - Better error message when custom folder access is denied - Fix being able to start netplay in custom songs after refreshing the page (#383) - Fix an error when importing songs from previous session and clicking on the white spot where you normally start multiplayer session - Fix canvas elements becoming smaller than 1x1 resolution and crashing the game (#390) - Fix song frame shadow cache on song select not being cleared when resizing the browser window, causing it to become blurry - Fix a pause-restart error when you hit both confirm keys on the restart button
2022-02-17 21:50:07 +01:00
this.cleaned = true
2019-01-25 02:42:05 +01:00
if(this.multiplayer === 1){
2018-10-05 19:03:59 +02:00
this.syncWith.clean()
}
2018-09-18 00:37:59 +02:00
this.stopMainLoop()
this.keyboard.clean()
this.view.clean()
snd.buffer.loadSettings()
2018-09-18 00:37:59 +02:00
if(!this.multiplayer){
2018-10-14 20:08:05 +02:00
debugObj.controller = null
if(debugObj.debug){
debugObj.debug.updateStatus()
}
}
2020-03-30 08:50:34 +02:00
if(this.lyrics){
this.lyrics.clean()
}
2018-09-18 00:37:59 +02:00
}
2018-09-12 19:10:00 +02:00
}