tangled
alpha
login
or
join now
alpine.girlfag.club
/
refbot
0
fork
atom
SpinShare Referee Bot
refbot.ellite.dev/overlay
0
fork
atom
overview
issues
13
pulls
pipelines
add ready-check as a valid progresslevel
alpinesystem
3 weeks ago
6fdf3eae
74926be7
+183
-177
2 changed files
expand all
collapse all
unified
split
models
Match.js
overlay
script.js
+1
-1
models/Match.js
···
65
65
const matchSchema = new Schema({
66
66
progressLevel: {
67
67
type: String,
68
68
-
enum: ['check-in', 'ban-phase', 'playing', 'picking-post-result', 'finished'],
68
68
+
enum: ['check-in', 'ban-phase', 'ready-check', 'playing', 'picking-post-result', 'finished'],
69
69
default: 'check-in',
70
70
},
71
71
meta: {
+182
-176
overlay/script.js
···
1
1
+
/* eslint-disable indent */
1
2
/* eslint-disable max-statements-per-line */
2
3
let ws = null;
3
4
let headerHideTimeout = null;
···
99
100
}
100
101
101
102
function handleMessage({ event, data }) {
103
103
+
console.log(data);
102
104
if (!data) return;
103
105
showMatchView();
104
106
105
107
if (data.mappool?.length) mappool = data.mappool;
106
108
107
109
switch (event) {
108
108
-
case 'match.checkIn':
109
109
-
updateCheckIn(data, false);
110
110
-
break;
110
110
+
case 'match.checkIn':
111
111
+
updateCheckIn(data, false);
112
112
+
break;
111
113
112
112
-
case 'match.approved':
113
113
-
updateCheckIn(data, true);
114
114
-
addFeed('Check-in approved - match starting!', 'feed-pick');
115
115
-
break;
114
114
+
case 'match.approved':
115
115
+
updateCheckIn(data, true);
116
116
+
addFeed('Check-in approved - match starting!', 'feed-pick');
117
117
+
break;
116
118
117
117
-
case 'match.snapshot': {
118
118
-
currentChartName = data.currentChart ?? null;
119
119
-
chartIsLive = data.progressLevel === 'playing';
120
120
-
p1Ready = data.players?.[0]?.ready ?? false;
121
121
-
p2Ready = data.players?.[1]?.ready ?? false;
122
122
-
updateReadyState();
123
123
-
updateScoreboard(data);
124
124
-
if (currentChartName) updateCurrentChart(getEntry(currentChartName));
125
125
-
else clearCurrentChart();
126
126
-
renderMapPool();
127
127
-
updatePhaseBarFromState(data);
128
128
-
showMatchView();
129
129
-
break;
130
130
-
}
119
119
+
case 'match.snapshot': {
120
120
+
currentChartName = data.currentChart ?? null;
121
121
+
chartIsLive = data.progressLevel === 'playing';
122
122
+
p1Ready = data.players?.[0]?.ready ?? false;
123
123
+
p2Ready = data.players?.[1]?.ready ?? false;
124
124
+
updateReadyState();
125
125
+
updateScoreboard(data);
126
126
+
if (currentChartName) updateCurrentChart(getEntry(currentChartName));
127
127
+
else clearCurrentChart();
128
128
+
renderMapPool();
129
129
+
updatePhaseBarFromState(data);
130
130
+
showMatchView();
131
131
+
break;
132
132
+
}
131
133
132
132
-
case 'match.start':
133
133
-
document.getElementById('checkin-banner').classList.remove('visible');
134
134
-
document.getElementById('end-banner').style.display = 'none';
135
135
-
currentChartName = null;
136
136
-
chartIsLive = false;
137
137
-
p1Ready = false;
138
138
-
p2Ready = false;
139
139
-
updateReadyState();
140
140
-
updateScoreboard(data);
141
141
-
renderMapPool();
142
142
-
clearCurrentChart();
143
143
-
addFeed('Match started - ban phase beginning', 'feed-pick');
144
144
-
showMatchView();
145
145
-
break;
134
134
+
case 'match.start':
135
135
+
document.getElementById('checkin-banner').classList.remove('visible');
136
136
+
document.getElementById('end-banner').style.display = 'none';
137
137
+
currentChartName = null;
138
138
+
chartIsLive = false;
139
139
+
p1Ready = false;
140
140
+
p2Ready = false;
141
141
+
updateReadyState();
142
142
+
updateScoreboard(data);
143
143
+
renderMapPool();
144
144
+
clearCurrentChart();
145
145
+
addFeed('Match started - ban phase beginning', 'feed-pick');
146
146
+
showMatchView();
147
147
+
break;
146
148
147
147
-
case 'match.banOrderDecided': {
148
148
-
const firstBanner = data.players?.find(p => p.discordId === data.firstBannerDiscordId)
149
149
-
?? data.players?.find(p => p.discordId === data.banPhase?.currentBannerDiscordId);
150
150
-
const fbn = firstBanner?.displayName ?? '?';
151
151
-
addFeed(`${fbn} will ban first`, 'feed-ban');
152
152
-
updatePhaseBar('banning', `${fbn} is banning...`);
153
153
-
updateScoreboard(data);
154
154
-
renderMapPool();
155
155
-
break;
156
156
-
}
149
149
+
case 'match.banOrderDecided': {
150
150
+
const firstBanner = data.players?.find(p => p.discordId === data.firstBannerDiscordId)
151
151
+
?? data.players?.find(p => p.discordId === data.banPhase?.currentBannerDiscordId);
152
152
+
const fbn = firstBanner?.displayName ?? '?';
153
153
+
addFeed(`${fbn} will ban first`, 'feed-ban');
154
154
+
updatePhaseBar('banning', `${fbn} is banning...`);
155
155
+
updateScoreboard(data);
156
156
+
renderMapPool();
157
157
+
break;
158
158
+
}
157
159
158
158
-
case 'match.ban': {
159
159
-
const banner = data.players?.find(p => p.discordId === data.bannedByDiscordId);
160
160
-
const bannerName = banner?.displayName ?? 'Someone';
161
161
-
const bannedEntry = data.mappool?.find(e => e.csvName === data.bannedChart);
162
162
-
const bannedDisplay = bannedEntry ? entryDisplay(bannedEntry) : data.bannedChart;
163
163
-
addFeed(`${bannerName} banned ${bannedDisplay}`, 'feed-ban');
164
164
-
const nextBanner = data.players?.find(p => p.discordId === data.banPhase?.currentBannerDiscordId);
165
165
-
if (nextBanner) {
166
166
-
updatePhaseBar('banning', `${nextBanner.displayName} is banning...`);
160
160
+
case 'match.ban': {
161
161
+
const banner = data.players?.find(p => p.discordId === data.bannedByDiscordId);
162
162
+
const bannerName = banner?.displayName ?? 'Someone';
163
163
+
const bannedEntry = data.mappool?.find(e => e.csvName === data.bannedChart);
164
164
+
const bannedDisplay = bannedEntry ? entryDisplay(bannedEntry) : data.bannedChart;
165
165
+
addFeed(`${bannerName} banned ${bannedDisplay}`, 'feed-ban');
166
166
+
const nextBanner = data.players?.find(p => p.discordId === data.banPhase?.currentBannerDiscordId);
167
167
+
if (nextBanner) {
168
168
+
updatePhaseBar('banning', `${nextBanner.displayName} is banning...`);
169
169
+
}
170
170
+
else {
171
171
+
updatePhaseBar(null);
172
172
+
}
173
173
+
updateScoreboard(data);
174
174
+
renderMapPool();
175
175
+
break;
167
176
}
168
168
-
else {
169
169
-
updatePhaseBar(null);
170
170
-
}
171
171
-
updateScoreboard(data);
172
172
-
renderMapPool();
173
173
-
break;
174
174
-
}
175
177
176
176
-
case 'match.firstChartDetermined': {
177
177
-
const entry = getEntry(data.chart ?? data.currentChart);
178
178
-
currentChartName = data.chart ?? data.currentChart ?? null;
179
179
-
chartIsLive = false;
180
180
-
p1Ready = false;
181
181
-
p2Ready = false;
182
182
-
updateReadyState();
183
183
-
updateScoreboard(data);
184
184
-
if (entry) updateCurrentChart(entry);
185
185
-
renderMapPool();
186
186
-
addFeed(`Last map standing: ${entry ? entryDisplay(entry) : currentChartName}`, 'feed-pick');
187
187
-
updatePhaseBar('playing', `Ready check: ${entry ? entryDisplay(entry) : '...'}`);
188
188
-
break;
189
189
-
}
190
190
-
191
191
-
case 'match.pickPhaseStart': {
192
192
-
const firstPicker = data.players?.find(p => p.discordId === data.currentPickerDiscordId);
193
193
-
const fpn = firstPicker?.displayName ?? '?';
194
194
-
updatePhaseBar('picking', `${fpn} is picking...`);
195
195
-
updateScoreboard(data);
196
196
-
renderMapPool();
197
197
-
clearCurrentChart();
198
198
-
addFeed(`Bans complete - ${fpn} picks first`, 'feed-pick');
199
199
-
break;
200
200
-
}
201
201
-
202
202
-
case 'match.pick': {
203
203
-
currentChartName = data.currentChart ?? null;
204
204
-
chartIsLive = false;
205
205
-
p1Ready = false;
206
206
-
p2Ready = false;
207
207
-
updateReadyState();
208
208
-
updateScoreboard(data);
209
209
-
const pickedEntry = currentChartName ? getEntry(currentChartName) : null;
210
210
-
if (pickedEntry) updateCurrentChart(pickedEntry);
211
211
-
renderMapPool();
212
212
-
if (pickedEntry) {
213
213
-
const picker = data.players?.find(p => p.discordId === data.pickedByDiscordId);
214
214
-
const pn = picker?.displayName ?? '?';
215
215
-
addFeed(`${pn} picked ${entryDisplay(pickedEntry)}`, 'feed-pick');
178
178
+
case 'match.firstChartDetermined': {
179
179
+
const entry = getEntry(data.chart ?? data.currentChart);
180
180
+
currentChartName = data.chart ?? data.currentChart ?? null;
181
181
+
chartIsLive = false;
182
182
+
p1Ready = false;
183
183
+
p2Ready = false;
184
184
+
updateReadyState();
185
185
+
updateScoreboard(data);
186
186
+
if (entry) updateCurrentChart(entry);
187
187
+
renderMapPool();
188
188
+
addFeed(`Last map standing: ${entry ? entryDisplay(entry) : currentChartName}`, 'feed-pick');
189
189
+
updatePhaseBar('playing', `Ready check: ${entry ? entryDisplay(entry) : '...'}`);
190
190
+
break;
216
191
}
217
217
-
updatePhaseBar('playing', `Ready check: ${pickedEntry ? entryDisplay(pickedEntry) : '...'}`);
218
218
-
break;
219
219
-
}
220
192
221
221
-
case 'match.playerReady': {
222
222
-
const prevP1Ready = p1Ready;
223
223
-
const prevP2Ready = p2Ready;
224
224
-
p1Ready = data.players?.[0]?.ready ?? false;
225
225
-
p2Ready = data.players?.[1]?.ready ?? false;
226
226
-
updateReadyState();
227
227
-
const newlyReadyName = !prevP1Ready && p1Ready
228
228
-
? (data.players?.[0]?.displayName ?? 'P1')
229
229
-
: !prevP2Ready && p2Ready
230
230
-
? (data.players?.[1]?.displayName ?? 'P2')
231
231
-
: null;
232
232
-
if (newlyReadyName) addFeed(`${newlyReadyName} is ready!`, 'feed-win');
233
233
-
break;
234
234
-
}
193
193
+
case 'match.pickPhaseStart': {
194
194
+
const firstPicker = data.players?.find(p => p.discordId === data.currentPickerDiscordId);
195
195
+
const fpn = firstPicker?.displayName ?? '?';
196
196
+
updatePhaseBar('picking', `${fpn} is picking...`);
197
197
+
updateScoreboard(data);
198
198
+
renderMapPool();
199
199
+
clearCurrentChart();
200
200
+
addFeed(`Bans complete - ${fpn} picks first`, 'feed-pick');
201
201
+
break;
202
202
+
}
235
203
236
236
-
case 'match.chartStart': {
237
237
-
p1Ready = true;
238
238
-
p2Ready = true;
239
239
-
updateReadyState();
240
240
-
currentChartName = data.currentChart ?? currentChartName;
241
241
-
chartIsLive = true;
242
242
-
updateScoreboard(data);
243
243
-
const liveEntry = currentChartName ? getEntry(currentChartName) : null;
244
244
-
if (liveEntry) updateCurrentChart(liveEntry);
245
245
-
renderMapPool();
246
246
-
if (liveEntry) addFeed(`Now playing: ${entryDisplay(liveEntry)}`, 'feed-pick');
247
247
-
updatePhaseBar('playing', `Playing: ${liveEntry ? entryDisplay(liveEntry) : '...'}`);
248
248
-
break;
249
249
-
}
204
204
+
case 'match.pick': {
205
205
+
currentChartName = data.currentChart ?? null;
206
206
+
chartIsLive = false;
207
207
+
p1Ready = false;
208
208
+
p2Ready = false;
209
209
+
updateReadyState();
210
210
+
updateScoreboard(data);
211
211
+
const pickedEntry = currentChartName ? getEntry(currentChartName) : null;
212
212
+
if (pickedEntry) updateCurrentChart(pickedEntry);
213
213
+
renderMapPool();
214
214
+
if (pickedEntry) {
215
215
+
const picker = data.players?.find(p => p.discordId === data.pickedByDiscordId);
216
216
+
const pn = picker?.displayName ?? '?';
217
217
+
addFeed(`${pn} picked ${entryDisplay(pickedEntry)}`, 'feed-pick');
218
218
+
}
219
219
+
updatePhaseBar('playing', `Ready check: ${pickedEntry ? entryDisplay(pickedEntry) : '...'}`);
220
220
+
break;
221
221
+
}
250
222
251
251
-
case 'match.chartResult': {
252
252
-
chartIsLive = false;
253
253
-
p1Ready = false;
254
254
-
p2Ready = false;
255
255
-
updateReadyState();
256
256
-
updateScoreboard(data);
257
257
-
const resultEntry = (data.mappool ?? [])
258
258
-
.filter(e => e.status?.played && e.result)
259
259
-
.sort((a, b) => new Date(b.status.playedAt) - new Date(a.status.playedAt))[0] ?? null;
260
260
-
currentChartName = null;
261
261
-
clearCurrentChart();
262
262
-
renderMapPool();
263
263
-
const chartTitle = resultEntry ? entryDisplay(resultEntry) : 'Chart';
264
264
-
const p1n = data.players?.[0]?.displayName ?? 'P1';
265
265
-
const p2n = data.players?.[1]?.displayName ?? 'P2';
266
266
-
const result = resultEntry?.result ?? {};
267
267
-
const s1 = fmtScore(result.score1, result.fc1, result.pfc1);
268
268
-
const s2 = fmtScore(result.score2, result.fc2, result.pfc2);
269
269
-
const winnerPlayer = data.players?.find(p => p.discordId === result.winnerDiscordId);
270
270
-
addFeed(`${chartTitle}: ${p1n} ${s1} vs ${p2n} ${s2} - ${winnerPlayer?.displayName ?? 'someone'} wins!`, 'feed-win');
271
271
-
const nextPicker = data.players?.find(p => p.discordId === data.currentPickerDiscordId);
272
272
-
if (nextPicker) updatePhaseBar('picking', `${nextPicker.displayName} is picking...`);
273
273
-
break;
274
274
-
}
223
223
+
case 'match.playerReady': {
224
224
+
const prevP1Ready = p1Ready;
225
225
+
const prevP2Ready = p2Ready;
226
226
+
p1Ready = data.players?.[0]?.ready ?? false;
227
227
+
p2Ready = data.players?.[1]?.ready ?? false;
228
228
+
updateReadyState();
229
229
+
const newlyReadyName = !prevP1Ready && p1Ready
230
230
+
? (data.players?.[0]?.displayName ?? 'P1')
231
231
+
: !prevP2Ready && p2Ready
232
232
+
? (data.players?.[1]?.displayName ?? 'P2')
233
233
+
: null;
234
234
+
if (newlyReadyName) addFeed(`${newlyReadyName} is ready!`, 'feed-win');
235
235
+
break;
236
236
+
}
275
237
276
276
-
case 'match.end':
277
277
-
chartIsLive = false;
278
278
-
p1Ready = false;
279
279
-
p2Ready = false;
280
280
-
updateReadyState();
281
281
-
updateScoreboard(data);
282
282
-
currentChartName = null;
283
283
-
clearCurrentChart();
284
284
-
renderMapPool();
285
285
-
updatePhaseBar(null);
286
286
-
if (data.winner) {
287
287
-
showEndBanner(data.winner);
288
288
-
addFeed(`Match over! ${data.winner} wins!`, 'feed-end');
238
238
+
case 'match.chartStart': {
239
239
+
p1Ready = true;
240
240
+
p2Ready = true;
241
241
+
updateReadyState();
242
242
+
currentChartName = data.currentChart ?? currentChartName;
243
243
+
chartIsLive = true;
244
244
+
updateScoreboard(data);
245
245
+
const liveEntry = currentChartName ? getEntry(currentChartName) : null;
246
246
+
if (liveEntry) updateCurrentChart(liveEntry);
247
247
+
renderMapPool();
248
248
+
if (liveEntry) addFeed(`Now playing: ${entryDisplay(liveEntry)}`, 'feed-pick');
249
249
+
updatePhaseBar('playing', `Playing: ${liveEntry ? entryDisplay(liveEntry) : '...'}`);
250
250
+
break;
289
251
}
290
290
-
break;
291
252
292
292
-
default:
293
293
-
updateScoreboard(data);
294
294
-
if (data.currentChart) {
295
295
-
currentChartName = data.currentChart;
296
296
-
updateCurrentChart(getEntry(currentChartName));
253
253
+
case 'match.chartResult': {
254
254
+
chartIsLive = false;
255
255
+
p1Ready = false;
256
256
+
p2Ready = false;
257
257
+
updateReadyState();
258
258
+
updateScoreboard(data);
259
259
+
const resultEntry = (data.mappool ?? [])
260
260
+
.filter(e => e.status?.played && e.result)
261
261
+
.sort((a, b) => new Date(b.status.playedAt) - new Date(a.status.playedAt))[0] ?? null;
262
262
+
currentChartName = null;
263
263
+
clearCurrentChart();
264
264
+
renderMapPool();
265
265
+
const chartTitle = resultEntry ? entryDisplay(resultEntry) : 'Chart';
266
266
+
const p1n = data.players?.[0]?.displayName ?? 'P1';
267
267
+
const p2n = data.players?.[1]?.displayName ?? 'P2';
268
268
+
const result = resultEntry?.result ?? {};
269
269
+
const s1 = fmtScore(result.score1, result.fc1, result.pfc1);
270
270
+
const s2 = fmtScore(result.score2, result.fc2, result.pfc2);
271
271
+
const winnerPlayer = data.players?.find(p => p.discordId === result.winnerDiscordId);
272
272
+
addFeed(`${chartTitle}: ${p1n} ${s1} vs ${p2n} ${s2} - ${winnerPlayer?.displayName ?? 'someone'} wins!`, 'feed-win');
273
273
+
const nextPicker = data.players?.find(p => p.discordId === data.currentPickerDiscordId);
274
274
+
if (nextPicker) updatePhaseBar('picking', `${nextPicker.displayName} is picking...`);
275
275
+
break;
297
276
}
298
298
-
renderMapPool();
277
277
+
278
278
+
case 'match.end':
279
279
+
chartIsLive = false;
280
280
+
p1Ready = false;
281
281
+
p2Ready = false;
282
282
+
updateReadyState();
283
283
+
updateScoreboard(data);
284
284
+
currentChartName = null;
285
285
+
clearCurrentChart();
286
286
+
renderMapPool();
287
287
+
updatePhaseBar(null);
288
288
+
if (data.winner) {
289
289
+
showEndBanner(data.winner);
290
290
+
addFeed(`Match over! ${data.winner} wins!`, 'feed-end');
291
291
+
}
292
292
+
break;
293
293
+
294
294
+
default:
295
295
+
updateScoreboard(data);
296
296
+
if (data.currentChart) {
297
297
+
currentChartName = data.currentChart;
298
298
+
updateCurrentChart(getEntry(currentChartName));
299
299
+
}
300
300
+
renderMapPool();
299
301
}
300
302
}
301
303
···
305
307
const banner = data.players?.find(p => p.discordId === data.banPhase?.currentBannerDiscordId);
306
308
if (banner) updatePhaseBar('banning', `${banner.displayName} is banning...`);
307
309
else updatePhaseBar(null);
310
310
+
}
311
311
+
else if (level === 'ready-check') {
312
312
+
const entry = data.currentChart ? getEntry(data.currentChart) : null;
313
313
+
updatePhaseBar('ready-check', `Readying up for: ${entry ? entryDisplay(entry) : '...'}`);
308
314
}
309
315
else if (level === 'picking-post-result') {
310
316
const picker = data.players?.find(p => p.discordId === data.currentPickerDiscordId);