From cd288d4fa45b0a96c6c93b84bf97c64ff3a428de Mon Sep 17 00:00:00 2001 From: LoveEevee Date: Sat, 4 Apr 2020 16:48:58 +0300 Subject: [PATCH] Add custom Don - Registered users can customise the colour of their Don and it will appear for other players - Bug fixes: - Add lyrics checkbox to admin page - 2P shows above "creative" or "with lyrics" labels - Prevent accidental alt and menu keyboard presses from triggering browser menus - Fixed mouse hitboxes on difficulty selection - Clean cached sounds and lyrics when another song is loading - Fixed debug jumping to the top-left of the screen when hidden - Fixed server volume not being applied to songs --- app.py | 67 ++++++++++- public/assets/img/don_anim_10combo.png | Bin 2040 -> 0 bytes public/assets/img/don_anim_10combo_a.png | Bin 0 -> 340 bytes public/assets/img/don_anim_10combo_b1.png | Bin 0 -> 340 bytes public/assets/img/don_anim_10combo_b2.png | Bin 0 -> 340 bytes public/assets/img/don_anim_clear.png | Bin 2040 -> 0 bytes public/assets/img/don_anim_clear_a.png | Bin 0 -> 340 bytes public/assets/img/don_anim_clear_b1.png | Bin 0 -> 340 bytes public/assets/img/don_anim_clear_b2.png | Bin 0 -> 340 bytes public/assets/img/don_anim_endclear.png | Bin 2040 -> 0 bytes public/assets/img/don_anim_gogo.png | Bin 3969 -> 0 bytes public/assets/img/don_anim_gogo_a.png | Bin 0 -> 585 bytes public/assets/img/don_anim_gogo_b1.png | Bin 0 -> 585 bytes public/assets/img/don_anim_gogo_b2.png | Bin 0 -> 585 bytes public/assets/img/don_anim_gogostart.png | Bin 2040 -> 0 bytes public/assets/img/don_anim_gogostart_a.png | Bin 0 -> 340 bytes public/assets/img/don_anim_gogostart_b1.png | Bin 0 -> 340 bytes public/assets/img/don_anim_gogostart_b2.png | Bin 0 -> 340 bytes public/assets/img/don_anim_normal.png | Bin 1397 -> 0 bytes public/assets/img/don_anim_normal_a.png | Bin 0 -> 259 bytes public/assets/img/don_anim_normal_b1.png | Bin 0 -> 259 bytes public/assets/img/don_anim_normal_b2.png | Bin 0 -> 259 bytes public/src/css/admin.css | 6 +- public/src/css/game.css | 1 + public/src/css/view.css | 26 +++++ public/src/js/account.js | 117 ++++++++++++++++++++ public/src/js/assets.js | 20 +++- public/src/js/canvasasset.js | 51 ++++++++- public/src/js/canvascache.js | 6 +- public/src/js/controller.js | 16 +++ public/src/js/debug.js | 3 + public/src/js/keyboard.js | 8 +- public/src/js/loader.js | 4 +- public/src/js/loadsong.js | 19 +++- public/src/js/main.js | 4 + public/src/js/p2.js | 4 +- public/src/js/scorestorage.js | 1 + public/src/js/songselect.js | 37 ++++--- public/src/js/strings.js | 19 ++++ public/src/js/viewassets.js | 24 ++-- public/src/views/account.html | 14 +++ schema.py | 9 ++ server.py | 27 ++++- templates/admin_song_detail.html | 13 ++- templates/admin_song_new.html | 5 + templates/admin_songs.html | 4 +- 46 files changed, 448 insertions(+), 57 deletions(-) delete mode 100644 public/assets/img/don_anim_10combo.png create mode 100644 public/assets/img/don_anim_10combo_a.png create mode 100644 public/assets/img/don_anim_10combo_b1.png create mode 100644 public/assets/img/don_anim_10combo_b2.png delete mode 100644 public/assets/img/don_anim_clear.png create mode 100644 public/assets/img/don_anim_clear_a.png create mode 100644 public/assets/img/don_anim_clear_b1.png create mode 100644 public/assets/img/don_anim_clear_b2.png delete mode 100644 public/assets/img/don_anim_endclear.png delete mode 100644 public/assets/img/don_anim_gogo.png create mode 100644 public/assets/img/don_anim_gogo_a.png create mode 100644 public/assets/img/don_anim_gogo_b1.png create mode 100644 public/assets/img/don_anim_gogo_b2.png delete mode 100644 public/assets/img/don_anim_gogostart.png create mode 100644 public/assets/img/don_anim_gogostart_a.png create mode 100644 public/assets/img/don_anim_gogostart_b1.png create mode 100644 public/assets/img/don_anim_gogostart_b2.png delete mode 100644 public/assets/img/don_anim_normal.png create mode 100644 public/assets/img/don_anim_normal_a.png create mode 100644 public/assets/img/don_anim_normal_b1.png create mode 100644 public/assets/img/don_anim_normal_b2.png 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 f43393fafa18c4250002cb2ffbc862a68f61cd3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2040 zcmeAS@N?(olHy`uVBq!ia0y~yV6k9eU^n1k28ytiviJfi<^Z1%S0KH-%JvqJVk`;r z3ubV5b|Vd_aIvS0V@O5Z+XIG-3=BMn7yJz0Ywa$3fEB22vfRNSw^(?9Y(b8 nMvfRNSw^(?9Y(b8 nMvfRNSw^(?9Y(b8 nMh=Rx{(VUMy}UEK4%%x-gg+$ zz8{VJksSS_Ie!Gl{%GWnM*av50f!OlZ>YCNbN&d9|B)T}2S|u{{`FfJ7}++sYJv24 My85}Sb4q9e0K2IkwEzGB 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 0000000000000000000000000000000000000000..bbec627ce0f0c47f4c4f6853617232936517626d GIT binary patch literal 585 zcmeAS@N?(olHy`uVBq!ia0y~y;HY9?V0U5!3NW0{S8xJSOeH~n!3+##lh0ZJIZU1| zjv*C{Zx1qZ0_6@b`08IvfRNSw^(?9Y(b8 nMV83NkQo95MK_I&EF+{wGX8bt4xjj9jmSd_EfaBRKv? ecH}28qD4KweGCJmM|gfHNZixa&t;ucLK6U^xgCuF 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 0000000000000000000000000000000000000000..5ae0a155b1437232d410fa176f950f197babfe5b GIT binary patch literal 259 zcmeAS@N?(olHy`uVBq!ia0y~yV7kP>!0yBd6kwR{Z}b>QF_i@Q1v4;|O+IS@!0yBd6kwR{Z}b>QF_i@Q1v4;|O+IS@!0yBd6kwR{Z}b>QF_i@Q1v4;|O+IS@ { @@ -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 %}