Merge pull request #175 from bui/cat-jump

SongSelect: category jumping
This commit is contained in:
Bui 2020-02-22 18:11:14 +00:00 committed by GitHub
commit 09c212df8b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 224 additions and 43 deletions

2
.gitignore vendored
View File

@ -35,6 +35,8 @@ $RECYCLE.BIN/
.Spotlight-V100 .Spotlight-V100
.Trashes .Trashes
.vscode
# Directories potentially created on remote AFP share # Directories potentially created on remote AFP share
.AppleDB .AppleDB
.AppleDesktop .AppleDesktop

2
app.py
View File

@ -86,7 +86,7 @@ def route_index():
@app.route('/api/preview') @app.route('/api/preview')
@app.cache.cached(timeout=15) @app.cache.cached(timeout=15, query_string=True)
def route_api_preview(): def route_api_preview():
song_id = request.args.get('id', None) song_id = request.args.get('id', None)
if not song_id or not re.match('^[0-9]+$', song_id): if not song_id or not re.match('^[0-9]+$', song_id):

Binary file not shown.

View File

@ -66,5 +66,14 @@
"", "",
"logo5": "logo5":
"",
"category":
"",
"categoryShadow":
"",
"categoryHighlight":
"" ""
} }

View File

@ -90,6 +90,7 @@ var assets = {
"se_don.wav", "se_don.wav",
"se_ka.wav", "se_ka.wav",
"se_pause.wav", "se_pause.wav",
"se_jump.wav",
"se_calibration.wav", "se_calibration.wav",
"v_results.wav", "v_results.wav",

View File

@ -45,6 +45,12 @@
shadow: new Path2D(vectors.optionsShadow) shadow: new Path2D(vectors.optionsShadow)
} }
this.categoryPath = {
main: new Path2D(vectors.category),
shadow: new Path2D(vectors.categoryShadow),
highlight: new Path2D(vectors.categoryHighlight)
}
this.regex = { this.regex = {
comma: /[,.]/, comma: /[,.]/,
ideographicComma: /[、。]/, ideographicComma: /[、。]/,
@ -225,10 +231,14 @@
highlight(config){ highlight(config){
var ctx = config.ctx var ctx = config.ctx
ctx.save() ctx.save()
var _x = config.x + 3.5 if(config.shape){
var _y = config.y + 3.5 ctx.translate(config.x, config.y)
var _w = config.w - 7 }else{
var _h = config.h - 7 var _x = config.x + 3.5
var _y = config.y + 3.5
var _w = config.w - 7
var _h = config.h - 7
}
if(config.animate){ if(config.animate){
ctx.globalAlpha = this.fade((this.getMS() - config.animateMS) % 2000 / 2000) ctx.globalAlpha = this.fade((this.getMS() - config.animateMS) % 2000 / 2000)
}else if(config.opacity){ }else if(config.opacity){
@ -243,19 +253,25 @@
h: _h, h: _h,
radius: config.radius radius: config.radius
}) })
}else{ }else if(!config.shape){
ctx.beginPath() ctx.beginPath()
ctx.rect(_x, _y, _w, _h) ctx.rect(_x, _y, _w, _h)
} }
if(config.shape){
var stroke = () => ctx.stroke(config.shape)
}else{
var stroke = () => ctx.stroke()
}
var size = config.size || 14
ctx.strokeStyle = "rgba(255, 249, 1, 0.45)" ctx.strokeStyle = "rgba(255, 249, 1, 0.45)"
ctx.lineWidth = 14 ctx.lineWidth = size
ctx.stroke() stroke()
ctx.strokeStyle = "rgba(255, 249, 1, .8)" ctx.strokeStyle = "rgba(255, 249, 1, .8)"
ctx.lineWidth = 8 ctx.lineWidth = 8 / 14 * size
ctx.stroke() stroke()
ctx.strokeStyle = "#fff" ctx.strokeStyle = "#fff"
ctx.lineWidth = 6 ctx.lineWidth = 6 / 14 * size
ctx.stroke() stroke()
ctx.restore() ctx.restore()
} }
@ -1472,6 +1488,48 @@
ctx.restore() ctx.restore()
} }
category(config){
var ctx = config.ctx
ctx.save()
ctx.translate(config.x, config.y)
if(config.scale || config.right){
ctx.scale((config.right ? -1 : 1) * (config.scale || 1), config.scale || 1)
}
ctx.translate(-15.5 + 14, -11)
for(var i = 0; i < 4; i++){
if(i < 2){
ctx.lineWidth = 6
ctx.strokeStyle = "#000"
ctx.stroke(this.categoryPath.main)
}else{
ctx.fillStyle = config.fill
ctx.fill(this.categoryPath.main)
ctx.fillStyle = "rgba(255, 255, 255, 0.25)"
ctx.fill(this.categoryPath.main)
ctx.fillStyle = "rgba(0, 0, 0, 0.25)"
ctx.fill(this.categoryPath.shadow)
}
if(i % 2 === 0){
ctx.translate(-14, 0)
}else if(i === 1){
ctx.translate(14, 0)
}
}
if(config.highlight){
this.highlight({
ctx: ctx,
x: 0,
y: 0,
opacity: 0.8,
shape: this.categoryPath.highlight,
size: 8
})
}
ctx.restore()
}
alpha(amount, ctx, callback, winW, winH){ alpha(amount, ctx, callback, winW, winH){
if(amount >= 1){ if(amount >= 1){
return callback(ctx) return callback(ctx)

View File

@ -123,7 +123,8 @@ class SongSelect{
songSkin: song.song_skin || {}, songSkin: song.song_skin || {},
music: song.music, music: song.music,
volume: song.volume, volume: song.volume,
maker: song.maker maker: song.maker,
canJump: true
}) })
} }
this.songs.sort((a, b) => { this.songs.sort((a, b) => {
@ -144,7 +145,8 @@ class SongSelect{
title: strings.randomSong, title: strings.randomSong,
skin: this.songSkin.random, skin: this.songSkin.random,
action: "random", action: "random",
category: strings.random category: strings.random,
canJump: true
}) })
if(touchEnabled){ if(touchEnabled){
if(fromTutorial === "tutorial"){ if(fromTutorial === "tutorial"){
@ -290,7 +292,8 @@ class SongSelect{
locked: true, locked: true,
hasPointer: false, hasPointer: false,
options: 0, options: 0,
selLock: false selLock: false,
catJump: false
} }
this.songSelecting = { this.songSelecting = {
speed: 800, speed: 800,
@ -315,13 +318,15 @@ class SongSelect{
this.gamepad = new Gamepad({ this.gamepad = new Gamepad({
confirm: ["b", "start", "ls", "rs"], confirm: ["b", "start", "ls", "rs"],
back: ["a"], back: ["a"],
left: ["l", "lb", "lt", "lsl"], left: ["l", "lsl", "lt"],
right: ["r", "rb", "rt", "lsr"], right: ["r", "lsr", "rt"],
up: ["u", "lsu"], up: ["u", "lsu"],
down: ["d", "lsd"], down: ["d", "lsd"],
session: ["back"], session: ["back"],
ctrl: ["y"], ctrl: ["y"],
shift: ["x"] shift: ["x"],
jump_left: ["lb"],
jump_right: ["rb"]
}, this.keyPress.bind(this)) }, this.keyPress.bind(this))
if(!assets.customSongs){ if(!assets.customSongs){
@ -353,7 +358,7 @@ class SongSelect{
} }
} }
keyPress(pressed, name, event){ keyPress(pressed, name, event, repeat){
if(pressed){ if(pressed){
if(!this.pressedKeys[name]){ if(!this.pressedKeys[name]){
this.pressedKeys[name] = this.getMS() + 300 this.pressedKeys[name] = this.getMS() + 300
@ -365,6 +370,7 @@ class SongSelect{
if(name === "ctrl" || name === "shift" || !this.redrawRunning){ if(name === "ctrl" || name === "shift" || !this.redrawRunning){
return return
} }
var shift = event ? event.shiftKey : this.pressedKeys["shift"]
if(this.state.screen === "song"){ if(this.state.screen === "song"){
if(name === "confirm"){ if(name === "confirm"){
this.toSelectDifficulty() this.toSelectDifficulty()
@ -373,9 +379,25 @@ class SongSelect{
}else if(name === "session"){ }else if(name === "session"){
this.toSession() this.toSession()
}else if(name === "left"){ }else if(name === "left"){
this.moveToSong(-1) if(shift){
if(!repeat){
this.categoryJump(-1)
}
}else{
this.moveToSong(-1)
}
}else if(name === "right"){ }else if(name === "right"){
this.moveToSong(1) if(shift){
if(!repeat){
this.categoryJump(1)
}
}else{
this.moveToSong(1)
}
}else if(name === "jump_left" && !repeat){
this.categoryJump(-1)
}else if(name === "jump_right" && !repeat){
this.categoryJump(1)
} }
}else if(this.state.screen === "difficulty"){ }else if(this.state.screen === "difficulty"){
if(name === "confirm"){ if(name === "confirm"){
@ -384,7 +406,7 @@ class SongSelect{
}else if(this.selectedDiff === 1){ }else if(this.selectedDiff === 1){
this.toOptions(1) this.toOptions(1)
}else{ }else{
this.toLoadSong(this.selectedDiff - this.diffOptions.length, this.pressedKeys["shift"], this.pressedKeys["ctrl"]) this.toLoadSong(this.selectedDiff - this.diffOptions.length, shift, this.pressedKeys["ctrl"])
} }
}else if(name === "back" || name === "session"){ }else if(name === "back" || name === "session"){
this.toSongSelect() this.toSongSelect()
@ -424,7 +446,9 @@ class SongSelect{
var touch = true var touch = true
} }
if(this.state.screen === "song"){ if(this.state.screen === "song"){
if(mouse.x > 641 && mouse.y > 603){ if(20 < mouse.y && mouse.y < 90 && 410 < mouse.x && mouse.x < 880 && (mouse.x < 540 || mouse.x > 750)){
this.categoryJump(mouse.x < 640 ? -1 : 1)
}else if(mouse.x > 641 && mouse.y > 603){
this.toSession() this.toSession()
}else{ }else{
var moveBy = this.songSelMouse(mouse.x, mouse.y) var moveBy = this.songSelMouse(mouse.x, mouse.y)
@ -473,7 +497,9 @@ class SongSelect{
var mouse = this.mouseOffset(event.offsetX, event.offsetY) var mouse = this.mouseOffset(event.offsetX, event.offsetY)
var moveTo = null var moveTo = null
if(this.state.screen === "song"){ if(this.state.screen === "song"){
if(mouse.x > 641 && mouse.y > 603 && p2.socket.readyState === 1 && !assets.customSongs){ if(20 < mouse.y && mouse.y < 90 && 410 < mouse.x && mouse.x < 880 && (mouse.x < 540 || mouse.x > 750)){
moveTo = mouse.x < 640 ? "categoryPrev" : "categoryNext"
}else if(mouse.x > 641 && mouse.y > 603 && p2.socket.readyState === 1 && !assets.customSongs){
moveTo = "session" moveTo = "session"
}else{ }else{
var moveTo = this.songSelMouse(mouse.x, mouse.y) var moveTo = this.songSelMouse(mouse.x, mouse.y)
@ -580,6 +606,20 @@ class SongSelect{
this.pointer(false) this.pointer(false)
} }
} }
categoryJump(moveBy){
if(this.state.locked === 1){
return
}
this.state.catJump = true
this.state.move = moveBy;
this.state.locked = 1
this.endPreview()
this.playSound("se_jump")
}
moveToDiff(moveBy){ moveToDiff(moveBy){
if(this.state.locked !== 1){ if(this.state.locked !== 1){
this.state.move = moveBy this.state.move = moveBy
@ -787,7 +827,7 @@ class SongSelect{
for(var key in this.pressedKeys){ for(var key in this.pressedKeys){
if(this.pressedKeys[key]){ if(this.pressedKeys[key]){
if(ms >= this.pressedKeys[key] + 50){ if(ms >= this.pressedKeys[key] + 50){
this.keyPress(true, key) this.keyPress(true, key, null, true)
this.pressedKeys[key] = ms this.pressedKeys[key] = ms
} }
} }
@ -949,16 +989,31 @@ class SongSelect{
}) })
var category = this.songs[this.selectedSong].category var category = this.songs[this.selectedSong].category
if(category){ var selectedSong = this.songs[this.selectedSong]
var selectedSong = this.songs[this.selectedSong] this.draw.category({
this.categoryCache.get({ ctx: ctx,
ctx: ctx, x: winW / 2 - 280 / 2 - 30,
x: winW / 2 - 280 / 2, y: frameTop + 60,
y: frameTop, fill: selectedSong.skin.background,
w: 280, highlight: this.state.moveHover === "categoryPrev"
h: this.songAsset.marginTop, })
id: category + selectedSong.skin.outline this.draw.category({
}, ctx => { ctx: ctx,
x: winW / 2 + 280 / 2 + 30,
y: frameTop + 60,
right: true,
fill: selectedSong.skin.background,
highlight: this.state.moveHover === "categoryNext"
})
this.categoryCache.get({
ctx: ctx,
x: winW / 2 - 280 / 2,
y: frameTop,
w: 280,
h: this.songAsset.marginTop,
id: category + selectedSong.skin.outline
}, ctx => {
if(category){
if(category in strings.categories){ if(category in strings.categories){
var categoryName = strings.categories[category] var categoryName = strings.categories[category]
}else{ }else{
@ -978,8 +1033,8 @@ class SongSelect{
{outline: selectedSong.skin.outline, letterBorder: 12, shadow: [3, 3, 3]}, {outline: selectedSong.skin.outline, letterBorder: 12, shadow: [3, 3, 3]},
{fill: "#fff"} {fill: "#fff"}
]) ])
}) }
} })
} }
if(screen === "song"){ if(screen === "song"){
@ -994,16 +1049,67 @@ class SongSelect{
var resize2 = changeSpeed - resize var resize2 = changeSpeed - resize
var scroll = resize2 - resize - scrollDelay * 2 var scroll = resize2 - resize - scrollDelay * 2
var elapsed = ms - this.state.moveMS var elapsed = ms - this.state.moveMS
if(this.state.move && ms > this.state.moveMS + resize2 - scrollDelay){
this.playSound("se_ka") if(this.state.catJump || (this.state.move && ms > this.state.moveMS + resize2 - scrollDelay)){
var isJump = this.state.catJump
var previousSelectedSong = this.selectedSong var previousSelectedSong = this.selectedSong
this.selectedSong = this.mod(this.songs.length, this.selectedSong + this.state.move)
if(!isJump){
this.playSound("se_ka")
this.selectedSong = this.mod(this.songs.length, this.selectedSong + this.state.move)
}else{
var currentCat = this.songs[this.selectedSong].category
var currentIdx = this.mod(this.songs.length, this.selectedSong)
if(this.state.move > 0){
var nextSong = this.songs.find(song => this.mod(this.songs.length, this.songs.indexOf(song)) > currentIdx && song.category !== currentCat && song.canJump)
if(!nextSong){
nextSong = this.songs[0]
}
}else{
var isFirstInCat = this.songs.findIndex(song => song.category === currentCat) == this.selectedSong
if(!isFirstInCat){
var nextSong = this.songs.find(song => this.mod(this.songs.length, this.songs.indexOf(song)) < currentIdx && song.category === currentCat && song.canJump)
}else{
var idx = this.songs.length - 1
var nextSong
var lastCat
for(;idx>=0;idx--){
if(this.songs[idx].category !== lastCat && this.songs[idx].action !== "back"){
lastCat = this.songs[idx].category
if(nextSong){
break
}
}
if(lastCat !== currentCat && idx < currentIdx){
nextSong = idx
}
}
nextSong = this.songs[nextSong]
}
if(!nextSong){
var rev = [...this.songs].reverse()
nextSong = rev.find(song => song.canJump)
}
}
this.selectedSong = this.songs.indexOf(nextSong)
this.state.catJump = false
}
if(previousSelectedSong !== this.selectedSong){ if(previousSelectedSong !== this.selectedSong){
pageEvents.send("song-select-move", this.songs[this.selectedSong]) pageEvents.send("song-select-move", this.songs[this.selectedSong])
} }
this.state.move = 0 this.state.move = 0
this.state.locked = 2 this.state.locked = 2
localStorage["selectedSong"] = this.selectedSong if(assets.customSongs){
assets.customSelected = this.selectedSong
}else if(!p2.session){
try{
localStorage["selectedSong"] = this.selectedSong
}catch(e){}
}
if(this.songs[this.selectedSong].action !== "back"){ if(this.songs[this.selectedSong].action !== "back"){
var cat = this.songs[this.selectedSong].category var cat = this.songs[this.selectedSong].category

View File

@ -96,6 +96,7 @@
otherControls: "他のコントロール", otherControls: "他のコントロール",
otherTutorial: [ otherTutorial: [
"%sはゲームを一時停止します", "%sはゲームを一時停止します",
"曲をえらぶしながら%sか%sキーを押してジャンルをスキップします",
"むずかしさをえらぶしながら%sキーを押しながらオートモードを有効", "むずかしさをえらぶしながら%sキーを押しながらオートモードを有効",
"むずかしさをえらぶしながら%sキーを押しながらネットプレイモードを有効" "むずかしさをえらぶしながら%sキーを押しながらネットプレイモードを有効"
], ],
@ -287,6 +288,7 @@ function StringsEn(){
otherControls: "Other controls", otherControls: "Other controls",
otherTutorial: [ otherTutorial: [
"%s \u2014 pause game", "%s \u2014 pause game",
'%s and %s while selecting song \u2014 navigate categories',
"%s while selecting difficulty \u2014 enable autoplay mode", "%s while selecting difficulty \u2014 enable autoplay mode",
"%s while selecting difficulty \u2014 enable 2P mode" "%s while selecting difficulty \u2014 enable 2P mode"
], ],
@ -478,6 +480,7 @@ function StringsCn(){
otherControls: "其他控制", otherControls: "其他控制",
otherTutorial: [ otherTutorial: [
"%s暂停游戏", "%s暂停游戏",
'%s and %s while selecting song \u2014 navigate categories',
"选择难度时按住%s以启用自动模式", "选择难度时按住%s以启用自动模式",
"选择难度时按住%s以启用网络对战模式" "选择难度时按住%s以启用网络对战模式"
], ],
@ -669,6 +672,7 @@ function StringsTw(){
otherControls: "其他控制", otherControls: "其他控制",
otherTutorial: [ otherTutorial: [
"%s暫停遊戲", "%s暫停遊戲",
'%s and %s while selecting song \u2014 navigate categories',
"選擇難度時按住%s以啟用自動模式", "選擇難度時按住%s以啟用自動模式",
"選擇難度時按住%s以啟用網上對打模式" "選擇難度時按住%s以啟用網上對打模式"
], ],
@ -860,6 +864,7 @@ function StringsKo(){
otherControls: "기타 컨트롤", otherControls: "기타 컨트롤",
otherTutorial: [ otherTutorial: [
"%s \u2014 게임을 일시 중지합니다", "%s \u2014 게임을 일시 중지합니다",
'%s and %s while selecting song \u2014 navigate categories',
"난이도 선택 동안 %s 홀드 \u2014 오토 모드 활성화", "난이도 선택 동안 %s 홀드 \u2014 오토 모드 활성화",
"난이도 선택 동안 %s 홀드 \u2014 넷 플레이 모드 활성화" "난이도 선택 동안 %s 홀드 \u2014 넷 플레이 모드 활성화"
], ],

View File

@ -59,13 +59,13 @@ class Tutorial{
this.endButton.setAttribute("alt", strings.tutorial.ok) this.endButton.setAttribute("alt", strings.tutorial.ok)
this.tutorialDiv.innerHTML = "" this.tutorialDiv.innerHTML = ""
var kbdSettings = settings.getItem("keyboardSettings") var kbdSettings = settings.getItem("keyboardSettings")
var pauseKey = pageEvents.kbd.indexOf("q") === -1 ? "Q" : "ESC" var pauseKey = "ESC"
var keys = [ var keys = [
kbdSettings.don_l[0].toUpperCase(), kbdSettings.don_l[0].toUpperCase(),
kbdSettings.don_r[0].toUpperCase(), kbdSettings.don_r[0].toUpperCase(),
kbdSettings.ka_l[0].toUpperCase(), kbdSettings.ka_l[0].toUpperCase(),
kbdSettings.ka_r[0].toUpperCase(), kbdSettings.ka_r[0].toUpperCase(),
pauseKey, "SHIFT", "CTRL" pauseKey, "SHIFT+LEFT", "SHIFT+RIGHT", "SHIFT", "CTRL"
] ]
var keyIndex = 0 var keyIndex = 0
strings.tutorial.basics.forEach(string => { strings.tutorial.basics.forEach(string => {