2016-03-27 11:49:47 +02:00
|
|
|
/*
|
|
|
|
===========================================================================
|
|
|
|
Copyright (C) 2015 the OpenMoHAA team
|
|
|
|
|
|
|
|
This file is part of OpenMoHAA source code.
|
|
|
|
|
|
|
|
OpenMoHAA source code is free software; you can redistribute it
|
|
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
|
|
or (at your option) any later version.
|
|
|
|
|
|
|
|
OpenMoHAA source code is distributed in the hope that it will be
|
|
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with OpenMoHAA source code; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
===========================================================================
|
|
|
|
*/
|
|
|
|
// dm_manager.cpp: Deathmatch Manager.
|
|
|
|
|
|
|
|
#include "player.h"
|
|
|
|
#include "dm_manager.h"
|
2023-02-01 19:29:13 +01:00
|
|
|
#include "playerstart.h"
|
2023-04-29 21:56:38 +02:00
|
|
|
#include "scriptexception.h"
|
2016-03-27 11:49:47 +02:00
|
|
|
|
|
|
|
cvar_t *g_tempaxisscore;
|
|
|
|
cvar_t *g_tempaxiswinsinrow;
|
|
|
|
cvar_t *g_tempalliesscore;
|
|
|
|
cvar_t *g_tempallieswinsinrow;
|
|
|
|
|
|
|
|
DM_Manager dmManager;
|
|
|
|
|
2023-09-23 19:37:55 +02:00
|
|
|
static CTeamSpawnClock g_teamSpawnClock;
|
2023-08-10 02:31:01 +02:00
|
|
|
|
|
|
|
typedef struct spawnsort_s {
|
|
|
|
PlayerStart *spawnpoint;
|
|
|
|
float fMetric;
|
|
|
|
} spawnsort_t;
|
|
|
|
|
|
|
|
static qboolean SpotWouldTelefrag(float *origin)
|
|
|
|
{
|
|
|
|
static Vector mins = Vector(-16, -16, 1);
|
|
|
|
static Vector maxs = Vector(16, 16, 97);
|
|
|
|
trace_t trace;
|
|
|
|
|
2023-08-16 02:37:57 +02:00
|
|
|
trace = G_Trace(Vector(origin), mins, maxs, Vector(origin), NULL, MASK_PLAYERSTART, qfalse, "SpotWouldTelefrag");
|
2023-08-10 02:31:01 +02:00
|
|
|
|
|
|
|
if (trace.startsolid || trace.allsolid) {
|
|
|
|
return qtrue;
|
|
|
|
} else {
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int compare_spawnsort(const void *pe1, const void *pe2)
|
|
|
|
{
|
|
|
|
float fDelta = ((spawnsort_t *)pe1)->fMetric - ((spawnsort_t *)pe2)->fMetric;
|
|
|
|
|
|
|
|
if (fDelta >= -0.001f) {
|
|
|
|
if (fDelta <= 0.001f) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static PlayerStart *GetRandomSpawnpointFromList(spawnsort_t *pSpots, int nSpots)
|
|
|
|
{
|
|
|
|
int i = nSpots;
|
|
|
|
float fTotalMetric;
|
|
|
|
float fMinPosMetric;
|
|
|
|
float fChosen;
|
|
|
|
|
|
|
|
if (nSpots <= 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
qsort(pSpots, nSpots, sizeof(spawnsort_t), compare_spawnsort);
|
|
|
|
|
|
|
|
if (pSpots[0].fMetric > 0.0f) {
|
|
|
|
if (nSpots > 5) {
|
|
|
|
nSpots = 5;
|
|
|
|
}
|
|
|
|
|
|
|
|
fMinPosMetric = pSpots[0].fMetric * nSpots;
|
|
|
|
fTotalMetric = fMinPosMetric;
|
|
|
|
|
|
|
|
if (nSpots <= 1) {
|
|
|
|
fChosen = fMinPosMetric;
|
|
|
|
} else {
|
|
|
|
i = 0;
|
|
|
|
fTotalMetric = 0.0f;
|
|
|
|
|
|
|
|
for (i = 0; i < nSpots; i++) {
|
|
|
|
if (pSpots[i].fMetric <= 0.0f) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
fChosen = pSpots[i].fMetric * (nSpots - i);
|
|
|
|
fTotalMetric += fChosen;
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i < nSpots) {
|
|
|
|
fChosen = fMinPosMetric;
|
|
|
|
}
|
|
|
|
|
|
|
|
fMinPosMetric = fTotalMetric;
|
|
|
|
}
|
|
|
|
|
|
|
|
fTotalMetric = (fMinPosMetric - i * fChosen * 0.90f) * G_Random();
|
|
|
|
for (i = 0; i < nSpots - 1; i++) {
|
|
|
|
fTotalMetric -= (nSpots - i) * pSpots[i].fMetric - (fChosen * 0.90f);
|
|
|
|
if (fTotalMetric <= 0.0f) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return pSpots[i].spawnpoint;
|
|
|
|
} else {
|
|
|
|
// return the spot anyway
|
|
|
|
return pSpots[0].spawnpoint;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
float SpawnpointMetric_Ffa(const float *origin, DM_Team *dmTeam, const Player *player)
|
|
|
|
{
|
|
|
|
float fMinEnemyDistSquared = 23170.0f * 23170.0f;
|
|
|
|
int i;
|
|
|
|
int nPlayers = dmManager.PlayerCount();
|
|
|
|
float fDist;
|
|
|
|
|
|
|
|
for (i = 1; i < nPlayers; i++) {
|
|
|
|
Player *teammate = dmManager.GetPlayer(i);
|
|
|
|
if (teammate == player || teammate->IsDead() || teammate->IsSpectator()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
fDist = (teammate->origin - Vector(origin)).lengthSquared();
|
|
|
|
|
|
|
|
if (fMinEnemyDistSquared > fDist) {
|
|
|
|
fMinEnemyDistSquared = fDist;
|
|
|
|
}
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
return fMinEnemyDistSquared - (G_Random(0.25f) + 1.0f) * (1024.0f * 1024.0f);
|
|
|
|
}
|
|
|
|
|
|
|
|
float SpawnpointMetric_Team(const float *origin, DM_Team *dmTeam, const Player *player)
|
|
|
|
{
|
|
|
|
float fMinEnemyDistSquared = 23170.0f * 23170.0f;
|
|
|
|
float fSumFriendDistSquared = 0.0f;
|
|
|
|
float fDistSquared;
|
|
|
|
float fMetric;
|
|
|
|
int i;
|
|
|
|
int nPlayers = dmManager.PlayerCount();
|
|
|
|
int nFriends = 0;
|
|
|
|
|
|
|
|
for (i = 1; i < nPlayers; i++) {
|
|
|
|
Player *teammate = dmManager.GetPlayer(i);
|
|
|
|
if (teammate == player || teammate->IsDead() || teammate->IsSpectator()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
fDistSquared = (teammate->origin - Vector(origin)).lengthSquared();
|
|
|
|
|
|
|
|
if (teammate->GetDM_Team() == dmTeam) {
|
|
|
|
nFriends++;
|
|
|
|
fSumFriendDistSquared += fDistSquared;
|
|
|
|
} else {
|
|
|
|
if (fMinEnemyDistSquared > fDistSquared) {
|
|
|
|
fMinEnemyDistSquared = fDistSquared;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fMetric = fMinEnemyDistSquared - (G_Random(0.25f) + 1.0f) * (1024.0f * 1024.0f);
|
|
|
|
|
|
|
|
if (nFriends) {
|
|
|
|
fMetric += 0.25f * ((23170.0f * 23170.0f) - fSumFriendDistSquared / nFriends);
|
|
|
|
}
|
|
|
|
|
|
|
|
return fMetric;
|
|
|
|
}
|
|
|
|
|
|
|
|
float SpawnpointMetric_Objective(const float *origin, DM_Team *dmTeam, const Player *player)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
return rand() * 0.0000000005f;
|
|
|
|
}
|
|
|
|
|
|
|
|
CLASS_DECLARATION(Listener, DM_Team, NULL) {
|
|
|
|
{NULL, NULL}
|
2016-03-27 11:49:47 +02:00
|
|
|
};
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
DM_Team::DM_Team()
|
|
|
|
{
|
|
|
|
m_teamwins = 0;
|
|
|
|
m_wins_in_a_row = 0;
|
|
|
|
m_teamnumber = -1;
|
|
|
|
m_iKills = 0;
|
|
|
|
m_iDeaths = 0;
|
|
|
|
m_bHasSpawnedPlayers = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
DM_Team::~DM_Team() {}
|
|
|
|
|
|
|
|
void DM_Team::Reset(void)
|
|
|
|
{
|
|
|
|
m_spawnpoints.ClearObjectList();
|
|
|
|
m_players.ClearObjectList();
|
2023-09-23 19:37:55 +02:00
|
|
|
|
|
|
|
if (g_gametype->integer == GT_TEAM) {
|
|
|
|
m_teamwins = 0;
|
|
|
|
}
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void DM_Team::AddPlayer(Player *player)
|
|
|
|
{
|
2023-09-23 19:37:55 +02:00
|
|
|
m_players.AddUniqueObject(player);
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void DM_Team::RemovePlayer(Player *player)
|
|
|
|
{
|
|
|
|
m_players.RemoveObject(player);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DM_Team::TeamWin(void)
|
|
|
|
{
|
|
|
|
m_teamwins++;
|
|
|
|
m_wins_in_a_row++;
|
|
|
|
|
|
|
|
for (int i = 1; i <= m_players.NumObjects(); i++) {
|
|
|
|
m_players.ObjectAt(i)->WonMatch();
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateTeamStatus();
|
|
|
|
|
|
|
|
if (m_teamnumber == TEAM_ALLIES) {
|
2023-08-15 01:27:35 +02:00
|
|
|
gi.cvar_set("g_scoreboardpicover", "textures/hud/allieswin");
|
2023-08-10 02:31:01 +02:00
|
|
|
} else if (m_teamnumber == TEAM_AXIS) {
|
2023-08-15 01:27:35 +02:00
|
|
|
gi.cvar_set("g_scoreboardpicover", "textures/hud/axiswin");
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DM_Team::TeamLoss(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
m_wins_in_a_row = 0;
|
|
|
|
|
|
|
|
for (int i = 1; i <= m_players.NumObjects(); i++) {
|
|
|
|
m_players.ObjectAt(i)->LostMatch();
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateTeamStatus();
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
void DM_Team::AddKills(Player *player, int numKills)
|
|
|
|
{
|
|
|
|
if (level.intermissiontime || dmManager.GetTeamWin()) {
|
|
|
|
return;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
player->AddKills(numKills);
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
if (m_teamnumber > TEAM_FREEFORALL) {
|
|
|
|
m_iKills += numKills;
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2024-02-05 21:03:10 +01:00
|
|
|
if ((g_gametype->integer >= GT_TEAM_ROUNDS && g_gametype->integer <= GT_TOW)
|
|
|
|
|| g_gametype->integer == GT_LIBERATION) {
|
2023-08-10 02:31:01 +02:00
|
|
|
player->AddDeaths(numKills);
|
|
|
|
} else {
|
|
|
|
m_teamwins += numKills;
|
|
|
|
}
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
void DM_Team::AddDeaths(Player *player, int numDeaths)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
if (level.intermissiontime || dmManager.GetTeamWin()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-02-05 21:03:10 +01:00
|
|
|
if ((g_gametype->integer >= GT_TEAM_ROUNDS && g_gametype->integer <= GT_TOW)
|
|
|
|
|| g_gametype->integer == GT_LIBERATION) {
|
2023-08-10 02:31:01 +02:00
|
|
|
return;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
player->AddDeaths(numDeaths);
|
|
|
|
|
|
|
|
if (m_teamnumber > TEAM_FREEFORALL) {
|
|
|
|
m_iDeaths += numDeaths;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
void DM_Team::TeamInvulnerable(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
for (int i = 1; i <= m_players.NumObjects(); i++) {
|
|
|
|
m_players.ObjectAt(i)->takedamage = DAMAGE_NO;
|
|
|
|
}
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
void DM_Team::BeginFight(void)
|
|
|
|
{
|
|
|
|
for (int i = 1; i <= m_players.NumObjects(); i++) {
|
|
|
|
m_players.ObjectAt(i)->BeginFight();
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
float DM_Team::PlayersRangeFromSpot(PlayerStart *spot)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
float bestplayerdistance = 9999999.0f;
|
|
|
|
Vector vDist;
|
|
|
|
int i;
|
|
|
|
gentity_t *ent;
|
|
|
|
Player *player;
|
|
|
|
|
|
|
|
if (g_gametype->integer > GT_FFA) {
|
|
|
|
return bestplayerdistance;
|
|
|
|
}
|
|
|
|
|
|
|
|
// find the nearest player from the post
|
|
|
|
for (i = 0, ent = g_entities; i < game.maxclients; i++, ent++) {
|
|
|
|
if (!ent->client || !ent->entity) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
player = (Player *)ent->entity;
|
|
|
|
if (!player->IsDead() && !player->IsSpectator() && m_teamnumber != player->GetTeam()) {
|
|
|
|
vDist = spot->origin - player->origin;
|
|
|
|
if (bestplayerdistance > vDist.length()) {
|
|
|
|
bestplayerdistance = vDist.length();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return bestplayerdistance;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
PlayerStart *DM_Team::FarthestSpawnPoint(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int iNumPoints;
|
|
|
|
float bestdistance;
|
|
|
|
float bestplayerdistance;
|
|
|
|
PlayerStart *pSpot;
|
|
|
|
PlayerStart *pBestSpot;
|
|
|
|
PlayerStart *pSpot2;
|
|
|
|
PlayerStart *pSpot3;
|
|
|
|
|
|
|
|
bestplayerdistance = 0.0f;
|
|
|
|
pBestSpot = NULL;
|
|
|
|
pSpot2 = NULL;
|
|
|
|
pSpot3 = NULL;
|
|
|
|
iNumPoints = m_spawnpoints.NumObjects();
|
|
|
|
|
|
|
|
for (i = 1; i <= iNumPoints; i++) {
|
|
|
|
pSpot = m_spawnpoints.ObjectAt(i);
|
|
|
|
|
|
|
|
bestdistance = PlayersRangeFromSpot(m_spawnpoints.ObjectAt(i));
|
|
|
|
if (bestdistance > bestplayerdistance) {
|
|
|
|
bestplayerdistance = bestdistance;
|
|
|
|
pSpot3 = pSpot2;
|
|
|
|
pSpot2 = pBestSpot;
|
|
|
|
pBestSpot = pSpot;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pSpot3 && G_Random() < 0.2f) {
|
|
|
|
return pSpot3;
|
|
|
|
} else if (pSpot2 && G_Random() < 0.3f) {
|
|
|
|
return pSpot2;
|
|
|
|
} else {
|
|
|
|
return pBestSpot;
|
|
|
|
}
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
PlayerStart *DM_Team::GetRandomSpawnpoint(void)
|
|
|
|
{
|
|
|
|
PlayerStart *spot = NULL;
|
|
|
|
int numPoints = m_spawnpoints.NumObjects();
|
|
|
|
|
|
|
|
if (numPoints) {
|
|
|
|
spot = FarthestSpawnPoint();
|
|
|
|
if (!spot) {
|
|
|
|
spot = m_spawnpoints.ObjectAt((int)(G_Random(numPoints) + 1.0f));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
warning("DM_Team::GetRandomSpawnpoint", "No spawnpoints found\n");
|
|
|
|
spot = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return spot;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
PlayerStart *DM_Team::GetRandomSpawnpointWithMetric(
|
|
|
|
Player *player, float (*MetricFunction)(const float *origin, DM_Team *dmTeam, const Player *player)
|
|
|
|
)
|
|
|
|
{
|
|
|
|
static float offset[4][3];
|
|
|
|
spawnsort_t points[1024];
|
|
|
|
PlayerStart *spot = NULL;
|
|
|
|
int numSpots = 0;
|
|
|
|
int iPoint = 0;
|
|
|
|
|
|
|
|
for (int i = 1; i <= m_spawnpoints.NumObjects(); i++) {
|
|
|
|
spot = m_spawnpoints.ObjectAt(i);
|
|
|
|
if (spot->m_bForbidSpawns || player->GetLastSpawnpoint() == spot) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!SpotWouldTelefrag(spot->origin)) {
|
|
|
|
points[numSpots].spawnpoint = spot;
|
|
|
|
points[numSpots].fMetric = MetricFunction(spot->origin, this, player);
|
|
|
|
numSpots++;
|
|
|
|
|
|
|
|
if (numSpots >= (sizeof(points) / sizeof(points[0]))) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
spot = GetRandomSpawnpointFromList(points, numSpots);
|
|
|
|
if (spot) {
|
|
|
|
return spot;
|
|
|
|
}
|
|
|
|
|
|
|
|
numSpots = 0;
|
|
|
|
|
|
|
|
for (int i = 1; i <= m_spawnpoints.NumObjects(); i++) {
|
|
|
|
spot = m_spawnpoints.ObjectAt(i);
|
|
|
|
if (spot->m_bForbidSpawns) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int j = 0; j < sizeof(offset) / sizeof(offset[0]); j++) {
|
|
|
|
Vector vNewSpawn = spot->origin + offset[j];
|
|
|
|
|
|
|
|
if (G_SightTrace(
|
|
|
|
spot->origin,
|
|
|
|
player->mins,
|
|
|
|
player->maxs,
|
|
|
|
vNewSpawn,
|
|
|
|
(Entity *)NULL,
|
|
|
|
(Entity *)NULL,
|
|
|
|
MASK_PLAYERSOLID,
|
|
|
|
qfalse,
|
|
|
|
"DM_Team::GetRandomSpawnpointWithMetric"
|
|
|
|
)
|
|
|
|
== 1) {
|
|
|
|
Vector vEnd = vNewSpawn - Vector(0, 0, 64);
|
|
|
|
|
|
|
|
trace_t trace = G_Trace(
|
|
|
|
vNewSpawn, player->mins, player->maxs, vEnd, player, MASK_PLAYERSOLID, qfalse, "TempSpawnPoint"
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!trace.allsolid && !trace.startsolid && trace.fraction != 1.0f && trace.fraction != 1.0f
|
|
|
|
&& trace.plane.dist >= 0.8f) {
|
|
|
|
points[numSpots].spawnpoint = new PlayerStart;
|
|
|
|
points[numSpots].spawnpoint->setOrigin(trace.endpos);
|
|
|
|
points[numSpots].spawnpoint->setAngles(spot->angles);
|
|
|
|
points[numSpots].fMetric = MetricFunction(vNewSpawn, this, player);
|
|
|
|
|
|
|
|
if (numSpots >= (sizeof(points) / sizeof(points[0]))) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
spot = GetRandomSpawnpointFromList(points, numSpots);
|
|
|
|
for (int i = 0; i < numSpots; i++) {
|
|
|
|
// delete all created spawnpoint
|
|
|
|
if (points[numSpots].spawnpoint != spot) {
|
|
|
|
delete points[numSpots].spawnpoint;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (spot) {
|
|
|
|
return spot;
|
|
|
|
}
|
|
|
|
|
|
|
|
numSpots = 0;
|
|
|
|
|
|
|
|
for (int i = 1; i <= m_spawnpoints.NumObjects(); i++) {
|
|
|
|
spot = m_spawnpoints.ObjectAt(i);
|
|
|
|
if (!spot->m_bForbidSpawns && player->GetLastSpawnpoint() != spot) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!SpotWouldTelefrag(spot->origin)) {
|
|
|
|
points[numSpots].spawnpoint = spot;
|
|
|
|
points[numSpots].fMetric = MetricFunction(spot->origin, this, player);
|
|
|
|
|
|
|
|
if (numSpots >= (sizeof(points) / sizeof(points[0]))) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return GetRandomSpawnpointFromList(points, numSpots);
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
PlayerStart *DM_Team::GetRandomFfaSpawnpoint(Player *player)
|
|
|
|
{
|
|
|
|
return GetRandomSpawnpointWithMetric(player, SpawnpointMetric_Ffa);
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
PlayerStart *DM_Team::GetRandomTeamSpawnpoint(Player *player)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
return GetRandomSpawnpointWithMetric(player, SpawnpointMetric_Team);
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
PlayerStart *DM_Team::GetRandomObjectiveSpawnpoint(Player *player)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
return GetRandomSpawnpointWithMetric(player, SpawnpointMetric_Objective);
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
bool DM_Team::IsDead(void) const
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
Player *player;
|
|
|
|
|
2023-09-23 19:37:55 +02:00
|
|
|
if (m_players.NumObjects() == 0) {
|
|
|
|
return dmManager.IsGameActive();
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
if (!m_bHasSpawnedPlayers) {
|
|
|
|
return false;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-09-23 19:37:55 +02:00
|
|
|
if (g_gametype->integer == GT_TOW) {
|
|
|
|
if (dmManager.AllowTeamRespawn(m_teamnumber)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (dmManager.AllowRespawn()) {
|
2023-08-10 02:31:01 +02:00
|
|
|
return false;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
for (int i = m_players.NumObjects(); i > 0; i--) {
|
|
|
|
player = m_players.ObjectAt(i);
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-09-23 19:37:55 +02:00
|
|
|
if (player->IsDead()) {
|
2023-08-10 02:31:01 +02:00
|
|
|
continue;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-09-23 19:37:55 +02:00
|
|
|
if (!player->IsSpectator()) {
|
2023-08-10 02:31:01 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
int DM_Team::NumLivePlayers(void) const
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
Player *player;
|
|
|
|
int num = 0;
|
|
|
|
|
|
|
|
for (int i = 1; i <= m_players.NumObjects(); i++) {
|
|
|
|
player = m_players.ObjectAt(i);
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
if (!player->IsDead() && !player->IsSpectator()) {
|
|
|
|
num++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return num;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
int DM_Team::TotalPlayersKills(void) const
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
Player *player;
|
|
|
|
int iKills = 0;
|
|
|
|
|
|
|
|
for (int i = 1; i <= m_players.NumObjects(); i++) {
|
|
|
|
player = m_players.ObjectAt(i);
|
2023-09-23 19:37:55 +02:00
|
|
|
iKills += player->GetNumKills();
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return iKills;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
bool DM_Team::IsEmpty(void) const
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-09-23 19:37:55 +02:00
|
|
|
return !m_players.NumObjects() || !m_bHasSpawnedPlayers;
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
int DM_Team::NumNotReady(void) const
|
|
|
|
{
|
|
|
|
Player *player;
|
|
|
|
int num = 0;
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
for (int i = 1; i <= m_players.NumObjects(); i++) {
|
|
|
|
player = m_players.ObjectAt(i);
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-09-23 19:37:55 +02:00
|
|
|
if (player->IsReady()) {
|
2023-08-10 02:31:01 +02:00
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
return num;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
void DM_Team::UpdateTeamStatus(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
for (int i = 1; i <= m_players.NumObjects(); i++) {
|
|
|
|
m_players.ObjectAt(i)->UpdateStatus(va("%d wins (%d in a row)", m_teamwins, m_wins_in_a_row));
|
|
|
|
}
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
SimpleAmmoType::SimpleAmmoType()
|
|
|
|
: amount(0)
|
|
|
|
{}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
Event EV_DM_Manager_DoRoundTransition
|
|
|
|
(
|
|
|
|
"doroundtransition",
|
|
|
|
EV_DEFAULT,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
"delayed function call to (possibly) determine round winner and restart next round"
|
|
|
|
);
|
|
|
|
Event EV_DM_Manager_FinishRoundTransition
|
|
|
|
(
|
2024-02-05 21:03:10 +01:00
|
|
|
"finishroundtransition",
|
|
|
|
EV_DEFAULT,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
"delayed function call to do the actual restart for the next round"
|
2023-08-10 02:31:01 +02:00
|
|
|
);
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
CLASS_DECLARATION(Listener, DM_Manager, NULL) {
|
|
|
|
{&EV_DM_Manager_DoRoundTransition, &DM_Manager::EventDoRoundTransition },
|
|
|
|
{&EV_DM_Manager_FinishRoundTransition, &DM_Manager::EventFinishRoundTransition},
|
|
|
|
{NULL, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
DM_Manager::DM_Manager()
|
|
|
|
{
|
|
|
|
m_team_spectator.m_maxplayers = MAX_CLIENTS;
|
|
|
|
m_team_spectator.setName("spectator");
|
|
|
|
m_team_spectator.setNumber(TEAM_SPECTATOR);
|
|
|
|
m_team_spectator.setIndex(TEAM_SPECTATOR);
|
|
|
|
|
|
|
|
m_team_freeforall.m_maxplayers = MAX_CLIENTS;
|
|
|
|
m_team_freeforall.setName("free-for-all");
|
|
|
|
m_team_freeforall.setNumber(TEAM_FREEFORALL);
|
|
|
|
m_team_freeforall.setIndex(TEAM_FREEFORALL);
|
|
|
|
|
|
|
|
m_team_allies.m_maxplayers = MAX_CLIENTS;
|
|
|
|
m_team_allies.setName("allies");
|
|
|
|
m_team_allies.setNumber(TEAM_ALLIES);
|
|
|
|
m_team_allies.setIndex(TEAM_ALLIES);
|
|
|
|
|
|
|
|
m_team_axis.m_maxplayers = MAX_CLIENTS;
|
|
|
|
m_team_axis.setName("axis");
|
|
|
|
m_team_axis.setNumber(TEAM_AXIS);
|
|
|
|
m_team_axis.setIndex(TEAM_AXIS);
|
|
|
|
|
2023-09-23 18:30:19 +02:00
|
|
|
m_fRoundTime = 0;
|
|
|
|
m_fRoundEndTime = 0;
|
|
|
|
m_bAllowRespawns = true;
|
|
|
|
m_bRoundBasedGame = false;
|
2023-08-10 02:31:01 +02:00
|
|
|
m_iTeamWin = 0;
|
2023-09-23 18:30:19 +02:00
|
|
|
m_iDefaultRoundLimit = 0;
|
2023-08-10 02:31:01 +02:00
|
|
|
m_csTeamClockSide = STRING_AXIS;
|
|
|
|
m_csTeamBombPlantSide = STRING_DRAW;
|
2023-09-23 18:30:19 +02:00
|
|
|
m_iNumTargetsToDestroy = 1;
|
2023-08-10 02:31:01 +02:00
|
|
|
m_iNumTargetsDestroyed = 0;
|
2024-02-05 21:03:10 +01:00
|
|
|
m_iNumBombsPlanted = 0;
|
2023-09-23 18:30:19 +02:00
|
|
|
m_bAllowAxisRespawn = true;
|
|
|
|
m_bAllowAlliedRespawn = true;
|
|
|
|
m_bRoundActive = false;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
2023-08-10 02:31:01 +02:00
|
|
|
|
|
|
|
DM_Manager::~DM_Manager() {}
|
|
|
|
|
|
|
|
void DM_Manager::Reset(void)
|
|
|
|
{
|
|
|
|
m_team_allies.Reset();
|
|
|
|
m_team_axis.Reset();
|
|
|
|
m_team_spectator.Reset();
|
|
|
|
m_team_freeforall.Reset();
|
|
|
|
|
|
|
|
m_players.ClearObjectList();
|
|
|
|
m_teams.ClearObjectList();
|
|
|
|
|
2023-08-15 01:27:35 +02:00
|
|
|
gi.cvar_set("g_scoreboardpicover", "");
|
2023-08-10 02:31:01 +02:00
|
|
|
|
|
|
|
//
|
|
|
|
// Added in 2.0
|
|
|
|
//
|
|
|
|
m_bAllowAxisRespawn = true;
|
|
|
|
m_bAllowAlliedRespawn = true;
|
2023-09-23 19:37:55 +02:00
|
|
|
|
|
|
|
// Reset the team spawn clock
|
|
|
|
g_teamSpawnClock.Reset();
|
|
|
|
level.m_bIgnoreClock = false;
|
2023-08-10 02:31:01 +02:00
|
|
|
|
|
|
|
if (g_gametype->integer == GT_TOW) {
|
2023-08-16 02:05:44 +02:00
|
|
|
g_TOWObjectiveMan.Reset();
|
2023-08-15 01:27:35 +02:00
|
|
|
gi.cvar_set("g_TOW_winstate", "0");
|
2023-08-10 02:31:01 +02:00
|
|
|
} else if (g_gametype->integer == GT_LIBERATION) {
|
2023-08-15 01:27:35 +02:00
|
|
|
gi.cvar_set("scoreboard_toggle1", "0");
|
|
|
|
gi.cvar_set("scoreboard_toggle2", "0");
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
void DM_Manager::AddPlayer(Player *player)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
m_players.AddUniqueObject(player);
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
void DM_Manager::RemovePlayer(Player *player)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
DM_Team *pDMTeam;
|
|
|
|
|
|
|
|
m_players.RemoveObject(player);
|
|
|
|
|
|
|
|
for (int i = m_teams.NumObjects(); i > 0; i--) {
|
|
|
|
pDMTeam = m_teams.ObjectAt(i);
|
|
|
|
|
|
|
|
if (pDMTeam->m_players.IndexOfObject(player)) {
|
|
|
|
pDMTeam->RemovePlayer(player);
|
|
|
|
|
|
|
|
if (!pDMTeam->m_players.NumObjects()) {
|
|
|
|
pDMTeam->m_bHasSpawnedPlayers = qfalse;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
player->SetDM_Team(NULL);
|
|
|
|
|
|
|
|
RebuildTeamConfigstrings();
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
bool DM_Manager::JoinTeam(Player *player, teamtype_t teamType)
|
|
|
|
{
|
|
|
|
DM_Team *team = player->GetDM_Team();
|
|
|
|
DM_Team *pDMTeam = GetTeam(teamType);
|
|
|
|
|
|
|
|
if (!pDMTeam) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pDMTeam->m_players.NumObjects() >= pDMTeam->m_maxplayers) {
|
|
|
|
gi.centerprintf(player->edict, gi.LV_ConvertString("That team is full"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (team) {
|
|
|
|
LeaveTeam(player);
|
|
|
|
}
|
|
|
|
|
|
|
|
pDMTeam->AddPlayer(player);
|
|
|
|
AddPlayer(player);
|
|
|
|
player->SetDM_Team(pDMTeam);
|
|
|
|
|
|
|
|
if (teamType == TEAM_SPECTATOR) {
|
|
|
|
player->EndFight();
|
|
|
|
} else {
|
|
|
|
player->BeginFight();
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DM_Manager::PlayerKilled(Player *player)
|
|
|
|
{
|
|
|
|
// Spawn a deadbody
|
|
|
|
player->DeadBody(NULL);
|
|
|
|
|
|
|
|
// Hide the model because of the dead body
|
|
|
|
player->hideModel();
|
|
|
|
|
|
|
|
// Don't let the player die a second time
|
|
|
|
player->takedamage = DAMAGE_NO;
|
|
|
|
|
|
|
|
CheckEndMatch();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DM_Manager::LeaveTeam(Player *player)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
DM_Team *team = player->GetDM_Team();
|
|
|
|
|
|
|
|
if (team) {
|
|
|
|
if (team->m_players.IndexOfObject(player)) {
|
|
|
|
team->RemovePlayer(player);
|
|
|
|
RemovePlayer(player);
|
|
|
|
player->SetDM_Team(NULL);
|
|
|
|
RebuildTeamConfigstrings();
|
|
|
|
} else {
|
|
|
|
warning("DM_Manager::LeaveTeam", "Could not find team in the arena\n");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
warning("DM_Manager::LeaveTeam", "Could not find a team for this player\n");
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
void DM_Manager::RebuildTeamConfigstrings(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
DM_TeamPtr team;
|
|
|
|
int teamcount;
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
teamcount = m_teams.NumObjects();
|
|
|
|
|
|
|
|
for (int i = 1; i <= teamcount; i++) {
|
|
|
|
team = m_teams.ObjectAt(i);
|
|
|
|
|
2023-08-15 01:27:35 +02:00
|
|
|
gi.setConfigstring(
|
2023-08-10 02:31:01 +02:00
|
|
|
CS_GENERAL_STRINGS + i,
|
|
|
|
va("%d %s %d player(s)", team->m_teamnumber, team->m_teamname.c_str(), team->m_players.NumObjects())
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-08-15 01:27:35 +02:00
|
|
|
gi.setConfigstring(CS_TEAMS, va("%d", teamcount));
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
int DM_Manager::compareScores(const void *elem1, const void *elem2)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
if (*(int *)elem1 < -1 || *(int *)elem2 < 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Player *p1 = (Player *)G_GetEntity(*(int *)elem1);
|
|
|
|
Player *p2 = (Player *)G_GetEntity(*(int *)elem2);
|
|
|
|
|
|
|
|
if (p1->GetNumKills() < p2->GetNumKills()) {
|
|
|
|
return 1;
|
|
|
|
} else if (p1->GetNumKills() == p2->GetNumKills()) {
|
|
|
|
// sort by death if they have the same number of kills
|
|
|
|
if (p1->GetNumDeaths() < p2->GetNumDeaths()) {
|
|
|
|
return 1;
|
|
|
|
} else if (p1->GetNumDeaths() == p2->GetNumDeaths()) {
|
|
|
|
// sort by netname if they have the same number of deaths
|
|
|
|
return Q_stricmp(p1->client->pers.netname, p2->client->pers.netname);
|
|
|
|
} else if (p1->GetNumDeaths() > p2->GetNumDeaths()) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else if (p1->GetNumKills() > p2->GetNumKills()) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// just to avoid the compiler warning
|
|
|
|
// shouldn't go there
|
|
|
|
return 0;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
void DM_Manager::Score(Player *player)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
int i, j;
|
|
|
|
int count = 0;
|
|
|
|
int stringlength = 0;
|
|
|
|
Player *currentPlayer;
|
|
|
|
int iPlayerList[MAX_CLIENTS];
|
|
|
|
DM_Team *pDMTeam;
|
|
|
|
|
2023-08-16 02:05:44 +02:00
|
|
|
//
|
|
|
|
// Since 2.0, added the player count for individual team, and added new indices for GT_TOW
|
|
|
|
// Since 2.30, added booleans for liberation scoreboard toggle
|
|
|
|
//
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
assert(player);
|
|
|
|
|
|
|
|
scoreString[0] = 0;
|
|
|
|
scoreLength = 0;
|
|
|
|
scoreEntries = 0;
|
|
|
|
|
|
|
|
pDMTeam = NULL;
|
|
|
|
|
|
|
|
// make the winning team at top
|
|
|
|
if (g_gametype->integer >= GT_TEAM_ROUNDS) {
|
|
|
|
if (m_team_allies.m_teamwins <= m_team_axis.m_teamwins) {
|
|
|
|
if (m_team_axis.m_teamwins > m_team_allies.m_teamwins) {
|
|
|
|
pDMTeam = &m_team_axis;
|
|
|
|
} else if (m_team_allies.m_wins_in_a_row > m_team_axis.m_wins_in_a_row) {
|
|
|
|
pDMTeam = &m_team_allies;
|
|
|
|
} else if (m_team_axis.m_wins_in_a_row <= m_team_allies.m_wins_in_a_row) {
|
|
|
|
// make the player's current team at top
|
|
|
|
pDMTeam = player->GetDM_Team();
|
|
|
|
|
|
|
|
if (pDMTeam != &m_team_allies && pDMTeam != &m_team_axis) {
|
|
|
|
pDMTeam = &m_team_allies;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pDMTeam = &m_team_axis;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pDMTeam = &m_team_allies;
|
|
|
|
}
|
|
|
|
} else if (g_gametype->integer > GT_FFA) {
|
|
|
|
if (m_team_allies.m_iKills <= m_team_axis.m_iKills) {
|
|
|
|
if (m_team_axis.m_iKills > m_team_allies.m_iKills) {
|
|
|
|
pDMTeam = &m_team_axis;
|
|
|
|
} else if (m_team_allies.m_iDeaths > m_team_axis.m_iDeaths) {
|
|
|
|
pDMTeam = &m_team_allies;
|
|
|
|
} else if (m_team_axis.m_iDeaths <= m_team_allies.m_iDeaths) {
|
|
|
|
pDMTeam = player->GetDM_Team();
|
|
|
|
|
|
|
|
if (pDMTeam != &m_team_allies && pDMTeam != &m_team_axis) {
|
|
|
|
pDMTeam = &m_team_allies;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pDMTeam = &m_team_axis;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pDMTeam = &m_team_allies;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(iPlayerList, -1, sizeof(iPlayerList));
|
|
|
|
|
|
|
|
for (i = 1, j = 0; i <= PlayerCount(); i++) {
|
|
|
|
currentPlayer = GetPlayer(i);
|
|
|
|
|
|
|
|
if (!currentPlayer) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
iPlayerList[j] = currentPlayer->client->ps.clientNum;
|
|
|
|
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// sort players by kills
|
|
|
|
qsort(iPlayerList, j, sizeof(int), compareScores);
|
|
|
|
|
2023-08-16 02:05:44 +02:00
|
|
|
switch (g_gametype->integer) {
|
|
|
|
case GT_TOW:
|
|
|
|
{
|
|
|
|
char buffer[1024];
|
|
|
|
|
|
|
|
// send the number for all tow objectives
|
|
|
|
Com_sprintf(
|
|
|
|
buffer,
|
|
|
|
sizeof(buffer),
|
|
|
|
"%i %i %i %i %i %i %i %i %i %i ",
|
|
|
|
gi.Cvar_Get("tow_allied_obj1", "", 0)->integer,
|
|
|
|
gi.Cvar_Get("tow_allied_obj2", "", 0)->integer,
|
|
|
|
gi.Cvar_Get("tow_allied_obj3", "", 0)->integer,
|
|
|
|
gi.Cvar_Get("tow_allied_obj4", "", 0)->integer,
|
|
|
|
gi.Cvar_Get("tow_allied_obj5", "", 0)->integer,
|
|
|
|
gi.Cvar_Get("tow_axis_obj1", "", 0)->integer,
|
|
|
|
gi.Cvar_Get("tow_axis_obj2", "", 0)->integer,
|
|
|
|
gi.Cvar_Get("tow_axis_obj3", "", 0)->integer,
|
|
|
|
gi.Cvar_Get("tow_axis_obj4", "", 0)->integer,
|
|
|
|
gi.Cvar_Get("tow_axis_obj5", "", 0)->integer
|
|
|
|
);
|
|
|
|
|
|
|
|
InsertEntryNoCount(buffer);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case GT_LIBERATION:
|
|
|
|
{
|
|
|
|
char buffer[1024];
|
|
|
|
|
|
|
|
// scoreboard index
|
|
|
|
Com_sprintf(
|
|
|
|
buffer,
|
|
|
|
sizeof(buffer),
|
|
|
|
"%i %i ",
|
2023-08-19 03:00:19 +02:00
|
|
|
gi.Cvar_Get("scoreboard_toggle1", "", 0)->integer,
|
|
|
|
gi.Cvar_Get("scoreboard_toggle2", "", 0)->integer
|
2023-08-16 02:05:44 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
InsertEntryNoCount(buffer);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
// build team info
|
|
|
|
if (g_gametype->integer > GT_FFA) {
|
|
|
|
BuildTeamInfo(pDMTeam);
|
|
|
|
BuildPlayerTeamInfo(pDMTeam, iPlayerList);
|
|
|
|
|
|
|
|
// insert an empty entry to not make the scoreboard tight
|
|
|
|
InsertEmpty();
|
|
|
|
|
|
|
|
if (pDMTeam != &m_team_allies) {
|
|
|
|
BuildTeamInfo(&m_team_allies);
|
|
|
|
BuildPlayerTeamInfo(&m_team_allies, iPlayerList);
|
|
|
|
} else if (pDMTeam != &m_team_axis) {
|
|
|
|
BuildTeamInfo(&m_team_axis);
|
|
|
|
BuildPlayerTeamInfo(&m_team_axis, iPlayerList);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// client will only show "Players" in FFA
|
|
|
|
// BuildTeamInfo( &m_team_freeforall );
|
|
|
|
|
|
|
|
BuildPlayerTeamInfo(NULL, iPlayerList, &m_team_spectator);
|
|
|
|
}
|
|
|
|
|
|
|
|
// spectator is the last team in the scoreboard
|
|
|
|
if (m_team_spectator.m_players.NumObjects()) {
|
|
|
|
InsertEmpty();
|
|
|
|
|
|
|
|
BuildTeamInfo(&m_team_spectator);
|
|
|
|
BuildPlayerTeamInfo(&m_team_spectator, iPlayerList);
|
|
|
|
}
|
|
|
|
|
|
|
|
// send the info to the client
|
|
|
|
gi.SendServerCommand(player->client->ps.clientNum, "scores %i %s", scoreEntries, scoreString);
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
void DM_Manager::PrintAllClients(str s)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
gentity_t *ent;
|
|
|
|
int i;
|
|
|
|
Player *player;
|
|
|
|
|
|
|
|
if (game.maxclients <= 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0, ent = g_entities; i < game.maxclients; i++, ent++) {
|
|
|
|
if (!ent->inuse || !ent->client || !ent->entity) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
player = (Player *)ent->entity;
|
|
|
|
player->HUDPrint(s);
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
void DM_Manager::InitGame(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-09-23 19:37:55 +02:00
|
|
|
int i;
|
2023-08-10 02:31:01 +02:00
|
|
|
|
2023-09-23 19:37:55 +02:00
|
|
|
if (fraglimit) {
|
|
|
|
if (fraglimit->integer < 0) {
|
|
|
|
gi.cvar_set("fraglimit", "0");
|
|
|
|
}
|
|
|
|
if (fraglimit->integer > 10000) {
|
|
|
|
gi.cvar_set("fraglimit", "10000");
|
|
|
|
}
|
|
|
|
fraglimit = gi.Cvar_Get("fraglimit", "0", CVAR_SERVERINFO);
|
|
|
|
}
|
2023-08-10 02:31:01 +02:00
|
|
|
|
2023-09-23 19:37:55 +02:00
|
|
|
if (timelimit) {
|
|
|
|
if (timelimit->integer < 0) {
|
|
|
|
gi.cvar_set("timelimit", "0");
|
|
|
|
}
|
|
|
|
// 180 minutes maximum
|
|
|
|
if (timelimit->integer > 10800) {
|
|
|
|
gi.cvar_set("timelimit", "10800");
|
|
|
|
}
|
|
|
|
timelimit = gi.Cvar_Get("timelimit", "0", CVAR_SERVERINFO);
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
|
|
|
|
2023-09-23 19:37:55 +02:00
|
|
|
for (i = 1; i <= level.m_SimpleArchivedEntities.NumObjects(); i++) {
|
2024-02-05 21:03:10 +01:00
|
|
|
SimpleArchivedEntity *const ent = level.m_SimpleArchivedEntities.ObjectAt(i);
|
|
|
|
const char *const classname = ent->getClassID();
|
2023-09-23 19:37:55 +02:00
|
|
|
|
|
|
|
if (!Q_stricmp(classname, "info_player_deathmatch")) {
|
2024-02-05 21:03:10 +01:00
|
|
|
PlayerStart *const spawnpoint = static_cast<PlayerStart *>(ent);
|
2023-09-23 19:37:55 +02:00
|
|
|
m_team_spectator.m_spawnpoints.AddObject(spawnpoint);
|
|
|
|
m_team_freeforall.m_spawnpoints.AddObject(spawnpoint);
|
2023-08-10 02:31:01 +02:00
|
|
|
|
2023-09-23 19:37:55 +02:00
|
|
|
if (g_gametype->integer == GT_FFA) {
|
|
|
|
m_team_allies.m_spawnpoints.AddObject(spawnpoint);
|
|
|
|
m_team_axis.m_spawnpoints.AddObject(spawnpoint);
|
|
|
|
}
|
|
|
|
} else if (!Q_stricmp(classname, "info_player_allied")) {
|
|
|
|
if (g_gametype->integer >= GT_TEAM) {
|
2024-02-05 21:03:10 +01:00
|
|
|
PlayerStart *const spawnpoint = static_cast<PlayerStart *>(ent);
|
2023-09-23 19:37:55 +02:00
|
|
|
m_team_allies.m_spawnpoints.AddObject(spawnpoint);
|
|
|
|
}
|
|
|
|
} else if (!Q_stricmp(classname, "info_player_axis")) {
|
|
|
|
if (g_gametype->integer >= GT_TEAM) {
|
2024-02-05 21:03:10 +01:00
|
|
|
PlayerStart *const spawnpoint = static_cast<PlayerStart *>(ent);
|
2023-09-23 19:37:55 +02:00
|
|
|
m_team_axis.m_spawnpoints.AddObject(spawnpoint);
|
|
|
|
}
|
|
|
|
} else if (!Q_stricmp(classname, "info_player_intermission")) {
|
2024-02-05 21:03:10 +01:00
|
|
|
PlayerStart *const spawnpoint = static_cast<PlayerStart *>(ent);
|
2023-09-23 19:37:55 +02:00
|
|
|
m_team_freeforall.m_spawnpoints.AddObject(spawnpoint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-13 18:23:26 +01:00
|
|
|
if (g_gametype->integer > GT_SINGLE_PLAYER) {
|
|
|
|
if (g_gametype->integer < GT_MAX_GAME_TYPE) {
|
|
|
|
m_teams.ClearObjectList();
|
|
|
|
m_teams.AddObject(&m_team_spectator);
|
|
|
|
m_teams.AddObject(&m_team_allies);
|
|
|
|
m_teams.AddObject(&m_team_axis);
|
|
|
|
} else {
|
|
|
|
Com_Printf("Unknown game mode");
|
|
|
|
}
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
m_fRoundTime = 0;
|
|
|
|
m_fRoundEndTime = 0;
|
|
|
|
m_iTeamWin = 0;
|
2023-09-23 19:37:55 +02:00
|
|
|
m_bIgnoringClockForBomb = false;
|
|
|
|
m_iNumTargetsDestroyed = 0;
|
2023-08-10 02:31:01 +02:00
|
|
|
m_iNumBombsPlanted = 0;
|
|
|
|
|
|
|
|
if (g_gametype->integer >= 0 && g_gametype->integer < GT_MAX_GAME_TYPE) {
|
|
|
|
if (g_gametype->integer <= GT_TEAM) {
|
2023-08-19 19:34:12 +02:00
|
|
|
m_bAllowRespawns = true;
|
|
|
|
m_bRoundBasedGame = false;
|
2023-08-10 02:31:01 +02:00
|
|
|
} else {
|
2023-08-19 19:34:12 +02:00
|
|
|
if (g_gametype->integer == GT_TOW || g_gametype->integer == GT_LIBERATION) {
|
2024-02-05 21:03:10 +01:00
|
|
|
m_bAllowRespawns = true;
|
2023-08-19 19:34:12 +02:00
|
|
|
m_bRoundBasedGame = true;
|
|
|
|
} else {
|
2024-02-05 21:03:10 +01:00
|
|
|
m_bAllowRespawns = false;
|
2023-08-19 19:34:12 +02:00
|
|
|
m_bRoundBasedGame = true;
|
|
|
|
}
|
2023-08-10 02:31:01 +02:00
|
|
|
|
|
|
|
g_tempaxisscore = gi.Cvar_Get("g_tempaxisscore", "0", 0);
|
|
|
|
g_tempaxiswinsinrow = gi.Cvar_Get("g_tempaxiswinsinrow", "0", 0);
|
|
|
|
g_tempalliesscore = gi.Cvar_Get("g_tempalliesscore", "0", 0);
|
|
|
|
g_tempallieswinsinrow = gi.Cvar_Get("g_tempallieswinsinrow", "0", 0);
|
|
|
|
|
|
|
|
m_team_axis.m_teamwins = g_tempaxisscore->integer;
|
|
|
|
m_team_axis.m_wins_in_a_row = g_tempaxiswinsinrow->integer;
|
|
|
|
m_team_allies.m_teamwins = g_tempalliesscore->integer;
|
|
|
|
m_team_allies.m_wins_in_a_row = g_tempallieswinsinrow->integer;
|
|
|
|
|
2023-08-15 01:27:35 +02:00
|
|
|
gi.cvar_set("g_tempaxisscore", "0");
|
|
|
|
gi.cvar_set("g_tempaxiswinsinrow", "0");
|
|
|
|
gi.cvar_set("g_tempalliesscore", "0");
|
|
|
|
gi.cvar_set("g_tempaxiswinsinrow", "0");
|
2023-08-10 02:31:01 +02:00
|
|
|
|
|
|
|
m_iTotalMapTime = gi.Cvar_Get("g_tempmaptime", "0", 0)->integer;
|
2023-08-15 01:27:35 +02:00
|
|
|
gi.cvar_set("g_tempmaptime", "0");
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
bool DM_Manager::CheckEndMatch()
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-14 20:25:53 +02:00
|
|
|
if (fraglimit) {
|
|
|
|
if (fraglimit->integer < 0) {
|
2023-08-15 01:27:35 +02:00
|
|
|
gi.cvar_set("fraglimit", "0");
|
2023-08-14 20:25:53 +02:00
|
|
|
}
|
|
|
|
if (fraglimit->integer > 10000) {
|
2023-08-15 01:27:35 +02:00
|
|
|
gi.cvar_set("fraglimit", "10000");
|
2023-08-14 20:25:53 +02:00
|
|
|
}
|
2023-08-18 01:08:47 +02:00
|
|
|
fraglimit = gi.Cvar_Get("fraglimit", "0", 0);
|
2023-08-14 20:25:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (timelimit) {
|
|
|
|
if (timelimit->integer < 0) {
|
2023-08-15 01:27:35 +02:00
|
|
|
gi.cvar_set("timelimit", "0");
|
2023-08-14 20:25:53 +02:00
|
|
|
}
|
|
|
|
// 180 minutes maximum
|
|
|
|
if (timelimit->integer > 10800) {
|
2023-08-15 01:27:35 +02:00
|
|
|
gi.cvar_set("timelimit", "10800");
|
2023-08-14 20:25:53 +02:00
|
|
|
}
|
2023-08-18 01:08:47 +02:00
|
|
|
timelimit = gi.Cvar_Get("timelimit", "0", 0);
|
2023-08-14 20:25:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_bRoundBasedGame || g_gametype->integer == GT_TOW || g_gametype->integer == GT_LIBERATION) {
|
|
|
|
if (g_gametype->integer == GT_TOW) {
|
|
|
|
cvar_t *g_TOW_winstate = gi.Cvar_Get("g_TOW_winstate", "", 0);
|
|
|
|
if (!g_TOW_winstate || !g_TOW_winstate->integer) {
|
|
|
|
int roundLimit = GetRoundLimit();
|
|
|
|
|
|
|
|
if (!level.m_bIgnoreClock && roundLimit > 0 && level.time >= m_iDefaultRoundLimit * 60 + m_fRoundTime) {
|
|
|
|
switch (m_csTeamClockSide) {
|
|
|
|
case STRING_AXIS:
|
2023-08-15 01:27:35 +02:00
|
|
|
gi.cvar_set("g_TOW_winstate", "1");
|
2023-08-14 20:25:53 +02:00
|
|
|
TeamWin(TEAM_AXIS);
|
|
|
|
break;
|
|
|
|
case STRING_ALLIES:
|
2023-08-15 01:27:35 +02:00
|
|
|
gi.cvar_set("g_TOW_winstate", "2");
|
2023-08-14 20:25:53 +02:00
|
|
|
TeamWin(TEAM_ALLIES);
|
|
|
|
break;
|
|
|
|
default:
|
2023-08-15 01:27:35 +02:00
|
|
|
gi.cvar_set("g_TOW_winstate", "3");
|
2023-08-14 20:25:53 +02:00
|
|
|
TeamWin(TEAM_NONE);
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
2023-08-14 20:25:53 +02:00
|
|
|
|
|
|
|
return true;
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
2023-08-14 20:25:53 +02:00
|
|
|
|
|
|
|
if (m_team_allies.IsDead()) {
|
2023-08-15 01:27:35 +02:00
|
|
|
gi.cvar_set("g_TOW_winstate", "1");
|
2023-08-14 20:25:53 +02:00
|
|
|
TeamWin(TEAM_AXIS);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_team_axis.IsDead()) {
|
2023-08-15 01:27:35 +02:00
|
|
|
gi.cvar_set("g_TOW_winstate", "2");
|
2023-08-14 20:25:53 +02:00
|
|
|
TeamWin(TEAM_ALLIES);
|
|
|
|
return true;
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
|
|
|
}
|
2023-08-14 20:25:53 +02:00
|
|
|
|
|
|
|
if (fraglimit->integer && TeamHitScoreLimit()) {
|
|
|
|
G_BeginIntermission2();
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (g_gametype->integer == GT_LIBERATION) {
|
|
|
|
if (fraglimit->integer && TeamHitScoreLimit()) {
|
2023-08-10 02:31:01 +02:00
|
|
|
G_BeginIntermission2();
|
|
|
|
return true;
|
2023-08-14 20:25:53 +02:00
|
|
|
} else {
|
|
|
|
return false;
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fraglimit->integer) {
|
2023-08-14 20:25:53 +02:00
|
|
|
if (g_gametype->integer >= GT_TEAM) {
|
|
|
|
if (TeamHitScoreLimit()) {
|
|
|
|
G_BeginIntermission2();
|
|
|
|
return true;
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
2023-08-14 20:25:53 +02:00
|
|
|
} else if (PlayerHitScoreLimit()) {
|
|
|
|
G_BeginIntermission2();
|
|
|
|
return true;
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
2024-09-09 22:19:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (timelimit->integer && level.inttime >= timelimit->integer * 60000) {
|
2023-08-14 20:25:53 +02:00
|
|
|
G_BeginIntermission2();
|
|
|
|
return true;
|
2023-08-10 02:31:01 +02:00
|
|
|
} else {
|
2023-08-14 20:25:53 +02:00
|
|
|
return false;
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
2023-08-14 20:25:53 +02:00
|
|
|
}
|
2023-08-10 02:31:01 +02:00
|
|
|
|
2023-08-14 20:25:53 +02:00
|
|
|
if (m_fRoundEndTime > 0.0f) {
|
|
|
|
return true;
|
|
|
|
}
|
2023-08-10 02:31:01 +02:00
|
|
|
|
2023-08-14 20:25:53 +02:00
|
|
|
if (m_fRoundTime <= 0.f) {
|
|
|
|
return false;
|
|
|
|
}
|
2023-08-10 02:31:01 +02:00
|
|
|
|
2023-08-14 20:25:53 +02:00
|
|
|
if (fraglimit->integer && TeamHitScoreLimit()) {
|
|
|
|
G_BeginIntermission2();
|
|
|
|
return true;
|
|
|
|
}
|
2023-08-10 02:31:01 +02:00
|
|
|
|
2023-08-14 20:25:53 +02:00
|
|
|
if (AllowRespawn() || (!m_team_axis.IsDead() && !m_team_allies.IsDead())) {
|
|
|
|
int roundLimit = GetRoundLimit();
|
2023-08-10 02:31:01 +02:00
|
|
|
|
2023-08-14 20:25:53 +02:00
|
|
|
if (roundLimit > 0 && level.time >= m_iDefaultRoundLimit * 60 + m_fRoundTime) {
|
2023-08-10 02:31:01 +02:00
|
|
|
if (m_csTeamBombPlantSide != STRING_DRAW) {
|
|
|
|
if (m_bIgnoringClockForBomb) {
|
|
|
|
if (m_iNumBombsPlanted > 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_bIgnoringClockForBomb = false;
|
|
|
|
} else if (m_iNumBombsPlanted > 0) {
|
|
|
|
G_PrintToAllClients("A bomb is still set!");
|
|
|
|
m_bIgnoringClockForBomb = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-14 20:25:53 +02:00
|
|
|
switch (m_csTeamClockSide) {
|
|
|
|
case STRING_ALLIES:
|
2023-08-10 02:31:01 +02:00
|
|
|
TeamWin(TEAM_ALLIES);
|
|
|
|
return true;
|
2023-08-14 20:25:53 +02:00
|
|
|
case STRING_AXIS:
|
2023-08-10 02:31:01 +02:00
|
|
|
TeamWin(TEAM_AXIS);
|
|
|
|
return true;
|
2023-08-14 20:25:53 +02:00
|
|
|
case STRING_KILLS:
|
2023-08-10 02:31:01 +02:00
|
|
|
if (m_team_allies.TotalPlayersKills() > m_team_axis.TotalPlayersKills()) {
|
|
|
|
TeamWin(TEAM_ALLIES);
|
|
|
|
} else if (m_team_axis.TotalPlayersKills() > m_team_allies.TotalPlayersKills()) {
|
|
|
|
TeamWin(TEAM_AXIS);
|
|
|
|
} else {
|
|
|
|
TeamWin(TEAM_NONE);
|
|
|
|
}
|
2023-08-14 20:25:53 +02:00
|
|
|
return true;
|
|
|
|
default:
|
2023-08-10 02:31:01 +02:00
|
|
|
TeamWin(TEAM_NONE);
|
2023-08-14 20:25:53 +02:00
|
|
|
return true;
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
|
|
|
}
|
2023-08-14 20:25:53 +02:00
|
|
|
} else {
|
|
|
|
DM_Team *pBombTeam;
|
|
|
|
DM_Team *pNonBombTeam;
|
2023-08-10 02:31:01 +02:00
|
|
|
|
2023-08-14 20:25:53 +02:00
|
|
|
if (g_gametype->integer != GT_OBJECTIVE) {
|
|
|
|
EndRound();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_csTeamBombPlantSide == STRING_DRAW) {
|
|
|
|
EndRound();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_csTeamBombPlantSide == STRING_AXIS) {
|
|
|
|
pBombTeam = &m_team_axis;
|
|
|
|
pNonBombTeam = &m_team_allies;
|
|
|
|
} else {
|
|
|
|
pBombTeam = &m_team_allies;
|
|
|
|
pNonBombTeam = &m_team_axis;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pBombTeam->IsDead()) {
|
|
|
|
EndRound();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_iNumBombsPlanted <= 0) {
|
|
|
|
m_bIgnoringClockForBomb = qfalse;
|
|
|
|
|
|
|
|
if (pNonBombTeam->IsDead() && m_iNumTargetsDestroyed < m_iNumTargetsToDestroy) {
|
|
|
|
TeamWin(pNonBombTeam->m_teamnumber);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
EndRound();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (pNonBombTeam->IsDead()) {
|
|
|
|
if (m_iNumBombsPlanted >= m_iNumTargetsToDestroy - m_iNumTargetsDestroyed) {
|
|
|
|
TeamWin(pBombTeam->m_teamnumber);
|
|
|
|
} else {
|
|
|
|
TeamWin(pNonBombTeam->m_teamnumber);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
} else if (m_iNumBombsPlanted >= m_iNumTargetsToDestroy - m_iNumTargetsDestroyed) {
|
|
|
|
if (!m_bIgnoringClockForBomb) {
|
|
|
|
G_PrintToAllClients("A bomb is still set!");
|
|
|
|
m_bIgnoringClockForBomb = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
EndRound();
|
|
|
|
return true;
|
|
|
|
}
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
bool DM_Manager::TeamHitScoreLimit(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
if (m_teams.NumObjects() < 1) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 1; i <= m_teams.NumObjects(); i++) {
|
2023-08-19 19:34:12 +02:00
|
|
|
if (m_teams.ObjectAt(i)->m_teamwins >= fraglimit->integer) {
|
2023-08-10 02:31:01 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
bool DM_Manager::PlayerHitScoreLimit(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
if (PlayerCount() < 1) {
|
|
|
|
return false;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
if (g_gametype->integer >= GT_TEAM_ROUNDS) {
|
|
|
|
return false;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
for (int i = 1; i <= PlayerCount(); i++) {
|
|
|
|
if (m_players.ObjectAt(i)->GetNumKills() >= fraglimit->integer) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
return false;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
void DM_Manager::EventDoRoundTransition(Event *ev)
|
|
|
|
{
|
|
|
|
if (!m_iTeamWin) {
|
|
|
|
if (G_FindClass(NULL, "projectile")) {
|
|
|
|
// wait for any projectile to explode
|
|
|
|
PostEvent(EV_DM_Manager_DoRoundTransition, 1.0f, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_team_allies.IsDead() && !m_team_axis.IsDead()) {
|
|
|
|
if (m_csTeamClockSide != STRING_KILLS) {
|
|
|
|
if (m_csTeamClockSide != STRING_DRAW) {
|
|
|
|
gi.Printf(
|
|
|
|
"WARNING: DM_Manager::EventDoRoundTransition received but no winner could be determined when "
|
|
|
|
"there should've been\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
TeamWin(TEAM_NONE);
|
|
|
|
} else {
|
|
|
|
if (m_team_allies.TotalPlayersKills() > m_team_axis.TotalPlayersKills()) {
|
|
|
|
TeamWin(TEAM_ALLIES);
|
|
|
|
} else if (m_team_axis.TotalPlayersKills() > m_team_allies.TotalPlayersKills()) {
|
|
|
|
TeamWin(TEAM_AXIS);
|
|
|
|
} else {
|
|
|
|
TeamWin(TEAM_NONE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (m_team_allies.IsDead() && m_team_axis.IsDead()) {
|
|
|
|
TeamWin(TEAM_NONE);
|
|
|
|
} else if (m_team_axis.IsDead()) {
|
|
|
|
TeamWin(TEAM_ALLIES);
|
|
|
|
} else {
|
|
|
|
TeamWin(TEAM_AXIS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_iTeamWin == TEAM_AXIS) {
|
|
|
|
G_CenterPrintToAllClients(va("\n\n\n%s\n", gi.LV_ConvertString("Axis win!\n")));
|
2023-08-18 15:10:35 +02:00
|
|
|
G_PrintToAllClients(va("%s\n", gi.LV_ConvertString("Axis win!\n")));
|
2023-08-10 02:31:01 +02:00
|
|
|
|
|
|
|
// Play the axis victory sound
|
|
|
|
world->Sound("den_victory_v");
|
|
|
|
|
|
|
|
Unregister(STRING_AXISWIN);
|
|
|
|
} else if (m_iTeamWin == TEAM_ALLIES) {
|
|
|
|
G_CenterPrintToAllClients(va("\n\n\n%s\n", gi.LV_ConvertString("Allies win!\n")));
|
2023-08-19 19:34:12 +02:00
|
|
|
G_PrintToAllClients(va("%s\n", gi.LV_ConvertString("Allies win!\n")));
|
2023-08-10 02:31:01 +02:00
|
|
|
|
|
|
|
// Play the allies victory sound
|
|
|
|
world->Sound("dfr_victory_v");
|
|
|
|
|
|
|
|
Unregister(STRING_ALLIESWIN);
|
|
|
|
} else {
|
|
|
|
G_CenterPrintToAllClients(va("\n\n\n%s\n", gi.LV_ConvertString("It's a draw!\n")));
|
2023-08-18 15:10:35 +02:00
|
|
|
G_PrintToAllClients(va("%s\n", gi.LV_ConvertString("It's a draw!\n")));
|
2023-08-10 02:31:01 +02:00
|
|
|
|
|
|
|
Unregister(STRING_DRAW);
|
|
|
|
}
|
|
|
|
|
|
|
|
G_DisplayScoresToAllClients();
|
|
|
|
PostEvent(EV_DM_Manager_FinishRoundTransition, 3.0f);
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
void DM_Manager::EventFinishRoundTransition(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
gentity_t *ent;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
CancelEventsOfType(EV_DM_Manager_FinishRoundTransition);
|
|
|
|
|
|
|
|
if (timelimit->integer && m_iTotalMapTime + level.inttime >= 60000 * timelimit->integer) {
|
|
|
|
G_BeginIntermission2();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-08-15 01:27:35 +02:00
|
|
|
gi.cvar_set("g_tempaxisscore", va("%d", m_team_axis.m_teamwins));
|
|
|
|
gi.cvar_set("g_tempaxiswinsinrow", va("%d", m_team_axis.m_wins_in_a_row));
|
|
|
|
gi.cvar_set("g_tempalliesscore", va("%d", m_team_allies.m_teamwins));
|
|
|
|
gi.cvar_set("g_tempallieswinsinrow", va("%d", m_team_allies.m_wins_in_a_row));
|
|
|
|
gi.cvar_set("g_tempmaptime", va("%d", m_iTotalMapTime + level.inttime));
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
for (i = 0, ent = g_entities; i < game.maxclients; ent++, i++) {
|
|
|
|
if (!ent->inuse || !ent->entity) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-08-10 13:34:04 +02:00
|
|
|
Player *p = (Player *)ent->entity;
|
|
|
|
p->client->pers.round_kills = p->GetNumDeaths();
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (g_gametype->integer == GT_TOW) {
|
2023-08-16 02:05:44 +02:00
|
|
|
g_TOWObjectiveMan.Reset();
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
gi.SendConsoleCommand("restart\n");
|
|
|
|
|
2023-09-23 19:37:55 +02:00
|
|
|
g_teamSpawnClock.Reset();
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int DM_Manager::PlayerCount(void) const
|
|
|
|
{
|
|
|
|
return m_players.NumObjects();
|
|
|
|
}
|
|
|
|
|
|
|
|
Player *DM_Manager::GetPlayer(int index) const
|
|
|
|
{
|
|
|
|
return m_players.ObjectAt(index);
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
teamtype_t DM_Manager::GetAutoJoinTeam(void)
|
|
|
|
{
|
|
|
|
int allies = m_team_allies.m_players.NumObjects();
|
|
|
|
int axis = m_team_axis.m_players.NumObjects();
|
|
|
|
|
|
|
|
if (allies < axis) {
|
|
|
|
return TEAM_ALLIES;
|
|
|
|
} else if (allies == axis) {
|
|
|
|
return (G_Random() >= 0.5f) ? TEAM_ALLIES : TEAM_AXIS;
|
|
|
|
} else {
|
|
|
|
return TEAM_AXIS;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
void DM_Manager::TeamWin(int teamnum)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
DM_Team *pTeamWin;
|
|
|
|
DM_Team *pTeamLose;
|
|
|
|
|
|
|
|
if (m_iTeamWin) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (teamnum == TEAM_AXIS) {
|
|
|
|
pTeamWin = &m_team_axis;
|
|
|
|
pTeamLose = &m_team_allies;
|
|
|
|
} else if (teamnum == TEAM_ALLIES) {
|
|
|
|
pTeamWin = &m_team_allies;
|
|
|
|
pTeamLose = &m_team_axis;
|
|
|
|
} else {
|
|
|
|
pTeamWin = NULL;
|
|
|
|
pTeamLose = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pTeamWin) {
|
|
|
|
pTeamWin->TeamWin();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pTeamLose) {
|
|
|
|
pTeamLose->TeamLoss();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_iTeamWin = teamnum ? teamnum : -1;
|
|
|
|
EndRound();
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
void DM_Manager::StartRound(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
gentity_t *ent;
|
|
|
|
int i;
|
|
|
|
Player *player;
|
|
|
|
|
|
|
|
m_fRoundTime = level.time;
|
|
|
|
if (m_fRoundTime < 0.1f) {
|
|
|
|
m_fRoundTime = 0.1f;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_fRoundEndTime = 0.0f;
|
2024-02-05 21:03:10 +01:00
|
|
|
m_bRoundActive = true;
|
2023-08-10 02:31:01 +02:00
|
|
|
|
|
|
|
// respawn all players
|
|
|
|
for (i = 0, ent = g_entities; i < game.maxclients; i++, ent++) {
|
|
|
|
if (!ent->inuse || !ent->client || !ent->entity) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
player = (Player *)ent->entity;
|
|
|
|
|
|
|
|
if ((player->GetTeam() == TEAM_ALLIES || player->GetTeam() == TEAM_AXIS) && !player->IsDead()
|
|
|
|
&& !player->IsSpectator()) {
|
|
|
|
player->PostEvent(EV_Player_Respawn, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-20 01:43:37 +02:00
|
|
|
level.RemoveWaitTill(STRING_ROUNDSTART);
|
2023-08-19 18:20:26 +02:00
|
|
|
level.Unregister(STRING_ROUNDSTART);
|
2023-08-15 01:27:35 +02:00
|
|
|
gi.setConfigstring(CS_WARMUP, va("%.0f", GetMatchStartTime()));
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
void DM_Manager::EndRound()
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-09-23 19:37:55 +02:00
|
|
|
m_bRoundActive = false;
|
|
|
|
|
|
|
|
if (m_fRoundEndTime <= 0) {
|
2023-08-10 02:31:01 +02:00
|
|
|
m_fRoundEndTime = level.time;
|
2023-09-23 19:37:55 +02:00
|
|
|
PostEvent(EV_DM_Manager_DoRoundTransition, 2);
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
bool DM_Manager::AllowRespawn() const
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-09-23 19:49:44 +02:00
|
|
|
if (GameAllowsRespawns()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_gametype->integer <= GT_TEAM) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_team_axis.m_players.NumObjects() && !m_team_axis.m_bHasSpawnedPlayers) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_team_allies.m_players.NumObjects() && !m_team_allies.m_bHasSpawnedPlayers) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
bool DM_Manager::WaitingForPlayers(void) const
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
if (g_gametype->integer <= GT_TEAM) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-09-23 19:37:55 +02:00
|
|
|
if (m_team_axis.IsEmpty() || m_team_allies.IsEmpty()) {
|
2023-08-10 02:31:01 +02:00
|
|
|
return true;
|
2023-09-23 19:37:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_team_axis.IsReady() || !m_team_allies.IsReady()) {
|
2023-08-10 02:31:01 +02:00
|
|
|
return true;
|
2023-09-23 19:37:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (m_team_axis.IsDead() || m_team_allies.IsDead()) {
|
2023-08-10 02:31:01 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
bool DM_Manager::IsGameActive(void) const
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-09-23 19:49:44 +02:00
|
|
|
return !GameHasRounds() || m_fRoundTime > 0;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
float DM_Manager::GetMatchStartTime(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-09-23 19:49:44 +02:00
|
|
|
int totalnotready;
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
if (g_gametype->integer <= GT_TEAM) {
|
|
|
|
return m_fRoundTime;
|
|
|
|
}
|
|
|
|
|
2024-02-05 21:03:10 +01:00
|
|
|
if (g_gametype->integer == GT_TEAM_ROUNDS || g_gametype->integer == GT_OBJECTIVE
|
|
|
|
|| g_gametype->integer == GT_LIBERATION) {
|
2023-09-23 19:49:44 +02:00
|
|
|
if (m_fRoundTime > 0 && (m_team_allies.IsEmpty() || m_team_allies.IsEmpty())) {
|
|
|
|
m_fRoundTime = 0;
|
|
|
|
return -1;
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
2023-09-23 19:49:44 +02:00
|
|
|
}
|
2023-08-10 02:31:01 +02:00
|
|
|
|
2023-09-23 19:49:44 +02:00
|
|
|
if (m_fRoundTime > 0) {
|
|
|
|
return m_fRoundTime;
|
|
|
|
}
|
2023-08-10 02:31:01 +02:00
|
|
|
|
2023-09-23 19:49:44 +02:00
|
|
|
if (m_team_allies.IsEmpty() || m_team_axis.IsEmpty()) {
|
|
|
|
return -1;
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
|
|
|
|
2023-09-23 19:49:44 +02:00
|
|
|
totalnotready = m_team_allies.NumNotReady() + m_team_axis.NumNotReady();
|
|
|
|
if (totalnotready > 0) {
|
|
|
|
return -1 - totalnotready;
|
|
|
|
} else {
|
|
|
|
return m_fRoundTime;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
int DM_Manager::GetRoundLimit() const
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
int round_limit = roundlimit->integer;
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
if (!round_limit) {
|
|
|
|
round_limit = m_iDefaultRoundLimit;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
return round_limit;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
void DM_Manager::SetDefaultRoundLimit(int roundlimit)
|
|
|
|
{
|
|
|
|
m_iDefaultRoundLimit = roundlimit;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
const_str DM_Manager::GetClockSide(void) const
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
return m_csTeamClockSide;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DM_Manager::SetClockSide(const_str s)
|
|
|
|
{
|
|
|
|
m_csTeamClockSide = s;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
const_str DM_Manager::GetBombPlantTeam(void) const
|
|
|
|
{
|
|
|
|
return m_csTeamBombPlantSide;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
void DM_Manager::SetBombPlantTeam(const_str s)
|
|
|
|
{
|
|
|
|
m_csTeamBombPlantSide = s;
|
|
|
|
}
|
|
|
|
|
|
|
|
int DM_Manager::GetTargetsToDestroy(void) const
|
|
|
|
{
|
|
|
|
return m_iNumTargetsToDestroy;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
void DM_Manager::SetTargetsToDestroy(int targets)
|
|
|
|
{
|
|
|
|
m_iNumTargetsToDestroy = targets;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
int DM_Manager::GetTargetsDestroyed(void) const
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
return m_iNumTargetsDestroyed;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
void DM_Manager::SetTargetsDestroyed(int targets)
|
|
|
|
{
|
|
|
|
m_iNumTargetsDestroyed = targets;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
int DM_Manager::GetBombsPlanted(void) const
|
|
|
|
{
|
|
|
|
return m_iNumBombsPlanted;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
void DM_Manager::SetBombsPlanted(int num)
|
|
|
|
{
|
|
|
|
m_iNumBombsPlanted = num;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
void DM_Manager::StopTeamRespawn(eController controller)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
if (controller == CONTROLLER_AXIS) {
|
|
|
|
m_bAllowAxisRespawn = false;
|
2023-08-14 19:21:07 +02:00
|
|
|
} else if (controller == CONTROLLER_ALLIES) {
|
2023-08-10 02:31:01 +02:00
|
|
|
m_bAllowAlliedRespawn = false;
|
|
|
|
}
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
bool DM_Manager::AllowTeamRespawn(int teamnum) const
|
|
|
|
{
|
|
|
|
if (teamnum == TEAM_ALLIES) {
|
|
|
|
return m_bAllowAlliedRespawn;
|
|
|
|
} else if (teamnum == TEAM_AXIS) {
|
|
|
|
return m_bAllowAxisRespawn;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
return false;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
int DM_Manager::GetTeamSpawnTimeLeft() const
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 23:06:03 +02:00
|
|
|
if (sv_team_spawn_interval->integer <= 0) {
|
2023-08-10 02:31:01 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
if (g_gametype->integer != GT_TOW && (g_gametype->integer > GT_TOW || g_gametype->integer != GT_TEAM)) {
|
|
|
|
return 0;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-09-23 19:37:55 +02:00
|
|
|
if (g_gametype->integer == GT_TEAM || g_gametype->integer == GT_TOW || g_gametype->integer == GT_LIBERATION) {
|
|
|
|
return g_teamSpawnClock.GetSecondsLeft();
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-09-23 19:37:55 +02:00
|
|
|
return 0;
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
DM_Team *DM_Manager::GetTeam(str name)
|
|
|
|
{
|
|
|
|
if (name.icmp("spectator") == 0) {
|
|
|
|
return &m_team_spectator;
|
|
|
|
} else if (name.icmp("freeforall") == 0) {
|
|
|
|
return &m_team_freeforall;
|
|
|
|
} else if (name.icmp("allies") == 0) {
|
|
|
|
return &m_team_allies;
|
|
|
|
} else if (name.icmp("axis") == 0) {
|
|
|
|
return &m_team_axis;
|
|
|
|
} else {
|
|
|
|
ScriptError("Invalid team %s !\n", name.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
DM_Team *DM_Manager::GetTeam(teamtype_t team)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
switch (team) {
|
|
|
|
case TEAM_NONE:
|
|
|
|
case TEAM_SPECTATOR:
|
|
|
|
return &m_team_spectator;
|
|
|
|
|
|
|
|
case TEAM_FREEFORALL:
|
|
|
|
return &m_team_freeforall;
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
case TEAM_ALLIES:
|
|
|
|
return &m_team_allies;
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
case TEAM_AXIS:
|
|
|
|
return &m_team_axis;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DM_Manager::InsertEntry(const char *entry)
|
|
|
|
{
|
|
|
|
size_t len = strlen(entry);
|
|
|
|
|
|
|
|
if (scoreLength + len < MAX_STRING_CHARS) {
|
2024-09-20 21:53:48 +02:00
|
|
|
Q_strncpyz(scoreString + scoreLength, entry, sizeof(scoreString) - scoreLength);
|
2023-08-10 02:31:01 +02:00
|
|
|
|
|
|
|
scoreLength += len;
|
|
|
|
scoreEntries++;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-02-05 21:03:10 +01:00
|
|
|
void DM_Manager::InsertEntryNoCount(const char *entry)
|
2023-08-16 02:05:44 +02:00
|
|
|
{
|
2024-02-05 21:03:10 +01:00
|
|
|
size_t len = strlen(entry);
|
2023-08-16 02:05:44 +02:00
|
|
|
|
2024-02-05 21:03:10 +01:00
|
|
|
if (scoreLength + len < MAX_STRING_CHARS) {
|
2024-09-20 21:53:48 +02:00
|
|
|
Q_strncpyz(scoreString + scoreLength, entry, sizeof(scoreString) - scoreLength);
|
2023-08-16 02:05:44 +02:00
|
|
|
|
2024-02-05 21:03:10 +01:00
|
|
|
scoreLength += len;
|
|
|
|
}
|
2023-08-16 02:05:44 +02:00
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
void DM_Manager::InsertEmpty(void)
|
|
|
|
{
|
|
|
|
if (g_gametype->integer > GT_FFA) {
|
|
|
|
InsertEntry("-2 \"\" \"\" \"\" \"\" \"\" ");
|
|
|
|
} else {
|
|
|
|
InsertEntry("-1 \"\" \"\" \"\" \"\" ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DM_Manager::BuildTeamInfo(DM_Team *dmTeam)
|
2023-08-16 02:05:44 +02:00
|
|
|
{
|
|
|
|
if (g_protocol >= protocol_e::PROTOCOL_MOHTA_MIN) {
|
|
|
|
BuildTeamInfo_ver15(dmTeam);
|
|
|
|
} else {
|
|
|
|
BuildTeamInfo_ver6(dmTeam);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DM_Manager::BuildTeamInfo_ver6(DM_Team *dmTeam)
|
2023-08-10 02:31:01 +02:00
|
|
|
{
|
|
|
|
int iPing = 0;
|
|
|
|
int iKills;
|
|
|
|
int iDeaths;
|
|
|
|
int iNumPlayers = 0;
|
|
|
|
Player *pTeamPlayer;
|
|
|
|
char entry[MAX_STRING_TOKENS];
|
|
|
|
|
|
|
|
for (int i = iNumPlayers; i > 0; i--) {
|
|
|
|
pTeamPlayer = dmTeam->m_players.ObjectAt(i);
|
|
|
|
|
|
|
|
if (pTeamPlayer->IsSubclassOfBot()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
iNumPlayers++;
|
|
|
|
iPing += pTeamPlayer->client->ps.ping;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iNumPlayers > 0) {
|
|
|
|
iPing /= iNumPlayers;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_gametype->integer >= GT_TEAM_ROUNDS) {
|
|
|
|
iKills = dmTeam->m_wins_in_a_row;
|
|
|
|
iDeaths = dmTeam->m_teamwins;
|
|
|
|
} else {
|
|
|
|
iKills = dmTeam->m_iKills;
|
|
|
|
iDeaths = dmTeam->m_iDeaths;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_gametype->integer > GT_FFA) {
|
|
|
|
if (dmTeam->m_teamnumber > TEAM_FREEFORALL) {
|
|
|
|
Com_sprintf(entry, sizeof(entry), "%i %i %i %i \"\" %i ", -1, dmTeam->m_teamnumber, iKills, iDeaths, iPing);
|
|
|
|
} else {
|
|
|
|
Com_sprintf(entry, sizeof(entry), "%i %i \"\" \"\" \"\" \"\" ", -1, dmTeam->m_teamnumber);
|
|
|
|
}
|
|
|
|
} else {
|
2024-02-21 19:37:49 +01:00
|
|
|
Com_sprintf(entry, sizeof(entry), "%i \"\" \"\" \"\" \"\" ", -1 - dmTeam->m_teamnumber);
|
2023-08-10 02:31:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
InsertEntry(entry);
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-08-16 02:05:44 +02:00
|
|
|
void DM_Manager::BuildTeamInfo_ver15(DM_Team *dmTeam)
|
|
|
|
{
|
|
|
|
int iPing = 0;
|
|
|
|
int iKills;
|
|
|
|
int iDeaths;
|
|
|
|
int iNumPlayers = 0;
|
|
|
|
Player *pTeamPlayer;
|
|
|
|
char entry[MAX_STRING_TOKENS];
|
|
|
|
|
|
|
|
for (int i = iNumPlayers; i > 0; i--) {
|
|
|
|
pTeamPlayer = dmTeam->m_players.ObjectAt(i);
|
|
|
|
|
|
|
|
if (pTeamPlayer->IsSubclassOfBot()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
iNumPlayers++;
|
|
|
|
iPing += pTeamPlayer->client->ps.ping;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iNumPlayers > 0) {
|
|
|
|
iPing /= iNumPlayers;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_gametype->integer >= GT_TEAM_ROUNDS) {
|
|
|
|
iKills = dmTeam->m_wins_in_a_row;
|
|
|
|
iDeaths = dmTeam->m_teamwins;
|
|
|
|
} else {
|
|
|
|
iKills = dmTeam->m_iKills;
|
|
|
|
iDeaths = dmTeam->m_iDeaths;
|
|
|
|
}
|
|
|
|
|
2023-08-20 13:27:09 +02:00
|
|
|
if (g_gametype->integer >= GT_TEAM) {
|
2023-08-16 02:05:44 +02:00
|
|
|
if (dmTeam->m_teamnumber > TEAM_FREEFORALL) {
|
|
|
|
Com_sprintf(
|
|
|
|
entry,
|
|
|
|
sizeof(entry),
|
|
|
|
"%i %i %i %i %i \"\" %i ",
|
|
|
|
-1,
|
|
|
|
dmTeam->m_teamnumber,
|
|
|
|
dmTeam->m_players.NumObjects(),
|
|
|
|
iKills,
|
|
|
|
iDeaths,
|
|
|
|
iPing
|
|
|
|
);
|
|
|
|
} else {
|
2024-02-05 21:03:10 +01:00
|
|
|
Com_sprintf(entry, sizeof(entry), "%i %i \"\" \"\" \"\" \"\" ", -1, dmTeam->m_teamnumber);
|
2023-08-16 02:05:44 +02:00
|
|
|
}
|
|
|
|
} else {
|
2023-08-20 13:27:09 +02:00
|
|
|
Com_sprintf(entry, sizeof(entry), "%i \"\" \"\" \"\" \"\" ", -1 - dmTeam->m_teamnumber);
|
2023-08-16 02:05:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
InsertEntry(entry);
|
|
|
|
}
|
|
|
|
|
2023-08-10 02:31:01 +02:00
|
|
|
void DM_Manager::BuildPlayerTeamInfo(DM_Team *dmTeam, int *iPlayerList, DM_Team *ignoreTeam)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2023-08-10 02:31:01 +02:00
|
|
|
char entry[MAX_STRING_CHARS];
|
|
|
|
Player *pTeamPlayer;
|
|
|
|
|
|
|
|
for (int i = 0; i < game.maxclients; i++) {
|
|
|
|
if (iPlayerList[i] == -1) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
pTeamPlayer = (Player *)G_GetEntity(iPlayerList[i]);
|
|
|
|
|
|
|
|
if (dmTeam != NULL && pTeamPlayer->GetDM_Team() != dmTeam) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ignoreTeam != NULL && pTeamPlayer->GetDM_Team() == ignoreTeam) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_gametype->integer >= GT_TEAM) {
|
|
|
|
Com_sprintf(
|
|
|
|
entry,
|
|
|
|
sizeof(entry),
|
|
|
|
"%i %i %i %i %s %s ",
|
|
|
|
pTeamPlayer->client->ps.clientNum,
|
2023-08-16 02:05:44 +02:00
|
|
|
IsAlivePlayer(pTeamPlayer) ? pTeamPlayer->GetTeam()
|
|
|
|
: -pTeamPlayer->GetTeam(), // negative team means death
|
2023-08-10 02:31:01 +02:00
|
|
|
pTeamPlayer->GetNumKills(),
|
|
|
|
pTeamPlayer->GetNumDeaths(),
|
|
|
|
G_TimeString(level.svsFloatTime - pTeamPlayer->edict->client->pers.enterTime),
|
|
|
|
pTeamPlayer->IsSubclassOfBot() ? "bot" : va("%d", pTeamPlayer->client->ps.ping)
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
Com_sprintf(
|
|
|
|
entry,
|
|
|
|
sizeof(entry),
|
|
|
|
"%i %i %i %s %s ",
|
|
|
|
pTeamPlayer->client->ps.clientNum,
|
|
|
|
pTeamPlayer->GetNumKills(),
|
|
|
|
pTeamPlayer->GetNumDeaths(),
|
|
|
|
G_TimeString(level.svsFloatTime - pTeamPlayer->edict->client->pers.enterTime),
|
|
|
|
pTeamPlayer->IsSubclassOfBot() ? "bot" : va("%d", pTeamPlayer->client->ps.ping)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
InsertEntry(entry);
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
2023-08-16 02:05:44 +02:00
|
|
|
|
|
|
|
bool DM_Manager::IsAlivePlayer(Player *player) const
|
|
|
|
{
|
2024-02-05 21:03:10 +01:00
|
|
|
return !player->IsDead() && !player->IsSpectator() && !player->IsInJail()
|
|
|
|
|| player->GetDM_Team() == &m_team_spectator;
|
2023-08-16 02:05:44 +02:00
|
|
|
}
|
2023-09-23 19:37:55 +02:00
|
|
|
|
|
|
|
CTeamSpawnClock::CTeamSpawnClock()
|
|
|
|
{
|
|
|
|
nextSpawnTime = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTeamSpawnClock::Reset()
|
|
|
|
{
|
|
|
|
nextSpawnTime = sv_team_spawn_interval->value;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTeamSpawnClock::Restart()
|
|
|
|
{
|
|
|
|
nextSpawnTime = level.time + sv_team_spawn_interval->value;
|
|
|
|
}
|
|
|
|
|
|
|
|
int CTeamSpawnClock::GetSecondsLeft()
|
|
|
|
{
|
|
|
|
int timeLeft;
|
|
|
|
|
|
|
|
timeLeft = ceil(nextSpawnTime - level.time);
|
|
|
|
if (timeLeft <= -1) {
|
|
|
|
Restart();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (timeLeft < 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return timeLeft;
|
|
|
|
}
|