#include "k_cluster.hpp" #include #include "d_player.h" #include "doomdef.h" #include "doomstat.h" #include "g_game.h" #include "hu_stuff.h" #include "k_kart.h" #include "m_fixed.h" #include "r_main.h" static fixed_t K_PlayerDistance3D(player_t* source, player_t* destination) { if ((!source->mo) || (!destination->mo)) return INT32_MAX; // Return a garbage value. return K_Distance3D(source->mo->x, source->mo->y, source->mo->z, destination->mo->x, destination->mo->y, destination->mo->z); } extern "C" { player_t* closesttocluster; fixed_t K_Distance3D(fixed_t x1, fixed_t y1, fixed_t z1, fixed_t x2, fixed_t y2, fixed_t z2) { fixed_t dist_xy = R_PointToDist2(x1, y1, x2, y2); return R_PointToDist2(0, z1, dist_xy, z2); } UINT32 K_GetDTFDifference(player_t* source, player_t* destination) { if ((!source->mo) || (!destination->mo)) return INT16_MAX; // Return a garbage value. if (destination->distancetofinish > source->distancetofinish) return (destination->distancetofinish - source->distancetofinish); return (source->distancetofinish - destination->distancetofinish); } boolean K_ClusterFilter_NoFilter(player_t* player, UINT32 fval) { // Ignores literally every parameter and returns true. return true; } // Store our last memory of the cluster player (because we're clearing it out for the second // cluster). static boolean clusterplayer_memory[MAXPLAYERS]; // Filters players based on various factors, such as playercount and position. // fval = pingame boolean K_ClusterFilter_BaseFilter(player_t* player, UINT32 fval) { UINT8 pingame = static_cast(fval & 0xFF); if (player->position >= pingame) { return false; // Ignore last place. *They* need help the most! } if (pingame > 6) { // FIXME: Double-cluster algorithm. // Can't do it from within this function, or we're going to have too much overhead. // However: at above 18 players, we shouldn't even *consider* double-clustering. if (!K_IsPlayerLosing(player)) { return false; // Ignore winning players to prevent sandbagging. } } else if (pingame > 4) { if (player->position <= 1) { return false; // Ignore the frontrunner. } } // Nothing to filter. return true; } INT32 K_CountNeighboringPlayersByDistance(player_t* sourcePlayer, fixed_t eps, playerfilter_f* cluster_filter, UINT32 filterval, vector3_t* out, void* nodevec) { INT64 meanx, meany, meanz; INT32 N; N = 0; vector3_t neighborvector; std::vector clusternodes; if (!sourcePlayer->mo) return 0; meanx = meany = meanz = 0; INT32 i; UINT8 pingame = 0; // To hell with this; scan for ourselves and pre-emptively flag ourselves. for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i]) continue; if (&players[i] == sourcePlayer) { clusterplayer[i] = true; } if (players[i].spectator) continue; // spectator if (!players[i].mo) continue; pingame++; } for (i = 0; i < MAXPLAYERS; i++) { player_t* player; if (!playeringame[i]) continue; player = &players[i]; if (player == sourcePlayer) { // Ignore ourselves. continue; } if (clusterplayer[i]) continue; // Ignore players we've scanned already. if (player->spectator) continue; // spectator if (!player->mo) continue; if (cluster_filter) { if (!cluster_filter(player, filterval)) continue; } // Scan all points in the database fixed_t dist = K_PlayerDistance3D(sourcePlayer, player); /*CONS_Printf("%s: checking %f against epsilon value %f\n", player_names[i], FIXED_TO_FLOAT(dist), FIXED_TO_FLOAT(eps) );*/ if (dist <= eps) { clusterplayer[i] = true; // CONS_Printf("%s is in this cluster\n", player_names[i]); if (nodevec) static_cast*>(nodevec)->push_back(player); // Scan our neighbor for any players close to them. K_CountNeighboringPlayersByDistance( player, eps, cluster_filter, filterval, &neighborvector, nodevec); } } clusternodes = *static_cast*>(nodevec); N = clusternodes.size(); if (N != 0) { // Return the average center point of this cluster. for (i = 0; i < N; i++) { if (!clusternodes[i]) continue; if (!clusternodes[i]->mo) continue; meanx += clusternodes[i]->mo->x / FRACUNIT; meany += clusternodes[i]->mo->y / FRACUNIT; meanz += clusternodes[i]->mo->z / FRACUNIT; } meanx /= N; meany /= N; meanz /= N; // CONS_Printf("mean x: %lld, mean y: %lld, mean z: %lld\n", meanx, meany, meanz); out->x = (fixed_t)(meanx) << FRACBITS; out->y = (fixed_t)(meany) << FRACBITS; out->z = (fixed_t)(meanz) << FRACBITS; // Get the player closest to the cluster. fixed_t disttocluster, bestdist; bestdist = INT32_MAX; for (i = 0; i < N; i++) { if (!clusternodes[i]) continue; if (!clusternodes[i]->mo) continue; disttocluster = K_Distance3D(clusternodes[i]->mo->x, clusternodes[i]->mo->y, clusternodes[i]->mo->z, out->x, out->y, out->z); if (disttocluster < bestdist) { closesttocluster = clusternodes[i]; bestdist = disttocluster; } } } else { out->z = out->y = out->x = 0; } return N; } static std::vector dtf_vec; static boolean cleardtf = true; static UINT32 K_GetU32Diff(UINT32 a, UINT32 b) { return (b > a) ? (b - a) : (a - b); } INT32 K_CountNeighboringPlayersByDTFEx(player_t* sourcePlayer, fixed_t eps, playerfilter_f* cluster_filter, UINT32 filterval, vector3_t* out, void* nodevec) { INT64 mean; INT32 N = 0; vector3_t neighborvector; if (!nodevec) { // Nice goddamn try. return 0; } if (!sourcePlayer->mo) return 0; mean = 0; INT32 i; for (i = 0; i < MAXPLAYERS; i++) { player_t* player; if (!playeringame[i]) continue; player = &players[i]; if (player == sourcePlayer) continue; // Ignore ourselves. if (clusterplayer[i]) continue; // Ignore players we've scanned already. if (player->spectator) continue; // spectator if (!player->mo) continue; if (cluster_filter) { if (!cluster_filter(player, filterval)) continue; } // Scan all points in the database if ((fixed_t)K_GetDTFDifference(sourcePlayer, player) <= eps) { // CONS_Printf("%d is less than %d, adding to nodes vector\n", // K_GetDTFDifference(sourcePlayer, player), eps); clusterplayer[i] = true; if (nodevec) { // Add to the result. static_cast*>(nodevec)->push_back(player); } // Scan our neighbor for any players close to them. K_CountNeighboringPlayersByDTFEx( player, eps, cluster_filter, filterval, &neighborvector, nodevec); } } N = static_cast*>(nodevec)->size(); if (N != 0) { // Return the average center point of this cluster. for (i = 0; i < N; i++) { if (!static_cast*>(nodevec)->at(i)) continue; if (!static_cast*>(nodevec)->at(i)->mo) continue; mean += static_cast*>(nodevec)->at(i)->distancetofinish; } mean /= N; out->x = (fixed_t)(mean); out->y = 0; out->z = 0; // Get the player closest to the cluster. UINT32 disttocluster, bestdist; bestdist = UINT32_MAX; for (i = 0; i < N; i++) { disttocluster = K_GetU32Diff( static_cast*>(nodevec)->at(i)->distancetofinish, mean); if (disttocluster < bestdist) { closesttocluster = static_cast*>(nodevec)->at(i); bestdist = disttocluster; } } } else { out->z = out->y = out->x = 0; } return N; } INT32 K_CountNeighboringPlayersByDTF(player_t* sourcePlayer, fixed_t eps, playerfilter_f* cluster_filter, UINT32 filterval, vector3_t* out) { if (cleardtf) dtf_vec.clear(); return K_CountNeighboringPlayersByDTFEx( sourcePlayer, eps, cluster_filter, filterval, out, &dtf_vec); } INT32 K_CountNeighboringPlayers(player_t* sourcePlayer, fixed_t eps, playerfilter_f* cluster_filter, UINT32 filterval, vector3_t* out) { // Dummy vector so the game doesn't SIGSEGV. // Turns out, it's also necessary for this system not to be placebo! std::vector dummy = {0}; dummy.clear(); INT32 result = K_CountNeighboringPlayersByDistance( sourcePlayer, eps, cluster_filter, filterval, out, &dummy); return result; } }