Merge pull request #72 from LoveEevee/p2-multiplayer-improvements

P2: Multiplayer improvements
This commit is contained in:
Bui 2018-11-13 10:05:47 +00:00 committed by GitHub
commit 2e00290af4
11 changed files with 139 additions and 37 deletions

View File

@ -32,3 +32,8 @@
text-align: center; text-align: center;
z-index: 1; z-index: 1;
} }
#p2-cancel-button{
display: none;
position: absolute;
bottom: -55px;
}

View File

@ -170,6 +170,7 @@ kbd{
} }
.taibtn{ .taibtn{
display: inline-block; display: inline-block;
background: #f6ead4;
padding: 0.4em 0.4em; padding: 0.4em 0.4em;
border-radius: 0.5em; border-radius: 0.5em;
border: 0.1em rgba(218, 205, 178, 1) solid; border: 0.1em rgba(218, 205, 178, 1) solid;

View File

@ -65,7 +65,8 @@ var assets = {
"title.ogg", "title.ogg",
"pause.wav", "pause.wav",
"cancel.wav", "cancel.wav",
"results.ogg" "results.ogg",
"sanka.wav"
], ],
"audioSfxLR": [ "audioSfxLR": [
"note_don.ogg", "note_don.ogg",

View File

@ -16,6 +16,7 @@ class Circle{
this.lastFrame = this.ms + 100 this.lastFrame = this.ms + 100
this.animationEnded = false this.animationEnded = false
this.timesHit = 0 this.timesHit = 0
this.timesKa = 0
this.requiredHits = config.requiredHits || 0 this.requiredHits = config.requiredHits || 0
this.rendaPlayed = false this.rendaPlayed = false
this.gogoTime = config.gogoTime this.gogoTime = config.gogoTime
@ -57,8 +58,11 @@ class Circle{
this.score = score this.score = score
this.isPlayed = score <= 0 ? score - 1 : (big ? 2 : 1) this.isPlayed = score <= 0 ? score - 1 : (big ? 2 : 1)
} }
hit(){ hit(keysKa){
this.timesHit++ this.timesHit++
if(keysKa){
this.timesKa++
}
} }
getScore(){ getScore(){
return this.score return this.score

View File

@ -32,13 +32,13 @@ class Controller{
run(syncWith){ run(syncWith){
this.game.run() this.game.run()
this.view.run() this.view.run()
this.startMainLoop()
if(syncWith){ if(syncWith){
syncWith.run() syncWith.run()
syncWith.game.elapsedTime = this.game.elapsedTime syncWith.game.elapsedTime = this.game.elapsedTime
syncWith.game.startDate = this.game.startDate syncWith.game.startDate = this.game.startDate
this.syncWith = syncWith this.syncWith = syncWith
} }
this.startMainLoop()
if(!this.multiplayer){ if(!this.multiplayer){
debugObj.controller = this debugObj.controller = this
if(debugObj.debug){ if(debugObj.debug){

View File

@ -54,7 +54,9 @@ class Game{
this.checkPlays() this.checkPlays()
// Event operations // Event operations
this.whenFadeoutMusic() this.whenFadeoutMusic()
this.whenLastCirclePlayed() if(this.controller.multiplayer !== 2){
this.whenLastCirclePlayed()
}
} }
getCircles(){ getCircles(){
return this.songData.circles return this.songData.circles
@ -91,9 +93,13 @@ class Game{
circle.played(-1, false) circle.played(-1, false)
this.updateCurrentCircle() this.updateCurrentCircle()
if(this.controller.multiplayer === 1){ if(this.controller.multiplayer === 1){
p2.send("drumroll", { var value = {
pace: (ms - circle.getMS()) / circle.timesHit pace: (ms - circle.getMS()) / circle.timesHit
}) }
if(type === "drumroll" || type === "daiDrumroll"){
value.kaAmount = circle.timesKa / circle.timesHit
}
p2.send("drumroll", value)
} }
}else{ }else{
var currentScore = 0 var currentScore = 0
@ -207,11 +213,15 @@ class Game{
this.updateGlobalScore(score, typeDai && keyDai ? 2 : 1, circle.gogoTime) this.updateGlobalScore(score, typeDai && keyDai ? 2 : 1, circle.gogoTime)
this.updateCurrentCircle() this.updateCurrentCircle()
if(this.controller.multiplayer == 1){ if(this.controller.multiplayer == 1){
p2.send("note", { var value = {
score: score, score: score,
ms: circle.getMS() - currentTime, ms: circle.getMS() - currentTime,
dai: typeDai ? keyDai ? 2 : 1 : 0 dai: typeDai ? keyDai ? 2 : 1 : 0
}) }
if((!keysDon || !typeDon) && (!keysKa || !typeKa)){
value.reverse = true
}
p2.send("note", value)
} }
}else{ }else{
if(circle.getMS() > currentTime || currentTime > circle.getEndTime()){ if(circle.getMS() > currentTime || currentTime > circle.getEndTime()){
@ -223,9 +233,9 @@ class Game{
this.checkBalloon(circle) this.checkBalloon(circle)
} }
}else if((keysDon || keysKa) && (type === "drumroll" || type === "daiDrumroll")){ }else if((keysDon || keysKa) && (type === "drumroll" || type === "daiDrumroll")){
this.checkDrumroll(circle) this.checkDrumroll(circle, keysKa)
if(keyDai){ if(keyDai){
this.checkDrumroll(circle) this.checkDrumroll(circle, keysKa)
} }
} }
} }
@ -249,11 +259,11 @@ class Game{
this.globalScore.drumroll ++ this.globalScore.drumroll ++
this.globalScore.points += score this.globalScore.points += score
} }
checkDrumroll(circle){ checkDrumroll(circle, keysKa){
var ms = this.elapsedTime var ms = this.elapsedTime
var dai = circle.getType() === "daiDrumroll" var dai = circle.getType() === "daiDrumroll"
var score = 100 var score = 100
circle.hit() circle.hit(keysKa)
var keyTime = this.controller.getKeyTime() var keyTime = this.controller.getKeyTime()
if(circle.getType() === "drumroll"){ if(circle.getType() === "drumroll"){
var sound = keyTime["don"] > keyTime["ka"] ? "don" : "ka" var sound = keyTime["don"] > keyTime["ka"] ? "don" : "ka"
@ -275,11 +285,24 @@ class Game{
this.globalScore.points += score * (dai ? 2 : 1) this.globalScore.points += score * (dai ? 2 : 1)
} }
whenLastCirclePlayed(){ whenLastCirclePlayed(){
var circles = this.songData.circles
var lastCircle = circles[circles.length - 1]
var ms = this.elapsedTime var ms = this.elapsedTime
if(!this.fadeOutStarted && ms >= lastCircle.getEndTime() + 2000){ if(!this.lastCircle){
var circles = this.songData.circles
this.lastCircle = circles[circles.length - 1].getEndTime()
if(this.controller.multiplayer){
var syncWith = this.controller.syncWith
var syncCircles = syncWith.game.songData.circles
var syncLastCircle = syncCircles[syncCircles.length - 1].getEndTime()
if(syncLastCircle > this.lastCircle){
this.lastCircle = syncLastCircle
}
}
}
if(!this.fadeOutStarted && ms >= this.lastCircle + 2000){
this.fadeOutStarted = ms this.fadeOutStarted = ms
if(this.controller.multiplayer){
this.controller.syncWith.game.fadeOutStarted = ms
}
} }
} }
whenFadeoutMusic(){ whenFadeoutMusic(){

View File

@ -65,12 +65,18 @@ class loadSong{
loadingText.firstChild.data = waitingText loadingText.firstChild.data = waitingText
loadingText.setAttribute("alt", waitingText) loadingText.setAttribute("alt", waitingText)
this.cancelButton = document.getElementById("p2-cancel-button")
this.cancelButton.style.display = "inline-block"
pageEvents.add(this.cancelButton, ["mousedown", "touchstart"], this.cancelLoad.bind(this))
this.song2Data = this.songData this.song2Data = this.songData
this.selectedSong2 = this.selectedSong this.selectedSong2 = this.selectedSong
pageEvents.add(p2, "message", event => { pageEvents.add(p2, "message", event => {
if(event.type === "gameload"){ if(event.type === "gameload"){
this.cancelButton.style.display = ""
if(event.value === this.selectedSong.difficulty){ if(event.value === this.selectedSong.difficulty){
p2.send("gamestart") this.startMultiplayer()
}else{ }else{
this.selectedSong2 = { this.selectedSong2 = {
title: this.selectedSong.title, title: this.selectedSong.title,
@ -80,13 +86,13 @@ class loadSong{
offset: this.selectedSong.offset offset: this.selectedSong.offset
} }
if(this.selectedSong.type === "tja"){ if(this.selectedSong.type === "tja"){
p2.send("gamestart") this.startMultiplayer()
}else{ }else{
loader.ajax(this.getSongPath(this.selectedSong2)).then(data => { loader.ajax(this.getSongPath(this.selectedSong2)).then(data => {
this.song2Data = data.replace(/\0/g, "").split("\n") this.song2Data = data.replace(/\0/g, "").split("\n")
p2.send("gamestart") this.startMultiplayer()
}, () => { }, () => {
p2.send("gamestart") this.startMultiplayer()
}) })
} }
} }
@ -97,6 +103,9 @@ class loadSong{
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)
taikoGame1.run(taikoGame2) taikoGame1.run(taikoGame2)
}else if(event.type === "left" || event.type === "gameend"){
this.clean()
new SongSelect(false, false, this.touchEnabled)
} }
}) })
p2.send("join", { p2.send("join", {
@ -110,7 +119,35 @@ class loadSong{
taikoGame.run() taikoGame.run()
} }
} }
startMultiplayer(repeat){
if(document.hasFocus()){
p2.send("gamestart")
}else{
if(!repeat){
assets.sounds["sanka"].play()
}
setTimeout(() => {
this.startMultiplayer(true)
}, 100)
}
}
cancelLoad(event){
if(event.type === "mousedown"){
if(event.which !== 1){
return
}
}else{
event.preventDefault()
}
p2.send("leave")
assets.sounds["don"].play()
this.cancelButton.style.pointerEvents = "none"
}
clean(){ clean(){
pageEvents.remove(p2, "message") pageEvents.remove(p2, "message")
if(this.cancelButton){
pageEvents.remove(this.cancelButton, ["mousedown", "touchstart"])
delete this.cancelButton
}
} }
} }

View File

@ -20,15 +20,19 @@ class Mekadon{
this.playAt(circle, 0, 450) this.playAt(circle, 0, 450)
} }
} }
playAt(circle, ms, score, dai){ playAt(circle, ms, score, dai, reverse){
var currentMs = circle.getMS() - this.getMS() var currentMs = circle.getMS() - this.getMS()
if(ms > currentMs - 10){ if(ms > currentMs - 10){
return this.playNow(circle, score, dai) return this.playNow(circle, score, dai, reverse)
} }
} }
playDrumrollAt(circle, ms, pace){ playDrumrollAt(circle, ms, pace, kaAmount){
if(pace && this.getMS() >= this.lastHit + pace){ if(pace && this.getMS() >= this.lastHit + pace){
this.playAt(circle, ms) var score = 1
if(kaAmount > 0){
score = Math.random() > kaAmount ? 1 : 2
}
this.playAt(circle, ms, score)
} }
} }
miss(circle){ miss(circle){
@ -41,7 +45,7 @@ class Mekadon{
return true return true
} }
} }
playNow(circle, score, dai){ playNow(circle, score, dai, reverse){
var kbd = this.controller.getBindings() var kbd = this.controller.getBindings()
var type = circle.getType() var type = circle.getType()
var keyDai = false var keyDai = false
@ -54,12 +58,19 @@ class Mekadon{
var ms = circle.getMS() var ms = circle.getMS()
} }
if(reverse){
if(type === "don" || type === "daiDon"){
type = "ka"
}else if(type === "ka" || type === "daiKa"){
type = "don"
}
}
if(type == "daiDon" && playDai){ if(type == "daiDon" && playDai){
this.setKey(kbd["don_l"], ms) this.setKey(kbd["don_l"], ms)
this.setKey(kbd["don_r"], ms) this.setKey(kbd["don_r"], ms)
this.lr = false this.lr = false
keyDai = true keyDai = true
}else if(type == "don" || type == "daiDon" || drumrollNotes){ }else if(type == "don" || type == "daiDon" || drumrollNotes && score !== 2){
this.setKey(this.lr ? kbd["don_l"] : kbd["don_r"], ms) this.setKey(this.lr ? kbd["don_l"] : kbd["don_r"], ms)
this.lr = !this.lr this.lr = !this.lr
}else if(type == "daiKa" && playDai){ }else if(type == "daiKa" && playDai){
@ -67,7 +78,7 @@ class Mekadon{
this.setKey(kbd["ka_r"], ms) this.setKey(kbd["ka_r"], ms)
this.lr = false this.lr = false
keyDai = true keyDai = true
}else if(type == "ka" || type == "daiKa"){ }else if(type == "ka" || type == "daiKa" || drumrollNotes){
this.setKey(this.lr ? kbd["ka_l"] : kbd["ka_r"], ms) this.setKey(this.lr ? kbd["ka_l"] : kbd["ka_r"], ms)
this.lr = !this.lr this.lr = !this.lr
} }
@ -77,7 +88,7 @@ class Mekadon{
} }
this.game.checkBalloon(circle) this.game.checkBalloon(circle)
}else if(type === "drumroll" || type === "daiDrumroll"){ }else if(type === "drumroll" || type === "daiDrumroll"){
this.game.checkDrumroll(circle) this.game.checkDrumroll(circle, score === 2)
}else{ }else{
this.controller.displayScore(score) this.controller.displayScore(score)
this.game.updateCombo(score) this.game.updateCombo(score)

View File

@ -106,6 +106,7 @@ class P2Connection{
this.notes = [] this.notes = []
this.drumrollPace = 45 this.drumrollPace = 45
this.dai = 2 this.dai = 2
this.kaAmount = 0
this.results = false this.results = false
break break
case "gameend": case "gameend":
@ -130,6 +131,9 @@ class P2Connection{
break break
case "drumroll": case "drumroll":
this.drumrollPace = response.value.pace this.drumrollPace = response.value.pace
if("kaAmount" in response.value){
this.kaAmount = response.value.kaAmount
}
break break
case "session": case "session":
this.clearMessage("users") this.clearMessage("users")
@ -160,7 +164,7 @@ class P2Connection{
} }
if(drumrollNotes){ if(drumrollNotes){
mekadon.playDrumrollAt(circle, 0, this.drumrollPace) mekadon.playDrumrollAt(circle, 0, this.drumrollPace, type === "drumroll" || type === "daiDrumroll" ? this.kaAmount : 0)
}else if(this.notes.length === 0){ }else if(this.notes.length === 0){
mekadon.play(circle) mekadon.play(circle)
}else{ }else{
@ -170,7 +174,7 @@ class P2Connection{
if(circle.getType() === "daiDon" || circle.getType() === "daiKa"){ if(circle.getType() === "daiDon" || circle.getType() === "daiKa"){
dai = this.dai dai = this.dai
} }
if(mekadon.playAt(circle, note.ms, note.score, dai)){ if(mekadon.playAt(circle, note.ms, note.score, dai, note.reverse)){
this.notes.shift() this.notes.shift()
} }
}else{ }else{

View File

@ -2,5 +2,6 @@
<div id="loading-song"> <div id="loading-song">
<div id="loading-don"></div> <div id="loading-don"></div>
<div class="loading-text stroke-sub" alt="Loading...">Loading...</div> <div class="loading-text stroke-sub" alt="Loading...">Loading...</div>
<div id="p2-cancel-button" class="taibtn stroke-sub" alt="Cancel">Cancel</div>
</div> </div>
</div> </div>

View File

@ -145,15 +145,30 @@ async def connection(ws, path):
await ws.send(msgobj("gameend")) 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" and not user["session"]: if type == "leave":
# Stop waiting # Stop waiting
del server_status["waiting"][user["gameid"]] if user["session"]:
del user["gameid"] if "other_user" in user and "ws" in user["other_user"]:
user["action"] = "ready" user["action"] = "songsel"
await asyncio.wait([ await asyncio.wait([
ws.send(msgobj("left")), ws.send(msgobj("left")),
notify_status() user["other_user"]["ws"].send(msgobj("users", []))
]) ])
else:
user["action"] = "ready"
user["session"] = False
await asyncio.wait([
ws.send(msgobj("gameend")),
ws.send(status_event())
])
else:
del server_status["waiting"][user["gameid"]]
del user["gameid"]
user["action"] = "ready"
await asyncio.wait([
ws.send(msgobj("left")),
notify_status()
])
if action == "loading": if action == "loading":
if type == "gamestart": if type == "gamestart":
user["action"] = "loaded" user["action"] = "loaded"