taiko-web development has returned to GitHub. This Gitea instance will be shut down soon.
If your taiko-web fork is unavailable due to DMCA takedown, you can contact GitHub Support and ask for it to be deleted.
Browse Source

P2: Multiplayer improvements

kekero 9 months ago
parent
commit
25d705ffcf

+ 5
- 0
public/src/css/loadsong.css View File

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

+ 1
- 0
public/src/css/main.css View File

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

+ 5
- 1
public/src/js/circle.js View File

@@ -16,6 +16,7 @@ class Circle{
16 16
 		this.lastFrame = this.ms + 100
17 17
 		this.animationEnded = false
18 18
 		this.timesHit = 0
19
+		this.timesKa = 0
19 20
 		this.requiredHits = config.requiredHits || 0
20 21
 		this.rendaPlayed = false
21 22
 		this.gogoTime = config.gogoTime
@@ -57,8 +58,11 @@ class Circle{
57 58
 		this.score = score
58 59
 		this.isPlayed = score <= 0 ? score - 1 : (big ? 2 : 1)
59 60
 	}
60
-	hit(){
61
+	hit(keysKa){
61 62
 		this.timesHit++
63
+		if(keysKa){
64
+			this.timesKa++
65
+		}
62 66
 	}
63 67
 	getScore(){
64 68
 		return this.score

+ 1
- 1
public/src/js/controller.js View File

@@ -32,13 +32,13 @@ class Controller{
32 32
 	run(syncWith){
33 33
 		this.game.run()
34 34
 		this.view.run()
35
-		this.startMainLoop()
36 35
 		if(syncWith){
37 36
 			syncWith.run()
38 37
 			syncWith.game.elapsedTime = this.game.elapsedTime
39 38
 			syncWith.game.startDate = this.game.startDate
40 39
 			this.syncWith = syncWith
41 40
 		}
41
+		this.startMainLoop()
42 42
 		if(!this.multiplayer){
43 43
 			debugObj.controller = this
44 44
 			if(debugObj.debug){

+ 35
- 12
public/src/js/game.js View File

@@ -54,7 +54,9 @@ class Game{
54 54
 		this.checkPlays()
55 55
 		// Event operations
56 56
 		this.whenFadeoutMusic()
57
-		this.whenLastCirclePlayed()
57
+		if(this.controller.multiplayer !== 2){
58
+			this.whenLastCirclePlayed()
59
+		}
58 60
 	}
59 61
 	getCircles(){
60 62
 		return this.songData.circles
@@ -91,9 +93,13 @@ class Game{
91 93
 							circle.played(-1, false)
92 94
 							this.updateCurrentCircle()
93 95
 							if(this.controller.multiplayer === 1){
94
-								p2.send("drumroll", {
96
+								var value = {
95 97
 									pace: (ms - circle.getMS()) / circle.timesHit
96
-								})
98
+								}
99
+								if(type === "drumroll" || type === "daiDrumroll"){
100
+									value.kaAmount = circle.timesKa / circle.timesHit
101
+								}
102
+								p2.send("drumroll", value)
97 103
 							}
98 104
 						}else{
99 105
 							var currentScore = 0
@@ -207,11 +213,15 @@ class Game{
207 213
 			this.updateGlobalScore(score, typeDai && keyDai ? 2 : 1, circle.gogoTime)
208 214
 			this.updateCurrentCircle()
209 215
 			if(this.controller.multiplayer == 1){
210
-				p2.send("note", {
216
+				var value = {
211 217
 					score: score,
212 218
 					ms: circle.getMS() - currentTime,
213 219
 					dai: typeDai ? keyDai ? 2 : 1 : 0
214
-				})
220
+				}
221
+				if((!keysDon || !typeDon) && (!keysKa || !typeKa)){
222
+					value.reverse = true
223
+				}
224
+				p2.send("note", value)
215 225
 			}
216 226
 		}else{
217 227
 			if(circle.getMS() > currentTime || currentTime > circle.getEndTime()){
@@ -223,9 +233,9 @@ class Game{
223 233
 					this.checkBalloon(circle)
224 234
 				}
225 235
 			}else if((keysDon || keysKa) && (type === "drumroll" || type === "daiDrumroll")){
226
-				this.checkDrumroll(circle)
236
+				this.checkDrumroll(circle, keysKa)
227 237
 				if(keyDai){
228
-					this.checkDrumroll(circle)
238
+					this.checkDrumroll(circle, keysKa)
229 239
 				}
230 240
 			}
231 241
 		}
@@ -249,11 +259,11 @@ class Game{
249 259
 		this.globalScore.drumroll ++
250 260
 		this.globalScore.points += score
251 261
 	}
252
-	checkDrumroll(circle){
262
+	checkDrumroll(circle, keysKa){
253 263
 		var ms = this.elapsedTime
254 264
 		var dai = circle.getType() === "daiDrumroll"
255 265
 		var score = 100
256
-		circle.hit()
266
+		circle.hit(keysKa)
257 267
 		var keyTime = this.controller.getKeyTime()
258 268
 		if(circle.getType() === "drumroll"){
259 269
 			var sound = keyTime["don"] > keyTime["ka"] ? "don" : "ka"
@@ -275,11 +285,24 @@ class Game{
275 285
 		this.globalScore.points += score * (dai ? 2 : 1)
276 286
 	}
277 287
 	whenLastCirclePlayed(){
278
-		var circles = this.songData.circles
279
-		var lastCircle = circles[circles.length - 1]
280 288
 		var ms = this.elapsedTime
281
-		if(!this.fadeOutStarted && ms >= lastCircle.getEndTime() + 2000){
289
+		if(!this.lastCircle){
290
+			var circles = this.songData.circles
291
+			this.lastCircle = circles[circles.length - 1].getEndTime()
292
+			if(this.controller.multiplayer){
293
+				var syncWith = this.controller.syncWith
294
+				var syncCircles = syncWith.game.songData.circles
295
+				var syncLastCircle = syncCircles[syncCircles.length - 1].getEndTime()
296
+				if(syncLastCircle > this.lastCircle){
297
+					this.lastCircle = syncLastCircle
298
+				}
299
+			}
300
+		}
301
+		if(!this.fadeOutStarted && ms >= this.lastCircle + 2000){
282 302
 			this.fadeOutStarted = ms
303
+			if(this.controller.multiplayer){
304
+				this.controller.syncWith.game.fadeOutStarted = ms
305
+			}
283 306
 		}
284 307
 	}
285 308
 	whenFadeoutMusic(){

+ 43
- 4
public/src/js/loadsong.js View File

@@ -65,12 +65,18 @@ class loadSong{
65 65
 			loadingText.firstChild.data = waitingText
66 66
 			loadingText.setAttribute("alt", waitingText)
67 67
 			
68
+			this.cancelButton = document.getElementById("p2-cancel-button")
69
+			this.cancelButton.style.display = "inline-block"
70
+			pageEvents.add(this.cancelButton, ["mousedown", "touchstart"], this.cancelLoad.bind(this))
71
+			
68 72
 			this.song2Data = this.songData
69 73
 			this.selectedSong2 = this.selectedSong
70 74
 			pageEvents.add(p2, "message", event => {
71 75
 				if(event.type === "gameload"){
76
+					this.cancelButton.style.display = ""
77
+					
72 78
 					if(event.value === this.selectedSong.difficulty){
73
-						p2.send("gamestart")
79
+						this.startMultiplayer()
74 80
 					}else{
75 81
 						this.selectedSong2 = {
76 82
 							title: this.selectedSong.title,
@@ -80,13 +86,13 @@ class loadSong{
80 86
 							offset: this.selectedSong.offset
81 87
 						}
82 88
 						if(this.selectedSong.type === "tja"){
83
-							p2.send("gamestart")
89
+							this.startMultiplayer()
84 90
 						}else{
85 91
 							loader.ajax(this.getSongPath(this.selectedSong2)).then(data => {
86 92
 								this.song2Data = data.replace(/\0/g, "").split("\n")
87
-								p2.send("gamestart")
93
+								this.startMultiplayer()
88 94
 							}, () => {
89
-								p2.send("gamestart")
95
+								this.startMultiplayer()
90 96
 							})
91 97
 						}
92 98
 					}
@@ -97,6 +103,9 @@ class loadSong{
97 103
 					var taikoGame1 = new Controller(this.selectedSong, this.songData, false, 1, this.touchEnabled)
98 104
 					var taikoGame2 = new Controller(this.selectedSong2, this.song2Data, true, 2, this.touchEnabled)
99 105
 					taikoGame1.run(taikoGame2)
106
+				}else if(event.type === "left" || event.type === "gameend"){
107
+					this.clean()
108
+					new SongSelect(false, false, this.touchEnabled)
100 109
 				}
101 110
 			})
102 111
 			p2.send("join", {
@@ -110,7 +119,37 @@ class loadSong{
110 119
 			taikoGame.run()
111 120
 		}
112 121
 	}
122
+	startMultiplayer(repeat){
123
+		if(document.hasFocus()){
124
+			p2.send("gamestart")
125
+		}else{
126
+			if(!repeat){
127
+				for(var i = 0; i < 3; i++){
128
+					assets.sounds["note_don"].play(i * 0.2)
129
+				}
130
+			}
131
+			setTimeout(() => {
132
+				this.startMultiplayer(true)
133
+			}, 100)
134
+		}
135
+	}
136
+	cancelLoad(event){
137
+		if(event.type === "mousedown"){
138
+			if(event.which !== 1){
139
+				return
140
+			}
141
+		}else{
142
+			event.preventDefault()
143
+		}
144
+		p2.send("leave")
145
+		assets.sounds["don"].play()
146
+		this.cancelButton.style.pointerEvents = "none"
147
+	}
113 148
 	clean(){
114 149
 		pageEvents.remove(p2, "message")
150
+		if(this.cancelButton){
151
+			pageEvents.remove(this.cancelButton, ["mousedown", "touchstart"])
152
+			delete this.cancelButton
153
+		}
115 154
 	}
116 155
 }

+ 19
- 8
public/src/js/mekadon.js View File

@@ -20,15 +20,19 @@ class Mekadon{
20 20
 			this.playAt(circle, 0, 450)
21 21
 		}
22 22
 	}
23
-	playAt(circle, ms, score, dai){
23
+	playAt(circle, ms, score, dai, reverse){
24 24
 		var currentMs = circle.getMS() - this.getMS()
25 25
 		if(ms > currentMs - 10){
26
-			return this.playNow(circle, score, dai)
26
+			return this.playNow(circle, score, dai, reverse)
27 27
 		}
28 28
 	}
29
-	playDrumrollAt(circle, ms, pace){
29
+	playDrumrollAt(circle, ms, pace, kaAmount){
30 30
 		if(pace && this.getMS() >= this.lastHit + pace){
31
-			this.playAt(circle, ms)
31
+			var score = 1
32
+			if(kaAmount > 0){
33
+				score = Math.random() > kaAmount ? 1 : 2
34
+			}
35
+			this.playAt(circle, ms, score)
32 36
 		}
33 37
 	}
34 38
 	miss(circle){
@@ -41,7 +45,7 @@ class Mekadon{
41 45
 			return true
42 46
 		}
43 47
 	}
44
-	playNow(circle, score, dai){
48
+	playNow(circle, score, dai, reverse){
45 49
 		var kbd = this.controller.getBindings()
46 50
 		var type = circle.getType()
47 51
 		var keyDai = false
@@ -54,12 +58,19 @@ class Mekadon{
54 58
 			var ms = circle.getMS()
55 59
 		}
56 60
 		
61
+		if(reverse){
62
+			if(type === "don" || type === "daiDon"){
63
+				type = "ka"
64
+			}else if(type === "ka" || type === "daiKa"){
65
+				type = "don"
66
+			}
67
+		}
57 68
 		if(type == "daiDon" && playDai){
58 69
 			this.setKey(kbd["don_l"], ms)
59 70
 			this.setKey(kbd["don_r"], ms)
60 71
 			this.lr = false
61 72
 			keyDai = true
62
-		}else if(type == "don" || type == "daiDon" || drumrollNotes){
73
+		}else if(type == "don" || type == "daiDon" || drumrollNotes && score !== 2){
63 74
 			this.setKey(this.lr ? kbd["don_l"] : kbd["don_r"], ms)
64 75
 			this.lr = !this.lr
65 76
 		}else if(type == "daiKa" && playDai){
@@ -67,7 +78,7 @@ class Mekadon{
67 78
 			this.setKey(kbd["ka_r"], ms)
68 79
 			this.lr = false
69 80
 			keyDai = true
70
-		}else if(type == "ka" || type == "daiKa"){
81
+		}else if(type == "ka" || type == "daiKa" || drumrollNotes){
71 82
 			this.setKey(this.lr ? kbd["ka_l"] : kbd["ka_r"], ms)
72 83
 			this.lr = !this.lr
73 84
 		}
@@ -77,7 +88,7 @@ class Mekadon{
77 88
 			}
78 89
 			this.game.checkBalloon(circle)
79 90
 		}else if(type === "drumroll" || type === "daiDrumroll"){
80
-			this.game.checkDrumroll(circle)
91
+			this.game.checkDrumroll(circle, score === 2)
81 92
 		}else{
82 93
 			this.controller.displayScore(score)
83 94
 			this.game.updateCombo(score)

+ 6
- 2
public/src/js/p2.js View File

@@ -106,6 +106,7 @@ class P2Connection{
106 106
 				this.notes = []
107 107
 				this.drumrollPace = 45
108 108
 				this.dai = 2
109
+				this.kaAmount = 0
109 110
 				this.results = false
110 111
 				break
111 112
 			case "gameend":
@@ -130,6 +131,9 @@ class P2Connection{
130 131
 				break
131 132
 			case "drumroll":
132 133
 				this.drumrollPace = response.value.pace
134
+				if("kaAmount" in response.value){
135
+					this.kaAmount = response.value.kaAmount
136
+				}
133 137
 				break
134 138
 			case "session":
135 139
 				this.clearMessage("users")
@@ -160,7 +164,7 @@ class P2Connection{
160 164
 			}
161 165
 			
162 166
 			if(drumrollNotes){
163
-				mekadon.playDrumrollAt(circle, 0, this.drumrollPace)
167
+				mekadon.playDrumrollAt(circle, 0, this.drumrollPace, type === "drumroll" || type === "daiDrumroll" ? this.kaAmount : 0)
164 168
 			}else if(this.notes.length === 0){
165 169
 				mekadon.play(circle)
166 170
 			}else{
@@ -170,7 +174,7 @@ class P2Connection{
170 174
 					if(circle.getType() === "daiDon" || circle.getType() === "daiKa"){
171 175
 						dai = this.dai
172 176
 					}
173
-					if(mekadon.playAt(circle, note.ms, note.score, dai)){
177
+					if(mekadon.playAt(circle, note.ms, note.score, dai, note.reverse)){
174 178
 						this.notes.shift()
175 179
 					}
176 180
 				}else{

+ 1
- 0
public/src/views/loadsong.html View File

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

+ 23
- 8
server.py View File

@@ -145,15 +145,30 @@ async def connection(ws, path):
145 145
 							await ws.send(msgobj("gameend"))
146 146
 				elif action == "waiting" or action == "loading" or action == "loaded":
147 147
 					# Waiting for another user
148
-					if type == "leave" and not user["session"]:
148
+					if type == "leave":
149 149
 						# Stop waiting
150
-						del server_status["waiting"][user["gameid"]]
151
-						del user["gameid"]
152
-						user["action"] = "ready"
153
-						await asyncio.wait([
154
-							ws.send(msgobj("left")),
155
-							notify_status()
156
-						])
150
+						if user["session"]:
151
+							if "other_user" in user and "ws" in user["other_user"]:
152
+								user["action"] = "songsel"
153
+								await asyncio.wait([
154
+									ws.send(msgobj("left")),
155
+									user["other_user"]["ws"].send(msgobj("users", []))
156
+								])
157
+							else:
158
+								user["action"] = "ready"
159
+								user["session"] = False
160
+								await asyncio.wait([
161
+									ws.send(msgobj("gameend")),
162
+									ws.send(status_event())
163
+								])
164
+						else:
165
+							del server_status["waiting"][user["gameid"]]
166
+							del user["gameid"]
167
+							user["action"] = "ready"
168
+							await asyncio.wait([
169
+								ws.send(msgobj("left")),
170
+								notify_status()
171
+							])
157 172
 					if action == "loading":
158 173
 						if type == "gamestart":
159 174
 							user["action"] = "loaded"

Loading…
Cancel
Save