#include "web_match.h" #include "web_scoreboard.h" #include "checkouts.h" #include "match.h" #include #include #include #include #include struct player_info { char *name, *rem, *sugg, *avg; }; struct player_info buffered_info[2], flushed_info[2]; void scoreboard_set_player_active(int pn) { char sel[64], *psel = sel; if (pn == -1) psel = NULL; else sprintf(sel, "#p%d-info", pn); EM_ASM({elemSetUniqClass($0, $1, $2)}, psel, "active", "[id$=-info]"); } static inline void buffer_str(char *str, char **buffer, char *flushed) { if (str == *buffer || (str && *buffer && !strcmp(str, *buffer))) return; if (*buffer && *buffer != flushed) free(*buffer); *buffer = str ? strdup(str) : NULL; } void scoreboard_set_player_name(int pn, char *str) { buffer_str(str, &buffered_info[pn-1].name, flushed_info[pn-1].name); } void scoreboard_set_player_rem(int pn, char *str) { buffer_str(str, &buffered_info[pn-1].rem, flushed_info[pn-1].rem); } void scoreboard_set_player_sugg(int pn, char *str) { buffer_str(str, &buffered_info[pn-1].sugg, flushed_info[pn-1].sugg); } void scoreboard_set_player_avg(int pn, double avg) { char *str = malloc(8); sprintf(str, "%.2f", avg); buffer_str(str, &buffered_info[pn-1].avg, flushed_info[pn-1].avg); } static inline bool buffered_str_changed(char *buffered, char *flushed) { return (!buffered != !flushed) || (buffered && flushed && strcmp(buffered, flushed)); } static inline void free_flushed_str(char *flushed, char *buffered) { if (flushed && flushed != buffered) free(flushed); } void scoreboard_flush_player_info(int pn) { char sel[64]; int len = sprintf(sel, "#p%d-", pn); if (buffered_str_changed(buffered_info[pn-1].name, flushed_info[pn-1].name)) { strcpy(sel + len, "name"); EM_ASM({elemSetContent($0, $1)}, sel, buffered_info[pn-1].name); } if (buffered_str_changed(buffered_info[pn-1].rem, flushed_info[pn-1].rem)) { strcpy(sel + len, "rem"); EM_ASM({elemSetContent($0, $1)}, sel, buffered_info[pn-1].rem); } if (buffered_str_changed(buffered_info[pn-1].sugg, flushed_info[pn-1].sugg)) { strcpy(sel + len, "sugg"); EM_ASM({elemSetContent($0, $1)}, sel, buffered_info[pn-1].sugg); } if (buffered_str_changed(buffered_info[pn-1].avg, flushed_info[pn-1].avg)) { strcpy(sel + len, "avg"); EM_ASM({elemSetContent($0, $1)}, sel, buffered_info[pn-1].avg); } free_flushed_str(flushed_info[pn-1].name, buffered_info[pn-1].name); free_flushed_str(flushed_info[pn-1].rem, buffered_info[pn-1].rem); free_flushed_str(flushed_info[pn-1].sugg, buffered_info[pn-1].sugg); free_flushed_str(flushed_info[pn-1].avg, buffered_info[pn-1].avg); flushed_info[pn-1] = buffered_info[pn-1]; } void scoreboard_flush() { scoreboard_flush_player_info(1); scoreboard_flush_player_info(2); } void update_player_name(int pn, char *str) { scoreboard_set_player_name(pn, str); } void update_player_sugg(int pn, int rem) { char str[15] = {0}, *p = str; if (rem > 1 && rem <= 170) { char *target; int i = 3, trem = rem; while (i && trem && (target = CHECKOUTS[--i][trem-1])) { p += sprintf(p, i == 2 ? "%s" : "-%s", target); trem -= segment_points(segment_from_name(target)); } } scoreboard_set_player_sugg(pn, str); } void update_player_avg(int pn, int n_darts) { struct leg *l = state->legs[pn-1]; scoreboard_set_player_avg(pn, l->n_visits ? (l->rem > 0 ? ((double)(l->start - l->rem) / l->n_visits) : ((double)l->start / (((l->n_visits - 1) * 3) + n_darts) * 3)) : 0); } void update_player_rem(int pn, int rem) { char str[5]; if (rem < 0 || rem == 1) strcpy(str, "BUST"); else sprintf(str, "%d", rem); scoreboard_set_player_rem(pn, str); if (pn == 1 || state->mode != M_PVC) update_player_sugg(pn, rem); } void show_player_info(int pn) { char sel[64]; sprintf(sel, "#p%d-info-inner", pn); EM_ASM({elemAddClass($0, $1)}, sel, "visible"); } void clear_player_info(int pn) { scoreboard_set_player_name(pn, NULL); scoreboard_set_player_rem(pn, NULL); scoreboard_set_player_sugg(pn, NULL); scoreboard_set_player_avg(pn, 0); } // FIXME move html/svg elem stuff to common file struct elem { char *ns, *name, *content; int n_attrs, size_attrs; char **attr_names, **attr_vals; }; struct elem *elem_init(char *ns, char *name, int size_attrs); void elem_free(struct elem *e); void elem_add_attr(struct elem *e, char *name, char *val); void elem_add_attr_double(struct elem *e, char *name, double val); void append_elemv(char *sel, int elemc, struct elem **elemv); struct elem *gen_div(char *str, char *class) { struct elem *e = elem_init(NULL, "div", 1); e->content = strdup(str); elem_add_attr(e, "class", class); return e; } void add_colour_class(char *buf, int pts) { if (pts >= 180) strcat(buf, " p180"); else if (pts >= 140) strcat(buf, " p140"); else if (pts >= 100) strcat(buf, " p100"); else if (pts >= 60) strcat(buf, " p60"); else if (pts >= 40) strcat(buf, " p40"); else if (pts >= 20) strcat(buf, " p20"); else strcat(buf, " p0"); } void draw_visits() { EM_ASM({elemSetContent($0, $1)}, "#visits", NULL); int n_visits = state->legs[0]->n_visits > state->legs[1]->n_visits ? state->legs[0]->n_visits : state->legs[1]->n_visits; struct elem **elemv = malloc(6 * n_visits * sizeof(*elemv)); int elemc = 0; for (int i = 0; i < n_visits; ++i) { char buf[32], buf2[32]; struct visit *v = state->legs[0]->visits + i; sprintf(buf, "%d", v->points); strcpy(buf2, "visit-col1"); add_colour_class(buf2, v->points); elemv[elemc++] = gen_div(buf, buf2); sprintf(buf, "%d", v->rem); elemv[elemc++] = gen_div(buf, "visit-col2"); sprintf(buf, "%d", i + 1); elemv[elemc++] = gen_div(buf, "visit-col3"); if (i < state->legs[1]->n_visits) { v = state->legs[1]->visits + i; sprintf(buf, "%d", v->rem); elemv[elemc++] = gen_div(buf, "visit-col4"); sprintf(buf, "%d", v->points); strcpy(buf2, "visit-col5"); add_colour_class(buf2, v->points); elemv[elemc++] = gen_div(buf, buf2); strcpy(buf, "… "); for (int j = 0; j < v->n_darts; ++j) { char *n = segment_name(v->darts[j]); sprintf(buf + strlen(buf), j == 0 ? "%4s" : " %4s", n); // FIXME free(n); } elemv[elemc++] = gen_div(buf, "visit-col6"); } else { elemv[elemc++] = gen_div("", "visit-col4"); elemv[elemc++] = gen_div("", "visit-col5"); elemv[elemc++] = gen_div("", "visit-col6"); } } append_elemv("#visits", elemc, elemv); EM_ASM({elemScrollToBottom($0)}, "#visits"); }