diff --git a/public/src/css/game.css b/public/src/css/game.css index 16aa43a..2944e82 100644 --- a/public/src/css/game.css +++ b/public/src/css/game.css @@ -80,3 +80,17 @@ background: #fff; border-color: #ae7a26; } +.touch-results #touch-pause-btn{ + display: none; +} +#fade-screen{ + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + background-color: transparent; + pointer-events: none; + z-index: 2; + transition: 1s background-color linear; +} diff --git a/public/src/css/songbg.css b/public/src/css/songbg.css index 4e79594..f338b68 100644 --- a/public/src/css/songbg.css +++ b/public/src/css/songbg.css @@ -166,3 +166,159 @@ .donbg-fastscroll .donlayer2{ animation: 1s linear donbg-scroll infinite; } + +#tetsuohana{ + position: absolute; + right: calc(-12px * var(--scale)); + left: calc(-12px * var(--scale)); + margin: auto; + z-index: 1; + overflow: hidden; + pointer-events: none; + top: calc(50% - 15px * var(--scale)); + width: calc(1304px * var(--scale)); + height: calc(375px * var(--scale)); + --frame: 0; + --low: calc(36px * var(--scale)); +} +#tetsuo, +#hana{ + position: absolute; + top: 0; + width: calc(292px * var(--scale)); + height: calc(425px * var(--scale)); + transform: translateY(calc(360px * var(--scale))); +} +#tetsuo-in, +#hana-in{ + width: 100%; + height: 100%; + background-repeat: no-repeat; + background-size: calc(292px * var(--scale) * 2); + background-position-y: calc(-425px * var(--frame) * var(--scale)); +} +#tetsuo{ + left: calc(173px * var(--scale)); +} +#hana{ + right: calc(178px * var(--scale)); +} +#hana-in{ + background-position-x: calc(-292px * var(--scale)); +} +#mikoshi{ + position: absolute; + top: 0; + left: calc(390px * var(--scale)); + width: calc(553px * var(--scale)); + height: calc(416px * var(--scale)); + transform: translateY(calc(461px * var(--scale))); +} +#mikoshi-in{ + width: 100%; + height: 100%; + background-repeat: no-repeat; + background-size: contain; +} +#flowers1, +#flowers2{ + position: absolute; + top: calc(218px * var(--scale)); + width: calc(483px * var(--scale)); + height: calc(159px * var(--scale)); + transform: translateY(calc(243px * var(--scale))) scaleX(var(--flip)); +} +#flowers1-in, +#flowers2-in{ + width: 100%; + height: 100%; + background-repeat: no-repeat; + background-size: calc(483px * var(--scale)); + background-position-y: calc(-159px * var(--frame) * var(--scale)); +} +#flowers1{ + left: 0; + --flip: 1; +} +#flowers2{ + right: calc(4px * var(--scale)); + --flip: -1; +} +#tetsuohana.fadein, +#tetsuohana.dance, +#tetsuohana.failed{ + height: calc(461px * var(--scale)); +} +#tetsuohana.fadein #tetsuo, +#tetsuohana.fadein #hana{ + transition: 0.5s transform cubic-bezier(0.2, 0.6, 0.4, 1.2); + transform: translateY(var(--low)); +} +@keyframes tetsuohana-dance{ + 0%{transform: translateY(var(--low))} + 50%{transform: translateY(0)} + 100%{transform: translateY(0)} +} +@keyframes tetsuohana-failed1{ + 0%{transform: translateY(calc(10px * var(--scale)))} + 50%{transform: translateY(0)} + 100%{transform: translateY(0)} +} +@keyframes tetsuohana-failed2{ + 0%{transform: translateY(0)} + 49%{transform: translateY(0)} + 50%{transform: translateY(calc(5px * var(--scale)))} + 100%{transform: translateY(calc(15px * var(--scale)))} +} +@keyframes tetsuohana-flowers{ + 0%{background-position-y: 0} + 50%{background-position-y: calc(-159px * var(--scale))} + 100%{background-position-y: calc(-318px * var(--scale))} +} +@keyframes tetsuohana-mikoshi{ + 0%{transform: translateY(calc(425px * var(--scale)))} + 100%{transform: translateY(0)} +} +#tetsuohana.dance #tetsuo, +#tetsuohana.dance #hana{ + --frame: 1; + transform: translateY(var(--low)); + animation: 0.5s ease-out tetsuohana-dance infinite forwards; +} +#tetsuohana.dance #tetsuo-in, +#tetsuohana.dance #hana-in{ + transform: translateY(0); + animation: 0.5s ease-out tetsuohana-dance infinite forwards reverse; +} +#tetsuohana.dance #flowers1, +#tetsuohana.dance #flowers2{ + transform: translateY(0) scaleX(var(--flip)); + transition: 0.34s transform ease-out; +} +#tetsuohana.dance #flowers1-in, +#tetsuohana.dance #flowers2-in{ + animation: 0.25s 0.4s step-end tetsuohana-flowers both; +} +#tetsuohana.dance #mikoshi-out{ + animation: 0.4s 0.4s ease-out tetsuohana-mikoshi both; +} +#tetsuohana.dance #mikoshi{ + transform: translateY(var(--low)); + animation: 0.5s 0.8s ease-out tetsuohana-dance infinite forwards; +} +#tetsuohana.dance #mikoshi-in{ + transform: translateY(0); + animation: 0.5s 0.8s ease-out tetsuohana-dance infinite forwards reverse; +} +#tetsuohana.failed #tetsuo, +#tetsuohana.failed #hana{ + --frame: 2; + top: calc(26px * var(--scale)); + transform: translateY(calc(46px * var(--scale))); + animation: 1.25s ease-out tetsuohana-failed1 forwards infinite; +} +#tetsuohana.failed #tetsuo-in, +#tetsuohana.failed #hana-in{ + transform: translateY(0); + animation: 1.25s ease-in tetsuohana-failed2 forwards infinite; +} diff --git a/public/src/js/assets.js b/public/src/js/assets.js index 0571b24..6ad90cf 100644 --- a/public/src/js/assets.js +++ b/public/src/js/assets.js @@ -41,7 +41,10 @@ var assets = { "touch_drum.png", "touch_pause.png", "touch_fullscreen.png", - "mimizu.png" + "mimizu.png", + "results_flowers.png", + "results_mikoshi.png", + "results_tetsuohana.png" ], "audioSfx": [ "don.wav", diff --git a/public/src/js/controller.js b/public/src/js/controller.js index 96c3acf..dd71cc8 100644 --- a/public/src/js/controller.js +++ b/public/src/js/controller.js @@ -130,6 +130,7 @@ class Controller{ if(!fadeIn){ this.clean() } + loader.screen.classList.remove("view") new SongSelect(false, fadeIn, this.touchEnabled) } restartSong(){ diff --git a/public/src/js/loadsong.js b/public/src/js/loadsong.js index 3776bbb..57c3565 100644 --- a/public/src/js/loadsong.js +++ b/public/src/js/loadsong.js @@ -39,6 +39,9 @@ class LoadSong{ type: type }) } + if(type === "don"){ + song.donBg = null + } if(type === "song"){ song.songBg = null } diff --git a/public/src/js/scoresheet.js b/public/src/js/scoresheet.js index 77c638a..4fdd589 100644 --- a/public/src/js/scoresheet.js +++ b/public/src/js/scoresheet.js @@ -10,6 +10,11 @@ class Scoresheet{ this.canvas = document.getElementById("canvas") this.ctx = this.canvas.getContext("2d") + this.game = document.getElementById("game") + + this.fadeScreen = document.createElement("div") + this.fadeScreen.id = "fade-screen" + this.game.appendChild(this.fadeScreen) this.font = "TnT, Meiryo, sans-serif" this.state = { @@ -114,6 +119,33 @@ class Scoresheet{ pageEvents.keyAdd(this, "all", "down", this.keyDown.bind(this)) pageEvents.add(this.canvas, ["mousedown", "touchstart"], this.mouseDown.bind(this)) + + if(!this.multiplayer){ + this.tetsuoHana = document.createElement("div") + this.tetsuoHana.id = "tetsuohana" + var flowersBg = "url('" + assets.image["results_flowers"].src + "')" + var mikoshiBg = "url('" + assets.image["results_mikoshi"].src + "')" + var tetsuoHanaBg = "url('" + assets.image["results_tetsuohana"].src + "')" + var id = ["flowers1", "flowers2", "mikoshi", "tetsuo", "hana"] + var bg = [flowersBg, flowersBg, mikoshiBg, tetsuoHanaBg, tetsuoHanaBg] + for(var i = 0; i < id.length; i++){ + if(id[i] === "mikoshi"){ + var divOut = document.createElement("div") + divOut.id = id[i] + "-out" + this.tetsuoHana.appendChild(divOut) + }else{ + var divOut = this.tetsuoHana + } + var div = document.createElement("div") + div.id = id[i] + var divIn = document.createElement("div") + divIn.id = id[i] + "-in" + divIn.style.backgroundImage = bg[i] + div.appendChild(divIn) + divOut.appendChild(div) + } + this.game.appendChild(this.tetsuoHana) + } } redraw(){ @@ -156,6 +188,10 @@ class Scoresheet{ this.canvas.style.height = (winH / this.pixelRatio) + "px" this.canvasCache.resize(winW / ratio, 80 + 1, ratio) + + if(!this.multiplayer){ + this.tetsuoHana.style.setProperty("--scale", ratio / this.pixelRatio) + } }else if(!document.hasFocus() && this.state.screen === "scoresShown"){ return }else{ @@ -249,8 +285,48 @@ class Scoresheet{ if(this.state.screen === "scoresShown" || this.state.screen === "fadeOut"){ var elapsed = Infinity - }else{ + }else if(this.redrawing){ var elapsed = ms - this.state.screenMS - this.state.startDelay + }else{ + var elapsed = 0 + } + + var gaugePercent = Math.round(this.results.gauge / 2) / 50 + if(players === 2){ + var gauge2 = Math.round(p2.results.gauge / 2) / 50 + if(gauge2 > gaugePercent){ + gaugePercent = gauge2 + } + } + var gaugeClear = 25 / 50 + var failedOffset = gaugePercent >= gaugeClear ? 0 : -2000 + if(elapsed >= 3100 + failedOffset){ + for(var p = 0; p < players; p++){ + ctx.save() + var results = this.results + if(p === 1){ + results = p2.results + } + var resultGauge = Math.round(results.gauge / 2) / 50 + var clear = resultGauge >= gaugeClear + if(p === 1 || !this.multiplayer && clear){ + ctx.translate(0, 290) + } + if(clear){ + ctx.globalCompositeOperation = "lighter" + } + ctx.globalAlpha = Math.min(1, Math.max(0, (elapsed - (3100 + failedOffset)) / 500)) * 0.5 + var grd = ctx.createLinearGradient(0, frameTop + 72, 0, frameTop + 368) + grd.addColorStop(0, "#000") + if(clear){ + grd.addColorStop(1, "#ffffba") + }else{ + grd.addColorStop(1, "transparent") + } + ctx.fillStyle = grd + ctx.fillRect(0, frameTop + 72, winW, 286) + ctx.restore() + } } if(elapsed >= 0){ @@ -431,6 +507,23 @@ class Scoresheet{ ctx.restore() } + if(!this.multiplayer){ + if(elapsed >= 400 && elapsed < 3100 + failedOffset){ + if(this.tetsuoHanaClass !== "fadein"){ + this.tetsuoHana.classList.add("fadein") + this.tetsuoHanaClass = "fadein" + } + }else if(elapsed >= 3100 + failedOffset){ + if(this.tetsuoHanaClass !== "dance"){ + if(this.tetsuoHanaClass){ + this.tetsuoHana.classList.remove(this.tetsuoHanaClass) + } + this.tetsuoHana.classList.add(gaugePercent >= gaugeClear ? "dance" : "failed") + this.tetsuoHanaClass = "dance" + } + } + } + if(elapsed >= 800){ ctx.save() ctx.setTransform(1, 0, 0, 1, 0, 0) @@ -646,22 +739,20 @@ class Scoresheet{ } if(this.state.screen === "fadeOut"){ - ctx.save() if(this.state.hasPointer === 1){ this.state.hasPointer = 2 this.canvas.style.cursor = "" } + if(!this.fadeScreenBlack){ + this.fadeScreenBlack = true + this.fadeScreen.style.backgroundColor = "#000" + } var elapsed = ms - this.state.screenMS - ctx.globalAlpha = Math.max(0, Math.min(1, elapsed / 1000)) - ctx.fillStyle = "#000" - ctx.fillRect(0, 0, winW, winH) - - ctx.restore() if(elapsed >= 1000){ this.clean() - this.controller.songSelection(true, false, p2.session ? this.touchEnabled : this.state.pointerLocked) + this.controller.songSelection(true) } } @@ -714,10 +805,17 @@ class Scoresheet{ this.redrawRunning = false pageEvents.keyRemove(this, "all") pageEvents.remove(this.canvas, ["mousedown", "touchstart"]) + if(this.multiplayer !== 2 && this.touchEnabled){ + pageEvents.remove(document.getElementById("touch-full-btn"), "touchend") + } if(p2.session){ pageEvents.remove(p2, "message") } + if(!this.multiplayer){ + delete this.tetsuoHana + } delete this.ctx delete this.canvas + delete this.fadeScreen } } diff --git a/public/src/js/view.js b/public/src/js/view.js index 8b2e4a0..6c14b62 100644 --- a/public/src/js/view.js +++ b/public/src/js/view.js @@ -7,6 +7,8 @@ this.cursor = document.getElementById("cursor") this.gameDiv = document.getElementById("game") + this.songBg = document.getElementById("songbg") + this.songStage = document.getElementById("song-stage") this.portraitClass = false this.touchp2Class = false @@ -970,12 +972,9 @@ } } setBackground(){ - var songBg = document.getElementById("songbg") - var songStage = document.getElementById("song-stage") - var selectedSong = this.controller.selectedSong var songSkinName = selectedSong.songSkin.name - var supportsBlend = "mixBlendMode" in songBg.style + var supportsBlend = "mixBlendMode" in this.songBg.style var songLayers = [document.getElementById("layer1"), document.getElementById("layer2")] if(selectedSong.category in this.categories){ @@ -987,24 +986,23 @@ if(!selectedSong.songSkin.song){ var id = selectedSong.songBg - songBg.classList.add("songbg-" + id) + this.songBg.classList.add("songbg-" + id) this.setLayers(songLayers, "bg_song_" + id + (supportsBlend ? "" : "a"), supportsBlend) }else if(selectedSong.songSkin.song !== "none"){ var notStatic = selectedSong.songSkin.song !== "static" if(notStatic){ - songBg.classList.add("songbg-" + selectedSong.songSkin.song) + this.songBg.classList.add("songbg-" + selectedSong.songSkin.song) } this.setLayers(songLayers, "bg_song_" + songSkinName + (notStatic ? "_" : ""), notStatic) } if(!selectedSong.songSkin.stage){ - songStage.classList.add("song-stage-" + selectedSong.songStage) + this.songStage.classList.add("song-stage-" + selectedSong.songStage) }else if(selectedSong.songSkin.stage !== "none"){ - this.setBgImage(songStage, assets.image["bg_stage_" + songSkinName].src) + this.setBgImage(this.songStage, assets.image["bg_stage_" + songSkinName].src) } } setDonBg(){ - var songBg = document.getElementById("songbg") var selectedSong = this.controller.selectedSong var songSkinName = selectedSong.songSkin.name var donLayers = [] @@ -1023,7 +1021,7 @@ donLayers.push(donLayer) } } - songBg.parentNode.insertBefore(this.donBg, songBg) + this.songBg.parentNode.insertBefore(this.donBg, this.songBg) var asset1, asset2 if(!selectedSong.songSkin.don){ this.donBg.classList.add("donbg-" + selectedSong.donBg) @@ -1694,10 +1692,10 @@ if(this.multiplayer !== 2){ if(this.touchEnabled){ pageEvents.remove(this.canvas, "touchstart") - pageEvents.remove(this.touchFullBtn, "touchend") pageEvents.remove(this.touchPauseBtn, "touchend") - this.gameDiv.classList.remove("touch-visible") + this.gameDiv.classList.add("touch-results") document.getElementById("version").classList.remove("version-hide") + this.touchDrumDiv.parentNode.removeChild(this.touchDrumDiv) delete this.touchDrumDiv delete this.touchDrumImg delete this.touchFullBtn @@ -1708,8 +1706,12 @@ pageEvents.remove(this.canvas, "mousedown") } pageEvents.mouseRemove(this) - loader.screen.classList.remove("view") + this.donBg.parentNode.removeChild(this.donBg) + this.songBg.parentNode.removeChild(this.songBg) + this.songStage.parentNode.removeChild(this.songStage) delete this.donBg + delete this.songBg + delete this.songStage delete this.pauseMenu delete this.cursor delete this.gameDiv