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
61
app.py
@ -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():
|
|||||||
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):
|
|||||||
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,6 +391,7 @@ 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({
|
||||||
@ -373,6 +399,7 @@ def route_api_register():
|
|||||||
'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():
|
|||||||
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'])
|
||||||
@ -401,11 +428,13 @@ def route_api_login():
|
|||||||
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():
|
|||||||
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():
|
|||||||
})
|
})
|
||||||
|
|
||||||
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):
|
||||||
|
Before Width: | Height: | Size: 2.0 KiB |
BIN
public/assets/img/don_anim_10combo_a.png
Normal file
After Width: | Height: | Size: 340 B |
BIN
public/assets/img/don_anim_10combo_b1.png
Normal file
After Width: | Height: | Size: 340 B |
BIN
public/assets/img/don_anim_10combo_b2.png
Normal file
After Width: | Height: | Size: 340 B |
Before Width: | Height: | Size: 2.0 KiB |
BIN
public/assets/img/don_anim_clear_a.png
Normal file
After Width: | Height: | Size: 340 B |
BIN
public/assets/img/don_anim_clear_b1.png
Normal file
After Width: | Height: | Size: 340 B |
BIN
public/assets/img/don_anim_clear_b2.png
Normal file
After Width: | Height: | Size: 340 B |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 3.9 KiB |
BIN
public/assets/img/don_anim_gogo_a.png
Normal file
After Width: | Height: | Size: 585 B |
BIN
public/assets/img/don_anim_gogo_b1.png
Normal file
After Width: | Height: | Size: 585 B |
BIN
public/assets/img/don_anim_gogo_b2.png
Normal file
After Width: | Height: | Size: 585 B |
Before Width: | Height: | Size: 2.0 KiB |
BIN
public/assets/img/don_anim_gogostart_a.png
Normal file
After Width: | Height: | Size: 340 B |
BIN
public/assets/img/don_anim_gogostart_b1.png
Normal file
After Width: | Height: | Size: 340 B |
BIN
public/assets/img/don_anim_gogostart_b2.png
Normal file
After Width: | Height: | Size: 340 B |
Before Width: | Height: | Size: 1.4 KiB |
BIN
public/assets/img/don_anim_normal_a.png
Normal file
After Width: | Height: | Size: 259 B |
BIN
public/assets/img/don_anim_normal_b1.png
Normal file
After Width: | Height: | Size: 259 B |
BIN
public/assets/img/don_anim_normal_b2.png
Normal file
After Width: | Height: | Size: 259 B |
@ -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 @@
|
|||||||
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{
|
|||||||
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{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
@ -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{
|
|||||||
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{
|
|||||||
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{
|
|||||||
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{
|
|||||||
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{
|
|||||||
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{
|
|||||||
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{
|
|||||||
}
|
}
|
||||||
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 = {
|
|||||||
"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{
|
|||||||
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{
|
|||||||
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{
|
|||||||
}
|
}
|
||||||
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{
|
|||||||
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{
|
|||||||
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{
|
|||||||
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{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
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{
|
|||||||
}
|
}
|
||||||
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{
|
|||||||
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{
|
|||||||
})
|
})
|
||||||
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{
|
|||||||
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{
|
|||||||
}), 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{
|
|||||||
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 = {
|
|||||||
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{
|
|||||||
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{
|
|||||||
}
|
}
|
||||||
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{
|
|||||||
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()
|
||||||
|
@ -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{
|
|||||||
}
|
}
|
||||||
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{
|
|||||||
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{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 = {
|
|||||||
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 = {
|
|||||||
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{
|
|||||||
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{
|
|||||||
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{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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{
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
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 @@
|
|||||||
<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 = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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',
|
||||||
|
27
server.py
@ -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):
|
|||||||
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):
|
|||||||
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):
|
|||||||
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):
|
|||||||
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):
|
|||||||
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"]
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
{% 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 @@
|
|||||||
</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 @@
|
|||||||
</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 @@
|
|||||||
<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>
|
||||||
|