ddnet/src/game/server/gamecontroller.cpp
ChillerDragon 96c9b1ab73 Rename rifle to laser
This makes it easier to copy code from/to 0.7
23df3c609e
2019-11-22 15:37:18 +01:00

612 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_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 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.0f;
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_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_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;
}