Multiplayer improvements and reupload score on error

- In multiplayer you will play at the bottom if you joined second
- Add a dialog to song select that score has not been saved
- Uploads the score when logged in again
- Translate error messages from the server
This commit is contained in:
LoveEevee 2020-03-14 07:50:04 +03:00
parent ad62ac800c
commit 7f1bb9d357
21 changed files with 543 additions and 211 deletions

28
app.py
View File

@ -190,15 +190,15 @@ def route_api_config():
@app.route('/api/register', methods=['POST'])
def route_api_register():
if session.get('username'):
return api_error('already_logged_in')
data = request.get_json()
if not schema.validate(data, schema.register):
return abort(400)
if session.get('username'):
session.clear()
username = data.get('username', '')
if len(username) > 20 or not re.match('^[a-zA-Z0-9_]{1,20}$', username):
if len(username) < 3 or len(username) > 20 or not re.match('^[a-zA-Z0-9_]{3,20}$', username):
return api_error('invalid_username')
if db.users.find_one({'username_lower': username.lower()}):
@ -226,13 +226,13 @@ def route_api_register():
@app.route('/api/login', methods=['POST'])
def route_api_login():
if session.get('username'):
return api_error('already_logged_in')
data = request.get_json()
if not schema.validate(data, schema.login):
return abort(400)
if session.get('username'):
session.clear()
username = data.get('username', '')
result = db.users.find_one({'username_lower': username.lower()})
if not result:
@ -263,15 +263,17 @@ def route_api_account_display_name():
if not schema.validate(data, schema.update_display_name):
return abort(400)
display_name = data.get('display_name', '')
if not display_name or len(display_name) > 20:
display_name = data.get('display_name', '').strip()
if not display_name:
display_name = session.get('username')
elif len(display_name) > 25:
return api_error('invalid_display_name')
db.users.update_one({'username': session.get('username')}, {
'$set': {'display_name': display_name}
})
return jsonify({'status': 'ok'})
return jsonify({'status': 'ok', 'display_name': display_name})
@app.route('/api/account/password', methods=['POST'])
@ -287,8 +289,8 @@ def route_api_account_password():
return api_error('current_password_invalid')
new_password = data.get('new_password', '').encode('utf-8')
if not 8 <= len(new_password) <= 5000:
return api_error('invalid_password')
if not 6 <= len(new_password) <= 5000:
return api_error('invalid_new_password')
salt = bcrypt.gensalt()
hashed = bcrypt.hashpw(new_password, salt)
@ -310,7 +312,7 @@ def route_api_account_remove():
user = db.users.find_one({'username': session.get('username')})
password = data.get('password', '').encode('utf-8')
if not bcrypt.checkpw(password, user['password']):
return api_error('current_password_invalid')
return api_error('verify_password_invalid')
db.scores.delete_many({'username': session.get('username')})
db.users.delete_one({'username': session.get('username')})

View File

@ -2,5 +2,5 @@
"songs_baseurl": "",
"assets_baseurl": "",
"email": "",
"_accounts": true
"accounts": true
}

View File

@ -368,3 +368,11 @@ kbd{
.accountdel-form{
margin: 0.3em auto;
}
.view-content .error-div{
display: none;
width: 80%;
margin: 0 auto;
padding: 0.5em;
font-size: 1.1em;
color: #d00;
}

View File

@ -35,6 +35,7 @@ class Account{
this.inputForms = []
this.shownDiv = ""
this.errorDiv = this.getElement("error-div")
this.getElement("displayname-hint").innerText = strings.account.displayName
this.displayname = this.getElement("displayname")
this.displayname.placeholder = strings.account.displayName
@ -116,6 +117,8 @@ class Account{
this.mode = register ? "register" : "login"
this.setAltText(this.getElement("view-title"), strings.account[this.mode])
this.errorDiv = this.getElement("error-div")
this.items = []
this.form = this.getElement("login-form")
this.getElement("username-hint").innerText = strings.account.username
@ -239,14 +242,14 @@ class Account{
password: this.form.password.value
}
if(!obj.username || !obj.password){
alert(strings.account.cannotBeEmpty.replace("%s", strings.account[!obj.username ? "username" : "password"]))
this.error(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)
this.error(strings.account.passwordsDoNotMatch)
return
}
}
@ -260,7 +263,7 @@ class Account{
pageEvents.send("login", account.username)
}
if(this.mode === "login"){
this.request("scores/get").then(response => {
this.request("scores/get", false, true).then(response => {
loadScores(response.scores)
}, () => {
loadScores({})
@ -273,9 +276,13 @@ class Account{
}
}, response => {
if(response && response.status === "error" && response.message){
alert(response.message)
if(response.message in strings.serverError){
this.error(strings.serverError[response.message])
}else{
this.error(response.message)
}
}else{
alert(strings.account.error)
this.error(strings.account.error)
}
})
}
@ -293,17 +300,12 @@ class Account{
account.loggedIn = false
delete account.username
delete account.displayName
var loadScores = scores => {
Cookies.remove("session")
var loadScores = () => {
scoreStorage.load()
this.onEnd(false, true)
pageEvents.send("logout")
}
this.request("logout").then(response => {
loadScores()
}, () => {
loadScores()
})
this.request("logout").then(loadScores, loadScores)
}
onSave(event){
if(event){
@ -316,6 +318,7 @@ class Account{
if(this.locked){
return
}
this.clearError()
var promises = []
var noNameChange = false
if(this.shownDiv === "pass"){
@ -329,7 +332,7 @@ class Account{
new_password: passwords[1]
}))
}else{
alert(strings.account.passwordsDoNotMatch)
this.error(strings.account.newPasswordsDoNotMatch)
return
}
}
@ -341,7 +344,6 @@ class Account{
account.loggedIn = false
delete account.username
delete account.displayName
Cookies.remove("session")
scoreStorage.load()
pageEvents.send("logout")
return Promise.resolve
@ -351,8 +353,8 @@ class Account{
if(!noNameChange && newName !== account.displayName){
promises.push(this.request("account/display_name", {
display_name: newName
}).then(() => {
account.displayName = newName
}).then(response => {
account.displayName = response.display_name
}))
}
var error = false
@ -361,9 +363,13 @@ class Account{
return
}
if(response && response.message){
alert(response.message)
if(response.message in strings.serverError){
this.error(strings.serverError[response.message])
}else{
this.error(response.message)
}
}else{
alert(strings.account.error)
this.error(strings.account.error)
}
}
Promise.all(promises).then(() => {
@ -389,11 +395,11 @@ class Account{
new SongSelect(false, false, touched)
}, 500)
}
request(url, obj){
request(url, obj, get){
this.lock(true)
return new Promise((resolve, reject) => {
var request = new XMLHttpRequest()
request.open("POST", "api/" + url)
request.open(get ? "GET" : "POST", "api/" + url)
pageEvents.load(request).then(() => {
this.lock(false)
if(request.status !== 200){
@ -435,6 +441,14 @@ class Account{
}
}
}
error(text){
this.errorDiv.innerText = text
this.errorDiv.style.display = "block"
}
clearError(){
this.errorDiv.innerText = ""
this.errorDiv.style.display = ""
}
clean(eventsOnly, noReset){
if(!eventsOnly){
cancelTouch = true
@ -442,6 +456,10 @@ class Account{
this.gamepad.clean()
}
if(this.mode === "account"){
if(!noReset){
this.accountPass.reset()
this.accountDel.reset()
}
pageEvents.remove(this.accounPassButton, ["click", "touchstart"])
pageEvents.remove(this.accountDelButton, ["click", "touchstart"])
pageEvents.remove(this.logoutButton, ["mousedown", "touchstart"])
@ -449,10 +467,7 @@ class Account{
for(var i = 0; i < this.inputForms.length; i++){
pageEvents.remove(this.inputForms[i], ["keydown", "keyup", "keypress"])
}
if(!noReset){
this.accountPass.reset()
this.accountDel.reset()
}
delete this.errorDiv
delete this.displayname
delete this.accountPassButton
delete this.accountPass
@ -473,6 +488,7 @@ class Account{
for(var i = 0; i < this.form.length; i++){
pageEvents.remove(this.registerButton, ["keydown", "keyup", "keypress"])
}
delete this.errorDiv
delete this.form
delete this.password2
delete this.remember

View File

@ -1,7 +1,6 @@
var assets = {
"js": [
"lib/md5.min.js",
"lib/js.cookie.min.js",
"loadsong.js",
"parseosu.js",
"titlescreen.js",

View File

@ -155,10 +155,16 @@ class Controller{
if(this.mainLoopRunning){
if(this.multiplayer !== 2){
requestAnimationFrame(() => {
this.viewLoop()
var player = this.multiplayer ? p2.player : 1
if(player === 1){
this.viewLoop()
}
if(this.multiplayer === 1){
this.syncWith.viewLoop()
}
if(player === 2){
this.viewLoop()
}
if(this.scoresheet){
if(this.view.ctx){
this.view.ctx.save()
@ -197,14 +203,14 @@ class Controller{
displayScore(score, notPlayed, bigNote){
this.view.displayScore(score, notPlayed, bigNote)
}
songSelection(fadeIn){
songSelection(fadeIn, scoreSaveFailed){
if(!fadeIn){
this.clean()
}
if(this.calibrationMode){
new SettingsView(this.touchEnabled, false, null, "latency")
}else{
new SongSelect(false, fadeIn, this.touchEnabled)
new SongSelect(false, fadeIn, this.touchEnabled, null, scoreSaveFailed)
}
}
restartSong(){

View File

@ -506,7 +506,7 @@ class Game{
if(this.musicFadeOut === 0){
if(this.controller.multiplayer === 1){
var obj = this.getGlobalScore()
obj.name = account.loggedIn ? account.displayName : strings.defaultName
obj.name = account.loggedIn ? account.displayName : null
p2.send("gameresults", obj)
}
this.musicFadeOut++

View File

@ -1,2 +0,0 @@
/*! 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

@ -109,7 +109,7 @@ class Loader{
assets.audioMusic.length +
assets.audioSfxLR.length +
assets.audioSfxLoud.length +
(gameConfig._accounts ? 1 : 0)
(gameConfig.accounts ? 1 : 0)
Promise.all(this.promises).then(() => {
@ -156,22 +156,17 @@ class Loader{
}
}))
if(gameConfig._accounts){
var token = Cookies.get("session")
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()
}
if(gameConfig.accounts){
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)
}
}))
}
settings = new Settings()

View File

@ -264,14 +264,14 @@ class LoadSong{
if(event.type === "gameload"){
this.cancelButton.style.display = ""
if(event.value === song.difficulty){
if(event.value.diff === song.difficulty){
this.startMultiplayer()
}else{
this.selectedSong2 = {}
for(var i in this.selectedSong){
this.selectedSong2[i] = this.selectedSong[i]
}
this.selectedSong2.difficulty = event.value
this.selectedSong2.difficulty = event.value.diff
if(song.type === "tja"){
this.startMultiplayer()
}else{

View File

@ -4,6 +4,7 @@ class P2Connection{
this.lastMessages = {}
this.otherConnected = false
this.name = null
this.player = 1
this.allEvents = new Map()
this.addEventListener("message", this.message.bind(this))
this.currentHash = ""
@ -103,6 +104,10 @@ class P2Connection{
}
message(response){
switch(response.type){
case "gameload":
if("player" in response.value){
this.player = response.value.player === 2 ? 2 : 1
}
case "gamestart":
this.otherConnected = true
this.notes = []
@ -129,7 +134,7 @@ class P2Connection{
case "gameresults":
this.results = {}
for(var i in response.value){
this.results[i] = response.value[i].toString()
this.results[i] = response.value[i] === null ? null : response.value[i].toString()
}
break
case "note":
@ -152,9 +157,12 @@ class P2Connection{
this.clearMessage("users")
this.otherConnected = true
this.session = true
if("player" in response.value){
this.player = response.value.player === 2 ? 2 : 1
}
break
case "name":
this.name = (response.value || "").toString() || null
this.name = response.value ? response.value.toString() : response.value
break
}
}

View File

@ -86,7 +86,7 @@ class PageEvents{
})
}
keyEvent(event){
if(!("key" in event)){
if(!("key" in event) || event.ctrlKey && (event.key === "c" || event.key === "x" || event.key === "v")){
return
}
if(this.kbd.indexOf(event.key.toLowerCase()) !== -1){

View File

@ -2,9 +2,19 @@ class Scoresheet{
constructor(controller, results, multiplayer, touchEnabled){
this.controller = controller
this.resultsObj = results
this.results = {}
this.player = [multiplayer ? (p2.player === 1 ? 0 : 1) : 0]
var player0 = this.player[0]
this.results = []
this.results[player0] = {}
this.rules = []
this.rules[player0] = this.controller.game.rules
if(multiplayer){
this.player.push(p2.player === 2 ? 0 : 1)
this.results[this.player[1]] = p2.results
this.rules[this.player[1]] = this.controller.syncWith.game.rules
}
for(var i in results){
this.results[i] = results[i].toString()
this.results[player0][i] = results[i] === null ? null : results[i].toString()
}
this.multiplayer = multiplayer
this.touchEnabled = touchEnabled
@ -248,7 +258,7 @@ class Scoresheet{
var frameTop = winH / 2 - 720 / 2
var frameLeft = winW / 2 - 1280 / 2
var players = this.multiplayer && p2.results ? 2 : 1
var players = this.multiplayer ? 2 : 1
var p2Offset = 298
var bgOffset = 0
@ -331,28 +341,21 @@ class Scoresheet{
}
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){
var gauge2 = this.controller.syncWith.game.rules.gaugePercent(p2.results.gauge)
if(gauge2 > gaugePercent && failedOffset !== 0 && gauge2 >= gaugeClear[1]){
var failedOffset = rules.clearReached(this.results[this.player[0]].gauge) ? 0 : -2000
if(players === 2 && failedOffset !== 0){
var p2results = this.results[this.player[1]]
if(p2results && this.controller.syncWith.game.rules.clearReached(p2results.gauge)){
failedOffset = 0
}
}
if(elapsed >= 3100 + failedOffset){
for(var p = 0; p < players; p++){
ctx.save()
var results = this.results
if(p === 1){
results = p2.results
var results = this.results[p]
if(!results){
continue
}
var playerRules = p === 0 ? rules : this.controller.syncWith.game.rules
var resultGauge = playerRules.gaugePercent(results.gauge)
var clear = resultGauge >= gaugeClear[p]
var clear = this.rules[p].clearReached(results.gauge)
if(p === 1 || !this.multiplayer && clear){
ctx.translate(0, 290)
}
@ -415,7 +418,7 @@ class Scoresheet{
this.draw.layeredText({
ctx: ctx,
text: this.results.title,
text: this.results[this.player[0]].title,
fontSize: 40,
fontFamily: this.font,
x: 1257,
@ -431,9 +434,11 @@ class Scoresheet{
ctx.save()
for(var p = 0; p < players; p++){
var results = this.results
var results = this.results[p]
if(!results){
continue
}
if(p === 1){
results = p2.results
ctx.translate(0, p2Offset)
}
@ -455,10 +460,11 @@ class Scoresheet{
ctx.fillText(text, 395, 308)
ctx.miterLimit = 10
if(p === 0){
var name = account.loggedIn ? account.displayName : strings.defaultName
var defaultName = p === 0 ? strings.defaultName : strings.default2PName
if(p === this.player[0]){
var name = account.loggedIn ? account.displayName : defaultName
}else{
var name = results.name
var name = results.name || defaultName
}
this.nameplateCache.get({
ctx: ctx,
@ -466,7 +472,7 @@ class Scoresheet{
y: 92,
w: 273,
h: 66,
id: p.toString() + "p",
id: p.toString() + "p" + name,
}, ctx => {
this.draw.nameplate({
ctx: ctx,
@ -609,7 +615,7 @@ class Scoresheet{
if(this.tetsuoHanaClass){
this.tetsuoHana.classList.remove(this.tetsuoHanaClass)
}
this.tetsuoHanaClass = rules.clearReached(this.results.gauge) ? "dance" : "failed"
this.tetsuoHanaClass = this.rules[this.player[0]].clearReached(this.results[this.player[0]].gauge) ? "dance" : "failed"
this.tetsuoHana.classList.add(this.tetsuoHanaClass)
}
}
@ -623,32 +629,32 @@ class Scoresheet{
ctx.translate(frameLeft, frameTop)
for(var p = 0; p < players; p++){
var results = this.results
var results = this.results[p]
if(!results){
continue
}
if(p === 1){
results = p2.results
ctx.translate(0, p2Offset)
}
var gaugePercent = rules.gaugePercent(results.gauge)
var w = 712
this.draw.gauge({
ctx: ctx,
x: 558 + w,
y: p === 1 ? 124 : 116,
clear: gaugeClear[p],
percentage: gaugePercent,
clear: this.rules[p].gaugeClear,
percentage: this.rules[p].gaugePercent(results.gauge),
font: this.font,
scale: w / 788,
scoresheet: true,
blue: p === 1,
multiplayer: p === 1
})
var playerRules = p === 0 ? rules : this.controller.syncWith.game.rules
this.draw.soul({
ctx: ctx,
x: 1215,
y: 144,
scale: 36 / 42,
cleared: playerRules.clearReached(results.gauge)
cleared: this.rules[p].clearReached(results.gauge)
})
}
})
@ -661,13 +667,12 @@ class Scoresheet{
var noCrownResultWait = -2000;
for(var p = 0; p < players; p++){
var results = this.results
if(p === 1){
results = p2.results
var results = this.results[p]
if(!results){
continue
}
var crownType = null
var playerRules = p === 0 ? rules : this.controller.syncWith.game.rules
if(playerRules.clearReached(results.gauge)){
if(this.rules[p].clearReached(results.gauge)){
crownType = results.bad === "0" ? "gold" : "silver"
}
if(crownType !== null){
@ -730,7 +735,10 @@ class Scoresheet{
var times = {}
var lastTime = 0
for(var p = 0; p < players; p++){
var results = p === 0 ? this.results : p2.results
var results = this.results[p]
if(!results){
continue
}
var currentTime = 3100 + noCrownResultWait + results.points.length * 30 * this.frame
if(currentTime > lastTime){
lastTime = currentTime
@ -739,7 +747,10 @@ class Scoresheet{
for(var i in printNumbers){
var largestTime = 0
for(var p = 0; p < players; p++){
var results = p === 0 ? this.results : p2.results
var results = this.results[p]
if(!results){
continue
}
times[printNumbers[i]] = lastTime + 500
var currentTime = lastTime + 500 + results[printNumbers[i]].length * 30 * this.frame
if(currentTime > largestTime){
@ -755,9 +766,11 @@ class Scoresheet{
}
for(var p = 0; p < players; p++){
var results = this.results
var results = this.results[p]
if(!results){
continue
}
if(p === 1){
results = p2.results
ctx.translate(0, p2Offset)
}
ctx.save()
@ -851,7 +864,7 @@ class Scoresheet{
if(elapsed >= 1000){
this.clean()
this.controller.songSelection(true)
this.controller.songSelection(true, this.scoreSaveFailed)
}
}
@ -918,10 +931,14 @@ class Scoresheet{
delete this.resultsObj.title
delete this.resultsObj.difficulty
delete this.resultsObj.gauge
scoreStorage.add(hash, difficulty, this.resultsObj, true, title)
scoreStorage.add(hash, difficulty, this.resultsObj, true, title).catch(() => {
this.scoreSaveFailed = true
})
}else if(oldScore && (crown === "gold" && oldScore.crown !== "gold" || crown && !oldScore.crown)){
oldScore.crown = crown
scoreStorage.add(hash, difficulty, oldScore, true, title)
scoreStorage.add(hash, difficulty, oldScore, true, title).catch(() => {
this.scoreSaveFailed = true
})
}
}
this.scoreSaved = true
@ -936,7 +953,7 @@ class Scoresheet{
snd.buffer.loadSettings()
this.redrawRunning = false
pageEvents.remove(this.canvas, ["mousedown", "touchstart"])
if(this.multiplayer !== 2 && this.touchEnabled){
if(this.touchEnabled){
pageEvents.remove(document.getElementById("touch-full-btn"), "touchend")
}
if(this.session){
@ -948,5 +965,7 @@ class Scoresheet{
delete this.ctx
delete this.canvas
delete this.fadeScreen
delete this.results
delete this.rules
}
}

View File

@ -6,23 +6,30 @@ class ScoreStorage{
this.scoreKeys = ["points", "good", "ok", "bad", "maxCombo", "drumroll"]
this.crownValue = ["", "silver", "gold"]
}
load(strings){
this.scores = {}
if(strings){
this.scoreStrings = this.prepareStrings(strings)
load(strings, loadFailed){
var scores = {}
var scoreStrings = {}
if(loadFailed){
try{
var localScores = localStorage.getItem("saveFailed")
if(localScores){
scoreStrings = JSON.parse(localScores)
}
}catch(e){}
}else if(strings){
scoreStrings = this.prepareStrings(strings)
}else if(account.loggedIn){
return
}else{
this.scoreStrings = {}
try{
var localScores = localStorage.getItem("scoreStorage")
if(localScores){
this.scoreStrings = JSON.parse(localScores)
scoreStrings = JSON.parse(localScores)
}
}catch(e){}
}
for(var hash in this.scoreStrings){
var scoreString = this.scoreStrings[hash]
for(var hash in scoreStrings){
var scoreString = scoreStrings[hash]
var songAdded = false
if(typeof scoreString === "string" && scoreString){
var diffArray = scoreString.split(";")
@ -42,14 +49,32 @@ class ScoreStorage{
score[name] = value
}
if(!songAdded){
this.scores[hash] = {title: null}
scores[hash] = {title: null}
songAdded = true
}
this.scores[hash][this.difficulty[i]] = score
scores[hash][this.difficulty[i]] = score
}
}
}
}
if(loadFailed){
for(var hash in scores){
for(var i in this.difficulty){
var diff = this.difficulty[i]
if(scores[hash][diff]){
this.add(hash, diff, scores[hash][diff], true, this.songTitles[hash] || null).then(() => {
localStorage.removeItem("saveFailed")
}, () => {})
}
}
}
}else{
this.scores = scores
this.scoreStrings = scoreStrings
}
if(strings){
this.load(false, true)
}
}
prepareScores(scores){
var output = []
@ -126,7 +151,7 @@ class ScoreStorage{
}
}
}
add(song, difficulty, scoreObject, isHash, setTitle){
add(song, difficulty, scoreObject, isHash, setTitle, saveFailed){
var hash = isHash ? song : this.titleHash(song)
if(!(hash in this.scores)){
this.scores[hash] = {}
@ -137,11 +162,29 @@ class ScoreStorage{
this.scores[hash][difficulty] = scoreObject
this.writeString(hash)
this.write()
var obj = {}
obj[hash] = this.scoreStrings[hash]
this.sendToServer({
scores: this.prepareScores(obj)
}).catch(() => this.add.apply(this, arguments))
if(saveFailed){
var failedScores = {}
try{
var localScores = localStorage.getItem("saveFailed")
if(localScores){
failedScores = JSON.parse(localScores)
}
}catch(e){}
if(!(hash in failedScores)){
failedScores[hash] = {}
}
failedScores[hash] = this.scoreStrings[hash]
try{
localStorage.setItem("saveFailed", JSON.stringify(failedScores))
}catch(e){}
return Promise.reject()
}else{
var obj = {}
obj[hash] = this.scoreStrings[hash]
return this.sendToServer({
scores: this.prepareScores(obj)
}).catch(() => this.add(song, difficulty, scoreObject, isHash, setTitle, true))
}
}
template(){
var template = {crown: ""}
@ -192,10 +235,10 @@ class ScoreStorage{
}
}).catch(() => {
if(retry){
this.scoreSaveFailed = true
account.loggedIn = false
delete account.username
delete account.displayName
Cookies.remove("session")
this.load()
pageEvents.send("logout")
return Promise.reject()

View File

@ -1,5 +1,5 @@
class SongSelect{
constructor(fromTutorial, fadeIn, touchEnabled, songId){
constructor(fromTutorial, fadeIn, touchEnabled, songId, scoreSaveFailed){
this.touchEnabled = touchEnabled
loader.changePage("songselect", false)
@ -167,6 +167,9 @@ class SongSelect{
category: strings.random
})
}
if(scoreSaveFailed){
scoreStorage.scoreSaveFailed = true
}
this.songs.push({
title: strings.aboutSimulator,
skin: this.songSkin.about,
@ -379,7 +382,12 @@ class SongSelect{
return
}
var shift = event ? event.shiftKey : this.pressedKeys["shift"]
if(this.state.screen === "song"){
if(this.state.scoreSaveFailed){
if(name === "confirm"){
this.playSound("se_don")
this.state.scoreSaveFailed = false
}
}else if(this.state.screen === "song"){
if(name === "confirm"){
this.toSelectDifficulty()
}else if(name === "back"){
@ -453,10 +461,15 @@ class SongSelect{
var ctrl = false
var touch = true
}
if(this.state.screen === "song"){
if(this.state.scoreSaveFailed){
if(408 < mouse.x && mouse.x < 872 && 470 < mouse.y && mouse.y < 550){
this.playSound("se_don")
this.state.scoreSaveFailed = false
}
}else 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(!p2.session && 60 < mouse.x && mouse.x < 332 && 640 < mouse.y && mouse.y < 706 && gameConfig._accounts){
}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()
@ -508,10 +521,14 @@ class SongSelect{
mouseMove(event){
var mouse = this.mouseOffset(event.offsetX, event.offsetY)
var moveTo = null
if(this.state.screen === "song"){
if(this.state.scoreSaveFailed){
if(408 < mouse.x && mouse.x < 872 && 470 < mouse.y && mouse.y < 550){
moveTo = "scoreSaveFailed"
}
}else 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(!p2.session && 60 < mouse.x && mouse.x < 332 && 640 < mouse.y && mouse.y < 706 && gameConfig._accounts){
}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"
@ -831,6 +848,7 @@ class SongSelect{
}
if(p2.session){
p2.send("gameend")
this.state.moveHover = null
}else{
localStorage["selectedSong"] = this.selectedSong
@ -992,12 +1010,22 @@ class SongSelect{
}else{
this.state.moveMS = ms - this.songSelecting.speed * this.songSelecting.resize + (ms - this.state.screenMS - 1000)
}
if(ms > this.state.screenMS + 500){
if(screen === "titleFadeIn" && ms > this.state.screenMS + 500){
this.state.screen = "title"
screen = "title"
}
}
if(screen === "song" && scoreStorage.scoreSaveFailed && !p2.session){
if(this.bgmEnabled){
this.playBgm(false)
}
scoreStorage.scoreSaveFailed = false
this.state.scoreSaveFailed = true
this.state.locked = true
this.playSound("se_pause")
}
if(screen === "song"){
if(this.songs[this.selectedSong].courses){
selectedWidth = this.songAsset.selectedWidth
@ -1441,7 +1469,8 @@ class SongSelect{
ctx: ctx,
font: this.font,
x: _x,
y: _y - 45
y: _y - 45,
two: p2.session && p2.player === 2
})
}
}
@ -1579,15 +1608,15 @@ class SongSelect{
if(this.selectedDiff === 4 + this.diffOptions.length){
currentDiff = 3
}
if(i === currentSong.p2Cursor && p2.socket.readyState === 1){
if(songSel && i === currentSong.p2Cursor && p2.socket.readyState === 1){
this.draw.diffCursor({
ctx: ctx,
font: this.font,
x: _x,
y: _y - (songSel ? 45 : 65),
two: true,
side: songSel ? false : (currentSong.p2Cursor === currentDiff),
scale: songSel ? 0.7 : 1
y: _y - 45,
two: !p2.session || p2.player === 1,
side: false,
scale: 0.7
})
}
if(!songSel){
@ -1603,7 +1632,8 @@ class SongSelect{
font: this.font,
x: _x,
y: _y - 65,
side: currentSong.p2Cursor === currentDiff && p2.socket.readyState === 1
side: currentSong.p2Cursor === currentDiff && p2.socket.readyState === 1,
two: p2.session && p2.player === 2
})
}
if(highlight){
@ -1644,6 +1674,22 @@ class SongSelect{
drawDifficulty(ctx, i, currentUra)
}
}
for(var i = 0; currentSong.courses && i < 4; i++){
if(!songSel && i === currentSong.p2Cursor && p2.socket.readyState === 1){
var _x = x + 402 + i * 100
var _y = y + 87
var currentDiff = this.selectedDiff - this.diffOptions.length
this.draw.diffCursor({
ctx: ctx,
font: this.font,
x: _x,
y: _y - 65,
two: !p2.session || p2.player === 1,
side: currentSong.p2Cursor === currentDiff,
scale: 1
})
}
}
var borders = (this.songAsset.border + this.songAsset.innerBorder) * 2
var textW = this.songAsset.width - borders
@ -1900,20 +1946,27 @@ class SongSelect{
ctx.lineTo(x + w - 4, y + 4)
ctx.fill()
if(!p2.session || p2.player === 1){
var name = account.loggedIn ? account.displayName : strings.defaultName
var rank = account.loggedIn || !gameConfig.accounts || p2.session ? false : strings.notLoggedIn
}else{
var name = p2.name || strings.defaultName
var rank = false
}
this.nameplateCache.get({
ctx: ctx,
x: frameLeft + 60,
y: frameTop + 640,
w: 273,
h: 66,
id: "1p",
id: "1p" + name + "\n" + rank,
}, 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,
name: name,
rank: rank,
font: this.font
})
})
@ -2049,25 +2102,131 @@ class SongSelect{
}
}
if(p2.session){
if(p2.player === 1){
var name = p2.name || strings.default2PName
}else{
var name = account.loggedIn ? account.displayName : strings.default2PName
}
this.nameplateCache.get({
ctx: ctx,
x: frameLeft + 949,
y: frameTop + 640,
w: 273,
h: 66,
id: "2p",
id: "2p" + name,
}, ctx => {
this.draw.nameplate({
ctx: ctx,
x: 3,
y: 3,
name: p2.name,
name: name,
font: this.font,
blue: true
})
})
}
if(this.state.scoreSaveFailed){
if(this.preview){
this.endPreview()
}
ctx.fillStyle = "rgba(0, 0, 0, 0.5)"
ctx.fillRect(0, 0, winW, winH)
ctx.save()
ctx.translate(frameLeft, frameTop)
var pauseRect = (ctx, mul) => {
this.draw.roundedRect({
ctx: ctx,
x: 269 * mul,
y: 93 * mul,
w: 742 * mul,
h: 494 * mul,
radius: 17 * mul
})
}
pauseRect(ctx, 1)
ctx.strokeStyle = "#fff"
ctx.lineWidth = 24
ctx.stroke()
ctx.strokeStyle = "#000"
ctx.lineWidth = 12
ctx.stroke()
this.draw.pattern({
ctx: ctx,
img: assets.image["bg_pause"],
shape: pauseRect,
dx: 68,
dy: 11
})
this.draw.wrappingText({
ctx: ctx,
text: strings.scoreSaveFailed,
fontSize: 30,
fontFamily: this.font,
x: 300,
y: 130,
width: 680,
height: 300,
lineHeight: 35,
fill: "#000",
verticalAlign: "middle",
textAlign: "center",
})
var _x = 640
var _y = 470
var _w = 464
var _h = 80
ctx.fillStyle = "#ffb447"
this.draw.roundedRect({
ctx: ctx,
x: _x - _w / 2,
y: _y,
w: _w,
h: _h,
radius: 30
})
ctx.fill()
var layers = [
{outline: "#000", letterBorder: 10},
{fill: "#fff"}
]
this.draw.layeredText({
ctx: ctx,
text: strings.ok,
x: _x,
y: _y + 18,
width: _w,
height: _h - 54,
fontSize: 40,
fontFamily: this.font,
letterSpacing: -1,
align: "center"
}, layers)
var highlight = 1
if(this.state.moveHover === "scoreSaveFailed"){
highlight = 2
}
if(highlight){
this.draw.highlight({
ctx: ctx,
x: _x - _w / 2 - 3.5,
y: _y - 3.5,
w: _w + 7,
h: _h + 7,
animate: highlight === 1,
animateMS: this.state.moveMS,
opacity: highlight === 2 ? 0.8 : 1,
radius: 30
})
}
ctx.restore()
}
if(screen === "titleFadeIn"){
ctx.save()
@ -2120,7 +2279,7 @@ class SongSelect{
})
}
this.draw.songFrame(config)
if(config.song.p2Cursor && p2.socket.readyState === 1){
if(config.song.p2Cursor !== null && p2.socket.readyState === 1){
this.draw.diffCursor({
ctx: ctx,
font: this.font,
@ -2167,6 +2326,9 @@ class SongSelect{
}
startPreview(loadOnly){
if(!loadOnly && this.state && this.state.scoreSaveFailed){
return
}
var currentSong = this.songs[this.selectedSong]
var id = currentSong.id
var prvTime = currentSong.preview
@ -2242,6 +2404,9 @@ class SongSelect{
}
}
playBgm(enabled){
if(enabled && this.state && this.state.scoreSaveFailed){
return
}
if(enabled && !this.bgmEnabled){
this.bgmEnabled = true
snd.musicGain.fadeIn(0.4)

View File

@ -37,6 +37,7 @@
this.oni = "おに"
this.songBranch = "譜面分岐あり"
this.defaultName = "どんちゃん"
this.default2PName = "かっちゃん"
this.notLoggedIn = "ログインしていない"
this.sessionStart = "オンラインセッションを開始する!"
this.sessionEnd = "オンラインセッションを終了する"
@ -197,6 +198,7 @@
register: "登録",
registerAccount: "アカウントを登録",
passwordsDoNotMatch: "パスワードが一致しません",
newPasswordsDoNotMatch: "New passwords do not match",
cannotBeEmpty: "%sは空にできません",
error: "リクエストの処理中にエラーが発生しました",
logout: "ログアウト",
@ -213,6 +215,17 @@
deleteAccount: "Delete Account",
verifyPassword: "Verify password to delete this account"
}
this.serverError = {
not_logged_in: "Not logged in",
invalid_username: "Invalid username, a username can only contain letters, numbers, and underscores, and must be between 3 and 20 characters long",
username_in_use: "A user already exists with that username",
invalid_password: "Cannot use this password, please check that your password is at least 6 characters long",
invalid_username_password: "Invalid Username or Password",
invalid_display_name: "Cannot use this name, please check that your new name is at most 25 characters long",
current_password_invalid: "Current password does not match",
invalid_new_password: "Cannot use this password, please check that your new password is at least 6 characters long",
verify_password_invalid: "Verification password does not match"
}
this.browserSupport = {
browserWarning: "サポートされていないブラウザを実行しています (%s)",
details: "詳しく",
@ -263,9 +276,11 @@ function StringsEn(){
this.oni = "Extreme"
this.songBranch = "Diverge Notes"
this.defaultName = "Don-chan"
this.default2PName = "Katsu-chan"
this.notLoggedIn = "Not logged in"
this.sessionStart = "Begin an Online Session!"
this.sessionEnd = "End Online Session"
this.scoreSaveFailed = "Could not connect to the server, your score has not been saved.\n\nPlease log in or refresh the page to try saving the score again."
this.loading = "Loading..."
this.waitingForP2 = "Waiting for Another Player..."
this.cancel = "Cancel"
@ -423,6 +438,7 @@ function StringsEn(){
register: "Register",
registerAccount: "Register account",
passwordsDoNotMatch: "Passwords do not match",
newPasswordsDoNotMatch: "New passwords do not match",
cannotBeEmpty: "%s cannot be empty",
error: "An error occurred while processing your request",
logout: "Log Out",
@ -439,6 +455,17 @@ function StringsEn(){
deleteAccount: "Delete Account",
verifyPassword: "Verify password to delete this account"
}
this.serverError = {
not_logged_in: "Not logged in",
invalid_username: "Invalid username, a username can only contain letters, numbers, and underscores, and must be between 3 and 20 characters long",
username_in_use: "A user already exists with that username",
invalid_password: "Cannot use this password, please check that your password is at least 6 characters long",
invalid_username_password: "Invalid Username or Password",
invalid_display_name: "Cannot use this name, please check that your new name is at most 25 characters long",
current_password_invalid: "Current password does not match",
invalid_new_password: "Cannot use this password, please check that your new password is at least 6 characters long",
verify_password_invalid: "Verification password does not match"
}
this.browserSupport = {
browserWarning: "You are running an unsupported browser (%s)",
details: "Details...",
@ -489,6 +516,7 @@ function StringsCn(){
this.oni = "魔王"
this.songBranch = "有谱面分歧"
this.defaultName = "小咚"
this.default2PName = "小咔"
this.notLoggedIn = "未登录"
this.sessionStart = "开始在线会话!"
this.sessionEnd = "结束在线会话"
@ -649,6 +677,7 @@ function StringsCn(){
register: "注册",
registerAccount: "注册帐号",
passwordsDoNotMatch: "密码不匹配",
newPasswordsDoNotMatch: "New passwords do not match",
cannotBeEmpty: "%s不能为空",
error: "处理您的请求时发生错误",
logout: "登出",
@ -665,6 +694,17 @@ function StringsCn(){
deleteAccount: "Delete Account",
verifyPassword: "Verify password to delete this account"
}
this.serverError = {
not_logged_in: "Not logged in",
invalid_username: "Invalid username, a username can only contain letters, numbers, and underscores, and must be between 3 and 20 characters long",
username_in_use: "A user already exists with that username",
invalid_password: "Cannot use this password, please check that your password is at least 6 characters long",
invalid_username_password: "Invalid Username or Password",
invalid_display_name: "Cannot use this name, please check that your new name is at most 25 characters long",
current_password_invalid: "Current password does not match",
invalid_new_password: "Cannot use this password, please check that your new password is at least 6 characters long",
verify_password_invalid: "Verification password does not match"
}
this.browserSupport = {
browserWarning: "You are running an unsupported browser (%s)",
details: "Details...",
@ -715,6 +755,7 @@ function StringsTw(){
this.oni = "魔王"
this.songBranch = "有譜面分歧"
this.defaultName = "小咚"
this.default2PName = "小咔"
this.notLoggedIn = "未登錄"
this.sessionStart = "開始多人模式!"
this.sessionEnd = "結束多人模式"
@ -875,6 +916,7 @@ function StringsTw(){
register: "註冊",
registerAccount: "註冊帳號",
passwordsDoNotMatch: "密碼不匹配",
newPasswordsDoNotMatch: "New passwords do not match",
cannotBeEmpty: "%s不能為空",
error: "處理您的請求時發生錯誤",
logout: "登出",
@ -891,6 +933,17 @@ function StringsTw(){
deleteAccount: "Delete Account",
verifyPassword: "Verify password to delete this account"
}
this.serverError = {
not_logged_in: "Not logged in",
invalid_username: "Invalid username, a username can only contain letters, numbers, and underscores, and must be between 3 and 20 characters long",
username_in_use: "A user already exists with that username",
invalid_password: "Cannot use this password, please check that your password is at least 6 characters long",
invalid_username_password: "Invalid Username or Password",
invalid_display_name: "Cannot use this name, please check that your new name is at most 25 characters long",
current_password_invalid: "Current password does not match",
invalid_new_password: "Cannot use this password, please check that your new password is at least 6 characters long",
verify_password_invalid: "Verification password does not match"
}
this.browserSupport = {
browserWarning: "You are running an unsupported browser (%s)",
details: "Details...",
@ -941,6 +994,7 @@ function StringsKo(){
this.oni = "귀신"
this.songBranch = "악보 분기 있습니다"
this.defaultName = "동이"
this.default2PName = "딱이"
this.notLoggedIn = "로그인하지 않았습니다"
this.sessionStart = "온라인 세션 시작!"
this.sessionEnd = "온라인 세션 끝내기"
@ -1101,6 +1155,7 @@ function StringsKo(){
register: "가입하기",
registerAccount: "계정 등록",
passwordsDoNotMatch: "비밀번호가 일치하지 않습니다",
newPasswordsDoNotMatch: "New passwords do not match",
cannotBeEmpty: "%s 비어 있을 수 없습니다",
error: "요청을 처리하는 동안 오류가 발생했습니다",
logout: "로그 아웃",
@ -1117,6 +1172,17 @@ function StringsKo(){
deleteAccount: "Delete Account",
verifyPassword: "Verify password to delete this account"
}
this.serverError = {
not_logged_in: "Not logged in",
invalid_username: "Invalid username, a username can only contain letters, numbers, and underscores, and must be between 3 and 20 characters long",
username_in_use: "A user already exists with that username",
invalid_password: "Cannot use this password, please check that your password is at least 6 characters long",
invalid_username_password: "Invalid Username or Password",
invalid_display_name: "Cannot use this name, please check that your new name is at most 25 characters long",
current_password_invalid: "Current password does not match",
invalid_new_password: "Cannot use this password, please check that your new password is at least 6 characters long",
verify_password_invalid: "Verification password does not match"
}
this.browserSupport = {
browserWarning: "You are running an unsupported browser (%s)",
details: "Details...",

View File

@ -129,6 +129,11 @@
this.nameplateCache = new CanvasCache(noSmoothing)
this.multiplayer = this.controller.multiplayer
if(this.multiplayer === 2){
this.player = p2.player === 2 ? 1 : 2
}else{
this.player = this.controller.multiplayer ? p2.player : 1
}
this.touchEnabled = this.controller.touchEnabled
this.touch = -Infinity
@ -224,13 +229,12 @@
this.winH = winH
this.ratio = ratio
if(this.multiplayer !== 2){
if(this.player !== 2){
this.canvas.width = winW
this.canvas.height = winH
ctx.scale(ratio, ratio)
this.canvas.style.width = (winW / this.pixelRatio) + "px"
this.canvas.style.height = (winH / this.pixelRatio) + "px"
this.titleCache.resize(640, 90, ratio)
}
if(!this.multiplayer){
@ -246,7 +250,7 @@
resized = true
}else if(this.controller.game.paused && !document.hasFocus()){
return
}else if(this.multiplayer !== 2){
}else if(this.player !== 2){
ctx.clearRect(0, 0, winW / ratio, winH / ratio)
}
winW /= ratio
@ -263,8 +267,8 @@
var frameTop = winH / 2 - 720 / 2
var frameLeft = winW / 2 - 1280 / 2
}
if(this.multiplayer === 2){
frameTop += this.multiplayer === 2 ? 165 : 176
if(this.player === 2){
frameTop += 165
}
if(touchMultiplayer){
if(!this.touchp2Class){
@ -284,11 +288,11 @@
this.drawGogoTime()
if(!touchMultiplayer || this.multiplayer === 1 && frameTop >= 0){
if(!touchMultiplayer || this.player === 1 && frameTop >= 0){
this.assets.drawAssets("background")
}
if(this.multiplayer !== 2){
if(this.player !== 2){
this.titleCache.get({
ctx: ctx,
x: winW - (touchMultiplayer && fullScreenSupported ? 750 : 650),
@ -356,7 +360,7 @@
var score = this.controller.getGlobalScore()
var gaugePercent = this.rules.gaugePercent(score.gauge)
if(this.multiplayer === 2){
if(this.player === 2){
var scoreImg = "bg_score_p2"
var scoreFill = "#6bbec0"
}else{
@ -379,17 +383,17 @@
size: 100,
paddingLeft: 0
}
this.scorePos = {x: 363, y: frameTop + (this.multiplayer === 2 ? 520 : 227)}
this.scorePos = {x: 363, y: frameTop + (this.player === 2 ? 520 : 227)}
var animPos = {
x1: this.slotPos.x + 13,
y1: this.slotPos.y + (this.multiplayer === 2 ? 27 : -27),
y1: this.slotPos.y + (this.player === 2 ? 27 : -27),
x2: winW - 38,
y2: frameTop + (this.multiplayer === 2 ? 484 : 293)
y2: frameTop + (this.player === 2 ? 484 : 293)
}
var taikoPos = {
x: 19,
y: frameTop + (this.multiplayer === 2 ? 464 : 184),
y: frameTop + (this.player === 2 ? 464 : 184),
w: 111,
h: 130
}
@ -397,15 +401,16 @@
this.nameplateCache.get({
ctx: ctx,
x: 167,
y: this.multiplayer === 2 ? 565 : 160,
y: this.player === 2 ? 565 : 160,
w: 219,
h: 53,
id: "1p",
}, ctx => {
var defaultName = this.player === 1 ? strings.defaultName : strings.default2PName
if(this.multiplayer === 2){
var name = p2.name || strings.defaultName
var name = p2.name || defaultName
}else{
var name = account.loggedIn ? account.displayName : strings.defaultName
var name = account.loggedIn ? account.displayName : defaultName
}
this.draw.nameplate({
ctx: ctx,
@ -414,19 +419,19 @@
scale: 0.8,
name: name,
font: this.font,
blue: this.multiplayer === 2
blue: this.player === 2
})
})
ctx.fillStyle = "#000"
ctx.fillRect(
0,
this.multiplayer === 2 ? 306 : 288,
this.player === 2 ? 306 : 288,
winW,
this.multiplayer === 1 ? 184 : 183
this.player === 1 ? 184 : 183
)
ctx.beginPath()
if(this.multiplayer === 2){
if(this.player === 2){
ctx.moveTo(0, 467)
ctx.lineTo(384, 467)
ctx.lineTo(384, 512)
@ -445,7 +450,7 @@
ctx.fillStyle = scoreFill
var leftSide = (ctx, mul) => {
ctx.beginPath()
if(this.multiplayer === 2){
if(this.player === 2){
ctx.moveTo(0, 468 * mul)
ctx.lineTo(380 * mul, 468 * mul)
ctx.lineTo(380 * mul, 512 * mul)
@ -475,7 +480,7 @@
// Score background
ctx.fillStyle = "#000"
ctx.beginPath()
if(this.multiplayer === 2){
if(this.player === 2){
this.draw.roundedCorner(ctx, 184, 512, 20, 0)
ctx.lineTo(384, 512)
this.draw.roundedCorner(ctx, 384, 560, 12, 2)
@ -493,16 +498,16 @@
ctx.drawImage(assets.image["difficulty"],
0, 144 * this.difficulty[this.controller.selectedSong.difficulty],
168, 143,
126, this.multiplayer === 2 ? 497 : 228,
126, this.player === 2 ? 497 : 228,
62, 53
)
}
// Badges
if(this.controller.autoPlayEnabled && !this.controller.multiplayer){
if(this.controller.autoPlayEnabled && !this.multiplayer){
this.ctx.drawImage(assets.image["badge_auto"],
183,
this.multiplayer === 2 ? 490 : 265,
this.player === 2 ? 490 : 265,
23,
23
)
@ -512,7 +517,7 @@
ctx.fillStyle = "#000"
ctx.beginPath()
var gaugeX = winW - 788 * 0.7 - 32
if(this.multiplayer === 2){
if(this.player === 2){
ctx.moveTo(gaugeX, 464)
ctx.lineTo(winW, 464)
ctx.lineTo(winW, 489)
@ -527,18 +532,18 @@
this.draw.gauge({
ctx: ctx,
x: winW,
y: this.multiplayer === 2 ? 468 : 273,
y: this.player === 2 ? 468 : 273,
clear: this.rules.gaugeClear,
percentage: gaugePercent,
font: this.font,
scale: 0.7,
multiplayer: this.multiplayer === 2,
blue: this.multiplayer === 2
multiplayer: this.player === 2,
blue: this.player === 2
})
this.draw.soul({
ctx: ctx,
x: winW - 40,
y: this.multiplayer === 2 ? 484 : 293,
y: this.player === 2 ? 484 : 293,
scale: 0.75,
cleared: this.rules.clearReached(score.gauge)
})
@ -566,29 +571,30 @@
}
this.scorePos = {
x: 155,
y: frameTop + (this.multiplayer === 2 ? 318 : 193)
y: frameTop + (this.player === 2 ? 318 : 193)
}
var animPos = {
x1: this.slotPos.x + 14,
y1: this.slotPos.y + (this.multiplayer === 2 ? 29 : -29),
y1: this.slotPos.y + (this.player === 2 ? 29 : -29),
x2: winW - 55,
y2: frameTop + (this.multiplayer === 2 ? 378 : 165)
y2: frameTop + (this.player === 2 ? 378 : 165)
}
var taikoPos = {x: 179, y: frameTop + 190, w: 138, h: 162}
this.nameplateCache.get({
ctx: ctx,
x: 320,
y: this.multiplayer === 2 ? 460 : 20,
y: this.player === 2 ? 460 : 20,
w: 273,
h: 66,
id: "1p",
}, ctx => {
var defaultName = this.player === 1 ? strings.defaultName : strings.default2PName
if(this.multiplayer === 2){
var name = p2.name || strings.defaultName
var name = p2.name || defaultName
}else{
var name = account.loggedIn ? account.displayName : strings.defaultName
var name = account.loggedIn ? account.displayName : defaultName
}
this.draw.nameplate({
ctx: ctx,
@ -596,7 +602,7 @@
y: 3,
name: name,
font: this.font,
blue: this.multiplayer === 2
blue: this.player === 2
})
})
@ -605,10 +611,10 @@
0,
184,
winW,
this.multiplayer === 1 ? 177 : 176
this.multiplayer && this.player === 1 ? 177 : 176
)
ctx.beginPath()
if(this.multiplayer === 2){
if(this.player === 2){
ctx.moveTo(328, 351)
ctx.lineTo(winW, 351)
ctx.lineTo(winW, 385)
@ -625,17 +631,17 @@
this.draw.gauge({
ctx: ctx,
x: winW,
y: this.multiplayer === 2 ? 357 : 135,
y: this.player === 2 ? 357 : 135,
clear: this.rules.gaugeClear,
percentage: gaugePercent,
font: this.font,
multiplayer: this.multiplayer === 2,
blue: this.multiplayer === 2
multiplayer: this.player === 2,
blue: this.player === 2
})
this.draw.soul({
ctx: ctx,
x: winW - 57,
y: this.multiplayer === 2 ? 378 : 165,
y: this.player === 2 ? 378 : 165,
cleared: this.rules.clearReached(score.gauge)
})
@ -667,7 +673,7 @@
ctx.drawImage(assets.image["difficulty"],
0, 144 * this.difficulty[this.controller.selectedSong.difficulty],
168, 143,
16, this.multiplayer === 2 ? 194 : 232,
16, this.player === 2 ? 194 : 232,
141, 120
)
var diff = this.controller.selectedSong.difficulty
@ -679,13 +685,13 @@
ctx.fillStyle = "#fff"
ctx.lineWidth = 7
ctx.miterLimit = 1
ctx.strokeText(text, 87, this.multiplayer === 2 ? 310 : 348)
ctx.fillText(text, 87, this.multiplayer === 2 ? 310 : 348)
ctx.strokeText(text, 87, this.player === 2 ? 310 : 348)
ctx.fillText(text, 87, this.player === 2 ? 310 : 348)
ctx.miterLimit = 10
}
// Badges
if(this.controller.autoPlayEnabled && !this.controller.multiplayer){
if(this.controller.autoPlayEnabled && !this.multiplayer){
this.ctx.drawImage(assets.image["badge_auto"],
125, 235, 34, 34
)
@ -694,7 +700,7 @@
// Score background
ctx.fillStyle = "#000"
ctx.beginPath()
if(this.multiplayer === 2){
if(this.player === 2){
ctx.moveTo(0, 312)
this.draw.roundedCorner(ctx, 176, 312, 20, 1)
ctx.lineTo(176, 353)
@ -719,11 +725,11 @@
}, {
// 560, 10
x: animPos.x1 + animPos.w / 6,
y: animPos.y1 - animPos.h * (this.multiplayer === 2 ? 2.5 : 3.5)
y: animPos.y1 - animPos.h * (this.player === 2 ? 2.5 : 3.5)
}, {
// 940, -150
x: animPos.x2 - animPos.w / 3,
y: animPos.y2 - animPos.h * (this.multiplayer === 2 ? 3.5 : 5)
y: animPos.y2 - animPos.h * (this.player === 2 ? 3.5 : 5)
}, {
// 1225, 165
x: animPos.x2,
@ -1443,12 +1449,12 @@
var selectedSong = this.controller.selectedSong
var songSkinName = selectedSong.songSkin.name
var donLayers = []
var filename = !selectedSong.songSkin.don && this.multiplayer === 2 ? "bg_don2_" : "bg_don_"
var filename = !selectedSong.songSkin.don && this.player === 2 ? "bg_don2_" : "bg_don_"
var prefix = ""
this.donBg = document.createElement("div")
this.donBg.classList.add("donbg")
if(this.multiplayer === 2){
if(this.player === 2){
this.donBg.classList.add("donbg-bottom")
}
for(var layer = 1; layer <= 3; layer++){

View File

@ -18,7 +18,7 @@ class ViewAssets{
sw: imgw,
sh: imgh - 1,
x: view.portrait ? -60 : 0,
y: view.portrait ? (view.multiplayer === 2 ? 560 : 35) : (view.multiplayer === 2 ? 360 : 2),
y: view.portrait ? (view.player === 2 ? 560 : 35) : (view.player === 2 ? 360 : 2),
w: w,
h: h - 1
}

View File

@ -2,9 +2,10 @@
<div class="view account-view">
<div class="view-title stroke-sub"></div>
<div class="view-content">
<div class="error-div"></div>
<div class="displayname-div">
<div class="displayname-hint"></div>
<input type="text" class="displayname">
<input type="text" class="displayname" maxlength="25">
</div>
<form class="accountpass-form">
<div>

View File

@ -2,9 +2,10 @@
<div class="view">
<div class="view-title stroke-sub"></div>
<div class="view-content">
<div class="error-div"></div>
<form class="login-form">
<div class="username-hint"></div>
<input type="text" name="username" required>
<input type="text" name="username" maxlength="20" required>
<div class="password-hint"></div>
<input type="password" name="password" required>
<div class="password2-div"></div>

View File

@ -103,8 +103,8 @@ async def connection(ws, path):
user["other_user"]["action"] = "loading"
user["other_user"]["other_user"] = user
await asyncio.wait([
ws.send(msgobj("gameload", waiting_diff)),
user["other_user"]["ws"].send(msgobj("gameload", diff)),
ws.send(msgobj("gameload", {"diff": waiting_diff, "player": 2})),
user["other_user"]["ws"].send(msgobj("gameload", {"diff": diff, "player": 1})),
ws.send(msgobj("name", user["other_user"]["name"])),
user["other_user"]["ws"].send(msgobj("name", user["name"]))
])
@ -138,10 +138,9 @@ async def connection(ws, path):
user["other_user"]["other_user"] = user
user["action"] = "invite"
user["session"] = value["id"]
sent_msg = msgobj("session")
await asyncio.wait([
ws.send(sent_msg),
user["other_user"]["ws"].send(sent_msg),
ws.send(msgobj("session", {"player": 2})),
user["other_user"]["ws"].send(msgobj("session", {"player": 1})),
ws.send(msgobj("invite")),
ws.send(msgobj("name", user["other_user"]["name"])),
user["other_user"]["ws"].send(msgobj("name", user["name"]))
@ -291,8 +290,8 @@ async def connection(ws, path):
user["action"] = "loading"
user["other_user"]["action"] = "loading"
await asyncio.wait([
ws.send(msgobj("gameload", user["other_user"]["gamediff"])),
user["other_user"]["ws"].send(msgobj("gameload", diff))
ws.send(msgobj("gameload", {"diff": user["other_user"]["gamediff"]})),
user["other_user"]["ws"].send(msgobj("gameload", {"diff": diff}))
])
else:
user["action"] = "waiting"