blankart/src/k_cluster.cpp
Anonimus 7c31ca0483 More changes to Alt Invincibility
Bottlenecker has been heavily buffed (8-tic deficit to invince timer at full strength)
The cluster now specifically only tracks losing players, and in the case that no new cluster point is found, instead reads from the last cluster player
Invincibility "hogs the item box" like Grow does, and (currently) can't be cancelled to prevent chaining
Maximum time limit has been buffed to 35 seconds
2025-06-29 01:30:17 -04:00

263 lines
No EOL
6.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;
std::vector<player_t *> *realvec;
if (nodevec != nullptr)
realvec = static_cast<std::vector<player_t *> *>(nodevec);
if (!sourcePlayer->mo)
return 0;
meanx = meany = meanz = 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 (K_PlayerDistance3D(sourcePlayer, player) <= eps)
{
clusterplayer[i] = true;
clusternodes.push_back(player);
if (realvec)
realvec->push_back(player);
// Scan our neighbor for any players close to them.
K_CountNeighboringPlayersByDistance(player, eps, &neighborvector, &clusternodes);
}
}
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++)
{
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.
// There's probably a better solution to this.
std::vector<player_t *> dummy = {0};
return K_CountNeighboringPlayersByDistance(sourcePlayer, eps, out, &dummy);
}
}