Commit f0e2f05f authored by nanahira's avatar nanahira

Merge branch 'master' of github.com:bui/taiko-web

parents 599c1e83 a899fd5c
...@@ -17,6 +17,7 @@ def get_db(): ...@@ -17,6 +17,7 @@ def get_db():
db = getattr(g, '_database', None) db = getattr(g, '_database', None)
if db is None: if db is None:
db = g._database = sqlite3.connect(DATABASE) db = g._database = sqlite3.connect(DATABASE)
db.row_factory = sqlite3.Row
return db return db
...@@ -110,8 +111,8 @@ def route_api_preview(): ...@@ -110,8 +111,8 @@ def route_api_preview():
if not song_row: if not song_row:
abort(400) abort(400)
song_type = song_row[0][12] song_type = song_row[0]['type']
prev_path = make_preview(song_id, song_type, song_row[0][15]) prev_path = make_preview(song_id, song_type, song_row[0]['preview'])
if not prev_path: if not prev_path:
return redirect(get_config()['songs_baseurl'] + '%s/main.ogg' % song_id) return redirect(get_config()['songs_baseurl'] + '%s/main.ogg' % song_id)
...@@ -126,43 +127,44 @@ def route_api_songs(): ...@@ -126,43 +127,44 @@ def route_api_songs():
raw_categories = query_db('select * from categories') raw_categories = query_db('select * from categories')
categories = {} categories = {}
for cat in raw_categories: for cat in raw_categories:
categories[cat[0]] = cat[1] categories[cat['id']] = cat['title']
raw_song_skins = query_db('select * from song_skins') raw_song_skins = query_db('select * from song_skins')
song_skins = {} song_skins = {}
for skin in raw_song_skins: for skin in raw_song_skins:
song_skins[skin[0]] = {'name': skin[1], 'song': skin[2], 'stage': skin[3], 'don': skin[4]} song_skins[skin[0]] = {'name': skin['name'], 'song': skin['song'], 'stage': skin['stage'], 'don': skin['don']}
songs_out = [] songs_out = []
for song in songs: for song in songs:
song_id = song[0] song_id = song['id']
song_type = song[12] song_type = song['type']
preview = song[15] preview = song['preview']
category_out = categories[song[11]] if song[11] in categories else "" category_out = categories[song['category']] if song['category'] in categories else ''
song_skin_out = song_skins[song[14]] if song[14] in song_skins else None song_skin_out = song_skins[song['skin_id']] if song['skin_id'] in song_skins else None
maker = None maker = None
if song[17] == 0: if song['maker_id'] == 0:
maker = 0 maker = 0
elif song[17] and song[17] > 0: elif song['maker_id'] and song['maker_id'] > 0:
maker = {'name': song[18], 'url': song[19], 'id': song[17]} maker = {'name': song['name'], 'url': song['url'], 'id': song['maker_id']}
songs_out.append({ songs_out.append({
'id': song_id, 'id': song_id,
'title': song[1], 'title': song['title'],
'title_lang': song[2], 'title_lang': song['title_lang'],
'subtitle': song[3], 'subtitle': song['subtitle'],
'subtitle_lang': song[4], 'subtitle_lang': song['subtitle_lang'],
'stars': [ 'stars': [
song[5], song[6], song[7], song[8], song[9] song['easy'], song['normal'], song['hard'], song['oni'], song['ura']
], ],
'preview': preview, 'preview': preview,
'category': category_out, 'category': category_out,
'type': song_type, 'type': song_type,
'offset': song[13], 'offset': song['offset'],
'song_skin': song_skin_out, 'song_skin': song_skin_out,
'volume': song[16], 'volume': song['volume'],
'maker': maker 'maker': maker,
'hash': song['hash']
}) })
return jsonify(songs_out) return jsonify(songs_out)
......
@font-face{
font-family: TnT;
src: url("TnT.ttf") format("truetype");
}
@font-face{
font-family: Kozuka;
src: url("Kozuka.otf") format("truetype");
}
\ No newline at end of file
...@@ -31,6 +31,8 @@ ...@@ -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)
......
var assets = { var assets = {
"js": [ "js": [
"lib/fontdetect.min.js", "lib/md5.min.js",
"loadsong.js", "loadsong.js",
"parseosu.js", "parseosu.js",
"titlescreen.js", "titlescreen.js",
...@@ -30,7 +30,8 @@ var assets = { ...@@ -30,7 +30,8 @@ var assets = {
"session.js", "session.js",
"importsongs.js", "importsongs.js",
"logo.js", "logo.js",
"settings.js" "settings.js",
"scorestorage.js"
], ],
"css": [ "css": [
"main.css", "main.css",
...@@ -42,7 +43,6 @@ var assets = { ...@@ -42,7 +43,6 @@ var assets = {
"view.css" "view.css"
], ],
"assetsCss": [ "assetsCss": [
"fonts/fonts.css",
"img/img.css" "img/img.css"
], ],
"img": [ "img": [
...@@ -131,10 +131,10 @@ var assets = { ...@@ -131,10 +131,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",
......
...@@ -47,6 +47,9 @@ function browserSupport(){ ...@@ -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 = []
......
...@@ -4,6 +4,7 @@ class CanvasCache{ ...@@ -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{ ...@@ -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{ ...@@ -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{ ...@@ -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
......
...@@ -1273,27 +1273,29 @@ ...@@ -1273,27 +1273,29 @@
ctx.translate(-47, -39) ctx.translate(-47, -39)
ctx.miterLimit = 1.7 ctx.miterLimit = 1.7
if(!this.crownCache.w){ if(config.whiteOutline){
this.crownCache.resize(140, 140, config.ratio) if(!this.crownCache.w){
this.crownCache.resize(140, 140, config.ratio)
}
var offset = 140 / 2 - 94 / 2
this.crownCache.get({
ctx: ctx,
x: -offset,
y: -offset,
w: 140,
h: 140,
id: "crown"
}, ctx => {
ctx.save()
ctx.translate(offset, offset)
ctx.strokeStyle = "#fff"
ctx.lineWidth = 35
ctx.miterLimit = 1.7
ctx.filter = "blur(1.5px)"
ctx.stroke(this.crownPath)
ctx.restore()
})
} }
var offset = 140 / 2 - 94 / 2
this.crownCache.get({
ctx: ctx,
x: -offset,
y: -offset,
w: 140,
h: 140,
id: "crown"
}, ctx => {
ctx.save()
ctx.translate(offset, offset)
ctx.strokeStyle = "#fff"
ctx.lineWidth = 35
ctx.miterLimit = 1.7
ctx.filter = "blur(1.5px)"
ctx.stroke(this.crownPath)
ctx.restore()
})
if(config.shine){ if(config.shine){
ctx.strokeStyle = "#fff" ctx.strokeStyle = "#fff"
...@@ -1302,7 +1304,7 @@ ...@@ -1302,7 +1304,7 @@
ctx.globalAlpha = 1 - config.shine ctx.globalAlpha = 1 - config.shine
} }
ctx.strokeStyle = "#000" ctx.strokeStyle = config.type ? "#000" : "#ffc616"
ctx.lineWidth = 18 ctx.lineWidth = 18
ctx.stroke(this.crownPath) ctx.stroke(this.crownPath)
...@@ -1313,21 +1315,25 @@ ...@@ -1313,21 +1315,25 @@
ctx.globalAlpha = 1 - config.shine ctx.globalAlpha = 1 - config.shine
} }
var grd = ctx.createLinearGradient(0, 0, 94, 0) if(config.type){
if(config.type === "gold"){ var grd = ctx.createLinearGradient(0, 0, 94, 0)
grd.addColorStop(0, "#ffffc5") if(config.type === "gold"){
grd.addColorStop(0.23, "#ffff44") grd.addColorStop(0, "#ffffc5")
grd.addColorStop(0.53, "#efbd12") grd.addColorStop(0.23, "#ffff44")
grd.addColorStop(0.83, "#ffff44") grd.addColorStop(0.53, "#efbd12")
grd.addColorStop(1, "#efbd12") grd.addColorStop(0.83, "#ffff44")
}else if(config.type === "silver"){ grd.addColorStop(1, "#efbd12")
grd.addColorStop(0, "#d6efef") }else if(config.type === "silver"){
grd.addColorStop(0.23, "#bddfde") grd.addColorStop(0, "#d6efef")
grd.addColorStop(0.53, "#97c1c0") grd.addColorStop(0.23, "#bddfde")
grd.addColorStop(0.83, "#bddfde") grd.addColorStop(0.53, "#97c1c0")
grd.addColorStop(1, "#97c1c0") grd.addColorStop(0.83, "#bddfde")
} grd.addColorStop(1, "#97c1c0")
ctx.fillStyle = grd }
ctx.fillStyle = grd
}else{
ctx.fillStyle = "#ffdb2c"
}
ctx.fill(this.crownPath) ctx.fill(this.crownPath)
ctx.restore() ctx.restore()
...@@ -1347,21 +1353,30 @@ ...@@ -1347,21 +1353,30 @@
var secondTop = config.multiplayer ? 0 : 8 var secondTop = config.multiplayer ? 0 : 8
config.percentage = Math.max(0, Math.min(1, config.percentage)) config.percentage = Math.max(0, Math.min(1, config.percentage))
var cleared = config.percentage - 1 / 50 >= config.clear var cleared = config.percentage >= config.clear
var gaugeW = 14 * 50 var gaugeW = 14 * 50
var gaugeClear = gaugeW * config.clear var gaugeClear = gaugeW * (config.clear - 1 / 50)
var gaugeFilled = gaugeW * config.percentage var gaugeFilled = gaugeW * config.percentage
ctx.fillStyle = "#000" ctx.fillStyle = "#000"
ctx.beginPath() ctx.beginPath()
if(config.scoresheet){ if(config.scoresheet){
ctx.moveTo(-4, 26) if(config.multiplayer){
ctx.lineTo(gaugeClear - 4, 26) ctx.moveTo(-4, -4)
this.roundedCorner(ctx, gaugeClear - 4, 4, 13, 0) ctx.lineTo(760, -4)
this.roundedCorner(ctx, 760, 4, 13, 1) this.roundedCorner(ctx, 760, 48, 13, 2)
ctx.lineTo(760, 56) this.roundedCorner(ctx, gaugeClear - 4, 48, 13, 3)
ctx.lineTo(-4, 56) ctx.lineTo(gaugeClear - 4, 26)
ctx.lineTo(-4, 26)
}else{
ctx.moveTo(-4, 26)
ctx.lineTo(gaugeClear - 4, 26)
this.roundedCorner(ctx, gaugeClear - 4, 4, 13, 0)
this.roundedCorner(ctx, 760, 4, 13, 1)
ctx.lineTo(760, 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)
...@@ -1375,42 +1390,44 @@ ...@@ -1375,42 +1390,44 @@
} }
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(gaugeFilled < gaugeW - 4){ if(gaugeClear < gaugeW){
ctx.fillStyle = "#684900" if(gaugeFilled < gaugeW - 4){
var x = Math.max(gaugeClear + 9, gaugeFilled - gaugeClear + 9) ctx.fillStyle = "#684900"
ctx.fillRect(x, secondTop, gaugeW - 4 - x, 44) var x = Math.max(gaugeClear + 9, gaugeFilled - gaugeClear + 9)
} ctx.fillRect(x, secondTop, gaugeW - 4 - x, 44)
if(gaugeFilled > gaugeClear + 14){ }
var w = Math.min(gaugeW - 4, gaugeFilled - gaugeClear - 14) if(gaugeFilled > gaugeClear + 14){
ctx.fillStyle = "#ff0" var w = Math.min(gaugeW - 4, gaugeFilled - gaugeClear - 14)
ctx.fillRect(gaugeClear + 9, secondTop + 2, w, 42) ctx.fillStyle = "#ff0"
ctx.fillStyle = "#fff" ctx.fillRect(gaugeClear + 9, secondTop + 2, w, 42)
ctx.fillRect(gaugeClear + 9, secondTop, w, 3) ctx.fillStyle = "#fff"
} ctx.fillRect(gaugeClear + 9, secondTop, w, 3)
ctx.fillStyle = cleared ? "#ff0" : "#684900" }
ctx.beginPath() ctx.fillStyle = cleared ? "#ff0" : "#684900"
if(config.multiplayer){ ctx.beginPath()
this.roundedCorner(ctx, gaugeClear, secondTop + 44, 10, 3) if(config.multiplayer){
ctx.lineTo(gaugeClear, secondTop) this.roundedCorner(ctx, gaugeClear, secondTop + 44, 10, 3)
ctx.lineTo(gaugeClear + 10, secondTop) ctx.lineTo(gaugeClear, secondTop)
}else{ ctx.lineTo(gaugeClear + 10, secondTop)
ctx.moveTo(gaugeClear, secondTop + 44) }else{
this.roundedCorner(ctx, gaugeClear, secondTop, 10, 0) ctx.moveTo(gaugeClear, secondTop + 44)
ctx.lineTo(gaugeClear + 10, secondTop + 44) this.roundedCorner(ctx, gaugeClear, secondTop, 10, 0)
ctx.lineTo(gaugeClear + 10, secondTop + 44)
}
ctx.fill()
} }
ctx.fill()
if(cleared){ if(cleared){
ctx.save() ctx.save()
ctx.clip() ctx.clip()
...@@ -1424,7 +1441,7 @@ ...@@ -1424,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
...@@ -1433,18 +1450,20 @@ ...@@ -1433,18 +1450,20 @@
ctx.lineTo(x, x < gaugeClear ? firstTop + 22 : secondTop + 44) ctx.lineTo(x, x < gaugeClear ? firstTop + 22 : secondTop + 44)
} }
ctx.stroke() ctx.stroke()
this.layeredText({ if(config.clear < 47 / 50){
ctx: ctx, this.layeredText({
text: strings.clear, ctx: ctx,
fontSize: 18, text: strings.clear,
fontFamily: config.font, fontSize: 18,
x: gaugeClear + 3, fontFamily: config.font,
y: config.multiplayer ? 22 : 11, x: gaugeClear + 3,
letterSpacing: -2 y: config.multiplayer ? 22 : 11,
}, [ letterSpacing: -2
{scale: [1.1, 1.01], outline: "#000", letterBorder: 6}, }, [
{scale: [1.11, 1], fill: cleared ? "#fff" : "#737373"} {scale: [1.1, 1.01], outline: "#000", letterBorder: 6},
]) {scale: [1.11, 1], fill: cleared ? "#fff" : "#737373"}
])
}
ctx.restore() ctx.restore()
} }
......
...@@ -4,6 +4,7 @@ class Controller{ ...@@ -4,6 +4,7 @@ class Controller{
this.songData = songData this.songData = songData
this.autoPlayEnabled = autoPlayEnabled this.autoPlayEnabled = autoPlayEnabled
this.mods = selectedSong.mods; this.mods = selectedSong.mods;
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 : ""
...@@ -177,7 +178,7 @@ class Controller{ ...@@ -177,7 +178,7 @@ class Controller{
gameEnded(){ gameEnded(){
var score = this.getGlobalScore() var score = this.getGlobalScore()
var vp var vp
if(Math.round(score.gauge / 2) - 1 >= 25){ if(this.game.rules.clearReached(score.gauge)){
if(score.bad === 0){ if(score.bad === 0){
vp = "fullcombo" vp = "fullcombo"
this.playSound("v_fullcombo", 1.350) this.playSound("v_fullcombo", 1.350)
......
...@@ -17,6 +17,7 @@ class Debug{ ...@@ -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{ ...@@ -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{ ...@@ -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{ ...@@ -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{ ...@@ -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{ ...@@ -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{ ...@@ -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{ ...@@ -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{ ...@@ -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{ ...@@ -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
} }
} }
...@@ -18,10 +18,11 @@ class Game{ ...@@ -18,10 +18,11 @@ class Game{
title: selectedSong.title, title: selectedSong.title,
difficulty: this.rules.difficulty difficulty: this.rules.difficulty
} }
this.HPGain = 100 / this.songData.circles.filter(circle => { var combo = this.songData.circles.filter(circle => {
var type = circle.type var type = circle.type
return (type === "don" || type === "ka" || type === "daiDon" || type === "daiKa") && (!circle.branch || circle.branch.active) return (type === "don" || type === "ka" || type === "daiDon" || type === "daiKa") && (!circle.branch || circle.branch.active)
}).length }).length
this.soulPoints = this.rules.soulPoints(combo)
this.paused = false this.paused = false
this.started = false this.started = false
this.mainMusicPlaying = false this.mainMusicPlaying = false
...@@ -639,12 +640,15 @@ class Game{ ...@@ -639,12 +640,15 @@ class Game{
switch(score){ switch(score){
case 450: case 450:
this.globalScore.good++ this.globalScore.good++
this.globalScore.gauge += this.soulPoints.good
break break
case 230: case 230:
this.globalScore.ok++ this.globalScore.ok++
this.globalScore.gauge += this.soulPoints.ok
break break
case 0: case 0:
this.globalScore.bad++ this.globalScore.bad++
this.globalScore.gauge += this.soulPoints.bad
break break
} }
if (this.songData.scoremode) { if (this.songData.scoremode) {
...@@ -658,12 +662,10 @@ class Game{ ...@@ -658,12 +662,10 @@ class Game{
} }
} }
// Gauge update // Gauge update
if(score !== 0){ if(this.globalScore.gauge < 0){
this.globalScore.gauge += this.HPGain
}else if(this.globalScore.gauge - this.HPGain > 0){
this.globalScore.gauge -= this.HPGain
}else{
this.globalScore.gauge = 0 this.globalScore.gauge = 0
}else if(this.globalScore.gauge > 10000){
this.globalScore.gauge = 10000
} }
// Points update // Points update
if (this.songData.scoremode == 2) { if (this.songData.scoremode == 2) {
...@@ -730,10 +732,6 @@ class Game{ ...@@ -730,10 +732,6 @@ class Game{
this.currentCircle = closestCircle this.currentCircle = closestCircle
} }
} }
this.HPGain = 100 / this.songData.circles.filter(circle => {
var type = circle.type
return (type === "don" || type === "ka" || type === "daiDon" || type === "daiKa") && (!circle.branch || circle.branch.active)
}).length
if(this.controller.multiplayer === 1){ if(this.controller.multiplayer === 1){
p2.send("branch", activeName) p2.send("branch", activeName)
} }
...@@ -813,7 +811,7 @@ class Game{ ...@@ -813,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){
......
...@@ -13,12 +13,62 @@ class GameRules{ ...@@ -13,12 +13,62 @@ 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
break break
} }
switch(this.difficulty){
case "easy":
this.gaugeClear = 30 / 50
break
case "normal":
case "hard":
this.gaugeClear = 35 / 50
break
case "oni":
case "ura":
this.gaugeClear = 40 / 50
break
default:
this.gaugeClear = 51 / 50
break
}
this.daiLeniency = 2 * frame this.daiLeniency = 2 * frame
} }
soulPoints(combo){
var good, ok, bad
switch(this.difficulty){
case "easy":
good = Math.floor(10000 / combo * 1.575)
ok = Math.floor(good * 0.75)
bad = Math.ceil(good * -2)
break
case "normal":
good = Math.floor(10000 / combo / 0.7)
ok = Math.floor(good * 0.75)
bad = Math.ceil(good / -0.75)
break
case "hard":
good = Math.floor(10000 / combo * 1.5)
ok = Math.floor(good * 0.75)
bad = Math.ceil(good / -0.8)
break
case "oni":
case "ura":
good = Math.floor(10000 / combo / 0.7)
ok = Math.floor(good * 0.5)
bad = Math.ceil(good * -1.6)
break
}
return {good: good, ok: ok, bad: bad}
}
gaugePercent(gauge){
return Math.floor(gauge / 200) / 50
}
clearReached(gauge){
return this.gaugePercent(gauge) >= this.gaugeClear
}
} }
...@@ -269,11 +269,18 @@ ...@@ -269,11 +269,18 @@
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
} }
var hash = md5.base64(event.target.result).slice(0, -2)
songObj.hash = hash
scoreStorage.songTitles[songObj.title] = hash
var score = scoreStorage.get(hash, false, true)
if(score){
score.title = songObj.title
}
}).catch(() => {}) }).catch(() => {})
reader.readAsText(file, "sjis") reader.readAsText(file, "sjis")
return promise return promise
...@@ -300,7 +307,7 @@ ...@@ -300,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(/\[.+?\]$/)
...@@ -313,7 +320,14 @@ ...@@ -313,7 +320,14 @@
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)
songObj.hash = hash
scoreStorage.songTitles[songObj.title] = hash
var score = scoreStorage.get(hash, false, true)
if(score){
score.title = songObj.title
}
}).catch(() => {}) }).catch(() => {})
reader.readAsText(file) reader.readAsText(file)
return promise return promise
...@@ -380,12 +394,21 @@ ...@@ -380,12 +394,21 @@
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--){
for(var cat in this.categories){ var hasTitle = false
if(path[i].indexOf(cat) !== -1){ for(var j in exclude){
return this.categories[cat] if(path[i].indexOf(exclude[j].toLowerCase()) !== -1){
hasTitle = true
break
}
}
if(!hasTitle){
for(var cat in this.categories){
if(path[i].indexOf(cat) !== -1){
return this.categories[cat]
}
} }
} }
} }
......
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}}}();
/**
* [js-md5]{@link https://github.com/emn178/js-md5}
*
* @namespace md5
* @version 0.7.3
* @author Chen, Yi-Cyuan [emn178@gmail.com]
* @copyright Chen, Yi-Cyuan 2014-2017
* @license MIT
*/
!function(){"use strict";function t(t){if(t)d[0]=d[16]=d[1]=d[2]=d[3]=d[4]=d[5]=d[6]=d[7]=d[8]=d[9]=d[10]=d[11]=d[12]=d[13]=d[14]=d[15]=0,this.blocks=d,this.buffer8=l;else if(a){var r=new ArrayBuffer(68);this.buffer8=new Uint8Array(r),this.blocks=new Uint32Array(r)}else this.blocks=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];this.h0=this.h1=this.h2=this.h3=this.start=this.bytes=this.hBytes=0,this.finalized=this.hashed=!1,this.first=!0}var r="input is invalid type",e="object"==typeof window,i=e?window:{};i.JS_MD5_NO_WINDOW&&(e=!1);var s=!e&&"object"==typeof self,h=!i.JS_MD5_NO_NODE_JS&&"object"==typeof process&&process.versions&&process.versions.node;h?i=global:s&&(i=self);var f=!i.JS_MD5_NO_COMMON_JS&&"object"==typeof module&&module.exports,o="function"==typeof define&&define.amd,a=!i.JS_MD5_NO_ARRAY_BUFFER&&"undefined"!=typeof ArrayBuffer,n="0123456789abcdef".split(""),u=[128,32768,8388608,-2147483648],y=[0,8,16,24],c=["hex","array","digest","buffer","arrayBuffer","base64"],p="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split(""),d=[],l;if(a){var A=new ArrayBuffer(68);l=new Uint8Array(A),d=new Uint32Array(A)}!i.JS_MD5_NO_NODE_JS&&Array.isArray||(Array.isArray=function(t){return"[object Array]"===Object.prototype.toString.call(t)}),!a||!i.JS_MD5_NO_ARRAY_BUFFER_IS_VIEW&&ArrayBuffer.isView||(ArrayBuffer.isView=function(t){return"object"==typeof t&&t.buffer&&t.buffer.constructor===ArrayBuffer});var b=function(r){return function(e){return new t(!0).update(e)[r]()}},v=function(){var r=b("hex");h&&(r=w(r)),r.create=function(){return new t},r.update=function(t){return r.create().update(t)};for(var e=0;e<c.length;++e){var i=c[e];r[i]=b(i)}return r},w=function(t){var e=eval("require('crypto')"),i=eval("require('buffer').Buffer"),s=function(s){if("string"==typeof s)return e.createHash("md5").update(s,"utf8").digest("hex");if(null===s||void 0===s)throw r;return s.constructor===ArrayBuffer&&(s=new Uint8Array(s)),Array.isArray(s)||ArrayBuffer.isView(s)||s.constructor===i?e.createHash("md5").update(new i(s)).digest("hex"):t(s)};return s};t.prototype.update=function(t){if(!this.finalized){var e,i=typeof t;if("string"!==i){if("object"!==i)throw r;if(null===t)throw r;if(a&&t.constructor===ArrayBuffer)t=new Uint8Array(t);else if(!(Array.isArray(t)||a&&ArrayBuffer.isView(t)))throw r;e=!0}for(var s,h,f=0,o=t.length,n=this.blocks,u=this.buffer8;f<o;){if(this.hashed&&(this.hashed=!1,n[0]=n[16],n[16]=n[1]=n[2]=n[3]=n[4]=n[5]=n[6]=n[7]=n[8]=n[9]=n[10]=n[11]=n[12]=n[13]=n[14]=n[15]=0),e)if(a)for(h=this.start;f<o&&h<64;++f)u[h++]=t[f];else for(h=this.start;f<o&&h<64;++f)n[h>>2]|=t[f]<<y[3&h++];else if(a)for(h=this.start;f<o&&h<64;++f)(s=t.charCodeAt(f))<128?u[h++]=s:s<2048?(u[h++]=192|s>>6,u[h++]=128|63&s):s<55296||s>=57344?(u[h++]=224|s>>12,u[h++]=128|s>>6&63,u[h++]=128|63&s):(s=65536+((1023&s)<<10|1023&t.charCodeAt(++f)),u[h++]=240|s>>18,u[h++]=128|s>>12&63,u[h++]=128|s>>6&63,u[h++]=128|63&s);else for(h=this.start;f<o&&h<64;++f)(s=t.charCodeAt(f))<128?n[h>>2]|=s<<y[3&h++]:s<2048?(n[h>>2]|=(192|s>>6)<<y[3&h++],n[h>>2]|=(128|63&s)<<y[3&h++]):s<55296||s>=57344?(n[h>>2]|=(224|s>>12)<<y[3&h++],n[h>>2]|=(128|s>>6&63)<<y[3&h++],n[h>>2]|=(128|63&s)<<y[3&h++]):(s=65536+((1023&s)<<10|1023&t.charCodeAt(++f)),n[h>>2]|=(240|s>>18)<<y[3&h++],n[h>>2]|=(128|s>>12&63)<<y[3&h++],n[h>>2]|=(128|s>>6&63)<<y[3&h++],n[h>>2]|=(128|63&s)<<y[3&h++]);this.lastByteIndex=h,this.bytes+=h-this.start,h>=64?(this.start=h-64,this.hash(),this.hashed=!0):this.start=h}return this.bytes>4294967295&&(this.hBytes+=this.bytes/4294967296<<0,this.bytes=this.bytes%4294967296),this}},t.prototype.finalize=function(){if(!this.finalized){this.finalized=!0;var t=this.blocks,r=this.lastByteIndex;t[r>>2]|=u[3&r],r>=56&&(this.hashed||this.hash(),t[0]=t[16],t[16]=t[1]=t[2]=t[3]=t[4]=t[5]=t[6]=t[7]=t[8]=t[9]=t[10]=t[11]=t[12]=t[13]=t[14]=t[15]=0),t[14]=this.bytes<<3,t[15]=this.hBytes<<3|this.bytes>>>29,this.hash()}},t.prototype.hash=function(){var t,r,e,i,s,h,f=this.blocks;this.first?r=((r=((t=((t=f[0]-680876937)<<7|t>>>25)-271733879<<0)^(e=((e=(-271733879^(i=((i=(-1732584194^2004318071&t)+f[1]-117830708)<<12|i>>>20)+t<<0)&(-271733879^t))+f[2]-1126478375)<<17|e>>>15)+i<<0)&(i^t))+f[3]-1316259209)<<22|r>>>10)+e<<0:(t=this.h0,r=this.h1,e=this.h2,r=((r+=((t=((t+=((i=this.h3)^r&(e^i))+f[0]-680876936)<<7|t>>>25)+r<<0)^(e=((e+=(r^(i=((i+=(e^t&(r^e))+f[1]-389564586)<<12|i>>>20)+t<<0)&(t^r))+f[2]+606105819)<<17|e>>>15)+i<<0)&(i^t))+f[3]-1044525330)<<22|r>>>10)+e<<0),r=((r+=((t=((t+=(i^r&(e^i))+f[4]-176418897)<<7|t>>>25)+r<<0)^(e=((e+=(r^(i=((i+=(e^t&(r^e))+f[5]+1200080426)<<12|i>>>20)+t<<0)&(t^r))+f[6]-1473231341)<<17|e>>>15)+i<<0)&(i^t))+f[7]-45705983)<<22|r>>>10)+e<<0,r=((r+=((t=((t+=(i^r&(e^i))+f[8]+1770035416)<<7|t>>>25)+r<<0)^(e=((e+=(r^(i=((i+=(e^t&(r^e))+f[9]-1958414417)<<12|i>>>20)+t<<0)&(t^r))+f[10]-42063)<<17|e>>>15)+i<<0)&(i^t))+f[11]-1990404162)<<22|r>>>10)+e<<0,r=((r+=((t=((t+=(i^r&(e^i))+f[12]+1804603682)<<7|t>>>25)+r<<0)^(e=((e+=(r^(i=((i+=(e^t&(r^e))+f[13]-40341101)<<12|i>>>20)+t<<0)&(t^r))+f[14]-1502002290)<<17|e>>>15)+i<<0)&(i^t))+f[15]+1236535329)<<22|r>>>10)+e<<0,r=((r+=((i=((i+=(r^e&((t=((t+=(e^i&(r^e))+f[1]-165796510)<<5|t>>>27)+r<<0)^r))+f[6]-1069501632)<<9|i>>>23)+t<<0)^t&((e=((e+=(t^r&(i^t))+f[11]+643717713)<<14|e>>>18)+i<<0)^i))+f[0]-373897302)<<20|r>>>12)+e<<0,r=((r+=((i=((i+=(r^e&((t=((t+=(e^i&(r^e))+f[5]-701558691)<<5|t>>>27)+r<<0)^r))+f[10]+38016083)<<9|i>>>23)+t<<0)^t&((e=((e+=(t^r&(i^t))+f[15]-660478335)<<14|e>>>18)+i<<0)^i))+f[4]-405537848)<<20|r>>>12)+e<<0,r=((r+=((i=((i+=(r^e&((t=((t+=(e^i&(r^e))+f[9]+568446438)<<5|t>>>27)+r<<0)^r))+f[14]-1019803690)<<9|i>>>23)+t<<0)^t&((e=((e+=(t^r&(i^t))+f[3]-187363961)<<14|e>>>18)+i<<0)^i))+f[8]+1163531501)<<20|r>>>12)+e<<0,r=((r+=((i=((i+=(r^e&((t=((t+=(e^i&(r^e))+f[13]-1444681467)<<5|t>>>27)+r<<0)^r))+f[2]-51403784)<<9|i>>>23)+t<<0)^t&((e=((e+=(t^r&(i^t))+f[7]+1735328473)<<14|e>>>18)+i<<0)^i))+f[12]-1926607734)<<20|r>>>12)+e<<0,r=((r+=((h=(i=((i+=((s=r^e)^(t=((t+=(s^i)+f[5]-378558)<<4|t>>>28)+r<<0))+f[8]-2022574463)<<11|i>>>21)+t<<0)^t)^(e=((e+=(h^r)+f[11]+1839030562)<<16|e>>>16)+i<<0))+f[14]-35309556)<<23|r>>>9)+e<<0,r=((r+=((h=(i=((i+=((s=r^e)^(t=((t+=(s^i)+f[1]-1530992060)<<4|t>>>28)+r<<0))+f[4]+1272893353)<<11|i>>>21)+t<<0)^t)^(e=((e+=(h^r)+f[7]-155497632)<<16|e>>>16)+i<<0))+f[10]-1094730640)<<23|r>>>9)+e<<0,r=((r+=((h=(i=((i+=((s=r^e)^(t=((t+=(s^i)+f[13]+681279174)<<4|t>>>28)+r<<0))+f[0]-358537222)<<11|i>>>21)+t<<0)^t)^(e=((e+=(h^r)+f[3]-722521979)<<16|e>>>16)+i<<0))+f[6]+76029189)<<23|r>>>9)+e<<0,r=((r+=((h=(i=((i+=((s=r^e)^(t=((t+=(s^i)+f[9]-640364487)<<4|t>>>28)+r<<0))+f[12]-421815835)<<11|i>>>21)+t<<0)^t)^(e=((e+=(h^r)+f[15]+530742520)<<16|e>>>16)+i<<0))+f[2]-995338651)<<23|r>>>9)+e<<0,r=((r+=((i=((i+=(r^((t=((t+=(e^(r|~i))+f[0]-198630844)<<6|t>>>26)+r<<0)|~e))+f[7]+1126891415)<<10|i>>>22)+t<<0)^((e=((e+=(t^(i|~r))+f[14]-1416354905)<<15|e>>>17)+i<<0)|~t))+f[5]-57434055)<<21|r>>>11)+e<<0,r=((r+=((i=((i+=(r^((t=((t+=(e^(r|~i))+f[12]+1700485571)<<6|t>>>26)+r<<0)|~e))+f[3]-1894986606)<<10|i>>>22)+t<<0)^((e=((e+=(t^(i|~r))+f[10]-1051523)<<15|e>>>17)+i<<0)|~t))+f[1]-2054922799)<<21|r>>>11)+e<<0,r=((r+=((i=((i+=(r^((t=((t+=(e^(r|~i))+f[8]+1873313359)<<6|t>>>26)+r<<0)|~e))+f[15]-30611744)<<10|i>>>22)+t<<0)^((e=((e+=(t^(i|~r))+f[6]-1560198380)<<15|e>>>17)+i<<0)|~t))+f[13]+1309151649)<<21|r>>>11)+e<<0,r=((r+=((i=((i+=(r^((t=((t+=(e^(r|~i))+f[4]-145523070)<<6|t>>>26)+r<<0)|~e))+f[11]-1120210379)<<10|i>>>22)+t<<0)^((e=((e+=(t^(i|~r))+f[2]+718787259)<<15|e>>>17)+i<<0)|~t))+f[9]-343485551)<<21|r>>>11)+e<<0,this.first?(this.h0=t+1732584193<<0,this.h1=r-271733879<<0,this.h2=e-1732584194<<0,this.h3=i+271733878<<0,this.first=!1):(this.h0=this.h0+t<<0,this.h1=this.h1+r<<0,this.h2=this.h2+e<<0,this.h3=this.h3+i<<0)},t.prototype.hex=function(){this.finalize();var t=this.h0,r=this.h1,e=this.h2,i=this.h3;return n[t>>4&15]+n[15&t]+n[t>>12&15]+n[t>>8&15]+n[t>>20&15]+n[t>>16&15]+n[t>>28&15]+n[t>>24&15]+n[r>>4&15]+n[15&r]+n[r>>12&15]+n[r>>8&15]+n[r>>20&15]+n[r>>16&15]+n[r>>28&15]+n[r>>24&15]+n[e>>4&15]+n[15&e]+n[e>>12&15]+n[e>>8&15]+n[e>>20&15]+n[e>>16&15]+n[e>>28&15]+n[e>>24&15]+n[i>>4&15]+n[15&i]+n[i>>12&15]+n[i>>8&15]+n[i>>20&15]+n[i>>16&15]+n[i>>28&15]+n[i>>24&15]},t.prototype.toString=t.prototype.hex,t.prototype.digest=function(){this.finalize();var t=this.h0,r=this.h1,e=this.h2,i=this.h3;return[255&t,t>>8&255,t>>16&255,t>>24&255,255&r,r>>8&255,r>>16&255,r>>24&255,255&e,e>>8&255,e>>16&255,e>>24&255,255&i,i>>8&255,i>>16&255,i>>24&255]},t.prototype.array=t.prototype.digest,t.prototype.arrayBuffer=function(){this.finalize();var t=new ArrayBuffer(16),r=new Uint32Array(t);return r[0]=this.h0,r[1]=this.h1,r[2]=this.h2,r[3]=this.h3,t},t.prototype.buffer=t.prototype.arrayBuffer,t.prototype.base64=function(){for(var t,r,e,i="",s=this.array(),h=0;h<15;)t=s[h++],r=s[h++],e=s[h++],i+=p[t>>>2]+p[63&(t<<4|r>>>4)]+p[63&(r<<2|e>>>6)]+p[63&e];return t=s[h],i+=p[t>>>2]+p[t<<4&63]+"=="};var _=v();f?module.exports=_:(i.md5=_,o&&define(function(){return _}))}();
\ No newline at end of file
...@@ -20,6 +20,7 @@ class Loader{ ...@@ -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{ ...@@ -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{ ...@@ -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{ ...@@ -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))
}) })
...@@ -206,6 +199,19 @@ class Loader{ ...@@ -206,6 +199,19 @@ class Loader{
settings = new Settings() settings = new Settings()
pageEvents.setKbd() pageEvents.setKbd()
scoreStorage = new ScoreStorage()
for(var i in assets.songsDefault){
var song = assets.songsDefault[i]
if(!song.hash){
song.hash = song.title
}
scoreStorage.songTitles[song.title] = song.hash
var score = scoreStorage.get(song.hash, false, true)
if(score){
score.title = song.title
}
}
Promise.all(this.promises).then(() => { Promise.all(this.promises).then(() => {
this.canvasTest.drawAllImages().then(result => { this.canvasTest.drawAllImages().then(result => {
perf.allImg = result perf.allImg = result
...@@ -235,12 +241,38 @@ class Loader{ ...@@ -235,12 +241,38 @@ 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)
this.error = true if(!this.error){
this.loaderPercentage.appendChild(document.createElement("br")) this.error = true
this.loaderPercentage.appendChild(document.createTextNode("An error occurred, please refresh")) this.loaderDiv.classList.add("loaderError")
this.clean() 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.createTextNode(errorOccured))
this.clean()
}
} }
assetLoaded(){ assetLoaded(){
if(!this.error){ if(!this.error){
......
...@@ -160,7 +160,7 @@ class LoadSong{ ...@@ -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(){
......
...@@ -83,6 +83,7 @@ var perf = { ...@@ -83,6 +83,7 @@ var perf = {
var strings var strings
var vectors var vectors
var settings var settings
var scoreStorage
pageEvents.add(root, ["touchstart", "touchmove", "touchend"], event => { pageEvents.add(root, ["touchstart", "touchmove", "touchend"], event => {
if(event.cancelable && cancelTouch && event.target.tagName !== "SELECT"){ if(event.cancelable && cancelTouch && event.target.tagName !== "SELECT"){
...@@ -101,7 +102,7 @@ pageEvents.add(versionDiv, ["click", "touchend"], event => { ...@@ -101,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()
......
...@@ -11,60 +11,62 @@ class PageEvents{ ...@@ -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
}) })
......
...@@ -154,6 +154,7 @@ ...@@ -154,6 +154,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];
...@@ -497,7 +498,7 @@ ...@@ -497,7 +498,7 @@
bpm: bpm, bpm: bpm,
scroll: scroll scroll: scroll
}) })
}else{ }else if(!regexSpace.test(symbol)){
error = true error = true
} }
break break
......
class Scoresheet{ class Scoresheet{
constructor(controller, results, multiplayer, touchEnabled){ constructor(controller, results, multiplayer, touchEnabled){
this.controller = controller this.controller = controller
this.resultsObj = results
this.results = {} this.results = {}
for(var i in results){ for(var i in results){
this.results[i] = results[i].toString() this.results[i] = results[i].toString()
...@@ -54,6 +55,7 @@ class Scoresheet{ ...@@ -54,6 +55,7 @@ class Scoresheet{
"ura": 4 "ura": 4
} }
this.scoreSaved = false
this.redrawRunning = true this.redrawRunning = true
this.redrawBind = this.redraw.bind(this) this.redrawBind = this.redraw.bind(this)
this.redraw() this.redraw()
...@@ -61,7 +63,8 @@ class Scoresheet{ ...@@ -61,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)
} }
...@@ -248,6 +251,9 @@ class Scoresheet{ ...@@ -248,6 +251,9 @@ class Scoresheet{
if(this.state.screen === "fadeIn" && elapsed < 1000){ if(this.state.screen === "fadeIn" && elapsed < 1000){
bgOffset = Math.min(1, this.draw.easeIn(1 - elapsed / 1000)) * (winH / 2) bgOffset = Math.min(1, this.draw.easeIn(1 - elapsed / 1000)) * (winH / 2)
} }
if((this.state.screen !== "fadeIn" || elapsed >= 1000) && !this.scoreSaved){
this.saveScore()
}
if(bgOffset){ if(bgOffset){
ctx.save() ctx.save()
...@@ -319,15 +325,19 @@ class Scoresheet{ ...@@ -319,15 +325,19 @@ class Scoresheet{
var elapsed = 0 var elapsed = 0
} }
var gaugePercent = Math.round(this.results.gauge / 2) / 50 var rules = this.controller.game.rules
var gaugePercent = rules.gaugePercent(this.results.gauge)
var gaugeClear = [rules.gaugeClear]
if(players === 2){
gaugeClear.push(this.controller.syncWith.game.rules.gaugeClear)
}
var failedOffset = gaugePercent >= gaugeClear[0] ? 0 : -2000
if(players === 2){ if(players === 2){
var gauge2 = Math.round(p2.results.gauge / 2) / 50 var gauge2 = this.controller.syncWith.game.rules.gaugePercent(p2.results.gauge)
if(gauge2 > gaugePercent){ if(gauge2 > gaugePercent && failedOffset !== 0 && gauge2 >= gaugeClear[1]){
gaugePercent = gauge2 failedOffset = 0
} }
} }
var gaugeClear = 25 / 50
var failedOffset = gaugePercent >= gaugeClear ? 0 : -2000
if(elapsed >= 3100 + failedOffset){ if(elapsed >= 3100 + failedOffset){
for(var p = 0; p < players; p++){ for(var p = 0; p < players; p++){
ctx.save() ctx.save()
...@@ -335,8 +345,9 @@ class Scoresheet{ ...@@ -335,8 +345,9 @@ class Scoresheet{
if(p === 1){ if(p === 1){
results = p2.results results = p2.results
} }
var resultGauge = Math.round(results.gauge / 2) / 50 var playerRules = p === 0 ? rules : this.controller.syncWith.game.rules
var clear = resultGauge >= gaugeClear var resultGauge = playerRules.gaugePercent(results.gauge)
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)
} }
...@@ -360,8 +371,8 @@ class Scoresheet{ ...@@ -360,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()
...@@ -574,7 +585,7 @@ class Scoresheet{ ...@@ -574,7 +585,7 @@ class Scoresheet{
if(this.tetsuoHanaClass){ if(this.tetsuoHanaClass){
this.tetsuoHana.classList.remove(this.tetsuoHanaClass) this.tetsuoHana.classList.remove(this.tetsuoHanaClass)
} }
this.tetsuoHanaClass = gaugePercent >= gaugeClear ? "dance" : "failed" this.tetsuoHanaClass = rules.clearReached(this.results.gauge) ? "dance" : "failed"
this.tetsuoHana.classList.add(this.tetsuoHanaClass) this.tetsuoHana.classList.add(this.tetsuoHanaClass)
} }
} }
...@@ -593,25 +604,27 @@ class Scoresheet{ ...@@ -593,25 +604,27 @@ class Scoresheet{
results = p2.results results = p2.results
ctx.translate(0, p2Offset) ctx.translate(0, p2Offset)
} }
var gaugePercent = Math.round(results.gauge / 2) / 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: 25 / 50, 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 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: gaugePercent - 1 / 50 >= 25 / 50 cleared: playerRules.clearReached(results.gauge)
}) })
} }
}) })
...@@ -629,7 +642,8 @@ class Scoresheet{ ...@@ -629,7 +642,8 @@ class Scoresheet{
results = p2.results results = p2.results
} }
var crownType = null var crownType = null
if(Math.round(results.gauge / 2) - 1 >= 25){ var playerRules = p === 0 ? rules : this.controller.syncWith.game.rules
if(playerRules.clearReached(results.gauge)){
crownType = results.bad === "0" ? "gold" : "silver" crownType = results.bad === "0" ? "gold" : "silver"
} }
if(crownType !== null){ if(crownType !== null){
...@@ -672,6 +686,7 @@ class Scoresheet{ ...@@ -672,6 +686,7 @@ class Scoresheet{
y: 218, y: 218,
scale: crownScale, scale: crownScale,
shine: shine, shine: shine,
whiteOutline: true,
ratio: ratio ratio: ratio
}) })
...@@ -789,9 +804,13 @@ class Scoresheet{ ...@@ -789,9 +804,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
p2.send("songsel") if(p2.session){
p2.send("songsel")
}else{
this.toSongsel(true)
}
} }
if(this.state.screen === "fadeOut"){ if(this.state.screen === "fadeOut"){
...@@ -853,6 +872,37 @@ class Scoresheet{ ...@@ -853,6 +872,37 @@ class Scoresheet{
return Date.now() return Date.now()
} }
saveScore(){
if(this.controller.saveScore){
if(this.resultsObj.points < 0){
this.resultsObj.points = 0
}
var title = this.controller.selectedSong.originalTitle
var hash = this.controller.selectedSong.hash
var difficulty = this.resultsObj.difficulty
var oldScore = scoreStorage.get(hash, difficulty, true)
var clearReached = this.controller.game.rules.clearReached(this.resultsObj.gauge)
var crown = ""
if(clearReached){
crown = this.resultsObj.bad === 0 ? "gold" : "silver"
}
if(!oldScore || oldScore.points <= this.resultsObj.points){
if(oldScore && (oldScore.crown === "gold" || oldScore.crown === "silver" && !crown)){
crown = oldScore.crown
}
this.resultsObj.crown = crown
delete this.resultsObj.title
delete this.resultsObj.difficulty
delete this.resultsObj.gauge
scoreStorage.add(hash, difficulty, this.resultsObj, true, title)
}else if(oldScore && (crown === "gold" && oldScore.crown !== "gold" || crown && !oldScore.crown)){
oldScore.crown = crown
scoreStorage.add(hash, difficulty, oldScore, true, title)
}
}
this.scoreSaved = true
}
clean(){ clean(){
this.keyboard.clean() this.keyboard.clean()
this.gamepad.clean() this.gamepad.clean()
...@@ -865,7 +915,7 @@ class Scoresheet{ ...@@ -865,7 +915,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){
......
class ScoreStorage{
constructor(){
this.scores = {}
this.songTitles = {}
this.difficulty = ["oni", "ura", "hard", "normal", "easy"]
this.scoreKeys = ["points", "good", "ok", "bad", "maxCombo", "drumroll"]
this.crownValue = ["", "silver", "gold"]
this.load()
}
load(){
this.scores = {}
this.scoreStrings = {}
try{
var localScores = localStorage.getItem("scoreStorage")
if(localScores){
this.scoreStrings = JSON.parse(localScores)
}
}catch(e){}
for(var hash in this.scoreStrings){
var scoreString = this.scoreStrings[hash]
var songAdded = false
if(typeof scoreString === "string" && scoreString){
var diffArray = scoreString.split(";")
for(var i in this.difficulty){
if(diffArray[i]){
var crown = parseInt(diffArray[i].slice(0, 1)) || 0
var score = {
crown: this.crownValue[crown] || ""
}
var scoreArray = diffArray[i].slice(1).split(",")
for(var j in this.scoreKeys){
var name = this.scoreKeys[j]
var value = parseInt(scoreArray[j], 36) || 0
if(value < 0){
value = 0
}
score[name] = value
}
if(!songAdded){
this.scores[hash] = {title: null}
songAdded = true
}
this.scores[hash][this.difficulty[i]] = score
}
}
}
}
}
save(){
for(var hash in this.scores){
this.writeString(hash)
}
this.write()
}
write(){
try{
localStorage.setItem("scoreStorage", JSON.stringify(this.scoreStrings))
}catch(e){}
}
writeString(hash){
var score = this.scores[hash]
var diffArray = []
var notEmpty = false
for(var i = this.difficulty.length; i--;){
var diff = this.difficulty[i]
if(score[diff]){
var scoreArray = []
var crown = this.crownValue.indexOf(score[diff].crown).toString()
for(var j in this.scoreKeys){
var name = this.scoreKeys[j]
var value = score[diff][name]
value = Math.floor(value).toString(36)
scoreArray.push(value)
}
diffArray.unshift(crown + scoreArray.join(","))
notEmpty = true
}else if(notEmpty){
diffArray.unshift("")
}
}
this.scoreStrings[hash] = diffArray.join(";")
}
titleHash(song){
if(song in this.songTitles){
return this.songTitles[song]
}else{
return song
}
}
get(song, difficulty, isHash){
if(!song){
return this.scores
}else{
var hash = isHash ? song : this.titleHash(song)
if(difficulty){
if(hash in this.scores){
return this.scores[hash][difficulty]
}
}else{
return this.scores[hash]
}
}
}
add(song, difficulty, scoreObject, isHash, setTitle){
var hash = isHash ? song : this.titleHash(song)
if(!(hash in this.scores)){
this.scores[hash] = {}
}
if(setTitle){
this.scores[hash].title = setTitle
}
this.scores[hash][difficulty] = scoreObject
this.writeString(hash)
this.write()
}
template(){
var template = {crown: ""}
for(var i in this.scoreKeys){
var name = this.scoreKeys[i]
template[name] = 0
}
return template
}
remove(song, difficulty, isHash){
var hash = isHash ? song : this.titleHash(song)
if(hash in this.scores){
if(difficulty){
if(difficulty in this.scores[hash]){
delete this.scores[hash][difficulty]
var noDiff = true
for(var i in this.difficulty){
if(this.scores[hash][this.difficulty[i]]){
noDiff = false
break
}
}
if(noDiff){
delete this.scores[hash]
delete this.scoreStrings[hash]
}else{
this.writeString(hash)
}
}
}else{
delete this.scores[hash]
delete this.scoreStrings[hash]
}
this.write()
}
}
}
...@@ -167,9 +167,29 @@ class SettingsView{ ...@@ -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.touchEnd.forEach(func => func(event)) 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))
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{ ...@@ -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{ ...@@ -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{ ...@@ -375,9 +396,14 @@ class SettingsView{
}else{ }else{
this.touched = false this.touched = false
} }
callback(event) if(event.type !== "touchend" || !this.touchMove.active){
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{ ...@@ -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{ ...@@ -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{ ...@@ -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
......
This diff is collapsed.
...@@ -96,6 +96,7 @@ ...@@ -96,6 +96,7 @@
this.maxCombo = "最大コンボ数" this.maxCombo = "最大コンボ数"
this.drumroll = "連打数" this.drumroll = "連打数"
this.errorOccured = "エラーが発生しました。再読み込みしてください。"
this.tutorial = { this.tutorial = {
basics: [ basics: [
"流れてくる音符がワクに重なったらバチで太鼓をたたこう!", "流れてくる音符がワクに重なったらバチで太鼓をたたこう!",
...@@ -119,7 +120,8 @@ ...@@ -119,7 +120,8 @@
"Gitリポジトリかメールでバグを報告してください。" "Gitリポジトリかメールでバグを報告してください。"
], ],
diagnosticWarning: "以下の端末診断情報も併せて報告してください!", diagnosticWarning: "以下の端末診断情報も併せて報告してください!",
issueTemplate: "###### 下記の問題を説明してください。 スクリーンショットと診断情報を含めてください。" issueTemplate: "###### 下記の問題を説明してください。 スクリーンショットと診断情報を含めてください。",
issues: "課題"
} }
this.session = { this.session = {
multiplayerSession: "オンラインセッション", multiplayerSession: "オンラインセッション",
...@@ -301,6 +303,7 @@ function StringsEn(){ ...@@ -301,6 +303,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!",
...@@ -324,7 +327,8 @@ function StringsEn(){ ...@@ -324,7 +327,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",
...@@ -456,10 +460,10 @@ function StringsCn(){ ...@@ -456,10 +460,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: "",
...@@ -506,6 +510,7 @@ function StringsCn(){ ...@@ -506,6 +510,7 @@ function StringsCn(){
this.maxCombo = "最多连段数" this.maxCombo = "最多连段数"
this.drumroll = "连打数" this.drumroll = "连打数"
this.errorOccured = "An error occurred, please refresh"
this.tutorial = { this.tutorial = {
basics: [ basics: [
"当流动的音符将与框框重叠时就用鼓棒敲打太鼓吧", "当流动的音符将与框框重叠时就用鼓棒敲打太鼓吧",
...@@ -529,11 +534,12 @@ function StringsCn(){ ...@@ -529,11 +534,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 = {
...@@ -664,7 +670,7 @@ function StringsTw(){ ...@@ -664,7 +670,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: "",
...@@ -711,6 +717,7 @@ function StringsTw(){ ...@@ -711,6 +717,7 @@ function StringsTw(){
this.maxCombo = "最多連段數" this.maxCombo = "最多連段數"
this.drumroll = "連打數" this.drumroll = "連打數"
this.errorOccured = "An error occurred, please refresh"
this.tutorial = { this.tutorial = {
basics: [ basics: [
"當流動的音符將與框框重疊時就用鼓棒敲打太鼓吧", "當流動的音符將與框框重疊時就用鼓棒敲打太鼓吧",
...@@ -734,11 +741,12 @@ function StringsTw(){ ...@@ -734,11 +741,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 = {
...@@ -916,6 +924,7 @@ function StringsKo(){ ...@@ -916,6 +924,7 @@ function StringsKo(){
this.maxCombo = "최대 콤보 수" this.maxCombo = "최대 콤보 수"
this.drumroll = "연타 횟수" this.drumroll = "연타 횟수"
this.errorOccured = "An error occurred, please refresh"
this.tutorial = { this.tutorial = {
basics: [ basics: [
"이동하는 음표가 테두리와 겹쳐졌을 때 북채로 태고를 두드리자!", "이동하는 음표가 테두리와 겹쳐졌을 때 북채로 태고를 두드리자!",
...@@ -939,7 +948,8 @@ function StringsKo(){ ...@@ -939,7 +948,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",
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
this.songBg = document.getElementById("songbg") this.songBg = document.getElementById("songbg")
this.songStage = document.getElementById("song-stage") this.songStage = document.getElementById("song-stage")
this.rules = this.controller.game.rules
this.portraitClass = false this.portraitClass = false
this.touchp2Class = false this.touchp2Class = false
this.darkDonBg = false this.darkDonBg = false
...@@ -347,7 +348,7 @@ ...@@ -347,7 +348,7 @@
} }
var score = this.controller.getGlobalScore() var score = this.controller.getGlobalScore()
var gaugePercent = Math.round(score.gauge / 2) / 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"
...@@ -501,7 +502,7 @@ ...@@ -501,7 +502,7 @@
ctx: ctx, ctx: ctx,
x: winW, x: winW,
y: this.multiplayer === 2 ? 468 : 273, y: this.multiplayer === 2 ? 468 : 273,
clear: 25 / 50, clear: this.rules.gaugeClear,
percentage: gaugePercent, percentage: gaugePercent,
font: this.font, font: this.font,
scale: 0.7, scale: 0.7,
...@@ -513,7 +514,7 @@ ...@@ -513,7 +514,7 @@
x: winW - 40, x: winW - 40,
y: this.multiplayer === 2 ? 484 : 293, y: this.multiplayer === 2 ? 484 : 293,
scale: 0.75, scale: 0.75,
cleared: gaugePercent - 1 / 50 >= 25 / 50 cleared: this.rules.clearReached(score.gauge)
}) })
// Note bar // Note bar
...@@ -576,7 +577,7 @@ ...@@ -576,7 +577,7 @@
ctx: ctx, ctx: ctx,
x: winW, x: winW,
y: this.multiplayer === 2 ? 357 : 135, y: this.multiplayer === 2 ? 357 : 135,
clear: 25 / 50, clear: this.rules.gaugeClear,
percentage: gaugePercent, percentage: gaugePercent,
font: this.font, font: this.font,
multiplayer: this.multiplayer === 2, multiplayer: this.multiplayer === 2,
...@@ -586,7 +587,7 @@ ...@@ -586,7 +587,7 @@
ctx: ctx, ctx: ctx,
x: winW - 57, x: winW - 57,
y: this.multiplayer === 2 ? 378 : 165, y: this.multiplayer === 2 ? 378 : 165,
cleared: gaugePercent - 1 / 50 >= 25 / 50 cleared: this.rules.clearReached(score.gauge)
}) })
// Note bar // Note bar
...@@ -1891,8 +1892,8 @@ ...@@ -1891,8 +1892,8 @@
} }
}else{ }else{
var animation = this.assets.don.getAnimation() var animation = this.assets.don.getAnimation()
var gauge = this.controller.getGlobalScore().gauge var score = this.controller.getGlobalScore()
var cleared = Math.round(gauge / 2) - 1 >= 25 var cleared = this.rules.clearReached(score.gauge)
if(animation === "gogo" || cleared && animation === "normal" || !cleared && animation === "clear"){ if(animation === "gogo" || cleared && animation === "normal" || !cleared && animation === "clear"){
this.assets.don.normalAnimation() this.assets.don.normalAnimation()
} }
......
...@@ -42,16 +42,20 @@ class ViewAssets{ ...@@ -42,16 +42,20 @@ class ViewAssets{
var length = this.don.getAnimationLength("gogo") var length = this.don.getAnimationLength("gogo")
this.don.setUpdateSpeed(4 / length) this.don.setUpdateSpeed(4 / length)
this.don.setAnimation("gogo") this.don.setAnimation("gogo")
}else if(Math.round(this.controller.getGlobalScore().gauge / 2) - 1 >= 25){
this.don.setAnimationStart(0)
var length = this.don.getAnimationLength("clear")
this.don.setUpdateSpeed(2 / length)
this.don.setAnimation("clear")
}else{ }else{
this.don.setAnimationStart(0) var score = this.controller.getGlobalScore()
var length = this.don.getAnimationLength("normal") var cleared = this.controller.game.rules.clearReached(score.gauge)
this.don.setUpdateSpeed(4 / length) if(cleared){
this.don.setAnimation("normal") this.don.setAnimationStart(0)
var length = this.don.getAnimationLength("clear")
this.don.setUpdateSpeed(2 / length)
this.don.setAnimation("clear")
}else{
this.don.setAnimationStart(0)
var length = this.don.getAnimationLength("normal")
this.don.setUpdateSpeed(4 / length)
this.don.setAnimation("normal")
}
} }
} }
this.don.addFrames("clear", 30, "don_anim_clear") this.don.addFrames("clear", 30, "don_anim_clear")
......
...@@ -4,8 +4,8 @@ ...@@ -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>
......
...@@ -24,7 +24,7 @@ ...@@ -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>
......
...@@ -262,7 +262,7 @@ async def connection(ws, path): ...@@ -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,25 @@ async def connection(ws, path): ...@@ -336,7 +336,25 @@ 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, "0.0.0.0", port) websockets.serve(connection, "0.0.0.0", 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()
@echo off
(
git log -1 --pretty="format:{\"commit\": \"%%H\", \"commit_short\": \"%%h\", \"version\": \"%%ad\", \"url\": \"https://github.com/bui/taiko-web/\"}" --date="format:%%y.%%m.%%d"
) > ../version.json
git log -1 --pretty="format:{\"commit\": \"%H\", \"commit_short\": \"%h\", \"version\": \"%ad\", \"url\": \"https://github.com/bui/taiko-web/\"}" --date="format:%y.%m.%d" > ../version.json
<html>
<head>
<title>Merge Image</title>
<style>
body{
transition: background-color 0.5s;
background-color: #fff;
font-family: sans-serif;
font-size: 20px;
}
input[type=number]{
font-family: monospace;
font-size: 18px;
padding: 5px;
}
#settings{
display: flex;
margin-bottom: 18px;
height: 40px;
}
label{
display: flex;
align-items: center;
height: 100%;
}
label:not(:first-child){
margin-left: 10px;
border-left: 2px solid #ccc;
padding-left: 8px;
}
input{
margin: 5px;
}
</style>
</head>
<body>
<div id="settings">
<label>
Max height
<input id="max-height" type="number" min="1" max="5000" step="1" value="2000">
px
</label>
<label>
Spacing
<input id="spacing" type="number" min="0" max="100" step="1" value="0">
px
</label>
<label>
<input id="vertical" type="checkbox" checked>
Vertical
</label>
</div>
<div id="hint">Drag and drop your images here...</div>
<canvas id="canvas"></canvas>
<script>
var canvas = document.getElementById("canvas")
var ctx = canvas.getContext("2d")
var allFiles
var maxHeightElement = document.getElementById("max-height")
var spacingElement = document.getElementById("spacing")
var maxHeight = parseInt(maxHeightElement.value)
var spacing = parseInt(spacingElement.value)
var vectical = true
document.addEventListener("dragover", event => {
event.preventDefault()
event.dataTransfer.dropEffect = "copy"
document.body.style.backgroundColor = "#ccc"
})
document.addEventListener("dragleave", () => {
document.body.style.backgroundColor = "#fff"
})
document.addEventListener("drop", event => {
document.getElementById("hint").style.display = "none"
document.body.style.backgroundColor = "#fff"
event.preventDefault()
allFiles = []
var promises = []
for(let file of event.dataTransfer.files){
promises.push(readFile(file))
}
Promise.all(promises).then(drawCanvas)
})
maxHeightElement.addEventListener("change", event => {
var value = parseInt(event.currentTarget.value)
if(value >= 1 && value <= 5000){
maxHeight = value
if(allFiles && allFiles.length){
drawCanvas()
}
}
})
spacingElement.addEventListener("change", event => {
var value = parseInt(event.currentTarget.value)
if(value >= 0 && value <= 100){
spacing = value
if(allFiles && allFiles.length){
drawCanvas()
}
}
})
document.getElementById("vertical").addEventListener("change", event => {
vertical = event.currentTarget.checked
if(allFiles && allFiles.length){
drawCanvas()
}
})
function readFile(file){
return new Promise((resolve, reject) => {
var reader = new FileReader()
reader.addEventListener("load", () => {
if(reader.result){
var img = document.createElement("img")
img.addEventListener("load", () => {
var noExt = file.name.slice(0, file.name.lastIndexOf("."))
if(parseInt(noExt) == noExt){
var name = parseInt(noExt)
}else{
var name = noExt
}
allFiles.push({
name: name,
img: img
})
resolve()
})
img.addEventListener("error", resolve)
img.addEventListener("abort", resolve)
img.src = reader.result
}else{
resolve()
}
})
reader.addEventListener("error", resolve)
reader.addEventListener("abort", resolve)
reader.readAsDataURL(file)
})
}
function drawCanvas(){
var x = 0
var y = 0
var biggestWidth = 0
var canvasWidth = 0
var canvasHeight = 0
allFiles.sort((a, b) => a.name > b.name ? 1 : -1)
for(var i in allFiles){
var file = allFiles[i]
if(vertical){
if(y + file.img.height > maxHeight + spacing){
y = 0
x += biggestWidth
biggestWidth = 0
}
file.x = x + (x === 0 ? 0 : spacing)
file.y = y + (y === 0 ? 0 : spacing)
y += file.img.height + (y === 0 ? 0 : spacing)
if(file.img.width > biggestWidth){
biggestWidth = file.img.width
}
if(y > canvasHeight){
canvasHeight = y
}
}else{
if(x + file.img.width > maxHeight + spacing){
x = 0
y += biggestWidth
biggestWidth = 0
}
file.x = x + (x === 0 ? 0 : spacing)
file.y = y + (y === 0 ? 0 : spacing)
x += file.img.width + (x === 0 ? 0 : spacing)
if(file.img.height > biggestWidth){
biggestWidth = file.img.height
}
if(x > canvasWidth){
canvasWidth = x
}
}
}
if(vertical){
canvasWidth = x + biggestWidth
}else{
canvasHeight = y + biggestWidth
}
canvas.width = canvasWidth
canvas.height = canvasHeight
for(var i in allFiles){
var file = allFiles[i]
ctx.drawImage(file.img, file.x, file.y, file.img.width, file.img.height)
}
}
</script>
</body>
</html>
from __future__ import division
import os
import sqlite3
import re
DATABASE = 'taiko.db'
conn = sqlite3.connect(DATABASE)
curs = conn.cursor()
def parse_osu(osu):
osu_lines = open(osu, 'r').read().replace('\x00', '').split('\n')
sections = {}
current_section = (None, [])
for line in osu_lines:
line = line.strip()
secm = re.match('^\[(\w+)\]$', line)
if secm:
if current_section:
sections[current_section[0]] = current_section[1]
current_section = (secm.group(1), [])
else:
if current_section:
current_section[1].append(line)
else:
current_section = ('Default', [line])
if current_section:
sections[current_section[0]] = current_section[1]
return sections
def get_osu_key(osu, section, key, default=None):
sec = osu[section]
for line in sec:
ok = line.split(':', 1)[0].strip()
ov = line.split(':', 1)[1].strip()
if ok.lower() == key.lower():
return ov
return default
def get_preview(song_id, song_type):
preview = 0
if song_type == "tja":
if os.path.isfile('public/songs/%s/main.tja' % song_id):
preview = get_tja_preview('public/songs/%s/main.tja' % song_id)
else:
osus = [osu for osu in os.listdir('public/songs/%s' % song_id) if osu in ['easy.osu', 'normal.osu', 'hard.osu', 'oni.osu']]
if osus:
osud = parse_osu('public/songs/%s/%s' % (song_id, osus[0]))
preview = int(get_osu_key(osud, 'General', 'PreviewTime', 0))
return preview
def get_tja_preview(tja):
tja_lines = open(tja, 'r').read().replace('\x00', '').split('\n')
for line in tja_lines:
line = line.strip()
if ':' in line:
name, value = line.split(':', 1)
if name.lower() == 'demostart':
value = value.strip()
try:
value = float(value)
except ValueError:
pass
else:
return int(value * 1000)
elif line.lower() == '#start':
break
return 0
if __name__ == '__main__':
songs = curs.execute('select id, type from songs').fetchall()
for song in songs:
preview = get_preview(song[0], song[1]) / 1000
curs.execute('update songs set preview = ? where id = ?', (preview, song[0]))
conn.commit()
import os
import sys
import hashlib
import base64
import sqlite3
def md5(md5hash, filename):
with open(filename, "rb") as file:
for chunk in iter(lambda: file.read(64 * 1024), b""):
md5hash.update(chunk)
def get_hashes(root):
hashes = {}
diffs = ["easy", "normal", "hard", "oni", "ura"]
dirs = os.listdir(root)
for dir in dirs:
dir_path = os.path.join(root, dir)
if dir.isdigit() and os.path.isdir(dir_path):
files = os.listdir(dir_path)
md5hash = hashlib.md5()
if "main.tja" in files:
md5(md5hash, os.path.join(dir_path, "main.tja"))
else:
for diff in diffs:
if diff + ".osu" in files:
md5(md5hash, os.path.join(dir_path, diff + ".osu"))
hashes[dir] = base64.b64encode(md5hash.digest())[:-2]
return hashes
def write_db(database, songs):
db = sqlite3.connect(database)
hashes = get_hashes(songs)
added = 0
for id in hashes:
added += 1
cur = db.cursor()
cur.execute("update songs set hash = ? where id = ?", (hashes[id].decode(), int(id)))
cur.close()
db.commit()
db.close()
if added:
print("{0} hashes have been added to the database.".format(added))
else:
print("Error: No songs were found in the given directory.")
if len(sys.argv) >= 3:
write_db(sys.argv[1], sys.argv[2])
else:
print("Usage: taikodb_hash.py ../taiko.db ../public/songs")
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment