diff --git a/public/src/css/debug.css b/public/src/css/debug.css new file mode 100644 index 0000000..7c838f0 --- /dev/null +++ b/public/src/css/debug.css @@ -0,0 +1,116 @@ +#debug{ + position: absolute; + top: 0; + left: 0; + width: 260px; + background: #fff; + border: 1px solid #333; + color: #000; + z-index: 50; + font-size: 14px; + font-family: TnT, Meiryo, sans-serif; +} + +#debug .title{ + position: relative; + height: 25px; + padding: 5px 0 0 5px; + box-sizing: border-box; + background: #bbb; + color: #fff; + cursor: default; + z-index: 1 +} + +#debug .title::before{ + left: auto; + -webkit-text-stroke: 0.25em #555; +} + +#debug .minimise{ + position: absolute; + top: 3px; + right: 3px; + width: 19px; + height: 19px; + background: #d77; + z-index: 1; +} + +#debug .content{ + height: calc(100% - 25px); + overflow-y: auto; + padding: 8px; + box-sizing: border-box; +} + +#debug .input-slider{ + display: flex; + width: 100%; + height: 30px; + margin: 5px 0 15px 0; +} +#debug .input-slider>input{ + width: 70%; + height: 100%; + box-sizing: border-box; + font-size: 18px; + font-family: monospace; + padding: 2px 4px; + text-align: center; +} +#debug .input-slider>span{ + display: block; + width: 10%; + height: 100%; + opacity: 0.8; + background: #666; + color: #fff; + text-align: center; + line-height: 2em; + cursor: pointer; +} +#debug .input-slider>span:hover{ + opacity: 1; + background: #333; +} + +#debug label{ + display: block; + margin: 15px 0; +} + +#debug input[type="checkbox"]{ + margin-right: 1em; +} + +#debug .bottom-btns{ + display: flex; + width: 100%; + justify-content: flex-end; +} +#debug .bottom-btns div{ + width: calc(50% - 3px); + height: 30px; + opacity: 0.8; + background: #666; + color: #fff; + text-align: center; + line-height: 2em; + cursor: pointer; +} +#debug .bottom-btns div:hover{ + opacity: 1; + background: #333; +} +#debug .restart-btn{ + display: none; + margin-right: 3px; +} +#debug .exit-btn{ + margin-left: 3px; +} + +#debug .autoplay-label{ + display: none; +} diff --git a/public/src/js/assets.js b/public/src/js/assets.js index fde1bec..3fa9dc8 100644 --- a/public/src/js/assets.js +++ b/public/src/js/assets.js @@ -124,7 +124,8 @@ var assets = { "songselect.html", "titlescreen.html", "tutorial.html", - "about.html" + "about.html", + "debug.html" ], "songs": [], diff --git a/public/src/js/circle.js b/public/src/js/circle.js index fdb0478..0bae2d8 100644 --- a/public/src/js/circle.js +++ b/public/src/js/circle.js @@ -3,10 +3,12 @@ class Circle{ // id, ms, type, text, speed, endTime, requiredHits this.id = config.id this.ms = config.start + this.originalMS = this.ms this.type = config.type this.text = config.txt this.speed = config.speed this.endTime = config.endTime || this.ms + this.originalEndTime = this.endTime this.isPlayed = 0 this.animating = false this.animT = 0 diff --git a/public/src/js/controller.js b/public/src/js/controller.js index 9ef9608..5c1c316 100644 --- a/public/src/js/controller.js +++ b/public/src/js/controller.js @@ -38,6 +38,12 @@ class Controller{ syncWith.startDate = this.game.startDate this.syncWith = syncWith } + if(!this.multiplayer){ + debugObj.controller = this + if(debugObj.debug){ + debugObj.debug.updateStatus() + } + } } loadUIEvents(){ this.pauseMenu = document.getElementById("pause-menu") @@ -217,5 +223,12 @@ class Controller{ delete this.restartBtn pageEvents.remove(this.songSelBtn, ["click", "touchend"]) delete this.songSelBtn + + if(!this.multiplayer){ + debugObj.controller = null + if(debugObj.debug){ + debugObj.debug.updateStatus() + } + } } } diff --git a/public/src/js/debug.js b/public/src/js/debug.js new file mode 100644 index 0000000..0c566ee --- /dev/null +++ b/public/src/js/debug.js @@ -0,0 +1,305 @@ +class Debug{ + constructor(){ + if(!assets.pages["debug"]){ + return + } + this.debugDiv = document.createElement("div") + this.debugDiv.id = "debug" + this.debugDiv.innerHTML = assets.pages["debug"] + document.body.appendChild(this.debugDiv) + + this.titleDiv = this.debugDiv.getElementsByClassName("title")[0] + this.minimiseDiv = this.debugDiv.getElementsByClassName("minimise")[0] + this.offsetDiv = this.debugDiv.getElementsByClassName("offset")[0] + this.measureNumDiv = this.debugDiv.getElementsByClassName("measure-num")[0] + this.restartCheckbox = this.debugDiv.getElementsByClassName("change-restart")[0] + this.autoplayLabel = this.debugDiv.getElementsByClassName("autoplay-label")[0] + this.autoplayCheckbox = this.debugDiv.getElementsByClassName("autoplay")[0] + this.restartBtn = this.debugDiv.getElementsByClassName("restart-btn")[0] + this.exitBtn = this.debugDiv.getElementsByClassName("exit-btn")[0] + + this.moving = false + pageEvents.add(window, ["mousedown", "mouseup", "blur"], this.stopMove.bind(this)) + pageEvents.add(window, "mousemove", this.onMove.bind(this)) + pageEvents.add(this.titleDiv, "mousedown", this.startMove.bind(this)) + pageEvents.add(this.minimiseDiv, "click", this.minimise.bind(this)) + pageEvents.add(this.restartBtn, "click", this.restartSong.bind(this)) + pageEvents.add(this.exitBtn, "click", this.clean.bind(this)) + pageEvents.add(this.autoplayCheckbox, "change", this.toggleAutoplay.bind(this)) + + this.offsetSlider = new InputSlider(this.offsetDiv, -60, 60, 3) + this.offsetSlider.onchange(this.offsetChange.bind(this)) + + this.measureNumSlider = new InputSlider(this.measureNumDiv, 0, 1000, 0) + this.measureNumSlider.onchange(this.measureNumChange.bind(this)) + this.measureNumSlider.set(0) + + this.moveTo(100, 100) + this.restore() + this.updateStatus() + } + startMove(event){ + if(event.which === 1){ + event.stopPropagation() + this.moving = { + x: event.offsetX, + y: event.offsetY + } + } + } + onMove(event){ + if(this.moving){ + var x = event.clientX - this.moving.x + var y = event.clientY - this.moving.y + this.moveTo(x, y) + } + } + stopMove(event){ + var x = event.clientX - this.moving.x + var y = event.clientY - this.moving.y + var w = this.debugDiv.offsetWidth + var h = this.debugDiv.offsetHeight + if(x + w > innerWidth){ + x = innerWidth - w + } + if(y + h > lastHeight){ + y = lastHeight - h + } + if(x < 0){ + x = 0 + } + if(y < 0){ + y = 0 + } + this.moveTo(x, y) + this.moving = false + } + moveTo(x, y){ + this.debugDiv.style.transform = "translate(" + x + "px, " + y + "px)" + } + restore(){ + debugObj.state = "open" + this.debugDiv.style.display = "" + } + minimise(){ + debugObj.state = "minimised" + this.debugDiv.style.display = "none" + } + updateStatus(){ + if(debugObj.controller && !this.controller){ + this.restartBtn.style.display = "block" + this.autoplayLabel.style.display = "block" + + this.controller = debugObj.controller + var selectedSong = this.controller.selectedSong + this.defaultOffset = selectedSong.offset || 0 + if(this.songFolder === selectedSong.folder){ + this.offsetChange(this.offsetSlider.get(), true) + }else{ + this.songFolder = selectedSong.folder + this.offsetSlider.set(this.defaultOffset) + } + + var measures = this.controller.parsedSongData.measures + this.measureNumSlider.setMinMax(0, measures.length - 1) + if(this.measureNum && measures.length > this.measureNum){ + var measureMS = measures[this.measureNum].ms + var game = this.controller.game + game.started = true + var timestamp = +new Date + var currentDate = timestamp - measureMS + game.startDate = currentDate + game.sndTime = timestamp - snd.buffer.getTime() * 1000 + var circles = game.songData.circles + for(var i in circles){ + game.currentCircle = i + if(circles[i].endTime >= measureMS){ + break + } + } + } + this.autoplayCheckbox.checked = this.controller.autoPlayEnabled + } + if(this.controller && !debugObj.controller){ + this.restartBtn.style.display = "" + this.autoplayLabel.style.display = "" + this.controller = null + } + } + offsetChange(value, noRestart){ + if(this.controller){ + var offset = (this.defaultOffset - value) * 1000 + var songData = this.controller.parsedSongData + songData.circles.forEach(circle => { + circle.ms = circle.originalMS + offset + circle.endTime = circle.originalEndTime + offset + }) + songData.measures.forEach(measure => { + measure.ms = measure.originalMS + offset + }) + if(this.restartCheckbox.checked && !noRestart){ + this.restartSong() + } + } + } + measureNumChange(value){ + this.measureNum = value + if(this.restartCheckbox.checked){ + this.restartSong() + } + } + restartSong(){ + if(this.controller){ + this.controller.restartSong() + } + } + toggleAutoplay(){ + if(this.controller){ + this.controller.autoPlayEnabled = this.autoplayCheckbox.checked + if(!this.controller.autoPlayEnabled){ + var keyboard = debugObj.controller.keyboard + var kbd = keyboard.getBindings() + keyboard.setKey(kbd.don_l, false) + keyboard.setKey(kbd.don_r, false) + keyboard.setKey(kbd.ka_l, false) + keyboard.setKey(kbd.ka_r, false) + } + } + } + clean(){ + this.offsetSlider.clean() + + pageEvents.remove(window, ["mousedown", "mouseup", "mousemove", "blur"]) + pageEvents.remove(this.title, "mousedown") + pageEvents.remove(this.minimiseDiv, "click") + pageEvents.remove(this.restartBtn, "click") + pageEvents.remove(this.exitBtn, "click") + pageEvents.remove(this.autoplayCheckbox, "change") + + delete this.titleDiv + delete this.minimiseDiv + delete this.offsetDiv + delete this.measureNumDiv + delete this.restartCheckbox + delete this.autoplayLabel + delete this.autoplayCheckbox + delete this.restartBtn + delete this.exitBtn + delete this.controller + + debugObj.state = "closed" + debugObj.debug = null + document.body.removeChild(this.debugDiv) + + delete this.debugDiv + } +} +class InputSlider{ + constructor(sliderDiv, min, max, fixedPoint){ + this.fixedPoint = fixedPoint + this.mul = Math.pow(10, fixedPoint) + this.min = min * this.mul + this.max = max * this.mul + + this.input = sliderDiv.getElementsByTagName("input")[0] + this.reset = sliderDiv.getElementsByClassName("reset")[0] + this.plus = sliderDiv.getElementsByClassName("plus")[0] + this.minus = sliderDiv.getElementsByClassName("minus")[0] + this.value = null + this.defaultValue = null + this.callbacks = [] + + pageEvents.add(this.plus, "click", this.add.bind(this)) + pageEvents.add(this.minus, "click", this.subtract.bind(this)) + pageEvents.add(this.reset, "click", this.resetValue.bind(this)) + pageEvents.add(this.input, "change", this.manualSet.bind(this)) + pageEvents.add(this.input, "keydown", this.captureKeys.bind(this)) + } + update(noCallback, force){ + var oldValue = this.input.value + if(this.value === null){ + this.input.value = "" + this.input.readOnly = true + }else{ + if(this.value > this.max){ + this.value = this.max + } + if(this.value < this.min){ + this.value = this.min + } + this.input.value = this.get().toFixed(this.fixedPoint) + this.input.readOnly = false + } + if(force || !noCallback && oldValue !== this.input.value){ + this.callbacks.forEach(callback => { + callback(this.get()) + }) + } + } + set(number){ + this.value = Math.floor(number * this.mul) + this.defaultValue = this.value + this.update(true) + } + setMinMax(min, max){ + this.min = min + this.max = max + this.update() + } + get(){ + if(this.value === null){ + return null + }else{ + return Math.floor(this.value) / this.mul + } + } + add(event){ + if(this.value !== null){ + var newValue = this.value + this.eventNumber(event) + if(newValue <= this.max){ + this.value = newValue + this.update() + } + } + } + subtract(event){ + if(this.value !== null){ + var newValue = this.value - this.eventNumber(event) + if(newValue >= this.min){ + this.value = newValue + this.update() + } + } + } + eventNumber(event){ + return (event.ctrlKey ? 10 : 1) * (event.shiftKey ? 10 : 1) * (event.altKey ? 10 : 1) * 1 + } + resetValue(){ + this.value = this.defaultValue + this.update() + } + onchange(callback){ + this.callbacks.push(callback) + } + manualSet(){ + var number = parseFloat(this.input.value) * this.mul + if(Number.isFinite(number) && this.min <= number && number <= this.max){ + this.value = number + } + this.update(false, true) + } + captureKeys(event){ + event.stopPropagation() + } + clean(){ + pageEvents.remove(this.plus, "click") + pageEvents.remove(this.minus, "click") + pageEvents.remove(this.reset, "click") + pageEvents.remove(this.input, ["change", "keydown"]) + + delete this.input + delete this.reset + delete this.plus + delete this.minus + } +} diff --git a/public/src/js/game.js b/public/src/js/game.js index 0cb85df..a194fda 100644 --- a/public/src/js/game.js +++ b/public/src/js/game.js @@ -60,8 +60,10 @@ class Game{ return this.songData.circles } updateCirclesStatus(){ + var nextSet = false var circles = this.songData.circles - circles.forEach(circle => { + for(var i in circles){ + var circle = circles[i] if(!circle.getPlayed()){ var ms = this.elapsedTime var type = circle.getType() @@ -69,7 +71,7 @@ class Game{ var endTime = circle.getEndTime() + (drumrollNotes ? 0 : this.rules.bad) if(ms >= circle.getMS()){ - if(drumrollNotes && !circle.rendaPlayed){ + if(drumrollNotes && !circle.rendaPlayed && ms < endTime){ circle.rendaPlayed = true if(this.rules.difficulty === "easy"){ assets.sounds["renda" + this.controller.snd].stop() @@ -107,9 +109,12 @@ class Game{ } } } + }else if(!this.controller.autoPlayEnabled && !nextSet){ + nextSet = true + this.currentCircle = i } } - }) + } } setHPGain(gain){ this.HPGain = gain diff --git a/public/src/js/main.js b/public/src/js/main.js index 7647985..268f06c 100644 --- a/public/src/js/main.js +++ b/public/src/js/main.js @@ -40,6 +40,19 @@ function resizeRoot(){ } } +function debug(){ + if(debugObj.state === "open"){ + debugObj.debug.clean() + return "Debug closed" + }else if(debugObj.state === "minimised"){ + debugObj.debug.restore() + return "Debug restored" + }else{ + debugObj.debug = new Debug() + return "Debug opened" + } +} + var root = document.documentElement var fullScreenSupported = "requestFullscreen" in root || "webkitRequestFullscreen" in root || "mozRequestFullScreen" in root @@ -49,6 +62,10 @@ var p2 var disableBlur = false var cancelTouch = true var lastHeight +var debugObj = { + state: "closed", + debug: null +} var perf = { blur: 0, allImg: 0, @@ -67,6 +84,20 @@ pageEvents.add(versionDiv, ["click", "touchend"], () => { }) resizeRoot() setInterval(resizeRoot, 100) +pageEvents.keyAdd(debugObj, "all", "down", event => { + if(event.keyCode === 186 && event.ctrlKey && event.shiftKey && !event.altKey){ + if(debugObj.state === "open"){ + debugObj.debug.minimise() + }else if(debugObj.state === "minimised"){ + debugObj.debug.restore() + }else{ + debugObj.debug = new Debug() + } + } + if(event.keyCode === 82 && debugObj.debug && debugObj.controller){ + debugObj.controller.restartSong() + } +}) var loader = new Loader(() => { new Titlescreen() diff --git a/public/src/js/parseosu.js b/public/src/js/parseosu.js index 8618450..c4b6280 100644 --- a/public/src/js/parseosu.js +++ b/public/src/js/parseosu.js @@ -150,6 +150,7 @@ class ParseOsu{ if(measureNumber === 0){ measures.push({ ms: start + this.offset, + originalMS: start + this.offset, speed: this.timingPoints[i].sliderMultiplier }) } diff --git a/public/src/js/parsetja.js b/public/src/js/parsetja.js index 52d56f0..1874c82 100644 --- a/public/src/js/parsetja.js +++ b/public/src/js/parsetja.js @@ -140,6 +140,7 @@ } this.measures.push({ ms: ms, + originalMS: ms, speed: speed }) if(firstMeasure){ @@ -148,6 +149,7 @@ for(var measureMs = ms - msPerMeasure; measureMs > 0; measureMs -= msPerMeasure){ this.measures.push({ ms: measureMs, + originalMS: ms, speed: speed }) } @@ -166,6 +168,7 @@ note.start = ms if(note.endDrumroll){ note.endDrumroll.endTime = ms + note.endDrumroll.originalEndTime = ms } var msPerMeasure = 60000 * measure / note.bpm ms += msPerMeasure / currentMeasure.length @@ -344,6 +347,7 @@ pushMeasure() if(lastDrumroll){ lastDrumroll.endTime = ms + lastDrumroll.originalEndTime = ms } return circles diff --git a/public/src/views/debug.html b/public/src/views/debug.html new file mode 100644 index 0000000..eba64ac --- /dev/null +++ b/public/src/views/debug.html @@ -0,0 +1,18 @@ +