blankart/src/k_cluster.cpp
2025-10-27 19:59:17 -04:00

304 lines
No EOL
7.4 KiB
C++

#include "doomdef.h"
#include "doomstat.h"
#include "hu_stuff.h"
#include "d_player.h"
#include "m_fixed.h"
#include "k_kart.h"
#include "r_main.h"
#include "g_game.h"
#include "k_cluster.hpp"
#include <vector>
static 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);
}
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);
}
static 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);
}
extern "C" {
player_t *closesttocluster;
INT32 K_CountNeighboringPlayersByDistance(player_t *sourcePlayer, fixed_t eps, vector3_t *out, void *nodevec)
{
INT64 meanx, meany, meanz;
INT32 N;
N = 0;
vector3_t neighborvector;
std::vector<player_t *>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 (!K_IsPlayerLosing(player))
continue; // Ignore winning players to prevent sandbagging.
if (player->position >= pingame)
continue; // Ignore last place. *They* need help the most!
// 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<std::vector<player_t *> *>(nodevec)->push_back(player);
// Scan our neighbor for any players close to them.
K_CountNeighboringPlayersByDistance(player, eps, &neighborvector, nodevec);
}
}
clusternodes = *static_cast<std::vector<player_t *> *>(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<player_t *> dtf_vec;
static boolean cleardtf = true;
static UINT32 K_GetU32Diff(UINT32 a, UINT32 b)
{
return (b > a) ? (b - a) : (a - b);
}
INT32 K_CountNeighboringPlayersByDTF(player_t *sourcePlayer, fixed_t eps, vector3_t *out)
{
INT64 mean;
INT32 N = 0;
vector3_t neighborvector;
if (!sourcePlayer->mo)
return 0;
if (cleardtf)
dtf_vec.clear();
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 (!K_IsPlayerLosing(player))
continue; // Ignore winning players to prevent sandbagging.
// 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;
dtf_vec.push_back(player); // Add to result
cleardtf = false;
// Scan our neighbor for any players close to them.
K_CountNeighboringPlayersByDTF(player, eps, &neighborvector);
cleardtf = true;
}
}
N = dtf_vec.size();
if (N != 0)
{
// Return the average center point of this cluster.
for (i = 0; i < N; i++)
{
if (!dtf_vec[i])
continue;
if (!dtf_vec[i]->mo)
continue;
mean += dtf_vec[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(dtf_vec[i]->distancetofinish, mean);
if (disttocluster < bestdist)
{
closesttocluster = dtf_vec[i];
bestdist = disttocluster;
}
}
}
else
{
out->z = out->y = out->x = 0;
}
return N;
}
INT32 K_CountNeighboringPlayers(player_t *sourcePlayer, fixed_t eps, 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<player_t *> dummy = {0};
dummy.clear();
INT32 result = K_CountNeighboringPlayersByDistance(sourcePlayer, eps, out, &dummy);
return result;
}
}