mirror of
https://github.com/jiojciojsioe3/a3cjroijsiojiorj.git
synced 2024-11-15 07:21:50 +08:00
Merge pull request #65 from LoveEevee/p2-add-multiplayer-session
P2: Add multiplayer session
This commit is contained in:
commit
29176e896e
@ -214,6 +214,7 @@ kbd{
|
|||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: 1px solid #a9a9a9;
|
border: 1px solid #a9a9a9;
|
||||||
|
user-select: all;
|
||||||
}
|
}
|
||||||
.text-warn{
|
.text-warn{
|
||||||
color: #d00;
|
color: #d00;
|
||||||
@ -226,6 +227,21 @@ kbd{
|
|||||||
.nowrap{
|
.nowrap{
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
#session-invite{
|
||||||
|
width: 100%;
|
||||||
|
height: 1.9em;
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 2em;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #a9a9a9;
|
||||||
|
padding: 0.3em;
|
||||||
|
margin: 0.3em 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
text-align: center;
|
||||||
|
user-select: all;
|
||||||
|
cursor: text;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
@keyframes bgscroll{
|
@keyframes bgscroll{
|
||||||
from{
|
from{
|
||||||
background-position: 0 top;
|
background-position: 0 top;
|
||||||
|
@ -31,6 +31,7 @@ var assets = {
|
|||||||
"bg_genre_7.png",
|
"bg_genre_7.png",
|
||||||
"bg_score_p1.png",
|
"bg_score_p1.png",
|
||||||
"bg_score_p2.png",
|
"bg_score_p2.png",
|
||||||
|
"bg_settings.png",
|
||||||
"badge_auto.png",
|
"badge_auto.png",
|
||||||
"touch_drum.png",
|
"touch_drum.png",
|
||||||
"touch_pause.png",
|
"touch_pause.png",
|
||||||
@ -117,7 +118,8 @@ var assets = {
|
|||||||
"titlescreen.html",
|
"titlescreen.html",
|
||||||
"tutorial.html",
|
"tutorial.html",
|
||||||
"about.html",
|
"about.html",
|
||||||
"debug.html"
|
"debug.html",
|
||||||
|
"session.html"
|
||||||
],
|
],
|
||||||
|
|
||||||
"songs": [],
|
"songs": [],
|
||||||
|
@ -188,6 +188,11 @@
|
|||||||
|
|
||||||
ctx.restore()
|
ctx.restore()
|
||||||
|
|
||||||
|
if(config.disabled){
|
||||||
|
ctx.fillStyle = "rgba(0, 0, 0, 0.5)"
|
||||||
|
ctx.fillRect(x, y, w, h)
|
||||||
|
}
|
||||||
|
|
||||||
if(config.highlight){
|
if(config.highlight){
|
||||||
this.highlight({
|
this.highlight({
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
|
@ -141,7 +141,7 @@ class Controller{
|
|||||||
}
|
}
|
||||||
displayResults(){
|
displayResults(){
|
||||||
if(this.multiplayer !== 2){
|
if(this.multiplayer !== 2){
|
||||||
this.scoresheet = new Scoresheet(this, this.getGlobalScore(), this.multiplayer)
|
this.scoresheet = new Scoresheet(this, this.getGlobalScore(), this.multiplayer, this.touchEnabled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
displayScore(score, notPlayed, bigNote){
|
displayScore(score, notPlayed, bigNote){
|
||||||
|
@ -294,7 +294,9 @@ class Game{
|
|||||||
this.musicFadeOut++
|
this.musicFadeOut++
|
||||||
}else if(this.musicFadeOut === 1 && ms >= started + 1600){
|
}else if(this.musicFadeOut === 1 && ms >= started + 1600){
|
||||||
this.controller.gameEnded()
|
this.controller.gameEnded()
|
||||||
|
if(!p2.session){
|
||||||
p2.send("gameend")
|
p2.send("gameend")
|
||||||
|
}
|
||||||
this.musicFadeOut++
|
this.musicFadeOut++
|
||||||
}else if(this.musicFadeOut === 2 && (ms >= started + 8600 && ms >= musicDuration + 250)){
|
}else if(this.musicFadeOut === 2 && (ms >= started + 8600 && ms >= musicDuration + 250)){
|
||||||
this.controller.displayResults()
|
this.controller.displayResults()
|
||||||
|
@ -4,7 +4,6 @@ class Loader{
|
|||||||
this.loadedAssets = 0
|
this.loadedAssets = 0
|
||||||
this.assetsDiv = document.getElementById("assets")
|
this.assetsDiv = document.getElementById("assets")
|
||||||
this.canvasTest = new CanvasTest()
|
this.canvasTest = new CanvasTest()
|
||||||
p2 = new P2Connection()
|
|
||||||
this.startTime = +new Date
|
this.startTime = +new Date
|
||||||
|
|
||||||
this.ajax("src/views/loader.html").then(this.run.bind(this))
|
this.ajax("src/views/loader.html").then(this.run.bind(this))
|
||||||
@ -97,6 +96,24 @@ class Loader{
|
|||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
if(location.hash.length === 6){
|
||||||
|
this.promises.push(new Promise(resolve => {
|
||||||
|
p2.open()
|
||||||
|
pageEvents.add(p2, "message", response => {
|
||||||
|
if(response.type === "session"){
|
||||||
|
resolve()
|
||||||
|
}else if(response.type === "gameend"){
|
||||||
|
p2.hash("")
|
||||||
|
p2.hashLock = false
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
p2.send("invite", location.hash.slice(1).toLowerCase())
|
||||||
|
}).then(() => {
|
||||||
|
pageEvents.remove(p2, "message")
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
this.promises.forEach(promise => {
|
this.promises.forEach(promise => {
|
||||||
promise.then(this.assetLoaded.bind(this))
|
promise.then(this.assetLoaded.bind(this))
|
||||||
})
|
})
|
||||||
|
@ -92,6 +92,7 @@ class loadSong{
|
|||||||
}
|
}
|
||||||
}else if(event.type === "gamestart"){
|
}else if(event.type === "gamestart"){
|
||||||
this.clean()
|
this.clean()
|
||||||
|
p2.clearMessage("songsel")
|
||||||
loader.changePage("game")
|
loader.changePage("game")
|
||||||
var taikoGame1 = new Controller(this.selectedSong, this.songData, false, 1, this.touchEnabled)
|
var taikoGame1 = new Controller(this.selectedSong, this.songData, false, 1, this.touchEnabled)
|
||||||
var taikoGame2 = new Controller(this.selectedSong2, this.song2Data, true, 2, this.touchEnabled)
|
var taikoGame2 = new Controller(this.selectedSong2, this.song2Data, true, 2, this.touchEnabled)
|
||||||
|
@ -58,7 +58,7 @@ var fullScreenSupported = "requestFullscreen" in root || "webkitRequestFullscree
|
|||||||
|
|
||||||
var pageEvents = new PageEvents()
|
var pageEvents = new PageEvents()
|
||||||
var snd = {}
|
var snd = {}
|
||||||
var p2
|
var p2 = new P2Connection()
|
||||||
var disableBlur = false
|
var disableBlur = false
|
||||||
var cancelTouch = true
|
var cancelTouch = true
|
||||||
var lastHeight
|
var lastHeight
|
||||||
@ -98,6 +98,11 @@ pageEvents.keyAdd(debugObj, "all", "down", event => {
|
|||||||
debugObj.controller.restartSong()
|
debugObj.controller.restartSong()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
if(location.hash.length === 6){
|
||||||
|
p2.hashLock = true
|
||||||
|
}else{
|
||||||
|
p2.hash("")
|
||||||
|
}
|
||||||
|
|
||||||
var loader = new Loader(() => {
|
var loader = new Loader(() => {
|
||||||
new Titlescreen()
|
new Titlescreen()
|
||||||
|
@ -5,6 +5,8 @@ class P2Connection{
|
|||||||
this.otherConnected = false
|
this.otherConnected = false
|
||||||
this.allEvents = new Map()
|
this.allEvents = new Map()
|
||||||
this.addEventListener("message", this.message.bind(this))
|
this.addEventListener("message", this.message.bind(this))
|
||||||
|
this.currentHash = ""
|
||||||
|
pageEvents.add(window, "hashchange", this.onhashchange.bind(this))
|
||||||
}
|
}
|
||||||
addEventListener(type, callback){
|
addEventListener(type, callback){
|
||||||
var addedType = this.allEvents.get(type)
|
var addedType = this.allEvents.get(type)
|
||||||
@ -24,8 +26,8 @@ class P2Connection{
|
|||||||
this.closed = false
|
this.closed = false
|
||||||
var wsProtocol = location.protocol == "https:" ? "wss:" : "ws:"
|
var wsProtocol = location.protocol == "https:" ? "wss:" : "ws:"
|
||||||
this.socket = new WebSocket(wsProtocol + "//" + location.host + "/p2")
|
this.socket = new WebSocket(wsProtocol + "//" + location.host + "/p2")
|
||||||
pageEvents.race(this.socket, "open", "close", listener =>{
|
pageEvents.race(this.socket, "open", "close").then(response => {
|
||||||
if(listener === "open"){
|
if(response.type === "open"){
|
||||||
return this.openEvent()
|
return this.openEvent()
|
||||||
}
|
}
|
||||||
return this.closeEvent()
|
return this.closeEvent()
|
||||||
@ -45,6 +47,11 @@ class P2Connection{
|
|||||||
closeEvent(){
|
closeEvent(){
|
||||||
this.removeEventListener(onmessage)
|
this.removeEventListener(onmessage)
|
||||||
this.otherConnected = false
|
this.otherConnected = false
|
||||||
|
this.session = false
|
||||||
|
if(this.hashLock){
|
||||||
|
this.hash("")
|
||||||
|
this.hashLock = false
|
||||||
|
}
|
||||||
if(!this.closed){
|
if(!this.closed){
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if(this.socket.readyState !== this.socket.OPEN){
|
if(this.socket.readyState !== this.socket.OPEN){
|
||||||
@ -76,17 +83,22 @@ class P2Connection{
|
|||||||
}catch(e){
|
}catch(e){
|
||||||
var response = {}
|
var response = {}
|
||||||
}
|
}
|
||||||
this.lastMessages[response.type] = response.value
|
this.lastMessages[response.type] = response
|
||||||
var addedType = this.allEvents.get("message")
|
var addedType = this.allEvents.get("message")
|
||||||
if(addedType){
|
if(addedType){
|
||||||
addedType.forEach(callback => callback(response))
|
addedType.forEach(callback => callback(response))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getMessage(type, callback){
|
getMessage(type){
|
||||||
if(type in this.lastMessages){
|
if(type in this.lastMessages){
|
||||||
return this.lastMessages[type]
|
return this.lastMessages[type]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
clearMessage(type){
|
||||||
|
if(type in this.lastMessages){
|
||||||
|
this.lastMessages[type] = null
|
||||||
|
}
|
||||||
|
}
|
||||||
message(response){
|
message(response){
|
||||||
switch(response.type){
|
switch(response.type){
|
||||||
case "gamestart":
|
case "gamestart":
|
||||||
@ -98,6 +110,11 @@ class P2Connection{
|
|||||||
break
|
break
|
||||||
case "gameend":
|
case "gameend":
|
||||||
this.otherConnected = false
|
this.otherConnected = false
|
||||||
|
this.session = false
|
||||||
|
if(this.hashLock){
|
||||||
|
this.hash("")
|
||||||
|
this.hashLock = false
|
||||||
|
}
|
||||||
break
|
break
|
||||||
case "gameresults":
|
case "gameresults":
|
||||||
this.results = {}
|
this.results = {}
|
||||||
@ -114,8 +131,24 @@ class P2Connection{
|
|||||||
case "drumroll":
|
case "drumroll":
|
||||||
this.drumrollPace = response.value.pace
|
this.drumrollPace = response.value.pace
|
||||||
break
|
break
|
||||||
|
case "session":
|
||||||
|
this.clearMessage("users")
|
||||||
|
this.otherConnected = true
|
||||||
|
this.session = true
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onhashchange(){
|
||||||
|
if(this.hashLock){
|
||||||
|
this.hash(this.currentHash)
|
||||||
|
}else{
|
||||||
|
location.reload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hash(string){
|
||||||
|
this.currentHash = string
|
||||||
|
history.replaceState("", "", location.pathname + (string ? "#" + string : ""))
|
||||||
|
}
|
||||||
play(circle, mekadon){
|
play(circle, mekadon){
|
||||||
if(this.otherConnected || this.notes.length > 0){
|
if(this.otherConnected || this.notes.length > 0){
|
||||||
var type = circle.getType()
|
var type = circle.getType()
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
class Scoresheet{
|
class Scoresheet{
|
||||||
constructor(controller, results, multiplayer){
|
constructor(controller, results, multiplayer, touchEnabled){
|
||||||
this.controller = controller
|
this.controller = controller
|
||||||
this.results = {}
|
this.results = {}
|
||||||
for(var i in results){
|
for(var i in results){
|
||||||
this.results[i] = results[i].toString()
|
this.results[i] = results[i].toString()
|
||||||
}
|
}
|
||||||
this.multiplayer = multiplayer
|
this.multiplayer = multiplayer
|
||||||
|
this.touchEnabled = touchEnabled
|
||||||
|
|
||||||
this.canvas = document.getElementById("canvas")
|
this.canvas = document.getElementById("canvas")
|
||||||
this.ctx = this.canvas.getContext("2d")
|
this.ctx = this.canvas.getContext("2d")
|
||||||
@ -15,7 +16,8 @@ class Scoresheet{
|
|||||||
screen: "fadeIn",
|
screen: "fadeIn",
|
||||||
screenMS: this.getMS(),
|
screenMS: this.getMS(),
|
||||||
startDelay: 3300,
|
startDelay: 3300,
|
||||||
hasPointer: 0
|
hasPointer: 0,
|
||||||
|
scoreNext: false
|
||||||
}
|
}
|
||||||
this.frame = 1000 / 60
|
this.frame = 1000 / 60
|
||||||
this.numbers = "001122334455667788900112233445".split("")
|
this.numbers = "001122334455667788900112233445".split("")
|
||||||
@ -33,6 +35,17 @@ class Scoresheet{
|
|||||||
|
|
||||||
assets.sounds["results"].play()
|
assets.sounds["results"].play()
|
||||||
assets.sounds["bgm_result"].playLoop(3, false, 0, 0.847, 17.689)
|
assets.sounds["bgm_result"].playLoop(3, false, 0, 0.847, 17.689)
|
||||||
|
|
||||||
|
if(p2.session){
|
||||||
|
if(p2.getMessage("songsel")){
|
||||||
|
this.toSongsel(true)
|
||||||
|
}
|
||||||
|
pageEvents.add(p2, "message", response => {
|
||||||
|
if(response.type === "songsel"){
|
||||||
|
this.toSongsel(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
keyDown(event, code){
|
keyDown(event, code){
|
||||||
if(!code){
|
if(!code){
|
||||||
@ -68,19 +81,30 @@ class Scoresheet{
|
|||||||
this.toNext()
|
this.toNext()
|
||||||
}
|
}
|
||||||
toNext(){
|
toNext(){
|
||||||
var ms = this.getMS()
|
var elapsed = this.getMS() - this.state.screenMS
|
||||||
var elapsed = ms - this.state.screenMS
|
|
||||||
if(this.state.screen === "fadeIn" && elapsed >= this.state.startDelay){
|
if(this.state.screen === "fadeIn" && elapsed >= this.state.startDelay){
|
||||||
this.state.screen = "scoresShown"
|
this.toScoresShown()
|
||||||
this.state.screenMS = ms
|
|
||||||
assets.sounds["note_don"].play()
|
|
||||||
}else if(this.state.screen === "scoresShown" && elapsed >= 1000){
|
}else if(this.state.screen === "scoresShown" && elapsed >= 1000){
|
||||||
|
this.toSongsel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toScoresShown(){
|
||||||
|
if(!p2.session){
|
||||||
|
this.state.screen = "scoresShown"
|
||||||
|
this.state.screenMS = this.getMS()
|
||||||
|
assets.sounds["note_don"].play()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toSongsel(fromP2){
|
||||||
|
if(!p2.session || fromP2){
|
||||||
snd.musicGain.fadeOut(0.5)
|
snd.musicGain.fadeOut(0.5)
|
||||||
this.state.screen = "fadeOut"
|
this.state.screen = "fadeOut"
|
||||||
this.state.screenMS = ms
|
this.state.screenMS = this.getMS()
|
||||||
|
if(!fromP2){
|
||||||
assets.sounds["note_don"].play()
|
assets.sounds["note_don"].play()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
startRedraw(){
|
startRedraw(){
|
||||||
this.redrawing = true
|
this.redrawing = true
|
||||||
@ -232,7 +256,7 @@ class Scoresheet{
|
|||||||
if(elapsed >= 0){
|
if(elapsed >= 0){
|
||||||
if(this.state.hasPointer === 0){
|
if(this.state.hasPointer === 0){
|
||||||
this.state.hasPointer = 1
|
this.state.hasPointer = 1
|
||||||
if(!this.state.pointerLocked){
|
if(!this.state.pointerLocked && !p2.session){
|
||||||
this.canvas.style.cursor = "pointer"
|
this.canvas.style.cursor = "pointer"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -615,6 +639,11 @@ class Scoresheet{
|
|||||||
ctx.restore()
|
ctx.restore()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(p2.session && !this.state.scoreNext && this.state.screen === "scoresShown" && ms - this.state.screenMS >= 10000){
|
||||||
|
this.state.scoreNext = true
|
||||||
|
p2.send("songsel")
|
||||||
|
}
|
||||||
|
|
||||||
if(this.state.screen === "fadeOut"){
|
if(this.state.screen === "fadeOut"){
|
||||||
ctx.save()
|
ctx.save()
|
||||||
if(this.state.hasPointer === 1){
|
if(this.state.hasPointer === 1){
|
||||||
@ -631,7 +660,7 @@ class Scoresheet{
|
|||||||
|
|
||||||
if(elapsed >= 1000){
|
if(elapsed >= 1000){
|
||||||
this.clean()
|
this.clean()
|
||||||
this.controller.songSelection(true, false, this.state.pointerLocked)
|
this.controller.songSelection(true, false, p2.session ? this.touchEnabled : this.state.pointerLocked)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
65
public/src/js/session.js
Normal file
65
public/src/js/session.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
class Session{
|
||||||
|
constructor(touchEnabled){
|
||||||
|
this.touchEnabled = touchEnabled
|
||||||
|
loader.changePage("session")
|
||||||
|
this.endButton = document.getElementById("tutorial-end-button")
|
||||||
|
if(touchEnabled){
|
||||||
|
document.getElementById("tutorial-outer").classList.add("touch-enabled")
|
||||||
|
}
|
||||||
|
this.sessionInvite = document.getElementById("session-invite")
|
||||||
|
|
||||||
|
pageEvents.add(window, ["mousedown", "touchstart"], this.mouseDown.bind(this))
|
||||||
|
pageEvents.keyOnce(this, 27, "down").then(this.onEnd.bind(this))
|
||||||
|
|
||||||
|
this.gamepad = new Gamepad({
|
||||||
|
"confirm": ["start", "b", "ls", "rs"]
|
||||||
|
}, this.onEnd.bind(this))
|
||||||
|
|
||||||
|
p2.hashLock = true
|
||||||
|
pageEvents.add(p2, "message", response => {
|
||||||
|
if(response.type === "invite"){
|
||||||
|
this.sessionInvite.innerText = location.origin + location.pathname + "#" + response.value
|
||||||
|
p2.hash(response.value)
|
||||||
|
}else if(response.type === "songsel"){
|
||||||
|
p2.clearMessage("users")
|
||||||
|
this.onEnd(false, true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
p2.send("invite")
|
||||||
|
}
|
||||||
|
mouseDown(event){
|
||||||
|
if(event.target === this.sessionInvite){
|
||||||
|
this.sessionInvite.focus()
|
||||||
|
}else{
|
||||||
|
getSelection().removeAllRanges()
|
||||||
|
this.sessionInvite.blur()
|
||||||
|
}
|
||||||
|
if(event.target === this.endButton){
|
||||||
|
this.onEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onEnd(event, fromP2){
|
||||||
|
if(!p2.session){
|
||||||
|
p2.send("leave")
|
||||||
|
p2.hash("")
|
||||||
|
p2.hashLock = false
|
||||||
|
}else if(!fromP2){
|
||||||
|
return p2.send("songsel")
|
||||||
|
}
|
||||||
|
if(event && event.type === "keydown"){
|
||||||
|
event.preventDefault()
|
||||||
|
}
|
||||||
|
this.clean()
|
||||||
|
assets.sounds["don"].play()
|
||||||
|
setTimeout(() => {
|
||||||
|
new SongSelect(false, false, this.touchEnabled)
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
clean(){
|
||||||
|
this.gamepad.clean()
|
||||||
|
pageEvents.remove(window, ["mousedown", "touchstart"])
|
||||||
|
pageEvents.keyRemove(this, 27)
|
||||||
|
delete this.endButton
|
||||||
|
delete this.sessionInvite
|
||||||
|
}
|
||||||
|
}
|
@ -176,10 +176,16 @@ class SongSelect{
|
|||||||
this.selectTextCache = new CanvasCache()
|
this.selectTextCache = new CanvasCache()
|
||||||
this.categoryCache = new CanvasCache()
|
this.categoryCache = new CanvasCache()
|
||||||
this.difficultyCache = new CanvasCache()
|
this.difficultyCache = new CanvasCache()
|
||||||
|
this.sessionCache = new CanvasCache()
|
||||||
|
|
||||||
this.difficulty = ["かんたん", "ふつう", "むずかしい", "おに"]
|
this.difficulty = ["かんたん", "ふつう", "むずかしい", "おに"]
|
||||||
this.difficultyId = ["easy", "normal", "hard", "oni", "ura"]
|
this.difficultyId = ["easy", "normal", "hard", "oni", "ura"]
|
||||||
|
|
||||||
|
this.sessionText = {
|
||||||
|
"sessionstart": "オンラインセッションを開始する!",
|
||||||
|
"sessionend": "オンラインセッションを終了する"
|
||||||
|
}
|
||||||
|
|
||||||
this.selectedSong = 0
|
this.selectedSong = 0
|
||||||
this.selectedDiff = 0
|
this.selectedDiff = 0
|
||||||
assets.sounds["bgm_songsel"].playLoop(0.1, false, 0, 1.442, 3.506)
|
assets.sounds["bgm_songsel"].playLoop(0.1, false, 0, 1.442, 3.506)
|
||||||
@ -187,12 +193,15 @@ class SongSelect{
|
|||||||
if(!fromTutorial && !("selectedSong" in localStorage)){
|
if(!fromTutorial && !("selectedSong" in localStorage)){
|
||||||
fromTutorial = touchEnabled ? "about" : "tutorial"
|
fromTutorial = touchEnabled ? "about" : "tutorial"
|
||||||
}
|
}
|
||||||
|
if(p2.session){
|
||||||
|
fromTutorial = false
|
||||||
|
}
|
||||||
|
|
||||||
if(fromTutorial){
|
if(fromTutorial){
|
||||||
this.selectedSong = this.songs.findIndex(song => song.action === fromTutorial)
|
this.selectedSong = this.songs.findIndex(song => song.action === fromTutorial)
|
||||||
this.playBgm(true)
|
this.playBgm(true)
|
||||||
}else{
|
}else{
|
||||||
if("selectedSong" in localStorage){
|
if((!p2.session || fadeIn) && "selectedSong" in localStorage){
|
||||||
this.selectedSong = Math.min(Math.max(0, localStorage["selectedSong"] |0), this.songs.length)
|
this.selectedSong = Math.min(Math.max(0, localStorage["selectedSong"] |0), this.songs.length)
|
||||||
}
|
}
|
||||||
assets.sounds["song-select"].play()
|
assets.sounds["song-select"].play()
|
||||||
@ -209,8 +218,9 @@ class SongSelect{
|
|||||||
this.songSelect.style.backgroundImage = "url('" + assets.image["bg_genre_" + sort].src + "')"
|
this.songSelect.style.backgroundImage = "url('" + assets.image["bg_genre_" + sort].src + "')"
|
||||||
|
|
||||||
this.previewId = 0
|
this.previewId = 0
|
||||||
|
var skipStart = fromTutorial || p2.session
|
||||||
this.state = {
|
this.state = {
|
||||||
screen: fromTutorial ? "song" : (fadeIn ? "titleFadeIn" : "title"),
|
screen: fadeIn ? "titleFadeIn" : (skipStart ? "song" : "title"),
|
||||||
screenMS: this.getMS(),
|
screenMS: this.getMS(),
|
||||||
move: 0,
|
move: 0,
|
||||||
moveMS: 0,
|
moveMS: 0,
|
||||||
@ -218,7 +228,8 @@ class SongSelect{
|
|||||||
moveHover: null,
|
moveHover: null,
|
||||||
locked: true,
|
locked: true,
|
||||||
hasPointer: false,
|
hasPointer: false,
|
||||||
options: 0
|
options: 0,
|
||||||
|
selLock: false
|
||||||
}
|
}
|
||||||
this.songSelecting = {
|
this.songSelecting = {
|
||||||
speed: 800,
|
speed: 800,
|
||||||
@ -231,11 +242,12 @@ class SongSelect{
|
|||||||
this.pressedKeys = {}
|
this.pressedKeys = {}
|
||||||
this.gamepad = new Gamepad({
|
this.gamepad = new Gamepad({
|
||||||
"13": ["b", "start", "ls", "rs"],
|
"13": ["b", "start", "ls", "rs"],
|
||||||
"8": ["a"],
|
"27": ["a"],
|
||||||
"37": ["l", "lb", "lt", "lsl"],
|
"37": ["l", "lb", "lt", "lsl"],
|
||||||
"39": ["r", "rb", "rt", "lsr"],
|
"39": ["r", "rb", "rt", "lsr"],
|
||||||
"38": ["u", "lsu"],
|
"38": ["u", "lsu"],
|
||||||
"40": ["d", "lsd"],
|
"40": ["d", "lsd"],
|
||||||
|
"8": ["back"],
|
||||||
"ctrl": ["y"],
|
"ctrl": ["y"],
|
||||||
"shift": ["x"]
|
"shift": ["x"]
|
||||||
})
|
})
|
||||||
@ -244,6 +256,9 @@ class SongSelect{
|
|||||||
|
|
||||||
pageEvents.keyAdd(this, "all", "down", this.keyDown.bind(this))
|
pageEvents.keyAdd(this, "all", "down", this.keyDown.bind(this))
|
||||||
pageEvents.add(loader.screen, "mousemove", this.mouseMove.bind(this))
|
pageEvents.add(loader.screen, "mousemove", this.mouseMove.bind(this))
|
||||||
|
pageEvents.add(loader.screen, "mouseleave", () => {
|
||||||
|
this.state.moveHover = null
|
||||||
|
})
|
||||||
pageEvents.add(loader.screen, ["mousedown", "touchstart"], this.mouseDown.bind(this))
|
pageEvents.add(loader.screen, ["mousedown", "touchstart"], this.mouseDown.bind(this))
|
||||||
if(touchEnabled && fullScreenSupported){
|
if(touchEnabled && fullScreenSupported){
|
||||||
this.touchFullBtn = document.getElementById("touch-full-btn")
|
this.touchFullBtn = document.getElementById("touch-full-btn")
|
||||||
@ -279,8 +294,10 @@ class SongSelect{
|
|||||||
var key = {
|
var key = {
|
||||||
confirm: code == 13 || code == 32 || code == 70 || code == 74,
|
confirm: code == 13 || code == 32 || code == 70 || code == 74,
|
||||||
// Enter, Space, F, J
|
// Enter, Space, F, J
|
||||||
cancel: code == 27 || code == 8,
|
cancel: code == 27,
|
||||||
// Esc, Backspace
|
// Esc
|
||||||
|
session: code == 8,
|
||||||
|
// Backspace
|
||||||
left: code == 37 || code == 68,
|
left: code == 37 || code == 68,
|
||||||
// Left, D
|
// Left, D
|
||||||
right: code == 39 || code == 75,
|
right: code == 39 || code == 75,
|
||||||
@ -298,6 +315,8 @@ class SongSelect{
|
|||||||
this.toSelectDifficulty()
|
this.toSelectDifficulty()
|
||||||
}else if(key.cancel){
|
}else if(key.cancel){
|
||||||
this.toTitleScreen()
|
this.toTitleScreen()
|
||||||
|
}else if(key.session){
|
||||||
|
this.toSession()
|
||||||
}else if(key.left){
|
}else if(key.left){
|
||||||
this.moveToSong(-1)
|
this.moveToSong(-1)
|
||||||
}else if(key.right){
|
}else if(key.right){
|
||||||
@ -312,7 +331,7 @@ class SongSelect{
|
|||||||
}else{
|
}else{
|
||||||
this.toLoadSong(this.selectedDiff - this.diffOptions.length, modifiers.shift, modifiers.ctrl)
|
this.toLoadSong(this.selectedDiff - this.diffOptions.length, modifiers.shift, modifiers.ctrl)
|
||||||
}
|
}
|
||||||
}else if(key.cancel){
|
}else if(key.cancel || key.session){
|
||||||
this.toSongSelect()
|
this.toSongSelect()
|
||||||
}else if(key.left){
|
}else if(key.left){
|
||||||
this.moveToDiff(-1)
|
this.moveToDiff(-1)
|
||||||
@ -350,12 +369,16 @@ class SongSelect{
|
|||||||
var touch = true
|
var touch = true
|
||||||
}
|
}
|
||||||
if(this.state.screen === "song"){
|
if(this.state.screen === "song"){
|
||||||
|
if(mouse.x > 513 && mouse.y > 603){
|
||||||
|
this.toSession()
|
||||||
|
}else{
|
||||||
var moveBy = this.songSelMouse(mouse.x, mouse.y)
|
var moveBy = this.songSelMouse(mouse.x, mouse.y)
|
||||||
if(moveBy === 0){
|
if(moveBy === 0){
|
||||||
this.toSelectDifficulty()
|
this.toSelectDifficulty()
|
||||||
}else if(moveBy !== null){
|
}else if(moveBy !== null){
|
||||||
this.moveToSong(moveBy)
|
this.moveToSong(moveBy)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}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 < 55 || mouse.x > 967 || mouse.y < 40 || mouse.y > 540){
|
if(mouse.x < 55 || mouse.x > 967 || mouse.y < 40 || mouse.y > 540){
|
||||||
@ -380,10 +403,14 @@ 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 > 513 && mouse.y > 603){
|
||||||
|
moveTo = "session"
|
||||||
|
}else{
|
||||||
var moveTo = this.songSelMouse(mouse.x, mouse.y)
|
var moveTo = this.songSelMouse(mouse.x, mouse.y)
|
||||||
if(moveTo === null && this.state.moveHover === 0 && !this.songs[this.selectedSong].stars){
|
if(moveTo === null && this.state.moveHover === 0 && !this.songs[this.selectedSong].stars){
|
||||||
this.state.moveMS = this.getMS() - this.songSelecting.speed
|
this.state.moveMS = this.getMS() - this.songSelecting.speed
|
||||||
}
|
}
|
||||||
|
}
|
||||||
this.state.moveHover = moveTo
|
this.state.moveHover = moveTo
|
||||||
}else if(this.state.screen === "difficulty"){
|
}else if(this.state.screen === "difficulty"){
|
||||||
var moveTo = this.diffSelMouse(mouse.x, mouse.y)
|
var moveTo = this.diffSelMouse(mouse.x, mouse.y)
|
||||||
@ -446,10 +473,17 @@ class SongSelect{
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
moveToSong(moveBy){
|
moveToSong(moveBy, fromP2){
|
||||||
if(this.state.locked !== 1){
|
|
||||||
var ms = this.getMS()
|
var ms = this.getMS()
|
||||||
if(this.songs[this.selectedSong].stars && this.state.locked === 0){
|
if(p2.session && !fromP2){
|
||||||
|
if(!this.state.selLock && ms > this.state.moveMS + 800){
|
||||||
|
this.state.selLock = true
|
||||||
|
p2.send("songsel", {
|
||||||
|
song: this.mod(this.songs.length, this.selectedSong + moveBy)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}else if(this.state.locked !== 1 || fromP2){
|
||||||
|
if(this.songs[this.selectedSong].stars && (this.state.locked === 0 || fromP2)){
|
||||||
this.state.moveMS = ms
|
this.state.moveMS = ms
|
||||||
}else{
|
}else{
|
||||||
this.state.moveMS = ms - this.songSelecting.speed * this.songSelecting.resize
|
this.state.moveMS = ms - this.songSelecting.speed * this.songSelecting.resize
|
||||||
@ -482,9 +516,19 @@ class SongSelect{
|
|||||||
assets.sounds["ka"].play()
|
assets.sounds["ka"].play()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
toSelectDifficulty(){
|
toSelectDifficulty(fromP2){
|
||||||
if(this.state.locked === 0){
|
|
||||||
var currentSong = this.songs[this.selectedSong]
|
var currentSong = this.songs[this.selectedSong]
|
||||||
|
if(p2.session && !fromP2 && currentSong.action !== "random"){
|
||||||
|
if(this.songs[this.selectedSong].stars){
|
||||||
|
if(!this.state.selLock){
|
||||||
|
this.state.selLock = true
|
||||||
|
p2.send("songsel", {
|
||||||
|
song: this.selectedSong,
|
||||||
|
selected: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else if(this.state.locked === 0 || fromP2){
|
||||||
if(currentSong.stars){
|
if(currentSong.stars){
|
||||||
this.state.screen = "difficulty"
|
this.state.screen = "difficulty"
|
||||||
this.state.screenMS = this.getMS()
|
this.state.screenMS = this.getMS()
|
||||||
@ -516,11 +560,18 @@ class SongSelect{
|
|||||||
}else if(currentSong.action === "about"){
|
}else if(currentSong.action === "about"){
|
||||||
this.toAbout()
|
this.toAbout()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
this.pointer(false)
|
this.pointer(false)
|
||||||
}
|
}
|
||||||
|
toSongSelect(fromP2){
|
||||||
|
if(p2.session && !fromP2){
|
||||||
|
if(!this.state.selLock){
|
||||||
|
this.state.selLock = true
|
||||||
|
p2.send("songsel", {
|
||||||
|
song: this.selectedSong
|
||||||
|
})
|
||||||
}
|
}
|
||||||
toSongSelect(){
|
}else if(fromP2 || this.state.locked !== 1){
|
||||||
if(this.state.locked !== 1){
|
|
||||||
this.state.screen = "song"
|
this.state.screen = "song"
|
||||||
this.state.screenMS = this.getMS()
|
this.state.screenMS = this.getMS()
|
||||||
this.state.locked = true
|
this.state.locked = true
|
||||||
@ -544,10 +595,10 @@ class SongSelect{
|
|||||||
}
|
}
|
||||||
var autoplay = false
|
var autoplay = false
|
||||||
var multiplayer = false
|
var multiplayer = false
|
||||||
if(this.state.options === 1){
|
if(p2.session || this.state.options === 2){
|
||||||
autoplay = true
|
|
||||||
}else if(this.state.options === 2){
|
|
||||||
multiplayer = true
|
multiplayer = true
|
||||||
|
}else if(this.state.options === 1){
|
||||||
|
autoplay = true
|
||||||
}else if(shift){
|
}else if(shift){
|
||||||
autoplay = shift
|
autoplay = shift
|
||||||
}else{
|
}else{
|
||||||
@ -564,17 +615,21 @@ class SongSelect{
|
|||||||
}, autoplay, multiplayer, touch)
|
}, autoplay, multiplayer, touch)
|
||||||
}
|
}
|
||||||
toOptions(moveBy){
|
toOptions(moveBy){
|
||||||
|
if(!p2.session){
|
||||||
assets.sounds["ka"].play()
|
assets.sounds["ka"].play()
|
||||||
this.selectedDiff = 1
|
this.selectedDiff = 1
|
||||||
this.state.options = this.mod(this.optionsList.length, this.state.options + moveBy)
|
this.state.options = this.mod(this.optionsList.length, this.state.options + moveBy)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
toTitleScreen(){
|
toTitleScreen(){
|
||||||
|
if(!p2.session){
|
||||||
assets.sounds["cancel"].play()
|
assets.sounds["cancel"].play()
|
||||||
this.clean()
|
this.clean()
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
new Titlescreen()
|
new Titlescreen()
|
||||||
}, 500)
|
}, 500)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
toTutorial(){
|
toTutorial(){
|
||||||
assets.sounds["don"].play()
|
assets.sounds["don"].play()
|
||||||
this.clean()
|
this.clean()
|
||||||
@ -589,6 +644,17 @@ class SongSelect{
|
|||||||
new About(this.touchEnabled)
|
new About(this.touchEnabled)
|
||||||
}, 500)
|
}, 500)
|
||||||
}
|
}
|
||||||
|
toSession(){
|
||||||
|
if(p2.session){
|
||||||
|
p2.send("gameend")
|
||||||
|
}else{
|
||||||
|
assets.sounds["don"].play()
|
||||||
|
this.clean()
|
||||||
|
setTimeout(() => {
|
||||||
|
new Session(this.touchEnabled)
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
redraw(){
|
redraw(){
|
||||||
if(!this.redrawRunning){
|
if(!this.redrawRunning){
|
||||||
@ -657,6 +723,28 @@ class SongSelect{
|
|||||||
|
|
||||||
this.difficultyCache.resize((44 + 56 + 2) * 5, 135 + 10, ratio + 0.5)
|
this.difficultyCache.resize((44 + 56 + 2) * 5, 135 + 10, ratio + 0.5)
|
||||||
|
|
||||||
|
var w = winW / ratio / 2
|
||||||
|
this.sessionCache.resize(w, 39 * 2, ratio + 0.5)
|
||||||
|
for(var id in this.sessionText){
|
||||||
|
this.sessionCache.set({
|
||||||
|
w: w,
|
||||||
|
h: 38,
|
||||||
|
id: id
|
||||||
|
}, ctx => {
|
||||||
|
var text = this.sessionText[id]
|
||||||
|
ctx.font = "28px " + this.font
|
||||||
|
ctx.textAlign = "center"
|
||||||
|
ctx.textBaseline = "middle"
|
||||||
|
ctx.strokeStyle = "#000"
|
||||||
|
ctx.lineWidth = 8
|
||||||
|
ctx.lineJoin = "round"
|
||||||
|
ctx.miterLimit = 1
|
||||||
|
ctx.strokeText(text, w / 2, 38 / 2)
|
||||||
|
ctx.fillStyle = "#fff"
|
||||||
|
ctx.fillText(text, w / 2, 38 / 2)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
this.selectableText = ""
|
this.selectableText = ""
|
||||||
}else if(!document.hasFocus()){
|
}else if(!document.hasFocus()){
|
||||||
this.pointer(false)
|
this.pointer(false)
|
||||||
@ -858,7 +946,8 @@ class SongSelect{
|
|||||||
x: _x,
|
x: _x,
|
||||||
y: songTop,
|
y: songTop,
|
||||||
song: this.songs[index],
|
song: this.songs[index],
|
||||||
highlight: highlight
|
highlight: highlight,
|
||||||
|
disabled: p2.session && this.songs[index].action && this.songs[index].action !== "random"
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for(var i = this.selectedSong + 1; ; i++){
|
for(var i = this.selectedSong + 1; ; i++){
|
||||||
@ -877,7 +966,8 @@ class SongSelect{
|
|||||||
x: _x,
|
x: _x,
|
||||||
y: songTop,
|
y: songTop,
|
||||||
song: this.songs[index],
|
song: this.songs[index],
|
||||||
highlight: highlight
|
highlight: highlight,
|
||||||
|
disabled: p2.session && this.songs[index].action && this.songs[index].action !== "random"
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -920,6 +1010,7 @@ class SongSelect{
|
|||||||
animateMS: this.state.moveMS,
|
animateMS: this.state.moveMS,
|
||||||
cached: selectedWidth === this.songAsset.fullWidth ? 3 : (selectedWidth === this.songAsset.selectedWidth ? 2 : (selectedWidth === this.songAsset.width ? 1 : 0)),
|
cached: selectedWidth === this.songAsset.fullWidth ? 3 : (selectedWidth === this.songAsset.selectedWidth ? 2 : (selectedWidth === this.songAsset.width ? 1 : 0)),
|
||||||
frameCache: this.songFrameCache,
|
frameCache: this.songFrameCache,
|
||||||
|
disabled: p2.session && currentSong.action && currentSong.action !== "random",
|
||||||
innerContent: (x, y, w, h) => {
|
innerContent: (x, y, w, h) => {
|
||||||
ctx.strokeStyle = "#000"
|
ctx.strokeStyle = "#000"
|
||||||
if(screen === "title" || screen === "titleFadeIn" || screen === "song"){
|
if(screen === "title" || screen === "titleFadeIn" || screen === "song"){
|
||||||
@ -1273,6 +1364,113 @@ class SongSelect{
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx.fillStyle = "#000"
|
||||||
|
ctx.fillRect(0, frameTop + 595, 1280 + frameLeft * 2, 125 + frameTop)
|
||||||
|
var x = 0
|
||||||
|
var y = frameTop + 603
|
||||||
|
var w = frameLeft + 638
|
||||||
|
var h = 117 + frameTop
|
||||||
|
this.draw.pattern({
|
||||||
|
ctx: ctx,
|
||||||
|
img: assets.image["bg_score_p1"],
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
w: w,
|
||||||
|
h: h,
|
||||||
|
dx: frameLeft + 10,
|
||||||
|
dy: frameTop + 15,
|
||||||
|
scale: 1.55
|
||||||
|
})
|
||||||
|
ctx.fillStyle = "rgba(249, 163, 149, 0.5)"
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.moveTo(x, y)
|
||||||
|
ctx.lineTo(x + w, y)
|
||||||
|
ctx.lineTo(x + w - 4, y + 4)
|
||||||
|
ctx.lineTo(x, y + 4)
|
||||||
|
ctx.fill()
|
||||||
|
ctx.fillStyle = "rgba(0, 0, 0, 0.25)"
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.moveTo(x + w, y)
|
||||||
|
ctx.lineTo(x + w, y + h)
|
||||||
|
ctx.lineTo(x + w - 4, y + h)
|
||||||
|
ctx.lineTo(x + w - 4, y + 4)
|
||||||
|
ctx.fill()
|
||||||
|
x = frameLeft + 642
|
||||||
|
if(p2.session){
|
||||||
|
this.draw.pattern({
|
||||||
|
ctx: ctx,
|
||||||
|
img: assets.image["bg_score_p2"],
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
w: w,
|
||||||
|
h: h,
|
||||||
|
dx: frameLeft + 15,
|
||||||
|
dy: frameTop - 20,
|
||||||
|
scale: 1.55
|
||||||
|
})
|
||||||
|
ctx.fillStyle = "rgba(138, 245, 247, 0.5)"
|
||||||
|
}else{
|
||||||
|
this.draw.pattern({
|
||||||
|
ctx: ctx,
|
||||||
|
img: assets.image["bg_settings"],
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
w: w,
|
||||||
|
h: h,
|
||||||
|
dx: frameLeft + 11,
|
||||||
|
dy: frameTop + 45,
|
||||||
|
scale: 3.1
|
||||||
|
})
|
||||||
|
ctx.fillStyle = "rgba(255, 255, 255, 0.5)"
|
||||||
|
}
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.moveTo(x, y + h)
|
||||||
|
ctx.lineTo(x, y)
|
||||||
|
ctx.lineTo(x + w, y)
|
||||||
|
ctx.lineTo(x + w, y + 4)
|
||||||
|
ctx.lineTo(x + 4, y + 4)
|
||||||
|
ctx.lineTo(x + 4, y + h)
|
||||||
|
ctx.fill()
|
||||||
|
if(screen !== "difficulty"){
|
||||||
|
var elapsed = (ms - this.state.screenMS) % 3100
|
||||||
|
var fade = 1
|
||||||
|
if(!p2.session && screen === "song"){
|
||||||
|
if(elapsed > 2800){
|
||||||
|
fade = (elapsed - 2800) / 300
|
||||||
|
}else if(2000 < elapsed){
|
||||||
|
if(elapsed < 2300){
|
||||||
|
fade = 1 - (elapsed - 2000) / 300
|
||||||
|
}else{
|
||||||
|
fade = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(fade > 0){
|
||||||
|
if(fade < 1){
|
||||||
|
ctx.globalAlpha = this.draw.easeIn(fade)
|
||||||
|
}
|
||||||
|
this.sessionCache.get({
|
||||||
|
ctx: ctx,
|
||||||
|
x: winW / 2,
|
||||||
|
y: y + (h - 32) / 2,
|
||||||
|
w: winW / 2,
|
||||||
|
h: 38,
|
||||||
|
id: p2.session ? "sessionend" : "sessionstart"
|
||||||
|
})
|
||||||
|
ctx.globalAlpha = 1
|
||||||
|
}
|
||||||
|
if(this.state.moveHover === "session"){
|
||||||
|
this.draw.highlight({
|
||||||
|
ctx: ctx,
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
w: w,
|
||||||
|
h: h,
|
||||||
|
opacity: 0.8
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(screen === "titleFadeIn"){
|
if(screen === "titleFadeIn"){
|
||||||
ctx.save()
|
ctx.save()
|
||||||
|
|
||||||
@ -1408,8 +1606,8 @@ class SongSelect{
|
|||||||
this.songs.forEach(song => {
|
this.songs.forEach(song => {
|
||||||
song.p2Cursor = null
|
song.p2Cursor = null
|
||||||
})
|
})
|
||||||
if(response){
|
if(response && response.value){
|
||||||
response.forEach(idDiff => {
|
response.value.forEach(idDiff => {
|
||||||
var id = idDiff.id |0
|
var id = idDiff.id |0
|
||||||
var diff = idDiff.diff
|
var diff = idDiff.diff
|
||||||
var diffId = this.difficultyId.indexOf(diff)
|
var diffId = this.difficultyId.indexOf(diff)
|
||||||
@ -1417,17 +1615,72 @@ class SongSelect{
|
|||||||
diffId = 3
|
diffId = 3
|
||||||
}
|
}
|
||||||
if(diffId >= 0){
|
if(diffId >= 0){
|
||||||
var currentSong = this.songs.find(song => song.id === id)
|
var index = 0
|
||||||
|
var currentSong = this.songs.find((song, i) => {
|
||||||
|
index = i
|
||||||
|
return song.id === id
|
||||||
|
})
|
||||||
currentSong.p2Cursor = diffId
|
currentSong.p2Cursor = diffId
|
||||||
|
if(currentSong.stars){
|
||||||
|
this.selectedSong = index
|
||||||
|
this.state.move = 0
|
||||||
|
if(this.state.screen !== "difficulty"){
|
||||||
|
this.toSelectDifficulty(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onsongsel(response){
|
||||||
|
if(response && response.value){
|
||||||
|
var selected = false
|
||||||
|
if("selected" in response.value){
|
||||||
|
selected = response.value.selected
|
||||||
|
}
|
||||||
|
if("song" in response.value){
|
||||||
|
var song = +response.value.song
|
||||||
|
if(song >= 0 && song < this.songs.length){
|
||||||
|
if(!selected){
|
||||||
|
this.state.locked = true
|
||||||
|
if(this.state.screen === "difficulty"){
|
||||||
|
this.toSongSelect(true)
|
||||||
|
}
|
||||||
|
var moveBy = song - this.selectedSong
|
||||||
|
if(moveBy){
|
||||||
|
if(this.selectedSong < song){
|
||||||
|
var altMoveBy = -this.mod(this.songs.length, this.selectedSong - song)
|
||||||
|
}else{
|
||||||
|
var altMoveBy = this.mod(this.songs.length, moveBy)
|
||||||
|
}
|
||||||
|
if(Math.abs(altMoveBy) < Math.abs(moveBy)){
|
||||||
|
moveBy = altMoveBy
|
||||||
|
}
|
||||||
|
this.moveToSong(moveBy, true)
|
||||||
|
}
|
||||||
|
}else if(this.songs[song].stars){
|
||||||
|
this.selectedSong = song
|
||||||
|
this.state.move = 0
|
||||||
|
if(this.state.screen !== "difficulty"){
|
||||||
|
this.toSelectDifficulty(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
startP2(){
|
startP2(){
|
||||||
this.onusers(p2.getMessage("users"))
|
this.onusers(p2.getMessage("users"))
|
||||||
|
if(p2.session){
|
||||||
|
this.onsongsel(p2.getMessage("songsel"))
|
||||||
|
}
|
||||||
pageEvents.add(p2, "message", response => {
|
pageEvents.add(p2, "message", response => {
|
||||||
if(response.type == "users"){
|
if(response.type == "users"){
|
||||||
this.onusers(response.value)
|
this.onusers(response)
|
||||||
|
}
|
||||||
|
if(p2.session && response.type == "songsel"){
|
||||||
|
this.onsongsel(response)
|
||||||
|
this.state.selLock = false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if(p2.closed){
|
if(p2.closed){
|
||||||
@ -1449,6 +1702,7 @@ class SongSelect{
|
|||||||
this.selectTextCache.clean()
|
this.selectTextCache.clean()
|
||||||
this.categoryCache.clean()
|
this.categoryCache.clean()
|
||||||
this.difficultyCache.clean()
|
this.difficultyCache.clean()
|
||||||
|
this.sessionCache.clean()
|
||||||
assets.sounds["bgm_songsel"].stop()
|
assets.sounds["bgm_songsel"].stop()
|
||||||
if(!this.bgmEnabled){
|
if(!this.bgmEnabled){
|
||||||
snd.musicGain.fadeIn()
|
snd.musicGain.fadeIn()
|
||||||
@ -1459,7 +1713,8 @@ class SongSelect{
|
|||||||
this.redrawRunning = false
|
this.redrawRunning = false
|
||||||
this.endPreview()
|
this.endPreview()
|
||||||
pageEvents.keyRemove(this, "all")
|
pageEvents.keyRemove(this, "all")
|
||||||
pageEvents.remove(loader.screen, ["mousemove", "mousedown", "touchstart"])
|
pageEvents.remove(loader.screen, ["mousemove", "mouseleave", "mousedown", "touchstart"])
|
||||||
|
pageEvents.remove(p2, "message")
|
||||||
if(this.touchEnabled && fullScreenSupported){
|
if(this.touchEnabled && fullScreenSupported){
|
||||||
pageEvents.remove(this.touchFullBtn, "click")
|
pageEvents.remove(this.touchFullBtn, "click")
|
||||||
delete this.touchFullBtn
|
delete this.touchFullBtn
|
||||||
|
@ -12,6 +12,13 @@ class Titlescreen{
|
|||||||
this.onPressed()
|
this.onPressed()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
if(p2.session){
|
||||||
|
pageEvents.add(p2, "message", response => {
|
||||||
|
if(response.type === "songsel"){
|
||||||
|
this.goNext(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
keyDown(event, code){
|
keyDown(event, code){
|
||||||
if(!code){
|
if(!code){
|
||||||
@ -34,13 +41,20 @@ class Titlescreen{
|
|||||||
this.titleScreen.style.cursor = "auto"
|
this.titleScreen.style.cursor = "auto"
|
||||||
this.clean()
|
this.clean()
|
||||||
assets.sounds["don"].play()
|
assets.sounds["don"].play()
|
||||||
setTimeout(this.goNext.bind(this), 500)
|
this.goNext()
|
||||||
}
|
}
|
||||||
goNext(){
|
goNext(fromP2){
|
||||||
if(this.touched || localStorage.getItem("tutorial") === "true"){
|
if(p2.session && !fromP2){
|
||||||
|
p2.send("songsel")
|
||||||
|
}else if(fromP2 || this.touched || localStorage.getItem("tutorial") === "true"){
|
||||||
|
pageEvents.remove(p2, "message")
|
||||||
|
setTimeout(() => {
|
||||||
new SongSelect(false, false, this.touched)
|
new SongSelect(false, false, this.touched)
|
||||||
|
}, 500)
|
||||||
}else{
|
}else{
|
||||||
|
setTimeout(() => {
|
||||||
new Tutorial()
|
new Tutorial()
|
||||||
|
}, 500)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
clean(){
|
clean(){
|
||||||
|
11
public/src/views/session.html
Normal file
11
public/src/views/session.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<div id="tutorial-outer">
|
||||||
|
<div id="tutorial">
|
||||||
|
<div id="tutorial-title" class="stroke-sub" alt="Multiplayer Session">Multiplayer Session</div>
|
||||||
|
<div id="tutorial-content">
|
||||||
|
Share this link with your friend to start playing together! Do not leave this screen while they join.
|
||||||
|
<div id="session-invite"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="tutorial-end-button" class="taibtn stroke-sub" alt="Cancel">Cancel</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
159
server.py
159
server.py
@ -3,11 +3,14 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import websockets
|
import websockets
|
||||||
import json
|
import json
|
||||||
|
import random
|
||||||
|
|
||||||
server_status = {
|
server_status = {
|
||||||
"waiting": {},
|
"waiting": {},
|
||||||
"users": []
|
"users": [],
|
||||||
|
"invites": {}
|
||||||
}
|
}
|
||||||
|
consonants = "bcdfghjklmnpqrstvwxyz"
|
||||||
|
|
||||||
def msgobj(type, value=None):
|
def msgobj(type, value=None):
|
||||||
if value == None:
|
if value == None:
|
||||||
@ -24,6 +27,9 @@ def status_event():
|
|||||||
})
|
})
|
||||||
return msgobj("users", value)
|
return msgobj("users", value)
|
||||||
|
|
||||||
|
def get_invite():
|
||||||
|
return "".join([random.choice(consonants) for x in range(5)])
|
||||||
|
|
||||||
async def notify_status():
|
async def notify_status():
|
||||||
ready_users = [user for user in server_status["users"] if "ws" in user and user["action"] == "ready"]
|
ready_users = [user for user in server_status["users"] if "ws" in user and user["action"] == "ready"]
|
||||||
if ready_users:
|
if ready_users:
|
||||||
@ -34,7 +40,8 @@ async def connection(ws, path):
|
|||||||
# User connected
|
# User connected
|
||||||
user = {
|
user = {
|
||||||
"ws": ws,
|
"ws": ws,
|
||||||
"action": "ready"
|
"action": "ready",
|
||||||
|
"session": False
|
||||||
}
|
}
|
||||||
server_status["users"].append(user)
|
server_status["users"].append(user)
|
||||||
try:
|
try:
|
||||||
@ -66,6 +73,8 @@ async def connection(ws, path):
|
|||||||
if action == "ready":
|
if action == "ready":
|
||||||
# Not playing or waiting
|
# Not playing or waiting
|
||||||
if type == "join":
|
if type == "join":
|
||||||
|
if value == None:
|
||||||
|
continue
|
||||||
waiting = server_status["waiting"]
|
waiting = server_status["waiting"]
|
||||||
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
|
||||||
@ -95,6 +104,7 @@ async def connection(ws, path):
|
|||||||
])
|
])
|
||||||
else:
|
else:
|
||||||
# Wait for another user
|
# Wait for another user
|
||||||
|
del user["other_user"]
|
||||||
user["action"] = "waiting"
|
user["action"] = "waiting"
|
||||||
user["gameid"] = id
|
user["gameid"] = id
|
||||||
waiting[id] = {
|
waiting[id] = {
|
||||||
@ -104,9 +114,37 @@ async def connection(ws, path):
|
|||||||
await ws.send(msgobj("waiting"))
|
await ws.send(msgobj("waiting"))
|
||||||
# Update others on waiting players
|
# Update others on waiting players
|
||||||
await notify_status()
|
await notify_status()
|
||||||
|
elif type == "invite":
|
||||||
|
if value == None:
|
||||||
|
# Session invite link requested
|
||||||
|
invite = get_invite()
|
||||||
|
server_status["invites"][invite] = user
|
||||||
|
user["action"] = "invite"
|
||||||
|
user["session"] = invite
|
||||||
|
await ws.send(msgobj("invite", invite))
|
||||||
|
elif value in server_status["invites"]:
|
||||||
|
# Join a session with the other user
|
||||||
|
user["other_user"] = server_status["invites"][value]
|
||||||
|
del server_status["invites"][value]
|
||||||
|
if "ws" in user["other_user"]:
|
||||||
|
user["other_user"]["other_user"] = user
|
||||||
|
user["action"] = "invite"
|
||||||
|
user["session"] = value
|
||||||
|
sent_msg = msgobj("session")
|
||||||
|
await asyncio.wait([
|
||||||
|
ws.send(sent_msg),
|
||||||
|
user["other_user"]["ws"].send(sent_msg)
|
||||||
|
])
|
||||||
|
await ws.send(msgobj("invite"))
|
||||||
|
else:
|
||||||
|
del user["other_user"]
|
||||||
|
await ws.send(msgobj("gameend"))
|
||||||
|
else:
|
||||||
|
# Session code is invalid
|
||||||
|
await ws.send(msgobj("gameend"))
|
||||||
elif action == "waiting" or action == "loading" or action == "loaded":
|
elif action == "waiting" or action == "loading" or action == "loaded":
|
||||||
# Waiting for another user
|
# Waiting for another user
|
||||||
if type == "leave":
|
if type == "leave" and not user["session"]:
|
||||||
# Stop waiting
|
# Stop waiting
|
||||||
del server_status["waiting"][user["gameid"]]
|
del server_status["waiting"][user["gameid"]]
|
||||||
del user["gameid"]
|
del user["gameid"]
|
||||||
@ -129,9 +167,22 @@ async def connection(ws, path):
|
|||||||
elif action == "playing":
|
elif action == "playing":
|
||||||
# Playing with another user
|
# Playing with another user
|
||||||
if "other_user" in user and "ws" in user["other_user"]:
|
if "other_user" in user and "ws" in user["other_user"]:
|
||||||
if type == "note" or type == "drumroll" or type == "gameresults":
|
if type == "note"\
|
||||||
|
or type == "drumroll"\
|
||||||
|
or type == "gameresults":
|
||||||
await user["other_user"]["ws"].send(msgobj(type, value))
|
await user["other_user"]["ws"].send(msgobj(type, value))
|
||||||
if type == "gameend":
|
elif type == "songsel" and user["session"]:
|
||||||
|
user["action"] = "songsel"
|
||||||
|
user["other_user"]["action"] = "songsel"
|
||||||
|
sent_msg1 = msgobj("songsel")
|
||||||
|
sent_msg2 = msgobj("users", [])
|
||||||
|
await asyncio.wait([
|
||||||
|
ws.send(sent_msg1),
|
||||||
|
ws.send(sent_msg2),
|
||||||
|
user["other_user"]["ws"].send(sent_msg1),
|
||||||
|
user["other_user"]["ws"].send(sent_msg2)
|
||||||
|
])
|
||||||
|
elif type == "gameend":
|
||||||
# User wants to disconnect
|
# User wants to disconnect
|
||||||
user["action"] = "ready"
|
user["action"] = "ready"
|
||||||
user["other_user"]["action"] = "ready"
|
user["other_user"]["action"] = "ready"
|
||||||
@ -147,6 +198,101 @@ async def connection(ws, path):
|
|||||||
else:
|
else:
|
||||||
# Other user disconnected
|
# Other user disconnected
|
||||||
user["action"] = "ready"
|
user["action"] = "ready"
|
||||||
|
user["session"] = False
|
||||||
|
await asyncio.wait([
|
||||||
|
ws.send(msgobj("gameend")),
|
||||||
|
ws.send(status_event())
|
||||||
|
])
|
||||||
|
elif action == "invite":
|
||||||
|
if type == "leave":
|
||||||
|
# Cancel session invite
|
||||||
|
if user["session"] in server_status["invites"]:
|
||||||
|
del server_status["invites"][user["session"]]
|
||||||
|
user["action"] = "ready"
|
||||||
|
user["session"] = False
|
||||||
|
if "other_user" in user and "ws" in user["other_user"]:
|
||||||
|
user["other_user"]["action"] = "ready"
|
||||||
|
user["other_user"]["session"] = False
|
||||||
|
sent_msg = status_event()
|
||||||
|
await asyncio.wait([
|
||||||
|
ws.send(msgobj("left")),
|
||||||
|
ws.send(sent_msg),
|
||||||
|
user["other_user"]["ws"].send(msgobj("gameend")),
|
||||||
|
user["other_user"]["ws"].send(sent_msg)
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
await asyncio.wait([
|
||||||
|
ws.send(msgobj("left")),
|
||||||
|
ws.send(status_event())
|
||||||
|
])
|
||||||
|
elif type == "songsel" and "other_user" in user:
|
||||||
|
if "ws" in user["other_user"]:
|
||||||
|
user["action"] = "songsel"
|
||||||
|
user["other_user"]["action"] = "songsel"
|
||||||
|
sent_msg = msgobj(type)
|
||||||
|
await asyncio.wait([
|
||||||
|
ws.send(sent_msg),
|
||||||
|
user["other_user"]["ws"].send(sent_msg)
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
user["action"] = "ready"
|
||||||
|
user["session"] = False
|
||||||
|
await asyncio.wait([
|
||||||
|
ws.send(msgobj("gameend")),
|
||||||
|
ws.send(status_event())
|
||||||
|
])
|
||||||
|
elif action == "songsel":
|
||||||
|
# Session song selection
|
||||||
|
if "other_user" in user and "ws" in user["other_user"]:
|
||||||
|
if type == "songsel":
|
||||||
|
# Change song select position
|
||||||
|
if user["other_user"]["action"] == "songsel":
|
||||||
|
sent_msg = msgobj(type, value)
|
||||||
|
await asyncio.wait([
|
||||||
|
ws.send(sent_msg),
|
||||||
|
user["other_user"]["ws"].send(sent_msg)
|
||||||
|
])
|
||||||
|
elif type == "join":
|
||||||
|
# Start game
|
||||||
|
if value == None:
|
||||||
|
continue
|
||||||
|
id = value["id"] if "id" in value else None
|
||||||
|
diff = value["diff"] if "diff" in value else None
|
||||||
|
if not id or not diff:
|
||||||
|
continue
|
||||||
|
if user["other_user"]["action"] == "waiting":
|
||||||
|
user["action"] = "loading"
|
||||||
|
user["other_user"]["action"] = "loading"
|
||||||
|
await asyncio.wait([
|
||||||
|
ws.send(msgobj("gameload", user["other_user"]["gamediff"])),
|
||||||
|
user["other_user"]["ws"].send(msgobj("gameload", diff))
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
user["action"] = "waiting"
|
||||||
|
user["gamediff"] = diff
|
||||||
|
await user["other_user"]["ws"].send(msgobj("users", [{
|
||||||
|
"id": id,
|
||||||
|
"diff": diff
|
||||||
|
}]))
|
||||||
|
elif type == "gameend":
|
||||||
|
# User wants to disconnect
|
||||||
|
user["action"] = "ready"
|
||||||
|
user["session"] = False
|
||||||
|
user["other_user"]["action"] = "ready"
|
||||||
|
user["other_user"]["session"] = False
|
||||||
|
sent_msg1 = msgobj("gameend")
|
||||||
|
sent_msg2 = status_event()
|
||||||
|
await asyncio.wait([
|
||||||
|
ws.send(sent_msg1),
|
||||||
|
ws.send(sent_msg2),
|
||||||
|
user["other_user"]["ws"].send(sent_msg1),
|
||||||
|
user["other_user"]["ws"].send(sent_msg2)
|
||||||
|
])
|
||||||
|
del user["other_user"]
|
||||||
|
else:
|
||||||
|
# Other user disconnected
|
||||||
|
user["action"] = "ready"
|
||||||
|
user["session"] = False
|
||||||
await asyncio.wait([
|
await asyncio.wait([
|
||||||
ws.send(msgobj("gameend")),
|
ws.send(msgobj("gameend")),
|
||||||
ws.send(status_event())
|
ws.send(status_event())
|
||||||
@ -157,6 +303,7 @@ async def connection(ws, path):
|
|||||||
del server_status["users"][server_status["users"].index(user)]
|
del server_status["users"][server_status["users"].index(user)]
|
||||||
if "other_user" in user and "ws" in user["other_user"]:
|
if "other_user" in user and "ws" in user["other_user"]:
|
||||||
user["other_user"]["action"] = "ready"
|
user["other_user"]["action"] = "ready"
|
||||||
|
user["other_user"]["session"] = False
|
||||||
await asyncio.wait([
|
await asyncio.wait([
|
||||||
user["other_user"]["ws"].send(msgobj("gameend")),
|
user["other_user"]["ws"].send(msgobj("gameend")),
|
||||||
user["other_user"]["ws"].send(status_event())
|
user["other_user"]["ws"].send(status_event())
|
||||||
@ -164,6 +311,8 @@ async def connection(ws, path):
|
|||||||
if user["action"] == "waiting":
|
if user["action"] == "waiting":
|
||||||
del server_status["waiting"][user["gameid"]]
|
del server_status["waiting"][user["gameid"]]
|
||||||
await notify_status()
|
await notify_status()
|
||||||
|
elif user["action"] == "invite" and user["session"] in server_status["invites"]:
|
||||||
|
del server_status["invites"][user["session"]]
|
||||||
|
|
||||||
asyncio.get_event_loop().run_until_complete(
|
asyncio.get_event_loop().run_until_complete(
|
||||||
websockets.serve(connection, "localhost", 34802)
|
websockets.serve(connection, "localhost", 34802)
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
<script src="/src/js/parsetja.js?{{version.commit_short}}"></script>
|
<script src="/src/js/parsetja.js?{{version.commit_short}}"></script>
|
||||||
<script src="/src/js/about.js?{{version.commit_short}}"></script>
|
<script src="/src/js/about.js?{{version.commit_short}}"></script>
|
||||||
<script src="/src/js/debug.js?{{version.commit_short}}"></script>
|
<script src="/src/js/debug.js?{{version.commit_short}}"></script>
|
||||||
|
<script src="/src/js/session.js?{{version.commit_short}}"></script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
Loading…
Reference in New Issue
Block a user