397 lines
No EOL
12 KiB
C++
397 lines
No EOL
12 KiB
C++
#include "k_cluster.hpp"
|
|
|
|
#include <vector>
|
|
|
|
#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<UINT8>(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<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 (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<std::vector<player_t*>*>(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<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_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<std::vector<player_t*>*>(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<std::vector<player_t*>*>(nodevec)->size();
|
|
|
|
if (N != 0)
|
|
{
|
|
// Return the average center point of this cluster.
|
|
for (i = 0; i < N; i++)
|
|
{
|
|
if (!static_cast<std::vector<player_t*>*>(nodevec)->at(i))
|
|
continue;
|
|
|
|
if (!static_cast<std::vector<player_t*>*>(nodevec)->at(i)->mo)
|
|
continue;
|
|
|
|
mean += static_cast<std::vector<player_t*>*>(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<std::vector<player_t*>*>(nodevec)->at(i)->distancetofinish, mean);
|
|
|
|
if (disttocluster < bestdist)
|
|
{
|
|
closesttocluster = static_cast<std::vector<player_t*>*>(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<player_t*> dummy = {0};
|
|
|
|
dummy.clear();
|
|
INT32 result = K_CountNeighboringPlayersByDistance(
|
|
sourcePlayer, eps, cluster_filter, filterval, out, &dummy);
|
|
|
|
return result;
|
|
}
|
|
} |