diff --git a/app.py b/app.py index d1713a3..903a9dc 100644 --- a/app.py +++ b/app.py @@ -136,6 +136,29 @@ def get_version(): return version +def get_db_don(user): + don_body_fill = user['don_body_fill'] if 'don_body_fill' in user else get_default_don('body_fill') + don_face_fill = user['don_face_fill'] if 'don_face_fill' in user else get_default_don('face_fill') + return {'body_fill': don_body_fill, 'face_fill': don_face_fill} + +def get_default_don(part=None): + if part == None: + return { + 'body_fill': get_default_don('body_fill'), + 'face_fill': get_default_don('face_fill') + } + elif part == 'body_fill': + return '#5fb7c1' + elif part == 'face_fill': + return '#ff5724' + +def is_hex(input): + try: + int(input, 16) + return True + except ValueError: + return False + @app.route('/') def route_index(): @@ -213,6 +236,7 @@ def route_admin_songs_new_post(): output['preview'] = float(request.form.get('preview')) or None output['volume'] = float(request.form.get('volume')) or None output['maker_id'] = int(request.form.get('maker_id')) or None + output['lyrics'] = True if request.form.get('lyrics') else False output['hash'] = None seq = db.seq.find_one({'name': 'songs'}) @@ -262,6 +286,7 @@ def route_admin_songs_id_post(id): output['preview'] = float(request.form.get('preview')) or None output['volume'] = float(request.form.get('volume')) or None output['maker_id'] = int(request.form.get('maker_id')) or None + output['lyrics'] = True if request.form.get('lyrics') else False output['hash'] = request.form.get('hash') if request.form.get('gen_hash'): @@ -366,13 +391,15 @@ def route_api_register(): salt = bcrypt.gensalt() hashed = bcrypt.hashpw(password, salt) - + don = get_default_don() + session_id = os.urandom(24).hex() db.users.insert_one({ 'username': username, 'username_lower': username.lower(), 'password': hashed, - 'display_name': username, + 'display_name': username, + 'don': don, 'user_level': 1, 'session_id': session_id }) @@ -380,7 +407,7 @@ def route_api_register(): session['session_id'] = session_id session['username'] = username session.permanent = True - return jsonify({'status': 'ok', 'username': username, 'display_name': username}) + return jsonify({'status': 'ok', 'username': username, 'display_name': username, 'don': don}) @app.route('/api/login', methods=['POST']) @@ -400,12 +427,14 @@ def route_api_login(): password = data.get('password', '').encode('utf-8') if not bcrypt.checkpw(password, result['password']): return api_error('invalid_username_password') - + + don = get_db_don(result) + session['session_id'] = result['session_id'] session['username'] = result['username'] session.permanent = True if data.get('remember') else False - return jsonify({'status': 'ok', 'username': result['username'], 'display_name': result['display_name']}) + return jsonify({'status': 'ok', 'username': result['username'], 'display_name': result['display_name'], 'don': don}) @app.route('/api/logout', methods=['POST']) @@ -435,6 +464,31 @@ def route_api_account_display_name(): return jsonify({'status': 'ok', 'display_name': display_name}) +@app.route('/api/account/don', methods=['POST']) +@login_required +def route_api_account_don(): + data = request.get_json() + if not schema.validate(data, schema.update_don): + return abort(400) + + don_body_fill = data.get('body_fill', '').strip() + don_face_fill = data.get('face_fill', '').strip() + if len(don_body_fill) != 7 or\ + not don_body_fill.startswith("#")\ + or not is_hex(don_body_fill[1:])\ + or len(don_face_fill) != 7\ + or not don_face_fill.startswith("#")\ + or not is_hex(don_face_fill[1:]): + return api_error('invalid_don') + + db.users.update_one({'username': session.get('username')}, {'$set': { + 'don_body_fill': don_body_fill, + 'don_face_fill': don_face_fill, + }}) + + return jsonify({'status': 'ok', 'don': {'body_fill': don_body_fill, 'face_fill': don_face_fill}}) + + @app.route('/api/account/password', methods=['POST']) @login_required def route_api_account_password(): @@ -518,7 +572,8 @@ def route_api_scores_get(): }) user = db.users.find_one({'username': username}) - return jsonify({'status': 'ok', 'scores': scores, 'username': user['username'], 'display_name': user['display_name']}) + don = get_db_don(user) + return jsonify({'status': 'ok', 'scores': scores, 'username': user['username'], 'display_name': user['display_name'], 'don': don}) def make_preview(song_id, song_type, preview): diff --git a/public/assets/img/don_anim_10combo.png b/public/assets/img/don_anim_10combo.png deleted file mode 100644 index f43393f..0000000 Binary files a/public/assets/img/don_anim_10combo.png and /dev/null differ diff --git a/public/assets/img/don_anim_10combo_a.png b/public/assets/img/don_anim_10combo_a.png new file mode 100644 index 0000000..ddfceeb Binary files /dev/null and b/public/assets/img/don_anim_10combo_a.png differ diff --git a/public/assets/img/don_anim_10combo_b1.png b/public/assets/img/don_anim_10combo_b1.png new file mode 100644 index 0000000..ddfceeb Binary files /dev/null and b/public/assets/img/don_anim_10combo_b1.png differ diff --git a/public/assets/img/don_anim_10combo_b2.png b/public/assets/img/don_anim_10combo_b2.png new file mode 100644 index 0000000..ddfceeb Binary files /dev/null and b/public/assets/img/don_anim_10combo_b2.png differ diff --git a/public/assets/img/don_anim_clear.png b/public/assets/img/don_anim_clear.png deleted file mode 100644 index f43393f..0000000 Binary files a/public/assets/img/don_anim_clear.png and /dev/null differ diff --git a/public/assets/img/don_anim_clear_a.png b/public/assets/img/don_anim_clear_a.png new file mode 100644 index 0000000..ddfceeb Binary files /dev/null and b/public/assets/img/don_anim_clear_a.png differ diff --git a/public/assets/img/don_anim_clear_b1.png b/public/assets/img/don_anim_clear_b1.png new file mode 100644 index 0000000..ddfceeb Binary files /dev/null and b/public/assets/img/don_anim_clear_b1.png differ diff --git a/public/assets/img/don_anim_clear_b2.png b/public/assets/img/don_anim_clear_b2.png new file mode 100644 index 0000000..ddfceeb Binary files /dev/null and b/public/assets/img/don_anim_clear_b2.png differ diff --git a/public/assets/img/don_anim_endclear.png b/public/assets/img/don_anim_endclear.png deleted file mode 100644 index f43393f..0000000 Binary files a/public/assets/img/don_anim_endclear.png and /dev/null differ diff --git a/public/assets/img/don_anim_gogo.png b/public/assets/img/don_anim_gogo.png deleted file mode 100644 index f6d84c4..0000000 Binary files a/public/assets/img/don_anim_gogo.png and /dev/null differ diff --git a/public/assets/img/don_anim_gogo_a.png b/public/assets/img/don_anim_gogo_a.png new file mode 100644 index 0000000..bbec627 Binary files /dev/null and b/public/assets/img/don_anim_gogo_a.png differ diff --git a/public/assets/img/don_anim_gogo_b1.png b/public/assets/img/don_anim_gogo_b1.png new file mode 100644 index 0000000..bbec627 Binary files /dev/null and b/public/assets/img/don_anim_gogo_b1.png differ diff --git a/public/assets/img/don_anim_gogo_b2.png b/public/assets/img/don_anim_gogo_b2.png new file mode 100644 index 0000000..bbec627 Binary files /dev/null and b/public/assets/img/don_anim_gogo_b2.png differ diff --git a/public/assets/img/don_anim_gogostart.png b/public/assets/img/don_anim_gogostart.png deleted file mode 100644 index f43393f..0000000 Binary files a/public/assets/img/don_anim_gogostart.png and /dev/null differ diff --git a/public/assets/img/don_anim_gogostart_a.png b/public/assets/img/don_anim_gogostart_a.png new file mode 100644 index 0000000..ddfceeb Binary files /dev/null and b/public/assets/img/don_anim_gogostart_a.png differ diff --git a/public/assets/img/don_anim_gogostart_b1.png b/public/assets/img/don_anim_gogostart_b1.png new file mode 100644 index 0000000..ddfceeb Binary files /dev/null and b/public/assets/img/don_anim_gogostart_b1.png differ diff --git a/public/assets/img/don_anim_gogostart_b2.png b/public/assets/img/don_anim_gogostart_b2.png new file mode 100644 index 0000000..ddfceeb Binary files /dev/null and b/public/assets/img/don_anim_gogostart_b2.png differ diff --git a/public/assets/img/don_anim_normal.png b/public/assets/img/don_anim_normal.png deleted file mode 100644 index 6bbb559..0000000 Binary files a/public/assets/img/don_anim_normal.png and /dev/null differ diff --git a/public/assets/img/don_anim_normal_a.png b/public/assets/img/don_anim_normal_a.png new file mode 100644 index 0000000..5ae0a15 Binary files /dev/null and b/public/assets/img/don_anim_normal_a.png differ diff --git a/public/assets/img/don_anim_normal_b1.png b/public/assets/img/don_anim_normal_b1.png new file mode 100644 index 0000000..5ae0a15 Binary files /dev/null and b/public/assets/img/don_anim_normal_b1.png differ diff --git a/public/assets/img/don_anim_normal_b2.png b/public/assets/img/don_anim_normal_b2.png new file mode 100644 index 0000000..5ae0a15 Binary files /dev/null and b/public/assets/img/don_anim_normal_b2.png differ diff --git a/public/src/css/admin.css b/public/src/css/admin.css index 033bb42..e3140e1 100644 --- a/public/src/css/admin.css +++ b/public/src/css/admin.css @@ -107,10 +107,14 @@ main { width: 50px; } -h1 small { +h1 .song-id { color: #4a4a4a; } +.song .song-id { + color: #a01300; +} + .form-field-indent { margin-left: 20px; } diff --git a/public/src/css/game.css b/public/src/css/game.css index 7918ce1..afa1c70 100644 --- a/public/src/css/game.css +++ b/public/src/css/game.css @@ -100,6 +100,7 @@ font-size: calc(45px * var(--scale)); line-height: 1.2; white-space: pre-wrap; + overflow-wrap: break-word; } #game.portrait #song-lyrics{ right: calc(20px * var(--scale)); diff --git a/public/src/css/view.css b/public/src/css/view.css index da0f13c..d670ee4 100644 --- a/public/src/css/view.css +++ b/public/src/css/view.css @@ -377,3 +377,29 @@ kbd{ font-size: 1.1em; color: #d00; } +.customdon-div{ + display: flex; + justify-content: center; + align-items: center; + text-align: right; +} +.customdon-canvas{ + max-width: 40vw; +} +.customdon-div label{ + display: block; + padding: 0.3em; +} +.customdon-div input[type="color"]{ + font-size: inherit; + width: 2.6em; + height: 1.6em; + padding: 0 0.1em; + vertical-align: middle; +} +.customdon-reset{ + width: 100%; + font-family: inherit; + font-size: 1em; + padding: 0.2em; +} diff --git a/public/src/js/account.js b/public/src/js/account.js index 1df6723..0c110ff 100644 --- a/public/src/js/account.js +++ b/public/src/js/account.js @@ -42,6 +42,35 @@ class Account{ this.displayname.value = account.displayName this.inputForms.push(this.displayname) + this.redrawRunning = true + this.customdonRedrawBind = this.customdonRedraw.bind(this) + this.start = new Date().getTime() + this.frames = [ + 0 ,0 ,0 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,6 ,5 ,4 ,3 ,2 ,1 , + 0 ,0 ,0 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,6 ,5 ,4 ,3 ,2 ,1 , + 0 ,0 ,0 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,6 ,5 ,7 ,8 ,9 ,10, + 11,11,11,11,10,9 ,8 ,7 ,13,12,12,13,14,15,16,17 + ] + this.customdonCache = new CanvasCache() + this.customdonCache.resize(723 * 2, 1858, 1) + this.customdonCanvas = this.getElement("customdon-canvas") + this.customdonCtx = this.customdonCanvas.getContext("2d") + this.customdonBodyFill = this.getElement("customdon-bodyfill") + this.customdonBodyFill.value = account.don.body_fill + var parent = this.customdonBodyFill.parentNode + parent.insertBefore(document.createTextNode(strings.account.customdon.bodyFill), parent.firstChild) + pageEvents.add(this.customdonBodyFill, "change", this.customdonChange.bind(this)) + this.customdonFaceFill = this.getElement("customdon-facefill") + this.customdonFaceFill.value = account.don.face_fill + var parent = this.customdonFaceFill.parentNode + parent.insertBefore(document.createTextNode(strings.account.customdon.faceFill), parent.firstChild) + pageEvents.add(this.customdonFaceFill, "change", this.customdonChange.bind(this)) + this.customdonResetBtn = this.getElement("customdon-reset") + this.customdonResetBtn.value = strings.account.customdon.reset + pageEvents.add(this.customdonResetBtn, ["click", "touchstart"], this.customdonReset.bind(this)) + this.customdonChange() + this.customdonRedraw() + this.accountPassButton = this.getElement("accountpass-btn") this.setAltText(this.accountPassButton, strings.account.changePassword) pageEvents.add(this.accountPassButton, ["click", "touchstart"], event => { @@ -83,6 +112,70 @@ class Account{ pageEvents.add(this.inputForms[i], ["keydown", "keyup", "keypress"], this.onFormPress.bind(this)) } } + customdonChange(){ + var ctx = this.customdonCtx + this.customdonCache.clear() + var w = 722 + var h = 1858 + this.customdonCache.set({ + w: w, h: h, id: "bodyFill" + }, ctx => { + ctx.drawImage(assets.image["don_anim_normal_b1"], 0, 0) + ctx.globalCompositeOperation = "source-atop" + ctx.fillStyle = this.customdonBodyFill.value + ctx.fillRect(0, 0, w, h) + }) + this.customdonCache.set({ + w: w, h: h, id: "faceFill" + }, ctx => { + ctx.drawImage(assets.image["don_anim_normal_b2"], 0, 0) + ctx.globalCompositeOperation = "source-atop" + ctx.fillStyle = this.customdonFaceFill.value + ctx.fillRect(0, 0, w, h) + + ctx.globalCompositeOperation = "source-over" + this.customdonCache.get({ + ctx: ctx, + x: 0, y: 0, w: w, h: h, + id: "bodyFill" + }) + }) + } + customdonReset(event){ + if(event.type === "touchstart"){ + event.preventDefault() + } + this.customdonBodyFill.value = defaultDon.body_fill + this.customdonFaceFill.value = defaultDon.face_fill + this.customdonChange() + } + customdonRedraw(){ + if(!this.redrawRunning){ + return + } + requestAnimationFrame(this.customdonRedrawBind) + if(!document.hasFocus()){ + return + } + var ms = new Date().getTime() + var ctx = this.customdonCtx + var frame = this.frames[Math.floor((ms - this.start) / 30) % this.frames.length] + var w = 360 + var h = 184 + var sx = Math.floor(frame / 10) * (w + 2) + var sy = (frame % 10) * (h + 2) + ctx.clearRect(0, 0, w, h) + this.customdonCache.get({ + ctx: ctx, + sx: sx, sy: sy, sw: w, sh: h, + x: -26, y: 0, w: w, h: h, + id: "faceFill" + }) + ctx.drawImage(assets.image["don_anim_normal_a"], + sx, sy, w, h, + -26, 0, w, h + ) + } showDiv(event, div){ if(event){ if(event.type === "touchstart"){ @@ -257,6 +350,7 @@ class Account{ account.loggedIn = true account.username = response.username account.displayName = response.display_name + account.don = response.don var loadScores = scores => { scoreStorage.load(scores) this.onEnd(false, true, true) @@ -300,6 +394,7 @@ class Account{ account.loggedIn = false delete account.username delete account.displayName + delete account.don var loadScores = () => { scoreStorage.load() this.onEnd(false, true) @@ -344,6 +439,7 @@ class Account{ account.loggedIn = false delete account.username delete account.displayName + delete account.don scoreStorage.load() pageEvents.send("logout") return Promise.resolve @@ -357,6 +453,16 @@ class Account{ account.displayName = response.display_name })) } + var bodyFill = this.customdonBodyFill.value + var faceFill = this.customdonFaceFill.value + if(!noNameChange && (bodyFill !== account.body_fill || this.customdonFaceFill.value !== account.face_fill)){ + promises.push(this.request("account/don", { + body_fill: bodyFill, + face_fill: faceFill + }).then(response => { + account.don = response.don + })) + } var error = false var errorFunc = response => { if(error){ @@ -470,6 +576,11 @@ class Account{ this.accountPass.reset() this.accountDel.reset() } + this.redrawRunning = false + this.customdonCache.clean() + pageEvents.remove(this.customdonBodyFill, "change") + pageEvents.remove(this.customdonFaceFill, "change") + pageEvents.remove(this.customdonResetBtn, ["click", "touchstart"]) pageEvents.remove(this.accounPassButton, ["click", "touchstart"]) pageEvents.remove(this.accountDelButton, ["click", "touchstart"]) pageEvents.remove(this.logoutButton, ["mousedown", "touchstart"]) @@ -479,6 +590,12 @@ class Account{ } delete this.errorDiv delete this.displayname + delete this.frames + delete this.customdonCanvas + delete this.customdonCtx + delete this.customdonBodyFill + delete this.customdonFaceFill + delete this.customdonResetBtn delete this.accountPassButton delete this.accountPass delete this.accountPassDiv diff --git a/public/src/js/assets.js b/public/src/js/assets.js index 7d769ee..919b5f3 100644 --- a/public/src/js/assets.js +++ b/public/src/js/assets.js @@ -58,11 +58,21 @@ var assets = { "dancing-don.gif", "bg-pattern-1.png", "difficulty.png", - "don_anim_normal.png", - "don_anim_10combo.png", - "don_anim_gogo.png", - "don_anim_gogostart.png", - "don_anim_clear.png", + "don_anim_normal_a.png", + "don_anim_normal_b1.png", + "don_anim_normal_b2.png", + "don_anim_10combo_a.png", + "don_anim_10combo_b1.png", + "don_anim_10combo_b2.png", + "don_anim_gogo_a.png", + "don_anim_gogo_b1.png", + "don_anim_gogo_b2.png", + "don_anim_gogostart_a.png", + "don_anim_gogostart_b1.png", + "don_anim_gogostart_b2.png", + "don_anim_clear_a.png", + "don_anim_clear_b1.png", + "don_anim_clear_b2.png", "fire_anim.png", "fireworks_anim.png", "bg_genre_0.png", diff --git a/public/src/js/canvasasset.js b/public/src/js/canvasasset.js index af1b847..9ef1b7c 100644 --- a/public/src/js/canvasasset.js +++ b/public/src/js/canvasasset.js @@ -44,11 +44,47 @@ class CanvasAsset{ mod(length, index){ return ((index % length) + length) % length } - addFrames(name, frames, image){ + addFrames(name, frames, image, don){ var framesObj = { - frames: frames + frames: frames, + don: don } - if(image){ + if(don){ + var img = assets.image[image + "_a"] + var cache1 = new CanvasCache() + var cache2 = new CanvasCache() + var w = img.width + var h = img.height + cache1.resize(w, h, 1) + cache2.resize(w, h, 1) + cache1.set({ + w: w, h: h, id: "1" + }, ctx => { + ctx.drawImage(assets.image[image + "_b1"], 0, 0) + ctx.globalCompositeOperation = "source-atop" + ctx.fillStyle = don.body_fill + ctx.fillRect(0, 0, w, h) + }) + cache2.set({ + w: w, h: h, id: "2" + }, ctx => { + ctx.drawImage(assets.image[image + "_b2"], 0, 0) + ctx.globalCompositeOperation = "source-atop" + ctx.fillStyle = don.face_fill + ctx.fillRect(0, 0, w, h) + + ctx.globalCompositeOperation = "source-over" + cache1.get({ + ctx: ctx, + x: 0, y: 0, w: w, h: h, + id: "1" + }) + ctx.drawImage(img, 0, 0) + }) + cache1.clean() + framesObj.cache = cache2 + framesObj.image = cache2.canvas + }else if(image){ framesObj.image = assets.image[image] } this.animationFrames[name] = framesObj @@ -61,6 +97,7 @@ class CanvasAsset{ if(framesObj.image){ this.image = framesObj.image } + this.don = framesObj.don }else{ this.animation = false } @@ -100,4 +137,12 @@ class CanvasAsset{ } this.beatInterval = beatMS } + clean(){ + for(var i in this.animationFrames){ + var frame = this.animationFrames[i] + if(frame.cache){ + frame.cache.clean() + } + } + } } diff --git a/public/src/js/canvascache.js b/public/src/js/canvascache.js index fbe9a79..bf4f861 100644 --- a/public/src/js/canvascache.js +++ b/public/src/js/canvascache.js @@ -91,8 +91,12 @@ class CanvasCache{ return } var z = this.scale + var sx = (img.x + (config.sx || 0)) * z |0 + var sy = (img.y + (config.sy || 0)) * z |0 + var sw = (config.sw || img.w) * z |0 + var sh = (config.sh || img.h) * z |0 config.ctx.drawImage(this.canvas, - img.x * z |0, img.y * z |0, img.w * z |0, img.h * z |0, + sx, sy, sw, sh, config.x |0, config.y |0, config.w |0, config.h |0 ) if(saved){ diff --git a/public/src/js/controller.js b/public/src/js/controller.js index 58d9315..3740bf2 100644 --- a/public/src/js/controller.js +++ b/public/src/js/controller.js @@ -8,8 +8,16 @@ class Controller{ this.touchEnabled = touchEnabled if(multiplayer === 2){ this.snd = p2.player === 2 ? "_p1" : "_p2" + this.don = p2.don || defaultDon }else{ this.snd = multiplayer ? "_p" + p2.player : "" + this.don = account.loggedIn ? account.don : defaultDon + } + if(this.snd === "_p2" && this.objEqual(defaultDon, this.don)){ + this.don = { + body_fill: defaultDon.face_fill, + face_fill: defaultDon.body_fill + } } this.calibrationMode = selectedSong.folder === "calibration" @@ -317,6 +325,14 @@ class Controller{ return this.mekadon.play(circle) } } + objEqual(a, b){ + for(var i in a){ + if(a[i] !== b[i]){ + return false + } + } + return true + } clean(){ if(this.multiplayer === 1){ this.syncWith.clean() diff --git a/public/src/js/debug.js b/public/src/js/debug.js index 447f24f..a3a441f 100644 --- a/public/src/js/debug.js +++ b/public/src/js/debug.js @@ -82,6 +82,9 @@ class Debug{ } } stopMove(event){ + if(this.debugDiv.style.display === "none"){ + return + } if(!event || event.type === "resize"){ var divPos = this.debugDiv.getBoundingClientRect() var x = divPos.left diff --git a/public/src/js/keyboard.js b/public/src/js/keyboard.js index d13ab7a..c0816e7 100644 --- a/public/src/js/keyboard.js +++ b/public/src/js/keyboard.js @@ -65,7 +65,13 @@ class Keyboard{ } keyEvent(event){ var key = event.key.toLowerCase() - if(key === "escape" || key === "backspace" || key === "tab"){ + if( + key === "escape" || + key === "backspace" || + key === "tab" || + key === "contextmenu" || + key === "alt" + ){ event.preventDefault() } if(!event.repeat){ diff --git a/public/src/js/loader.js b/public/src/js/loader.js index fd98cfc..e574b00 100644 --- a/public/src/js/loader.js +++ b/public/src/js/loader.js @@ -178,6 +178,7 @@ class Loader{ account.loggedIn = true account.username = response.username account.displayName = response.display_name + account.don = response.don scoreStorage.load(response.scores) pageEvents.send("login", account.username) } @@ -236,7 +237,8 @@ class Loader{ }) p2.send("invite", { id: location.hash.slice(1).toLowerCase(), - name: account.loggedIn ? account.displayName : null + name: account.loggedIn ? account.displayName : null, + don: account.loggedIn ? account.don : null }) setTimeout(() => { if(p2.socket.readyState !== 1){ diff --git a/public/src/js/loadsong.js b/public/src/js/loadsong.js index b2e3f98..d3835f2 100644 --- a/public/src/js/loadsong.js +++ b/public/src/js/loadsong.js @@ -37,7 +37,18 @@ class LoadSong{ this.promises = [] if(song.folder !== "calibration"){ assets.sounds["v_start"].play() - var songObj = assets.songs.find(song => song.id === id) + var songObj + assets.songs.forEach(song => { + if(song.id === id){ + songObj = song + }else{ + if(song.sound){ + song.sound.clean() + delete song.sound + } + delete song.lyricsData + } + }) }else{ var songObj = { "music": "muted", @@ -167,6 +178,9 @@ class LoadSong{ }), url) img.src = url } + if(songObj.volume && songObj.volume !== 1){ + this.promises.push(new Promise(resolve => setTimeout(resolve, 500))) + } Promise.all(this.promises).then(() => { if(!this.error){ this.setupMultiplayer() @@ -338,7 +352,8 @@ class LoadSong{ p2.send("join", { id: song.folder, diff: song.difficulty, - name: account.loggedIn ? account.displayName : null + name: account.loggedIn ? account.displayName : null, + don: account.loggedIn ? account.don : null }) }else{ this.clean() diff --git a/public/src/js/main.js b/public/src/js/main.js index c01209c..254caa1 100644 --- a/public/src/js/main.js +++ b/public/src/js/main.js @@ -80,6 +80,10 @@ var perf = { allImg: 0, load: 0 } +var defaultDon = { + body_fill: "#5fb7c1", + face_fill: "#ff5724" +} var strings var vectors var settings diff --git a/public/src/js/p2.js b/public/src/js/p2.js index a8a1d34..179850a 100644 --- a/public/src/js/p2.js +++ b/public/src/js/p2.js @@ -131,6 +131,7 @@ class P2Connection{ this.hashLock = false } this.name = null + this.don = null scoreStorage.clearP2() break case "gameresults": @@ -165,7 +166,8 @@ class P2Connection{ } break case "name": - this.name = response.value ? response.value.toString() : response.value + this.name = response.value ? (response.value.name || "").toString() : "" + this.don = response.value ? (response.value.don) : null break case "getcrowns": if(response.value){ diff --git a/public/src/js/scorestorage.js b/public/src/js/scorestorage.js index 82f6d11..2415082 100644 --- a/public/src/js/scorestorage.js +++ b/public/src/js/scorestorage.js @@ -285,6 +285,7 @@ class ScoreStorage{ account.loggedIn = false delete account.username delete account.displayName + delete account.don this.load() pageEvents.send("logout") return Promise.reject() diff --git a/public/src/js/songselect.js b/public/src/js/songselect.js index 0ef845b..4532057 100644 --- a/public/src/js/songselect.js +++ b/public/src/js/songselect.js @@ -492,7 +492,7 @@ class SongSelect{ } }else if(this.state.screen === "difficulty"){ var moveBy = this.diffSelMouse(mouse.x, mouse.y) - if(mouse.x < 183 || mouse.x > 1095 || mouse.y < 40 || mouse.y > 540){ + if(mouse.x < 183 || mouse.x > 1095 || mouse.y < 54 || mouse.y > 554){ this.toSongSelect() }else if(moveBy === 0){ this.selectedDiff = 0 @@ -596,11 +596,11 @@ class SongSelect{ } diffSelMouse(x, y){ if(this.state.locked === 0){ - if(223 < x && x < 367 && 118 < y && y < 422){ + if(223 < x && x < 367 && 132 < y && y < 436){ return Math.floor((x - 223) / ((367 - 223) / 2)) - }else if(this.songs[this.selectedSong].maker && this.songs[this.selectedSong].maker.id > 0 && this.songs[this.selectedSong].maker.url && x > 230 && x < 485 && y > 432 && y < 519) { + }else if(this.songs[this.selectedSong].maker && this.songs[this.selectedSong].maker.id > 0 && this.songs[this.selectedSong].maker.url && x > 230 && x < 485 && y > 446 && y < 533) { return "maker" - }else if(550 < x && x < 1050 && 95 < y && y < 524){ + }else if(550 < x && x < 1050 && 109 < y && y < 538){ var moveBy = Math.floor((x - 550) / ((1050 - 550) / 5)) + this.diffOptions.length var currentSong = this.songs[this.selectedSong] if( @@ -1631,17 +1631,6 @@ class SongSelect{ if(this.selectedDiff === 4 + this.diffOptions.length){ currentDiff = 3 } - if(songSel && i === currentSong.p2Cursor && p2.socket.readyState === 1){ - this.draw.diffCursor({ - ctx: ctx, - font: this.font, - x: _x, - y: _y - 45, - two: !p2.session || p2.player === 1, - side: false, - scale: 0.7 - }) - } if(!songSel){ var highlight = 0 if(this.state.moveHover - this.diffOptions.length === i){ @@ -1870,6 +1859,24 @@ class SongSelect{ } } + for(var i = 0; currentSong.courses && i < 4; i++){ + if(currentSong.courses[this.difficultyId[i]] || currentUra){ + if(songSel && i === currentSong.p2Cursor && p2.socket.readyState === 1){ + var _x = x + 33 + i * 60 + var _y = y + 120 + this.draw.diffCursor({ + ctx: ctx, + font: this.font, + x: _x, + y: _y - 45, + two: !p2.session || p2.player === 1, + side: false, + scale: 0.7 + }) + } + } + } + if(!songSel && currentSong.courses.ura){ var fade = ((ms - this.state.screenMS) % 1200) / 1200 var _x = x + 402 + 4 * 100 + fade * 25 diff --git a/public/src/js/strings.js b/public/src/js/strings.js index 5dd8ddd..9d99ff2 100644 --- a/public/src/js/strings.js +++ b/public/src/js/strings.js @@ -995,8 +995,23 @@ var translations = { en: "Save", }, displayName: { + ja: null, en: "Displayed Name", }, + customdon: { + bodyFill: { + ja: null, + en: "Body", + }, + faceFill: { + ja: null, + en: "Face", + }, + reset: { + ja: null, + en: "Reset", + } + }, changePassword: { ja: null, en: "Change Password", @@ -1043,6 +1058,10 @@ var translations = { ja: null, en: "Cannot use this name, please check that your new name is at most 25 characters long", }, + invalid_don: { + ja: null, + en: "Could not save your custom Don" + }, current_password_invalid: { ja: null, en: "Current password does not match", diff --git a/public/src/js/viewassets.js b/public/src/js/viewassets.js index 9affaf7..5158611 100644 --- a/public/src/js/viewassets.js +++ b/public/src/js/viewassets.js @@ -9,18 +9,17 @@ class ViewAssets{ this.don = this.createAsset("background", frame => { var imgw = 360 var imgh = 184 - var scale = 165 var w = imgw var h = imgh return { - sx: Math.floor(frame / 10) * imgw, - sy: (frame % 10) * imgh + 1, + sx: Math.floor(frame / 10) * (imgw + 2), + sy: (frame % 10) * (imgh + 2), sw: imgw, - sh: imgh - 1, + sh: imgh, x: view.portrait ? -60 : 0, - y: view.portrait ? (view.player === 2 ? 560 : 35) : (view.player === 2 ? 360 : 2), - w: w, - h: h - 1 + y: view.portrait ? (view.player === 2 ? 560 : 35) : (view.player === 2 ? 360 : 0), + w: w / h * (h + 0.5), + h: h + 0.5 } }) this.don.addFrames("normal", [ @@ -28,15 +27,15 @@ class ViewAssets{ 0 ,0 ,0 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,6 ,5 ,4 ,3 ,2 ,1 , 0 ,0 ,0 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,6 ,5 ,7 ,8 ,9 ,10, 11,11,11,11,10,9 ,8 ,7 ,13,12,12,13,14,15,16,17 - ], "don_anim_normal") - this.don.addFrames("10combo", 22, "don_anim_10combo") + ], "don_anim_normal", this.controller.don) + this.don.addFrames("10combo", 22, "don_anim_10combo", this.controller.don) this.don.addFrames("gogo", [ 42,43,43,44,45,46,47,48,49,50,51,52,53,54, 55,0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,11,12,13, 14,14,15,16,17,18,19,20,21,22,23,24,25,26, 27,28,29,30,31,32,33,34,35,36,37,38,39,40,41 - ], "don_anim_gogo") - this.don.addFrames("gogostart", 27, "don_anim_gogostart") + ], "don_anim_gogo", this.controller.don) + this.don.addFrames("gogostart", 27, "don_anim_gogostart", this.controller.don) this.don.normalAnimation = () => { if(this.view.gogoTime){ var length = this.don.getAnimationLength("gogo") @@ -58,7 +57,7 @@ class ViewAssets{ } } } - this.don.addFrames("clear", 30, "don_anim_clear") + this.don.addFrames("clear", 30, "don_anim_clear", this.controller.don) this.don.normalAnimation() // Bar @@ -176,6 +175,7 @@ class ViewAssets{ }) } clean(){ + this.don.clean() delete this.ctx delete this.don delete this.fire diff --git a/public/src/views/account.html b/public/src/views/account.html index b236652..e493dbb 100644 --- a/public/src/views/account.html +++ b/public/src/views/account.html @@ -7,6 +7,20 @@
+
+ +
+ + + +
+
diff --git a/schema.py b/schema.py index 1ba0c81..a012b02 100644 --- a/schema.py +++ b/schema.py @@ -34,6 +34,15 @@ update_display_name = { } } +update_don = { + '$schema': 'http://json-schema.org/schema#', + 'type': 'object', + 'properties': { + 'body_fill': {'type': 'string'}, + 'face_fill': {'type': 'string'} + } +} + update_password = { '$schema': 'http://json-schema.org/schema#', 'type': 'object', diff --git a/server.py b/server.py index b7057fc..754f958 100644 --- a/server.py +++ b/server.py @@ -43,7 +43,8 @@ async def connection(ws, path): "ws": ws, "action": "ready", "session": False, - "name": None + "name": None, + "don": None } server_status["users"].append(user) try: @@ -81,6 +82,7 @@ async def connection(ws, path): 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 + user["don"] = value["don"] if "don" in value else None if not id or not diff: continue if id not in waiting: @@ -95,6 +97,7 @@ async def connection(ws, path): else: # Join the other user and start game user["name"] = value["name"] if "name" in value else None + user["don"] = value["don"] if "don" in value else None user["other_user"] = waiting[id]["user"] waiting_diff = waiting[id]["diff"] del waiting[id] @@ -107,8 +110,14 @@ async def connection(ws, path): await asyncio.wait([ 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"])) + ws.send(msgobj("name", { + "name": user["other_user"]["name"], + "don": user["other_user"]["don"] + })), + user["other_user"]["ws"].send(msgobj("name", { + "name": user["name"], + "don": user["don"] + })) ]) else: # Wait for another user @@ -130,10 +139,12 @@ async def connection(ws, path): user["action"] = "invite" user["session"] = invite user["name"] = value["name"] if "name" in value else None + user["don"] = value["don"] if "don" in value else None await ws.send(msgobj("invite", invite)) elif value and "id" in value and value["id"] in server_status["invites"]: # Join a session with the other user user["name"] = value["name"] if "name" in value else None + user["don"] = value["don"] if "don" in value else None user["other_user"] = server_status["invites"][value["id"]] del server_status["invites"][value["id"]] if "ws" in user["other_user"]: @@ -146,8 +157,14 @@ async def connection(ws, path): 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"])) + ws.send(msgobj("name", { + "name": user["other_user"]["name"], + "don": user["other_user"]["don"] + })), + user["other_user"]["ws"].send(msgobj("name", { + "name": user["name"], + "don": user["don"] + })) ]) else: del user["other_user"] diff --git a/templates/admin_song_detail.html b/templates/admin_song_detail.html index ff221c8..d7bffd4 100644 --- a/templates/admin_song_detail.html +++ b/templates/admin_song_detail.html @@ -1,6 +1,10 @@ {% extends 'admin.html' %} {% block content %} -

{{ song.title }} (ID: {{ song.id }})

+{% if song.title_lang.en %} +

{{ song.title_lang.en }} ({{ song.title }}) (ID: {{ song.id }})

+{% else %} +

{{ song.title }} (ID: {{ song.id }})

+{% endif %} {% for cat, message in get_flashed_messages(with_categories=true) %}
{{ message }}
{% endfor %} @@ -116,9 +120,14 @@
+
+

+ +
+

- +
diff --git a/templates/admin_song_new.html b/templates/admin_song_new.html index 35557b0..f4a57b4 100644 --- a/templates/admin_song_new.html +++ b/templates/admin_song_new.html @@ -116,6 +116,11 @@ +
+

+ +
+
diff --git a/templates/admin_songs.html b/templates/admin_songs.html index 2ef70ee..3f0d0da 100644 --- a/templates/admin_songs.html +++ b/templates/admin_songs.html @@ -11,9 +11,9 @@
{% if song.title_lang.en %} -

{{ song.title_lang.en }} ({{ song.title }})

+

{{ song.id }}. {{ song.title_lang.en }} ({{ song.title }})

{% else %} -

{{ song.title }}

+

{{ song.id }}. {{ song.title }}

{% endif %}