diff --git a/.gitignore b/.gitignore index 0bc88c6..994f1e6 100644 --- a/.gitignore +++ b/.gitignore @@ -46,4 +46,5 @@ public/api taiko.db version.json public/index.html -config.json \ No newline at end of file +config.json +public/assets/song_skins diff --git a/app.py b/app.py index 89976ef..1f7bf6b 100644 --- a/app.py +++ b/app.py @@ -154,17 +154,26 @@ def route_api_preview(): @app.route('/api/songs') def route_api_songs(): songs = query_db('select * from songs where enabled = 1') + raw_categories = query_db('select * from categories') categories = {} def_category = {'title': None, 'title_en': None} for cat in raw_categories: categories[cat[0]] = {'title': cat[1], 'title_en': cat[2]} + + raw_song_skins = query_db('select * from song_skins') + song_skins = {} + for skin in raw_song_skins: + song_skins[skin[0]] = {'name': skin[1], 'song': skin[2], 'stage': skin[3]} + songs_out = [] for song in songs: song_id = song[0] song_type = song[12] preview = get_preview(song_id, song_type) + category_out = categories[song[11]] if song[11] in categories else def_category + song_skin_out = song_skins[song[14]] if song[14] in categories else None songs_out.append({ 'id': song_id, @@ -179,7 +188,8 @@ def route_api_songs(): 'category': category_out['title'], 'category_en': category_out['title_en'], 'type': song_type, - 'offset': song[13] + 'offset': song[13], + 'song_skin': song_skin_out }) return jsonify(songs_out) diff --git a/public/src/css/songbg.css b/public/src/css/songbg.css index a9f1392..e3f147a 100644 --- a/public/src/css/songbg.css +++ b/public/src/css/songbg.css @@ -67,6 +67,15 @@ animation: 1s linear songbg-pulse infinite; mix-blend-mode: color-dodge; } +.songbg-strobe #layer2{ + animation: 0.4s linear songbg-strobe infinite; +} +.songbg-pulse #layer2{ + animation: 0.4s linear songbg-pulse infinite; +} +.songbg-slowfade #layer2{ + animation: 2s cubic-bezier(0.68, -0.55, 0.27, 1.55) songbg-pulse infinite; +} .touch-visible #layer2{ display: none; background-image: none; diff --git a/public/src/js/loadsong.js b/public/src/js/loadsong.js index ff8d175..32c0ee4 100644 --- a/public/src/js/loadsong.js +++ b/public/src/js/loadsong.js @@ -4,26 +4,60 @@ class loadSong{ this.autoPlayEnabled = autoPlayEnabled this.multiplayer = multiplayer this.touchEnabled = touchEnabled + loader.changePage("loadsong") this.run() } run(){ - var id = this.selectedSong.folder + var song = this.selectedSong + var id = song.folder var promises = [] assets.sounds["start"].play() - this.selectedSong.songBg = this.randInt(1, 5) - this.selectedSong.songStage = this.randInt(1, 3) - - promises.push(new Promise(resolve => { - var img = document.createElement("img") - pageEvents.load(img).then(() => { - this.selectedSong.customBg = true - }, () => this.songBg(id)).then(resolve) - img.id = "music-bg" - img.src = gameConfig.songs_baseurl + id + "/bg.png" - document.getElementById("assets").appendChild(img) - })) + song.songBg = this.randInt(1, 5) + song.songStage = this.randInt(1, 3) + if(song.songSkin && song.songSkin.name){ + var imgLoad = [] + for(var type in song.songSkin){ + var value = song.songSkin[type] + if(type !== "name" && value && value !== "none"){ + var filename = "bg_" + type + "_" + song.songSkin.name + if(value === "static"){ + imgLoad.push({ + filename: filename, + type: type + }) + }else{ + imgLoad.push({ + filename: filename + "_a", + type: type + }) + imgLoad.push({ + filename: filename + "_b", + type: type + }) + } + } + } + var skinBase = gameConfig.assets_baseurl + "song_skins/" + for(var i = 0; i < imgLoad.length; i++){ + let img = document.createElement("img") + let filename = imgLoad[i].filename + let promise = pageEvents.load(img) + if(imgLoad[i].type === "song"){ + promises.push(promise.then(() => { + return this.scaleImg(img, filename) + })) + }else{ + promises.push(promise.then(() => { + assets.image[filename] = img + })) + } + img.src = skinBase + filename + ".png" + } + }else{ + promises.push(this.songBg(id)) + } promises.push(new Promise((resolve, reject) => { var songObj @@ -42,7 +76,7 @@ class loadSong{ }, reject) } })) - promises.push(loader.ajax(this.getSongPath(this.selectedSong)).then(data => { + promises.push(loader.ajax(this.getSongPath(song)).then(data => { this.songData = data.replace(/\0/g, "").split("\n") })) Promise.all(promises).then(() => { @@ -63,27 +97,7 @@ class loadSong{ let filenameAb = filename + (i === 0 ? "a" : "b") let img = document.createElement("img") promises.push(pageEvents.load(img).then(() => { - if(this.touchEnabled){ - return new Promise((resolve, reject) => { - var canvas = document.createElement("canvas") - var w = Math.floor(img.width / 2) - var h = Math.floor(img.height / 2) - canvas.width = w - canvas.height = h - var ctx = canvas.getContext("2d") - ctx.drawImage(img, 0, 0, w, h) - canvas.toBlob(blob => { - let img2 = document.createElement("img") - pageEvents.load(img2).then(() => { - assets.image[filenameAb] = img2 - resolve() - }, reject) - img2.src = URL.createObjectURL(blob) - }) - }) - }else{ - assets.image[filenameAb] = img - } + return this.scaleImg(img, filenameAb) })) img.src = gameConfig.assets_baseurl + "img/" + filenameAb + ".png" } @@ -91,6 +105,30 @@ class loadSong{ } }) } + scaleImg(img, filename){ + return new Promise((resolve, reject) => { + if(this.touchEnabled){ + var canvas = document.createElement("canvas") + var w = Math.floor(img.width / 2) + var h = Math.floor(img.height / 2) + canvas.width = w + canvas.height = h + var ctx = canvas.getContext("2d") + ctx.drawImage(img, 0, 0, w, h) + canvas.toBlob(blob => { + let img2 = document.createElement("img") + pageEvents.load(img2).then(() => { + assets.image[filename] = img2 + resolve() + }, reject) + img2.src = URL.createObjectURL(blob) + }) + }else{ + assets.image[filename] = img + resolve() + } + }) + } randInt(min, max){ return Math.floor(Math.random() * (max - min + 1)) + min } @@ -103,6 +141,8 @@ class loadSong{ } } setupMultiplayer(){ + var song = this.selectedSong + if(this.multiplayer){ var loadingText = document.getElementsByClassName("loading-text")[0] var waitingText = "Waiting for Another Player..." @@ -114,22 +154,22 @@ class loadSong{ pageEvents.add(this.cancelButton, ["mousedown", "touchstart"], this.cancelLoad.bind(this)) this.song2Data = this.songData - this.selectedSong2 = this.selectedSong + this.selectedSong2 = song pageEvents.add(p2, "message", event => { if(event.type === "gameload"){ this.cancelButton.style.display = "" - if(event.value === this.selectedSong.difficulty){ + if(event.value === song.difficulty){ this.startMultiplayer() }else{ this.selectedSong2 = { - title: this.selectedSong.title, - folder: this.selectedSong.folder, + title: song.title, + folder: song.folder, difficulty: event.value, - type: this.selectedSong.type, - offset: this.selectedSong.offset + type: song.type, + offset: song.offset } - if(this.selectedSong.type === "tja"){ + if(song.type === "tja"){ this.startMultiplayer() }else{ loader.ajax(this.getSongPath(this.selectedSong2)).then(data => { @@ -144,7 +184,7 @@ class loadSong{ this.clean() p2.clearMessage("songsel") loader.changePage("game") - var taikoGame1 = new Controller(this.selectedSong, this.songData, false, 1, this.touchEnabled) + var taikoGame1 = new Controller(song, this.songData, false, 1, this.touchEnabled) var taikoGame2 = new Controller(this.selectedSong2, this.song2Data, true, 2, this.touchEnabled) taikoGame1.run(taikoGame2) }else if(event.type === "left" || event.type === "gameend"){ @@ -153,13 +193,13 @@ class loadSong{ } }) p2.send("join", { - id: this.selectedSong.folder, - diff: this.selectedSong.difficulty + id: song.folder, + diff: song.difficulty }) }else{ this.clean() loader.changePage("game") - var taikoGame = new Controller(this.selectedSong, this.songData, this.autoPlayEnabled, false, this.touchEnabled) + var taikoGame = new Controller(song, this.songData, this.autoPlayEnabled, false, this.touchEnabled) taikoGame.run() } } diff --git a/public/src/js/songselect.js b/public/src/js/songselect.js index 47ace54..f9e80a6 100644 --- a/public/src/js/songselect.js +++ b/public/src/js/songselect.js @@ -97,7 +97,8 @@ class SongSelect{ category: song.category, preview: song.preview || 0, type: song.type, - offset: song.offset + offset: song.offset, + songSkin: song.song_skin }) } this.songs.sort((a, b) => { @@ -614,7 +615,8 @@ class SongSelect{ "difficulty": this.difficultyId[difficulty], "category": selectedSong.category, "type": selectedSong.type, - "offset": selectedSong.offset + "offset": selectedSong.offset, + "songSkin": selectedSong.songSkin }, autoplay, multiplayer, touch) } toOptions(moveBy){ diff --git a/public/src/js/view.js b/public/src/js/view.js index a36e1b4..606b2d4 100644 --- a/public/src/js/view.js +++ b/public/src/js/view.js @@ -960,6 +960,8 @@ var songStage = document.getElementById("song-stage") var selectedSong = this.controller.selectedSong + var songSkinName = selectedSong.songSkin.name + if(selectedSong.category in this.categories){ var catId = this.categories[selectedSong.category].sort }else{ @@ -967,17 +969,31 @@ } this.setBgImage(this.gameDiv, assets.image["bg_genre_" + catId].src) - if(selectedSong.customBg){ - var bg = gameConfig.songs_baseurl + selectedSong.folder + "/bg.png" - this.setBgImage(songBg, bg) - }else{ + if(!selectedSong.songSkin.song){ var id = selectedSong.songBg songBg.classList.add("songbg-" + id) - this.setBgImage(document.getElementById("layer1"), assets.image["bg_song_" + id + "a"].src) - this.setBgImage(document.getElementById("layer2"), assets.image["bg_song_" + id + "b"].src) + this.setLayers("bg_song_" + id, true) + }else if(selectedSong.songSkin.song !== "none"){ + var notStatic = selectedSong.songSkin.song !== "static" + if(notStatic){ + songBg.classList.add("songbg-" + selectedSong.songSkin.song) + } + this.setLayers("bg_song_" + songSkinName + (notStatic ? "_" : ""), notStatic) } - songStage.classList.add("song-stage-" + selectedSong.songStage) + if(!selectedSong.songSkin.stage){ + songStage.classList.add("song-stage-" + selectedSong.songStage) + }else if(selectedSong.songSkin.stage !== "none"){ + this.setBgImage(songStage, assets.image["bg_stage_" + songSkinName].src) + } + } + setLayers(file, ab){ + if(ab){ + this.setBgImage(document.getElementById("layer1"), assets.image[file + "a"].src) + this.setBgImage(document.getElementById("layer2"), assets.image[file + "b"].src) + }else{ + this.setBgImage(document.getElementById("layer1"), assets.image[file].src) + } } setBgImage(element, url){ element.style.backgroundImage = "url('" + url + "')"