ddnet/src/game/server/gamecontroller.cpp

763 lines
22 KiB
C++
Raw Normal View History

2010-11-20 10:37:14 +00:00
/* (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. */
2010-05-29 07:25:38 +00:00
#include <engine/shared/config.h>
2010-05-29 07:25:38 +00:00
#include <game/generated/protocol.h>
2022-06-16 16:06:35 +00:00
#include <game/mapitems.h>
#include <game/teamscore.h>
2010-05-29 07:25:38 +00:00
#include "gamecontext.h"
#include "gamecontroller.h"
#include "player.h"
2022-06-16 16:06:35 +00:00
#include "entities/character.h"
#include "entities/door.h"
#include "entities/dragger.h"
#include "entities/gun.h"
#include "entities/light.h"
2022-06-16 16:06:35 +00:00
#include "entities/pickup.h"
#include "entities/projectile.h"
2010-05-29 07:25:38 +00:00
IGameController::IGameController(class CGameContext *pGameServer)
2008-08-27 20:04:07 +00:00
{
2010-05-29 07:25:38 +00:00
m_pGameServer = pGameServer;
m_pConfig = m_pGameServer->Config();
2010-05-29 07:25:38 +00:00
m_pServer = m_pGameServer->Server();
m_pGameType = "unknown";
2008-08-27 20:04:07 +00:00
//
2010-05-29 07:25:38 +00:00
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;
2010-05-29 07:25:38 +00:00
m_UnbalancedTick = -1;
m_ForceBalanced = false;
2010-05-29 07:25:38 +00:00
m_aNumSpawnPoints[0] = 0;
m_aNumSpawnPoints[1] = 0;
m_aNumSpawnPoints[2] = 0;
m_CurrentRecord = 0;
2008-08-27 20:04:07 +00:00
}
2022-02-14 23:32:04 +00:00
IGameController::~IGameController() = default;
2008-10-02 14:44:35 +00:00
void IGameController::DoActivityCheck()
{
if(g_Config.m_SvInactiveKickTime == 0)
return;
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
DoTeamChange(GameServer()->m_apPlayers[i], TEAM_SPECTATORS);
}
break;
case 1:
{
// move player to spectator if the reserved slots aren't filled yet, kick him otherwise
int Spectators = 0;
for(auto &pPlayer : GameServer()->m_apPlayers)
if(pPlayer && pPlayer->GetTeam() == TEAM_SPECTATORS)
++Spectators;
if(Spectators >= g_Config.m_SvSpectatorSlots)
Server()->Kick(i, "Kicked for inactivity");
else
DoTeamChange(GameServer()->m_apPlayers[i], TEAM_SPECTATORS);
}
break;
case 2:
{
// kick the player
Server()->Kick(i, "Kicked for inactivity");
}
}
}
}
}
}
float IGameController::EvaluateSpawnPos(CSpawnEval *pEval, vec2 Pos, int DDTeam)
{
2010-05-29 07:25:38 +00:00
float Score = 0.0f;
CCharacter *pC = static_cast<CCharacter *>(GameServer()->m_World.FindFirst(CGameWorld::ENTTYPE_CHARACTER));
2010-05-29 07:25:38 +00:00
for(; pC; pC = (CCharacter *)pC->TypeNext())
{
// ignore players in other teams
if(GameServer()->GetDDRaceTeam(pC->GetPlayer()->GetCID()) != DDTeam)
continue;
2010-05-29 07:25:38 +00:00
float d = distance(Pos, pC->m_Pos);
Score += d == 0 ? 1000000000.0f : 1.0f / d;
}
2010-05-29 07:25:38 +00:00
return Score;
}
void IGameController::EvaluateSpawnType(CSpawnEval *pEval, int Type, int DDTeam)
{
// j == 0: Find an empty slot, j == 1: Take any slot if no empty one found
for(int j = 0; j < 2 && !pEval->m_Got; j++)
{
// get spawn point
for(int i = 0; i < m_aNumSpawnPoints[Type]; i++)
2011-06-14 23:03:14 +00:00
{
vec2 P = m_aaSpawnPoints[Type][i];
if(j == 0)
{
// check if the position is occupado
CCharacter *apEnts[MAX_CLIENTS];
int Num = GameServer()->m_World.FindEntities(m_aaSpawnPoints[Type][i], 64, (CEntity **)apEnts, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER);
vec2 aPositions[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)
2011-06-14 23:03:14 +00:00
{
Result = Index;
if(!GameServer()->m_World.m_Core.m_aTuning[0].m_PlayerCollision)
break;
for(int c = 0; c < Num; ++c)
if(GameServer()->Collision()->CheckPoint(m_aaSpawnPoints[Type][i] + aPositions[Index]) ||
distance(apEnts[c]->m_Pos, m_aaSpawnPoints[Type][i] + aPositions[Index]) <= apEnts[c]->GetProximityRadius())
{
Result = -1;
break;
}
2011-06-14 23:03:14 +00:00
}
if(Result == -1)
continue; // try next spawn point
P += aPositions[Result];
}
float S = EvaluateSpawnPos(pEval, P, DDTeam);
if(!pEval->m_Got || (j == 0 && pEval->m_Score > S))
{
pEval->m_Got = true;
pEval->m_Score = S;
pEval->m_Pos = P;
}
}
}
}
bool IGameController::CanSpawn(int Team, vec2 *pOutPos, int DDTeam)
{
2010-05-29 07:25:38 +00:00
CSpawnEval Eval;
2008-11-21 14:18:55 +00:00
// spectators can't spawn
if(Team == TEAM_SPECTATORS)
2008-11-21 14:18:55 +00:00
return false;
EvaluateSpawnType(&Eval, 0, DDTeam);
EvaluateSpawnType(&Eval, 1, DDTeam);
EvaluateSpawnType(&Eval, 2, DDTeam);
2011-02-21 11:35:14 +00:00
2010-05-29 07:25:38 +00:00
*pOutPos = Eval.m_Pos;
return Eval.m_Got;
}
bool IGameController::OnEntity(int Index, vec2 Pos, int Layer, int Flags, int Number)
2008-01-13 11:43:43 +00:00
{
if(Index < 0)
2011-04-11 22:27:52 +00:00
return false;
int x, y;
x = (Pos.x - 16.0f) / 32.0f;
y = (Pos.y - 16.0f) / 32.0f;
int aSides[8];
aSides[0] = GameServer()->Collision()->Entity(x, y + 1, Layer);
aSides[1] = GameServer()->Collision()->Entity(x + 1, y + 1, Layer);
aSides[2] = GameServer()->Collision()->Entity(x + 1, y, Layer);
aSides[3] = GameServer()->Collision()->Entity(x + 1, y - 1, Layer);
aSides[4] = GameServer()->Collision()->Entity(x, y - 1, Layer);
aSides[5] = GameServer()->Collision()->Entity(x - 1, y - 1, Layer);
aSides[6] = GameServer()->Collision()->Entity(x - 1, y, Layer);
aSides[7] = GameServer()->Collision()->Entity(x - 1, y + 1, Layer);
if(Index >= ENTITY_SPAWN && Index <= ENTITY_SPAWN_BLUE)
{
int Type = Index - ENTITY_SPAWN;
m_aaSpawnPoints[Type][m_aNumSpawnPoints[Type]] = Pos;
m_aNumSpawnPoints[Type] = minimum(m_aNumSpawnPoints[Type] + 1, (int)std::size(m_aaSpawnPoints[0]));
}
else if(Index == ENTITY_DOOR)
{
for(int i = 0; i < 8; i++)
{
if(aSides[i] >= ENTITY_LASER_SHORT && aSides[i] <= ENTITY_LASER_LONG)
{
new CDoor(
2011-01-29 00:59:50 +00:00
&GameServer()->m_World, //GameWorld
Pos, //Pos
pi / 4 * i, //Rotation
32 * 3 + 32 * (aSides[i] - ENTITY_LASER_SHORT) * 3, //Length
2011-01-29 00:59:50 +00:00
Number //Number
);
}
}
}
else if(Index == ENTITY_CRAZY_SHOTGUN_EX)
2010-08-14 10:46:54 +00:00
{
2010-11-01 01:51:17 +00:00
int Dir;
if(!Flags)
Dir = 0;
else if(Flags == ROTATION_90)
Dir = 1;
else if(Flags == ROTATION_180)
Dir = 2;
else
Dir = 3;
2011-01-29 00:59:50 +00:00
float Deg = Dir * (pi / 2);
CProjectile *pBullet = new CProjectile(
2010-11-01 01:51:17 +00:00
&GameServer()->m_World,
WEAPON_SHOTGUN, //Type
-1, //Owner
Pos, //Pos
2011-01-29 00:59:50 +00:00
vec2(sin(Deg), cos(Deg)), //Dir
2010-11-01 01:51:17 +00:00
-2, //Span
true, //Freeze
true, //Explosive
2011-01-29 00:59:50 +00:00
0, //Force
(g_Config.m_SvShotgunBulletSound) ? SOUND_GRENADE_EXPLODE : -1, //SoundImpact
Layer,
Number);
pBullet->SetBouncing(2 - (Dir % 2));
2010-08-14 10:46:54 +00:00
}
2010-11-01 01:51:17 +00:00
else if(Index == ENTITY_CRAZY_SHOTGUN)
2010-08-14 10:46:54 +00:00
{
2010-11-01 01:51:17 +00:00
int Dir;
if(!Flags)
Dir = 0;
2010-11-01 01:51:17 +00:00
else if(Flags == (TILEFLAG_ROTATE))
Dir = 1;
else if(Flags == (TILEFLAG_VFLIP | TILEFLAG_HFLIP))
2010-11-01 01:51:17 +00:00
Dir = 2;
else
Dir = 3;
float Deg = Dir * (pi / 2);
CProjectile *pBullet = new CProjectile(
&GameServer()->m_World,
2010-11-01 01:51:17 +00:00
WEAPON_SHOTGUN, //Type
-1, //Owner
Pos, //Pos
2011-01-29 00:59:50 +00:00
vec2(sin(Deg), cos(Deg)), //Dir
2010-11-01 01:51:17 +00:00
-2, //Span
true, //Freeze
false, //Explosive
0,
SOUND_GRENADE_EXPLODE,
Layer,
Number);
pBullet->SetBouncing(2 - (Dir % 2));
2010-08-14 10:46:54 +00:00
}
int Type = -1;
int SubType = 0;
if(Index == ENTITY_ARMOR_1)
Type = POWERUP_ARMOR;
else if(Index == ENTITY_ARMOR_SHOTGUN)
Type = POWERUP_ARMOR_SHOTGUN;
else if(Index == ENTITY_ARMOR_GRENADE)
Type = POWERUP_ARMOR_GRENADE;
else if(Index == ENTITY_ARMOR_NINJA)
Type = POWERUP_ARMOR_NINJA;
else if(Index == ENTITY_ARMOR_LASER)
Type = POWERUP_ARMOR_LASER;
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_LASER)
{
Type = POWERUP_WEAPON;
SubType = WEAPON_LASER;
}
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 aSides2[8];
aSides2[0] = GameServer()->Collision()->Entity(x, y + 2, Layer);
aSides2[1] = GameServer()->Collision()->Entity(x + 2, y + 2, Layer);
aSides2[2] = GameServer()->Collision()->Entity(x + 2, y, Layer);
aSides2[3] = GameServer()->Collision()->Entity(x + 2, y - 2, Layer);
aSides2[4] = GameServer()->Collision()->Entity(x, y - 2, Layer);
aSides2[5] = GameServer()->Collision()->Entity(x - 2, y - 2, Layer);
aSides2[6] = GameServer()->Collision()->Entity(x - 2, y, Layer);
aSides2[7] = GameServer()->Collision()->Entity(x - 2, y + 2, Layer);
2019-07-08 21:08:42 +00:00
float AngularSpeed = 0.0f;
int Ind = Index - ENTITY_LASER_STOP;
int M;
if(Ind < 0)
{
2011-01-29 00:59:50 +00:00
Ind = -Ind;
M = 1;
}
2011-01-29 00:59:50 +00:00
else if(Ind == 0)
M = 0;
else
2011-01-29 00:59:50 +00:00
M = -1;
2011-01-29 00:59:50 +00:00
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(aSides[i] >= ENTITY_LASER_SHORT && aSides[i] <= ENTITY_LASER_LONG)
2011-01-29 00:59:50 +00:00
{
CLight *pLight = new CLight(&GameServer()->m_World, Pos, pi / 4 * i, 32 * 3 + 32 * (aSides[i] - ENTITY_LASER_SHORT) * 3, Layer, Number);
pLight->m_AngularSpeed = AngularSpeed;
if(aSides2[i] >= ENTITY_LASER_C_SLOW && aSides2[i] <= ENTITY_LASER_C_FAST)
2011-01-29 00:59:50 +00:00
{
pLight->m_Speed = 1 + (aSides2[i] - ENTITY_LASER_C_SLOW) * 2;
pLight->m_CurveLength = pLight->m_Length;
2011-01-29 00:59:50 +00:00
}
else if(aSides2[i] >= ENTITY_LASER_O_SLOW && aSides2[i] <= ENTITY_LASER_O_FAST)
2011-01-29 00:59:50 +00:00
{
pLight->m_Speed = 1 + (aSides2[i] - ENTITY_LASER_O_SLOW) * 2;
pLight->m_CurveLength = 0;
2011-01-29 00:59:50 +00:00
}
else
pLight->m_CurveLength = pLight->m_Length;
2011-01-29 00:59:50 +00:00
}
}
}
2011-01-29 00:59:50 +00:00
else if(Index >= ENTITY_DRAGGER_WEAK && Index <= ENTITY_DRAGGER_STRONG)
{
2022-05-02 18:31:17 +00:00
new CDragger(&GameServer()->m_World, Pos, Index - ENTITY_DRAGGER_WEAK + 1, false, Layer, Number);
2011-01-29 00:59:50 +00:00
}
else if(Index >= ENTITY_DRAGGER_WEAK_NW && Index <= ENTITY_DRAGGER_STRONG_NW)
{
2022-05-02 18:31:17 +00:00
new CDragger(&GameServer()->m_World, Pos, Index - ENTITY_DRAGGER_WEAK_NW + 1, true, Layer, Number);
2011-01-29 00:59:50 +00:00
}
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);
}
2015-07-09 00:08:14 +00:00
if(Type != -1)
{
CPickup *pPickup = new CPickup(&GameServer()->m_World, Type, SubType, Layer, Number);
pPickup->m_Pos = Pos;
return true;
}
return false;
}
void IGameController::OnPlayerConnect(CPlayer *pPlayer)
{
int ClientID = pPlayer->GetCID();
pPlayer->Respawn();
if(!Server()->ClientPrevIngame(ClientID))
{
char aBuf[128];
str_format(aBuf, sizeof(aBuf), "team_join player='%d:%s' team=%d", ClientID, Server()->ClientName(ClientID), pPlayer->GetTeam());
GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf);
}
}
void IGameController::OnPlayerDisconnect(class CPlayer *pPlayer, const char *pReason)
{
pPlayer->OnDisconnect();
int ClientID = pPlayer->GetCID();
if(Server()->ClientIngame(ClientID))
{
char aBuf[512];
if(pReason && *pReason)
str_format(aBuf, sizeof(aBuf), "'%s' has left the game (%s)", Server()->ClientName(ClientID), pReason);
else
str_format(aBuf, sizeof(aBuf), "'%s' has left the game", Server()->ClientName(ClientID));
GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf, -1, CGameContext::CHAT_SIX);
str_format(aBuf, sizeof(aBuf), "leave player='%d:%s'", ClientID, Server()->ClientName(ClientID));
GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "game", aBuf);
}
}
2010-05-29 07:25:38 +00:00
void IGameController::EndRound()
{
2010-05-29 07:25:38 +00:00
if(m_Warmup) // game can't end when we are running warmup
2007-10-06 17:36:24 +00:00
return;
2010-05-29 07:25:38 +00:00
GameServer()->m_World.m_Paused = true;
m_GameOverTick = Server()->Tick();
m_SuddenDeath = 0;
}
2010-05-29 07:25:38 +00:00
void IGameController::ResetGame()
{
2010-05-29 07:25:38 +00:00
GameServer()->m_World.m_ResetRequested = true;
}
2010-05-29 07:25:38 +00:00
const char *IGameController::GetTeamName(int Team)
{
if(Team == 0)
return "game";
return "spectators";
}
2011-02-05 01:33:53 +00:00
//static bool IsSeparator(char c) { return c == ';' || c == ' ' || c == ',' || c == '\t'; }
2010-05-29 07:25:38 +00:00
void IGameController::StartRound()
{
2010-05-29 07:25:38 +00:00
ResetGame();
2010-05-29 07:25:38 +00:00
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);
}
2010-05-29 07:25:38 +00:00
void IGameController::ChangeMap(const char *pToMap)
{
Server()->ChangeMap(pToMap);
}
void IGameController::OnReset()
{
2020-10-26 14:14:07 +00:00
for(auto &pPlayer : GameServer()->m_apPlayers)
if(pPlayer)
pPlayer->Respawn();
}
2010-05-29 07:25:38 +00:00
int IGameController::OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon)
{
return 0;
}
2010-05-29 07:25:38 +00:00
void IGameController::OnCharacterSpawn(class CCharacter *pChr)
2008-08-27 20:04:07 +00:00
{
// default health
2010-05-29 07:25:38 +00:00
pChr->IncreaseHealth(10);
2008-08-27 20:04:07 +00:00
// give default weapons
2016-10-08 17:42:42 +00:00
pChr->GiveWeapon(WEAPON_HAMMER);
pChr->GiveWeapon(WEAPON_GUN);
2008-08-27 20:04:07 +00:00
}
void IGameController::HandleCharacterTiles(CCharacter *pChr, int MapIndex)
{
// Do nothing by default
}
2010-05-29 07:25:38 +00:00
void IGameController::DoWarmup(int Seconds)
2007-10-06 17:36:24 +00:00
{
2010-05-29 07:25:38 +00:00
if(Seconds < 0)
m_Warmup = 0;
else
m_Warmup = Seconds * Server()->TickSpeed();
2007-10-06 17:36:24 +00:00
}
2010-05-29 07:25:38 +00:00
bool IGameController::IsForceBalanced()
{
return false;
}
bool IGameController::CanBeMovedOnBalance(int ClientID)
2010-05-29 07:25:38 +00:00
{
return true;
}
void IGameController::Tick()
{
2007-10-06 17:36:24 +00:00
// do warmup
2010-05-29 07:25:38 +00:00
if(m_Warmup)
2007-10-06 17:36:24 +00:00
{
2010-05-29 07:25:38 +00:00
m_Warmup--;
if(!m_Warmup)
StartRound();
2007-10-06 17:36:24 +00:00
}
2010-05-29 07:25:38 +00:00
if(m_GameOverTick != -1)
{
// game over.. wait for restart
if(Server()->Tick() > m_GameOverTick + Server()->TickSpeed() * 10)
{
2010-05-29 07:25:38 +00:00
StartRound();
m_RoundCount++;
}
}
DoActivityCheck();
}
2010-05-29 07:25:38 +00:00
void IGameController::Snap(int SnappingClient)
{
2011-03-04 16:08:10 +00:00
CNetObj_GameInfo *pGameInfoObj = (CNetObj_GameInfo *)Server()->SnapNewItem(NETOBJTYPE_GAMEINFO, 0, sizeof(CNetObj_GameInfo));
if(!pGameInfoObj)
return;
2011-03-04 16:08:10 +00:00
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;
Added the following settings to Close #123. sv_time_in_broadcast, 1, 0, 1, CFGFLAG_SERVER, "Whether to display time in broadcast every interval or not by default, later the choice can be changed by players via chat commands" sv_time_in_broadcast_interval, 1, 0, 60, CFGFLAG_SERVER, "How often to update the broadcast time" sv_time_in_gametimer, 0, 0, 1, CFGFLAG_SERVER, "Whether to display time in the round/game timer or not by default, later the choice can be changed by players via chat commands" Added the following Chat commands to give the player the choice over their settings: "saytime", "", CFGFLAG_CHAT|CFGFLAG_SERVER, ConSayTime, this, "Privately messages you your current time in this current running race" "saytimeall", "", CFGFLAG_CHAT|CFGFLAG_SERVER, ConSayTimeAll, this, "Publicly messages everyone your current time in this current running race" "time", "", CFGFLAG_CHAT|CFGFLAG_SERVER, ConTime, this, "Privately shows you your current time in this current running race in the broadcast message" "broadcasttime", "?s", CFGFLAG_CHAT|CFGFLAG_SERVER, ConSetBroadcastTime, this, "Personal Setting of showing time in the broadcast, broadcasttime s, where s = on for on, off for off, toggle for toggle and nothing to show current status" "servergametime", "?s", CFGFLAG_CHAT|CFGFLAG_SERVER, ConSetServerGameTime, this, "Personal Setting of showing time in the round/game timer, servergametime s, where s = on for on off for off, toggle for toggle and nothing to show current status" Fixed Chat Command "eyeemote" and made it a set + toggle instead of just toggle for better bin techneques Moved some vars from CCharacter to CPlayer to keep their status evern after death but not after disconnect. So now players have the choice to see which timer they wanna see if any. Note: These changes are all untested Stay away from this update on your main server until they are tested, i don't even know if they will compile propperly
2011-12-29 12:17:34 +00:00
CCharacter *pChr;
CPlayer *pPlayer = SnappingClient != SERVER_DEMO_CLIENT ? GameServer()->m_apPlayers[SnappingClient] : 0;
CPlayer *pPlayer2;
if(pPlayer && (pPlayer->m_TimerType == CPlayer::TIMERTYPE_GAMETIMER || pPlayer->m_TimerType == CPlayer::TIMERTYPE_GAMETIMER_AND_BROADCAST) && pPlayer->GetClientVersion() >= VERSION_DDNET_GAMETICK)
{
if((pPlayer->GetTeam() == TEAM_SPECTATORS || pPlayer->IsPaused()) && pPlayer->m_SpectatorID != SPEC_FREEVIEW && (pPlayer2 = GameServer()->m_apPlayers[pPlayer->m_SpectatorID]))
{
2017-01-04 13:14:10 +00:00
if((pChr = pPlayer2->GetCharacter()) && pChr->m_DDRaceState == DDRACE_STARTED)
{
pGameInfoObj->m_WarmupTimer = -pChr->m_StartTime;
pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_RACETIME;
}
}
2017-01-04 13:14:10 +00:00
else if((pChr = pPlayer->GetCharacter()) && pChr->m_DDRaceState == DDRACE_STARTED)
{
2017-01-04 13:14:10 +00:00
pGameInfoObj->m_WarmupTimer = -pChr->m_StartTime;
pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_RACETIME;
}
}
2019-05-21 08:11:02 +00:00
CNetObj_GameInfoEx *pGameInfoEx = (CNetObj_GameInfoEx *)Server()->SnapNewItem(NETOBJTYPE_GAMEINFOEX, 0, sizeof(CNetObj_GameInfoEx));
2019-05-21 08:11:02 +00:00
if(!pGameInfoEx)
return;
pGameInfoEx->m_Flags =
GAMEINFOFLAG_TIMESCORE |
GAMEINFOFLAG_GAMETYPE_RACE |
GAMEINFOFLAG_GAMETYPE_DDRACE |
GAMEINFOFLAG_GAMETYPE_DDNET |
GAMEINFOFLAG_UNLIMITED_AMMO |
GAMEINFOFLAG_RACE_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 |
GAMEINFOFLAG_RACE;
pGameInfoEx->m_Flags2 = GAMEINFOFLAG2_HUD_DDRACE;
2022-10-27 15:50:08 +00:00
if(g_Config.m_SvNoWeakHook)
pGameInfoEx->m_Flags2 |= GAMEINFOFLAG2_NO_WEAK_HOOK;
pGameInfoEx->m_Version = GAMEINFO_CURVERSION;
2020-03-29 02:36:38 +00:00
if(Server()->IsSixup(SnappingClient))
{
2020-06-12 19:22:54 +00:00
protocol7::CNetObj_GameData *pGameData = static_cast<protocol7::CNetObj_GameData *>(Server()->SnapNewItem(-protocol7::NETOBJTYPE_GAMEDATA, 0, sizeof(protocol7::CNetObj_GameData)));
2020-03-29 02:36:38 +00:00
if(!pGameData)
return;
2020-06-12 19:22:54 +00:00
pGameData->m_GameStartTick = m_RoundStartTick;
pGameData->m_GameStateFlags = 0;
2020-06-16 15:54:01 +00:00
if(m_GameOverTick != -1)
pGameData->m_GameStateFlags |= protocol7::GAMESTATEFLAG_GAMEOVER;
if(m_SuddenDeath)
pGameData->m_GameStateFlags |= protocol7::GAMESTATEFLAG_SUDDENDEATH;
if(GameServer()->m_World.m_Paused)
pGameData->m_GameStateFlags |= protocol7::GAMESTATEFLAG_PAUSED;
2020-06-12 19:22:54 +00:00
pGameData->m_GameStateEndTick = 0;
2020-06-16 15:54:01 +00:00
protocol7::CNetObj_GameDataRace *pRaceData = static_cast<protocol7::CNetObj_GameDataRace *>(Server()->SnapNewItem(-protocol7::NETOBJTYPE_GAMEDATARACE, 0, sizeof(protocol7::CNetObj_GameDataRace)));
if(!pRaceData)
return;
pRaceData->m_BestTime = round_to_int(m_CurrentRecord * 1000);
2020-06-20 14:30:58 +00:00
pRaceData->m_Precision = 0;
2020-06-16 15:54:01 +00:00
pRaceData->m_RaceFlags = protocol7::RACEFLAG_HIDE_KILLMSG | protocol7::RACEFLAG_KEEP_WANTED_WEAPON;
2020-03-29 02:36:38 +00:00
}
2022-02-13 19:57:27 +00:00
if(!GameServer()->Switchers().empty())
{
int Team = pPlayer && pPlayer->GetCharacter() ? pPlayer->GetCharacter()->Team() : 0;
if(pPlayer && (pPlayer->GetTeam() == TEAM_SPECTATORS || pPlayer->IsPaused()) && pPlayer->m_SpectatorID != SPEC_FREEVIEW && GameServer()->m_apPlayers[pPlayer->m_SpectatorID] && GameServer()->m_apPlayers[pPlayer->m_SpectatorID]->GetCharacter())
Team = GameServer()->m_apPlayers[pPlayer->m_SpectatorID]->GetCharacter()->Team();
if(Team == TEAM_SUPER)
return;
2021-08-23 22:43:12 +00:00
CNetObj_SwitchState *pSwitchState = static_cast<CNetObj_SwitchState *>(Server()->SnapNewItem(NETOBJTYPE_SWITCHSTATE, Team, sizeof(CNetObj_SwitchState)));
if(!pSwitchState)
return;
pSwitchState->m_HighestSwitchNumber = clamp((int)GameServer()->Switchers().size() - 1, 0, 255);
mem_zero(pSwitchState->m_aStatus, sizeof(pSwitchState->m_aStatus));
2021-08-23 22:43:12 +00:00
2022-06-15 17:34:41 +00:00
std::vector<std::pair<int, int>> vEndTicks; // <EndTick, SwitchNumber>
for(int i = 0; i <= pSwitchState->m_HighestSwitchNumber; i++)
{
int Status = (int)GameServer()->Switchers()[i].m_aStatus[Team];
pSwitchState->m_aStatus[i / 32] |= (Status << (i % 32));
int EndTick = GameServer()->Switchers()[i].m_aEndTick[Team];
if(EndTick > 0 && EndTick < Server()->Tick() + 3 * Server()->TickSpeed() && GameServer()->Switchers()[i].m_aLastUpdateTick[Team] < Server()->Tick())
{
// only keep track of EndTicks that have less than three second left and are not currently being updated by a player being present on a switch tile, to limit how often these are sent
vEndTicks.emplace_back(std::pair<int, int>(GameServer()->Switchers()[i].m_aEndTick[Team], i));
}
}
// send the endtick of switchers that are about to toggle back (up to four, prioritizing those with the earliest endticks)
mem_zero(pSwitchState->m_aSwitchNumbers, sizeof(pSwitchState->m_aSwitchNumbers));
mem_zero(pSwitchState->m_aEndTicks, sizeof(pSwitchState->m_aEndTicks));
2022-06-15 17:34:41 +00:00
std::sort(vEndTicks.begin(), vEndTicks.end());
const int NumTimedSwitchers = minimum((int)vEndTicks.size(), (int)std::size(pSwitchState->m_aEndTicks));
for(int i = 0; i < NumTimedSwitchers; i++)
{
2022-06-15 17:34:41 +00:00
pSwitchState->m_aSwitchNumbers[i] = vEndTicks[i].second;
pSwitchState->m_aEndTicks[i] = vEndTicks[i].first;
}
}
}
int IGameController::GetAutoTeam(int NotThisID)
{
2018-02-04 15:00:47 +00:00
// this will force the auto balancer to work overtime as well
#ifdef CONF_DEBUG
2010-05-29 07:25:38 +00:00
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)
{
2011-01-03 11:50:38 +00:00
if(GameServer()->m_apPlayers[i]->GetTeam() >= TEAM_RED && GameServer()->m_apPlayers[i]->GetTeam() <= TEAM_BLUE)
2010-05-29 07:25:38 +00:00
aNumplayers[GameServer()->m_apPlayers[i]->GetTeam()]++;
}
}
2010-05-29 07:25:38 +00:00
int Team = 0;
if(CanJoinTeam(Team, NotThisID))
2010-05-29 07:25:38 +00:00
return Team;
2008-03-23 14:59:58 +00:00
return -1;
}
bool IGameController::CanJoinTeam(int Team, int NotThisID)
2008-03-23 14:59:58 +00:00
{
if(Team == TEAM_SPECTATORS || (GameServer()->m_apPlayers[NotThisID] && GameServer()->m_apPlayers[NotThisID]->GetTeam() != TEAM_SPECTATORS))
2010-05-29 07:25:38 +00:00
return true;
int aNumplayers[2] = {0, 0};
2008-03-23 14:59:58 +00:00
for(int i = 0; i < MAX_CLIENTS; i++)
{
if(GameServer()->m_apPlayers[i] && i != NotThisID)
2008-03-23 14:59:58 +00:00
{
2011-01-03 11:50:38 +00:00
if(GameServer()->m_apPlayers[i]->GetTeam() >= TEAM_RED && GameServer()->m_apPlayers[i]->GetTeam() <= TEAM_BLUE)
2010-05-29 07:25:38 +00:00
aNumplayers[GameServer()->m_apPlayers[i]->GetTeam()]++;
2008-03-23 14:59:58 +00:00
}
}
return (aNumplayers[0] + aNumplayers[1]) < Server()->MaxClients() - g_Config.m_SvSpectatorSlots;
}
2010-05-29 07:25:38 +00:00
int IGameController::ClampTeam(int Team)
{
2011-01-03 11:50:38 +00:00
if(Team < 0)
return TEAM_SPECTATORS;
return 0;
}
2021-06-23 05:05:49 +00:00
int64_t IGameController::GetMaskForPlayerWorldEvent(int Asker, int ExceptID)
{
// Send all world events to everyone by default
return CmaskAllExceptOne(ExceptID);
}
void IGameController::DoTeamChange(CPlayer *pPlayer, int Team, bool DoChatMsg)
{
Team = ClampTeam(Team);
if(Team == pPlayer->GetTeam())
return;
pPlayer->SetTeam(Team);
int ClientID = pPlayer->GetCID();
char aBuf[128];
DoChatMsg = false;
if(DoChatMsg)
{
str_format(aBuf, sizeof(aBuf), "'%s' joined the %s", Server()->ClientName(ClientID), GameServer()->m_pController->GetTeamName(Team));
GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf);
}
str_format(aBuf, sizeof(aBuf), "team_join player='%d:%s' m_Team=%d", ClientID, Server()->ClientName(ClientID), Team);
GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf);
// OnPlayerInfoChange(pPlayer);
}