2018-09-11 00:17:13 +02:00
|
|
|
class Loader{
|
2018-10-06 15:24:23 +02:00
|
|
|
constructor(callback){
|
|
|
|
this.callback = callback
|
2018-09-11 00:17:13 +02:00
|
|
|
this.loadedAssets = 0
|
|
|
|
this.assetsDiv = document.getElementById("assets")
|
2019-01-16 13:33:42 +01:00
|
|
|
this.screen = document.getElementById("screen")
|
2018-12-13 10:18:52 +01:00
|
|
|
this.startTime = Date.now()
|
2018-10-14 12:37:27 +02:00
|
|
|
|
2019-01-16 13:33:42 +01:00
|
|
|
var promises = []
|
|
|
|
|
|
|
|
promises.push(this.ajax("/src/views/loader.html").then(page => {
|
|
|
|
this.screen.innerHTML = page
|
|
|
|
}))
|
|
|
|
|
|
|
|
promises.push(this.ajax("/api/config").then(conf => {
|
|
|
|
gameConfig = JSON.parse(conf)
|
|
|
|
}))
|
|
|
|
|
|
|
|
Promise.all(promises).then(this.run.bind(this))
|
2018-09-11 00:17:13 +02:00
|
|
|
}
|
2019-01-16 13:33:42 +01:00
|
|
|
run(){
|
2018-09-18 00:37:59 +02:00
|
|
|
this.promises = []
|
2020-03-09 13:36:57 +01:00
|
|
|
this.loaderDiv = document.querySelector("#loader")
|
2018-09-11 00:17:13 +02:00
|
|
|
this.loaderPercentage = document.querySelector("#loader .percentage")
|
2018-09-18 00:37:59 +02:00
|
|
|
this.loaderProgress = document.querySelector("#loader .progress")
|
2019-01-16 13:33:42 +01:00
|
|
|
|
2019-02-06 19:49:25 +01:00
|
|
|
var queryString = gameConfig._version.commit_short ? "?" + gameConfig._version.commit_short : ""
|
2019-01-16 13:33:42 +01:00
|
|
|
|
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
|
|
|
if(gameConfig.custom_js){
|
|
|
|
var script = document.createElement("script")
|
|
|
|
this.addPromise(pageEvents.load(script))
|
|
|
|
script.src = gameConfig.custom_js + queryString
|
|
|
|
document.head.appendChild(script)
|
|
|
|
}
|
2019-01-16 13:33:42 +01:00
|
|
|
assets.js.forEach(name => {
|
|
|
|
var script = document.createElement("script")
|
|
|
|
this.addPromise(pageEvents.load(script))
|
|
|
|
script.src = "/src/js/" + name + queryString
|
|
|
|
document.head.appendChild(script)
|
|
|
|
})
|
|
|
|
|
2019-04-04 22:40:11 +02:00
|
|
|
this.addPromise(new Promise((resolve, reject) => {
|
|
|
|
if(
|
|
|
|
versionLink.href !== gameConfig._version.url &&
|
|
|
|
gameConfig._version.commit &&
|
|
|
|
versionLink.href.indexOf(gameConfig._version.commit) === -1
|
|
|
|
){
|
|
|
|
// Version in the config does not match version on the page
|
|
|
|
reject()
|
|
|
|
}
|
2019-01-16 13:33:42 +01:00
|
|
|
var cssCount = document.styleSheets.length + assets.css.length
|
|
|
|
assets.css.forEach(name => {
|
|
|
|
var stylesheet = document.createElement("link")
|
|
|
|
stylesheet.rel = "stylesheet"
|
|
|
|
stylesheet.href = "/src/css/" + name + queryString
|
|
|
|
document.head.appendChild(stylesheet)
|
|
|
|
})
|
2019-02-06 19:27:31 +01:00
|
|
|
assets.assetsCss.forEach(name => {
|
|
|
|
var stylesheet = document.createElement("link")
|
|
|
|
stylesheet.rel = "stylesheet"
|
|
|
|
stylesheet.href = gameConfig.assets_baseurl + name + queryString
|
|
|
|
document.head.appendChild(stylesheet)
|
|
|
|
})
|
2019-01-16 13:33:42 +01:00
|
|
|
var checkStyles = () => {
|
|
|
|
if(document.styleSheets.length >= cssCount){
|
|
|
|
resolve()
|
|
|
|
clearInterval(interval)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var interval = setInterval(checkStyles, 100)
|
|
|
|
checkStyles()
|
|
|
|
}))
|
|
|
|
|
|
|
|
assets.fonts.forEach(name => {
|
|
|
|
var font = document.createElement("h1")
|
|
|
|
font.style.fontFamily = name
|
|
|
|
font.appendChild(document.createTextNode("I am a font"))
|
|
|
|
this.assetsDiv.appendChild(font)
|
|
|
|
})
|
|
|
|
|
|
|
|
assets.img.forEach(name => {
|
|
|
|
var id = this.getFilename(name)
|
|
|
|
var image = document.createElement("img")
|
|
|
|
this.addPromise(pageEvents.load(image))
|
|
|
|
image.id = name
|
|
|
|
image.src = gameConfig.assets_baseurl + "img/" + name
|
|
|
|
this.assetsDiv.appendChild(image)
|
|
|
|
assets.image[id] = image
|
|
|
|
})
|
|
|
|
|
|
|
|
assets.views.forEach(name => {
|
|
|
|
var id = this.getFilename(name)
|
|
|
|
this.addPromise(this.ajax("/src/views/" + name + queryString).then(page => {
|
|
|
|
assets.pages[id] = page
|
|
|
|
}))
|
|
|
|
})
|
|
|
|
|
|
|
|
this.addPromise(this.ajax("/api/songs").then(songs => {
|
|
|
|
assets.songsDefault = JSON.parse(songs)
|
|
|
|
assets.songs = assets.songsDefault
|
|
|
|
}))
|
|
|
|
|
2019-01-29 17:23:04 +01:00
|
|
|
this.addPromise(this.ajax(gameConfig.assets_baseurl + "img/vectors.json" + queryString).then(response => {
|
2019-01-29 17:10:56 +01:00
|
|
|
vectors = JSON.parse(response)
|
|
|
|
}))
|
|
|
|
|
2019-01-16 13:33:42 +01:00
|
|
|
this.afterJSCount =
|
2019-02-06 20:55:13 +01:00
|
|
|
["blurPerformance", "P2Connection"].length +
|
2019-01-16 13:33:42 +01:00
|
|
|
assets.fonts.length +
|
|
|
|
assets.audioSfx.length +
|
|
|
|
assets.audioMusic.length +
|
|
|
|
assets.audioSfxLR.length +
|
|
|
|
assets.audioSfxLoud.length
|
|
|
|
|
|
|
|
Promise.all(this.promises).then(() => {
|
|
|
|
|
|
|
|
snd.buffer = new SoundBuffer()
|
|
|
|
snd.musicGain = snd.buffer.createGain()
|
|
|
|
snd.sfxGain = snd.buffer.createGain()
|
|
|
|
snd.previewGain = snd.buffer.createGain()
|
|
|
|
snd.sfxGainL = snd.buffer.createGain("left")
|
|
|
|
snd.sfxGainR = snd.buffer.createGain("right")
|
|
|
|
snd.sfxLoudGain = snd.buffer.createGain()
|
|
|
|
snd.buffer.setCrossfade(
|
|
|
|
[snd.musicGain, snd.previewGain],
|
|
|
|
[snd.sfxGain, snd.sfxGainL, snd.sfxGainR],
|
|
|
|
0.5
|
|
|
|
)
|
|
|
|
snd.sfxLoudGain.setVolume(1.2)
|
2019-03-15 22:34:48 +01:00
|
|
|
snd.buffer.saveSettings()
|
2019-01-16 13:33:42 +01:00
|
|
|
|
2019-02-06 20:55:13 +01:00
|
|
|
this.afterJSCount = 0
|
2019-01-16 13:33:42 +01:00
|
|
|
|
2019-02-06 20:55:13 +01:00
|
|
|
assets.fonts.forEach(name => {
|
|
|
|
this.addPromise(new Promise(resolve => {
|
|
|
|
FontDetect.onFontLoaded(name, resolve, resolve, {msTimeout: Infinity})
|
2018-10-07 20:58:42 +02:00
|
|
|
}))
|
2019-02-06 20:55:13 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
assets.audioSfx.forEach(name => {
|
|
|
|
this.addPromise(this.loadSound(name, snd.sfxGain))
|
|
|
|
})
|
|
|
|
assets.audioMusic.forEach(name => {
|
|
|
|
this.addPromise(this.loadSound(name, snd.musicGain))
|
|
|
|
})
|
|
|
|
assets.audioSfxLR.forEach(name => {
|
|
|
|
this.addPromise(this.loadSound(name, snd.sfxGain).then(sound => {
|
|
|
|
var id = this.getFilename(name)
|
|
|
|
assets.sounds[id + "_p1"] = assets.sounds[id].copy(snd.sfxGainL)
|
|
|
|
assets.sounds[id + "_p2"] = assets.sounds[id].copy(snd.sfxGainR)
|
|
|
|
}))
|
|
|
|
})
|
|
|
|
assets.audioSfxLoud.forEach(name => {
|
|
|
|
this.addPromise(this.loadSound(name, snd.sfxLoudGain))
|
|
|
|
})
|
|
|
|
|
|
|
|
this.canvasTest = new CanvasTest()
|
|
|
|
this.addPromise(this.canvasTest.blurPerformance().then(result => {
|
|
|
|
perf.blur = result
|
|
|
|
if(result > 1000 / 50){
|
|
|
|
// Less than 50 fps with blur enabled
|
|
|
|
disableBlur = true
|
2018-11-01 23:05:18 +01:00
|
|
|
}
|
2019-01-16 13:33:42 +01:00
|
|
|
}))
|
2019-02-06 20:55:13 +01:00
|
|
|
|
2019-02-14 23:10:34 +01:00
|
|
|
var readyEvent = "normal"
|
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
|
|
|
var songId
|
|
|
|
var hashLower = location.hash.toLowerCase()
|
2019-02-06 20:55:13 +01:00
|
|
|
p2 = new P2Connection()
|
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
|
|
|
if(hashLower.startsWith("#song=")){
|
|
|
|
var number = parseInt(location.hash.slice(6))
|
|
|
|
if(number > 0){
|
|
|
|
songId = number
|
2019-02-14 23:10:34 +01:00
|
|
|
readyEvent = "song-id"
|
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
|
|
|
}
|
|
|
|
}else if(location.hash.length === 6){
|
2019-02-06 20:55:13 +01:00
|
|
|
p2.hashLock = true
|
|
|
|
this.addPromise(new Promise(resolve => {
|
|
|
|
p2.open()
|
|
|
|
pageEvents.add(p2, "message", response => {
|
|
|
|
if(response.type === "session"){
|
2019-02-14 23:10:34 +01:00
|
|
|
pageEvents.send("session-start", "invited")
|
|
|
|
readyEvent = "session-start"
|
2019-02-06 20:55:13 +01:00
|
|
|
resolve()
|
|
|
|
}else if(response.type === "gameend"){
|
|
|
|
p2.hash("")
|
|
|
|
p2.hashLock = false
|
2019-02-14 23:10:34 +01:00
|
|
|
readyEvent = "session-expired"
|
2019-02-06 20:55:13 +01:00
|
|
|
resolve()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
p2.send("invite", location.hash.slice(1).toLowerCase())
|
|
|
|
setTimeout(() => {
|
|
|
|
if(p2.socket.readyState !== 1){
|
|
|
|
p2.hash("")
|
|
|
|
p2.hashLock = false
|
|
|
|
resolve()
|
|
|
|
}
|
|
|
|
}, 10000)
|
|
|
|
}).then(() => {
|
|
|
|
pageEvents.remove(p2, "message")
|
|
|
|
}))
|
|
|
|
}else{
|
|
|
|
p2.hash("")
|
|
|
|
}
|
|
|
|
|
2019-04-04 22:40:11 +02:00
|
|
|
settings = new Settings()
|
2019-04-05 21:53:51 +02:00
|
|
|
pageEvents.setKbd()
|
2019-04-04 22:40:11 +02:00
|
|
|
|
2020-03-07 02:48:30 +01:00
|
|
|
scoreStorage = new ScoreStorage()
|
|
|
|
for(var i in assets.songsDefault){
|
|
|
|
var song = assets.songsDefault[i]
|
|
|
|
if(!song.hash){
|
|
|
|
song.hash = song.title
|
|
|
|
}
|
|
|
|
scoreStorage.songTitles[song.title] = song.hash
|
2020-03-09 13:36:57 +01:00
|
|
|
var score = scoreStorage.get(song.hash, false, true)
|
2020-03-07 02:48:30 +01:00
|
|
|
if(score){
|
|
|
|
score.title = song.title
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-06 20:55:13 +01:00
|
|
|
Promise.all(this.promises).then(() => {
|
|
|
|
this.canvasTest.drawAllImages().then(result => {
|
|
|
|
perf.allImg = result
|
|
|
|
perf.load = Date.now() - this.startTime
|
|
|
|
this.canvasTest.clean()
|
|
|
|
this.clean()
|
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
|
|
|
this.callback(songId)
|
2019-02-14 23:10:34 +01:00
|
|
|
pageEvents.send("ready", readyEvent)
|
2019-02-06 20:55:13 +01:00
|
|
|
})
|
|
|
|
}, this.errorMsg.bind(this))
|
|
|
|
|
|
|
|
|
2019-01-16 13:33:42 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
addPromise(promise){
|
|
|
|
this.promises.push(promise)
|
2019-04-04 22:40:11 +02:00
|
|
|
promise.then(this.assetLoaded.bind(this), this.errorMsg.bind(this))
|
2018-09-18 00:37:59 +02:00
|
|
|
}
|
|
|
|
loadSound(name, gain){
|
|
|
|
var id = this.getFilename(name)
|
2018-10-28 10:59:49 +01:00
|
|
|
return gain.load(gameConfig.assets_baseurl + "audio/" + name).then(sound => {
|
2018-09-18 00:37:59 +02:00
|
|
|
assets.sounds[id] = sound
|
2018-09-11 00:17:13 +02:00
|
|
|
})
|
2015-07-17 10:22:46 +02:00
|
|
|
}
|
2018-09-18 00:37:59 +02:00
|
|
|
getFilename(name){
|
|
|
|
return name.slice(0, name.lastIndexOf("."))
|
|
|
|
}
|
2018-12-03 23:23:11 +01:00
|
|
|
errorMsg(error){
|
2020-03-09 13:36:57 +01:00
|
|
|
if(Array.isArray(error) && error[1] instanceof HTMLElement){
|
|
|
|
error = error[0] + ": " + error[1].outerHTML
|
|
|
|
}
|
2018-12-03 23:23:11 +01:00
|
|
|
console.error(error)
|
2019-02-14 23:10:34 +01:00
|
|
|
pageEvents.send("loader-error", error)
|
2020-03-09 13:36:57 +01:00
|
|
|
if(!this.error){
|
|
|
|
this.error = true
|
|
|
|
this.loaderDiv.classList.add("loaderError")
|
|
|
|
if(typeof allStrings === "object"){
|
|
|
|
var lang = localStorage.lang
|
|
|
|
if(!lang){
|
|
|
|
var userLang = navigator.languages.slice()
|
|
|
|
userLang.unshift(navigator.language)
|
|
|
|
for(var i in userLang){
|
|
|
|
for(var j in allStrings){
|
|
|
|
if(allStrings[j].regex.test(userLang[i])){
|
|
|
|
lang = j
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(!lang){
|
|
|
|
lang = "en"
|
|
|
|
}
|
|
|
|
var errorOccured = allStrings[lang].errorOccured
|
|
|
|
}else{
|
|
|
|
var errorOccured = "An error occurred, please refresh"
|
|
|
|
}
|
|
|
|
this.loaderPercentage.appendChild(document.createElement("br"))
|
|
|
|
this.loaderPercentage.appendChild(document.createTextNode(errorOccured))
|
|
|
|
this.clean()
|
|
|
|
}
|
2015-07-17 10:22:46 +02:00
|
|
|
}
|
2018-09-11 00:17:13 +02:00
|
|
|
assetLoaded(){
|
2018-09-18 00:37:59 +02:00
|
|
|
if(!this.error){
|
|
|
|
this.loadedAssets++
|
2019-01-16 13:33:42 +01:00
|
|
|
var percentage = Math.floor(this.loadedAssets * 100 / (this.promises.length + this.afterJSCount))
|
2018-09-18 00:37:59 +02:00
|
|
|
this.loaderProgress.style.width = percentage + "%"
|
|
|
|
this.loaderPercentage.firstChild.data = percentage + "%"
|
|
|
|
}
|
|
|
|
}
|
2019-01-16 13:33:42 +01:00
|
|
|
changePage(name, patternBg){
|
2018-10-09 08:59:36 +02:00
|
|
|
this.screen.innerHTML = assets.pages[name]
|
2019-01-16 13:33:42 +01:00
|
|
|
this.screen.classList[patternBg ? "add" : "remove"]("pattern-bg")
|
2018-09-18 00:37:59 +02:00
|
|
|
}
|
|
|
|
ajax(url, customRequest){
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
var request = new XMLHttpRequest()
|
|
|
|
request.open("GET", url)
|
|
|
|
pageEvents.load(request).then(() => {
|
|
|
|
resolve(request.response)
|
|
|
|
}, reject)
|
|
|
|
if(customRequest){
|
|
|
|
customRequest(request)
|
|
|
|
}
|
|
|
|
request.send()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
clean(){
|
2018-12-13 10:18:52 +01:00
|
|
|
var fontDetectDiv = document.getElementById("fontdetectHelper")
|
2019-04-04 22:40:11 +02:00
|
|
|
if(fontDetectDiv){
|
|
|
|
fontDetectDiv.parentNode.removeChild(fontDetectDiv)
|
|
|
|
}
|
2018-09-18 00:37:59 +02:00
|
|
|
delete this.loaderPercentage
|
|
|
|
delete this.loaderProgress
|
|
|
|
delete this.promises
|
2018-10-12 20:04:28 +02:00
|
|
|
pageEvents.remove(root, "touchstart")
|
2015-07-17 10:22:46 +02:00
|
|
|
}
|
2018-09-11 00:17:13 +02:00
|
|
|
}
|