diff options
-rw-r--r-- | web/static/dartboat.js | 4 | ||||
-rw-r--r-- | web/static/style.css | 83 | ||||
-rw-r--r-- | web/web_control.c | 8 | ||||
-rw-r--r-- | web/web_dom.c | 5 | ||||
-rw-r--r-- | web/web_dom.h | 2 | ||||
-rw-r--r-- | web/web_match.h | 3 | ||||
-rw-r--r-- | web/web_prompt.c | 1 | ||||
-rw-r--r-- | web/web_scoreboard.c | 105 |
8 files changed, 177 insertions, 34 deletions
diff --git a/web/static/dartboat.js b/web/static/dartboat.js index 5c7f011..ecbee74 100644 --- a/web/static/dartboat.js +++ b/web/static/dartboat.js @@ -50,6 +50,10 @@ function elemSetUniqClass(sel, c, sel_set) { sel && e.matches(selstr) ? 'add' : 'remove'](cstr)); } +function rootSetStylePropertyInt(prop, val) { + document.documentElement.style.setProperty(UTF8ToString(prop), val); +} + function elemScrollToBottom(sel) { const e = $(UTF8ToString(sel)); e.scrollTop = e.scrollHeight; diff --git a/web/static/style.css b/web/static/style.css index 49daa96..f0209f8 100644 --- a/web/static/style.css +++ b/web/static/style.css @@ -46,6 +46,10 @@ src: url('fonts/Inter-num.woff2') format('woff2'); } +:root { + --num-players: 0; +} + html { font-size: clamp(1.5vh, 2.5vw, 2vh); @@ -601,15 +605,16 @@ body { justify-content: center; } -#visits .visit-p1-name { grid-column: 2 / span 2; } -#visits .visit-p2-name { grid-column: 5 / span 2; } -#visits .visit-p1-darts { grid-column: 1; } -#visits .visit-p1-pts { grid-column: 2; } -#visits .visit-p1-rem { grid-column: 3; } -#visits .visit-n { grid-column: 4; } -#visits .visit-p2-rem { grid-column: 5; } -#visits .visit-p2-pts { grid-column: 6; } -#visits .visit-p2-darts { grid-column: 7; } +#visits .visit-name { grid-column: span 2; } +#visits:not(.dense) .visit-p1-name { grid-column: 2 / span 2; } +#visits:not(.dense) .visit-p2-name { grid-column: 5 / span 2; } +#visits:not(.dense) .visit-p1-darts { grid-column: 1; } +#visits:not(.dense) .visit-p1-pts { grid-column: 2; } +#visits:not(.dense) .visit-p1-rem { grid-column: 3; } +#visits:not(.dense) .visit-n { grid-column: 4; } +#visits:not(.dense) .visit-p2-rem { grid-column: 5; } +#visits:not(.dense) .visit-p2-pts { grid-column: 6; } +#visits:not(.dense) .visit-p2-darts { grid-column: 7; } #visits .p0 { color: #e00018; } #visits .p20 { color: #e06000; } @@ -619,35 +624,45 @@ body { #visits .p140 { color: #20e018; } #visits .p180 { color: #20e018; font-weight: bold; } #visits .throws-first { color: #999; } -#visits .throws-first:before { content: '\f151'; font-size: 0.6em; } +#visits .throws-first:before { content: '\f151'; font-size: 0.6em; + font-family: 'bootstrap-icons-sub'; + vertical-align: -0.125em; +} -#visits .visit-p1-name, #visits .visit-p2-name { +#visits .visit-name { + margin: 0 0.2rem; +} +#visits .visit-name, #visits .visit-p1-name, #visits .visit-p2-name { color: #555; border-bottom: 2px solid #555; } #visits .visit-n { color: #555; } -#visits .visit-p1-darts, #visits .visit-p2-darts { +#visits .visit-dense-darts, .visit-p1-darts, #visits .visit-p2-darts { color: #555; font-size: 0.75em; white-space: nowrap; } -#visits .visit-p1-darts { +#visits .visit-dense-darts { + margin-top: calc(1.5 * -0.2em / 0.75); + grid-column: span 2; +} +#visits:not(.dense) .visit-p1-darts { justify-content: right; } -#visits .visit-p1-darts:not(:empty):after { +#visits:not(.dense) .visit-p1-darts:not(:empty):after { content: '\2003'; } -#visits .visit-p2-darts { +#visits:not(.dense) .visit-p2-darts { justify-content: left; } -#visits .visit-p2-darts:not(:empty):before { +#visits:not(.dense) .visit-p2-darts:not(:empty):before { content: '\2003'; } @media (max-aspect-ratio: 8/5) { - #visits { + #visits:not(.dense) { grid-template-columns: min-content calc(calc(100% - 27ch) / 2) @@ -655,27 +670,35 @@ body { calc(calc(100% - 27ch) / 2) min-content; } - #visits .visit-p1-name, #visits .visit-p2-name { + #visits:not(.dense) .visit-p1-name, #visits .visit-p2-name { display: none; } - #visits .visit-p1-spacer { grid-column: 1; width: 1ch } - #visits .visit-p2-spacer { grid-column: 9; width: 1ch } - #visits .visit-p1-darts { grid-column: 1; } - #visits .visit-p1-pts { grid-column: 3; } - #visits .visit-p1-rem { grid-column: 4; } - #visits .visit-n { grid-column: 5; } - #visits .visit-p2-rem { grid-column: 6; } - #visits .visit-p2-pts { grid-column: 7; } - #visits .visit-p2-darts { grid-column: 9; } - - #visits .visit-p1-darts:not(:empty):after { + #visits:not(.dense) .visit-p1-spacer { grid-column: 1; width: 1ch } + #visits:not(.dense) .visit-p2-spacer { grid-column: 9; width: 1ch } + #visits:not(.dense) .visit-p1-darts { grid-column: 1; } + #visits:not(.dense) .visit-p1-pts { grid-column: 3; } + #visits:not(.dense) .visit-p1-rem { grid-column: 4; } + #visits:not(.dense) .visit-n { grid-column: 5; } + #visits:not(.dense) .visit-p2-rem { grid-column: 6; } + #visits:not(.dense) .visit-p2-pts { grid-column: 7; } + #visits:not(.dense) .visit-p2-darts { grid-column: 9; } + + #visits:not(.dense) .visit-p1-darts:not(:empty):after { content: '\2003…'; } - #visits .visit-p2-darts:not(:empty):before { + #visits:not(.dense) .visit-p2-darts:not(:empty):before { content: '…\2003'; } } +#visits.dense { + grid-template-columns: min-content repeat(var(--num-players), min-content min-content); +} + +#visits.dense > * { + min-width: 5ch; +} + /* modals */ .modal { diff --git a/web/web_control.c b/web/web_control.c index b0a777c..ca45ed4 100644 --- a/web/web_control.c +++ b/web/web_control.c @@ -263,6 +263,10 @@ void match_mode_selected(int mode) } else if (mode == M_PVPVP) { match_opts->p1_name = "Player 1"; match_opts->p2_name = "Player 2"; + } else if (mode == M_PVCVCVC) { + match_opts->p1_name = "User"; + match_opts->p2_type = PT_COMP; + match_opts->p2_name = "Computer 1"; } prompt_match_opts(); @@ -278,6 +282,10 @@ void start_match() match_opts->p2_name ? match_opts->p2_name : "oi"); if (match_opts->mode == M_PVPVP) match_add_player(match_opts->start_pts, PT_USER, "Player 3"); + else if (match_opts->mode == M_PVCVCVC) { + match_add_player(match_opts->start_pts, PT_COMP, "Computer 2"); + match_add_player(match_opts->start_pts, PT_COMP, "Computer 3"); + } scoreboard_show_info(match_num_players()); for (int i = 1; i <= match_num_players(); ++i) { diff --git a/web/web_dom.c b/web/web_dom.c index 162684a..54a6c8d 100644 --- a/web/web_dom.c +++ b/web/web_dom.c @@ -144,6 +144,11 @@ inline void dom_set_uniq_class(char *sel, char *class, char *sel_set) EM_ASM({elemSetUniqClass($0, $1, $2)}, sel, class, sel_set); } +inline void dom_set_style_property_int(char *prop, int val) +{ + EM_ASM({rootSetStylePropertyInt($0, $1)}, prop, val); +} + inline void dom_scroll_to_bottom(char *sel) { EM_ASM({elemScrollToBottom($0)}, sel); diff --git a/web/web_dom.h b/web/web_dom.h index 8813d1a..45afd15 100644 --- a/web/web_dom.h +++ b/web/web_dom.h @@ -30,6 +30,8 @@ void dom_remove_class(char *sel, char *class); void dom_toggle_class(char *sel, char *class); void dom_set_uniq_class(char *sel, char *class, char *sel_set); +void dom_set_style_property_int(char *prop, int val); + void dom_scroll_to_bottom(char *sel); void dom_scroll_to_center_child(char *sel, char *sel_child); diff --git a/web/web_match.h b/web/web_match.h index 0654eb7..fc2dabe 100644 --- a/web/web_match.h +++ b/web/web_match.h @@ -10,7 +10,8 @@ enum match_mode { M_PVP, M_CVC, M_PVPVP, - M_LAST = M_PVPVP + M_PVCVCVC, + M_LAST = M_PVCVCVC }; enum player_type { diff --git a/web/web_prompt.c b/web/web_prompt.c index f7bfe8d..f65259e 100644 --- a/web/web_prompt.c +++ b/web/web_prompt.c @@ -198,6 +198,7 @@ void prompt_select_mode() add_list_opt("Two-player scoreboard"); add_list_opt("Computer vs computer"); add_list_opt("Three-player scoreboard"); + add_list_opt("Play against three computers"); flush_list_opts(); } diff --git a/web/web_scoreboard.c b/web/web_scoreboard.c index 3543d0f..f298dfe 100644 --- a/web/web_scoreboard.c +++ b/web/web_scoreboard.c @@ -279,9 +279,108 @@ static void buf_darts(char *buf, size_t size, struct visit *v) } } +void draw_visits_dense() +{ + dom_set_content("#visits", NULL); + dom_add_class("#visits", "dense"); + + int np = match_num_players(); + dom_set_style_property_int("--num-players", np); + + // FIXME + int n_visits = state->legs[0]->n_visits > state->legs[1]->n_visits ? + state->legs[0]->n_visits : state->legs[1]->n_visits; + + struct dom_elem **elemv = malloc( + (np * 2 + 1) * (2 * n_visits + 2) * sizeof(*elemv)); + int elemc = 0; + + elemv[elemc++] = create_div("", ""); + for (int i = 0; i < np; ++i) { + elemv[elemc++] = create_div(state->legs[i]->name, + "visit-name"); + } + + char buf[32], buf2[32]; + elemv[elemc++] = create_div("0", "visit-n"); + for (int i = 0; i < np; ++i) { + if (i + 1 == match_opts->throws_first) + elemv[elemc++] = create_div("", "throws-first"); + else + elemv[elemc++] = create_div("", ""); + snprintf(buf, sizeof(buf), "%d", state->legs[i]->start); + elemv[elemc++] = create_div(buf, ""); + } + + for (int i = 0; i < n_visits; ++i) { + snprintf(buf, sizeof(buf), "%d", i + 1); + elemv[elemc++] = create_div(buf, "visit-n"); + + for (int j = 0; j < np; ++j) { + if (i >= state->legs[j]->n_visits) { + elemv[elemc++] = create_div("", ""); + elemv[elemc++] = create_div("", ""); + continue; + } + + struct visit *v = state->legs[j]->visits + i; + + snprintf(buf, sizeof(buf), "%d", v->points); + snprintf(buf2, sizeof(buf2), "%s", + points_class(v->points)); + elemv[elemc++] = create_div(buf, buf2); + + snprintf(buf, sizeof(buf), "%d", v->rem); + elemv[elemc++] = create_div(buf, ""); + } + + bool n_darts = false; + for (int j = 0; j < np; ++j) { + if (i < state->legs[j]->n_visits && + state->legs[j]->visits[i].n_darts) + n_darts = true; + } + if (!n_darts) continue; + + elemv[elemc++] = create_div("", ""); + for (int j = 0; j < np; ++j) { + if (i >= state->legs[j]->n_visits) { + elemv[elemc++] = create_div("", + "visit-dense-darts"); + continue; + } + + struct visit *v = state->legs[j]->visits + i; + + if (v->n_darts) { + buf_darts(buf, sizeof(buf), v); + elemv[elemc++] = create_div(buf, + "visit-dense-darts"); + } else { + elemv[elemc++] = create_div("", + "visit-dense-darts"); + } + } + } + + dom_append_elemv("#visits", elemc, elemv); + for (int i = 0; i < elemc; ++i) + dom_elem_free(elemv[i]); + free(elemv); + + dom_scroll_to_bottom("#visits"); + dom_scroll_to_center_child("#visits", ".visit-n"); +} + void draw_visits() { + if (match_num_players() > 2) { + draw_visits_dense(); + return; + } + dom_set_content("#visits", NULL); + dom_remove_class("#visits", "dense"); int n_visits = state->legs[0]->n_visits > state->legs[1]->n_visits ? state->legs[0]->n_visits : state->legs[1]->n_visits; @@ -300,7 +399,7 @@ void draw_visits() snprintf(buf, sizeof(buf), "%d", state->legs[0]->start); if (match_opts->throws_first == 1) elemv[elemc++] = create_div("", // content added via CSS - "visit-p1-pts throws-first icon"); + "visit-p1-pts throws-first"); elemv[elemc++] = create_div(buf, "visit-p1-rem"); elemv[elemc++] = create_div("0", "visit-n"); if (match_num_players() != 1) { @@ -308,7 +407,7 @@ void draw_visits() elemv[elemc++] = create_div(buf, "visit-p2-rem"); if (match_opts->throws_first == 2) elemv[elemc++] = create_div("", - "visit-p2-pts throws-first icon"); + "visit-p2-pts throws-first"); } for (int i = 0; i < n_visits; ++i) { @@ -331,10 +430,10 @@ void draw_visits() snprintf(buf, sizeof(buf), "%d", v->rem); elemv[elemc++] = create_div(buf, "visit-p1-rem"); +p2: snprintf(buf, sizeof(buf), "%d", i + 1); elemv[elemc++] = create_div(buf, "visit-n"); -p2: if (i >= state->legs[1]->n_visits) continue; v = state->legs[1]->visits + i; |