X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=events.cpp;h=9826f0ecbbc487e8a23ec1932f4b4b52e0d66a39;hb=e89ed5aca344eac629ea515ade96fbde5ecab0c0;hp=e7db8f41353f3b226c22602afe13fe5cda8a8326;hpb=bc56d92a0711e307873093b369a172f89c68a816;p=pkanalytics diff --git a/events.cpp b/events.cpp index e7db8f4..9826f0e 100644 --- a/events.cpp +++ b/events.cpp @@ -24,7 +24,7 @@ QVariant EventsModel::headerData(int section, Qt::Orientation orientation, int r if (section == 0) { return "Time"; } else if (section == 1) { - return "Player"; + return "Who/what"; } else { return "Type"; } @@ -38,19 +38,50 @@ QVariant EventsModel::data(const QModelIndex &index, int role) const if (role != Qt::DisplayRole) { return QVariant(); } + const Event &e = events[index.row()]; if (index.column() == 0) { - return QString::fromUtf8(format_timestamp(events[index.row()].t)); + return QString::fromUtf8(format_timestamp(e.t)); } else if (index.column() == 1) { - optional player_id = events[index.row()].player_id; + optional player_id = e.player_id; + optional formation_id = e.formation_id; if (player_id) { auto p_it = players.find(*player_id); const Player &p = p_it->second; return QString::fromUtf8(p.name + " (" + p.number + ")"); + } else if (formation_id) { + auto f_it = formations.find(*formation_id); + const Formation &f = f_it->second; + return QString::fromUtf8(f.name); + } else if (e.type == "formation_offense" || e.type == "formation_defense") { + return "(None/unknown)"; } else { return QVariant(); } } else if (index.column() == 2) { - return QString::fromUtf8(events[index.row()].type); + string type = e.type; + type[0] = toupper(e.type[0]); + for (char &ch : type) { + if (ch == '_') { + ch = ' '; + } + } + + // Various fixups. + if (type == "Pull oob") { + type = "Pull OOB"; + } else if (type == "Formation defense") { + type = "Defensive formation"; + } else if (type == "Formation offense") { + type = "Offensive formation"; + } else if (type == "Set offense") { + type = "On offense"; + } else if (type == "Set defense") { + type = "On defense"; + } else if (type == "Catch") { + type = "Catch/take"; + } + + return QString::fromUtf8(type); } return QVariant(); } @@ -90,8 +121,34 @@ void EventsModel::load_data() abort(); } + // Read the formations. + ret = sqlite3_prepare_v2(db, "SELECT formation, name FROM formation", -1, &stmt, 0); + if (ret != SQLITE_OK) { + fprintf(stderr, "SELECT prepare: %s\n", sqlite3_errmsg(db)); + abort(); + } + for ( ;; ) { + ret = sqlite3_step(stmt); + if (ret == SQLITE_ROW) { + Formation f; + f.formation_id = sqlite3_column_int(stmt, 0); + f.name = (const char *) sqlite3_column_text(stmt, 1); + formations[f.formation_id] = std::move(f); + } else if (ret == SQLITE_DONE) { + break; + } else { + fprintf(stderr, "SELECT step: %s\n", sqlite3_errmsg(db)); + abort(); + } + } + ret = sqlite3_finalize(stmt); + if (ret != SQLITE_OK) { + fprintf(stderr, "SELECT finalize: %s\n", sqlite3_errmsg(db)); + abort(); + } + // Read the events. - ret = sqlite3_prepare_v2(db, "SELECT event, t, player, type FROM event WHERE match=? ORDER BY t", -1, &stmt, 0); + ret = sqlite3_prepare_v2(db, "SELECT event, t, player, formation, type FROM event WHERE match=? ORDER BY t", -1, &stmt, 0); if (ret != SQLITE_OK) { fprintf(stderr, "SELECT prepare: %s\n", sqlite3_errmsg(db)); abort(); @@ -106,7 +163,10 @@ void EventsModel::load_data() if (sqlite3_column_type(stmt, 2) == SQLITE_INTEGER) { // Non-NULL. e.player_id = sqlite3_column_int(stmt, 2); } - e.type = (const char *)sqlite3_column_text(stmt, 3); + if (sqlite3_column_type(stmt, 3) == SQLITE_INTEGER) { // Non-NULL. + e.formation_id = sqlite3_column_int(stmt, 3); + } + e.type = (const char *)sqlite3_column_text(stmt, 4); events.push_back(std::move(e)); } else if (ret == SQLITE_DONE) { break; @@ -122,7 +182,7 @@ void EventsModel::load_data() } } -unsigned EventsModel::insert_event(uint64_t t, optional player_id, const string &type) +unsigned EventsModel::insert_event(uint64_t t, optional player_id, optional formation_id, const string &type) { auto it = lower_bound(events.begin(), events.end(), t, [](const Event &e, uint64_t t) { return e.t < t; }); @@ -132,6 +192,7 @@ unsigned EventsModel::insert_event(uint64_t t, optional player_id, const st Event e; e.t = t; e.player_id = player_id; + e.formation_id = formation_id; e.type = type; events.insert(events.begin() + pos, e); @@ -139,7 +200,7 @@ unsigned EventsModel::insert_event(uint64_t t, optional player_id, const st // Insert the new row into the database. sqlite3_stmt *stmt; - int ret = sqlite3_prepare_v2(db, "INSERT INTO event (match, t, player, type) VALUES (?, ?, ?, ?)", -1, &stmt, 0); + int ret = sqlite3_prepare_v2(db, "INSERT INTO event (match, t, player, formation, type) VALUES (?, ?, ?, ?, ?)", -1, &stmt, 0); if (ret != SQLITE_OK) { fprintf(stderr, "INSERT prepare: %s\n", sqlite3_errmsg(db)); abort(); @@ -152,7 +213,12 @@ unsigned EventsModel::insert_event(uint64_t t, optional player_id, const st } else { sqlite3_bind_null(stmt, 3); } - sqlite3_bind_text(stmt, 4, type.data(), type.size(), SQLITE_STATIC); + if (formation_id) { + sqlite3_bind_int64(stmt, 4, *formation_id); + } else { + sqlite3_bind_null(stmt, 4); + } + sqlite3_bind_text(stmt, 5, type.data(), type.size(), SQLITE_STATIC); ret = sqlite3_step(stmt); if (ret == SQLITE_ROW) { @@ -248,6 +314,8 @@ EventsModel::Status EventsModel::get_status_at(uint64_t t) s.our_score = 0; s.their_score = 0; s.attack_state = Status::NOT_STARTED; + s.offensive_formation = 0; + s.defensive_formation = 0; s.stoppage = false; s.pull_state = Status::SHOULD_PULL; uint64_t last_gained_possession = 0; @@ -322,6 +390,20 @@ EventsModel::Status EventsModel::get_status_at(uint64_t t) last_stoppage = 0; } } + if (e.type == "formation_offense") { + if (e.formation_id) { + s.offensive_formation = *e.formation_id; + } else { + s.offensive_formation = 0; + } + } + if (e.type == "formation_defense") { + if (e.formation_id) { + s.defensive_formation = *e.formation_id; + } else { + s.defensive_formation = 0; + } + } } if (s.stoppage && last_stoppage != 0) { time_spent_in_stoppage += (t - last_stoppage); @@ -361,7 +443,7 @@ void EventsModel::set_team_at(uint64_t t, const set &new_team) if (e.t > t) { break; } - if (e.type == "goal" || e.type == "their_goal" || e.type == "stoppage" || e.type == "reset") { + if (e.type == "goal" || e.type == "their_goal" || e.type == "stoppage" || e.type == "reset" || e.type == "set_offense" || e.type == "set_defense") { backdate_point = e.t + 1; } if (e.player_id.has_value() && !new_team.count(*e.player_id)) { @@ -385,16 +467,44 @@ void EventsModel::set_team_at(uint64_t t, const set &new_team) set old_team = get_team_at(backdate_point); for (int player_id : old_team) { if (!new_team.count(player_id)) { - insert_event(backdate_point, player_id, "out"); + insert_event(backdate_point, player_id, nullopt, "out"); } } for (int player_id : new_team) { if (!old_team.count(player_id)) { - insert_event(backdate_point, player_id, "in"); + insert_event(backdate_point, player_id, nullopt, "in"); } } } +void EventsModel::set_formation_at(uint64_t t, bool offense, unsigned formation) +{ + // If there's another goal/stoppage no more than 20 seconds ago, + // we assume that the formation started at that point (it just took + // the operator a bit of time to see it). If not, we assume we + // changed in the middle of a point. + uint64_t backdate_point = 0; + for (const Event &e : events) { + if (e.t > t) { + break; + } + if (e.type == "goal" || e.type == "their_goal" || e.type == "stoppage" || e.type == "reset" || e.type == "set_offense" || e.type == "set_defense" || e.type == "in" || e.type == "out" || e.type == "pull" || e.type == "their_pull") { + backdate_point = e.t + 1; + } + if (e.type == "formation_offense" || e.type == "formation_defense") { + backdate_point = 0; + } + } + if (backdate_point != 0 && t - backdate_point < 20000) { + t = backdate_point; + } + if (offense) { + insert_event(t, nullopt, formation == 0 ? nullopt : optional{formation}, "formation_offense"); + } else { + insert_event(t, nullopt, formation == 0 ? nullopt : optional{formation}, "formation_defense"); + } +} + vector EventsModel::sort_team(const set &team) const { vector ret(team.begin(), team.end());