Add everything for accounts

This commit is contained in:
LoveEevee 2020-03-13 05:34:54 +03:00
parent 47545e9da9
commit 2af924a985
21 changed files with 1291 additions and 146 deletions

View File

@ -291,3 +291,80 @@ kbd{
.left-buttons .taibtn{
z-index: 1;
}
.accountpass-form,
.accountdel-form,
.login-form{
text-align: center;
width: 80%;
margin: auto;
}
.accountpass-form .accountpass-div,
.accountdel-form .accountdel-div,
.login-form .password2-div{
display: none;
}
.account-view .displayname,
.accountpass-form input[type=password],
.accountdel-form input[type=password],
.login-form input[type=text],
.login-form input[type=password]{
width: 100%;
font-size: 1.4em;
margin: 0.1em 0;
padding: 0.3em;
box-sizing: border-box;
}
.accountpass-form input[type=password]{
width: calc(100% / 3);
}
.accountpass-form input[type=password]::placeholder{
font-size: 0.8em;
}
.login-form input[type=checkbox]{
transform: scale(1.4);
}
.account-view .displayname-hint,
.login-form .username-hint,
.login-form .password-hint,
.login-form .remember-label{
display: block;
font-size: 1.1em;
padding: 0.5em;
}
.login-form .remember-label{
padding: 0.85em;
}
.account-view .save-btn{
float: right;
padding: 0.4em 1.5em;
font-weight: bold;
border-color: #000;
color: #000;
z-index: 1;
}
.account-view .view-end-button{
margin-right: 0.4em;
font-weight: normal;
border-color: #dacdb2;
color: #555;
}
.account-view .save-btn:hover,
.account-view .save-btn.selected,
.account-view .view-end-button:hover,
.account-view .view-end-button.selected{
color: #fff;
border-color: #fff;
}
.account-view .displayname-div{
width: 80%;
margin: 0 auto;
}
.accountpass-form .accountpass-btn,
.accountdel-form .accountdel-btn,
.login-form .login-btn{
z-index: 1;
}
.accountpass-form,
.accountdel-form{
margin: 0.3em auto;
}

482
public/src/js/account.js Normal file
View File

@ -0,0 +1,482 @@
class Account{
constructor(touchEnabled){
this.touchEnabled = touchEnabled
cancelTouch = false
this.locked = false
if(account.loggedIn){
this.accountForm()
}else{
this.loginForm()
}
this.selected = this.items.length - 1
this.keyboard = new Keyboard({
confirm: ["enter", "space", "don_l", "don_r"],
previous: ["left", "up", "ka_l"],
next: ["right", "down", "ka_r"],
back: ["escape"]
}, this.keyPressed.bind(this))
this.gamepad = new Gamepad({
"confirm": ["b", "ls", "rs"],
"previous": ["u", "l", "lb", "lt", "lsu", "lsl"],
"next": ["d", "r", "rb", "rt", "lsd", "lsr"],
"back": ["start", "a"]
}, this.keyPressed.bind(this))
pageEvents.send("account", account.loggedIn)
}
accountForm(){
loader.changePage("account", true)
this.mode = "account"
this.setAltText(this.getElement("view-title"), account.username)
this.items = []
this.inputForms = []
this.shownDiv = ""
this.getElement("displayname-hint").innerText = strings.account.displayName
this.displayname = this.getElement("displayname")
this.displayname.placeholder = strings.account.displayName
this.displayname.value = account.displayName
this.inputForms.push(this.displayname)
this.accountPassButton = this.getElement("accountpass-btn")
this.setAltText(this.accountPassButton, strings.account.changePassword)
pageEvents.add(this.accountPassButton, ["click", "touchstart"], event => {
this.showDiv(event, "pass")
})
this.accountPass = this.getElement("accountpass-form")
for(var i = 0; i < this.accountPass.length; i++){
this.accountPass[i].placeholder = strings.account.currentNewRepeat[i]
this.inputForms.push(this.accountPass[i])
}
this.accountPassDiv = this.getElement("accountpass-div")
this.accountDelButton = this.getElement("accountdel-btn")
this.setAltText(this.accountDelButton, strings.account.deleteAccount)
pageEvents.add(this.accountDelButton, ["click", "touchstart"], event => {
this.showDiv(event, "del")
})
this.accountDel = this.getElement("accountdel-form")
this.accountDel.password.placeholder = strings.account.verifyPassword
this.inputForms.push(this.accountDel.password)
this.accountDelDiv = this.getElement("accountdel-div")
this.logoutButton = this.getElement("logout-btn")
this.setAltText(this.logoutButton, strings.account.logout)
pageEvents.add(this.logoutButton, ["mousedown", "touchstart"], this.onLogout.bind(this))
this.items.push(this.logoutButton)
this.endButton = this.getElement("view-end-button")
this.setAltText(this.endButton, strings.account.cancel)
pageEvents.add(this.endButton, ["mousedown", "touchstart"], this.onEnd.bind(this))
this.items.push(this.endButton)
this.saveButton = this.getElement("save-btn")
this.setAltText(this.saveButton, strings.account.save)
pageEvents.add(this.saveButton, ["mousedown", "touchstart"], this.onSave.bind(this))
this.items.push(this.saveButton)
for(var i = 0; i < this.inputForms.length; i++){
pageEvents.add(this.inputForms[i], ["keydown", "keyup", "keypress"], this.onFormPress.bind(this))
}
}
showDiv(event, div){
if(event){
if(event.type === "touchstart"){
event.preventDefault()
}else if(event.which !== 1){
return
}
}
if(this.locked){
return
}
var otherDiv = this.shownDiv && this.shownDiv !== div
var display = this.shownDiv === div ? "" : "block"
this.shownDiv = display ? div : ""
switch(div){
case "pass":
if(otherDiv){
this.accountDelDiv.style.display = ""
}
this.accountPassDiv.style.display = display
break
case "del":
if(otherDiv){
this.accountPassDiv.style.display = ""
}
this.accountDelDiv.style.display = display
break
}
}
loginForm(register, fromSwitch){
loader.changePage("login", true)
this.mode = register ? "register" : "login"
this.setAltText(this.getElement("view-title"), strings.account[this.mode])
this.items = []
this.form = this.getElement("login-form")
this.getElement("username-hint").innerText = strings.account.username
this.form.username.placeholder = strings.account.enterUsername
this.getElement("password-hint").innerText = strings.account.password
this.form.password.placeholder = strings.account.enterPassword
this.password2 = this.getElement("password2-div")
this.remember = this.getElement("remember-div")
this.getElement("remember-label").appendChild(document.createTextNode(strings.account.remember))
this.loginButton = this.getElement("login-btn")
this.registerButton = this.getElement("register-btn")
if(register){
var pass2 = document.createElement("input")
pass2.type = "password"
pass2.name = "password2"
pass2.required = true
pass2.placeholder = strings.account.repeatPassword
this.password2.appendChild(pass2)
this.password2.style.display = "block"
this.remember.style.display = "none"
this.setAltText(this.loginButton, strings.account.registerAccount)
this.setAltText(this.registerButton, strings.account.login)
}else{
this.setAltText(this.loginButton, strings.account.login)
this.setAltText(this.registerButton, strings.account.register)
}
pageEvents.add(this.form, "submit", this.onLogin.bind(this))
pageEvents.add(this.loginButton, ["mousedown", "touchstart"], this.onLogin.bind(this))
pageEvents.add(this.registerButton, ["mousedown", "touchstart"], this.onSwitchMode.bind(this))
this.items.push(this.registerButton)
if(!register){
this.items.push(this.loginButton)
}
for(var i = 0; i < this.form.length; i++){
pageEvents.add(this.form[i], ["keydown", "keyup", "keypress"], this.onFormPress.bind(this))
}
this.endButton = this.getElement("view-end-button")
this.setAltText(this.endButton, strings.account.back)
pageEvents.add(this.endButton, ["mousedown", "touchstart"], this.onEnd.bind(this))
this.items.push(this.endButton)
if(fromSwitch){
this.selected = 0
this.endButton.classList.remove("selected")
this.registerButton.classList.add("selected")
}
}
getElement(name){
return loader.screen.getElementsByClassName(name)[0]
}
setAltText(element, text){
element.innerText = text
element.setAttribute("alt", text)
}
keyPressed(pressed, name){
if(!pressed || this.locked){
return
}
var selected = this.items[this.selected]
if(name === "confirm"){
if(selected === this.endButton){
this.onEnd()
}else if(selected === this.registerButton){
this.onSwitchMode()
}else if(selected === this.loginButton){
this.onLogin()
}
}else if(name === "previous" || name === "next"){
selected.classList.remove("selected")
this.selected = this.mod(this.items.length, this.selected + (name === "next" ? 1 : -1))
this.items[this.selected].classList.add("selected")
assets.sounds["se_ka"].play()
}else if(name === "back"){
this.onEnd()
}
}
mod(length, index){
return ((index % length) + length) % length
}
onFormPress(event){
event.stopPropagation()
if(event.type === "keypress" && event.keyCode === 13){
if(this.mode === "account"){
this.onSave()
}else{
this.onLogin()
}
}
}
onSwitchMode(event){
if(event){
if(event.type === "touchstart"){
event.preventDefault()
}else if(event.which !== 1){
return
}
}
if(this.locked){
return
}
this.clean(true)
this.loginForm(this.mode === "login", true)
}
onLogin(event){
if(event){
if(event.type === "touchstart"){
event.preventDefault()
}else if(event.which !== 1){
return
}
}
if(this.locked){
return
}
var obj = {
username: this.form.username.value,
password: this.form.password.value
}
if(!obj.username || !obj.password){
alert(strings.account.cannotBeEmpty.replace("%s", strings.account[!obj.username ? "username" : "password"]))
return
}
if(this.mode === "login"){
obj.remember = this.form.remember.checked
}else{
if(obj.password !== this.form.password2.value){
alert(strings.account.passwordsDoNotMatch)
return
}
}
this.request(this.mode, obj).then(response => {
account.loggedIn = true
account.username = response.username
account.displayName = response.display_name
var loadScores = scores => {
scoreStorage.load(scores)
this.onEnd(false, true)
pageEvents.send("login", account.username)
}
if(this.mode === "login"){
this.request("scores/get").then(response => {
loadScores(response.scores)
}, () => {
loadScores({})
})
}else{
scoreStorage.save().catch(() => {}).finally(() => {
this.onEnd(false, true)
pageEvents.send("login", account.username)
})
}
}, response => {
if(response && response.status === "error" && response.message){
alert(response.message)
}else{
alert(strings.account.error)
}
})
}
onLogout(){
if(event){
if(event.type === "touchstart"){
event.preventDefault()
}else if(event.which !== 1){
return
}
}
if(this.locked){
return
}
account.loggedIn = false
delete account.username
delete account.displayName
var loadScores = scores => {
Cookies.remove("token")
scoreStorage.load()
this.onEnd(false, true)
pageEvents.send("logout")
}
this.request("logout").then(response => {
loadScores()
}, () => {
loadScores()
})
}
onSave(event){
if(event){
if(event.type === "touchstart"){
event.preventDefault()
}else if(event.which !== 1){
return
}
}
if(this.locked){
return
}
var promises = []
var noNameChange = false
if(this.shownDiv === "pass"){
var passwords = []
for(var i = 0; i < this.accountPass.length; i++){
passwords.push(this.accountPass[i].value)
}
if(passwords[1] === passwords[2]){
promises.push(this.request("account/password", {
current_password: passwords[0],
new_password: passwords[1]
}))
}else{
alert(strings.account.passwordsDoNotMatch)
return
}
}
if(this.shownDiv === "del" && this.accountDel.password.value){
noNameChange = true
promises.push(this.request("account/remove", {
password: this.accountDel.password.value
}).then(() => {
account.loggedIn = false
delete account.username
delete account.displayName
Cookies.remove("token")
scoreStorage.load()
pageEvents.send("logout")
return Promise.resolve
}))
}
var newName = this.displayname.value.trim()
if(!noNameChange && newName !== account.displayName){
promises.push(this.request("account/display_name", {
display_name: newName
}))
}
var error = false
var errorFunc = response => {
if(error){
return
}
if(response && response.message){
alert(response.message)
}else{
alert(strings.account.error)
}
}
Promise.all(promises).then(() => {
this.onEnd(false, true)
}, errorFunc).catch(errorFunc)
}
onEnd(event, noSound){
var touched = false
if(event){
if(event.type === "touchstart"){
event.preventDefault()
touched = true
}else if(event.which !== 1){
return
}
}
if(this.locked){
return
}
this.clean()
assets.sounds["se_don"].play()
setTimeout(() => {
new SongSelect(false, false, touched)
}, 500)
}
request(url, obj){
this.lock(true)
return new Promise((resolve, reject) => {
var request = new XMLHttpRequest()
request.open(obj ? "POST" : "GET", "api/" + url)
pageEvents.load(request).then(() => {
this.lock(false)
if(request.status !== 200){
reject()
return
}
try{
var json = JSON.parse(request.response)
}catch(e){
reject()
return
}
if(json.status === "ok"){
resolve(json)
}else{
reject(json)
}
}, () => {
this.lock(false)
reject()
})
if(obj){
request.setRequestHeader("Content-Type", "application/json;charset=UTF-8")
request.send(JSON.stringify(obj))
}else{
request.send()
}
})
}
lock(isLocked){
this.locked = isLocked
if(this.mode === "login" || this.mode === "register"){
for(var i = 0; i < this.form.length; i++){
this.form[i].disabled = isLocked
}
}else if(this.mode === "account"){
for(var i = 0; i < this.inputForms.length; i++){
this.inputForms[i].disabled = isLocked
}
}
}
clean(eventsOnly){
if(!eventsOnly){
cancelTouch = true
this.keyboard.clean()
this.gamepad.clean()
}
if(this.mode === "account"){
pageEvents.remove(this.accounPassButton, ["click", "touchstart"])
pageEvents.remove(this.accountDelButton, ["click", "touchstart"])
pageEvents.remove(this.logoutButton, ["mousedown", "touchstart"])
pageEvents.remove(this.saveButton, ["mousedown", "touchstart"])
for(var i = 0; i < this.inputForms.length; i++){
pageEvents.remove(this.inputForms[i], ["keydown", "keyup", "keypress"])
}
this.accountPass.reset()
this.accountDel.reset()
delete this.displayname
delete this.accountPassButton
delete this.accountPass
delete this.accountPassDiv
delete this.accountDelButton
delete this.accountDel
delete this.accountDelDiv
delete this.logoutButton
delete this.saveButton
delete this.inputForms
}else if(this.mode === "login" || this.mode === "register"){
if(!eventsOnly){
this.form.reset()
}
pageEvents.remove(this.form, "submit")
pageEvents.remove(this.loginButton, ["mousedown", "touchstart"])
pageEvents.remove(this.registerButton, ["mousedown", "touchstart"])
for(var i = 0; i < this.form.length; i++){
pageEvents.remove(this.registerButton, ["keydown", "keyup", "keypress"])
}
delete this.form
delete this.password2
delete this.remember
delete this.loginButton
delete this.registerButton
}
pageEvents.remove(this.endButton, ["mousedown", "touchstart"])
delete this.endButton
delete this.items
}
}

View File

@ -1,6 +1,7 @@
var assets = {
"js": [
"lib/md5.min.js",
"lib/js.cookie.min.js",
"loadsong.js",
"parseosu.js",
"titlescreen.js",
@ -31,7 +32,8 @@ var assets = {
"importsongs.js",
"logo.js",
"settings.js",
"scorestorage.js"
"scorestorage.js",
"account.js"
],
"css": [
"main.css",
@ -137,7 +139,9 @@ var assets = {
"about.html",
"debug.html",
"session.html",
"settings.html"
"settings.html",
"account.html",
"login.html"
],
"songs": [],

View File

@ -706,12 +706,12 @@
})
}else if(r.smallHiragana.test(symbol)){
// Small hiragana, small katakana
drawn.push({text: symbol, x: 0, y: 0, w: 30})
drawn.push({text: symbol, kana: true, x: 0, y: 0, w: 30})
}else if(r.hiragana.test(symbol)){
// Hiragana, katakana
drawn.push({text: symbol, x: 0, y: 0, w: 35})
drawn.push({text: symbol, kana: true, x: 0, y: 0, w: 35})
}else{
drawn.push({text: symbol, x: 0, y: 0, w: 39})
drawn.push({text: symbol, kana: true, x: 0, y: 0, w: 39})
}
}
@ -720,6 +720,9 @@
if(config.letterSpacing){
symbol.w += config.letterSpacing
}
if(config.kanaSpacing && symbol.kana){
symbol.w += config.kanaSpacing
}
drawnWidth += symbol.w * mul
}
@ -1549,6 +1552,99 @@
ctx.restore()
}
nameplate(config){
var ctx = config.ctx
var w = 264
var h = 57
var r = h / 2
var pi = Math.PI
ctx.save()
ctx.translate(config.x, config.y)
if(config.scale){
ctx.scale(config.scale, config.scale)
}
ctx.fillStyle="rgba(0, 0, 0, 0.25)"
ctx.beginPath()
ctx.arc(r + 4, r + 5, r, pi / 2, pi / -2)
ctx.arc(w - r + 4, r + 5, r, pi / -2, pi / 2)
ctx.fill()
ctx.beginPath()
ctx.moveTo(r, 0)
this.roundedCorner(ctx, w, 0, r, 1)
ctx.lineTo(r, r)
ctx.fillStyle = config.blue ? "#67cecb" : "#ff421d"
ctx.fill()
ctx.beginPath()
ctx.moveTo(r, r)
this.roundedCorner(ctx, w, h, r, 2)
ctx.lineTo(r, h)
ctx.fillStyle = "rgba(255, 255, 255, 0.8)"
ctx.fill()
ctx.strokeStyle = "#000"
ctx.lineWidth = 4
ctx.beginPath()
ctx.moveTo(r, 0)
ctx.arc(w - r, r, r, pi / -2, pi / 2)
ctx.lineTo(r, h)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(r, r - 1)
ctx.lineTo(w, r - 1)
ctx.lineWidth = 2
ctx.stroke()
ctx.beginPath()
ctx.arc(r, r, r, 0, pi * 2)
ctx.fillStyle = config.blue ? "#67cecb" : "#ff421d"
ctx.fill()
ctx.lineWidth = 4
ctx.stroke()
ctx.font = this.bold(config.font) + "28px " + config.font
ctx.textAlign = "center"
ctx.textBaseline = "middle"
ctx.lineWidth = 5
ctx.miterLimit = 1
ctx.strokeStyle = "#fff"
ctx.fillStyle = "#000"
var text = config.blue ? "2P" : "1P"
ctx.strokeText(text, r + 2, r + 1)
ctx.fillText(text, r + 2, r + 1)
if(config.rank){
this.layeredText({
ctx: ctx,
text: config.rank,
fontSize: 20,
fontFamily: config.font,
x: w / 2 + r * 0.7,
y: r * 0.5,
width: 180,
align: "center",
baseline: "middle"
}, [
{fill: "#000"}
])
}
this.layeredText({
ctx: ctx,
text: config.name || "",
fontSize: 21,
fontFamily: config.font,
x: w / 2 + r * 0.7,
y: r * 1.5 - 0.5,
width: 180,
kanaSpacing: 10,
align: "center",
baseline: "middle"
}, [
{outline: "#000", letterBorder: 6},
{fill: "#fff"}
])
ctx.restore()
}
alpha(amount, ctx, callback, winW, winH){
if(amount >= 1){
return callback(ctx)

View File

@ -505,7 +505,9 @@ class Game{
var musicDuration = duration * 1000 - this.controller.offset
if(this.musicFadeOut === 0){
if(this.controller.multiplayer === 1){
p2.send("gameresults", this.getGlobalScore())
var obj = this.getGlobalScore()
obj.name = account.loggedIn ? account.displayName : strings.defaultName
p2.send("gameresults", obj)
}
this.musicFadeOut++
}else if(this.musicFadeOut === 1 && ms >= started + 1600){

View File

@ -202,12 +202,16 @@
var tja = new ParseTja(data, "oni", 0, 0, true)
var songObj = {
id: index + 1,
order: index + 1,
type: "tja",
chart: file,
stars: [],
stars: {},
music: "muted"
}
var coursesAdded = false
var titleLang = {}
var titleLangAdded = false
var subtitleLangAdded = false
var subtitleLang = {}
var dir = file.webkitRelativePath.toLowerCase()
dir = dir.slice(0, dir.lastIndexOf("/") + 1)
@ -221,7 +225,11 @@
}
songObj.subtitle = subtitle
songObj.preview = meta.demostart || 0
songObj.stars[this.courseTypes[diff]] = (meta.level || "0") + (meta.branch ? " B" : "")
songObj.courses[diff] = {
stars: meta.level || 0,
branch: !!meta.branch
}
coursesAdded = true
if(meta.wave){
songObj.music = this.otherFiles[dir + meta.wave.toLowerCase()] || songObj.music
}
@ -264,32 +272,27 @@
}
if(meta["title" + id]){
titleLang[id] = meta["title" + id]
titleLangAdded = true
}else if(songTitle in this.songTitle && this.songTitle[songTitle][id]){
titleLang[id] = this.songTitle[songTitle][id] + ura
titleLangAdded = true
}
if(meta["subtitle" + id]){
subtitleLang[id] = meta["subtitle" + id]
subtitleLangAdded = true
}
}
}
var titleLangArray = []
for(var id in titleLang){
titleLangArray.push(id + " " + titleLang[id])
if(titleLangAdded){
songObj.title_lang = titleLang
}
if(titleLangArray.length !== 0){
songObj.title_lang = titleLangArray.join("\n")
}
var subtitleLangArray = []
for(var id in subtitleLang){
subtitleLangArray.push(id + " " + subtitleLang[id])
}
if(subtitleLangArray.length !== 0){
songObj.subtitle_lang = subtitleLangArray.join("\n")
if(subtitleLangAdded){
songObj.subtitle_lang = subtitleLang
}
if(!songObj.category){
songObj.category = category || this.getCategory(file, [songTitle || songObj.title, file.name.slice(0, file.name.lastIndexOf("."))])
}
if(songObj.stars.length !== 0){
if(coursesAdded){
this.songs[index] = songObj
}
var hash = md5.base64(event.target.result).slice(0, -2)
@ -316,12 +319,20 @@
dir = dir.slice(0, dir.lastIndexOf("/") + 1)
var songObj = {
id: index + 1,
order: index + 1,
type: "osu",
chart: file,
subtitle: osu.metadata.ArtistUnicode || osu.metadata.Artist,
subtitle_lang: osu.metadata.Artist || osu.metadata.ArtistUnicode,
subtitle_lang: {
en: osu.metadata.Artist || osu.metadata.ArtistUnicode
},
preview: osu.generalInfo.PreviewTime / 1000,
stars: [null, null, null, parseInt(osu.difficulty.overallDifficulty) || 1],
courses: {
oni:{
stars: parseInt(osu.difficulty.overallDifficulty) || 0,
branch: false
}
},
music: this.otherFiles[dir + osu.generalInfo.AudioFilename.toLowerCase()] || "muted"
}
var filename = file.name.slice(0, file.name.lastIndexOf("."))
@ -333,7 +344,9 @@
suffix = " " + matches[0]
}
songObj.title = title + suffix
songObj.title_lang = (osu.metadata.Title || osu.metadata.TitleUnicode) + suffix
songObj.title_lang = {
en: (osu.metadata.Title || osu.metadata.TitleUnicode) + suffix
}
}else{
songObj.title = filename
}

2
public/src/js/lib/js.cookie.min.js vendored Normal file
View File

@ -0,0 +1,2 @@
/*! js-cookie v3.0.0-rc.0 | MIT */
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self,function(){var r=e.Cookies,n=e.Cookies=t();n.noConflict=function(){return e.Cookies=r,n}}())}(this,function(){"use strict";function e(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)e[n]=r[n]}return e}var t={read:function(e){return e.replace(/%3B/g,";")},write:function(e){return e.replace(/;/g,"%3B")}};return function r(n,i){function o(r,o,u){if("undefined"!=typeof document){"number"==typeof(u=e({},i,u)).expires&&(u.expires=new Date(Date.now()+864e5*u.expires)),u.expires&&(u.expires=u.expires.toUTCString()),r=t.write(r).replace(/=/g,"%3D"),o=n.write(String(o),r);var c="";for(var f in u)u[f]&&(c+="; "+f,!0!==u[f]&&(c+="="+u[f].split(";")[0]));return document.cookie=r+"="+o+c}}return Object.create({set:o,get:function(e){if("undefined"!=typeof document&&(!arguments.length||e)){for(var r=document.cookie?document.cookie.split("; "):[],i={},o=0;o<r.length;o++){var u=r[o].split("="),c=u.slice(1).join("="),f=t.read(u[0]).replace(/%3D/g,"=");if(i[f]=n.read(c,f),e===f)break}return e?i[e]:i}},remove:function(t,r){o(t,"",e({},r,{expires:-1}))},withAttributes:function(t){return r(this.converter,e({},this.attributes,t))},withConverter:function(t){return r(e({},this.converter,t),this.attributes)}},{attributes:{value:Object.freeze(i)},converter:{value:Object.freeze(n)}})}(t,{path:"/"})});

View File

@ -104,11 +104,12 @@ class Loader{
}))
this.afterJSCount =
["blurPerformance", "P2Connection"].length +
["blurPerformance"].length +
assets.audioSfx.length +
assets.audioMusic.length +
assets.audioSfxLR.length +
assets.audioSfxLoud.length
assets.audioSfxLoud.length +
(gameConfig._accounts ? 1 : 0)
Promise.all(this.promises).then(() => {
@ -155,65 +156,92 @@ class Loader{
}
}))
var readyEvent = "normal"
var songId
var hashLower = location.hash.toLowerCase()
p2 = new P2Connection()
if(hashLower.startsWith("#song=")){
var number = parseInt(location.hash.slice(6))
if(number > 0){
songId = number
readyEvent = "song-id"
if(gameConfig._accounts){
var token = Cookies.get("token")
if(token){
this.addPromise(this.ajax("/api/scores/get").then(response => {
response = JSON.parse(response)
if(response.status === "ok"){
account.loggedIn = true
account.username = response.username
account.displayName = response.display_name
scoreStorage.load(response.scores)
pageEvents.send("login", account.username)
}
}))
}else{
this.assetLoaded()
}
}else if(location.hash.length === 6){
p2.hashLock = true
this.addPromise(new Promise(resolve => {
p2.open()
pageEvents.add(p2, "message", response => {
if(response.type === "session"){
pageEvents.send("session-start", "invited")
readyEvent = "session-start"
resolve()
}else if(response.type === "gameend"){
p2.hash("")
p2.hashLock = false
readyEvent = "session-expired"
resolve()
}
})
p2.send("invite", location.hash.slice(1).toLowerCase())
setTimeout(() => {
if(p2.socket.readyState !== 1){
p2.hash("")
p2.hashLock = false
resolve()
}
}, 10000)
}).then(() => {
pageEvents.remove(p2, "message")
}))
}else{
p2.hash("")
}
settings = new Settings()
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(() => {
this.canvasTest.drawAllImages().then(result => {
if(!account.loggedIn){
scoreStorage.load()
}
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
}
}
var promises = []
var readyEvent = "normal"
var songId
var hashLower = location.hash.toLowerCase()
p2 = new P2Connection()
if(hashLower.startsWith("#song=")){
var number = parseInt(location.hash.slice(6))
if(number > 0){
songId = number
readyEvent = "song-id"
}
}else if(location.hash.length === 6){
p2.hashLock = true
promises.push(new Promise(resolve => {
p2.open()
pageEvents.add(p2, "message", response => {
if(response.type === "session"){
pageEvents.send("session-start", "invited")
readyEvent = "session-start"
resolve()
}else if(response.type === "gameend"){
p2.hash("")
p2.hashLock = false
readyEvent = "session-expired"
resolve()
}
})
p2.send("invite", {
id: location.hash.slice(1).toLowerCase(),
name: account.loggedIn ? account.displayName : null
})
setTimeout(() => {
if(p2.socket.readyState !== 1){
p2.hash("")
p2.hashLock = false
resolve()
}
}, 10000)
}).then(() => {
pageEvents.remove(p2, "message")
}))
}else{
p2.hash("")
}
promises.push(this.canvasTest.drawAllImages())
Promise.all(promises).then(result => {
perf.allImg = result
perf.load = Date.now() - this.startTime
this.canvasTest.clean()

View File

@ -297,7 +297,8 @@ class LoadSong{
})
p2.send("join", {
id: song.folder,
diff: song.difficulty
diff: song.difficulty,
name: account.loggedIn ? account.displayName : null
})
}else{
this.clean()

View File

@ -84,6 +84,7 @@ var strings
var vectors
var settings
var scoreStorage
var account = {}
pageEvents.add(root, ["touchstart", "touchmove", "touchend"], event => {
if(event.cancelable && cancelTouch && event.target.tagName !== "SELECT"){

View File

@ -3,6 +3,7 @@ class P2Connection{
this.closed = true
this.lastMessages = {}
this.otherConnected = false
this.name = null
this.allEvents = new Map()
this.addEventListener("message", this.message.bind(this))
this.currentHash = ""
@ -123,6 +124,7 @@ class P2Connection{
this.hash("")
this.hashLock = false
}
this.name = null
break
case "gameresults":
this.results = {}
@ -151,6 +153,9 @@ class P2Connection{
this.otherConnected = true
this.session = true
break
case "name":
this.name = (response.value || "").toString() || null
break
}
}
onhashchange(){

View File

@ -86,6 +86,9 @@ class PageEvents{
})
}
keyEvent(event){
if(!("key" in event)){
return
}
if(this.kbd.indexOf(event.key.toLowerCase()) !== -1){
this.lastKeyEvent = Date.now()
event.preventDefault()

View File

@ -39,6 +39,7 @@ class Scoresheet{
this.draw = new CanvasDraw(noSmoothing)
this.canvasCache = new CanvasCache(noSmoothing)
this.nameplateCache = new CanvasCache(noSmoothing)
this.keyboard = new Keyboard({
confirm: ["enter", "space", "esc", "don_l", "don_r"]
@ -208,6 +209,7 @@ class Scoresheet{
this.canvas.style.height = (winH / this.pixelRatio) + "px"
this.canvasCache.resize(winW / ratio, 80 + 1, ratio)
this.nameplateCache.resize(274, 134, ratio + 0.2)
if(!this.multiplayer){
this.tetsuoHana.style.setProperty("--scale", ratio / this.pixelRatio)
@ -233,6 +235,9 @@ class Scoresheet{
if(!this.canvasCache.canvas){
this.canvasCache.resize(winW / ratio, 80 + 1, ratio)
}
if(!this.nameplateCache.canvas){
this.nameplateCache.resize(274, 67, ratio + 0.2)
}
}
this.winW = winW
this.winH = winH
@ -450,6 +455,29 @@ class Scoresheet{
ctx.fillText(text, 395, 308)
ctx.miterLimit = 10
if(p === 0){
var name = account.loggedIn ? account.displayName : strings.defaultName
}else{
var name = results.name
}
this.nameplateCache.get({
ctx: ctx,
x: 259,
y: 92,
w: 273,
h: 66,
id: p.toString() + "p",
}, ctx => {
this.draw.nameplate({
ctx: ctx,
x: 3,
y: 3,
name: name,
font: this.font,
blue: p === 1
})
})
if(this.controller.autoPlayEnabled){
ctx.drawImage(assets.image["badge_auto"],
431, 311, 34, 34

View File

@ -5,17 +5,22 @@ class ScoreStorage{
this.difficulty = ["oni", "ura", "hard", "normal", "easy"]
this.scoreKeys = ["points", "good", "ok", "bad", "maxCombo", "drumroll"]
this.crownValue = ["", "silver", "gold"]
this.load()
}
load(){
load(strings){
this.scores = {}
this.scoreStrings = {}
try{
var localScores = localStorage.getItem("scoreStorage")
if(localScores){
this.scoreStrings = JSON.parse(localScores)
}
}catch(e){}
if(strings){
this.scoreStrings = strings
}else if(account.loggedIn){
return
}else{
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
@ -46,16 +51,22 @@ class ScoreStorage{
}
}
}
save(){
save(localOnly){
for(var hash in this.scores){
this.writeString(hash)
}
this.write()
return this.sendToServer({
scores: this.scoreStrings,
is_import: true
})
}
write(){
try{
localStorage.setItem("scoreStorage", JSON.stringify(this.scoreStrings))
}catch(e){}
if(!account.loggedIn){
try{
localStorage.setItem("scoreStorage", JSON.stringify(this.scoreStrings))
}catch(e){}
}
}
writeString(hash){
var score = this.scores[hash]
@ -112,6 +123,11 @@ class ScoreStorage{
this.scores[hash][difficulty] = scoreObject
this.writeString(hash)
this.write()
var obj = {}
obj[hash] = this.scoreStrings[hash]
this.sendToServer({
scores: obj
}).catch(() => this.add.apply(this, arguments))
}
template(){
var template = {crown: ""}
@ -146,6 +162,42 @@ class ScoreStorage{
delete this.scoreStrings[hash]
}
this.write()
this.sendToServer({
scores: this.scoreStrings,
is_import: true
})
}
}
sendToServer(obj, retry){
if(account.loggedIn){
var request = new XMLHttpRequest()
request.open("POST", "api/scores/save")
var promise = pageEvents.load(request).then(response => {
if(request.status !== 200){
return Promise.reject()
}
}).catch(() => {
if(retry){
account.loggedIn = false
delete account.username
delete account.displayName
Cookies.remove("token")
this.load()
pageEvents.send("logout")
return Promise.reject()
}else{
return new Promise(resolve => {
setTimeout(() => {
resolve()
}, 3000)
}).then(() => this.sendToServer(obj, true))
}
})
request.setRequestHeader("Content-Type", "application/json;charset=UTF-8")
request.send(JSON.stringify(obj))
return promise
}else{
return Promise.resolve()
}
}
}

View File

@ -34,7 +34,10 @@ class Session{
pageEvents.send("session-start", "host")
}
})
p2.send("invite")
p2.send("invite", {
id: null,
name: account.loggedIn ? account.displayName : null
})
pageEvents.send("session")
}
getElement(name){

View File

@ -116,7 +116,7 @@ class SongSelect{
originalTitle: song.title,
subtitle: subtitle,
skin: song.category in this.songSkin ? this.songSkin[song.category] : this.songSkin.default,
stars: song.stars,
courses: song.courses,
category: song.category,
preview: song.preview || 0,
type: song.type,
@ -126,14 +126,19 @@ class SongSelect{
volume: song.volume,
maker: song.maker,
canJump: true,
hash: song.hash || song.title
hash: song.hash || song.title,
order: song.order
})
}
this.songs.sort((a, b) => {
var catA = a.category in this.songSkin ? this.songSkin[a.category] : this.songSkin.default
var catB = b.category in this.songSkin ? this.songSkin[b.category] : this.songSkin.default
if(catA.sort === catB.sort){
return a.id > b.id ? 1 : -1
if(a.order === b.order){
return a.id > b.id ? 1 : -1
}else{
return a.order > b.order ? 1 : -1
}
}else{
return catA.sort > catB.sort ? 1 : -1
}
@ -226,6 +231,7 @@ class SongSelect{
this.difficultyCache = new CanvasCache(noSmoothing)
this.sessionCache = new CanvasCache(noSmoothing)
this.currentSongCache = new CanvasCache(noSmoothing)
this.nameplateCache = new CanvasCache(noSmoothing)
this.difficulty = [strings.easy, strings.normal, strings.hard, strings.oni]
this.difficultyId = ["easy", "normal", "hard", "oni", "ura"]
@ -450,7 +456,11 @@ class SongSelect{
if(this.state.screen === "song"){
if(20 < mouse.y && mouse.y < 90 && 410 < mouse.x && mouse.x < 880 && (mouse.x < 540 || mouse.x > 750)){
this.categoryJump(mouse.x < 640 ? -1 : 1)
}else if(mouse.x > 641 && mouse.y > 603){
}else if(!p2.session && 60 < mouse.x && mouse.x < 332 && 640 < mouse.y && mouse.y < 706 && gameConfig._accounts){
this.toAccount()
}else if(p2.session && 438 < mouse.x && mouse.x < 834 && mouse.y > 603){
this.toSession()
}else if(!p2.session && mouse.x > 641 && mouse.y > 603 && p2.socket.readyState === 1 && !assets.customSongs){
this.toSession()
}else{
var moveBy = this.songSelMouse(mouse.x, mouse.y)
@ -501,11 +511,15 @@ class SongSelect{
if(this.state.screen === "song"){
if(20 < mouse.y && mouse.y < 90 && 410 < mouse.x && mouse.x < 880 && (mouse.x < 540 || mouse.x > 750)){
moveTo = mouse.x < 640 ? "categoryPrev" : "categoryNext"
}else if(mouse.x > 641 && mouse.y > 603 && p2.socket.readyState === 1 && !assets.customSongs){
}else if(!p2.session && 60 < mouse.x && mouse.x < 332 && 640 < mouse.y && mouse.y < 706 && gameConfig._accounts){
moveTo = "account"
}else if(p2.session && 438 < mouse.x && mouse.x < 834 && mouse.y > 603){
moveTo = "session"
}else if(!p2.session && mouse.x > 641 && mouse.y > 603 && p2.socket.readyState === 1 && !assets.customSongs){
moveTo = "session"
}else{
var moveTo = this.songSelMouse(mouse.x, mouse.y)
if(moveTo === null && this.state.moveHover === 0 && !this.songs[this.selectedSong].stars){
if(moveTo === null && this.state.moveHover === 0 && !this.songs[this.selectedSong].courses){
this.state.moveMS = this.getMS() - this.songSelecting.speed
}
}
@ -544,7 +558,7 @@ class SongSelect{
var dir = x > 0 ? 1 : -1
x = Math.abs(x)
var selectedWidth = this.songAsset.selectedWidth
if(!this.songs[this.selectedSong].stars){
if(!this.songs[this.selectedSong].courses){
selectedWidth = this.songAsset.width
}
var moveBy = Math.ceil((x - selectedWidth / 2 - this.songAsset.marginLeft / 2) / (this.songAsset.width + this.songAsset.marginLeft)) * dir
@ -565,7 +579,13 @@ class SongSelect{
}else if(550 < x && x < 1050 && 95 < y && y < 524){
var moveBy = Math.floor((x - 550) / ((1050 - 550) / 5)) + this.diffOptions.length
var currentSong = this.songs[this.selectedSong]
if(this.state.ura && moveBy === this.diffOptions.length + 3 || currentSong.stars[moveBy - this.diffOptions.length]){
if(
this.state.ura
&& moveBy === this.diffOptions.length + 3
|| currentSong.courses[
this.difficultyId[moveBy - this.diffOptions.length]
]
){
return moveBy
}
}
@ -583,7 +603,7 @@ class SongSelect{
})
}
}else if(this.state.locked !== 1 || fromP2){
if(this.songs[this.selectedSong].stars && (this.state.locked === 0 || fromP2)){
if(this.songs[this.selectedSong].courses && (this.state.locked === 0 || fromP2)){
this.state.moveMS = ms
}else{
this.state.moveMS = ms - this.songSelecting.speed * this.songSelecting.resize
@ -645,7 +665,7 @@ class SongSelect{
toSelectDifficulty(fromP2){
var currentSong = this.songs[this.selectedSong]
if(p2.session && !fromP2 && currentSong.action !== "random"){
if(this.songs[this.selectedSong].stars){
if(this.songs[this.selectedSong].courses){
if(!this.state.selLock){
this.state.selLock = true
p2.send("songsel", {
@ -655,7 +675,7 @@ class SongSelect{
}
}
}else if(this.state.locked === 0 || fromP2){
if(currentSong.stars){
if(currentSong.courses){
this.state.screen = "difficulty"
this.state.screenMS = this.getMS()
this.state.locked = true
@ -677,7 +697,7 @@ class SongSelect{
this.state.locked = true
do{
var i = Math.floor(Math.random() * this.songs.length)
}while(!this.songs[i].stars)
}while(!this.songs[i].courses)
var moveBy = i - this.selectedSong
setTimeout(() => {
this.moveToSong(moveBy)
@ -744,17 +764,18 @@ class SongSelect{
}else if(p2.socket.readyState === 1 && !assets.customSongs){
multiplayer = ctrl
}
var diff = this.difficultyId[difficulty]
new LoadSong({
"title": selectedSong.title,
"originalTitle": selectedSong.originalTitle,
"folder": selectedSong.id,
"difficulty": this.difficultyId[difficulty],
"difficulty": diff,
"category": selectedSong.category,
"type": selectedSong.type,
"offset": selectedSong.offset,
"songSkin": selectedSong.songSkin,
"stars": selectedSong.stars[difficulty],
"stars": selectedSong.courses[diff].stars,
"hash": selectedSong.hash
}, autoplay, multiplayer, touch)
}
@ -797,6 +818,13 @@ class SongSelect{
new SettingsView(this.touchEnabled)
}, 500)
}
toAccount(){
this.playSound("se_don")
this.clean()
setTimeout(() => {
new Account(this.touchEnabled)
}, 500)
}
toSession(){
if(p2.socket.readyState !== 1 || assets.customSongs){
return
@ -893,6 +921,8 @@ class SongSelect{
var textW = strings.id === "en" ? 350 : 280
this.selectTextCache.resize((textW + 53 + 60 + 1) * 2, this.songAsset.marginTop + 15, ratio + 0.5)
this.nameplateCache.resize(274, 134, ratio + 0.2)
var categories = 0
var lastCategory
this.songs.forEach(song => {
@ -921,7 +951,7 @@ class SongSelect{
fontFamily: this.font,
x: w / 2,
y: 38 / 2,
width: w - 30,
width: id === "sessionend" ? 385 : w - 30,
align: "center",
baseline: "middle"
}, [
@ -969,7 +999,7 @@ class SongSelect{
}
if(screen === "song"){
if(this.songs[this.selectedSong].stars){
if(this.songs[this.selectedSong].courses){
selectedWidth = this.songAsset.selectedWidth
}
@ -1054,7 +1084,7 @@ class SongSelect{
if(elapsed < resize){
selectedWidth = this.songAsset.width + (((resize - elapsed) / resize) * (selectedWidth - this.songAsset.width))
}else if(elapsed > resize2){
this.playBgm(!this.songs[this.selectedSong].stars)
this.playBgm(!this.songs[this.selectedSong].courses)
this.state.locked = 1
selectedWidth = this.songAsset.width + ((elapsed - resize2) / resize * (selectedWidth - this.songAsset.width))
}else{
@ -1062,7 +1092,7 @@ class SongSelect{
selectedWidth = this.songAsset.width
}
}else{
this.playBgm(!this.songs[this.selectedSong].stars)
this.playBgm(!this.songs[this.selectedSong].courses)
this.state.locked = 0
}
}else if(screen === "difficulty"){
@ -1071,7 +1101,7 @@ class SongSelect{
this.state.locked = 0
}
if(this.state.move){
var hasUra = currentSong.stars[4]
var hasUra = currentSong.courses.ura
var previousSelection = this.selectedDiff
do{
if(hasUra && this.state.move > 0){
@ -1089,12 +1119,12 @@ class SongSelect{
this.selectedDiff = this.mod(this.diffOptions.length + 5, this.selectedDiff + this.state.move)
}
}while(
this.selectedDiff >= this.diffOptions.length && !currentSong.stars[this.selectedDiff - this.diffOptions.length]
this.selectedDiff >= this.diffOptions.length && !currentSong.courses[this.difficultyId[this.selectedDiff - this.diffOptions.length]]
|| this.selectedDiff === this.diffOptions.length + 3 && this.state.ura
|| this.selectedDiff === this.diffOptions.length + 4 && !this.state.ura
)
this.state.move = 0
}else if(this.selectedDiff < 0 || this.selectedDiff >= this.diffOptions.length && !currentSong.stars[this.selectedDiff - this.diffOptions.length]){
}else if(this.selectedDiff < 0 || this.selectedDiff >= this.diffOptions.length && !currentSong.courses[this.difficultyId[this.selectedDiff - this.diffOptions.length]]){
this.selectedDiff = 0
}
}
@ -1164,7 +1194,7 @@ class SongSelect{
var currentSong = this.songs[this.selectedSong]
var highlight = 0
if(!currentSong.stars){
if(!currentSong.courses){
highlight = 2
}
if(this.state.moveHover === 0){
@ -1418,7 +1448,7 @@ class SongSelect{
}
}
var drawDifficulty = (ctx, i, currentUra) => {
if(currentSong.stars[i] || currentUra){
if(currentSong.courses[this.difficultyId[i]] || currentUra){
var score = scoreStorage.get(currentSong.hash, false, true)
var crownDiff = currentUra ? "ura" : this.difficultyId[i]
var crownType = ""
@ -1502,9 +1532,9 @@ class SongSelect{
outlineSize: currentUra ? this.songAsset.letterBorder : 0
})
})
var songStarsArray = (currentUra ? currentSong.stars[4] : currentSong.stars[i]).toString().split(" ")
var songStars = songStarsArray[0]
var songBranch = songStarsArray[1] === "B"
var songStarsObj = (currentUra ? currentSong.courses.ura : currentSong.courses[this.difficultyId[i]])
var songStars = songStarsObj.stars
var songBranch = songStarsObj.branch
var elapsedMS = this.state.screenMS > this.state.moveMS || !songSel ? this.state.screenMS : this.state.moveMS
var fade = ((ms - elapsedMS) % 2000) / 2000
if(songBranch && fade > 0.25 && fade < 0.75){
@ -1591,8 +1621,8 @@ class SongSelect{
}
}
}
for(var i = 0; currentSong.stars && i < 4; i++){
var currentUra = i === 3 && (this.state.ura && !songSel || currentSong.stars[4] && songSel)
for(var i = 0; currentSong.courses && i < 4; i++){
var currentUra = i === 3 && (this.state.ura && !songSel || currentSong.courses.ura && songSel)
if(songSel && currentUra){
drawDifficulty(ctx, i, false)
var elapsedMS = this.state.screenMS > this.state.moveMS ? this.state.screenMS : this.state.moveMS
@ -1753,7 +1783,7 @@ class SongSelect{
}
}
if(!songSel && currentSong.stars[4]){
if(!songSel && currentSong.courses.ura){
var fade = ((ms - this.state.screenMS) % 1200) / 1200
var _x = x + 402 + 4 * 100 + fade * 25
var _y = y + 258
@ -1842,7 +1872,7 @@ class SongSelect{
ctx.fillRect(0, frameTop + 595, 1280 + frameLeft * 2, 125 + frameTop)
var x = 0
var y = frameTop + 603
var w = frameLeft + 638
var w = p2.session ? frameLeft + 638 - 200 : frameLeft + 638
var h = 117 + frameTop
this.draw.pattern({
ctx: ctx,
@ -1869,7 +1899,81 @@ class SongSelect{
ctx.lineTo(x + w - 4, y + h)
ctx.lineTo(x + w - 4, y + 4)
ctx.fill()
x = frameLeft + 642
this.nameplateCache.get({
ctx: ctx,
x: frameLeft + 60,
y: frameTop + 640,
w: 273,
h: 66,
id: "1p",
}, ctx => {
this.draw.nameplate({
ctx: ctx,
x: 3,
y: 3,
name: account.loggedIn ? account.displayName : strings.defaultName,
rank: account.loggedIn || !gameConfig._accounts || p2.session ? false : strings.notLoggedIn,
font: this.font
})
})
if(this.state.moveHover === "account"){
this.draw.highlight({
ctx: ctx,
x: frameLeft + 59.5,
y: frameTop + 639.5,
w: 271,
h: 64,
radius: 28.5,
opacity: 0.8,
size: 10
})
}
if(p2.session){
x = x + w + 4
w = 396
this.draw.pattern({
ctx: ctx,
img: assets.image["bg_settings"],
x: x,
y: y,
w: w,
h: h,
dx: frameLeft + 11,
dy: frameTop + 45,
scale: 3.1
})
ctx.fillStyle = "rgba(255, 255, 255, 0.5)"
ctx.beginPath()
ctx.moveTo(x, y + h)
ctx.lineTo(x, y)
ctx.lineTo(x + w, y)
ctx.lineTo(x + w, y + 4)
ctx.lineTo(x + 4, y + 4)
ctx.lineTo(x + 4, y + h)
ctx.fill()
ctx.fillStyle = "rgba(0, 0, 0, 0.25)"
ctx.beginPath()
ctx.moveTo(x + w, y)
ctx.lineTo(x + w, y + h)
ctx.lineTo(x + w - 4, y + h)
ctx.lineTo(x + w - 4, y + 4)
ctx.fill()
if(this.state.moveHover === "session"){
this.draw.highlight({
ctx: ctx,
x: x,
y: y,
w: w,
h: h,
opacity: 0.8
})
}
}
x = p2.session ? frameLeft + 642 + 200 : frameLeft + 642
w = p2.session ? frameLeft + 638 - 200 : frameLeft + 638
if(p2.session){
this.draw.pattern({
ctx: ctx,
@ -1925,7 +2029,7 @@ class SongSelect{
}
this.sessionCache.get({
ctx: ctx,
x: winW / 2,
x: p2.session ? winW / 4 : winW / 2,
y: y + (h - 32) / 2,
w: winW / 2,
h: 38,
@ -1933,7 +2037,7 @@ class SongSelect{
})
ctx.globalAlpha = 1
}
if(this.state.moveHover === "session"){
if(!p2.session && this.state.moveHover === "session"){
this.draw.highlight({
ctx: ctx,
x: x,
@ -1944,6 +2048,25 @@ class SongSelect{
})
}
}
if(p2.session){
this.nameplateCache.get({
ctx: ctx,
x: frameLeft + 949,
y: frameTop + 640,
w: 273,
h: 66,
id: "2p",
}, ctx => {
this.draw.nameplate({
ctx: ctx,
x: 3,
y: 3,
name: p2.name,
font: this.font,
blue: true
})
})
}
if(screen === "titleFadeIn"){
ctx.save()
@ -2019,7 +2142,7 @@ class SongSelect{
if(!score){
break
}
if(config.song.stars[i] && score[diff] && score[diff].crown){
if(config.song.courses[this.difficultyId[i]] && score[diff] && score[diff].crown){
this.draw.crown({
ctx: ctx,
type: score[diff].crown,
@ -2148,7 +2271,7 @@ class SongSelect{
})
if(currentSong){
currentSong.p2Cursor = diffId
if(p2.session && currentSong.stars){
if(p2.session && currentSong.courses){
this.selectedSong = index
this.state.move = 0
if(this.state.screen !== "difficulty"){
@ -2192,7 +2315,7 @@ class SongSelect{
}
this.moveToSong(moveBy, true)
}
}else if(this.songs[song].stars){
}else if(this.songs[song].courses){
this.selectedSong = song
this.state.move = 0
if(this.state.screen !== "difficulty"){
@ -2238,16 +2361,11 @@ class SongSelect{
getLocalTitle(title, titleLang){
if(titleLang){
titleLang = titleLang.split("\n")
titleLang.forEach(line => {
var space = line.indexOf(" ")
var id = line.slice(0, space)
if(id === strings.id){
title = line.slice(space + 1)
}else if(titleLang.length === 1 && strings.id === "en" && !(id in allStrings)){
title = line
for(var id in titleLang){
if(id === strings.id && titleLang[id]){
return titleLang[id]
}
})
}
}
return title
}

View File

@ -36,6 +36,8 @@
this.hard = "むずかしい"
this.oni = "おに"
this.songBranch = "譜面分岐あり"
this.defaultName = "どんちゃん"
this.notLoggedIn = "ログインしていない"
this.sessionStart = "オンラインセッションを開始する!"
this.sessionEnd = "オンラインセッションを終了する"
this.loading = "ロード中..."
@ -184,6 +186,24 @@
content: "Audio latency: %s\nVideo latency: %s\n\nYou can configure these latency values in the settings."
}
}
this.account = {
username: "ユーザー名",
enterUsername: "ユーザー名を入力",
password: "パスワード",
enterPassword: "パスワードを入力",
repeatPassword: "パスワードを再入力",
remember: "ログイン状態を保持する",
login: "ログイン",
register: "登録",
registerAccount: "アカウントを登録",
passwordsDoNotMatch: "パスワードが一致しません",
cannotBeEmpty: "%sは空にできません",
error: "リクエストの処理中にエラーが発生しました",
logout: "ログアウト",
back: "もどる",
cancel: "Cancel",
save: "Save"
}
this.browserSupport = {
browserWarning: "サポートされていないブラウザを実行しています (%s)",
details: "詳しく",
@ -233,6 +253,8 @@ function StringsEn(){
this.hard = "Hard"
this.oni = "Extreme"
this.songBranch = "Diverge Notes"
this.defaultName = "Don-chan"
this.notLoggedIn = "Not logged in"
this.sessionStart = "Begin an Online Session!"
this.sessionEnd = "End Online Session"
this.loading = "Loading..."
@ -381,6 +403,33 @@ function StringsEn(){
content: "Audio latency: %s\nVideo latency: %s\n\nYou can configure these latency values in the settings."
}
}
this.account = {
username: "Username",
enterUsername: "Enter Username",
password: "Password",
enterPassword: "Enter Password",
repeatPassword: "Repeat Password",
remember: "Remember me",
login: "Log In",
register: "Register",
registerAccount: "Register account",
passwordsDoNotMatch: "Passwords do not match",
cannotBeEmpty: "%s cannot be empty",
error: "An error occurred while processing your request",
logout: "Log Out",
back: "Back",
cancel: "Cancel",
save: "Save",
displayName: "Displayed Name",
changePassword: "Change Password",
currentNewRepeat: [
"Current Password",
"New Password",
"Repeat New Password"
],
deleteAccount: "Delete Account",
verifyPassword: "Verify password to delete this account"
}
this.browserSupport = {
browserWarning: "You are running an unsupported browser (%s)",
details: "Details...",
@ -430,6 +479,8 @@ function StringsCn(){
this.hard = "困难"
this.oni = "魔王"
this.songBranch = "有谱面分歧"
this.defaultName = "小咚"
this.notLoggedIn = "未登录"
this.sessionStart = "开始在线会话!"
this.sessionEnd = "结束在线会话"
this.loading = "加载中..."
@ -578,6 +629,24 @@ function StringsCn(){
content: "Audio latency: %s\nVideo latency: %s\n\nYou can configure these latency values in the settings."
}
}
this.account = {
username: "登录名",
enterUsername: "输入用户名",
password: "密码",
enterPassword: "输入密码",
repeatPassword: "重新输入密码",
remember: "记住登录",
login: "登录",
register: "注册",
registerAccount: "注册帐号",
passwordsDoNotMatch: "密码不匹配",
cannotBeEmpty: "%s不能为空",
error: "处理您的请求时发生错误",
logout: "登出",
back: "返回",
cancel: "Cancel",
save: "Save"
}
this.browserSupport = {
browserWarning: "You are running an unsupported browser (%s)",
details: "Details...",
@ -627,6 +696,8 @@ function StringsTw(){
this.hard = "困難"
this.oni = "魔王"
this.songBranch = "有譜面分歧"
this.defaultName = "小咚"
this.notLoggedIn = "未登錄"
this.sessionStart = "開始多人模式!"
this.sessionEnd = "結束多人模式"
this.loading = "讀取中..."
@ -775,6 +846,24 @@ function StringsTw(){
content: "Audio latency: %s\nVideo latency: %s\n\nYou can configure these latency values in the settings."
}
}
this.account = {
username: "使用者名稱",
enterUsername: "輸入用戶名",
password: "密碼",
enterPassword: "輸入密碼",
repeatPassword: "再次輸入密碼",
remember: "記住登錄",
login: "登入",
register: "註冊",
registerAccount: "註冊帳號",
passwordsDoNotMatch: "密碼不匹配",
cannotBeEmpty: "%s不能為空",
error: "處理您的請求時發生錯誤",
logout: "登出",
back: "返回",
cancel: "Cancel",
save: "Save"
}
this.browserSupport = {
browserWarning: "You are running an unsupported browser (%s)",
details: "Details...",
@ -824,6 +913,8 @@ function StringsKo(){
this.hard = "어려움"
this.oni = "귀신"
this.songBranch = "악보 분기 있습니다"
this.defaultName = "동이"
this.notLoggedIn = "로그인하지 않았습니다"
this.sessionStart = "온라인 세션 시작!"
this.sessionEnd = "온라인 세션 끝내기"
this.loading = "로딩 중..."
@ -972,6 +1063,24 @@ function StringsKo(){
content: "Audio latency: %s\nVideo latency: %s\n\nYou can configure these latency values in the settings."
}
}
this.account = {
username: "사용자 이름",
enterUsername: "사용자 이름을 입력하십시오",
password: "비밀번호",
enterPassword: "비밀번호 입력",
repeatPassword: "비밀번호 재입력",
remember: "자동 로그인",
login: "로그인",
register: "가입하기",
registerAccount: "계정 등록",
passwordsDoNotMatch: "비밀번호가 일치하지 않습니다",
cannotBeEmpty: "%s 비어 있을 수 없습니다",
error: "요청을 처리하는 동안 오류가 발생했습니다",
logout: "로그 아웃",
back: "돌아간다",
cancel: "Cancel",
save: "Save"
}
this.browserSupport = {
browserWarning: "You are running an unsupported browser (%s)",
details: "Details...",

View File

@ -126,6 +126,7 @@
this.comboCache = new CanvasCache(noSmoothing)
this.pauseCache = new CanvasCache(noSmoothing)
this.branchCache = new CanvasCache(noSmoothing)
this.nameplateCache = new CanvasCache(noSmoothing)
this.multiplayer = this.controller.multiplayer
@ -235,6 +236,11 @@
if(!this.multiplayer){
this.pauseCache.resize(81 * this.pauseOptions.length * 2, 464, ratio)
}
if(this.portrait){
this.nameplateCache.resize(220, 54, ratio + 0.2)
}else{
this.nameplateCache.resize(274, 67, ratio + 0.2)
}
this.fillComboCache()
this.setDonBgHeight()
resized = true
@ -388,6 +394,32 @@
h: 130
}
if(this.multiplayer !== 2){
this.nameplateCache.get({
ctx: ctx,
x: 167,
y: 160,
w: 219,
h: 53,
id: "1p",
}, ctx => {
if(this.multiplayer === 2){
var name = p2.name || strings.defaultName
}else{
var name = account.loggedIn ? account.displayName : strings.defaultName
}
this.draw.nameplate({
ctx: ctx,
x: 3,
y: 3,
scale: 0.8,
name: name,
font: this.font,
blue: this.multiplayer === 2
})
})
}
ctx.fillStyle = "#000"
ctx.fillRect(
0,
@ -547,6 +579,29 @@
}
var taikoPos = {x: 179, y: frameTop + 190, w: 138, h: 162}
this.nameplateCache.get({
ctx: ctx,
x: 320,
y: this.multiplayer === 2 ? frameTop + 305 : frameTop + 20,
w: 273,
h: 66,
id: "1p",
}, ctx => {
if(this.multiplayer === 2){
var name = p2.name || strings.defaultName
}else{
var name = account.loggedIn ? account.displayName : strings.defaultName
}
this.draw.nameplate({
ctx: ctx,
x: 3,
y: 3,
name: name,
font: this.font,
blue: this.multiplayer === 2
})
})
ctx.fillStyle = "#000"
ctx.fillRect(
0,

View File

@ -0,0 +1,33 @@
<div class="view-outer">
<div class="view account-view">
<div class="view-title stroke-sub"></div>
<div class="view-content">
<div class="displayname-div">
<div class="displayname-hint"></div>
<input type="text" class="displayname">
</div>
<form class="accountpass-form">
<div>
<div class="accountpass-btn taibtn stroke-sub link-btn"></div>
</div>
<div class="accountpass-div">
<input type="password" name="password"><input type="password" name="newpassword" autocomplete="new-password"><input type="password" name="newpassword2" autocomplete="new-password">
</div>
</form>
<form class="accountdel-form">
<div>
<div class="accountdel-btn taibtn stroke-sub link-btn"></div>
</div>
<div class="accountdel-div">
<input type="password" name="password">
</div>
</form>
</div>
<div id="diag-txt"></div>
<div class="left-buttons">
<div class="logout-btn taibtn stroke-sub link-btn"></div>
</div>
<div class="save-btn taibtn stroke-sub selected"></div>
<div class="view-end-button taibtn stroke-sub"></div>
</div>
</div>

View File

@ -0,0 +1,24 @@
<div class="view-outer">
<div class="view">
<div class="view-title stroke-sub"></div>
<div class="view-content">
<form class="login-form">
<div class="username-hint"></div>
<input type="text" name="username" required>
<div class="password-hint"></div>
<input type="password" name="password" required>
<div class="password2-div"></div>
<div class="remember-div">
<label class="remember-label">
<input type="checkbox" checked="checked" name="remember">
</label>
</div>
<div class="login-btn taibtn stroke-sub link-btn"></div>
</form>
</div>
<div class="left-buttons">
<div class="register-btn taibtn stroke-sub link-btn"></div>
</div>
<div class="view-end-button taibtn stroke-sub selected"></div>
</div>
</div>

View File

@ -42,7 +42,8 @@ async def connection(ws, path):
user = {
"ws": ws,
"action": "ready",
"session": False
"session": False,
"name": None
}
server_status["users"].append(user)
try:
@ -79,6 +80,7 @@ async def connection(ws, path):
waiting = server_status["waiting"]
id = value["id"] if "id" in value else None
diff = value["diff"] if "diff" in value else None
user["name"] = value["name"] if "name" in value else None
if not id or not diff:
continue
if id not in waiting:
@ -92,6 +94,7 @@ async def connection(ws, path):
await ws.send(msgobj("waiting"))
else:
# Join the other user and start game
user["name"] = value["name"] if "name" in value else None
user["other_user"] = waiting[id]["user"]
waiting_diff = waiting[id]["diff"]
del waiting[id]
@ -101,7 +104,9 @@ async def connection(ws, path):
user["other_user"]["other_user"] = user
await asyncio.wait([
ws.send(msgobj("gameload", waiting_diff)),
user["other_user"]["ws"].send(msgobj("gameload", diff))
user["other_user"]["ws"].send(msgobj("gameload", diff)),
ws.send(msgobj("name", user["other_user"]["name"])),
user["other_user"]["ws"].send(msgobj("name", user["name"]))
])
else:
# Wait for another user
@ -116,27 +121,31 @@ async def connection(ws, path):
# Update others on waiting players
await notify_status()
elif type == "invite":
if value == None:
if value and "id" in value and value["id"] == None:
# Session invite link requested
invite = get_invite()
server_status["invites"][invite] = user
user["action"] = "invite"
user["session"] = invite
user["name"] = value["name"] if "name" in value else None
await ws.send(msgobj("invite", invite))
elif value in server_status["invites"]:
elif value and "id" in value and value["id"] in server_status["invites"]:
# Join a session with the other user
user["other_user"] = server_status["invites"][value]
del server_status["invites"][value]
user["name"] = value["name"] if "name" in value else None
user["other_user"] = server_status["invites"][value["id"]]
del server_status["invites"][value["id"]]
if "ws" in user["other_user"]:
user["other_user"]["other_user"] = user
user["action"] = "invite"
user["session"] = value
user["session"] = value["id"]
sent_msg = msgobj("session")
await asyncio.wait([
ws.send(sent_msg),
user["other_user"]["ws"].send(sent_msg)
user["other_user"]["ws"].send(sent_msg),
ws.send(msgobj("invite")),
ws.send(msgobj("name", user["other_user"]["name"])),
user["other_user"]["ws"].send(msgobj("name", user["name"]))
])
await ws.send(msgobj("invite"))
else:
del user["other_user"]
await ws.send(msgobj("gameend"))