Merge pull request #189 from bui/bug-fixes

Lots of bug fixes
This commit is contained in:
Bui 2020-03-09 14:15:54 +00:00 committed by GitHub
commit a899fd5cfe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 471 additions and 239 deletions

View File

@ -1,8 +0,0 @@
@font-face{
font-family: TnT;
src: url("TnT.ttf") format("truetype");
}
@font-face{
font-family: Kozuka;
src: url("Kozuka.otf") format("truetype");
}

View File

@ -31,6 +31,8 @@
this.items = [] this.items = []
this.getLink(this.linkIssues).innerText = strings.about.issues
this.linkIssues.setAttribute("alt", strings.about.issues)
var versionUrl = gameConfig._version.url var versionUrl = gameConfig._version.url
this.getLink(this.linkIssues).href = versionUrl + "issues" this.getLink(this.linkIssues).href = versionUrl + "issues"
this.items.push(this.linkIssues) this.items.push(this.linkIssues)

View File

@ -1,6 +1,5 @@
var assets = { var assets = {
"js": [ "js": [
"lib/fontdetect.min.js",
"lib/md5.min.js", "lib/md5.min.js",
"loadsong.js", "loadsong.js",
"parseosu.js", "parseosu.js",
@ -44,7 +43,6 @@ var assets = {
"view.css" "view.css"
], ],
"assetsCss": [ "assetsCss": [
"fonts/fonts.css",
"img/img.css" "img/img.css"
], ],
"img": [ "img": [
@ -126,10 +124,10 @@ var assets = {
"bgm_setsume.mp3", "bgm_setsume.mp3",
"bgm_settings.mp3" "bgm_settings.mp3"
], ],
"fonts": [ "fonts": {
"Kozuka", "Kozuka": "Kozuka.otf",
"TnT" "TnT": "TnT.ttf"
], },
"views": [ "views": [
"game.html", "game.html",
"loadsong.html", "loadsong.html",

View File

@ -47,6 +47,9 @@ function browserSupport(){
var el = document.createElement("a") var el = document.createElement("a")
el.style.setProperty("--a", 1) el.style.setProperty("--a", 1)
return el.style.length !== 0 return el.style.length !== 0
},
"Font Loading API": function(){
return typeof FontFace === "function"
} }
} }
failedTests = [] failedTests = []

View File

@ -4,6 +4,7 @@ class CanvasCache{
if(w){ if(w){
this.resize(w, h, scale) this.resize(w, h, scale)
} }
this.index = Number.MIN_SAFE_INTEGER
} }
resize(w, h, scale){ resize(w, h, scale){
if(this.canvas){ if(this.canvas){
@ -33,7 +34,7 @@ class CanvasCache{
return return
} }
var saved = false var saved = false
var time = Date.now() var index = this.index++
if(!img){ if(!img){
var w = config.w var w = config.w
var h = config.h var h = config.h
@ -44,11 +45,11 @@ class CanvasCache{
} }
if(this.y + h > this.h){ if(this.y + h > this.h){
var clear = true var clear = true
var oldest = {time: time} var oldest = {index: index}
this.map.forEach((oldImg, id) => { this.map.forEach((oldImg, id) => {
if(oldImg.time < oldest.time){ if(oldImg.index < oldest.index){
oldest.id = id oldest.id = id
oldest.time = oldImg.time oldest.index = oldImg.index
} }
}) })
var oldImg = this.map.get(oldest.id) var oldImg = this.map.get(oldest.id)
@ -84,7 +85,7 @@ class CanvasCache{
this.map.set(config.id, img) this.map.set(config.id, img)
callback(this.ctx) callback(this.ctx)
} }
img.time = time img.index = index
if(setOnly){ if(setOnly){
this.ctx.restore() this.ctx.restore()
return return

View File

@ -1304,7 +1304,7 @@
ctx.globalAlpha = 1 - config.shine ctx.globalAlpha = 1 - config.shine
} }
ctx.strokeStyle = config.type ? "#000" : "rgba(255, 193, 0, 0.5)" ctx.strokeStyle = config.type ? "#000" : "#ffc616"
ctx.lineWidth = 18 ctx.lineWidth = 18
ctx.stroke(this.crownPath) ctx.stroke(this.crownPath)
@ -1362,12 +1362,21 @@
ctx.fillStyle = "#000" ctx.fillStyle = "#000"
ctx.beginPath() ctx.beginPath()
if(config.scoresheet){ if(config.scoresheet){
if(config.multiplayer){
ctx.moveTo(-4, -4)
ctx.lineTo(760, -4)
this.roundedCorner(ctx, 760, 48, 13, 2)
this.roundedCorner(ctx, gaugeClear - 4, 48, 13, 3)
ctx.lineTo(gaugeClear - 4, 26)
ctx.lineTo(-4, 26)
}else{
ctx.moveTo(-4, 26) ctx.moveTo(-4, 26)
ctx.lineTo(gaugeClear - 4, 26) ctx.lineTo(gaugeClear - 4, 26)
this.roundedCorner(ctx, gaugeClear - 4, 4, 13, 0) this.roundedCorner(ctx, gaugeClear - 4, 4, 13, 0)
this.roundedCorner(ctx, 760, 4, 13, 1) this.roundedCorner(ctx, 760, 4, 13, 1)
ctx.lineTo(760, 56) ctx.lineTo(760, 56)
ctx.lineTo(-4, 56) ctx.lineTo(-4, 56)
}
}else if(config.multiplayer){ }else if(config.multiplayer){
ctx.moveTo(gaugeClear - 7, 27) ctx.moveTo(gaugeClear - 7, 27)
ctx.lineTo(788, 27) ctx.lineTo(788, 27)
@ -1381,18 +1390,19 @@
} }
ctx.fill() ctx.fill()
if(gaugeFilled <= gaugeClear){ if(gaugeFilled < gaugeClear){
ctx.fillStyle = config.blue ? "#184d55" : "#680000" ctx.fillStyle = config.blue ? "#184d55" : "#680000"
var x = Math.max(0, gaugeFilled - 5) var x = Math.max(0, gaugeFilled - 5)
ctx.fillRect(x, firstTop, gaugeClear - x + 2, 22) ctx.fillRect(x, firstTop, gaugeClear - x + 2 + (gaugeClear < gaugeW ? 0 : -7), 22)
} }
if(gaugeFilled > 0){ if(gaugeFilled > 0){
var w = Math.min(gaugeClear + 1, gaugeFilled - 4) var w = Math.min(gaugeW - 5, gaugeClear + 1, gaugeFilled - 4)
ctx.fillStyle = config.blue ? "#00edff" : "#ff3408" ctx.fillStyle = config.blue ? "#00edff" : "#ff3408"
ctx.fillRect(0, firstTop + 2, w, 20) ctx.fillRect(0, firstTop + 2, w, 20)
ctx.fillStyle = config.blue ? "#9cffff" : "#ffa191" ctx.fillStyle = config.blue ? "#9cffff" : "#ffa191"
ctx.fillRect(0, firstTop, w, 3) ctx.fillRect(0, firstTop, w, 3)
} }
if(gaugeClear < gaugeW){
if(gaugeFilled < gaugeW - 4){ if(gaugeFilled < gaugeW - 4){
ctx.fillStyle = "#684900" ctx.fillStyle = "#684900"
var x = Math.max(gaugeClear + 9, gaugeFilled - gaugeClear + 9) var x = Math.max(gaugeClear + 9, gaugeFilled - gaugeClear + 9)
@ -1417,6 +1427,7 @@
ctx.lineTo(gaugeClear + 10, secondTop + 44) ctx.lineTo(gaugeClear + 10, secondTop + 44)
} }
ctx.fill() ctx.fill()
}
if(cleared){ if(cleared){
ctx.save() ctx.save()
ctx.clip() ctx.clip()
@ -1430,7 +1441,7 @@
ctx.lineWidth = 5 ctx.lineWidth = 5
for(var i = 0; i < 49; i++){ for(var i = 0; i < 49; i++){
var x = 14 + i * 14 - ctx.lineWidth / 2 var x = 14 + i * 14 - ctx.lineWidth / 2
if(i === 26){ if(i === config.clear * 50 - 1){
ctx.stroke() ctx.stroke()
ctx.beginPath() ctx.beginPath()
ctx.lineWidth = 4 ctx.lineWidth = 4
@ -1439,6 +1450,7 @@
ctx.lineTo(x, x < gaugeClear ? firstTop + 22 : secondTop + 44) ctx.lineTo(x, x < gaugeClear ? firstTop + 22 : secondTop + 44)
} }
ctx.stroke() ctx.stroke()
if(config.clear < 47 / 50){
this.layeredText({ this.layeredText({
ctx: ctx, ctx: ctx,
text: strings.clear, text: strings.clear,
@ -1451,6 +1463,7 @@
{scale: [1.1, 1.01], outline: "#000", letterBorder: 6}, {scale: [1.1, 1.01], outline: "#000", letterBorder: 6},
{scale: [1.11, 1], fill: cleared ? "#fff" : "#737373"} {scale: [1.11, 1], fill: cleared ? "#fff" : "#737373"}
]) ])
}
ctx.restore() ctx.restore()
} }

View File

@ -3,6 +3,7 @@ class Controller{
this.selectedSong = selectedSong this.selectedSong = selectedSong
this.songData = songData this.songData = songData
this.autoPlayEnabled = autoPlayEnabled this.autoPlayEnabled = autoPlayEnabled
this.saveScore = !autoPlayEnabled
this.multiplayer = multiplayer this.multiplayer = multiplayer
this.touchEnabled = touchEnabled this.touchEnabled = touchEnabled
this.snd = this.multiplayer ? "_p" + this.multiplayer : "" this.snd = this.multiplayer ? "_p" + this.multiplayer : ""

View File

@ -17,6 +17,7 @@ class Debug{
this.branchSelect = this.branchSelectDiv.getElementsByTagName("select")[0] this.branchSelect = this.branchSelectDiv.getElementsByTagName("select")[0]
this.branchResetBtn = this.branchSelectDiv.getElementsByClassName("reset")[0] this.branchResetBtn = this.branchSelectDiv.getElementsByClassName("reset")[0]
this.volumeDiv = this.byClass("music-volume") this.volumeDiv = this.byClass("music-volume")
this.restartLabel = this.byClass("change-restart-label")
this.restartCheckbox = this.byClass("change-restart") this.restartCheckbox = this.byClass("change-restart")
this.autoplayLabel = this.byClass("autoplay-label") this.autoplayLabel = this.byClass("autoplay-label")
this.autoplayCheckbox = this.byClass("autoplay") this.autoplayCheckbox = this.byClass("autoplay")
@ -24,15 +25,19 @@ class Debug{
this.exitBtn = this.byClass("exit-btn") this.exitBtn = this.byClass("exit-btn")
this.moving = false this.moving = false
pageEvents.add(window, ["mousedown", "mouseup", "blur"], this.stopMove.bind(this)) this.windowSymbol = Symbol()
pageEvents.add(window, ["mousedown", "mouseup", "touchstart", "touchend", "blur", "resize"], this.stopMove.bind(this), this.windowSymbol)
pageEvents.mouseAdd(this, this.onMove.bind(this)) pageEvents.mouseAdd(this, this.onMove.bind(this))
pageEvents.add(this.titleDiv, "mousedown", this.startMove.bind(this)) pageEvents.add(window, "touchmove", this.onMove.bind(this))
pageEvents.add(this.minimiseDiv, "click", this.minimise.bind(this)) pageEvents.add(this.titleDiv, ["mousedown", "touchstart"], this.startMove.bind(this))
pageEvents.add(this.restartBtn, "click", this.restartSong.bind(this)) pageEvents.add(this.minimiseDiv, ["click", "touchstart"], this.minimise.bind(this))
pageEvents.add(this.exitBtn, "click", this.clean.bind(this)) pageEvents.add(this.restartBtn, ["click", "touchstart"], this.restartSong.bind(this))
pageEvents.add(this.exitBtn, ["click", "touchstart"], this.clean.bind(this))
pageEvents.add(this.restartLabel, "touchstart", this.touchBox.bind(this))
pageEvents.add(this.autoplayLabel, "touchstart", this.touchBox.bind(this))
pageEvents.add(this.autoplayCheckbox, "change", this.toggleAutoplay.bind(this)) pageEvents.add(this.autoplayCheckbox, "change", this.toggleAutoplay.bind(this))
pageEvents.add(this.branchSelect, "change", this.branchChange.bind(this)) pageEvents.add(this.branchSelect, "change", this.branchChange.bind(this))
pageEvents.add(this.branchResetBtn, "click", this.branchReset.bind(this)) pageEvents.add(this.branchResetBtn, ["click", "touchstart"], this.branchReset.bind(this))
this.offsetSlider = new InputSlider(this.offsetDiv, -60, 60, 3) this.offsetSlider = new InputSlider(this.offsetDiv, -60, 60, 3)
this.offsetSlider.onchange(this.offsetChange.bind(this)) this.offsetSlider.onchange(this.offsetChange.bind(this))
@ -54,24 +59,38 @@ class Debug{
return this.debugDiv.getElementsByClassName(name)[0] return this.debugDiv.getElementsByClassName(name)[0]
} }
startMove(event){ startMove(event){
if(event.which === 1){ if(event.which === 1 || event.type === "touchstart"){
event.stopPropagation() event.stopPropagation()
this.moving = { var divPos = this.debugDiv.getBoundingClientRect()
x: event.offsetX, var click = event.type === "touchstart" ? event.changedTouches[0] : event
y: event.offsetY var x = click.pageX - divPos.left
} var y = click.pageY - divPos.top
this.moving = {x: x, y: y}
} }
} }
onMove(event){ onMove(event){
if(this.moving){ if(this.moving){
var x = event.clientX - this.moving.x var click = event.type === "touchmove" ? event.changedTouches[0] : event
var y = event.clientY - this.moving.y var x = click.clientX - this.moving.x
var y = click.clientY - this.moving.y
this.moveTo(x, y) this.moveTo(x, y)
} }
} }
stopMove(event){ stopMove(event){
var x = event.clientX - this.moving.x if(!event || event.type === "resize"){
var y = event.clientY - this.moving.y var divPos = this.debugDiv.getBoundingClientRect()
var x = divPos.left
var y = divPos.top
}else{
var click = event.type === "touchstart" || event.type === "touchend" ? event.changedTouches[0] : event
if(event.type == "blur"){
var x = this.moving.x
var y = this.moving.y
}else{
var x = click.clientX - this.moving.x
var y = click.clientY - this.moving.y
}
}
var w = this.debugDiv.offsetWidth var w = this.debugDiv.offsetWidth
var h = this.debugDiv.offsetHeight var h = this.debugDiv.offsetHeight
if(x + w > innerWidth){ if(x + w > innerWidth){
@ -95,6 +114,7 @@ class Debug{
restore(){ restore(){
debugObj.state = "open" debugObj.state = "open"
this.debugDiv.style.display = "" this.debugDiv.style.display = ""
this.stopMove()
} }
minimise(){ minimise(){
debugObj.state = "minimised" debugObj.state = "minimised"
@ -156,6 +176,7 @@ class Debug{
this.branchHideDiv.style.display = "" this.branchHideDiv.style.display = ""
this.controller = null this.controller = null
} }
this.stopMove()
} }
offsetChange(value, noRestart){ offsetChange(value, noRestart){
if(this.controller){ if(this.controller){
@ -197,10 +218,12 @@ class Debug{
this.controller.restartSong() this.controller.restartSong()
} }
} }
toggleAutoplay(){ toggleAutoplay(event){
if(this.controller){ if(this.controller){
this.controller.autoPlayEnabled = this.autoplayCheckbox.checked this.controller.autoPlayEnabled = this.autoplayCheckbox.checked
if(!this.controller.autoPlayEnabled){ if(this.controller.autoPlayEnabled){
this.controller.saveScore = false
}else{
var keyboard = debugObj.controller.keyboard var keyboard = debugObj.controller.keyboard
keyboard.setKey(false, "don_l") keyboard.setKey(false, "don_l")
keyboard.setKey(false, "don_r") keyboard.setKey(false, "don_r")
@ -229,21 +252,30 @@ class Debug{
this.branchSelect.value = "auto" this.branchSelect.value = "auto"
this.branchChange(null, noRestart) this.branchChange(null, noRestart)
} }
touchBox(event){
event.currentTarget.click()
}
clean(){ clean(){
this.offsetSlider.clean() this.offsetSlider.clean()
this.measureNumSlider.clean() this.measureNumSlider.clean()
this.volumeSlider.clean()
pageEvents.remove(window, ["mousedown", "mouseup", "blur"]) pageEvents.remove(window, ["mousedown", "mouseup", "touchstart", "touchend", "blur", "resize"], this.windowSymbol)
pageEvents.mouseRemove(this) pageEvents.mouseRemove(this)
pageEvents.remove(this.titleDiv, "mousedown") pageEvents.remove(window, "touchmove")
pageEvents.remove(this.title, "mousedown") pageEvents.remove(this.titleDiv, ["mousedown", "touchstart"])
pageEvents.remove(this.minimiseDiv, "click") pageEvents.remove(this.minimiseDiv, ["click", "touchstart"])
pageEvents.remove(this.restartBtn, "click") pageEvents.remove(this.restartBtn, ["click", "touchstart"])
pageEvents.remove(this.exitBtn, "click") pageEvents.remove(this.exitBtn, ["click", "touchstart"])
pageEvents.remove(this.restartLabel, "touchstart")
pageEvents.remove(this.autoplayLabel, "touchstart")
pageEvents.remove(this.autoplayCheckbox, "change") pageEvents.remove(this.autoplayCheckbox, "change")
pageEvents.remove(this.branchSelect, "change") pageEvents.remove(this.branchSelect, "change")
pageEvents.remove(this.branchResetBtn, "click") pageEvents.remove(this.branchResetBtn, ["click", "touchstart"])
delete this.offsetSlider
delete this.measureNumSlider
delete this.volumeSlider
delete this.titleDiv delete this.titleDiv
delete this.minimiseDiv delete this.minimiseDiv
delete this.offsetDiv delete this.offsetDiv
@ -259,6 +291,7 @@ class Debug{
delete this.restartBtn delete this.restartBtn
delete this.exitBtn delete this.exitBtn
delete this.controller delete this.controller
delete this.windowSymbol
debugObj.state = "closed" debugObj.state = "closed"
debugObj.debug = null debugObj.debug = null
@ -281,10 +314,22 @@ class InputSlider{
this.value = null this.value = null
this.defaultValue = null this.defaultValue = null
this.callbacks = [] this.callbacks = []
this.touchEnd = []
this.windowSymbol = Symbol()
pageEvents.add(this.input, ["touchstart", "touchend"], event => {
event.stopPropagation()
})
pageEvents.add(window, ["mouseup", "touchstart", "touchend", "blur"], event => {
if(event.type !== "touchstart"){
this.touchEnd.forEach(func => func(event))
}else if(event.target !== this.input){
this.input.blur()
}
}, this.windowSymbol)
pageEvents.add(this.plus, "click", this.add.bind(this)) this.addTouchRepeat(this.plus, this.add.bind(this))
pageEvents.add(this.minus, "click", this.subtract.bind(this)) this.addTouchRepeat(this.minus, this.subtract.bind(this))
pageEvents.add(this.reset, "click", this.resetValue.bind(this)) this.addTouch(this.reset, this.resetValue.bind(this))
pageEvents.add(this.input, "change", this.manualSet.bind(this)) pageEvents.add(this.input, "change", this.manualSet.bind(this))
pageEvents.add(this.input, "keydown", this.captureKeys.bind(this)) pageEvents.add(this.input, "keydown", this.captureKeys.bind(this))
} }
@ -364,15 +409,49 @@ class InputSlider{
captureKeys(event){ captureKeys(event){
event.stopPropagation() event.stopPropagation()
} }
addTouch(element, callback){
pageEvents.add(element, ["mousedown", "touchstart"], event => {
if(event.type === "touchstart"){
event.preventDefault()
}else if(event.which !== 1){
return
}
callback(event)
})
}
addTouchRepeat(element, callback){
this.addTouch(element, event => {
var active = true
var func = () => {
active = false
this.touchEnd.splice(this.touchEnd.indexOf(func), 1)
}
this.touchEnd.push(func)
var repeat = delay => {
if(active && this.touchEnd){
callback(event)
setTimeout(() => repeat(50), delay)
}
}
repeat(400)
})
}
removeTouch(element){
pageEvents.remove(element, ["mousedown", "touchstart"])
}
clean(){ clean(){
pageEvents.remove(this.plus, "click") this.removeTouch(this.plus)
pageEvents.remove(this.minus, "click") this.removeTouch(this.minus)
pageEvents.remove(this.reset, "click") this.removeTouch(this.reset)
pageEvents.remove(this.input, ["change", "keydown"]) pageEvents.remove(this.input, ["touchstart", "touchend"])
pageEvents.remove(window, ["mouseup", "touchstart", "touchend", "blur"], this.windowSymbol)
pageEvents.remove(this.input, ["touchstart", "change", "keydown"])
delete this.input delete this.input
delete this.reset delete this.reset
delete this.plus delete this.plus
delete this.minus delete this.minus
delete this.windowSymbol
delete this.touchEnd
} }
} }

View File

@ -811,7 +811,7 @@ class Game{
offsets.push(offset) offsets.push(offset)
progress.hit++ progress.hit++
progress.last = current progress.last = current
this.globalScore.gauge = 100 / (progress.requirement / progress.hit) this.globalScore.gauge = 10000 / (progress.requirement / progress.hit)
} }
} }
calibrationReset(to, togglePause){ calibrationReset(to, togglePause){

View File

@ -13,6 +13,7 @@ class GameRules{
case "hard": case "hard":
case "oni": case "oni":
case "ura": case "ura":
default:
this.good = 3 / 2 * frame this.good = 3 / 2 * frame
this.ok = 9 / 2 * frame this.ok = 9 / 2 * frame
this.bad = 13 / 2 * frame this.bad = 13 / 2 * frame
@ -30,6 +31,9 @@ class GameRules{
case "ura": case "ura":
this.gaugeClear = 40 / 50 this.gaugeClear = 40 / 50
break break
default:
this.gaugeClear = 51 / 50
break
} }
this.daiLeniency = 2 * frame this.daiLeniency = 2 * frame
@ -61,8 +65,10 @@ class GameRules{
} }
return {good: good, ok: ok, bad: bad} return {good: good, ok: ok, bad: bad}
} }
gaugePercent(gauge){
return Math.floor(gauge / 200) / 50
}
clearReached(gauge){ clearReached(gauge){
var gaugePercent = Math.round(gauge / 200) / 50 return this.gaugePercent(gauge) >= this.gaugeClear
return gaugePercent >= this.gaugeClear
} }
} }

View File

@ -269,7 +269,7 @@
songObj.subtitle_lang = subtitleLangArray.join("\n") songObj.subtitle_lang = subtitleLangArray.join("\n")
} }
if(!songObj.category){ if(!songObj.category){
songObj.category = category || this.getCategory(file) songObj.category = category || this.getCategory(file, [songTitle || songObj.title, file.name.slice(0, file.name.lastIndexOf("."))])
} }
if(songObj.stars.length !== 0){ if(songObj.stars.length !== 0){
this.songs[index] = songObj this.songs[index] = songObj
@ -277,7 +277,7 @@
var hash = md5.base64(event.target.result).slice(0, -2) var hash = md5.base64(event.target.result).slice(0, -2)
songObj.hash = hash songObj.hash = hash
scoreStorage.songTitles[songObj.title] = hash scoreStorage.songTitles[songObj.title] = hash
var score = scoreStorage.get(hash) var score = scoreStorage.get(hash, false, true)
if(score){ if(score){
score.title = songObj.title score.title = songObj.title
} }
@ -307,7 +307,7 @@
music: this.otherFiles[dir + osu.generalInfo.AudioFilename.toLowerCase()] || "muted" music: this.otherFiles[dir + osu.generalInfo.AudioFilename.toLowerCase()] || "muted"
} }
var filename = file.name.slice(0, file.name.lastIndexOf(".")) var filename = file.name.slice(0, file.name.lastIndexOf("."))
var title = osu.metadata.TitleUnicode || osu.metadata.Title var title = osu.metadata.TitleUnicode || osu.metadata.Title || file.name.slice(0, file.name.lastIndexOf("."))
if(title){ if(title){
var suffix = "" var suffix = ""
var matches = filename.match(/\[.+?\]$/) var matches = filename.match(/\[.+?\]$/)
@ -320,11 +320,11 @@
songObj.title = filename songObj.title = filename
} }
this.songs[index] = songObj this.songs[index] = songObj
songObj.category = category || this.getCategory(file) songObj.category = category || this.getCategory(file, [osu.metadata.TitleUnicode, osu.metadata.Title, file.name.slice(0, file.name.lastIndexOf("."))])
var hash = md5.base64(event.target.result).slice(0, -2) var hash = md5.base64(event.target.result).slice(0, -2)
songObj.hash = hash songObj.hash = hash
scoreStorage.songTitles[songObj.title] = hash scoreStorage.songTitles[songObj.title] = hash
var score = scoreStorage.get(hash) var score = scoreStorage.get(hash, false, true)
if(score){ if(score){
score.title = songObj.title score.title = songObj.title
} }
@ -394,9 +394,17 @@
return name.slice(0, name.lastIndexOf(".")) return name.slice(0, name.lastIndexOf("."))
} }
getCategory(file){ getCategory(file, exclude){
var path = file.webkitRelativePath.toLowerCase().split("/") var path = file.webkitRelativePath.toLowerCase().split("/")
for(var i = path.length - 2; i >= 0; i--){ for(var i = path.length - 2; i >= 0; i--){
var hasTitle = false
for(var j in exclude){
if(path[i].indexOf(exclude[j].toLowerCase()) !== -1){
hasTitle = true
break
}
}
if(!hasTitle){
for(var cat in this.categories){ for(var cat in this.categories){
if(path[i].indexOf(cat) !== -1){ if(path[i].indexOf(cat) !== -1){
return this.categories[cat] return this.categories[cat]
@ -404,6 +412,7 @@
} }
} }
} }
}
getSkin(dir, config){ getSkin(dir, config){
var configArray = config.toLowerCase().split(",") var configArray = config.toLowerCase().split(",")

View File

@ -1 +0,0 @@
FontDetect=function(){function e(){if(!n){n=!0;var e=document.body,t=document.body.firstChild,i=document.createElement("div");i.id="fontdetectHelper",r=document.createElement("span"),r.innerText="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",i.appendChild(r),e.insertBefore(i,t),i.style.position="absolute",i.style.visibility="hidden",i.style.top="-200px",i.style.left="-100000px",i.style.width="100000px",i.style.height="200px",i.style.fontSize="100px"}}function t(e,t){return e instanceof Element?window.getComputedStyle(e).getPropertyValue(t):window.jQuery?$(e).css(t):""}var n=!1,i=["serif","sans-serif","monospace","cursive","fantasy"],r=null;return{onFontLoaded:function(t,i,r,o){if(t){var s=o&&o.msInterval?o.msInterval:100,a=o&&o.msTimeout?o.msTimeout:2e3;if(i||r){if(n||e(),this.isFontLoaded(t))return void(i&&i(t));var l=this,f=(new Date).getTime(),d=setInterval(function(){if(l.isFontLoaded(t))return clearInterval(d),void i(t);var e=(new Date).getTime();e-f>a&&(clearInterval(d),r&&r(t))},s)}}},isFontLoaded:function(t){var o=0,s=0;n||e();for(var a=0;a<i.length;++a){if(r.style.fontFamily='"'+t+'",'+i[a],o=r.offsetWidth,a>0&&o!=s)return!1;s=o}return!0},whichFont:function(e){for(var n=t(e,"font-family"),r=n.split(","),o=r.shift();o;){o=o.replace(/^\s*['"]?\s*([^'"]*)\s*['"]?\s*$/,"$1");for(var s=0;s<i.length;s++)if(o==i[s])return o;if(this.isFontLoaded(o))return o;o=r.shift()}return null}}}();

View File

@ -20,6 +20,7 @@ class Loader{
} }
run(){ run(){
this.promises = [] this.promises = []
this.loaderDiv = document.querySelector("#loader")
this.loaderPercentage = document.querySelector("#loader .percentage") this.loaderPercentage = document.querySelector("#loader .percentage")
this.loaderProgress = document.querySelector("#loader .progress") this.loaderProgress = document.querySelector("#loader .progress")
@ -70,12 +71,11 @@ class Loader{
checkStyles() checkStyles()
})) }))
assets.fonts.forEach(name => { for(var name in assets.fonts){
var font = document.createElement("h1") this.addPromise(new FontFace(name, "url('" + gameConfig.assets_baseurl + "fonts/" + assets.fonts[name] + "')").load().then(font => {
font.style.fontFamily = name document.fonts.add(font)
font.appendChild(document.createTextNode("I am a font")) }))
this.assetsDiv.appendChild(font) }
})
assets.img.forEach(name => { assets.img.forEach(name => {
var id = this.getFilename(name) var id = this.getFilename(name)
@ -105,7 +105,6 @@ class Loader{
this.afterJSCount = this.afterJSCount =
["blurPerformance", "P2Connection"].length + ["blurPerformance", "P2Connection"].length +
assets.fonts.length +
assets.audioSfx.length + assets.audioSfx.length +
assets.audioMusic.length + assets.audioMusic.length +
assets.audioSfxLR.length + assets.audioSfxLR.length +
@ -130,12 +129,6 @@ class Loader{
this.afterJSCount = 0 this.afterJSCount = 0
assets.fonts.forEach(name => {
this.addPromise(new Promise(resolve => {
FontDetect.onFontLoaded(name, resolve, resolve, {msTimeout: Infinity})
}))
})
assets.audioSfx.forEach(name => { assets.audioSfx.forEach(name => {
this.addPromise(this.loadSound(name, snd.sfxGain)) this.addPromise(this.loadSound(name, snd.sfxGain))
}) })
@ -213,7 +206,7 @@ class Loader{
song.hash = song.title song.hash = song.title
} }
scoreStorage.songTitles[song.title] = song.hash scoreStorage.songTitles[song.title] = song.hash
var score = scoreStorage.get(song.hash) var score = scoreStorage.get(song.hash, false, true)
if(score){ if(score){
score.title = song.title score.title = song.title
} }
@ -248,13 +241,39 @@ class Loader{
return name.slice(0, name.lastIndexOf(".")) return name.slice(0, name.lastIndexOf("."))
} }
errorMsg(error){ errorMsg(error){
if(Array.isArray(error) && error[1] instanceof HTMLElement){
error = error[0] + ": " + error[1].outerHTML
}
console.error(error) console.error(error)
pageEvents.send("loader-error", error) pageEvents.send("loader-error", error)
if(!this.error){
this.error = true 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.createElement("br"))
this.loaderPercentage.appendChild(document.createTextNode("An error occurred, please refresh")) this.loaderPercentage.appendChild(document.createTextNode(errorOccured))
this.clean() this.clean()
} }
}
assetLoaded(){ assetLoaded(){
if(!this.error){ if(!this.error){
this.loadedAssets++ this.loadedAssets++

View File

@ -160,7 +160,7 @@ class LoadSong{
console.error(error) console.error(error)
pageEvents.send("load-song-error", error) pageEvents.send("load-song-error", error)
errorMessage(new Error(error).stack) errorMessage(new Error(error).stack)
alert("An error occurred, please refresh") alert(strings.errorOccured)
}) })
} }
loadSongBg(){ loadSongBg(){

View File

@ -102,7 +102,7 @@ pageEvents.add(versionDiv, ["click", "touchend"], event => {
resizeRoot() resizeRoot()
setInterval(resizeRoot, 100) setInterval(resizeRoot, 100)
pageEvents.keyAdd(debugObj, "all", "down", event => { pageEvents.keyAdd(debugObj, "all", "down", event => {
if((event.keyCode === 186 || event.keyCode === 59) && event.ctrlKey && event.shiftKey && !event.altKey){ if((event.keyCode === 186 || event.keyCode === 59) && event.ctrlKey && (event.shiftKey || event.altKey)){
// Semicolon // Semicolon
if(debugObj.state === "open"){ if(debugObj.state === "open"){
debugObj.debug.minimise() debugObj.debug.minimise()

View File

@ -11,60 +11,62 @@ class PageEvents{
this.add(window, "blur", this.blurEvent.bind(this)) this.add(window, "blur", this.blurEvent.bind(this))
this.kbd = [] this.kbd = []
} }
add(target, type, callback){ add(target, type, callback, symbol){
if(Array.isArray(type)){ if(Array.isArray(type)){
type.forEach(type => this.add(target, type, callback)) type.forEach(type => this.add(target, type, callback, symbol))
return return
} }
this.remove(target, type) this.remove(target, type)
var addedEvent = this.allEvents.get(target) var addedEvent = this.allEvents.get(symbol || target)
if(!addedEvent){ if(!addedEvent){
addedEvent = new Map() addedEvent = new Map()
this.allEvents.set(target, addedEvent) this.allEvents.set(symbol || target, addedEvent)
} }
addedEvent.set(type, callback) addedEvent.set(type, callback)
return target.addEventListener(type, callback) return target.addEventListener(type, callback)
} }
remove(target, type){ remove(target, type, symbol){
if(Array.isArray(type)){ if(Array.isArray(type)){
type.forEach(type => this.remove(target, type)) type.forEach(type => this.remove(target, type, symbol))
return return
} }
var addedEvent = this.allEvents.get(target) var addedEvent = this.allEvents.get(symbol || target)
if(addedEvent){ if(addedEvent){
var callback = addedEvent.get(type) var callback = addedEvent.get(type)
if(callback){ if(callback){
target.removeEventListener(type, callback) target.removeEventListener(type, callback)
addedEvent.delete(type) addedEvent.delete(type)
if(addedEvent.size == 0){ if(addedEvent.size == 0){
return this.allEvents.delete(target) return this.allEvents.delete(symbol || target)
} }
} }
} }
} }
once(target, type){ once(target, type, symbol){
return new Promise(resolve => { return new Promise(resolve => {
this.add(target, type, event => { this.add(target, type, event => {
this.remove(target, type) this.remove(target, type)
return resolve(event) return resolve(event)
}) }, symbol)
}) })
} }
race(){ race(){
var symbols = []
var target = arguments[0] var target = arguments[0]
return new Promise(resolve => { return new Promise(resolve => {
for(var i = 1;i < arguments.length; i++){ for(var i = 1;i < arguments.length; i++){
symbols[i] = Symbol()
let type = arguments[i] let type = arguments[i]
this.add(target, type, event => { this.add(target, type, event => {
resolve({ resolve({
type: type, type: type,
event: event event: event
}) })
}) }, symbols[i])
} }
}).then(response => { }).then(response => {
for(var i = 1;i < arguments.length; i++){ for(var i = 1;i < arguments.length; i++){
this.remove(target, arguments[i]) this.remove(target, arguments[i], symbols[i])
} }
return response return response
}) })

View File

@ -149,6 +149,7 @@
var circles = [] var circles = []
var circleID = 0 var circleID = 0
var regexAZ = /[A-Z]/ var regexAZ = /[A-Z]/
var regexSpace = /\s/
var isAllDon = (note_chain, start_pos) => { var isAllDon = (note_chain, start_pos) => {
for (var i = start_pos; i < note_chain.length; ++i) { for (var i = start_pos; i < note_chain.length; ++i) {
var note = note_chain[i]; var note = note_chain[i];
@ -470,7 +471,7 @@
bpm: bpm, bpm: bpm,
scroll: scroll scroll: scroll
}) })
}else{ }else if(!regexSpace.test(symbol)){
error = true error = true
} }
break break

View File

@ -63,7 +63,8 @@ class Scoresheet{
assets.sounds["v_results"].play() assets.sounds["v_results"].play()
assets.sounds["bgm_result"].playLoop(3, false, 0, 0.847, 17.689) assets.sounds["bgm_result"].playLoop(3, false, 0, 0.847, 17.689)
if(p2.session){ this.session = p2.session
if(this.session){
if(p2.getMessage("songsel")){ if(p2.getMessage("songsel")){
this.toSongsel(true) this.toSongsel(true)
} }
@ -324,14 +325,15 @@ class Scoresheet{
var elapsed = 0 var elapsed = 0
} }
var gaugePercent = Math.round(this.results.gauge / 200) / 50 var rules = this.controller.game.rules
var gaugeClear = [this.controller.game.rules.gaugeClear] var gaugePercent = rules.gaugePercent(this.results.gauge)
var gaugeClear = [rules.gaugeClear]
if(players === 2){ if(players === 2){
gaugeClear.push(this.controller.syncWith.game.rules.gaugeClear) gaugeClear.push(this.controller.syncWith.game.rules.gaugeClear)
} }
var failedOffset = gaugePercent >= gaugeClear[0] ? 0 : -2000 var failedOffset = gaugePercent >= gaugeClear[0] ? 0 : -2000
if(players === 2){ if(players === 2){
var gauge2 = Math.round(p2.results.gauge / 200) / 50 var gauge2 = this.controller.syncWith.game.rules.gaugePercent(p2.results.gauge)
if(gauge2 > gaugePercent && failedOffset !== 0 && gauge2 >= gaugeClear[1]){ if(gauge2 > gaugePercent && failedOffset !== 0 && gauge2 >= gaugeClear[1]){
failedOffset = 0 failedOffset = 0
} }
@ -343,7 +345,8 @@ class Scoresheet{
if(p === 1){ if(p === 1){
results = p2.results results = p2.results
} }
var resultGauge = Math.round(results.gauge / 200) / 50 var playerRules = p === 0 ? rules : this.controller.syncWith.game.rules
var resultGauge = playerRules.gaugePercent(results.gauge)
var clear = resultGauge >= gaugeClear[p] var clear = resultGauge >= gaugeClear[p]
if(p === 1 || !this.multiplayer && clear){ if(p === 1 || !this.multiplayer && clear){
ctx.translate(0, 290) ctx.translate(0, 290)
@ -368,8 +371,8 @@ class Scoresheet{
if(elapsed >= 0){ if(elapsed >= 0){
if(this.state.hasPointer === 0){ if(this.state.hasPointer === 0){
this.state.hasPointer = 1 this.state.hasPointer = 1
if(!this.state.pointerLocked && !p2.session){ if(!this.state.pointerLocked){
this.canvas.style.cursor = "pointer" this.canvas.style.cursor = this.session ? "" : "pointer"
} }
} }
ctx.save() ctx.save()
@ -578,7 +581,7 @@ class Scoresheet{
if(this.tetsuoHanaClass){ if(this.tetsuoHanaClass){
this.tetsuoHana.classList.remove(this.tetsuoHanaClass) this.tetsuoHana.classList.remove(this.tetsuoHanaClass)
} }
this.tetsuoHanaClass = this.controller.game.rules.clearReached(this.results.gauge) ? "dance" : "failed" this.tetsuoHanaClass = rules.clearReached(this.results.gauge) ? "dance" : "failed"
this.tetsuoHana.classList.add(this.tetsuoHanaClass) this.tetsuoHana.classList.add(this.tetsuoHanaClass)
} }
} }
@ -597,26 +600,27 @@ class Scoresheet{
results = p2.results results = p2.results
ctx.translate(0, p2Offset) ctx.translate(0, p2Offset)
} }
var gaugePercent = Math.round(results.gauge / 200) / 50 var gaugePercent = rules.gaugePercent(results.gauge)
var w = 712 var w = 712
this.draw.gauge({ this.draw.gauge({
ctx: ctx, ctx: ctx,
x: 558 + w, x: 558 + w,
y: 116, y: p === 1 ? 124 : 116,
clear: gaugeClear[p], clear: gaugeClear[p],
percentage: gaugePercent, percentage: gaugePercent,
font: this.font, font: this.font,
scale: w / 788, scale: w / 788,
scoresheet: true, scoresheet: true,
blue: p === 1 blue: p === 1,
multiplayer: p === 1
}) })
var rules = p === 0 ? this.controller.game.rules : this.controller.syncWith.game.rules var playerRules = p === 0 ? rules : this.controller.syncWith.game.rules
this.draw.soul({ this.draw.soul({
ctx: ctx, ctx: ctx,
x: 1215, x: 1215,
y: 144, y: 144,
scale: 36 / 42, scale: 36 / 42,
cleared: rules.clearReached(results.gauge) cleared: playerRules.clearReached(results.gauge)
}) })
} }
}) })
@ -634,8 +638,8 @@ class Scoresheet{
results = p2.results results = p2.results
} }
var crownType = null var crownType = null
var rules = p === 0 ? this.controller.game.rules : this.controller.syncWith.game.rules var playerRules = p === 0 ? rules : this.controller.syncWith.game.rules
if(rules.clearReached(results.gauge)){ if(playerRules.clearReached(results.gauge)){
crownType = results.bad === "0" ? "gold" : "silver" crownType = results.bad === "0" ? "gold" : "silver"
} }
if(crownType !== null){ if(crownType !== null){
@ -796,9 +800,13 @@ class Scoresheet{
ctx.restore() ctx.restore()
} }
if(p2.session && !this.state.scoreNext && this.state.screen === "scoresShown" && ms - this.state.screenMS >= 10000){ if(this.session && !this.state.scoreNext && this.state.screen === "scoresShown" && ms - this.state.screenMS >= 10000){
this.state.scoreNext = true this.state.scoreNext = true
if(p2.session){
p2.send("songsel") p2.send("songsel")
}else{
this.toSongsel(true)
}
} }
if(this.state.screen === "fadeOut"){ if(this.state.screen === "fadeOut"){
@ -861,7 +869,7 @@ class Scoresheet{
} }
saveScore(){ saveScore(){
if(!this.controller.autoPlayEnabled){ if(this.controller.saveScore){
if(this.resultsObj.points < 0){ if(this.resultsObj.points < 0){
this.resultsObj.points = 0 this.resultsObj.points = 0
} }
@ -903,7 +911,7 @@ class Scoresheet{
if(this.multiplayer !== 2 && this.touchEnabled){ if(this.multiplayer !== 2 && this.touchEnabled){
pageEvents.remove(document.getElementById("touch-full-btn"), "touchend") pageEvents.remove(document.getElementById("touch-full-btn"), "touchend")
} }
if(p2.session){ if(this.session){
pageEvents.remove(p2, "message") pageEvents.remove(p2, "message")
} }
if(!this.multiplayer){ if(!this.multiplayer){

View File

@ -167,9 +167,29 @@ class SettingsView{
this.viewOuter.classList.add("touch-enabled") this.viewOuter.classList.add("touch-enabled")
} }
this.touchEnd = [] this.touchEnd = []
pageEvents.add(this.viewOuter, ["mouseup", "touchend"], event => { this.windowSymbol = Symbol()
this.touchMove = {
active: false,
x: 0,
y: 0
}
pageEvents.add(window, ["mouseup", "touchstart", "touchmove", "touchend", "blur"], event => {
var move = this.touchMove
if(event.type === "touchstart"){
var cursor = event.changedTouches[0]
move.active = false
move.x = cursor.pageX
move.y = cursor.pageY
}else if(event.type === "touchmove"){
var cursor = event.changedTouches[0]
if (Math.abs(move.x - cursor.pageX) > 10 || Math.abs(move.y - cursor.pageY) > 10){
move.active = true
}
}else{
this.touchEnd.forEach(func => func(event)) this.touchEnd.forEach(func => func(event))
}) move.active = false
}
}, this.windowSymbol)
var gamepadEnabled = false var gamepadEnabled = false
if("getGamepads" in navigator){ if("getGamepads" in navigator){
@ -234,7 +254,7 @@ class SettingsView{
this.selected = this.items.length this.selected = this.items.length
settingBox.classList.add("selected") settingBox.classList.add("selected")
} }
this.addTouch(settingBox, event => this.setValue(i)) this.addTouchEnd(settingBox, event => this.setValue(i))
this.items.push({ this.items.push({
id: i, id: i,
settingBox: settingBox, settingBox: settingBox,
@ -365,9 +385,10 @@ class SettingsView{
getElement(name){ getElement(name){
return loader.screen.getElementsByClassName(name)[0] return loader.screen.getElementsByClassName(name)[0]
} }
addTouch(element, callback){ addTouch(element, callback, end){
pageEvents.add(element, ["mousedown", "touchstart"], event => { var touchEvent = end ? "touchend" : "touchstart"
if(event.type === "touchstart"){ pageEvents.add(element, ["mousedown", touchEvent], event => {
if(event.type === touchEvent){
event.preventDefault() event.preventDefault()
this.touched = true this.touched = true
}else if(event.which !== 1){ }else if(event.which !== 1){
@ -375,9 +396,14 @@ class SettingsView{
}else{ }else{
this.touched = false this.touched = false
} }
if(event.type !== "touchend" || !this.touchMove.active){
callback(event) callback(event)
}
}) })
} }
addTouchEnd(element, callback){
this.addTouch(element, callback, true)
}
addTouchRepeat(element, callback){ addTouchRepeat(element, callback){
this.addTouch(element, event => { this.addTouch(element, event => {
var active = true var active = true
@ -398,6 +424,9 @@ class SettingsView{
removeTouch(element){ removeTouch(element){
pageEvents.remove(element, ["mousedown", "touchstart"]) pageEvents.remove(element, ["mousedown", "touchstart"])
} }
removeTouchEnd(element){
pageEvents.remove(element, ["mousedown", "touchend"])
}
getValue(name, valueDiv){ getValue(name, valueDiv){
var current = settings.items[name] var current = settings.items[name]
var value = settings.getItem(name) var value = settings.getItem(name)
@ -879,9 +908,9 @@ class SettingsView{
this.keyboard.clean() this.keyboard.clean()
this.gamepad.clean() this.gamepad.clean()
assets.sounds["bgm_settings"].stop() assets.sounds["bgm_settings"].stop()
pageEvents.remove(this.viewOuter, ["mouseup", "touchend"]) pageEvents.remove(window, ["mouseup", "touchstart", "touchmove", "touchend", "blur"], this.windowSymbol)
for(var i in this.items){ for(var i in this.items){
this.removeTouch(this.items[i].settingBox) this.removeTouchEnd(this.items[i].settingBox)
} }
for(var i in this.latencyItems){ for(var i in this.latencyItems){
this.removeTouch(this.latencyItems[i].settingBox) this.removeTouch(this.latencyItems[i].settingBox)
@ -899,6 +928,8 @@ class SettingsView{
this.removeTouch(this.latencySettings) this.removeTouch(this.latencySettings)
this.removeTouch(this.latencyDefaultButton) this.removeTouch(this.latencyDefaultButton)
this.removeTouch(this.latencyEndButton) this.removeTouch(this.latencyEndButton)
delete this.windowSymbol
delete this.touchMove
delete this.viewOuter delete this.viewOuter
delete this.touchEnd delete this.touchEnd
delete this.tutorialTitle delete this.tutorialTitle

View File

@ -609,11 +609,17 @@ class SongSelect{
} }
} }
categoryJump(moveBy){ categoryJump(moveBy, fromP2){
if(this.state.locked === 1){ if(p2.session && !fromP2){
return var ms = this.getMS()
if(!this.state.selLock && ms > this.state.moveMS + 800){
this.state.selLock = true
p2.send("catjump", {
song: this.selectedSong,
move: moveBy
})
} }
}else if(this.state.locked !== 1 || fromP2){
this.state.catJump = true this.state.catJump = true
this.state.move = moveBy; this.state.move = moveBy;
this.state.locked = 1 this.state.locked = 1
@ -621,6 +627,7 @@ class SongSelect{
this.endPreview() this.endPreview()
this.playSound("se_jump") this.playSound("se_jump")
} }
}
moveToDiff(moveBy){ moveToDiff(moveBy){
if(this.state.locked !== 1){ if(this.state.locked !== 1){
@ -925,7 +932,7 @@ class SongSelect{
} }
this.selectableText = "" this.selectableText = ""
}else if(!document.hasFocus()){ }else if(!document.hasFocus() && !p2.session){
this.pointer(false) this.pointer(false)
return return
}else{ }else{
@ -1128,7 +1135,15 @@ class SongSelect{
disabled: p2.session && this.songs[index].action && this.songs[index].action !== "random" disabled: p2.session && this.songs[index].action && this.songs[index].action !== "random"
}) })
} }
var startFrom
for(var i = this.selectedSong + 1; ; i++){ for(var i = this.selectedSong + 1; ; i++){
var _x = winW / 2 + (i - this.selectedSong - 1) * (this.songAsset.width + this.songAsset.marginLeft) + this.songAsset.marginLeft + selectedWidth / 2 + xOffset
if(_x > winW){
startFrom = i - 1
break
}
}
for(var i = startFrom; i > this.selectedSong ; i--){
var highlight = 0 var highlight = 0
if(i - this.selectedSong === this.state.moveHover){ if(i - this.selectedSong === this.state.moveHover){
highlight = 1 highlight = 1
@ -1136,9 +1151,6 @@ class SongSelect{
var index = this.mod(this.songs.length, i) var index = this.mod(this.songs.length, i)
var currentSong = this.songs[index] var currentSong = this.songs[index]
var _x = winW / 2 + (i - this.selectedSong - 1) * (this.songAsset.width + this.songAsset.marginLeft) + this.songAsset.marginLeft + selectedWidth / 2 + xOffset var _x = winW / 2 + (i - this.selectedSong - 1) * (this.songAsset.width + this.songAsset.marginLeft) + this.songAsset.marginLeft + selectedWidth / 2 + xOffset
if(_x > winW){
break
}
this.drawClosedSong({ this.drawClosedSong({
ctx: ctx, ctx: ctx,
x: _x, x: _x,
@ -1150,6 +1162,43 @@ class SongSelect{
} }
} }
var currentSong = this.songs[this.selectedSong]
var highlight = 0
if(!currentSong.stars){
highlight = 2
}
if(this.state.moveHover === 0){
highlight = 1
}
var selectedSkin = this.songSkin.selected
if(screen === "title" || screen === "titleFadeIn" || this.state.locked === 3){
selectedSkin = currentSong.skin
highlight = 2
}else if(songSelMoving){
selectedSkin = currentSong.skin
highlight = 0
}
var selectedHeight = this.songAsset.height
if(screen === "difficulty"){
selectedWidth = this.songAsset.fullWidth
selectedHeight = this.songAsset.fullHeight
highlight = 0
}
if(this.currentSongTitle !== currentSong.title){
this.currentSongTitle = currentSong.title
this.currentSongCache.clear()
}
if(ms > this.state.screenMS + 2000 && selectedWidth === this.songAsset.width){
this.drawSongCrown({
ctx: ctx,
song: currentSong,
x: winW / 2 - selectedWidth / 2 + xOffset,
y: songTop + this.songAsset.height - selectedHeight
})
}
if(screen === "title" || screen === "titleFadeIn" || screen === "song"){ if(screen === "title" || screen === "titleFadeIn" || screen === "song"){
var textW = strings.id === "en" ? 350 : 280 var textW = strings.id === "en" ? 350 : 280
this.selectTextCache.get({ this.selectTextCache.get({
@ -1230,35 +1279,7 @@ class SongSelect{
}) })
} }
var currentSong = this.songs[this.selectedSong] if(ms <= this.state.screenMS + 2000 && selectedWidth === this.songAsset.width){
var highlight = 0
if(!currentSong.stars){
highlight = 2
}
if(this.state.moveHover === 0){
highlight = 1
}
var selectedSkin = this.songSkin.selected
if(screen === "title" || screen === "titleFadeIn" || this.state.locked === 3){
selectedSkin = currentSong.skin
highlight = 2
}else if(songSelMoving){
selectedSkin = currentSong.skin
highlight = 0
}
var selectedHeight = this.songAsset.height
if(screen === "difficulty"){
selectedWidth = this.songAsset.fullWidth
selectedHeight = this.songAsset.fullHeight
highlight = 0
}
if(this.currentSongTitle !== currentSong.title){
this.currentSongTitle = currentSong.title
this.currentSongCache.clear()
}
if(selectedWidth === this.songAsset.width){
this.drawSongCrown({ this.drawSongCrown({
ctx: ctx, ctx: ctx,
song: currentSong, song: currentSong,
@ -1398,7 +1419,7 @@ class SongSelect{
} }
var drawDifficulty = (ctx, i, currentUra) => { var drawDifficulty = (ctx, i, currentUra) => {
if(currentSong.stars[i] || currentUra){ if(currentSong.stars[i] || currentUra){
var score = scoreStorage.get(currentSong.hash) var score = scoreStorage.get(currentSong.hash, false, true)
var crownDiff = currentUra ? "ura" : this.difficultyId[i] var crownDiff = currentUra ? "ura" : this.difficultyId[i]
var crownType = "" var crownType = ""
if(score && score[crownDiff]){ if(score && score[crownDiff]){
@ -1585,6 +1606,8 @@ class SongSelect{
alphaFade = this.draw.easeIn((fade - 0.45) * 20) alphaFade = this.draw.easeIn((fade - 0.45) * 20)
} }
this.draw.alpha(alphaFade, ctx, ctx => { this.draw.alpha(alphaFade, ctx, ctx => {
ctx.fillStyle = this.songSkin.selected.background
ctx.fillRect(x + 7 + i * 60, y + 60, 52, 352)
drawDifficulty(ctx, i, true) drawDifficulty(ctx, i, true)
}, winW, winH) }, winW, winH)
}else{ }else{
@ -1990,7 +2013,7 @@ class SongSelect{
drawSongCrown(config){ drawSongCrown(config){
if(!config.song.action && config.song.hash){ if(!config.song.action && config.song.hash){
var ctx = config.ctx var ctx = config.ctx
var score = scoreStorage.get(config.song.hash) var score = scoreStorage.get(config.song.hash, false, true)
for(var i = this.difficultyId.length; i--;){ for(var i = this.difficultyId.length; i--;){
var diff = this.difficultyId[i] var diff = this.difficultyId[i]
if(!score){ if(!score){
@ -2140,13 +2163,19 @@ class SongSelect{
onsongsel(response){ onsongsel(response){
if(response && response.value){ if(response && response.value){
var selected = false var selected = false
if("selected" in response.value){ if(response.type === "songsel" && "selected" in response.value){
selected = response.value.selected selected = response.value.selected
} }
if("song" in response.value){ if("song" in response.value){
var song = +response.value.song var song = +response.value.song
if(song >= 0 && song < this.songs.length){ if(song >= 0 && song < this.songs.length){
if(!selected){ if(response.type === "catjump"){
var moveBy = response.value.move
if(moveBy === -1 || moveBy === 1){
this.selectedSong = song
this.categoryJump(moveBy, true)
}
}else if(!selected){
this.state.locked = true this.state.locked = true
if(this.state.screen === "difficulty"){ if(this.state.screen === "difficulty"){
this.toSongSelect(true) this.toSongSelect(true)
@ -2174,6 +2203,16 @@ class SongSelect{
} }
} }
} }
oncatjump(response){
if(response && response.value){
if("song" in response.value){
var song = +response.value.song
if(song >= 0 && song < this.songs.length){
this.state.locked = true
}
}
}
}
startP2(){ startP2(){
this.onusers(p2.getMessage("users")) this.onusers(p2.getMessage("users"))
if(p2.session){ if(p2.session){
@ -2183,7 +2222,7 @@ class SongSelect{
if(response.type == "users"){ if(response.type == "users"){
this.onusers(response) this.onusers(response)
} }
if(p2.session && response.type == "songsel"){ if(p2.session && (response.type == "songsel" || response.type == "catjump")){
this.onsongsel(response) this.onsongsel(response)
this.state.selLock = false this.state.selLock = false
} }

View File

@ -86,6 +86,7 @@
this.maxCombo = "最大コンボ数" this.maxCombo = "最大コンボ数"
this.drumroll = "連打数" this.drumroll = "連打数"
this.errorOccured = "エラーが発生しました。再読み込みしてください。"
this.tutorial = { this.tutorial = {
basics: [ basics: [
"流れてくる音符がワクに重なったらバチで太鼓をたたこう!", "流れてくる音符がワクに重なったらバチで太鼓をたたこう!",
@ -109,7 +110,8 @@
"Gitリポジトリかメールでバグを報告してください。" "Gitリポジトリかメールでバグを報告してください。"
], ],
diagnosticWarning: "以下の端末診断情報も併せて報告してください!", diagnosticWarning: "以下の端末診断情報も併せて報告してください!",
issueTemplate: "###### 下記の問題を説明してください。 スクリーンショットと診断情報を含めてください。" issueTemplate: "###### 下記の問題を説明してください。 スクリーンショットと診断情報を含めてください。",
issues: "課題"
} }
this.session = { this.session = {
multiplayerSession: "オンラインセッション", multiplayerSession: "オンラインセッション",
@ -281,6 +283,7 @@ function StringsEn(){
this.maxCombo = "MAX Combo" this.maxCombo = "MAX Combo"
this.drumroll = "Drumroll" this.drumroll = "Drumroll"
this.errorOccured = "An error occurred, please refresh"
this.tutorial = { this.tutorial = {
basics: [ basics: [
"When a note overlaps the frame, that is your cue to hit the drum!", "When a note overlaps the frame, that is your cue to hit the drum!",
@ -304,7 +307,8 @@ function StringsEn(){
"You can report bugs either via our Git repository or email." "You can report bugs either via our Git repository or email."
], ],
diagnosticWarning: "Be sure to include the following diagnostic data!", diagnosticWarning: "Be sure to include the following diagnostic data!",
issueTemplate: "###### Describe the problem you are having below. Please include a screenshot and the diagnostic information." issueTemplate: "###### Describe the problem you are having below. Please include a screenshot and the diagnostic information.",
issues: "Issues"
} }
this.session = { this.session = {
multiplayerSession: "Multiplayer Session", multiplayerSession: "Multiplayer Session",
@ -426,10 +430,10 @@ function StringsCn(){
this.hard = "困难" this.hard = "困难"
this.oni = "魔王" this.oni = "魔王"
this.songBranch = "有谱面分歧" this.songBranch = "有谱面分歧"
this.sessionStart = "开始在线会话!" this.sessionStart = "开始在线会话"
this.sessionEnd = "结束在线会话" this.sessionEnd = "结束在线会话"
this.loading = "加载中..." this.loading = "加载中..."
this.waitingForP2 = "Waiting for Another Player..." this.waitingForP2 = "正在等待对方玩家..."
this.cancel = "取消" this.cancel = "取消"
this.note = { this.note = {
don: "咚", don: "咚",
@ -476,6 +480,7 @@ function StringsCn(){
this.maxCombo = "最多连段数" this.maxCombo = "最多连段数"
this.drumroll = "连打数" this.drumroll = "连打数"
this.errorOccured = "An error occurred, please refresh"
this.tutorial = { this.tutorial = {
basics: [ basics: [
"当流动的音符将与框框重叠时就用鼓棒敲打太鼓吧", "当流动的音符将与框框重叠时就用鼓棒敲打太鼓吧",
@ -499,11 +504,12 @@ function StringsCn(){
"You can report bugs either via our Git repository or email." "You can report bugs either via our Git repository or email."
], ],
diagnosticWarning: "Be sure to include the following diagnostic data!", diagnosticWarning: "Be sure to include the following diagnostic data!",
issueTemplate: "###### Describe the problem you are having below. Please include a screenshot and the diagnostic information." issueTemplate: "###### Describe the problem you are having below. Please include a screenshot and the diagnostic information.",
issues: "工单"
} }
this.session = { this.session = {
multiplayerSession: "Multiplayer Session", multiplayerSession: "在线会话",
linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.", linkTutorial: "复制下方地址,给你的朋友即可开始一起游戏!当他们与您联系之前,请不要离开此页面。",
cancel: "取消" cancel: "取消"
} }
this.settings = { this.settings = {
@ -624,7 +630,7 @@ function StringsTw(){
this.sessionStart = "開始多人模式!" this.sessionStart = "開始多人模式!"
this.sessionEnd = "結束多人模式" this.sessionEnd = "結束多人模式"
this.loading = "讀取中..." this.loading = "讀取中..."
this.waitingForP2 = "Waiting for Another Player..." this.waitingForP2 = "正在等待對方玩家..."
this.cancel = "取消" this.cancel = "取消"
this.note = { this.note = {
don: "咚", don: "咚",
@ -671,6 +677,7 @@ function StringsTw(){
this.maxCombo = "最多連段數" this.maxCombo = "最多連段數"
this.drumroll = "連打數" this.drumroll = "連打數"
this.errorOccured = "An error occurred, please refresh"
this.tutorial = { this.tutorial = {
basics: [ basics: [
"當流動的音符將與框框重疊時就用鼓棒敲打太鼓吧", "當流動的音符將與框框重疊時就用鼓棒敲打太鼓吧",
@ -694,11 +701,12 @@ function StringsTw(){
"You can report bugs either via our Git repository or email." "You can report bugs either via our Git repository or email."
], ],
diagnosticWarning: "Be sure to include the following diagnostic data!", diagnosticWarning: "Be sure to include the following diagnostic data!",
issueTemplate: "###### Describe the problem you are having below. Please include a screenshot and the diagnostic information." issueTemplate: "###### Describe the problem you are having below. Please include a screenshot and the diagnostic information.",
issues: "問題"
} }
this.session = { this.session = {
multiplayerSession: "Multiplayer Session", multiplayerSession: "多人模式",
linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.", linkTutorial: "複製下方地址,給你的朋友即可開始一起遊戲!當他們與您聯繫之前,請不要離開此頁面。",
cancel: "取消" cancel: "取消"
} }
this.settings = { this.settings = {
@ -866,6 +874,7 @@ function StringsKo(){
this.maxCombo = "최대 콤보 수" this.maxCombo = "최대 콤보 수"
this.drumroll = "연타 횟수" this.drumroll = "연타 횟수"
this.errorOccured = "An error occurred, please refresh"
this.tutorial = { this.tutorial = {
basics: [ basics: [
"이동하는 음표가 테두리와 겹쳐졌을 때 북채로 태고를 두드리자!", "이동하는 음표가 테두리와 겹쳐졌을 때 북채로 태고를 두드리자!",
@ -889,7 +898,8 @@ function StringsKo(){
"You can report bugs either via our Git repository or email." "You can report bugs either via our Git repository or email."
], ],
diagnosticWarning: "Be sure to include the following diagnostic data!", diagnosticWarning: "Be sure to include the following diagnostic data!",
issueTemplate: "###### Describe the problem you are having below. Please include a screenshot and the diagnostic information." issueTemplate: "###### Describe the problem you are having below. Please include a screenshot and the diagnostic information.",
issues: "이슈"
} }
this.session = { this.session = {
multiplayerSession: "Multiplayer Session", multiplayerSession: "Multiplayer Session",

View File

@ -348,7 +348,7 @@
} }
var score = this.controller.getGlobalScore() var score = this.controller.getGlobalScore()
var gaugePercent = Math.round(score.gauge / 200) / 50 var gaugePercent = this.rules.gaugePercent(score.gauge)
if(this.multiplayer === 2){ if(this.multiplayer === 2){
var scoreImg = "bg_score_p2" var scoreImg = "bg_score_p2"

View File

@ -4,8 +4,8 @@
<div class="view-content"></div> <div class="view-content"></div>
<div id="diag-txt"></div> <div id="diag-txt"></div>
<div class="left-buttons"> <div class="left-buttons">
<div id="link-issues" class="taibtn stroke-sub link-btn" alt="Issues"> <div id="link-issues" class="taibtn stroke-sub link-btn">
<a target="_blank">Issues</a> <a target="_blank"></a>
</div> </div>
<div id="link-email" class="taibtn stroke-sub link-btn"> <div id="link-email" class="taibtn stroke-sub link-btn">
<a></a> <a></a>

View File

@ -24,7 +24,7 @@
<div class="music-volume input-slider"> <div class="music-volume input-slider">
<span class="reset">x</span><input type="text" value="" readonly><span class="minus">-</span><span class="plus">+</span> <span class="reset">x</span><input type="text" value="" readonly><span class="minus">-</span><span class="plus">+</span>
</div> </div>
<label><input class="change-restart" type="checkbox">Restart on change</label> <label class="change-restart-label"><input class="change-restart" type="checkbox">Restart on change</label>
<label class="autoplay-label"><input class="autoplay" type="checkbox">Auto play</label> <label class="autoplay-label"><input class="autoplay" type="checkbox">Auto play</label>
<div class="bottom-btns"> <div class="bottom-btns">
<div class="restart-btn">Restart song</div> <div class="restart-btn">Restart song</div>

View File

@ -262,7 +262,7 @@ async def connection(ws, path):
elif action == "songsel": elif action == "songsel":
# Session song selection # Session song selection
if "other_user" in user and "ws" in user["other_user"]: if "other_user" in user and "ws" in user["other_user"]:
if type == "songsel": if type == "songsel" or type == "catjump":
# Change song select position # Change song select position
if user["other_user"]["action"] == "songsel": if user["other_user"]["action"] == "songsel":
sent_msg = msgobj(type, value) sent_msg = msgobj(type, value)
@ -336,7 +336,26 @@ async def connection(ws, path):
port = int(sys.argv[1]) if len(sys.argv) > 1 else 34802 port = int(sys.argv[1]) if len(sys.argv) > 1 else 34802
print('Starting server on port %d' % port) print('Starting server on port %d' % port)
asyncio.get_event_loop().run_until_complete( loop = asyncio.get_event_loop()
tasks = asyncio.gather(
websockets.serve(connection, "localhost", port) websockets.serve(connection, "localhost", port)
) )
asyncio.get_event_loop().run_forever() try:
loop.run_until_complete(tasks)
loop.run_forever()
except KeyboardInterrupt:
print("Stopping server")
def shutdown_exception_handler(loop, context):
if "exception" not in context or not isinstance(context["exception"], asyncio.CancelledError):
loop.default_exception_handler(context)
loop.set_exception_handler(shutdown_exception_handler)
tasks = asyncio.gather(*asyncio.all_tasks(loop=loop), loop=loop, return_exceptions=True)
tasks.add_done_callback(lambda t: loop.stop())
tasks.cancel()
while not tasks.done() and not loop.is_closed():
loop.run_forever()
finally:
if hasattr(loop, "shutdown_asyncgens"):
loop.run_until_complete(loop.shutdown_asyncgens())
loop.close()