mirror of
https://github.com/ddnet/ddnet.git
synced 2024-09-21 10:04:18 +00:00
f31e081bd4
OK, maybe not actually remove because it is kept for fallback when the new method isn't available. The whole gametype parsing business had the same downsides as user agent parsing on the web, hence I removed it while keeping behavior the same. This allows servers to explicitly opt in or out of certain bug workarounds and other client behavior. This increases the complexity of different configurations that are available in the client (which is a bad thing).
611 lines
16 KiB
C++
611 lines
16 KiB
C++
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
|
|
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
|
#include <engine/shared/config.h>
|
|
#include <game/mapitems.h>
|
|
|
|
#include <game/generated/protocol.h>
|
|
|
|
#include "entities/pickup.h"
|
|
#include "gamecontroller.h"
|
|
#include "gamecontext.h"
|
|
|
|
#include "entities/light.h"
|
|
#include "entities/dragger.h"
|
|
#include "entities/gun.h"
|
|
#include "entities/projectile.h"
|
|
#include "entities/plasma.h"
|
|
#include "entities/door.h"
|
|
#include <game/layers.h>
|
|
|
|
|
|
IGameController::IGameController(class CGameContext *pGameServer)
|
|
{
|
|
m_pGameServer = pGameServer;
|
|
m_pServer = m_pGameServer->Server();
|
|
m_pGameType = "unknown";
|
|
|
|
//
|
|
DoWarmup(g_Config.m_SvWarmup);
|
|
m_GameOverTick = -1;
|
|
m_SuddenDeath = 0;
|
|
m_RoundStartTick = Server()->Tick();
|
|
m_RoundCount = 0;
|
|
m_GameFlags = 0;
|
|
m_aMapWish[0] = 0;
|
|
|
|
m_UnbalancedTick = -1;
|
|
m_ForceBalanced = false;
|
|
|
|
m_aNumSpawnPoints[0] = 0;
|
|
m_aNumSpawnPoints[1] = 0;
|
|
m_aNumSpawnPoints[2] = 0;
|
|
|
|
m_CurrentRecord = 0;
|
|
}
|
|
|
|
IGameController::~IGameController()
|
|
{
|
|
}
|
|
|
|
float IGameController::EvaluateSpawnPos(CSpawnEval *pEval, vec2 Pos)
|
|
{
|
|
float Score = 0.0f;
|
|
CCharacter *pC = static_cast<CCharacter *>(GameServer()->m_World.FindFirst(CGameWorld::ENTTYPE_CHARACTER));
|
|
for(; pC; pC = (CCharacter *)pC->TypeNext())
|
|
{
|
|
// team mates are not as dangerous as enemies
|
|
float Scoremod = 1.0f;
|
|
if(pEval->m_FriendlyTeam != -1 && pC->GetPlayer()->GetTeam() == pEval->m_FriendlyTeam)
|
|
Scoremod = 0.5f;
|
|
|
|
float d = distance(Pos, pC->m_Pos);
|
|
Score += Scoremod * (d == 0 ? 1000000000.0f : 1.0f/d);
|
|
}
|
|
|
|
return Score;
|
|
}
|
|
|
|
void IGameController::EvaluateSpawnType(CSpawnEval *pEval, int Type)
|
|
{
|
|
// get spawn point
|
|
for(int i = 0; i < m_aNumSpawnPoints[Type]; i++)
|
|
{
|
|
// check if the position is occupado
|
|
CCharacter *aEnts[MAX_CLIENTS];
|
|
int Num = GameServer()->m_World.FindEntities(m_aaSpawnPoints[Type][i], 64, (CEntity**)aEnts, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER);
|
|
vec2 Positions[5] = { vec2(0.0f, 0.0f), vec2(-32.0f, 0.0f), vec2(0.0f, -32.0f), vec2(32.0f, 0.0f), vec2(0.0f, 32.0f) }; // start, left, up, right, down
|
|
int Result = -1;
|
|
for(int Index = 0; Index < 5 && Result == -1; ++Index)
|
|
{
|
|
Result = Index;
|
|
if(!GameServer()->m_World.m_Core.m_Tuning[0].m_PlayerCollision)
|
|
break;
|
|
for(int c = 0; c < Num; ++c)
|
|
if(GameServer()->Collision()->CheckPoint(m_aaSpawnPoints[Type][i]+Positions[Index]) ||
|
|
distance(aEnts[c]->m_Pos, m_aaSpawnPoints[Type][i]+Positions[Index]) <= aEnts[c]->m_ProximityRadius)
|
|
{
|
|
Result = -1;
|
|
break;
|
|
}
|
|
}
|
|
if(Result == -1)
|
|
continue; // try next spawn point
|
|
|
|
vec2 P = m_aaSpawnPoints[Type][i]+Positions[Result];
|
|
float S = EvaluateSpawnPos(pEval, P);
|
|
if(!pEval->m_Got || pEval->m_Score > S)
|
|
{
|
|
pEval->m_Got = true;
|
|
pEval->m_Score = S;
|
|
pEval->m_Pos = P;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool IGameController::CanSpawn(int Team, vec2 *pOutPos)
|
|
{
|
|
CSpawnEval Eval;
|
|
|
|
// spectators can't spawn
|
|
if(Team == TEAM_SPECTATORS)
|
|
return false;
|
|
|
|
EvaluateSpawnType(&Eval, 0);
|
|
EvaluateSpawnType(&Eval, 1);
|
|
EvaluateSpawnType(&Eval, 2);
|
|
|
|
*pOutPos = Eval.m_Pos;
|
|
return Eval.m_Got;
|
|
}
|
|
|
|
bool IGameController::OnEntity(int Index, vec2 Pos, int Layer, int Flags, int Number)
|
|
{
|
|
if (Index < 0)
|
|
return false;
|
|
|
|
int Type = -1;
|
|
int SubType = 0;
|
|
|
|
int x,y;
|
|
x=(Pos.x-16.0f)/32.0f;
|
|
y=(Pos.y-16.0f)/32.0f;
|
|
int sides[8];
|
|
sides[0]=GameServer()->Collision()->Entity(x,y+1, Layer);
|
|
sides[1]=GameServer()->Collision()->Entity(x+1,y+1, Layer);
|
|
sides[2]=GameServer()->Collision()->Entity(x+1,y, Layer);
|
|
sides[3]=GameServer()->Collision()->Entity(x+1,y-1, Layer);
|
|
sides[4]=GameServer()->Collision()->Entity(x,y-1, Layer);
|
|
sides[5]=GameServer()->Collision()->Entity(x-1,y-1, Layer);
|
|
sides[6]=GameServer()->Collision()->Entity(x-1,y, Layer);
|
|
sides[7]=GameServer()->Collision()->Entity(x-1,y+1, Layer);
|
|
|
|
if(Index == ENTITY_SPAWN)
|
|
m_aaSpawnPoints[0][m_aNumSpawnPoints[0]++] = Pos;
|
|
else if(Index == ENTITY_SPAWN_RED)
|
|
m_aaSpawnPoints[1][m_aNumSpawnPoints[1]++] = Pos;
|
|
else if(Index == ENTITY_SPAWN_BLUE)
|
|
m_aaSpawnPoints[2][m_aNumSpawnPoints[2]++] = Pos;
|
|
|
|
else if(Index == ENTITY_DOOR)
|
|
{
|
|
for(int i = 0; i < 8;i++)
|
|
{
|
|
if (sides[i] >= ENTITY_LASER_SHORT && sides[i] <= ENTITY_LASER_LONG)
|
|
{
|
|
new CDoor
|
|
(
|
|
&GameServer()->m_World, //GameWorld
|
|
Pos, //Pos
|
|
pi / 4 * i, //Rotation
|
|
32 * 3 + 32 *(sides[i] - ENTITY_LASER_SHORT) * 3, //Length
|
|
Number //Number
|
|
);
|
|
}
|
|
}
|
|
}
|
|
else if(Index == ENTITY_CRAZY_SHOTGUN_EX)
|
|
{
|
|
int Dir;
|
|
if(!Flags)
|
|
Dir = 0;
|
|
else if(Flags == ROTATION_90)
|
|
Dir = 1;
|
|
else if(Flags == ROTATION_180)
|
|
Dir = 2;
|
|
else
|
|
Dir = 3;
|
|
float Deg = Dir * (pi / 2);
|
|
CProjectile *bullet = new CProjectile
|
|
(
|
|
&GameServer()->m_World,
|
|
WEAPON_SHOTGUN, //Type
|
|
-1, //Owner
|
|
Pos, //Pos
|
|
vec2(sin(Deg), cos(Deg)), //Dir
|
|
-2, //Span
|
|
true, //Freeze
|
|
true, //Explosive
|
|
0, //Force
|
|
(g_Config.m_SvShotgunBulletSound)?SOUND_GRENADE_EXPLODE:-1,//SoundImpact
|
|
Layer,
|
|
Number
|
|
);
|
|
bullet->SetBouncing(2 - (Dir % 2));
|
|
}
|
|
else if(Index == ENTITY_CRAZY_SHOTGUN)
|
|
{
|
|
int Dir;
|
|
if(!Flags)
|
|
Dir=0;
|
|
else if(Flags == (TILEFLAG_ROTATE))
|
|
Dir = 1;
|
|
else if(Flags == (TILEFLAG_VFLIP|TILEFLAG_HFLIP))
|
|
Dir = 2;
|
|
else
|
|
Dir = 3;
|
|
float Deg = Dir * ( pi / 2);
|
|
CProjectile *bullet = new CProjectile
|
|
(
|
|
&GameServer()->m_World,
|
|
WEAPON_SHOTGUN, //Type
|
|
-1, //Owner
|
|
Pos, //Pos
|
|
vec2(sin(Deg), cos(Deg)), //Dir
|
|
-2, //Span
|
|
true, //Freeze
|
|
false, //Explosive
|
|
0,
|
|
SOUND_GRENADE_EXPLODE,
|
|
Layer,
|
|
Number
|
|
);
|
|
bullet->SetBouncing(2 - (Dir % 2));
|
|
}
|
|
|
|
if(Index == ENTITY_ARMOR_1)
|
|
Type = POWERUP_ARMOR;
|
|
else if(Index == ENTITY_HEALTH_1)
|
|
Type = POWERUP_HEALTH;
|
|
else if(Index == ENTITY_WEAPON_SHOTGUN)
|
|
{
|
|
Type = POWERUP_WEAPON;
|
|
SubType = WEAPON_SHOTGUN;
|
|
}
|
|
else if(Index == ENTITY_WEAPON_GRENADE)
|
|
{
|
|
Type = POWERUP_WEAPON;
|
|
SubType = WEAPON_GRENADE;
|
|
}
|
|
else if(Index == ENTITY_WEAPON_RIFLE)
|
|
{
|
|
Type = POWERUP_WEAPON;
|
|
SubType = WEAPON_RIFLE;
|
|
}
|
|
else if(Index == ENTITY_POWERUP_NINJA)
|
|
{
|
|
Type = POWERUP_NINJA;
|
|
SubType = WEAPON_NINJA;
|
|
}
|
|
else if(Index >= ENTITY_LASER_FAST_CCW && Index <= ENTITY_LASER_FAST_CW)
|
|
{
|
|
int sides2[8];
|
|
sides2[0]=GameServer()->Collision()->Entity(x, y + 2, Layer);
|
|
sides2[1]=GameServer()->Collision()->Entity(x + 2, y + 2, Layer);
|
|
sides2[2]=GameServer()->Collision()->Entity(x + 2, y, Layer);
|
|
sides2[3]=GameServer()->Collision()->Entity(x + 2, y - 2, Layer);
|
|
sides2[4]=GameServer()->Collision()->Entity(x,y - 2, Layer);
|
|
sides2[5]=GameServer()->Collision()->Entity(x - 2, y - 2, Layer);
|
|
sides2[6]=GameServer()->Collision()->Entity(x - 2, y, Layer);
|
|
sides2[7]=GameServer()->Collision()->Entity(x - 2, y + 2, Layer);
|
|
|
|
float AngularSpeed = 0.0;
|
|
int Ind=Index-ENTITY_LASER_STOP;
|
|
int M;
|
|
if( Ind < 0)
|
|
{
|
|
Ind = -Ind;
|
|
M = 1;
|
|
}
|
|
else if(Ind == 0)
|
|
M = 0;
|
|
else
|
|
M = -1;
|
|
|
|
|
|
if(Ind == 0)
|
|
AngularSpeed = 0.0f;
|
|
else if(Ind == 1)
|
|
AngularSpeed = pi / 360;
|
|
else if(Ind == 2)
|
|
AngularSpeed = pi / 180;
|
|
else if(Ind == 3)
|
|
AngularSpeed = pi / 90;
|
|
AngularSpeed *= M;
|
|
|
|
for(int i=0; i<8;i++)
|
|
{
|
|
if(sides[i] >= ENTITY_LASER_SHORT && sides[i] <= ENTITY_LASER_LONG)
|
|
{
|
|
CLight *Lgt = new CLight(&GameServer()->m_World, Pos, pi / 4 * i, 32 * 3 + 32 * (sides[i] - ENTITY_LASER_SHORT) * 3, Layer, Number);
|
|
Lgt->m_AngularSpeed = AngularSpeed;
|
|
if(sides2[i] >= ENTITY_LASER_C_SLOW && sides2[i] <= ENTITY_LASER_C_FAST)
|
|
{
|
|
Lgt->m_Speed = 1 + (sides2[i] - ENTITY_LASER_C_SLOW) * 2;
|
|
Lgt->m_CurveLength = Lgt->m_Length;
|
|
}
|
|
else if(sides2[i] >= ENTITY_LASER_O_SLOW && sides2[i] <= ENTITY_LASER_O_FAST)
|
|
{
|
|
Lgt->m_Speed = 1 + (sides2[i] - ENTITY_LASER_O_SLOW) * 2;
|
|
Lgt->m_CurveLength = 0;
|
|
}
|
|
else
|
|
Lgt->m_CurveLength = Lgt->m_Length;
|
|
}
|
|
}
|
|
|
|
}
|
|
else if(Index >= ENTITY_DRAGGER_WEAK && Index <= ENTITY_DRAGGER_STRONG)
|
|
{
|
|
CDraggerTeam(&GameServer()->m_World, Pos, Index - ENTITY_DRAGGER_WEAK + 1, false, Layer, Number);
|
|
}
|
|
else if(Index >= ENTITY_DRAGGER_WEAK_NW && Index <= ENTITY_DRAGGER_STRONG_NW)
|
|
{
|
|
CDraggerTeam(&GameServer()->m_World, Pos, Index - ENTITY_DRAGGER_WEAK_NW + 1, true, Layer, Number);
|
|
}
|
|
else if(Index == ENTITY_PLASMAE)
|
|
{
|
|
new CGun(&GameServer()->m_World, Pos, false, true, Layer, Number);
|
|
}
|
|
else if(Index == ENTITY_PLASMAF)
|
|
{
|
|
new CGun(&GameServer()->m_World, Pos, true, false, Layer, Number);
|
|
}
|
|
else if(Index == ENTITY_PLASMA)
|
|
{
|
|
new CGun(&GameServer()->m_World, Pos, true, true, Layer, Number);
|
|
}
|
|
else if(Index == ENTITY_PLASMAU)
|
|
{
|
|
new CGun(&GameServer()->m_World, Pos, false, false, Layer, Number);
|
|
}
|
|
|
|
if(Type != -1)
|
|
{
|
|
CPickup *pPickup = new CPickup(&GameServer()->m_World, Type, SubType, Layer, Number);
|
|
pPickup->m_Pos = Pos;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void IGameController::EndRound()
|
|
{
|
|
if(m_Warmup) // game can't end when we are running warmup
|
|
return;
|
|
|
|
GameServer()->m_World.m_Paused = true;
|
|
m_GameOverTick = Server()->Tick();
|
|
m_SuddenDeath = 0;
|
|
}
|
|
|
|
void IGameController::ResetGame()
|
|
{
|
|
GameServer()->m_World.m_ResetRequested = true;
|
|
}
|
|
|
|
const char *IGameController::GetTeamName(int Team)
|
|
{
|
|
if(Team == 0)
|
|
return "game";
|
|
return "spectators";
|
|
}
|
|
|
|
//static bool IsSeparator(char c) { return c == ';' || c == ' ' || c == ',' || c == '\t'; }
|
|
|
|
void IGameController::StartRound()
|
|
{
|
|
ResetGame();
|
|
|
|
m_RoundStartTick = Server()->Tick();
|
|
m_SuddenDeath = 0;
|
|
m_GameOverTick = -1;
|
|
GameServer()->m_World.m_Paused = false;
|
|
m_ForceBalanced = false;
|
|
Server()->DemoRecorder_HandleAutoStart();
|
|
char aBuf[256];
|
|
str_format(aBuf, sizeof(aBuf), "start round type='%s' teamplay='%d'", m_pGameType, m_GameFlags&GAMEFLAG_TEAMS);
|
|
GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf);
|
|
}
|
|
|
|
void IGameController::ChangeMap(const char *pToMap)
|
|
{
|
|
str_copy(g_Config.m_SvMap, pToMap, sizeof(g_Config.m_SvMap));
|
|
}
|
|
|
|
void IGameController::PostReset()
|
|
{
|
|
for(int i = 0; i < MAX_CLIENTS; i++)
|
|
if(GameServer()->m_apPlayers[i])
|
|
GameServer()->m_apPlayers[i]->Respawn();
|
|
}
|
|
|
|
int IGameController::OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void IGameController::OnCharacterSpawn(class CCharacter *pChr)
|
|
{
|
|
// default health
|
|
pChr->IncreaseHealth(10);
|
|
|
|
// give default weapons
|
|
pChr->GiveWeapon(WEAPON_HAMMER);
|
|
pChr->GiveWeapon(WEAPON_GUN);
|
|
}
|
|
|
|
void IGameController::DoWarmup(int Seconds)
|
|
{
|
|
if(Seconds < 0)
|
|
m_Warmup = 0;
|
|
else
|
|
m_Warmup = Seconds*Server()->TickSpeed();
|
|
}
|
|
|
|
bool IGameController::IsForceBalanced()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
bool IGameController::CanBeMovedOnBalance(int ClientID)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void IGameController::Tick()
|
|
{
|
|
// do warmup
|
|
if(m_Warmup)
|
|
{
|
|
m_Warmup--;
|
|
if(!m_Warmup)
|
|
StartRound();
|
|
}
|
|
|
|
if(m_GameOverTick != -1)
|
|
{
|
|
// game over.. wait for restart
|
|
if(Server()->Tick() > m_GameOverTick+Server()->TickSpeed()*10)
|
|
{
|
|
StartRound();
|
|
m_RoundCount++;
|
|
}
|
|
}
|
|
// check for inactive players
|
|
if(g_Config.m_SvInactiveKickTime > 0)
|
|
{
|
|
for(int i = 0; i < MAX_CLIENTS; ++i)
|
|
{
|
|
#ifdef CONF_DEBUG
|
|
if(g_Config.m_DbgDummies)
|
|
{
|
|
if(i >= MAX_CLIENTS-g_Config.m_DbgDummies)
|
|
break;
|
|
}
|
|
#endif
|
|
if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS && Server()->GetAuthedState(i) == AUTHED_NO)
|
|
{
|
|
if(Server()->Tick() > GameServer()->m_apPlayers[i]->m_LastActionTick+g_Config.m_SvInactiveKickTime*Server()->TickSpeed()*60)
|
|
{
|
|
switch(g_Config.m_SvInactiveKick)
|
|
{
|
|
case 0:
|
|
{
|
|
// move player to spectator
|
|
GameServer()->m_apPlayers[i]->SetTeam(TEAM_SPECTATORS);
|
|
}
|
|
break;
|
|
case 1:
|
|
{
|
|
// move player to spectator if the reserved slots aren't filled yet, kick him otherwise
|
|
int Spectators = 0;
|
|
for(int j = 0; j < MAX_CLIENTS; ++j)
|
|
if(GameServer()->m_apPlayers[j] && GameServer()->m_apPlayers[j]->GetTeam() == TEAM_SPECTATORS)
|
|
++Spectators;
|
|
if(Spectators >= g_Config.m_SvSpectatorSlots)
|
|
Server()->Kick(i, "Kicked for inactivity");
|
|
else
|
|
GameServer()->m_apPlayers[i]->SetTeam(TEAM_SPECTATORS);
|
|
}
|
|
break;
|
|
case 2:
|
|
{
|
|
// kick the player
|
|
Server()->Kick(i, "Kicked for inactivity");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void IGameController::Snap(int SnappingClient)
|
|
{
|
|
CNetObj_GameInfo *pGameInfoObj = (CNetObj_GameInfo *)Server()->SnapNewItem(NETOBJTYPE_GAMEINFO, 0, sizeof(CNetObj_GameInfo));
|
|
if(!pGameInfoObj)
|
|
return;
|
|
|
|
pGameInfoObj->m_GameFlags = m_GameFlags;
|
|
pGameInfoObj->m_GameStateFlags = 0;
|
|
if(m_GameOverTick != -1)
|
|
pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_GAMEOVER;
|
|
if(m_SuddenDeath)
|
|
pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_SUDDENDEATH;
|
|
if(GameServer()->m_World.m_Paused)
|
|
pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_PAUSED;
|
|
pGameInfoObj->m_RoundStartTick = m_RoundStartTick;
|
|
pGameInfoObj->m_WarmupTimer = m_Warmup;
|
|
|
|
pGameInfoObj->m_RoundNum = 0;
|
|
pGameInfoObj->m_RoundCurrent = m_RoundCount+1;
|
|
|
|
CCharacter *pChr;
|
|
CPlayer *pPlayer = SnappingClient > -1 ? GameServer()->m_apPlayers[SnappingClient] : 0;
|
|
CPlayer *pPlayer2;
|
|
|
|
if(pPlayer && (pPlayer->m_TimerType == CPlayer::TIMERTYPE_GAMETIMER || pPlayer->m_TimerType == CPlayer::TIMERTYPE_GAMETIMER_AND_BROADCAST) && pPlayer->m_ClientVersion >= VERSION_DDNET_GAMETICK)
|
|
{
|
|
if((pPlayer->GetTeam() == -1 || pPlayer->IsPaused())
|
|
&& pPlayer->m_SpectatorID != SPEC_FREEVIEW
|
|
&& (pPlayer2 = GameServer()->m_apPlayers[pPlayer->m_SpectatorID]))
|
|
{
|
|
if((pChr = pPlayer2->GetCharacter()) && pChr->m_DDRaceState == DDRACE_STARTED)
|
|
{
|
|
pGameInfoObj->m_WarmupTimer = -pChr->m_StartTime;
|
|
pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_RACETIME;
|
|
}
|
|
}
|
|
else if((pChr = pPlayer->GetCharacter()) && pChr->m_DDRaceState == DDRACE_STARTED)
|
|
{
|
|
pGameInfoObj->m_WarmupTimer = -pChr->m_StartTime;
|
|
pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_RACETIME;
|
|
}
|
|
}
|
|
|
|
CNetObj_GameInfoEx *pGameInfoEx = (CNetObj_GameInfoEx *)Server()->SnapNewItem(NETOBJTYPE_GAMEINFOEX, 0, sizeof(CNetObj_GameInfoEx));
|
|
if(!pGameInfoEx)
|
|
return;
|
|
|
|
pGameInfoEx->m_Flags = 0
|
|
| GAMEINFOFLAG_TIMESCORE
|
|
| GAMEINFOFLAG_GAMETYPE_RACE
|
|
| GAMEINFOFLAG_GAMETYPE_DDRACE
|
|
| GAMEINFOFLAG_GAMETYPE_DDNET
|
|
| GAMEINFOFLAG_UNLIMITED_AMMO
|
|
| GAMEINFOFLAG_DDRACE_RECORD_MESSAGE
|
|
| GAMEINFOFLAG_ALLOW_EYE_WHEEL
|
|
| GAMEINFOFLAG_ALLOW_HOOK_COLL
|
|
| GAMEINFOFLAG_ALLOW_ZOOM
|
|
| GAMEINFOFLAG_BUG_DDRACE_GHOST
|
|
| GAMEINFOFLAG_BUG_DDRACE_INPUT
|
|
| GAMEINFOFLAG_PREDICT_DDRACE
|
|
| GAMEINFOFLAG_PREDICT_DDRACE_TILES
|
|
| GAMEINFOFLAG_ENTITIES_DDNET
|
|
| GAMEINFOFLAG_ENTITIES_DDRACE
|
|
| GAMEINFOFLAG_ENTITIES_RACE;
|
|
pGameInfoEx->m_Version = GAMEINFO_CURVERSION;
|
|
}
|
|
|
|
int IGameController::GetAutoTeam(int NotThisID)
|
|
{
|
|
// this will force the auto balancer to work overtime as well
|
|
#ifdef CONF_DEBUG
|
|
if(g_Config.m_DbgStress)
|
|
return 0;
|
|
#endif
|
|
|
|
int aNumplayers[2] = {0,0};
|
|
for(int i = 0; i < MAX_CLIENTS; i++)
|
|
{
|
|
if(GameServer()->m_apPlayers[i] && i != NotThisID)
|
|
{
|
|
if(GameServer()->m_apPlayers[i]->GetTeam() >= TEAM_RED && GameServer()->m_apPlayers[i]->GetTeam() <= TEAM_BLUE)
|
|
aNumplayers[GameServer()->m_apPlayers[i]->GetTeam()]++;
|
|
}
|
|
}
|
|
|
|
int Team = 0;
|
|
|
|
if(CanJoinTeam(Team, NotThisID))
|
|
return Team;
|
|
return -1;
|
|
}
|
|
|
|
bool IGameController::CanJoinTeam(int Team, int NotThisID)
|
|
{
|
|
if(Team == TEAM_SPECTATORS || (GameServer()->m_apPlayers[NotThisID] && GameServer()->m_apPlayers[NotThisID]->GetTeam() != TEAM_SPECTATORS))
|
|
return true;
|
|
|
|
int aNumplayers[2] = {0,0};
|
|
for(int i = 0; i < MAX_CLIENTS; i++)
|
|
{
|
|
if(GameServer()->m_apPlayers[i] && i != NotThisID)
|
|
{
|
|
if(GameServer()->m_apPlayers[i]->GetTeam() >= TEAM_RED && GameServer()->m_apPlayers[i]->GetTeam() <= TEAM_BLUE)
|
|
aNumplayers[GameServer()->m_apPlayers[i]->GetTeam()]++;
|
|
}
|
|
}
|
|
|
|
return (aNumplayers[0] + aNumplayers[1]) < Server()->MaxClients()-g_Config.m_SvSpectatorSlots;
|
|
}
|
|
|
|
int IGameController::ClampTeam(int Team)
|
|
{
|
|
if(Team < 0)
|
|
return TEAM_SPECTATORS;
|
|
return 0;
|
|
}
|