#include "web_control.h" #include "web_dom.h" #include "web_match.h" #include "web_opts.h" #include "web_prompt.h" #include "web_scoreboard.h" #include "web_svg.h" #include "comp.h" #include #include #include #include #include void set_active_player(int pn) { state->active_player = pn; scoreboard_set_player_active(pn); if (match_player_is_comp(pn)) prompt_bot_visit(); else prompt_visit(); } void update_user_rem_from_pts(int pts) { update_player_rem(state->active_player, state_active_leg()->rem - pts); scoreboard_flush_player_info(state->active_player); } EMSCRIPTEN_KEEPALIVE void end_boat_visit(int rem, double avg, int match_id) { if (!state || match_id != state->id) return; svg_clear_points(); update_player_rem(state->active_player, rem); scoreboard_set_player_avg(state->active_player, avg); prompt_set_input(NULL); prompt_set_msgr(NULL); draw_visits(); handle_next(); } EMSCRIPTEN_KEEPALIVE void draw_boat_throwing(int pts, char *str, double x, double y, int match_id) { if (!state || match_id != state->id) return; char pts_str[10]; sprintf(pts_str, "%d", pts); struct leg *l = state_active_leg(); int rem = l->n_visits > 1 ? l->visits[l->n_visits-2].rem : l->start; svg_draw_point(x, y); update_player_rem(state->active_player, rem - pts); prompt_set_input(pts_str); prompt_set_msgr(str); free(str); prompt_flush(); scoreboard_flush_player_info(state->active_player); } static void schedule_boat_visit_draws(struct leg *l, struct visit *v, double avg) { int pts = 0; char str[15] = {0}; size_t len_str = 0; for (int i = 0; i < v->n_darts; ++i) { pts += segment_points(v->darts[i]); char *seg_name = segment_name(v->darts[i]); len_str += sprintf(str + len_str, i ? "-%s" : "%s", seg_name); free(seg_name); struct ccoords c = v->ccoords[i]; int delay = delay_ms * (i + 1); if (delay_ms >= SVG_THROW_ANIM_MS) delay -= SVG_THROW_ANIM_MS; char *tmp = malloc(len_str + 1); // free in draw_boat_throwing memcpy(tmp, str, len_str + 1); EM_ASM({scheduleCCall($0, $1, $2, $3, $4, $5, $6)}, "draw_boat_throwing", delay, pts, tmp, c.x, c.y, state->id); } EM_ASM({scheduleCCall($0, $1, $2, $3, $4)}, "end_boat_visit", delay_ms * (v->n_darts + 1), l->rem, avg, state->id); } void boat_visit() { struct leg *l = state_active_leg(); if (state->comp_undone[state->active_player - 1]) { --state->comp_undone[state->active_player - 1]; l->rem = l->visits[l->n_visits++].rem; } else { comp_visit(l); } struct visit *v = l->visits + l->n_visits - 1; double avg = v->rem > 0 ? (double)(l->start - l->rem) / l->n_visits : (double)l->start / (((l->n_visits - 1) * 3) + v->n_darts) * 3; if (delay_ms) schedule_boat_visit_draws(l, v, avg); else end_boat_visit(l->rem, avg, state->id); } void handle_next() { if (!state) { prompt_select_mode(); } else if (match_is_over()) { if (state->num_darts || match_player_is_comp(match_winning_player())) prompt_end_match(); else prompt_num_darts(); } else { if (state->active_player) set_active_player(match_next_player()); else set_active_player(match_opts->throws_first); if (match_player_is_comp(state->active_player)) boat_visit(); } prompt_flush(); scoreboard_flush(); } void user_visit(int points) { if (!is_points_valid(points, state_active_leg()->rem)) { oi(); return; } struct leg *l = state_active_leg(); if (l->n_visits == l->size_visits) leg_grow_visits(l); struct visit *v = l->visits + l->n_visits++; v->points = points; l->rem -= points; v->rem = l->rem; update_player_rem(state->active_player, l->rem); if (v->rem > 0) update_player_avg(state->active_player, 0); draw_visits(); handle_next(); } void user_visit_to_rem(int rem) { user_visit(state_active_leg()->rem - rem); } static void undo_active() { struct leg *l = state_active_leg(); if (match_player_is_comp(state->active_player)) { l->rem += l->visits[--l->n_visits].points; ++state->comp_undone[state->active_player - 1]; } else { struct visit *v = l->visits + --l->n_visits; l->rem += v->points; memset(v, 0, sizeof(*v)); } update_player_avg(state->active_player, 0); update_player_rem(state->active_player, l->rem); state->active_player = match_prev_throw_player(); } void user_undo() { if (!match_first_user_has_thrown()) { oi(); return; } if (state->num_darts) { state->num_darts = 0; struct leg *l = state_active_leg(); scoreboard_set_player_avg(state->active_player, ((double)(l->start - l->visits[l->n_visits-2].rem) / (l->n_visits - 1))); scoreboard_set_player_active(state->active_player); handle_next(); return; } state->active_player = match_prev_throw_player(); while (match_player_is_comp(state->active_player)) undo_active(); undo_active(); draw_visits(); handle_next(); } void user_num_darts(int n) { if (n < 1 || n > 3) { oi(); return; } state->num_darts = n; update_player_avg(state->active_player, n); handle_next(); } void match_mode_selected(int mode) { if (mode < M_FIRST || mode > M_LAST) { oi(); return; } if (match_opts) match_opts_free(); match_opts = calloc(1, sizeof(*match_opts)); match_opts->mode = mode; match_opts->start_pts = 501; match_opts->throws_first = 1; // names need to be freed if we stop using string literals if (mode == M_PVC) { match_opts->p1_name = "User"; match_opts->p2_name = "Computer"; match_opts->p2_type = PT_COMP; } else if (mode == M_P) { match_opts->p1_name = "Player 1"; match_opts->p2_name = NULL; } else if (mode == M_PVP) { match_opts->p1_name = "Player 1"; match_opts->p2_name = "Player 2"; } else if (mode == M_CVC) { match_opts->p1_name = "Computer 1"; match_opts->p1_type = PT_COMP; match_opts->p2_name = "Computer 2"; match_opts->p2_type = PT_COMP; } prompt_match_opts(); prompt_flush(); } void start_match() { match_new(); match_add_player(match_opts->start_pts, match_opts->p1_type, match_opts->p1_name); match_add_player(match_opts->start_pts, match_opts->p2_type, match_opts->p2_name ? match_opts->p2_name : "oi"); show_player_info(1); update_player_name(1, state->legs[0]->name); update_player_rem(1, state->legs[0]->rem); update_player_avg(1, 0); if (match_num_players() != 1) { show_player_info(2); update_player_name(2, state->legs[1]->name); update_player_rem(2, state->legs[1]->rem); update_player_avg(2, 0); } dom_enable_exit_dialogue(true); dom_add_class("#key-exit", "visible"); draw_visits(); handle_next(); } void end_match() { // clean up in case match was ended early svg_clear_points(); prompt_set_input(NULL); scoreboard_set_player_active(-1); dom_remove_class("#key-exit", "visible"); dom_enable_exit_dialogue(false); if (state) free_state(); handle_next(); }