diff options
Diffstat (limited to 'src/game/server')
24 files changed, 469 insertions, 211 deletions
diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index 821aade5..1c76f655 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -115,15 +115,11 @@ void CCharacter::HandleNinja() if(m_ActiveWeapon != WEAPON_NINJA) return; - vec2 Direction = normalize(vec2(m_LatestInput.m_TargetX, m_LatestInput.m_TargetY)); - if ((Server()->Tick() - m_Ninja.m_ActivationTick) > (g_pData->m_Weapons.m_Ninja.m_Duration * Server()->TickSpeed() / 1000)) { // time's up, return m_aWeapons[WEAPON_NINJA].m_Got = false; m_ActiveWeapon = m_LastWeapon; - if(m_ActiveWeapon == WEAPON_NINJA) - m_ActiveWeapon = WEAPON_GUN; SetWeapon(m_ActiveWeapon); return; @@ -183,7 +179,7 @@ void CCharacter::HandleNinja() if(m_NumObjectsHit < 10) m_apHitObjects[m_NumObjectsHit++] = aEnts[i]; - aEnts[i]->TakeDamage(vec2(0, 10.0f), g_pData->m_Weapons.m_Ninja.m_pBase->m_Damage, m_pPlayer->GetCID(), WEAPON_NINJA); + aEnts[i]->TakeDamage(vec2(0, -10.0f), g_pData->m_Weapons.m_Ninja.m_pBase->m_Damage, m_pPlayer->GetCID(), WEAPON_NINJA); } } @@ -436,8 +432,6 @@ void CCharacter::HandleWeapons() //ninja HandleNinja(); - vec2 Direction = normalize(vec2(m_LatestInput.m_TargetX, m_LatestInput.m_TargetY)); - // check reload timer if(m_ReloadTimer) { @@ -490,7 +484,8 @@ void CCharacter::GiveNinja() m_Ninja.m_ActivationTick = Server()->Tick(); m_aWeapons[WEAPON_NINJA].m_Got = true; m_aWeapons[WEAPON_NINJA].m_Ammo = -1; - m_LastWeapon = m_ActiveWeapon; + if (m_ActiveWeapon != WEAPON_NINJA) + m_LastWeapon = m_ActiveWeapon; m_ActiveWeapon = WEAPON_NINJA; GameServer()->CreateSound(m_Pos, SOUND_PICKUP_NINJA); @@ -512,7 +507,7 @@ void CCharacter::OnPredictedInput(CNetObj_PlayerInput *pNewInput) mem_copy(&m_Input, pNewInput, sizeof(m_Input)); m_NumInputs++; - // or are not allowed to aim in the center + // it is not allowed to aim in the center if(m_Input.m_TargetX == 0 && m_Input.m_TargetY == 0) m_Input.m_TargetY = -1; } @@ -522,6 +517,10 @@ void CCharacter::OnDirectInput(CNetObj_PlayerInput *pNewInput) mem_copy(&m_LatestPrevInput, &m_LatestInput, sizeof(m_LatestInput)); mem_copy(&m_LatestInput, pNewInput, sizeof(m_LatestInput)); + // it is not allowed to aim in the center + if(m_LatestInput.m_TargetX == 0 && m_LatestInput.m_TargetY == 0) + m_LatestInput.m_TargetY = -1; + if(m_NumInputs > 2 && m_pPlayer->GetTeam() != TEAM_SPECTATORS) { HandleWeaponSwitch(); @@ -531,6 +530,18 @@ void CCharacter::OnDirectInput(CNetObj_PlayerInput *pNewInput) mem_copy(&m_LatestPrevInput, &m_LatestInput, sizeof(m_LatestInput)); } +void CCharacter::ResetInput() +{ + m_Input.m_Direction = 0; + m_Input.m_Hook = 0; + // simulate releasing the fire button + if((m_Input.m_Fire&1) != 0) + m_Input.m_Fire++; + m_Input.m_Fire &= INPUT_STATE_MASK; + m_Input.m_Jump = 0; + m_LatestPrevInput = m_LatestInput = m_Input; +} + void CCharacter::Tick() { if(m_pPlayer->m_ForceBalanced) @@ -646,6 +657,20 @@ void CCharacter::TickDefered() } } +void CCharacter::TickPaused() +{ + ++m_AttackTick; + ++m_DamageTakenTick; + ++m_Ninja.m_ActivationTick; + ++m_ReckoningTick; + if(m_LastAction != -1) + ++m_LastAction; + if(m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart > -1) + ++m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart; + if(m_EmoteStop > -1) + ++m_EmoteStop; +} + bool CCharacter::IncreaseHealth(int Amount) { if(m_Health >= 10) @@ -748,7 +773,15 @@ bool CCharacter::TakeDamage(vec2 Force, int Dmg, int From, int Weapon) // do damage Hit sound if(From >= 0 && From != m_pPlayer->GetCID() && GameServer()->m_apPlayers[From]) - GameServer()->CreateSound(GameServer()->m_apPlayers[From]->m_ViewPos, SOUND_HIT, CmaskOne(From)); + { + int Mask = CmaskOne(From); + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() == TEAM_SPECTATORS && GameServer()->m_apPlayers[i]->m_SpectatorID == From) + Mask |= CmaskOne(i); + } + GameServer()->CreateSound(GameServer()->m_apPlayers[From]->m_ViewPos, SOUND_HIT, Mask); + } // check for death if(m_Health <= 0) @@ -821,7 +854,8 @@ void CCharacter::Snap(int SnappingClient) pCharacter->m_Direction = m_Input.m_Direction; - if(m_pPlayer->GetCID() == SnappingClient || SnappingClient == -1 || m_pPlayer->GetCID() == GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID) + if(m_pPlayer->GetCID() == SnappingClient || SnappingClient == -1 || + (!g_Config.m_SvStrictSpectateMode && m_pPlayer->GetCID() == GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID)) { pCharacter->m_Health = m_Health; pCharacter->m_Armor = m_Armor; diff --git a/src/game/server/entities/character.h b/src/game/server/entities/character.h index 611dc427..aa127979 100644 --- a/src/game/server/entities/character.h +++ b/src/game/server/entities/character.h @@ -30,6 +30,7 @@ public: virtual void Destroy(); virtual void Tick(); virtual void TickDefered(); + virtual void TickPaused(); virtual void Snap(int SnappingClient); bool IsGrounded(); @@ -43,6 +44,7 @@ public: void OnPredictedInput(CNetObj_PlayerInput *pNewInput); void OnDirectInput(CNetObj_PlayerInput *pNewInput); + void ResetInput(); void FireWeapon(); void Die(int Killer, int Weapon); diff --git a/src/game/server/entities/flag.cpp b/src/game/server/entities/flag.cpp index d279e4df..558ee154 100644 --- a/src/game/server/entities/flag.cpp +++ b/src/game/server/entities/flag.cpp @@ -23,6 +23,13 @@ void CFlag::Reset() m_GrabTick = 0; } +void CFlag::TickPaused() +{ + ++m_DropTick; + if(m_GrabTick) + ++m_GrabTick; +} + void CFlag::Snap(int SnappingClient) { if(NetworkClipped(SnappingClient)) diff --git a/src/game/server/entities/flag.h b/src/game/server/entities/flag.h index 2f91bc02..be22d32b 100644 --- a/src/game/server/entities/flag.h +++ b/src/game/server/entities/flag.h @@ -21,6 +21,7 @@ public: CFlag(CGameWorld *pGameWorld, int Team); virtual void Reset(); + virtual void TickPaused(); virtual void Snap(int SnappingClient); }; diff --git a/src/game/server/entities/laser.cpp b/src/game/server/entities/laser.cpp index 30ed0a9b..eb40c4e1 100644 --- a/src/game/server/entities/laser.cpp +++ b/src/game/server/entities/laser.cpp @@ -21,15 +21,15 @@ CLaser::CLaser(CGameWorld *pGameWorld, vec2 Pos, vec2 Direction, float StartEner bool CLaser::HitCharacter(vec2 From, vec2 To) { vec2 At; - CCharacter *OwnerChar = GameServer()->GetPlayerChar(m_Owner); - CCharacter *Hit = GameServer()->m_World.IntersectCharacter(m_Pos, To, 0.f, At, OwnerChar); - if(!Hit) + CCharacter *pOwnerChar = GameServer()->GetPlayerChar(m_Owner); + CCharacter *pHit = GameServer()->m_World.IntersectCharacter(m_Pos, To, 0.f, At, pOwnerChar); + if(!pHit) return false; m_From = From; m_Pos = At; m_Energy = -1; - Hit->TakeDamage(vec2(0.f, 0.f), GameServer()->Tuning()->m_LaserDamage, m_Owner, WEAPON_RIFLE); + pHit->TakeDamage(vec2(0.f, 0.f), GameServer()->Tuning()->m_LaserDamage, m_Owner, WEAPON_RIFLE); return true; } @@ -44,7 +44,6 @@ void CLaser::DoBounce() } vec2 To = m_Pos + m_Dir * m_Energy; - vec2 OrgTo = To; if(GameServer()->Collision()->IntersectLine(m_Pos, To, 0x0, &To)) { @@ -92,6 +91,11 @@ void CLaser::Tick() DoBounce(); } +void CLaser::TickPaused() +{ + ++m_EvalTick; +} + void CLaser::Snap(int SnappingClient) { if(NetworkClipped(SnappingClient)) diff --git a/src/game/server/entities/laser.h b/src/game/server/entities/laser.h index 1d7fa227..8ae6f792 100644 --- a/src/game/server/entities/laser.h +++ b/src/game/server/entities/laser.h @@ -12,6 +12,7 @@ public: virtual void Reset(); virtual void Tick(); + virtual void TickPaused(); virtual void Snap(int SnappingClient); protected: diff --git a/src/game/server/entities/pickup.cpp b/src/game/server/entities/pickup.cpp index ba26d85b..1aff5750 100644 --- a/src/game/server/entities/pickup.cpp +++ b/src/game/server/entities/pickup.cpp @@ -117,6 +117,12 @@ void CPickup::Tick() } } +void CPickup::TickPaused() +{ + if(m_SpawnTick != -1) + ++m_SpawnTick; +} + void CPickup::Snap(int SnappingClient) { if(m_SpawnTick != -1 || NetworkClipped(SnappingClient)) diff --git a/src/game/server/entities/pickup.h b/src/game/server/entities/pickup.h index 77347de2..fe45b5ae 100644 --- a/src/game/server/entities/pickup.h +++ b/src/game/server/entities/pickup.h @@ -14,6 +14,7 @@ public: virtual void Reset(); virtual void Tick(); + virtual void TickPaused(); virtual void Snap(int SnappingClient); private: diff --git a/src/game/server/entities/projectile.cpp b/src/game/server/entities/projectile.cpp index 2baa24b1..e89e0e6f 100644 --- a/src/game/server/entities/projectile.cpp +++ b/src/game/server/entities/projectile.cpp @@ -82,6 +82,11 @@ void CProjectile::Tick() } } +void CProjectile::TickPaused() +{ + ++m_StartTick; +} + void CProjectile::FillInfo(CNetObj_Projectile *pProj) { pProj->m_X = (int)m_Pos.x; diff --git a/src/game/server/entities/projectile.h b/src/game/server/entities/projectile.h index 5df04bcd..7b5c5e0e 100644 --- a/src/game/server/entities/projectile.h +++ b/src/game/server/entities/projectile.h @@ -14,6 +14,7 @@ public: virtual void Reset(); virtual void Tick(); + virtual void TickPaused(); virtual void Snap(int SnappingClient); private: diff --git a/src/game/server/entity.h b/src/game/server/entity.h index b9b33eb7..0f838730 100644 --- a/src/game/server/entity.h +++ b/src/game/server/entity.h @@ -106,6 +106,12 @@ public: virtual void TickDefered() {} /* + Function: TickPaused + Called when the game is paused, to freeze the state and position of the entity. + */ + virtual void TickPaused() {} + + /* Function: snap Called when a new snapshot is being generated for a specific client. diff --git a/src/game/server/eventhandler.cpp b/src/game/server/eventhandler.cpp index deb1ca4e..354bd4ab 100644 --- a/src/game/server/eventhandler.cpp +++ b/src/game/server/eventhandler.cpp @@ -46,7 +46,7 @@ void CEventHandler::Snap(int SnappingClient) { if(SnappingClient == -1 || CmaskIsSet(m_aClientMasks[i], SnappingClient)) { - NETEVENT_COMMON *ev = (NETEVENT_COMMON *)&m_aData[m_aOffsets[i]]; + CNetEvent_Common *ev = (CNetEvent_Common *)&m_aData[m_aOffsets[i]]; if(SnappingClient == -1 || distance(GameServer()->m_apPlayers[SnappingClient]->m_ViewPos, vec2(ev->m_X, ev->m_Y)) < 1500.0f) { void *d = GameServer()->Server()->SnapNewItem(m_aTypes[i], i, m_aSizes[i]); diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 82adcbef..5d2f22b8 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -33,6 +33,7 @@ void CGameContext::Construct(int Resetting) m_pVoteOptionFirst = 0; m_pVoteOptionLast = 0; m_NumVoteOptions = 0; + m_LockTeams = 0; if(Resetting==NO_RESET) m_pVoteOptionHeap = new CHeap(); @@ -93,7 +94,7 @@ void CGameContext::CreateDamageInd(vec2 Pos, float Angle, int Amount) for(int i = 0; i < Amount; i++) { float f = mix(s, e, float(i+1)/float(Amount+2)); - NETEVENT_DAMAGEIND *pEvent = (NETEVENT_DAMAGEIND *)m_Events.Create(NETEVENTTYPE_DAMAGEIND, sizeof(NETEVENT_DAMAGEIND)); + CNetEvent_DamageInd *pEvent = (CNetEvent_DamageInd *)m_Events.Create(NETEVENTTYPE_DAMAGEIND, sizeof(CNetEvent_DamageInd)); if(pEvent) { pEvent->m_X = (int)Pos.x; @@ -106,7 +107,7 @@ void CGameContext::CreateDamageInd(vec2 Pos, float Angle, int Amount) void CGameContext::CreateHammerHit(vec2 Pos) { // create the event - NETEVENT_HAMMERHIT *pEvent = (NETEVENT_HAMMERHIT *)m_Events.Create(NETEVENTTYPE_HAMMERHIT, sizeof(NETEVENT_HAMMERHIT)); + CNetEvent_HammerHit *pEvent = (CNetEvent_HammerHit *)m_Events.Create(NETEVENTTYPE_HAMMERHIT, sizeof(CNetEvent_HammerHit)); if(pEvent) { pEvent->m_X = (int)Pos.x; @@ -118,7 +119,7 @@ void CGameContext::CreateHammerHit(vec2 Pos) void CGameContext::CreateExplosion(vec2 Pos, int Owner, int Weapon, bool NoDamage) { // create the event - NETEVENT_EXPLOSION *pEvent = (NETEVENT_EXPLOSION *)m_Events.Create(NETEVENTTYPE_EXPLOSION, sizeof(NETEVENT_EXPLOSION)); + CNetEvent_Explosion *pEvent = (CNetEvent_Explosion *)m_Events.Create(NETEVENTTYPE_EXPLOSION, sizeof(CNetEvent_Explosion)); if(pEvent) { pEvent->m_X = (int)Pos.x; @@ -162,7 +163,7 @@ void create_smoke(vec2 Pos) void CGameContext::CreatePlayerSpawn(vec2 Pos) { // create the event - NETEVENT_SPAWN *ev = (NETEVENT_SPAWN *)m_Events.Create(NETEVENTTYPE_SPAWN, sizeof(NETEVENT_SPAWN)); + CNetEvent_Spawn *ev = (CNetEvent_Spawn *)m_Events.Create(NETEVENTTYPE_SPAWN, sizeof(CNetEvent_Spawn)); if(ev) { ev->m_X = (int)Pos.x; @@ -173,7 +174,7 @@ void CGameContext::CreatePlayerSpawn(vec2 Pos) void CGameContext::CreateDeath(vec2 Pos, int ClientID) { // create the event - NETEVENT_DEATH *pEvent = (NETEVENT_DEATH *)m_Events.Create(NETEVENTTYPE_DEATH, sizeof(NETEVENT_DEATH)); + CNetEvent_Death *pEvent = (CNetEvent_Death *)m_Events.Create(NETEVENTTYPE_DEATH, sizeof(CNetEvent_Death)); if(pEvent) { pEvent->m_X = (int)Pos.x; @@ -188,7 +189,7 @@ void CGameContext::CreateSound(vec2 Pos, int Sound, int Mask) return; // create a sound - NETEVENT_SOUNDWORLD *pEvent = (NETEVENT_SOUNDWORLD *)m_Events.Create(NETEVENTTYPE_SOUNDWORLD, sizeof(NETEVENT_SOUNDWORLD), Mask); + CNetEvent_SoundWorld *pEvent = (CNetEvent_SoundWorld *)m_Events.Create(NETEVENTTYPE_SOUNDWORLD, sizeof(CNetEvent_SoundWorld), Mask); if(pEvent) { pEvent->m_X = (int)Pos.x; @@ -204,7 +205,15 @@ void CGameContext::CreateSoundGlobal(int Sound, int Target) CNetMsg_Sv_SoundGlobal Msg; Msg.m_SoundID = Sound; - Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, Target); + if(Target == -2) + Server()->SendPackMsg(&Msg, MSGFLAG_NOSEND, -1); + else + { + int Flag = MSGFLAG_VITAL; + if(Target != -1) + Flag |= MSGFLAG_NORECORD; + Server()->SendPackMsg(&Msg, Flag, Target); + } } @@ -225,7 +234,7 @@ void CGameContext::SendChat(int ChatterClientID, int Team, const char *pText) str_format(aBuf, sizeof(aBuf), "%d:%d:%s: %s", ChatterClientID, Team, Server()->ClientName(ChatterClientID), pText); else str_format(aBuf, sizeof(aBuf), "*** %s", pText); - Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "chat", aBuf); + Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, Team!=CHAT_ALL?"teamchat":"chat", aBuf); if(Team == CHAT_ALL) { @@ -343,7 +352,8 @@ void CGameContext::SendVoteStatus(int ClientID, int Total, int Yes, int No) void CGameContext::AbortVoteKickOnDisconnect(int ClientID) { - if(m_VoteCloseTime && !str_comp_num(m_aVoteCommand, "kick ", 5) && str_toint(&m_aVoteCommand[5]) == ClientID) + if(m_VoteCloseTime && ((!str_comp_num(m_aVoteCommand, "kick ", 5) && str_toint(&m_aVoteCommand[5]) == ClientID) || + (!str_comp_num(m_aVoteCommand, "set_team ", 9) && str_toint(&m_aVoteCommand[9]) == ClientID))) m_VoteCloseTime = -1; } @@ -378,6 +388,22 @@ void CGameContext::SendTuningParams(int ClientID) Server()->SendMsg(&Msg, MSGFLAG_VITAL, ClientID); } +void CGameContext::SwapTeams() +{ + if(!m_pController->IsTeamplay()) + return; + + SendChat(-1, CGameContext::CHAT_ALL, "Teams were swapped"); + + for(int i = 0; i < MAX_CLIENTS; ++i) + { + if(m_apPlayers[i] && m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS) + m_apPlayers[i]->SetTeam(m_apPlayers[i]->GetTeam()^1, false); + } + + (void)m_pController->CheckTeamBalance(); +} + void CGameContext::OnTick() { // check tuning @@ -456,7 +482,9 @@ void CGameContext::OnTick() if(m_VoteEnforce == VOTE_ENFORCE_YES) { + Server()->SetRconCID(IServer::RCON_CID_VOTE); Console()->ExecuteLine(m_aVoteCommand); + Server()->SetRconCID(IServer::RCON_CID_SERV); EndVote(); SendChat(-1, CGameContext::CHAT_ALL, "Vote passed"); @@ -691,12 +719,12 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) } if(KickID == ClientID) { - SendChatTarget(ClientID, "You cant kick yourself"); + SendChatTarget(ClientID, "You can't kick yourself"); return; } if(Server()->IsAuthed(KickID)) { - SendChatTarget(ClientID, "You cant kick admins"); + SendChatTarget(ClientID, "You can't kick admins"); char aBufKick[128]; str_format(aBufKick, sizeof(aBufKick), "'%s' called for vote to kick you", Server()->ClientName(ClientID)); SendChatTarget(KickID, aBufKick); @@ -712,7 +740,6 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) char aAddrStr[NETADDR_MAXSTRSIZE] = {0}; Server()->GetClientAddr(KickID, aAddrStr, sizeof(aAddrStr)); str_format(aCmd, sizeof(aCmd), "ban %s %d Banned by vote", aAddrStr, g_Config.m_SvVoteKickBantime); - Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aCmd); } } else if(str_comp_nocase(pMsg->m_Type, "spectate") == 0) @@ -731,13 +758,13 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) } if(SpectateID == ClientID) { - SendChatTarget(ClientID, "You cant move yourself"); + SendChatTarget(ClientID, "You can't move yourself"); return; } str_format(aChatmsg, sizeof(aChatmsg), "'%s' called for vote to move '%s' to spectators (%s)", Server()->ClientName(ClientID), Server()->ClientName(SpectateID), pReason); str_format(aDesc, sizeof(aDesc), "move '%s' to spectators", Server()->ClientName(SpectateID)); - str_format(aCmd, sizeof(aCmd), "set_team %d -1", SpectateID); + str_format(aCmd, sizeof(aCmd), "set_team %d -1 %d", SpectateID, g_Config.m_SvVoteSpectateRejoindelay); } if(aCmd[0]) @@ -773,6 +800,23 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) if(pPlayer->GetTeam() == pMsg->m_Team || (g_Config.m_SvSpamprotection && pPlayer->m_LastSetTeam && pPlayer->m_LastSetTeam+Server()->TickSpeed()*3 > Server()->Tick())) return; + if(pMsg->m_Team != TEAM_SPECTATORS && m_LockTeams) + { + pPlayer->m_LastSetTeam = Server()->Tick(); + SendBroadcast("Teams are locked", ClientID); + return; + } + + if(pPlayer->m_TeamChangeTick > Server()->Tick()) + { + pPlayer->m_LastSetTeam = Server()->Tick(); + int TimeLeft = (pPlayer->m_TeamChangeTick - Server()->Tick())/Server()->TickSpeed(); + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "Time to wait before changing team: %02d:%02d", TimeLeft/60, TimeLeft%60); + SendBroadcast(aBuf, ClientID); + return; + } + // Switch team on given client and kill/respawn him if(m_pController->CanJoinTeam(pMsg->m_Team, ClientID)) { @@ -783,6 +827,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) m_VoteUpdate = true; pPlayer->SetTeam(pMsg->m_Team); (void)m_pController->CheckTeamBalance(); + pPlayer->m_TeamChangeTick = Server()->Tick(); } else SendBroadcast("Teams must be balanced, please join other team", ClientID); @@ -790,7 +835,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) else { char aBuf[128]; - str_format(aBuf, sizeof(aBuf), "Only %d active players are allowed", g_Config.m_SvMaxClients-g_Config.m_SvSpectatorSlots); + str_format(aBuf, sizeof(aBuf), "Only %d active players are allowed", Server()->MaxClients()-g_Config.m_SvSpectatorSlots); SendBroadcast(aBuf, ClientID); } } @@ -992,6 +1037,16 @@ void CGameContext::ConTuneDump(IConsole::IResult *pResult, void *pUserData) } } +void CGameContext::ConPause(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + + if(pSelf->m_pController->IsGameOver()) + return; + + pSelf->m_World.m_Paused ^= 1; +} + void CGameContext::ConChangeMap(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; @@ -1024,14 +1079,15 @@ void CGameContext::ConSetTeam(IConsole::IResult *pResult, void *pUserData) CGameContext *pSelf = (CGameContext *)pUserData; int ClientID = clamp(pResult->GetInteger(0), 0, (int)MAX_CLIENTS-1); int Team = clamp(pResult->GetInteger(1), -1, 1); + int Delay = pResult->NumArguments()>2 ? pResult->GetInteger(2) : 0; + if(!pSelf->m_apPlayers[ClientID]) + return; char aBuf[256]; str_format(aBuf, sizeof(aBuf), "moved client %d to team %d", ClientID, Team); pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); - if(!pSelf->m_apPlayers[ClientID]) - return; - + pSelf->m_apPlayers[ClientID]->m_TeamChangeTick = pSelf->Server()->Tick()+pSelf->Server()->TickSpeed()*Delay*60; pSelf->m_apPlayers[ClientID]->SetTeam(Team); (void)pSelf->m_pController->CheckTeamBalance(); } @@ -1042,16 +1098,75 @@ void CGameContext::ConSetTeamAll(IConsole::IResult *pResult, void *pUserData) int Team = clamp(pResult->GetInteger(0), -1, 1); char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "moved all clients to team %d", Team); - pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); + str_format(aBuf, sizeof(aBuf), "All players were moved to the %s", pSelf->m_pController->GetTeamName(Team)); + pSelf->SendChat(-1, CGameContext::CHAT_ALL, aBuf); for(int i = 0; i < MAX_CLIENTS; ++i) if(pSelf->m_apPlayers[i]) - pSelf->m_apPlayers[i]->SetTeam(Team); + pSelf->m_apPlayers[i]->SetTeam(Team, false); + + (void)pSelf->m_pController->CheckTeamBalance(); +} + +void CGameContext::ConSwapTeams(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + pSelf->SwapTeams(); +} + +void CGameContext::ConShuffleTeams(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + if(!pSelf->m_pController->IsTeamplay()) + return; + + int CounterRed = 0; + int CounterBlue = 0; + int PlayerTeam = 0; + for(int i = 0; i < MAX_CLIENTS; ++i) + if(pSelf->m_apPlayers[i] && pSelf->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS) + ++PlayerTeam; + PlayerTeam = (PlayerTeam+1)/2; + + pSelf->SendChat(-1, CGameContext::CHAT_ALL, "Teams were shuffled"); + + for(int i = 0; i < MAX_CLIENTS; ++i) + { + if(pSelf->m_apPlayers[i] && pSelf->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS) + { + if(CounterRed == PlayerTeam) + pSelf->m_apPlayers[i]->SetTeam(TEAM_BLUE, false); + else if(CounterBlue == PlayerTeam) + pSelf->m_apPlayers[i]->SetTeam(TEAM_RED, false); + else + { + if(rand() % 2) + { + pSelf->m_apPlayers[i]->SetTeam(TEAM_BLUE, false); + ++CounterBlue; + } + else + { + pSelf->m_apPlayers[i]->SetTeam(TEAM_RED, false); + ++CounterRed; + } + } + } + } (void)pSelf->m_pController->CheckTeamBalance(); } +void CGameContext::ConLockTeams(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + pSelf->m_LockTeams ^= 1; + if(pSelf->m_LockTeams) + pSelf->SendChat(-1, CGameContext::CHAT_ALL, "Teams were locked"); + else + pSelf->SendChat(-1, CGameContext::CHAT_ALL, "Teams were unlocked"); +} + void CGameContext::ConAddVote(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; @@ -1248,7 +1363,9 @@ void CGameContext::ConForceVote(IConsole::IResult *pResult, void *pUserData) return; } - str_format(aBuf, sizeof(aBuf), "set_team %d -1", SpectateID); + str_format(aBuf, sizeof(aBuf), "admin moved '%s' to spectator (%s)", pSelf->Server()->ClientName(SpectateID), pReason); + pSelf->SendChatTarget(-1, aBuf); + str_format(aBuf, sizeof(aBuf), "set_team %d -1 %d", SpectateID, g_Config.m_SvVoteSpectateRejoindelay); pSelf->Console()->ExecuteLine(aBuf); } } @@ -1269,11 +1386,18 @@ void CGameContext::ConClearVotes(IConsole::IResult *pResult, void *pUserData) void CGameContext::ConVote(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; + + // check if there is a vote running + if(!pSelf->m_VoteCloseTime) + return; + if(str_comp_nocase(pResult->GetString(0), "yes") == 0) pSelf->m_VoteEnforce = CGameContext::VOTE_ENFORCE_YES; else if(str_comp_nocase(pResult->GetString(0), "no") == 0) pSelf->m_VoteEnforce = CGameContext::VOTE_ENFORCE_NO; char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "admin forced vote %s", pResult->GetString(0)); + pSelf->SendChatTarget(-1, aBuf); str_format(aBuf, sizeof(aBuf), "forcing vote %s", pResult->GetString(0)); pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); } @@ -1297,22 +1421,26 @@ void CGameContext::OnConsoleInit() m_pServer = Kernel()->RequestInterface<IServer>(); m_pConsole = Kernel()->RequestInterface<IConsole>(); - Console()->Register("tune", "si", CFGFLAG_SERVER, ConTuneParam, this, ""); - Console()->Register("tune_reset", "", CFGFLAG_SERVER, ConTuneReset, this, ""); - Console()->Register("tune_dump", "", CFGFLAG_SERVER, ConTuneDump, this, ""); - - Console()->Register("change_map", "?r", CFGFLAG_SERVER|CFGFLAG_STORE, ConChangeMap, this, ""); - Console()->Register("restart", "?i", CFGFLAG_SERVER|CFGFLAG_STORE, ConRestart, this, ""); - Console()->Register("broadcast", "r", CFGFLAG_SERVER, ConBroadcast, this, ""); - Console()->Register("say", "r", CFGFLAG_SERVER, ConSay, this, ""); - Console()->Register("set_team", "ii", CFGFLAG_SERVER, ConSetTeam, this, ""); - Console()->Register("set_team_all", "i", CFGFLAG_SERVER, ConSetTeamAll, this, ""); - - Console()->Register("add_vote", "sr", CFGFLAG_SERVER, ConAddVote, this, ""); - Console()->Register("remove_vote", "s", CFGFLAG_SERVER, ConRemoveVote, this, ""); - Console()->Register("force_vote", "ss?r", CFGFLAG_SERVER, ConForceVote, this, ""); - Console()->Register("clear_votes", "", CFGFLAG_SERVER, ConClearVotes, this, ""); - Console()->Register("vote", "r", CFGFLAG_SERVER, ConVote, this, ""); + Console()->Register("tune", "si", CFGFLAG_SERVER, ConTuneParam, this, "Tune variable to value"); + Console()->Register("tune_reset", "", CFGFLAG_SERVER, ConTuneReset, this, "Reset tuning"); + Console()->Register("tune_dump", "", CFGFLAG_SERVER, ConTuneDump, this, "Dump tuning"); + + Console()->Register("pause", "", CFGFLAG_SERVER, ConPause, this, "Pause/unpause game"); + Console()->Register("change_map", "?r", CFGFLAG_SERVER|CFGFLAG_STORE, ConChangeMap, this, "Change map"); + Console()->Register("restart", "?i", CFGFLAG_SERVER|CFGFLAG_STORE, ConRestart, this, "Restart in x seconds (0 = abort)"); + Console()->Register("broadcast", "r", CFGFLAG_SERVER, ConBroadcast, this, "Broadcast message"); + Console()->Register("say", "r", CFGFLAG_SERVER, ConSay, this, "Say in chat"); + Console()->Register("set_team", "ii?i", CFGFLAG_SERVER, ConSetTeam, this, "Set team of player to team"); + Console()->Register("set_team_all", "i", CFGFLAG_SERVER, ConSetTeamAll, this, "Set team of all players to team"); + Console()->Register("swap_teams", "", CFGFLAG_SERVER, ConSwapTeams, this, "Swap the current teams"); + Console()->Register("shuffle_teams", "", CFGFLAG_SERVER, ConShuffleTeams, this, "Shuffle the current teams"); + Console()->Register("lock_teams", "", CFGFLAG_SERVER, ConLockTeams, this, "Lock/unlock teams"); + + Console()->Register("add_vote", "sr", CFGFLAG_SERVER, ConAddVote, this, "Add a voting option"); + Console()->Register("remove_vote", "s", CFGFLAG_SERVER, ConRemoveVote, this, "remove a voting option"); + Console()->Register("force_vote", "ss?r", CFGFLAG_SERVER, ConForceVote, this, "Force a voting option"); + Console()->Register("clear_votes", "", CFGFLAG_SERVER, ConClearVotes, this, "Clears the voting options"); + Console()->Register("vote", "r", CFGFLAG_SERVER, ConVote, this, "Force a vote to yes/no"); Console()->Chain("sv_motd", ConchainSpecialMotdupdate, this); } @@ -1400,6 +1528,17 @@ void CGameContext::OnShutdown() void CGameContext::OnSnap(int ClientID) { + // add tuning to demo + CTuningParams StandardTuning; + if(ClientID == -1 && Server()->DemoRecorder_IsRecording() && mem_comp(&StandardTuning, &m_Tuning, sizeof(CTuningParams)) != 0) + { + CMsgPacker Msg(NETMSGTYPE_SV_TUNEPARAMS); + int *pParams = (int *)&m_Tuning; + for(unsigned i = 0; i < sizeof(m_Tuning)/sizeof(int); i++) + Msg.AddInt(pParams[i]); + Server()->SendMsg(&Msg, MSGFLAG_RECORD|MSGFLAG_NOSEND, ClientID); + } + m_World.Snap(ClientID); m_pController->Snap(ClientID); m_Events.Snap(ClientID); diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index 6288850d..2ffb0ab4 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -48,12 +48,16 @@ class CGameContext : public IGameServer static void ConTuneParam(IConsole::IResult *pResult, void *pUserData); static void ConTuneReset(IConsole::IResult *pResult, void *pUserData); static void ConTuneDump(IConsole::IResult *pResult, void *pUserData); + static void ConPause(IConsole::IResult *pResult, void *pUserData); static void ConChangeMap(IConsole::IResult *pResult, void *pUserData); static void ConRestart(IConsole::IResult *pResult, void *pUserData); static void ConBroadcast(IConsole::IResult *pResult, void *pUserData); static void ConSay(IConsole::IResult *pResult, void *pUserData); static void ConSetTeam(IConsole::IResult *pResult, void *pUserData); static void ConSetTeamAll(IConsole::IResult *pResult, void *pUserData); + static void ConSwapTeams(IConsole::IResult *pResult, void *pUserData); + static void ConShuffleTeams(IConsole::IResult *pResult, void *pUserData); + static void ConLockTeams(IConsole::IResult *pResult, void *pUserData); static void ConAddVote(IConsole::IResult *pResult, void *pUserData); static void ConRemoveVote(IConsole::IResult *pResult, void *pUserData); static void ConForceVote(IConsole::IResult *pResult, void *pUserData); @@ -85,6 +89,8 @@ public: // helper functions class CCharacter *GetPlayerChar(int ClientID); + int m_LockTeams; + // voting void StartVote(const char *pDesc, const char *pCommand, const char *pReason); void EndVote(); @@ -141,6 +147,9 @@ public: void CheckPureTuning(); void SendTuningParams(int ClientID); + // + void SwapTeams(); + // engine events virtual void OnInit(); virtual void OnConsoleInit(); diff --git a/src/game/server/gamecontroller.cpp b/src/game/server/gamecontroller.cpp index 6685bba3..7001ca32 100644 --- a/src/game/server/gamecontroller.cpp +++ b/src/game/server/gamecontroller.cpp @@ -63,10 +63,25 @@ void IGameController::EvaluateSpawnType(CSpawnEval *pEval, int Type) for(int i = 0; i < m_aNumSpawnPoints[Type]; i++) { // check if the position is occupado - if(GameServer()->m_World.FindEntities(m_aaSpawnPoints[Type][i], 64, 0, 1, CGameWorld::ENTTYPE_CHARACTER)) - continue; + 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; + 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]; + vec2 P = m_aaSpawnPoints[Type][i]+Positions[Result]; float S = EvaluateSpawnPos(pEval, P); if(!pEval->m_Got || pEval->m_Score > S) { @@ -77,47 +92,6 @@ void IGameController::EvaluateSpawnType(CSpawnEval *pEval, int Type) } } -void IGameController::FindFreeSpawn(CSpawnEval *pEval, int Type) -{ - // pick the spawn point that is least occupied and has free space for spawning around it - for(int i = 0; i < m_aNumSpawnPoints[Type]; i++) - { - - CCharacter *aEnts[MAX_CLIENTS]; - int Num = GameServer()->m_World.FindEntities(m_aaSpawnPoints[Type][i], 64, (CEntity**)aEnts, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER); - float Score = 0.0f; - for(int c = 0; c < Num; ++c) - Score += 96.0f - distance(aEnts[c]->m_Pos, m_aaSpawnPoints[Type][i]); - - if(!pEval->m_Got || pEval->m_Score > Score) - { - // start, left, up, right, down - 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) }; - - // check for free space - int Result = -1; - for(int Index = 0; Index < 5 && Result == -1; ++Index) - { - Result = Index; - 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 - - pEval->m_Got = true; - pEval->m_Score = Score; - pEval->m_Pos = m_aaSpawnPoints[Type][i]+Positions[Result]; - } - } -} - bool IGameController::CanSpawn(int Team, vec2 *pOutPos) { CSpawnEval Eval; @@ -146,28 +120,6 @@ bool IGameController::CanSpawn(int Team, vec2 *pOutPos) EvaluateSpawnType(&Eval, 2); } - // handle crappy maps - if(!Eval.m_Got) - { - if(IsTeamplay()) - { - // first try own team spawn, then normal spawn and then enemy - FindFreeSpawn(&Eval, 1+(Team&1)); - if(!Eval.m_Got) - { - FindFreeSpawn(&Eval, 0); - if(!Eval.m_Got) - FindFreeSpawn(&Eval, 1+((Team+1)&1)); - } - } - else - { - FindFreeSpawn(&Eval, 0); - FindFreeSpawn(&Eval, 1); - FindFreeSpawn(&Eval, 2); - } - } - *pOutPos = Eval.m_Pos; return Eval.m_Got; } @@ -265,6 +217,7 @@ void IGameController::StartRound() m_aTeamscore[TEAM_RED] = 0; m_aTeamscore[TEAM_BLUE] = 0; 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); @@ -292,7 +245,11 @@ void IGameController::CycleMap() return; if(m_RoundCount < g_Config.m_SvRoundsPerMap-1) + { + if(g_Config.m_SvRoundSwap) + GameServer()->SwapTeams(); return; + } // handle maprotation const char *pMapRotation = g_Config.m_SvMaprotation; @@ -473,8 +430,12 @@ void IGameController::Tick() } } + // game is Paused + if(GameServer()->m_World.m_Paused) + ++m_RoundStartTick; + // do team-balancing - if (IsTeamplay() && m_UnbalancedTick != -1 && Server()->Tick() > m_UnbalancedTick+g_Config.m_SvTeambalanceTime*Server()->TickSpeed()*60) + if(IsTeamplay() && m_UnbalancedTick != -1 && Server()->Tick() > m_UnbalancedTick+g_Config.m_SvTeambalanceTime*Server()->TickSpeed()*60) { GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", "Balancing teams"); @@ -533,6 +494,13 @@ void IGameController::Tick() { 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()->IsAuthed(i)) { if(Server()->Tick() > GameServer()->m_apPlayers[i]->m_LastActionTick+g_Config.m_SvInactiveKickTime*Server()->TickSpeed()*60) @@ -568,6 +536,8 @@ void IGameController::Tick() } } } + + DoWincheck(); } @@ -640,7 +610,7 @@ bool IGameController::CanJoinTeam(int Team, int NotThisID) } } - return (aNumplayers[0] + aNumplayers[1]) < g_Config.m_SvMaxClients-g_Config.m_SvSpectatorSlots; + return (aNumplayers[0] + aNumplayers[1]) < Server()->MaxClients()-g_Config.m_SvSpectatorSlots; } bool IGameController::CheckTeamBalance() @@ -706,51 +676,50 @@ bool IGameController::CanChangeTeam(CPlayer *pPlayer, int JoinTeam) return true; } -void IGameController::DoPlayerScoreWincheck() +void IGameController::DoWincheck() { - if(m_GameOverTick == -1 && !m_Warmup) + if(m_GameOverTick == -1 && !m_Warmup && !GameServer()->m_World.m_ResetRequested) { - // gather some stats - int Topscore = 0; - int TopscoreCount = 0; - for(int i = 0; i < MAX_CLIENTS; i++) + if(IsTeamplay()) { - if(GameServer()->m_apPlayers[i]) + // check score win condition + if((g_Config.m_SvScorelimit > 0 && (m_aTeamscore[TEAM_RED] >= g_Config.m_SvScorelimit || m_aTeamscore[TEAM_BLUE] >= g_Config.m_SvScorelimit)) || + (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) { - if(GameServer()->m_apPlayers[i]->m_Score > Topscore) - { - Topscore = GameServer()->m_apPlayers[i]->m_Score; - TopscoreCount = 1; - } - else if(GameServer()->m_apPlayers[i]->m_Score == Topscore) - TopscoreCount++; + if(m_aTeamscore[TEAM_RED] != m_aTeamscore[TEAM_BLUE]) + EndRound(); + else + m_SuddenDeath = 1; } } - - // check score win condition - if((g_Config.m_SvScorelimit > 0 && Topscore >= g_Config.m_SvScorelimit) || - (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) + else { - if(TopscoreCount == 1) - EndRound(); - else - m_SuddenDeath = 1; - } - } -} + // gather some stats + int Topscore = 0; + int TopscoreCount = 0; + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(GameServer()->m_apPlayers[i]) + { + if(GameServer()->m_apPlayers[i]->m_Score > Topscore) + { + Topscore = GameServer()->m_apPlayers[i]->m_Score; + TopscoreCount = 1; + } + else if(GameServer()->m_apPlayers[i]->m_Score == Topscore) + TopscoreCount++; + } + } -void IGameController::DoTeamScoreWincheck() -{ - if(m_GameOverTick == -1 && !m_Warmup) - { - // check score win condition - if((g_Config.m_SvScorelimit > 0 && (m_aTeamscore[TEAM_RED] >= g_Config.m_SvScorelimit || m_aTeamscore[TEAM_BLUE] >= g_Config.m_SvScorelimit)) || - (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) - { - if(m_aTeamscore[TEAM_RED] != m_aTeamscore[TEAM_BLUE]) - EndRound(); - else - m_SuddenDeath = 1; + // check score win condition + if((g_Config.m_SvScorelimit > 0 && Topscore >= g_Config.m_SvScorelimit) || + (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) + { + if(TopscoreCount == 1) + EndRound(); + else + m_SuddenDeath = 1; + } } } } diff --git a/src/game/server/gamecontroller.h b/src/game/server/gamecontroller.h index 6ccfe977..1675fe35 100644 --- a/src/game/server/gamecontroller.h +++ b/src/game/server/gamecontroller.h @@ -39,7 +39,6 @@ protected: float EvaluateSpawnPos(CSpawnEval *pEval, vec2 Pos); void EvaluateSpawnType(CSpawnEval *pEval, int Type); - void FindFreeSpawn(CSpawnEval *pEval, int Type); bool EvaluateSpawn(class CPlayer *pP, vec2 *pPos); void CycleMap(); @@ -65,12 +64,12 @@ public: const char *m_pGameType; bool IsTeamplay() const; + bool IsGameOver() const { return m_GameOverTick != -1; } IGameController(class CGameContext *pGameServer); virtual ~IGameController(); - void DoTeamScoreWincheck(); - void DoPlayerScoreWincheck(); + virtual void DoWincheck(); void DoWarmup(int Seconds); diff --git a/src/game/server/gamemodes/ctf.cpp b/src/game/server/gamemodes/ctf.cpp index b1d3d2fa..9e45c1fe 100644 --- a/src/game/server/gamemodes/ctf.cpp +++ b/src/game/server/gamemodes/ctf.cpp @@ -1,6 +1,9 @@ /* (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/server/entities/character.h> #include <game/server/entities/flag.h> #include <game/server/player.h> @@ -63,6 +66,30 @@ int CGameControllerCTF::OnCharacterDeath(class CCharacter *pVictim, class CPlaye return HadFlag; } +void CGameControllerCTF::DoWincheck() +{ + if(m_GameOverTick == -1 && !m_Warmup) + { + // check score win condition + if((g_Config.m_SvScorelimit > 0 && (m_aTeamscore[TEAM_RED] >= g_Config.m_SvScorelimit || m_aTeamscore[TEAM_BLUE] >= g_Config.m_SvScorelimit)) || + (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) + { + if(m_SuddenDeath) + { + if(m_aTeamscore[TEAM_RED]/100 != m_aTeamscore[TEAM_BLUE]/100) + EndRound(); + } + else + { + if(m_aTeamscore[TEAM_RED] != m_aTeamscore[TEAM_BLUE]) + EndRound(); + else + m_SuddenDeath = 1; + } + } + } +} + bool CGameControllerCTF::CanBeMovedOnBalance(int ClientID) { CCharacter* Character = GameServer()->m_apPlayers[ClientID]->GetCharacter(); @@ -117,7 +144,8 @@ void CGameControllerCTF::Tick() { IGameController::Tick(); - DoTeamScoreWincheck(); + if(GameServer()->m_World.m_ResetRequested || GameServer()->m_World.m_Paused) + return; for(int fi = 0; fi < 2; fi++) { @@ -220,14 +248,19 @@ void CGameControllerCTF::Tick() for(int c = 0; c < MAX_CLIENTS; c++) { - if(!GameServer()->m_apPlayers[c]) + CPlayer *pPlayer = GameServer()->m_apPlayers[c]; + if(!pPlayer) continue; - if(GameServer()->m_apPlayers[c]->GetTeam() == fi) - GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_EN, GameServer()->m_apPlayers[c]->GetCID()); + if(pPlayer->GetTeam() == TEAM_SPECTATORS && pPlayer->m_SpectatorID != SPEC_FREEVIEW && GameServer()->m_apPlayers[pPlayer->m_SpectatorID] && GameServer()->m_apPlayers[pPlayer->m_SpectatorID]->GetTeam() == fi) + GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_EN, c); + else if(pPlayer->GetTeam() == fi) + GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_EN, c); else - GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_PL, GameServer()->m_apPlayers[c]->GetCID()); + GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_PL, c); } + // demo record entry + GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_EN, -2); break; } } diff --git a/src/game/server/gamemodes/ctf.h b/src/game/server/gamemodes/ctf.h index 44bd9e8e..72747ed7 100644 --- a/src/game/server/gamemodes/ctf.h +++ b/src/game/server/gamemodes/ctf.h @@ -11,6 +11,7 @@ public: class CFlag *m_apFlags[2]; CGameControllerCTF(class CGameContext *pGameServer); + virtual void DoWincheck(); virtual bool CanBeMovedOnBalance(int ClientID); virtual void Snap(int SnappingClient); virtual void Tick(); diff --git a/src/game/server/gamemodes/dm.cpp b/src/game/server/gamemodes/dm.cpp index d2b69b43..bdca4c9a 100644 --- a/src/game/server/gamemodes/dm.cpp +++ b/src/game/server/gamemodes/dm.cpp @@ -11,6 +11,5 @@ CGameControllerDM::CGameControllerDM(class CGameContext *pGameServer) void CGameControllerDM::Tick() { - DoPlayerScoreWincheck(); IGameController::Tick(); } diff --git a/src/game/server/gamemodes/mod.cpp b/src/game/server/gamemodes/mod.cpp index 127be2bc..eb8fd7c8 100644 --- a/src/game/server/gamemodes/mod.cpp +++ b/src/game/server/gamemodes/mod.cpp @@ -15,8 +15,6 @@ CGameControllerMOD::CGameControllerMOD(class CGameContext *pGameServer) void CGameControllerMOD::Tick() { // this is the main part of the gamemode, this function is run every tick - DoPlayerScoreWincheck(); // checks for winners, no teams version - //DoTeamScoreWincheck(); // checks for winners, two teams version IGameController::Tick(); } diff --git a/src/game/server/gamemodes/tdm.cpp b/src/game/server/gamemodes/tdm.cpp index 54e645b3..50ecd93e 100644 --- a/src/game/server/gamemodes/tdm.cpp +++ b/src/game/server/gamemodes/tdm.cpp @@ -48,6 +48,5 @@ void CGameControllerTDM::Snap(int SnappingClient) void CGameControllerTDM::Tick() { - DoTeamScoreWincheck(); IGameController::Tick(); } diff --git a/src/game/server/gameworld.cpp b/src/game/server/gameworld.cpp index 6444cce5..d803ae67 100644 --- a/src/game/server/gameworld.cpp +++ b/src/game/server/gameworld.cpp @@ -173,6 +173,17 @@ void CGameWorld::Tick() pEnt = m_pNextTraverseEntity; } } + else + { + // update all objects + for(int i = 0; i < NUM_ENTTYPES; i++) + for(CEntity *pEnt = m_apFirstEntityTypes[i]; pEnt; ) + { + m_pNextTraverseEntity = pEnt->m_pNextTypeEntity; + pEnt->TickPaused(); + pEnt = m_pNextTraverseEntity; + } + } RemoveEntities(); } @@ -183,7 +194,6 @@ CCharacter *CGameWorld::IntersectCharacter(vec2 Pos0, vec2 Pos1, float Radius, v { // Find other players float ClosestLen = distance(Pos0, Pos1) * 100.0f; - vec2 LineDir = normalize(Pos1-Pos0); CCharacter *pClosest = 0; CCharacter *p = (CCharacter *)FindFirst(ENTTYPE_CHARACTER); diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index aeafe3a4..75c2c1c6 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -15,17 +15,18 @@ CPlayer::CPlayer(CGameContext *pGameServer, int ClientID, int Team) m_RespawnTick = Server()->Tick(); m_DieTick = Server()->Tick(); m_ScoreStartTick = Server()->Tick(); - Character = 0; - this->m_ClientID = ClientID; + m_pCharacter = 0; + m_ClientID = ClientID; m_Team = GameServer()->m_pController->ClampTeam(Team); m_SpectatorID = SPEC_FREEVIEW; m_LastActionTick = Server()->Tick(); + m_TeamChangeTick = Server()->Tick(); } CPlayer::~CPlayer() { - delete Character; - Character = 0; + delete m_pCharacter; + m_pCharacter = 0; } void CPlayer::Tick() @@ -59,23 +60,37 @@ void CPlayer::Tick() } } - if(!Character && m_DieTick+Server()->TickSpeed()*3 <= Server()->Tick()) - m_Spawning = true; - - if(Character) + if(!GameServer()->m_World.m_Paused) { - if(Character->IsAlive()) - { - m_ViewPos = Character->m_Pos; - } - else + if(!m_pCharacter && m_Team == TEAM_SPECTATORS && m_SpectatorID == SPEC_FREEVIEW) + m_ViewPos -= vec2(clamp(m_ViewPos.x-m_LatestActivity.m_TargetX, -500.0f, 500.0f), clamp(m_ViewPos.y-m_LatestActivity.m_TargetY, -400.0f, 400.0f)); + + if(!m_pCharacter && m_DieTick+Server()->TickSpeed()*3 <= Server()->Tick()) + m_Spawning = true; + + if(m_pCharacter) { - delete Character; - Character = 0; + if(m_pCharacter->IsAlive()) + { + m_ViewPos = m_pCharacter->m_Pos; + } + else + { + delete m_pCharacter; + m_pCharacter = 0; + } } + else if(m_Spawning && m_RespawnTick <= Server()->Tick()) + TryRespawn(); } - else if(m_Spawning && m_RespawnTick <= Server()->Tick()) - TryRespawn(); + else + { + ++m_RespawnTick; + ++m_DieTick; + ++m_ScoreStartTick; + ++m_LastActionTick; + ++m_TeamChangeTick; + } } void CPlayer::PostTick() @@ -160,23 +175,38 @@ void CPlayer::OnDisconnect(const char *pReason) void CPlayer::OnPredictedInput(CNetObj_PlayerInput *NewInput) { - if(Character) - Character->OnPredictedInput(NewInput); + // skip the input if chat is active + if((m_PlayerFlags&PLAYERFLAG_CHATTING) && (NewInput->m_PlayerFlags&PLAYERFLAG_CHATTING)) + return; + + if(m_pCharacter) + m_pCharacter->OnPredictedInput(NewInput); } void CPlayer::OnDirectInput(CNetObj_PlayerInput *NewInput) { + if(NewInput->m_PlayerFlags&PLAYERFLAG_CHATTING) + { + // skip the input if chat is active + if(m_PlayerFlags&PLAYERFLAG_CHATTING) + return; + + // reset input + if(m_pCharacter) + m_pCharacter->ResetInput(); + + m_PlayerFlags = NewInput->m_PlayerFlags; + return; + } + m_PlayerFlags = NewInput->m_PlayerFlags; - if(Character) - Character->OnDirectInput(NewInput); + if(m_pCharacter) + m_pCharacter->OnDirectInput(NewInput); - if(!Character && m_Team != TEAM_SPECTATORS && (NewInput->m_Fire&1)) + if(!m_pCharacter && m_Team != TEAM_SPECTATORS && (NewInput->m_Fire&1)) m_Spawning = true; - if(!Character && m_Team == TEAM_SPECTATORS && m_SpectatorID == SPEC_FREEVIEW) - m_ViewPos = vec2(NewInput->m_TargetX, NewInput->m_TargetY); - // check for activity if(NewInput->m_Direction || m_LatestActivity.m_TargetX != NewInput->m_TargetX || m_LatestActivity.m_TargetY != NewInput->m_TargetY || NewInput->m_Jump || @@ -190,18 +220,18 @@ void CPlayer::OnDirectInput(CNetObj_PlayerInput *NewInput) CCharacter *CPlayer::GetCharacter() { - if(Character && Character->IsAlive()) - return Character; + if(m_pCharacter && m_pCharacter->IsAlive()) + return m_pCharacter; return 0; } void CPlayer::KillCharacter(int Weapon) { - if(Character) + if(m_pCharacter) { - Character->Die(m_ClientID, Weapon); - delete Character; - Character = 0; + m_pCharacter->Die(m_ClientID, Weapon); + delete m_pCharacter; + m_pCharacter = 0; } } @@ -211,7 +241,7 @@ void CPlayer::Respawn() m_Spawning = true; } -void CPlayer::SetTeam(int Team) +void CPlayer::SetTeam(int Team, bool DoChatMsg) { // clamp the team Team = GameServer()->m_pController->ClampTeam(Team); @@ -219,8 +249,11 @@ void CPlayer::SetTeam(int Team) return; char aBuf[512]; - str_format(aBuf, sizeof(aBuf), "'%s' joined the %s", Server()->ClientName(m_ClientID), GameServer()->m_pController->GetTeamName(Team)); - GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf); + if(DoChatMsg) + { + str_format(aBuf, sizeof(aBuf), "'%s' joined the %s", Server()->ClientName(m_ClientID), GameServer()->m_pController->GetTeamName(Team)); + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf); + } KillCharacter(); @@ -252,7 +285,7 @@ void CPlayer::TryRespawn() return; m_Spawning = false; - Character = new(m_ClientID) CCharacter(&GameServer()->m_World); - Character->Spawn(this, SpawnPos); + m_pCharacter = new(m_ClientID) CCharacter(&GameServer()->m_World); + m_pCharacter->Spawn(this, SpawnPos); GameServer()->CreatePlayerSpawn(SpawnPos); } diff --git a/src/game/server/player.h b/src/game/server/player.h index 9d5e462c..dd804a95 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -20,7 +20,7 @@ public: void TryRespawn(); void Respawn(); - void SetTeam(int Team); + void SetTeam(int Team, bool DoChatMsg=true); int GetTeam() const { return m_Team; }; int GetCID() const { return m_ClientID; }; @@ -78,6 +78,7 @@ public: int m_ScoreStartTick; bool m_ForceBalanced; int m_LastActionTick; + int m_TeamChangeTick; struct { int m_TargetX; @@ -96,7 +97,7 @@ public: } m_Latency; private: - CCharacter *Character; + CCharacter *m_pCharacter; CGameContext *m_pGameServer; CGameContext *GameServer() const { return m_pGameServer; } |