/*
=======================================================================
SelectSpawnPoint
=======================================================================
*/
/*
================
PlayersRangeFromSpot
Returns the distance to the nearest player from the given spot
================
*/
float PlayersRangeFromSpot (edict_t *spot)
{
edict_t *player;
float bestplayerdistance;
vec3_t v;
int n;
float playerdistance;
bestplayerdistance = 9999999;
for (n = 1; n <= maxclients->value; n++)
{
player = &g_edicts[n];
if (!player->inuse)
continue;
if (player->health <= 0)
continue;
VectorSubtract (spot->s.origin, player->s.origin, v);
playerdistance = VectorLength (v);
if (playerdistance < bestplayerdistance)
bestplayerdistance = playerdistance;
}
return bestplayerdistance;
}
/*
================
SelectNextDeathmatchSpawnPoint
go to next deathmatch spawn point
================
* 1.803 - unused
edict_t *SelectNextDeathmatchSpawnPoint (void)
{
static edict_t *spot;
spot = NULL;
while ((spot = G_Find(spot, FOFS(classname), "info_player_deathmatch")) != NULL)
if (spot == NULL)
return NULL;
return spot;
}
*/
/*
================
SelectRandomJailSpawn
================
*/
edict_t *SelectRandomJailSpawn (edict_t* playr)
{
edict_t *spot, *spot1, *spot2;
int count = 0;
int selection;
float range, range1, range2;
spot = NULL;
range1 = range2 = 99999;
spot1 = spot2 = NULL;
while ((spot = G_Find(spot, FOFS(classname), "info_player_deathmatch")) != NULL)
{
if (spot->jail && (spot->teamnumber == playr->teamnumber || spot->teamnumber == 0))
count++;
range = PlayersRangeFromSpot(spot);
if (range < range1)
{
range1 = range;
spot1 = spot;
}
else if (range < range2)
{
range2 = range;
spot2 = spot;
}
}
if (!count)
return NULL;
if (count <= 2)
{
spot1 = spot2 = NULL;
}
else
count -= 2;
selection = (int)nu_rand(count);
spot = NULL;
do {
spot = G_Find(spot, FOFS(classname), "info_player_deathmatch");
//if (!spot->jail || (spot->teamnumber != playr->teamnumber && spot->teamnumber != 0))
if (spot == spot1 || spot == spot2 || !spot->jail || // 1.803
(spot->teamnumber != playr->teamnumber && spot->teamnumber != 0))
{
selection++;
}
} while(selection--);
return spot;
}
/*
================
SelectRandomDeathmatchSpawnPoint
go to a random point, but NOT the two points closest
to other players
================
*/
edict_t *SelectRandomDeathmatchSpawnPoint (void)
{
edict_t *spot, *spot1, *spot2;
int count = 0;
int selection;
float range, range1, range2;
spot = NULL;
range1 = range2 = 99999;
spot1 = spot2 = NULL;
while ((spot = G_Find(spot, FOFS(classname), "info_player_deathmatch")) != NULL)
{
if (spot->teamnumber < 128 && !spot->jail)
count++;
range = PlayersRangeFromSpot(spot);
if (range < range1)
{
range1 = range;
spot1 = spot;
}
else if (range < range2)
{
range2 = range;
spot2 = spot;
}
}
if (!count)
return NULL;
if (count <= 2)
{
spot1 = spot2 = NULL;
}
else
count -= 2;
selection = (int)nu_rand(count);
spot = NULL;
do {
spot = G_Find(spot, FOFS(classname), "info_player_deathmatch");
if (spot == spot1 || spot == spot2 || spot->teamnumber >= 128 || spot->jail)
selection++;
} while(selection--);
return spot;
}
/*
================
SelectFarthestDeathmatchSpawnPoint
================
*/
edict_t *SelectFarthestDeathmatchSpawnPoint (void)
{
edict_t *bestspot;
float bestdistance, bestplayerdistance;
edict_t *spot;
int i;
// lets just end up using the first one thats further than 200 units from anyone else
//self->client->resp.playermode & PMODE_ELIMINATED & PMODE_OBSERVER
for (i = 1000; i > 50; i-=5)
{
spot = NULL;
bestspot = NULL;
bestdistance = i;
while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL)
{
bestplayerdistance = PlayersRangeFromSpot (spot);
// sadif (bestplayerdistance > i && bestplayerdistance > bestdistance + (rand() % i / 3))
if (bestplayerdistance > i && bestplayerdistance > bestdistance + (nu_rand(i) / 3))
{
bestspot = spot;
bestdistance = bestplayerdistance;
}
}
if (bestspot)
{
return bestspot;
}
}
// if there is a player just spawned on each and every start spot
// we have no choice to turn one into a telefrag meltdown
spot = SelectRandomDeathmatchSpawnPoint();
return spot;
}
// This will return the distance of the enemy that is closest to this spawn point
int ClosestEnemy (int teamnum, edict_t *spot)
{
edict_t *player;
float bestplayerdistance;
vec3_t v;
int n;
float playerdistance;
bestplayerdistance = 9999999;
for (n = 1; n <= maxclients->value; n++)
{
player = &g_edicts[n];
if (!player->inuse)
continue;
//if (player->health <= 0 || (player->client->resp.playermode & PMODE_ELIMINATED & PMODE_OBSERVER))
if(!CanInteract(player)) // 1.74 - take jailers into account too?
continue;
if (player->teamnumber == teamnum)
continue;
VectorSubtract (spot->s.origin, player->s.origin, v);
playerdistance = VectorLength (v);
if (playerdistance < bestplayerdistance)
{
bestplayerdistance = playerdistance;
}
}
return bestplayerdistance;
}
// DP
// This will make quake2 find the spawn point that is farthest from
// an enemy, if the closest enemy is within 512 units, otherwise it
// will return a random spawn point. If either of the spawnpoints is
// within 64 units of a teammate, it will just return a random spot,
// and will gib your teammate if it cant get a valid random one after
// 10 tries
edict_t *FindPBallSpawn(edict_t *self);
edict_t *SelectRandomPBSpawn (int teamnum);
edict_t *SelectFarthestDeathmatchTeamSpawnPoint (edict_t *self)
{
edict_t *bestspot;
edict_t *lastgoodspot;
int i;
bestspot = NULL;
lastgoodspot = NULL;
i = 0;
// If we are playing, and it isnt just the respawnall command, lets
// Check for enemy locations. It could be bad to do otherwise
if (MatchIsRoundInProgress() && !IsChangeState())
bestspot = FindPBallSpawn(self);
// We want to use the farthest spawn from enemy if needed.
// But we also need to have it more than 64 units from a teammember.
// So lets check random spawns next, then make sure tehy are 64 units away
if (!bestspot)
bestspot = SelectRandomPBSpawn(self->teamnumber);
if (bestspot)
lastgoodspot = bestspot;
else
return lastgoodspot;
while (PlayersRangeFromSpot(bestspot) <= 64 && i < 10)
{
bestspot = SelectRandomPBSpawn(self->teamnumber);
i++;
if (bestspot)
lastgoodspot = bestspot;
else
return lastgoodspot;
}
return bestspot;
}
// Just pick a random spot for the right team. We'll circle outward for the next one
// if someone is too close
edict_t *SelectRandomPBSpawn (int teamnum)
{
edict_t *spot, *spot1, *spot2;
int count = 0;
int selection;
float range1, range2;
spot = NULL;
range1 = range2 = 99999;
spot1 = spot2 = NULL;
while ((spot = G_Find(spot, FOFS(classname), "info_player_deathmatch")) != NULL)
{
if (spot->teamnumber == teamnum && !spot->jail)
count++; // Only count the ones for the right team
}
if (!count)
return NULL;
selection = (int)nu_rand(count);
spot = NULL;
do
{
spot = G_Find(spot, FOFS(classname), "info_player_deathmatch");
if (!(spot->teamnumber == teamnum && !spot->jail))
selection++;
}
while (selection--);
return spot;
}
edict_t *FindPBallSpawn (edict_t *self)
{
edict_t *bestspot;
float bestdistance, bestplayerdistance;
edict_t *spot;
int closest;
spot = NULL;
bestspot = NULL;
bestdistance = 0;
closest = 9999999;
while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL)
{
if (spot->teamnumber != self->teamnumber || spot->jail)
continue;
bestplayerdistance = ClosestEnemy (self->teamnumber,spot);
if (bestplayerdistance <= closest)
closest = bestplayerdistance;
if (bestplayerdistance >= bestdistance)
{
bestspot = spot;
bestdistance = bestplayerdistance;
}
}
if (closest < 512 && bestspot) {
// If there is a guy within 512 units of a spawn point for our team...
// we want to use the farthest away spawn point.
return bestspot;
}
return NULL;
// spot = G_Find (NULL, FOFS(classname), "info_player_deathmatch");
}
edict_t *SelectDeathmatchSpawnPoint (edict_t *self)
{
// DP SPOT
edict_t *spot;
if (self->client->resp.playermode & PMODE_OBSERVER)
{
spot = SelectRandomDeathmatchSpawnPoint();
}
//else if (ctftype == CTFTYPE_DM)
else if (!GameIsTeamsGame()) // 1.803
{
spot = SelectRandomDeathmatchSpawnPoint();
}
else if (self->client->resp.playermode & PMODE_JAILED)
{
spot = SelectRandomJailSpawn(self);
}
else
{
spot = SelectFarthestDeathmatchTeamSpawnPoint(self);
if (!spot || spot->teamnumber != self->teamnumber)
spot = SelectRandomDeathmatchSpawnPoint();
}
if (!spot) // 1.803, for debugging
{
gi.dprintf("ERROR: couldn't find spawn for: %s, team: %s, jailed: %s, mode: %s\n",
self->client->pers.netname, TeamsGetName(TeamsGetTeam(self)),
(self->client->resp.playermode & PMODE_JAILED) ? "yes" : "no",
GameIsTeamsGame() ? "team" : "DM");
}
return spot;
// return SelectNextDeathmatchSpawnPoint();
// ENDDP
}
/*
==========
SelectSpawnPoint
Chooses a player start, deathmatch start, coop start, etc
============
*/
void SelectSpawnPoint (edict_t *ent, vec3_t origin, vec3_t angles)
{
edict_t *spot = NULL;
edict_t* chain;
spot = SelectDeathmatchSpawnPoint(ent);
/* 1.803
// find a single player start spot
if (!spot)
{
while ((spot = G_Find (spot, FOFS(classname), "info_player_start")) != NULL)
{
if (!game.spawnpoint[0] && !spot->targetname)
break;
if (!game.spawnpoint[0] || !spot->targetname)
continue;
if (Q_strcasecmp(game.spawnpoint, spot->targetname) == 0)
break;
}
if (!spot)
{
if (!game.spawnpoint[0])
{ // there wasn't a spawnpoint without a target, so use any
spot = G_Find (spot, FOFS(classname), "info_player_start");
}
if (!spot)
gi.error ("Couldn't find spawn point %s\n", game.spawnpoint);
}
}*/
if (!spot) // 1.803
{
// unable to find a proper spawn point, so just use whatever.
spot = G_Find(spot, FOFS(classname), "info_player_start");
if (!spot)
spot = G_Find(spot, FOFS(classname), "info_player_deathmatch");
if (!spot)
gi.error ("Couldn't find spawn point %s\n", game.spawnpoint);
}
chain = spot;
for(chain = spot; chain->previousowner != NULL && chain->previousowner != chain; chain = chain->previousowner)
{ } // NULL SET (everything done in for statement
// SPAWN WITH WEAPONS CODE - I made it so it creates a chain of PREVIOUSOWNERS
// this way i can have as many people as I want spawn in the same spawnpoint at
// the exact same instant
chain->previousowner = ent;
chain->think = SpawnSpecialEnts;
chain->nextthink = level.time + 0.1f;
ent->previousowner = NULL; // Fix for endless loop
VectorCopy(spot->s.origin, origin);
origin[2] += 1;
VectorCopy(spot->s.angles, angles);
}