diff --git a/public/index.html b/public/index.html index ef60072..44beb4f 100644 --- a/public/index.html +++ b/public/index.html @@ -45,6 +45,7 @@ + diff --git a/public/src/js/assets.js b/public/src/js/assets.js index 9006cc1..dda9af1 100644 --- a/public/src/js/assets.js +++ b/public/src/js/assets.js @@ -7,6 +7,7 @@ var assets = { "don-1.png", "big-don-0.png", "big-don-1.png", + "balloon.png", "taiko.png", "taiko-key-blue.png", "taiko-key-red.png", @@ -28,7 +29,10 @@ var assets = { "muzu_oni.png", "don_anim_normal.png", "don_anim_10combo.png", - "balloon.png" + "don_anim_gogo.png", + "don_anim_gogostart.png", + "fire_anim.png", + "fireworks_anim.png" ], "audioSfx": [ "start.wav", diff --git a/public/src/js/canvasasset.js b/public/src/js/canvasasset.js index 082d48b..d3d37d0 100644 --- a/public/src/js/canvasasset.js +++ b/public/src/js/canvasasset.js @@ -1,19 +1,17 @@ class CanvasAsset{ - constructor(view, position, image){ + constructor(view, layer, position){ this.ctx = view.ctx this.controller = view.controller - if(image){ - this.image = assets.image[image] - } this.position = position this.animationFrames = {} this.speed = 1000 / 60 this.animationStart = 0 + this.layer = layer } draw(){ - var u = (a, b) => typeof a === "undefined" ? b : a - var frame = 0 if(this.animation){ + var u = (a, b) => typeof a === "undefined" ? b : a + var frame = 0 var ms = this.controller.getElapsedTime().ms if(this.animationEnd){ if(ms > this.animationEnd.ms){ @@ -28,14 +26,17 @@ class CanvasAsset{ }else{ frame = this.mod(this.animation, index) } - } - var pos = this.position(frame) - if(this.image){ - this.ctx.drawImage(this.image, - u(pos.sx, pos.x), u(pos.sy, pos.y), - u(pos.sw, pos.w), u(pos.sh, pos.h), - pos.x, pos.y, pos.w, pos.h - ) + var pos = this.position(frame) + if(this.image){ + this.ctx.drawImage(this.image, + u(pos.sx, pos.x), u(pos.sy, pos.y), + u(pos.sw, pos.w), u(pos.sh, pos.h), + pos.x, pos.y, pos.w, pos.h + ) + } + if(pos.callback){ + pos.callback() + } } } mod(length, index){ @@ -52,17 +53,21 @@ class CanvasAsset{ } setAnimation(name){ var framesObj = this.animationFrames[name] - this.animation = framesObj.frames this.animationName = name - if(framesObj.image){ - this.image = framesObj.image + if(framesObj){ + this.animation = framesObj.frames + if(framesObj.image){ + this.image = framesObj.image + } + }else{ + this.animation = false } } getAnimation(){ return this.animationName } - getAnimationLength(){ - var frames = this.animationFrames["10combo"].frames + getAnimationLength(name){ + var frames = this.animationFrames[name].frames if(Array.isArray(frames)){ return frames.length }else{ diff --git a/public/src/js/circle.js b/public/src/js/circle.js index c80a7ea..84039d0 100644 --- a/public/src/js/circle.js +++ b/public/src/js/circle.js @@ -1,21 +1,24 @@ class Circle{ - constructor(id, ms, type, text, speed, endTime, requiredHits){ - this.id = id - this.ms = ms - this.type = type - this.text = text - this.speed = speed - this.endTime = endTime ? endTime : ms + 150 + constructor(config){ + // id, ms, type, text, speed, endTime, requiredHits + this.id = config.id + this.ms = config.start + this.type = config.type + this.text = config.txt + this.speed = config.speed + this.endTime = config.endTime || this.ms + 150 this.isPlayed = 0 this.animating = false this.animT = 0 this.score = 0 - this.lastFrame = ms + 100 + this.lastFrame = this.ms + 100 this.animationEnded = false this.status = -1 this.timesHit = 0 - this.requiredHits = requiredHits ? requiredHits : 0 + this.requiredHits = config.requiredHits || 0 this.rendaPlayed = false + this.gogoTime = config.gogoTime + this.gogoChecked = false } getMS(){ return this.ms diff --git a/public/src/js/game.js b/public/src/js/game.js index 02f68f1..915b938 100644 --- a/public/src/js/game.js +++ b/public/src/js/game.js @@ -41,7 +41,7 @@ class Game{ initTiming(){ // Date when the chrono is started (before the game begins) this.offsetDate = new Date() - this.offsetTime = Math.max(0, this.timeForDistanceCircle - this.songData.circles[0].ms) |0 + this.offsetTime = Math.max(0, this.timeForDistanceCircle - this.songData.circles[0].ms) |0 this.setElapsedTime(-this.offsetTime) // The real start for the game will start when chrono will reach 0 this.startDate = new Date() @@ -192,9 +192,9 @@ class Game{ this.controller.displayScore(score, true) } this.updateCombo(score) - this.updateGlobalScore(score, typeDai && keyDai ? 2 : 1) + this.updateGlobalScore(score, typeDai && keyDai ? 2 : 1, circle.gogoTime) this.updateCurrentCircle() - circle.played(score, keyDai) + circle.played(score, score === 0 ? typeDai : keyDai) if(this.controller.multiplayer == 1){ p2.send("note", { score: score, @@ -243,7 +243,14 @@ class Game{ }else{ var sound = keyTime["don"] > keyTime["ka"] ? "daiDon" : "daiKa" } - var circleAnim = new Circle(0, this.getElapsedTime().ms, sound, "", circle.speed) + var circleAnim = new Circle({ + id: 0, + start: this.getElapsedTime().ms, + type: sound, + txt: "", + speed: circle.speed, + gogoTime: circle.gogoTime + }) circleAnim.played(score, dai) circleAnim.animate() this.controller.view.drumroll.push(circleAnim) @@ -252,7 +259,7 @@ class Game{ } whenLastCirclePlayed(){ var circles = this.songData.circles - var lastCircle = circles[this.songData.circles.length - 1] + var lastCircle = circles[circles.length - 1] var ms = this.getElapsedTime().ms if(!this.fadeOutStarted && ms >= lastCircle.getEndTime() + 1900){ this.fadeOutStarted = ms @@ -365,7 +372,7 @@ class Game{ getGlobalScore(){ return this.globalScore } - updateGlobalScore(score, multiplier){ + updateGlobalScore(score, multiplier, gogoTime){ // Circle score switch(score){ case 450: @@ -389,6 +396,9 @@ class Game{ // Points update score += Math.max(0, Math.floor((Math.min(this.combo, 100) - 1) / 10) * 100) - this.globalScore.points += score * multiplier + if(gogoTime){ + multiplier *= 1.2 + } + this.globalScore.points += Math.floor(score * multiplier / 10) * 10 } } diff --git a/public/src/js/mekadon.js b/public/src/js/mekadon.js index 4972705..5758820 100644 --- a/public/src/js/mekadon.js +++ b/public/src/js/mekadon.js @@ -33,7 +33,7 @@ class Mekadon{ this.controller.displayScore(0, true) this.game.updateCurrentCircle() this.game.updateCombo(0) - this.game.updateGlobalScore(0, 1) + this.game.updateGlobalScore(0, 1, circle.gogoTime) return true } } @@ -72,7 +72,7 @@ class Mekadon{ }else{ this.controller.displayScore(score) this.game.updateCombo(score) - this.game.updateGlobalScore(score, keyDai ? 2 : 1) + this.game.updateGlobalScore(score, keyDai ? 2 : 1, circle.gogoTime) this.game.updateCurrentCircle() } circle.updateStatus(score) diff --git a/public/src/js/parsesong.js b/public/src/js/parsesong.js index 587abd5..1b44ff2 100644 --- a/public/src/js/parsesong.js +++ b/public/src/js/parsesong.js @@ -121,7 +121,8 @@ class ParseSong{ timingPoints.push({ start: start, sliderMultiplier: sliderMultiplier, - measure: parseInt(values[this.osu.METER]) + measure: parseInt(values[this.osu.METER]), + gogoTime: parseInt(values[this.osu.KIAIMODE]) }) } return timingPoints @@ -142,7 +143,7 @@ class ParseSong{ speed: this.timingPoints[i].sliderMultiplier }) measureNumber++ - if(measureNumber == this.timingPoints[i].measure + 1){ + if(measureNumber === this.timingPoints[i].measure + 1){ measureNumber = 0 } } @@ -229,6 +230,7 @@ class ParseSong{ var emptyValue = false var start = parseInt(values[this.osu.TIME]) var speed = this.difficulty.originalMultiplier + var gogoTime = false var osuType = parseInt(values[this.osu.TYPE]) var hitSound = parseInt(values[this.osu.HITSOUND]) @@ -237,6 +239,7 @@ class ParseSong{ break } speed = this.timingPoints[j].sliderMultiplier + gogoTime = this.timingPoints[j].gogoTime } if(osuType & this.osu.SPINNER){ @@ -244,7 +247,16 @@ class ParseSong{ var endTime = parseInt(values[this.osu.ENDTIME]) var hitMultiplier = this.difficultyRange(this.difficulty.overallDifficulty, 3, 5, 7.5) * 1.65 var requiredHits = Math.floor(Math.max(1, (endTime - start) / 1000 * hitMultiplier)) - circles.push(new Circle(circleID, start, "balloon", "ふうせん", speed, endTime, requiredHits)) + circles.push(new Circle({ + id: circleID, + start: start, + type: "balloon", + txt: "ふうせん", + speed: speed, + endTime: endTime, + requiredHits: requiredHits, + gogoTime: gogoTime + })) }else if(osuType & this.osu.SLIDER){ @@ -258,7 +270,15 @@ class ParseSong{ type = "drumroll" txt = "連打ーっ!!" } - circles.push(new Circle(circleID, start, type, txt, speed, endTime)) + circles.push(new Circle({ + id: circleID, + start: start, + type: type, + txt: txt, + speed: speed, + endTime: endTime, + gogoTime: gogoTime + })) }else if(osuType & this.osu.CIRCLE){ var type @@ -284,7 +304,14 @@ class ParseSong{ emptyValue = true } if(!emptyValue){ - circles.push(new Circle(circleID, start, type, txt, speed)) + circles.push(new Circle({ + id: circleID, + start: start, + type: type, + txt: txt, + speed: speed, + gogoTime: gogoTime + })) } }else{ emptyValue = true diff --git a/public/src/js/view.js b/public/src/js/view.js index f95d5af..b0de68a 100644 --- a/public/src/js/view.js +++ b/public/src/js/view.js @@ -42,37 +42,13 @@ class View{ this.currentDonFace = 0 this.currentBigDonFace = 1 this.nextBeat = 0 + this.gogoTime = 0 + this.gogoTimeStarted = -Infinity this.drumroll = [] this.beatInterval = this.controller.getSongData().beatInfo.beatInterval - this.assets = [] - this.don = this.createAsset(frame => { - var imgw = 360 - var imgh = 184 - var scale = 165 - var w = (this.barH * imgw) / scale - var h = (this.barH * imgh) / scale - return { - sx: 0, - sy: frame * imgh, - sw: imgw, - sh: imgh, - x: this.taikoSquareW - w + this.barH * 0.2, - y: this.barY - h, - w: w, - h: h - } - }) - this.don.addFrames("normal", [ - 0 ,0 ,0 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,6 ,5 ,4 ,3 ,2 ,1 , - 0 ,0 ,0 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,6 ,5 ,4 ,3 ,2 ,1 , - 0 ,0 ,0 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,6 ,5 ,7 ,8 ,9 ,10, - 11,11,11,11,10,9 ,8 ,7 ,13,12,12,13,14,15,16,17 - ], "don_anim_normal") - this.don.addFrames("10combo", 22, "don_anim_10combo") - this.don.setAnimation("normal") - this.don.setUpdateSpeed(this.beatInterval / 16) + this.assets = new ViewAssets(this) } run(){ this.ctx.font = "normal 14pt TnT" @@ -146,11 +122,12 @@ class View{ this.ctx.clearRect(0, 0, this.canvas.scaledWidth, this.canvas.scaledHeight) // Draw - this.drawAssets() + this.assets.drawAssets("background") this.drawBar() this.drawSlot() - this.drawMeasures() this.drawHPBar() + this.assets.drawAssets("bar") + this.drawMeasures() this.drawScore() this.drawCircles(this.controller.getCircles()) this.drawCircles(this.drumroll) @@ -160,7 +137,9 @@ class View{ this.drawCombo() this.drawGlobalScore() this.updateDonFaces() + this.drawGogoTime() this.mouseIdle() + this.assets.drawAssets("foreground") //this.drawTime() } updateDonFaces(){ @@ -449,6 +428,12 @@ class View{ // Start animation to HP bar circle.animate() } + if(ms >= circle.ms && !circle.gogoChecked){ + if(this.gogoTime != circle.gogoTime){ + this.toggleGogoTime(circle) + } + circle.gogoChecked = true + } if(circle.isAnimated()){ var animationDuration = 470 if(ms <= finishTime + animationDuration){ @@ -654,6 +639,22 @@ class View{ var ms = this.controller.getElapsedTime().ms var keyTime = this.controller.getKeyTime() var sound = keyTime["don"] > keyTime["ka"] ? "don" : "ka" + if(this.gogoTime || ms <= this.gogoTimeStarted + 100){ + var grd = this.ctx.createLinearGradient(0, this.barY, this.winW, this.barH) + grd.addColorStop(0, "#512a2c") + grd.addColorStop(0.46, "#6f2a2d") + grd.addColorStop(0.76, "#8a4763") + grd.addColorStop(1, "#2c2a2c") + this.ctx.fillStyle = grd + this.ctx.rect(0, this.barY, this.winW, this.barH) + var alpha = Math.min(100, this.controller.getElapsedTime().ms - this.gogoTimeStarted) / 100 + if(!this.gogoTime){ + alpha = 1 - alpha + } + this.ctx.globalAlpha = alpha + this.ctx.fill() + this.ctx.globalAlpha = 1 + } if(keyTime[sound] > ms - 200){ var gradients = { "don": ["#f54c25", "#232323"], @@ -710,28 +711,67 @@ class View{ this.ctx.closePath() this.ctx.stroke() } - createAsset(image, position){ - var asset = new CanvasAsset(this, image, position) - this.assets.push(asset) - return asset - } - drawAssets(){ - if(this.controller.multiplayer !== 2){ - this.assets.forEach(asset => { - asset.draw() + toggleGogoTime(circle){ + this.gogoTime = circle.gogoTime + this.gogoTimeStarted = circle.ms + + if(this.gogoTime){ + this.assets.fireworks.forEach(fireworksAsset => { + fireworksAsset.setAnimation("normal") + fireworksAsset.setAnimationStart(circle.ms) + var length = fireworksAsset.getAnimationLength("normal") + fireworksAsset.setAnimationEnd(circle.ms + length * fireworksAsset.speed, () => { + fireworksAsset.setAnimation(false) + }) }) + this.assets.fire.setAnimation("normal") + var don = this.assets.don + don.setAnimation("gogostart") + var length = don.getAnimationLength("gogo") + don.setUpdateSpeed(this.beatInterval / (length / 4)) + var start = circle.ms - (circle.ms % this.beatInterval) + don.setAnimationStart(start) + var length = don.getAnimationLength("gogostart") + don.setAnimationEnd(start + length * don.speed, don.normalAnimation) + } + } + drawGogoTime(){ + var ms = this.controller.getElapsedTime().ms + + if(this.gogoTime){ + var circles = this.controller.parsedSongData.circles + var lastCircle = circles[circles.length - 1] + var endTime = lastCircle.getEndTime() + 3000 + if(ms >= endTime){ + this.toggleGogoTime({ + gogoTime: 0, + ms: endTime + }) + } + }else{ + if(this.assets.don.getAnimation() === "gogo"){ + this.assets.don.normalAnimation() + } + if(ms >= this.gogoTimeStarted + 100){ + this.assets.fire.setAnimation(false) + } } } updateCombo(combo){ - if(combo > 0 && combo % 10 === 0 && this.don.getAnimation() != "10combo"){ - this.don.setAnimation("10combo") + var don = this.assets.don + var animation = don.getAnimation() + if( + combo > 0 + && combo % 10 === 0 + && animation !== "10combo" + && animation !== "gogostart" + && animation !== "gogo" + ){ + don.setAnimation("10combo") var ms = this.controller.getElapsedTime().ms - this.don.setAnimationStart(ms) - var length = this.don.getAnimationLength("10combo") - this.don.setAnimationEnd(ms + length * this.don.speed, () => { - this.don.setAnimationStart(0) - this.don.setAnimation("normal") - }) + don.setAnimationStart(ms) + var length = don.getAnimationLength("10combo") + don.setAnimationEnd(ms + length * don.speed, don.normalAnimation) } } onmousemove(event){ diff --git a/public/src/js/viewassets.js b/public/src/js/viewassets.js new file mode 100644 index 0000000..60a51f6 --- /dev/null +++ b/public/src/js/viewassets.js @@ -0,0 +1,118 @@ +class ViewAssets{ + constructor(view){ + this.view = view + this.controller = this.view.controller + this.allAssets = [] + this.beatInterval = this.view.beatInterval + this.ctx = this.view.ctx + + this.don = this.createAsset("background", frame => { + var imgw = 360 + var imgh = 184 + var scale = 165 + var w = (this.view.barH * imgw) / scale + var h = (this.view.barH * imgh) / scale + return { + sx: Math.floor(frame / 10) * imgw, + sy: (frame % 10) * imgh, + sw: imgw, + sh: imgh, + x: this.view.taikoSquareW - w + this.view.barH * 0.2, + y: this.view.barY - h, + w: w, + h: h + } + }) + this.don.addFrames("normal", [ + 0 ,0 ,0 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,6 ,5 ,4 ,3 ,2 ,1 , + 0 ,0 ,0 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,6 ,5 ,4 ,3 ,2 ,1 , + 0 ,0 ,0 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,6 ,5 ,7 ,8 ,9 ,10, + 11,11,11,11,10,9 ,8 ,7 ,13,12,12,13,14,15,16,17 + ], "don_anim_normal") + this.don.addFrames("10combo", 22, "don_anim_10combo") + this.don.addFrames("gogo", [ + 42,43,43,44,45,46,47,48,49,50,51,52,53,54, + 55,0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,11,12,13, + 14,14,15,16,17,18,19,20,21,22,23,24,25,26, + 27,28,29,30,31,32,33,34,35,36,37,38,39,40,41 + ], "don_anim_gogo") + this.don.addFrames("gogostart", 27, "don_anim_gogostart") + this.don.normalAnimation = () => { + if(this.view.gogoTime){ + var length = this.don.getAnimationLength("gogo") + this.don.setUpdateSpeed(this.beatInterval / (length / 4)) + this.don.setAnimation("gogo") + }else{ + this.don.setAnimationStart(0) + this.don.setUpdateSpeed(this.beatInterval / 16) + this.don.setAnimation("normal") + } + } + this.don.normalAnimation() + this.fire = this.createAsset("bar", frame => { + var imgw = 360 + var imgh = 370 + var scale = 175 + var ms = this.controller.getElapsedTime().ms + var grow = Math.min(100, ms - this.view.gogoTimeStarted) / 100 + if(!this.view.gogoTime){ + grow = 1 - grow + } + var w = (this.view.barH * imgw) / scale * grow + var h = (this.view.barH * imgh) / scale * grow + this.ctx.globalCompositeOperation = "lighter" + return { + sx: frame * imgw, + sy: 0, + sw: imgw, + sh: imgh, + x: this.view.slotX - w / 2, + y: this.view.circleY - h / 2, + w: w, + h: h, + callback: () => { + this.ctx.globalCompositeOperation = "source-over" + } + } + }) + this.fire.addFrames("normal", 7, "fire_anim") + this.fire.setUpdateSpeed(this.beatInterval / 8) + this.fireworks = [] + for(let i = 0; i < 5 ; i++){ + var fireworksAsset = this.createAsset("foreground", frame => { + var imgw = 230 + var imgh = 460 + var scale = 165 + var w = (this.view.barH * imgw) / scale + var h = (this.view.barH * imgh) / scale + return { + sx: Math.floor(frame / 4) * imgw, + sy: (frame % 4) * imgh, + sw: imgw, + sh: imgh, + x: this.view.winW / 4 * i - w / 2 * (i / 2), + y: this.view.winH - h, + w: w, + h: h + } + }) + fireworksAsset.addFrames("normal", 30, "fireworks_anim") + fireworksAsset.setUpdateSpeed(this.beatInterval / 16) + this.fireworks.push(fireworksAsset) + } + } + createAsset(layer, position){ + var asset = new CanvasAsset(this.view, layer, position) + this.allAssets.push(asset) + return asset + } + drawAssets(layer){ + if(this.controller.multiplayer !== 2 || layer === "bar"){ + this.allAssets.forEach(asset => { + if(layer === asset.layer){ + asset.draw() + } + }) + } + } +}