Commit 6c02bf90 authored by Bui's avatar Bui Committed by GitHub

Merge pull request #208 from bui/add-custom-don

Add custom Don
parents ed97f9c5 69b5aa29
...@@ -136,6 +136,29 @@ def get_version(): ...@@ -136,6 +136,29 @@ def get_version():
return 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('/') @app.route('/')
def route_index(): def route_index():
...@@ -213,6 +236,7 @@ def route_admin_songs_new_post(): ...@@ -213,6 +236,7 @@ def route_admin_songs_new_post():
output['preview'] = float(request.form.get('preview')) or None output['preview'] = float(request.form.get('preview')) or None
output['volume'] = float(request.form.get('volume')) or None output['volume'] = float(request.form.get('volume')) or None
output['maker_id'] = int(request.form.get('maker_id')) 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 output['hash'] = None
seq = db.seq.find_one({'name': 'songs'}) seq = db.seq.find_one({'name': 'songs'})
...@@ -262,6 +286,7 @@ def route_admin_songs_id_post(id): ...@@ -262,6 +286,7 @@ def route_admin_songs_id_post(id):
output['preview'] = float(request.form.get('preview')) or None output['preview'] = float(request.form.get('preview')) or None
output['volume'] = float(request.form.get('volume')) or None output['volume'] = float(request.form.get('volume')) or None
output['maker_id'] = int(request.form.get('maker_id')) 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') output['hash'] = request.form.get('hash')
if request.form.get('gen_hash'): if request.form.get('gen_hash'):
...@@ -366,13 +391,15 @@ def route_api_register(): ...@@ -366,13 +391,15 @@ def route_api_register():
salt = bcrypt.gensalt() salt = bcrypt.gensalt()
hashed = bcrypt.hashpw(password, salt) hashed = bcrypt.hashpw(password, salt)
don = get_default_don()
session_id = os.urandom(24).hex() session_id = os.urandom(24).hex()
db.users.insert_one({ db.users.insert_one({
'username': username, 'username': username,
'username_lower': username.lower(), 'username_lower': username.lower(),
'password': hashed, 'password': hashed,
'display_name': username, 'display_name': username,
'don': don,
'user_level': 1, 'user_level': 1,
'session_id': session_id 'session_id': session_id
}) })
...@@ -380,7 +407,7 @@ def route_api_register(): ...@@ -380,7 +407,7 @@ def route_api_register():
session['session_id'] = session_id session['session_id'] = session_id
session['username'] = username session['username'] = username
session.permanent = True 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']) @app.route('/api/login', methods=['POST'])
...@@ -400,12 +427,14 @@ def route_api_login(): ...@@ -400,12 +427,14 @@ def route_api_login():
password = data.get('password', '').encode('utf-8') password = data.get('password', '').encode('utf-8')
if not bcrypt.checkpw(password, result['password']): if not bcrypt.checkpw(password, result['password']):
return api_error('invalid_username_password') return api_error('invalid_username_password')
don = get_db_don(result)
session['session_id'] = result['session_id'] session['session_id'] = result['session_id']
session['username'] = result['username'] session['username'] = result['username']
session.permanent = True if data.get('remember') else False 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']) @app.route('/api/logout', methods=['POST'])
...@@ -435,6 +464,31 @@ def route_api_account_display_name(): ...@@ -435,6 +464,31 @@ def route_api_account_display_name():
return jsonify({'status': 'ok', 'display_name': 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']) @app.route('/api/account/password', methods=['POST'])
@login_required @login_required
def route_api_account_password(): def route_api_account_password():
...@@ -518,7 +572,8 @@ def route_api_scores_get(): ...@@ -518,7 +572,8 @@ def route_api_scores_get():
}) })
user = db.users.find_one({'username': username}) 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): def make_preview(song_id, song_type, preview):
......
...@@ -107,10 +107,14 @@ main { ...@@ -107,10 +107,14 @@ main {
width: 50px; width: 50px;
} }
h1 small { h1 .song-id {
color: #4a4a4a; color: #4a4a4a;
} }
.song .song-id {
color: #a01300;
}
.form-field-indent { .form-field-indent {
margin-left: 20px; margin-left: 20px;
} }
......
...@@ -100,6 +100,7 @@ ...@@ -100,6 +100,7 @@
font-size: calc(45px * var(--scale)); font-size: calc(45px * var(--scale));
line-height: 1.2; line-height: 1.2;
white-space: pre-wrap; white-space: pre-wrap;
overflow-wrap: break-word;
} }
#game.portrait #song-lyrics{ #game.portrait #song-lyrics{
right: calc(20px * var(--scale)); right: calc(20px * var(--scale));
......
...@@ -377,3 +377,29 @@ kbd{ ...@@ -377,3 +377,29 @@ kbd{
font-size: 1.1em; font-size: 1.1em;
color: #d00; color: #d00;
} }
.customdon-div{
display: flex;
justify-content: center;
align-items: center;
text-align: right;
}
.customdon-canvas{
width: 13em;
}
.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;
}
...@@ -42,6 +42,35 @@ class Account{ ...@@ -42,6 +42,35 @@ class Account{
this.displayname.value = account.displayName this.displayname.value = account.displayName
this.inputForms.push(this.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.accountPassButton = this.getElement("accountpass-btn")
this.setAltText(this.accountPassButton, strings.account.changePassword) this.setAltText(this.accountPassButton, strings.account.changePassword)
pageEvents.add(this.accountPassButton, ["click", "touchstart"], event => { pageEvents.add(this.accountPassButton, ["click", "touchstart"], event => {
...@@ -83,6 +112,70 @@ class Account{ ...@@ -83,6 +112,70 @@ class Account{
pageEvents.add(this.inputForms[i], ["keydown", "keyup", "keypress"], this.onFormPress.bind(this)) 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){ showDiv(event, div){
if(event){ if(event){
if(event.type === "touchstart"){ if(event.type === "touchstart"){
...@@ -257,6 +350,7 @@ class Account{ ...@@ -257,6 +350,7 @@ class Account{
account.loggedIn = true account.loggedIn = true
account.username = response.username account.username = response.username
account.displayName = response.display_name account.displayName = response.display_name
account.don = response.don
var loadScores = scores => { var loadScores = scores => {
scoreStorage.load(scores) scoreStorage.load(scores)
this.onEnd(false, true, true) this.onEnd(false, true, true)
...@@ -300,6 +394,7 @@ class Account{ ...@@ -300,6 +394,7 @@ class Account{
account.loggedIn = false account.loggedIn = false
delete account.username delete account.username
delete account.displayName delete account.displayName
delete account.don
var loadScores = () => { var loadScores = () => {
scoreStorage.load() scoreStorage.load()
this.onEnd(false, true) this.onEnd(false, true)
...@@ -344,6 +439,7 @@ class Account{ ...@@ -344,6 +439,7 @@ class Account{
account.loggedIn = false account.loggedIn = false
delete account.username delete account.username
delete account.displayName delete account.displayName
delete account.don
scoreStorage.load() scoreStorage.load()
pageEvents.send("logout") pageEvents.send("logout")
return Promise.resolve return Promise.resolve
...@@ -357,6 +453,16 @@ class Account{ ...@@ -357,6 +453,16 @@ class Account{
account.displayName = response.display_name 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 error = false
var errorFunc = response => { var errorFunc = response => {
if(error){ if(error){
...@@ -470,6 +576,11 @@ class Account{ ...@@ -470,6 +576,11 @@ class Account{
this.accountPass.reset() this.accountPass.reset()
this.accountDel.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.accounPassButton, ["click", "touchstart"])
pageEvents.remove(this.accountDelButton, ["click", "touchstart"]) pageEvents.remove(this.accountDelButton, ["click", "touchstart"])
pageEvents.remove(this.logoutButton, ["mousedown", "touchstart"]) pageEvents.remove(this.logoutButton, ["mousedown", "touchstart"])
...@@ -479,6 +590,12 @@ class Account{ ...@@ -479,6 +590,12 @@ class Account{
} }
delete this.errorDiv delete this.errorDiv
delete this.displayname 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.accountPassButton
delete this.accountPass delete this.accountPass
delete this.accountPassDiv delete this.accountPassDiv
......
...@@ -58,11 +58,21 @@ var assets = { ...@@ -58,11 +58,21 @@ var assets = {
"dancing-don.gif", "dancing-don.gif",
"bg-pattern-1.png", "bg-pattern-1.png",
"difficulty.png", "difficulty.png",
"don_anim_normal.png", "don_anim_normal_a.png",
"don_anim_10combo.png", "don_anim_normal_b1.png",
"don_anim_gogo.png", "don_anim_normal_b2.png",
"don_anim_gogostart.png", "don_anim_10combo_a.png",
"don_anim_clear.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", "fire_anim.png",
"fireworks_anim.png", "fireworks_anim.png",
"bg_genre_0.png", "bg_genre_0.png",
......
...@@ -44,11 +44,47 @@ class CanvasAsset{ ...@@ -44,11 +44,47 @@ class CanvasAsset{
mod(length, index){ mod(length, index){
return ((index % length) + length) % length return ((index % length) + length) % length
} }
addFrames(name, frames, image){ addFrames(name, frames, image, don){
var framesObj = { 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] framesObj.image = assets.image[image]
} }
this.animationFrames[name] = framesObj this.animationFrames[name] = framesObj
...@@ -61,6 +97,7 @@ class CanvasAsset{ ...@@ -61,6 +97,7 @@ class CanvasAsset{
if(framesObj.image){ if(framesObj.image){
this.image = framesObj.image this.image = framesObj.image
} }
this.don = framesObj.don
}else{ }else{
this.animation = false this.animation = false
} }
...@@ -100,4 +137,12 @@ class CanvasAsset{ ...@@ -100,4 +137,12 @@ class CanvasAsset{
} }
this.beatInterval = beatMS this.beatInterval = beatMS
} }
clean(){
for(var i in this.animationFrames){
var frame = this.animationFrames[i]
if(frame.cache){
frame.cache.clean()
}
}
}
} }
...@@ -91,8 +91,12 @@ class CanvasCache{ ...@@ -91,8 +91,12 @@ class CanvasCache{
return return
} }
var z = this.scale 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, 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 config.x |0, config.y |0, config.w |0, config.h |0
) )
if(saved){ if(saved){
......
...@@ -8,8 +8,16 @@ class Controller{ ...@@ -8,8 +8,16 @@ class Controller{
this.touchEnabled = touchEnabled this.touchEnabled = touchEnabled
if(multiplayer === 2){ if(multiplayer === 2){
this.snd = p2.player === 2 ? "_p1" : "_p2" this.snd = p2.player === 2 ? "_p1" : "_p2"
this.don = p2.don || defaultDon
}else{ }else{
this.snd = multiplayer ? "_p" + p2.player : "" 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" this.calibrationMode = selectedSong.folder === "calibration"
...@@ -317,6 +325,14 @@ class Controller{ ...@@ -317,6 +325,14 @@ class Controller{
return this.mekadon.play(circle) return this.mekadon.play(circle)
} }
} }
objEqual(a, b){
for(var i in a){
if(a[i] !== b[i]){
return false
}
}
return true
}
clean(){ clean(){
if(this.multiplayer === 1){ if(this.multiplayer === 1){
this.syncWith.clean() this.syncWith.clean()
......
...@@ -82,6 +82,9 @@ class Debug{ ...@@ -82,6 +82,9 @@ class Debug{
} }
} }
stopMove(event){ stopMove(event){
if(this.debugDiv.style.display === "none"){
return
}
if(!event || event.type === "resize"){ if(!event || event.type === "resize"){
var divPos = this.debugDiv.getBoundingClientRect() var divPos = this.debugDiv.getBoundingClientRect()
var x = divPos.left var x = divPos.left
......
...@@ -65,7 +65,13 @@ class Keyboard{ ...@@ -65,7 +65,13 @@ class Keyboard{
} }
keyEvent(event){ keyEvent(event){
var key = event.key.toLowerCase() 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() event.preventDefault()
} }
if(!event.repeat){ if(!event.repeat){
......
...@@ -178,6 +178,7 @@ class Loader{ ...@@ -178,6 +178,7 @@ class Loader{
account.loggedIn = true account.loggedIn = true
account.username = response.username account.username = response.username
account.displayName = response.display_name account.displayName = response.display_name
account.don = response.don
scoreStorage.load(response.scores) scoreStorage.load(response.scores)
pageEvents.send("login", account.username) pageEvents.send("login", account.username)
} }
...@@ -236,7 +237,8 @@ class Loader{ ...@@ -236,7 +237,8 @@ class Loader{
}) })
p2.send("invite", { p2.send("invite", {
id: location.hash.slice(1).toLowerCase(), 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(() => { setTimeout(() => {
if(p2.socket.readyState !== 1){ if(p2.socket.readyState !== 1){
......
...@@ -37,7 +37,18 @@ class LoadSong{ ...@@ -37,7 +37,18 @@ class LoadSong{
this.promises = [] this.promises = []
if(song.folder !== "calibration"){ if(song.folder !== "calibration"){
assets.sounds["v_start"].play() 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{ }else{
var songObj = { var songObj = {
"music": "muted", "music": "muted",
...@@ -167,6 +178,9 @@ class LoadSong{ ...@@ -167,6 +178,9 @@ class LoadSong{
}), url) }), url)
img.src = url img.src = url
} }
if(songObj.volume && songObj.volume !== 1){
this.promises.push(new Promise(resolve => setTimeout(resolve, 500)))
}
Promise.all(this.promises).then(() => { Promise.all(this.promises).then(() => {
if(!this.error){ if(!this.error){
this.setupMultiplayer() this.setupMultiplayer()
...@@ -338,7 +352,8 @@ class LoadSong{ ...@@ -338,7 +352,8 @@ class LoadSong{
p2.send("join", { p2.send("join", {
id: song.folder, id: song.folder,
diff: song.difficulty, diff: song.difficulty,
name: account.loggedIn ? account.displayName : null name: account.loggedIn ? account.displayName : null,
don: account.loggedIn ? account.don : null
}) })
}else{ }else{
this.clean() this.clean()
......
...@@ -80,6 +80,10 @@ var perf = { ...@@ -80,6 +80,10 @@ var perf = {
allImg: 0, allImg: 0,
load: 0 load: 0
} }
var defaultDon = {
body_fill: "#5fb7c1",
face_fill: "#ff5724"
}
var strings var strings
var vectors var vectors
var settings var settings
......
...@@ -131,6 +131,7 @@ class P2Connection{ ...@@ -131,6 +131,7 @@ class P2Connection{
this.hashLock = false this.hashLock = false
} }
this.name = null this.name = null
this.don = null
scoreStorage.clearP2() scoreStorage.clearP2()
break break
case "gameresults": case "gameresults":
...@@ -165,7 +166,8 @@ class P2Connection{ ...@@ -165,7 +166,8 @@ class P2Connection{
} }
break break
case "name": 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 break
case "getcrowns": case "getcrowns":
if(response.value){ if(response.value){
......
...@@ -285,6 +285,7 @@ class ScoreStorage{ ...@@ -285,6 +285,7 @@ class ScoreStorage{
account.loggedIn = false account.loggedIn = false
delete account.username delete account.username
delete account.displayName delete account.displayName
delete account.don
this.load() this.load()
pageEvents.send("logout") pageEvents.send("logout")
return Promise.reject() return Promise.reject()
......
...@@ -36,7 +36,8 @@ class Session{ ...@@ -36,7 +36,8 @@ class Session{
}) })
p2.send("invite", { p2.send("invite", {
id: null, id: null,
name: account.loggedIn ? account.displayName : null name: account.loggedIn ? account.displayName : null,
don: account.loggedIn ? account.don : null
}) })
pageEvents.send("session") pageEvents.send("session")
} }
......
...@@ -492,7 +492,7 @@ class SongSelect{ ...@@ -492,7 +492,7 @@ class SongSelect{
} }
}else if(this.state.screen === "difficulty"){ }else if(this.state.screen === "difficulty"){
var moveBy = this.diffSelMouse(mouse.x, mouse.y) 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() this.toSongSelect()
}else if(moveBy === 0){ }else if(moveBy === 0){
this.selectedDiff = 0 this.selectedDiff = 0
...@@ -596,11 +596,11 @@ class SongSelect{ ...@@ -596,11 +596,11 @@ class SongSelect{
} }
diffSelMouse(x, y){ diffSelMouse(x, y){
if(this.state.locked === 0){ 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)) 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" 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 moveBy = Math.floor((x - 550) / ((1050 - 550) / 5)) + this.diffOptions.length
var currentSong = this.songs[this.selectedSong] var currentSong = this.songs[this.selectedSong]
if( if(
...@@ -1631,17 +1631,6 @@ class SongSelect{ ...@@ -1631,17 +1631,6 @@ class SongSelect{
if(this.selectedDiff === 4 + this.diffOptions.length){ if(this.selectedDiff === 4 + this.diffOptions.length){
currentDiff = 3 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){ if(!songSel){
var highlight = 0 var highlight = 0
if(this.state.moveHover - this.diffOptions.length === i){ if(this.state.moveHover - this.diffOptions.length === i){
...@@ -1870,6 +1859,24 @@ class SongSelect{ ...@@ -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){ if(!songSel && currentSong.courses.ura){
var fade = ((ms - this.state.screenMS) % 1200) / 1200 var fade = ((ms - this.state.screenMS) % 1200) / 1200
var _x = x + 402 + 4 * 100 + fade * 25 var _x = x + 402 + 4 * 100 + fade * 25
......
...@@ -995,8 +995,23 @@ var translations = { ...@@ -995,8 +995,23 @@ var translations = {
en: "Save", en: "Save",
}, },
displayName: { displayName: {
ja: null,
en: "Displayed Name", en: "Displayed Name",
}, },
customdon: {
bodyFill: {
ja: null,
en: "Body",
},
faceFill: {
ja: null,
en: "Face",
},
reset: {
ja: null,
en: "Reset",
}
},
changePassword: { changePassword: {
ja: null, ja: null,
en: "Change Password", en: "Change Password",
...@@ -1043,6 +1058,10 @@ var translations = { ...@@ -1043,6 +1058,10 @@ var translations = {
ja: null, ja: null,
en: "Cannot use this name, please check that your new name is at most 25 characters long", 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: { current_password_invalid: {
ja: null, ja: null,
en: "Current password does not match", en: "Current password does not match",
......
...@@ -9,18 +9,17 @@ class ViewAssets{ ...@@ -9,18 +9,17 @@ class ViewAssets{
this.don = this.createAsset("background", frame => { this.don = this.createAsset("background", frame => {
var imgw = 360 var imgw = 360
var imgh = 184 var imgh = 184
var scale = 165
var w = imgw var w = imgw
var h = imgh var h = imgh
return { return {
sx: Math.floor(frame / 10) * imgw, sx: Math.floor(frame / 10) * (imgw + 2),
sy: (frame % 10) * imgh + 1, sy: (frame % 10) * (imgh + 2),
sw: imgw, sw: imgw,
sh: imgh - 1, sh: imgh,
x: view.portrait ? -60 : 0, x: view.portrait ? -60 : 0,
y: view.portrait ? (view.player === 2 ? 560 : 35) : (view.player === 2 ? 360 : 2), y: view.portrait ? (view.player === 2 ? 560 : 35) : (view.player === 2 ? 360 : 0),
w: w, w: w / h * (h + 0.5),
h: h - 1 h: h + 0.5
} }
}) })
this.don.addFrames("normal", [ this.don.addFrames("normal", [
...@@ -28,15 +27,15 @@ class ViewAssets{ ...@@ -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 ,4 ,3 ,2 ,1 ,
0 ,0 ,0 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,6 ,5 ,7 ,8 ,9 ,10, 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 11,11,11,11,10,9 ,8 ,7 ,13,12,12,13,14,15,16,17
], "don_anim_normal") ], "don_anim_normal", this.controller.don)
this.don.addFrames("10combo", 22, "don_anim_10combo") this.don.addFrames("10combo", 22, "don_anim_10combo", this.controller.don)
this.don.addFrames("gogo", [ this.don.addFrames("gogo", [
42,43,43,44,45,46,47,48,49,50,51,52,53,54, 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, 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, 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 27,28,29,30,31,32,33,34,35,36,37,38,39,40,41
], "don_anim_gogo") ], "don_anim_gogo", this.controller.don)
this.don.addFrames("gogostart", 27, "don_anim_gogostart") this.don.addFrames("gogostart", 27, "don_anim_gogostart", this.controller.don)
this.don.normalAnimation = () => { this.don.normalAnimation = () => {
if(this.view.gogoTime){ if(this.view.gogoTime){
var length = this.don.getAnimationLength("gogo") var length = this.don.getAnimationLength("gogo")
...@@ -58,7 +57,7 @@ class ViewAssets{ ...@@ -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() this.don.normalAnimation()
// Bar // Bar
...@@ -176,6 +175,7 @@ class ViewAssets{ ...@@ -176,6 +175,7 @@ class ViewAssets{
}) })
} }
clean(){ clean(){
this.don.clean()
delete this.ctx delete this.ctx
delete this.don delete this.don
delete this.fire delete this.fire
......
...@@ -7,6 +7,20 @@ ...@@ -7,6 +7,20 @@
<div class="displayname-hint"></div> <div class="displayname-hint"></div>
<input type="text" class="displayname" maxlength="25"> <input type="text" class="displayname" maxlength="25">
</div> </div>
<div class="customdon-div">
<canvas class="customdon-canvas" width="254" height="184"></canvas>
<div>
<label>
<input type="color" class="customdon-bodyfill">
</label>
<label>
<input type="color" class="customdon-facefill">
</label>
<label>
<input type="button" class="customdon-reset" value="Reset">
</label>
</div>
</div>
<form class="accountpass-form"> <form class="accountpass-form">
<div> <div>
<div class="accountpass-btn taibtn stroke-sub link-btn"></div> <div class="accountpass-btn taibtn stroke-sub link-btn"></div>
......
...@@ -34,6 +34,15 @@ update_display_name = { ...@@ -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 = { update_password = {
'$schema': 'http://json-schema.org/schema#', '$schema': 'http://json-schema.org/schema#',
'type': 'object', 'type': 'object',
......
...@@ -43,7 +43,8 @@ async def connection(ws, path): ...@@ -43,7 +43,8 @@ async def connection(ws, path):
"ws": ws, "ws": ws,
"action": "ready", "action": "ready",
"session": False, "session": False,
"name": None "name": None,
"don": None
} }
server_status["users"].append(user) server_status["users"].append(user)
try: try:
...@@ -81,6 +82,7 @@ async def connection(ws, path): ...@@ -81,6 +82,7 @@ async def connection(ws, path):
id = value["id"] if "id" in value else None id = value["id"] if "id" in value else None
diff = value["diff"] if "diff" in value else None diff = value["diff"] if "diff" in value else None
user["name"] = value["name"] if "name" 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: if not id or not diff:
continue continue
if id not in waiting: if id not in waiting:
...@@ -95,6 +97,7 @@ async def connection(ws, path): ...@@ -95,6 +97,7 @@ async def connection(ws, path):
else: else:
# Join the other user and start game # Join the other user and start game
user["name"] = value["name"] if "name" in value else None 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"] user["other_user"] = waiting[id]["user"]
waiting_diff = waiting[id]["diff"] waiting_diff = waiting[id]["diff"]
del waiting[id] del waiting[id]
...@@ -107,8 +110,14 @@ async def connection(ws, path): ...@@ -107,8 +110,14 @@ async def connection(ws, path):
await asyncio.wait([ await asyncio.wait([
ws.send(msgobj("gameload", {"diff": waiting_diff, "player": 2})), ws.send(msgobj("gameload", {"diff": waiting_diff, "player": 2})),
user["other_user"]["ws"].send(msgobj("gameload", {"diff": diff, "player": 1})), user["other_user"]["ws"].send(msgobj("gameload", {"diff": diff, "player": 1})),
ws.send(msgobj("name", user["other_user"]["name"])), ws.send(msgobj("name", {
user["other_user"]["ws"].send(msgobj("name", user["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: else:
# Wait for another user # Wait for another user
...@@ -130,10 +139,12 @@ async def connection(ws, path): ...@@ -130,10 +139,12 @@ async def connection(ws, path):
user["action"] = "invite" user["action"] = "invite"
user["session"] = invite user["session"] = invite
user["name"] = value["name"] if "name" in value else None 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)) await ws.send(msgobj("invite", invite))
elif value and "id" in value and value["id"] in server_status["invites"]: elif value and "id" in value and value["id"] in server_status["invites"]:
# Join a session with the other user # Join a session with the other user
user["name"] = value["name"] if "name" in value else None 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"]] user["other_user"] = server_status["invites"][value["id"]]
del server_status["invites"][value["id"]] del server_status["invites"][value["id"]]
if "ws" in user["other_user"]: if "ws" in user["other_user"]:
...@@ -146,8 +157,14 @@ async def connection(ws, path): ...@@ -146,8 +157,14 @@ async def connection(ws, path):
ws.send(msgobj("session", {"player": 2})), ws.send(msgobj("session", {"player": 2})),
user["other_user"]["ws"].send(msgobj("session", {"player": 1})), user["other_user"]["ws"].send(msgobj("session", {"player": 1})),
ws.send(msgobj("invite")), ws.send(msgobj("invite")),
ws.send(msgobj("name", user["other_user"]["name"])), ws.send(msgobj("name", {
user["other_user"]["ws"].send(msgobj("name", user["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: else:
del user["other_user"] del user["other_user"]
......
{% extends 'admin.html' %} {% extends 'admin.html' %}
{% block content %} {% block content %}
<h1>{{ song.title }} <small>(ID: {{ song.id }})</small></h1> {% if song.title_lang.en %}
<h1>{{ song.title_lang.en }} <small>({{ song.title }})</small> <small class="song-id">(ID: {{ song.id }})</small></h1>
{% else %}
<h1>{{ song.title }} <small class="song-id">(ID: {{ song.id }})</small></h1>
{% endif %}
{% for cat, message in get_flashed_messages(with_categories=true) %} {% for cat, message in get_flashed_messages(with_categories=true) %}
<div class="message{% if cat %} message-{{cat}}{% endif %}">{{ message }}</div> <div class="message{% if cat %} message-{{cat}}{% endif %}">{{ message }}</div>
{% endfor %} {% endfor %}
...@@ -116,9 +120,14 @@ ...@@ -116,9 +120,14 @@
</select> </select>
</div> </div>
<div class="form-field">
<p><label for="lyrics">Lyrics</label></p>
<span class="checkbox"><input type="checkbox" name="lyrics" id="lyrics"{% if song.lyrics %} checked{% endif %}><label for="lyrics"> Enabled</label></span>
</div>
<div class="form-field"> <div class="form-field">
<p><label for="hash">Hash</label></p> <p><label for="hash">Hash</label></p>
<input type="text" id="hash" value="{{song.hash}}" name="hash"> <span class="checkbox"><input type="checkbox" name="gen_hash" id="gen_hash"{><label for="gen_hash"> Generate</label></span> <input type="text" id="hash" value="{{song.hash}}" name="hash"> <span class="checkbox"><input type="checkbox" name="gen_hash" id="gen_hash"><label for="gen_hash"> Generate</label></span>
</div> </div>
<button type="submit" class="save-song">Save</button> <button type="submit" class="save-song">Save</button>
......
...@@ -116,6 +116,11 @@ ...@@ -116,6 +116,11 @@
</select> </select>
</div> </div>
<div class="form-field">
<p><label for="lyrics">Lyrics</label></p>
<span class="checkbox"><input type="checkbox" name="lyrics" id="lyrics"><label for="lyrics"> Enabled</label></span>
</div>
<button type="submit" class="save-song">Save</button> <button type="submit" class="save-song">Save</button>
</form> </form>
</div> </div>
......
...@@ -11,9 +11,9 @@ ...@@ -11,9 +11,9 @@
<a href="/admin/songs/{{ song.id }}" class="song-link"> <a href="/admin/songs/{{ song.id }}" class="song-link">
<div class="song"> <div class="song">
{% if song.title_lang.en %} {% if song.title_lang.en %}
<p>{{ song.title_lang.en }} <small>({{ song.title }})</small></p> <p><span class="song-id">{{ song.id }}.</span> {{ song.title_lang.en }} <small>({{ song.title }})</small></p>
{% else %} {% else %}
<p>{{ song.title }}</p> <p><span class="song-id">{{ song.id }}.</span> {{ song.title }}</p>
{% endif %} {% endif %}
</div> </div>
</a> </a>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment