@jjlmoya/utils-sports 1.60.0 → 1.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/tool/rugbyScoreKeeper/component.astro +42 -30
- package/src/tool/rugbyScoreKeeper/i18n/de.ts +4 -0
- package/src/tool/rugbyScoreKeeper/i18n/en.ts +4 -0
- package/src/tool/rugbyScoreKeeper/i18n/es.ts +4 -0
- package/src/tool/rugbyScoreKeeper/i18n/fr.ts +4 -0
- package/src/tool/rugbyScoreKeeper/i18n/id.ts +4 -0
- package/src/tool/rugbyScoreKeeper/i18n/it.ts +4 -0
- package/src/tool/rugbyScoreKeeper/i18n/ja.ts +4 -0
- package/src/tool/rugbyScoreKeeper/i18n/ko.ts +4 -0
- package/src/tool/rugbyScoreKeeper/i18n/nl.ts +4 -0
- package/src/tool/rugbyScoreKeeper/i18n/pl.ts +4 -0
- package/src/tool/rugbyScoreKeeper/i18n/pt.ts +4 -0
- package/src/tool/rugbyScoreKeeper/i18n/ru.ts +4 -0
- package/src/tool/rugbyScoreKeeper/i18n/sv.ts +4 -0
- package/src/tool/rugbyScoreKeeper/i18n/tr.ts +4 -0
- package/src/tool/rugbyScoreKeeper/i18n/zh.ts +4 -0
- package/src/tool/rugbyScoreKeeper/logic.test.ts +12 -1
- package/src/tool/rugbyScoreKeeper/logic.ts +11 -14
- package/src/tool/rugbyScoreKeeper/rugby-scorekeeper.css +166 -30
- package/src/tool/rugbyScoreKeeper/storage.test.ts +66 -0
- package/src/tool/rugbyScoreKeeper/storage.ts +126 -0
- package/src/tool/rugbyScoreKeeper/ui-helpers.ts +4 -10
- package/src/tool/rugbyScoreKeeper/ui-render.ts +149 -0
- package/src/tool/rugbyScoreKeeper/ui-runtime.ts +260 -0
- package/src/tool/rugbyScoreKeeper/ui.ts +4 -0
- package/src/tool/rugbyScoreKeeper/ui-init.ts +0 -264
package/package.json
CHANGED
|
@@ -8,16 +8,21 @@ interface Props {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
const { ui } = Astro.props;
|
|
11
|
-
const t = (ui ?? {}) as RugbyScoreKeeperUI;
|
|
11
|
+
const t = (ui ?? {}) as unknown as RugbyScoreKeeperUI;
|
|
12
12
|
---
|
|
13
13
|
|
|
14
14
|
<div class="rg-app" id="rg-app" data-rg-ui={JSON.stringify(t)}>
|
|
15
15
|
|
|
16
16
|
<div class="rg-main-card">
|
|
17
17
|
|
|
18
|
+
<div class="rg-save-status" id="rg-save-status" role="status">
|
|
19
|
+
<span class="rg-save-dot" aria-hidden="true"></span>
|
|
20
|
+
<span id="rg-save-status-text">{t.savedLocally}</span>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
18
23
|
<div class="rg-strip">
|
|
19
|
-
<div class="rg-team">
|
|
20
|
-
<input type="text" class="rg-name" id="rg-name-home" value={t.home} aria-label={t.home} />
|
|
24
|
+
<div class="rg-team rg-team-home">
|
|
25
|
+
<input type="text" class="rg-name" id="rg-name-home" value={t.home} maxlength="40" aria-label={t.home} />
|
|
21
26
|
<div class="rg-score" id="rg-score-home" aria-live="polite">0</div>
|
|
22
27
|
<div class="rg-bonus" id="rg-bonus-home"></div>
|
|
23
28
|
</div>
|
|
@@ -25,8 +30,8 @@ const t = (ui ?? {}) as RugbyScoreKeeperUI;
|
|
|
25
30
|
<span class="rg-vs">VS</span>
|
|
26
31
|
<div class="rg-half-badge" id="rg-half-badge">{t.half1}</div>
|
|
27
32
|
</div>
|
|
28
|
-
<div class="rg-team">
|
|
29
|
-
<input type="text" class="rg-name" id="rg-name-away" value={t.away} aria-label={t.away} />
|
|
33
|
+
<div class="rg-team rg-team-away">
|
|
34
|
+
<input type="text" class="rg-name" id="rg-name-away" value={t.away} maxlength="40" aria-label={t.away} />
|
|
30
35
|
<div class="rg-score" id="rg-score-away" aria-live="polite">0</div>
|
|
31
36
|
<div class="rg-bonus" id="rg-bonus-away"></div>
|
|
32
37
|
</div>
|
|
@@ -34,23 +39,24 @@ const t = (ui ?? {}) as RugbyScoreKeeperUI;
|
|
|
34
39
|
|
|
35
40
|
<div class="rg-columns">
|
|
36
41
|
<div class="rg-col" data-team="home">
|
|
37
|
-
<div class="rg-col-label">{t.home}</div>
|
|
38
|
-
<button class="rg-btn rg-btn-try" data-action="try"
|
|
39
|
-
<button class="rg-btn rg-btn-conv" id="rg-conv-home" data-action="conv" disabled
|
|
40
|
-
<button class="rg-btn rg-btn-pen" data-action="pen"
|
|
41
|
-
<button class="rg-btn rg-btn-drop" data-action="drop"
|
|
42
|
+
<div class="rg-col-label" id="rg-col-label-home">{t.home}</div>
|
|
43
|
+
<button type="button" class="rg-btn rg-btn-try" data-action="try">{t.tryLabel}</button>
|
|
44
|
+
<button type="button" class="rg-btn rg-btn-conv" id="rg-conv-home" data-action="conv" disabled>{t.conversion}</button>
|
|
45
|
+
<button type="button" class="rg-btn rg-btn-pen" data-action="pen">{t.penalty}</button>
|
|
46
|
+
<button type="button" class="rg-btn rg-btn-drop" data-action="drop">{t.dropGoal}</button>
|
|
42
47
|
</div>
|
|
43
48
|
<div class="rg-col" data-team="away">
|
|
44
|
-
<div class="rg-col-label">{t.away}</div>
|
|
45
|
-
<button class="rg-btn rg-btn-try" data-action="try"
|
|
46
|
-
<button class="rg-btn rg-btn-conv" id="rg-conv-away" data-action="conv" disabled
|
|
47
|
-
<button class="rg-btn rg-btn-pen" data-action="pen"
|
|
48
|
-
<button class="rg-btn rg-btn-drop" data-action="drop"
|
|
49
|
+
<div class="rg-col-label" id="rg-col-label-away">{t.away}</div>
|
|
50
|
+
<button type="button" class="rg-btn rg-btn-try" data-action="try">{t.tryLabel}</button>
|
|
51
|
+
<button type="button" class="rg-btn rg-btn-conv" id="rg-conv-away" data-action="conv" disabled>{t.conversion}</button>
|
|
52
|
+
<button type="button" class="rg-btn rg-btn-pen" data-action="pen">{t.penalty}</button>
|
|
53
|
+
<button type="button" class="rg-btn rg-btn-drop" data-action="drop">{t.dropGoal}</button>
|
|
49
54
|
</div>
|
|
50
55
|
</div>
|
|
51
56
|
|
|
52
57
|
<div class="rg-mid-row">
|
|
53
58
|
<div class="rg-clock-panel">
|
|
59
|
+
<div class="rg-panel-header">{t.matchClock}</div>
|
|
54
60
|
<div class="rg-clock-ring">
|
|
55
61
|
<svg class="rg-clock-svg" viewBox="0 0 120 120">
|
|
56
62
|
<circle class="rg-clock-bg" cx="60" cy="60" r="54" />
|
|
@@ -61,17 +67,23 @@ const t = (ui ?? {}) as RugbyScoreKeeperUI;
|
|
|
61
67
|
<div class="rg-clock-label">{t.half} <span id="rg-half-label">1</span></div>
|
|
62
68
|
</div>
|
|
63
69
|
</div>
|
|
64
|
-
<button class="rg-btn rg-btn-clock" id="rg-btn-clock">{t.startMatch}</button>
|
|
70
|
+
<button type="button" class="rg-btn rg-btn-clock" id="rg-btn-clock">{t.startMatch}</button>
|
|
65
71
|
</div>
|
|
66
72
|
<div class="rg-sinbin-panel">
|
|
67
|
-
<div class="rg-panel-header">{t.
|
|
73
|
+
<div class="rg-panel-header">{t.sinBinTitle}</div>
|
|
68
74
|
<div class="rg-sinbin-form">
|
|
69
|
-
<input type="text" class="rg-input" id="rg-sinbin-input" placeholder={t.sinBinPlayer} aria-label={t.sinBinPlayer} />
|
|
70
|
-
<
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
<input type="text" class="rg-input" id="rg-sinbin-input" maxlength="80" placeholder={t.sinBinPlayer} aria-label={t.sinBinPlayer} />
|
|
76
|
+
<div class="rg-choice-row">
|
|
77
|
+
<div class="rg-choice-group" role="group" aria-label={t.teamLabel}>
|
|
78
|
+
<button type="button" class="rg-choice rg-choice-home is-active" data-sinbin-team="home" aria-pressed="true">{t.home}</button>
|
|
79
|
+
<button type="button" class="rg-choice rg-choice-away" data-sinbin-team="away" aria-pressed="false">{t.away}</button>
|
|
80
|
+
</div>
|
|
81
|
+
<div class="rg-choice-group" role="group" aria-label={t.durationLabel}>
|
|
82
|
+
<button type="button" class="rg-choice is-active" data-sinbin-duration="600" aria-pressed="true">10 {t.minutesShort}</button>
|
|
83
|
+
<button type="button" class="rg-choice" data-sinbin-duration="300" aria-pressed="false">5 {t.minutesShort}</button>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
<button type="button" class="rg-btn rg-btn-yellow" id="rg-btn-sinbin">{t.sinBinAdd}</button>
|
|
75
87
|
</div>
|
|
76
88
|
<div class="rg-sinbin-list" id="rg-sinbin-list">
|
|
77
89
|
<div class="rg-sinbin-empty">{t.sinBinEmpty}</div>
|
|
@@ -85,13 +97,13 @@ const t = (ui ?? {}) as RugbyScoreKeeperUI;
|
|
|
85
97
|
<div class="rg-history-list" id="rg-history-list">
|
|
86
98
|
<div class="rg-history-empty">{t.eventEmpty}</div>
|
|
87
99
|
</div>
|
|
88
|
-
<button class="rg-btn rg-btn-undo" id="rg-btn-undo" disabled>{t.undoBtn}</button>
|
|
100
|
+
<button type="button" class="rg-btn rg-btn-undo" id="rg-btn-undo" disabled>{t.undoBtn}</button>
|
|
89
101
|
</div>
|
|
90
102
|
<div class="rg-summary">
|
|
91
103
|
<div class="rg-panel-header">{t.scoringSummary}</div>
|
|
92
104
|
<table class="rg-table">
|
|
93
105
|
<thead>
|
|
94
|
-
<tr><th></th><th>{t.home}</th><th>{t.away}</th></tr>
|
|
106
|
+
<tr><th></th><th class="rg-th-home" id="rg-summary-home">{t.home}</th><th class="rg-th-away" id="rg-summary-away">{t.away}</th></tr>
|
|
95
107
|
</thead>
|
|
96
108
|
<tbody id="rg-summary-body">
|
|
97
109
|
<tr><td>{t.tryScored}</td><td>0</td><td>0</td></tr>
|
|
@@ -114,25 +126,25 @@ const t = (ui ?? {}) as RugbyScoreKeeperUI;
|
|
|
114
126
|
<div class="rg-banner-text" id="rg-banner-text"></div>
|
|
115
127
|
</div>
|
|
116
128
|
|
|
117
|
-
<div class="rg-reset-modal" id="rg-modal" role="dialog" aria-modal="true" aria-labelledby="rg-modal-title">
|
|
129
|
+
<div class="rg-reset-modal" id="rg-modal" role="dialog" aria-modal="true" aria-hidden="true" aria-labelledby="rg-modal-title">
|
|
118
130
|
<div class="rg-modal-content">
|
|
119
131
|
<h2 class="rg-modal-title" id="rg-modal-title">{t.resetMatch}</h2>
|
|
120
132
|
<div class="rg-modal-text">{t.resetConfirm}</div>
|
|
121
133
|
<div class="rg-modal-btns">
|
|
122
|
-
<button class="rg-btn rg-btn-modal-cancel" id="rg-modal-cancel">{t.cancel}</button>
|
|
123
|
-
<button class="rg-btn rg-btn-modal-confirm" id="rg-modal-confirm">{t.confirm}</button>
|
|
134
|
+
<button type="button" class="rg-btn rg-btn-modal-cancel" id="rg-modal-cancel">{t.cancel}</button>
|
|
135
|
+
<button type="button" class="rg-btn rg-btn-modal-confirm" id="rg-modal-confirm">{t.confirm}</button>
|
|
124
136
|
</div>
|
|
125
137
|
</div>
|
|
126
138
|
</div>
|
|
127
139
|
|
|
128
140
|
<div class="rg-actions">
|
|
129
|
-
<button class="rg-btn rg-btn-reset" id="rg-btn-reset">{t.resetMatch}</button>
|
|
141
|
+
<button type="button" class="rg-btn rg-btn-reset" id="rg-btn-reset">{t.resetMatch}</button>
|
|
130
142
|
</div>
|
|
131
143
|
|
|
132
144
|
</div>
|
|
133
145
|
</div>
|
|
134
146
|
|
|
135
147
|
<script>
|
|
136
|
-
import { initRugbyScorekeeper } from './ui-
|
|
148
|
+
import { initRugbyScorekeeper } from './ui-runtime';
|
|
137
149
|
initRugbyScorekeeper();
|
|
138
150
|
</script>
|
|
@@ -216,5 +216,9 @@ export const content: RugbyScoreKeeperLocaleContent = {
|
|
|
216
216
|
timeOn: 'Zeit Läuft',
|
|
217
217
|
fullTime: 'Spielende',
|
|
218
218
|
minutesShort: 'Min.',
|
|
219
|
+
savedLocally: 'Auf diesem Gerät gespeichert',
|
|
220
|
+
saveUnavailable: 'Speichern nicht verfügbar',
|
|
221
|
+
teamLabel: 'Team',
|
|
222
|
+
durationLabel: 'Dauer',
|
|
219
223
|
},
|
|
220
224
|
};
|
|
@@ -216,5 +216,9 @@ export const content: RugbyScoreKeeperLocaleContent = {
|
|
|
216
216
|
timeOn: 'Time On',
|
|
217
217
|
fullTime: 'Full time',
|
|
218
218
|
minutesShort: 'min',
|
|
219
|
+
savedLocally: 'Saved on this device',
|
|
220
|
+
saveUnavailable: 'Saving unavailable',
|
|
221
|
+
teamLabel: 'Team',
|
|
222
|
+
durationLabel: 'Duration',
|
|
219
223
|
},
|
|
220
224
|
};
|
|
@@ -216,5 +216,9 @@ export const content: RugbyScoreKeeperLocaleContent = {
|
|
|
216
216
|
timeOn: 'Tiempo en Marcha',
|
|
217
217
|
fullTime: 'Fin del partido',
|
|
218
218
|
minutesShort: 'min',
|
|
219
|
+
savedLocally: 'Guardado en este dispositivo',
|
|
220
|
+
saveUnavailable: 'No se puede guardar',
|
|
221
|
+
teamLabel: 'Equipo',
|
|
222
|
+
durationLabel: 'Duración',
|
|
219
223
|
},
|
|
220
224
|
};
|
|
@@ -216,5 +216,9 @@ export const content: RugbyScoreKeeperLocaleContent = {
|
|
|
216
216
|
timeOn: 'Temps en Marche',
|
|
217
217
|
fullTime: 'Fin du match',
|
|
218
218
|
minutesShort: 'min',
|
|
219
|
+
savedLocally: 'Enregistré sur cet appareil',
|
|
220
|
+
saveUnavailable: 'Enregistrement indisponible',
|
|
221
|
+
teamLabel: 'Équipe',
|
|
222
|
+
durationLabel: 'Durée',
|
|
219
223
|
},
|
|
220
224
|
};
|
|
@@ -216,5 +216,9 @@ export const content: RugbyScoreKeeperLocaleContent = {
|
|
|
216
216
|
timeOn: 'Waktu Berjalan',
|
|
217
217
|
fullTime: 'Pertandingan Selesai',
|
|
218
218
|
minutesShort: 'mnt',
|
|
219
|
+
savedLocally: 'Tersimpan di perangkat ini',
|
|
220
|
+
saveUnavailable: 'Penyimpanan tidak tersedia',
|
|
221
|
+
teamLabel: 'Tim',
|
|
222
|
+
durationLabel: 'Durasi',
|
|
219
223
|
},
|
|
220
224
|
};
|
|
@@ -216,5 +216,9 @@ export const content: RugbyScoreKeeperLocaleContent = {
|
|
|
216
216
|
timeOn: 'Tempo in Gioco',
|
|
217
217
|
fullTime: 'Fine partita',
|
|
218
218
|
minutesShort: 'min',
|
|
219
|
+
savedLocally: 'Salvato su questo dispositivo',
|
|
220
|
+
saveUnavailable: 'Salvataggio non disponibile',
|
|
221
|
+
teamLabel: 'Squadra',
|
|
222
|
+
durationLabel: 'Durata',
|
|
219
223
|
},
|
|
220
224
|
};
|
|
@@ -216,5 +216,9 @@ export const content: RugbyScoreKeeperLocaleContent = {
|
|
|
216
216
|
timeOn: 'Tijd Loopt',
|
|
217
217
|
fullTime: 'Einde wedstrijd',
|
|
218
218
|
minutesShort: 'min',
|
|
219
|
+
savedLocally: 'Op dit apparaat opgeslagen',
|
|
220
|
+
saveUnavailable: 'Opslaan niet beschikbaar',
|
|
221
|
+
teamLabel: 'Team',
|
|
222
|
+
durationLabel: 'Duur',
|
|
219
223
|
},
|
|
220
224
|
};
|
|
@@ -216,5 +216,9 @@ export const content: RugbyScoreKeeperLocaleContent = {
|
|
|
216
216
|
timeOn: 'Czas włączony',
|
|
217
217
|
fullTime: 'Koniec meczu',
|
|
218
218
|
minutesShort: 'min',
|
|
219
|
+
savedLocally: 'Zapisano na tym urządzeniu',
|
|
220
|
+
saveUnavailable: 'Zapisywanie niedostępne',
|
|
221
|
+
teamLabel: 'Drużyna',
|
|
222
|
+
durationLabel: 'Czas',
|
|
219
223
|
},
|
|
220
224
|
};
|
|
@@ -216,5 +216,9 @@ export const content: RugbyScoreKeeperLocaleContent = {
|
|
|
216
216
|
timeOn: 'Tempo em Jogo',
|
|
217
217
|
fullTime: 'Fim de jogo',
|
|
218
218
|
minutesShort: 'min',
|
|
219
|
+
savedLocally: 'Guardado neste dispositivo',
|
|
220
|
+
saveUnavailable: 'Não foi possível guardar',
|
|
221
|
+
teamLabel: 'Equipa',
|
|
222
|
+
durationLabel: 'Duração',
|
|
219
223
|
},
|
|
220
224
|
};
|
|
@@ -216,5 +216,9 @@ export const content: RugbyScoreKeeperLocaleContent = {
|
|
|
216
216
|
timeOn: 'Время вкл',
|
|
217
217
|
fullTime: 'Матч окончен',
|
|
218
218
|
minutesShort: 'мин',
|
|
219
|
+
savedLocally: 'Сохранено на этом устройстве',
|
|
220
|
+
saveUnavailable: 'Сохранение недоступно',
|
|
221
|
+
teamLabel: 'Команда',
|
|
222
|
+
durationLabel: 'Длительность',
|
|
219
223
|
},
|
|
220
224
|
};
|
|
@@ -216,5 +216,9 @@ export const content: RugbyScoreKeeperLocaleContent = {
|
|
|
216
216
|
timeOn: 'Tid igång',
|
|
217
217
|
fullTime: 'Matchen är slut',
|
|
218
218
|
minutesShort: 'min',
|
|
219
|
+
savedLocally: 'Sparat på den här enheten',
|
|
220
|
+
saveUnavailable: 'Det går inte att spara',
|
|
221
|
+
teamLabel: 'Lag',
|
|
222
|
+
durationLabel: 'Längd',
|
|
219
223
|
},
|
|
220
224
|
};
|
|
@@ -216,5 +216,9 @@ export const content: RugbyScoreKeeperLocaleContent = {
|
|
|
216
216
|
timeOn: 'Süre başladı',
|
|
217
217
|
fullTime: 'Maç bitti',
|
|
218
218
|
minutesShort: 'dk',
|
|
219
|
+
savedLocally: 'Bu cihazda kaydedildi',
|
|
220
|
+
saveUnavailable: 'Kaydetme kullanılamıyor',
|
|
221
|
+
teamLabel: 'Takım',
|
|
222
|
+
durationLabel: 'Süre',
|
|
219
223
|
},
|
|
220
224
|
};
|
|
@@ -70,9 +70,11 @@ describe('rugbyScoreKeeper game clock', () => {
|
|
|
70
70
|
});
|
|
71
71
|
|
|
72
72
|
it('undoes a sin-bin event and its timer together', () => {
|
|
73
|
-
const withSinBin = addSinBin(createInitialState(), ' Player <x> ', 600);
|
|
73
|
+
const withSinBin = addSinBin(createInitialState(), ' Player <x> ', 600, 'away');
|
|
74
74
|
expect(withSinBin.sinBin).toHaveLength(1);
|
|
75
75
|
expect(withSinBin.sinBin[0]?.player).toBe('Player <x>');
|
|
76
|
+
expect(withSinBin.sinBin[0]?.team).toBe('away');
|
|
77
|
+
expect(withSinBin.history[0]?.team).toBe('away');
|
|
76
78
|
const undone = undoLast(withSinBin);
|
|
77
79
|
expect(undone.sinBin).toEqual([]);
|
|
78
80
|
expect(undone.history).toEqual([]);
|
|
@@ -99,4 +101,13 @@ describe('rugbyScoreKeeper game clock', () => {
|
|
|
99
101
|
const homeTry = scoreTry(state, 'home');
|
|
100
102
|
expect(losingBonus(homeTry.home, homeTry.away, 'away')).toBe(true);
|
|
101
103
|
});
|
|
104
|
+
|
|
105
|
+
it('only reduces sin-bin time by clock time actually played before halftime', () => {
|
|
106
|
+
let state = addSinBin(createInitialState(), 'Number 4', 600, 'home');
|
|
107
|
+
state = startMatch({ ...state, elapsed: 2390 });
|
|
108
|
+
state = tickClock(state, 120);
|
|
109
|
+
expect(state.elapsed).toBe(2400);
|
|
110
|
+
expect(state.clockRunning).toBe(false);
|
|
111
|
+
expect(state.sinBin[0]?.remaining).toBe(590);
|
|
112
|
+
});
|
|
102
113
|
});
|
|
@@ -21,6 +21,7 @@ export interface TeamScore {
|
|
|
21
21
|
|
|
22
22
|
export interface SinBinEntry {
|
|
23
23
|
id: number;
|
|
24
|
+
team: TeamKey;
|
|
24
25
|
player: string;
|
|
25
26
|
remaining: number;
|
|
26
27
|
total: number;
|
|
@@ -105,13 +106,13 @@ export function scoreDropGoal(state: MatchState, team: TeamKey): MatchState {
|
|
|
105
106
|
return s;
|
|
106
107
|
}
|
|
107
108
|
|
|
108
|
-
export function addSinBin(state: MatchState, player: string, duration: number): MatchState {
|
|
109
|
+
export function addSinBin(state: MatchState, player: string, duration: number, team: TeamKey = 'home'): MatchState {
|
|
109
110
|
const cleanPlayer = player.trim();
|
|
110
111
|
if (state.matchEnded || !cleanPlayer || !Number.isFinite(duration) || duration <= 0) return state;
|
|
111
112
|
const s = structuredClone(state);
|
|
112
113
|
const id = Math.max(Date.now(), ...s.sinBin.map((entry) => entry.id + 1));
|
|
113
|
-
s.sinBin.push({ id, player: cleanPlayer, remaining: duration, total: duration });
|
|
114
|
-
pushEvent(s, 'sinbin',
|
|
114
|
+
s.sinBin.push({ id, team, player: cleanPlayer, remaining: duration, total: duration });
|
|
115
|
+
pushEvent(s, 'sinbin', team, { label: `${cleanPlayer} SIN BIN`, player: cleanPlayer, sinBinId: id });
|
|
115
116
|
return s;
|
|
116
117
|
}
|
|
117
118
|
|
|
@@ -127,17 +128,13 @@ export function tickSinBin(state: MatchState, delta: number): MatchState {
|
|
|
127
128
|
export function tickClock(state: MatchState, delta: number): MatchState {
|
|
128
129
|
const s = structuredClone(state);
|
|
129
130
|
if (!s.clockRunning || s.matchEnded || !Number.isFinite(delta) || delta <= 0) return s;
|
|
130
|
-
s.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
s.clockRunning = false;
|
|
138
|
-
s.matchEnded = true;
|
|
139
|
-
}
|
|
140
|
-
return tickSinBin(s, delta);
|
|
131
|
+
const limit = s.half === 1 ? 2400 : 4800;
|
|
132
|
+
const nextElapsed = Math.min(s.elapsed + delta, limit);
|
|
133
|
+
const appliedDelta = nextElapsed - s.elapsed;
|
|
134
|
+
s.elapsed = nextElapsed;
|
|
135
|
+
s.clockRunning = s.elapsed < limit;
|
|
136
|
+
s.matchEnded = s.elapsed === 4800;
|
|
137
|
+
return tickSinBin(s, appliedDelta);
|
|
141
138
|
}
|
|
142
139
|
|
|
143
140
|
export function startMatch(state: MatchState): MatchState {
|