1 (edited by SandSnake 2015-08-11 15:11:43)

Topic: [Idea]CTF without respawn

Hi all, I've idea for a simple mod, but I have no skill in programming to do it. Maybe somebody will be interested in to make it. The rules are simple: There is no respawn until one team capute flag or everybody die. I think that game maybe will be more interesting because now almost everbody fly around a map like mad monkeys instead like silent snakes big_smile

2

Re: [Idea]CTF without respawn

Interesting.

Not Luck, Just Magic.

3

Re: [Idea]CTF without respawn

I like that idea. Maybe I'll find some time to code it.

4 (edited by SandSnake 2015-08-11 13:50:46)

Re: [Idea]CTF without respawn

heinrich5991 wrote:

I like that idea. Maybe I'll find some time to code it.

Thanks heinrich5991, that would be great big_smile

5 (edited by Pathos 2015-08-11 23:40:33)

Re: [Idea]CTF without respawn

The beauty is the part where "almost everbody fly around a map like mad monkeys" because of its speed. Such speedy games require low ping, but for wherever low ping available, I think this is what makes Teeworlds such a great game. These kinds of games are usually well received in Korea/Japan.

I don't think having an entire mod is really necessary, but it's a pretty decent idea though. I think having points per frag in a CTF game (maybe 10, 100, 333 points per kill, for 1,000 points games and whatnot, force respawn time in CTF game to 9,999 maybe) can be done too. Perhaps players can play with certain amount of lives. Heinrich, if you're going to make this, please make lives customizable in the config/rcon (which I'm sure you already thought of).

6

Re: [Idea]CTF without respawn

Pathos wrote:

The beauty is the part where "almost everbody fly around a map like mad monkeys" because of its speed. Such speedy games require low ping, but for wherever low ping available, I think this is what makes Teeworlds such a great game. These kinds of games are usually well received in Korea/Japan.

I love Teewords for dynamics too, but I meant that some players behave like kamikaze, run thoughtlessly. In this mod players will must be just more careful, change tactic and plan according to situation. smile

7

Re: [Idea]CTF without respawn

heinrich5991 wrote:

I like that idea. Maybe I'll find some time to code it.

very excited, can't wait to see it big_smile

8 (edited by SandSnake 2015-08-13 16:26:51)

Re: [Idea]CTF without respawn

I wonder whether after capute the flag better is full (all players) or only dead respawn.  I think better is full, because it's more fair. What do you think?

9

Re: [Idea]CTF without respawn

Great idea ! I'd code it but I'm coding a server atm, but this is really good, it's like with counter strike, no respawn before one of the teams reach the goal.

And then yeah, everybody needs to respawn, not only dead people.

while(!Success())
    TryAgain();
Try until you succeed.

10 (edited by SandSnake 2015-08-14 15:57:05)

Re: [Idea]CTF without respawn

Thanks Neox76, but I still have doubts. With ALL RESP everybody take gun, and half will not have it. On the other hand with NO FULL RESP tees who are no dead after capute the flag are full armed and may quick kill unarmed Tees , but then again respawned Tees can quick take guns and fight, then more players have gun. I'm confused yikes Maybe better is NO FULL RESP because each round is different, and in FULL RESPAWN each is very similar.

11 (edited by xush 2015-08-14 18:39:27)

Re: [Idea]CTF without respawn

So I will start developing this mod now. As i am new to teeworlds development, you are very welcome to contribute or post anything wrong/unclean as an issue on GitHub or post here.

https://github.com/xushTW/CTFRespawn (Just initialized, might not contain any changes when you view it)

By the way, what would you guys suggest as mod name (will use something temporary for now)?

Edit: For thoose who are intrested in the developing progress: here you can find a summary of which changes have been done.

Real programmers don't comment their code - it was hard to write, it should be hard to understand.
Proudly verkeckt since 2010.

12 (edited by Savander 2015-08-14 17:17:16)

Re: [Idea]CTF without respawn

@xush, i think you should just fork original code, then modifying itself ^^

HCTF (Hardcore CTF, haha )

Ps. you should remove binaries (teeworlds_srv)

just add .gitignore file

[example]

bam
.bam
config.lua
datasrc/__pycache__
datasrc/*.pyc
objs
src/game/generated
SDL.dll
freetype.dll
autoexec.cfg

crapnet*
dilate*
fake_server*
map_resave*
map_version*
mastersrv*
packetgen*
teeworlds*
teeworlds_srv*
tileset_border*
versionsrv*
.cproject
*.bat
*.exe 

13

Re: [Idea]CTF without respawn

oh yes, i know about this. Im trying out my new git client, and it seems like im doing something wrong.

I will clean up the repo in the future.

Real programmers don't comment their code - it was hard to write, it should be hard to understand.
Proudly verkeckt since 2010.

14 (edited by SandSnake 2015-08-14 18:44:30)

Re: [Idea]CTF without respawn

xush wrote:

     
So I will start developing this mod now. As i am new to teeworlds developing, you are very welcome to contribute or post anything wrong/unclean as an issue on GitHub or post here.
https://github.com/xushTW/CTFRespawn (Just initialized, might not contain any changes when you view it)
By the way, what would you guys suggest as mod name (will use something temporary for now)?
Edit: For thoose who are intrested in the developing progress: here you can find a summary of which changes have been done.

omg xush awsome big_smile by the way are you going to make version with ALL RESP or ONLY DEAD RESP?  Now I'm thinking the second way is better.

Savander wrote:

HCTF (Hardcore CTF, haha )

HardcoreCTF is great name big_smile

15

Re: [Idea]CTF without respawn

Just added a command to configure ALL RESP or ONLY DEAD RESP, so everyone is happy smile

Unfortunally I can't work on it too long today (its friday!), so you may have to wait some days until its fully working.

Real programmers don't comment their code - it was hard to write, it should be hard to understand.
Proudly verkeckt since 2010.

16

Re: [Idea]CTF without respawn

xush wrote:

Just added a command to configure ALL RESP or ONLY DEAD RESP, so everyone is happy smile

Unfortunally I can't work on it too long today (its friday!), so you may have to wait some days until its fully working.

No problemo, I'm understand smile Have a good time! big_smile

17

Re: [Idea]CTF without respawn

xush wrote:

So I will start developing this mod now. As i am new to teeworlds development, you are very welcome to contribute or post anything wrong/unclean as an issue on GitHub or post here.

https://github.com/xushTW/CTFRespawn (Just initialized, might not contain any changes when you view it)

By the way, what would you guys suggest as mod name (will use something temporary for now)?

Edit: For thoose who are intrested in the developing progress: here you can find a summary of which changes have been done.

I'm sorry, I haven't seen that you develop the mod, now I developed it on my own too. hmm

Here's the source code, I named the mod 'CFT' because it's garbled CTF – and it was the original thread title. smile

From a47551c6daabd75c7eda0c3e56f697dbfe1a179e Mon Sep 17 00:00:00 2001
From: heinrich5991 <heinrich5991@gmail.com>
Date: Sat, 22 Aug 2015 17:33:27 +0200
Subject: [PATCH] Implement the 'CFT' mod, Capture The Flag without respawn

The basic idea is simple, don't respawn until either one team is dead or the
flag is captured.

Still needs some fine tuning wrt. initial joining.
---
 datasrc/compile.py                     |  50 ++++++++++++++
 datasrc/network.py                     |   8 +++
 scripts/cmd5.py                        |   1 +
 src/game/server/entities/character.cpp |   8 +--
 src/game/server/gamecontext.cpp        |  71 ++++++++++----------
 src/game/server/gamecontroller.cpp     |  51 ++++++++-------
 src/game/server/gamecontroller.h       |   1 +
 src/game/server/gamemodes/ctf.cpp      | 115 +++++++++++++++++++++++++++++++--
 src/game/server/gamemodes/ctf.h        |   6 ++
 src/game/server/gamemodes/dm.cpp       |  15 -----
 src/game/server/gamemodes/dm.h         |  13 ----
 src/game/server/gamemodes/mod.cpp      |  20 ------
 src/game/server/gamemodes/mod.h        |  16 -----
 src/game/server/gamemodes/tdm.cpp      |  52 ---------------
 src/game/server/gamemodes/tdm.h        |  16 -----
 src/game/server/player.cpp             |  70 ++++++++++++--------
 src/game/server/player.h               |   9 ++-
 17 files changed, 292 insertions(+), 230 deletions(-)
 delete mode 100644 src/game/server/gamemodes/dm.cpp
 delete mode 100644 src/game/server/gamemodes/dm.h
 delete mode 100644 src/game/server/gamemodes/mod.cpp
 delete mode 100644 src/game/server/gamemodes/mod.h
 delete mode 100644 src/game/server/gamemodes/tdm.cpp
 delete mode 100644 src/game/server/gamemodes/tdm.h

diff --git a/datasrc/compile.py b/datasrc/compile.py
index 088e500..346e8f6 100644
--- a/datasrc/compile.py
+++ b/datasrc/compile.py
@@ -159,6 +159,56 @@ class CNetObjHandler
     lines += ['#include <engine/message.h>']
     lines += ['#include "protocol.h"']
 
+    lines += ["""
+int SnapTeam(int Team)
+{
+    switch(Team)
+    {
+    case TEAM_RED_DEAD:
+    case TEAM_BLUE_DEAD:
+        return TEAM_SPECTATORS;
+    default:
+        return Team;
+    }
+}
+
+bool IsSpectatorTeam(int Team)
+{
+    return SnapTeam(Team) == TEAM_SPECTATORS;
+}
+
+bool IsAliveTeam(int Team)
+{
+    return Team == TEAM_RED || Team == TEAM_BLUE;
+}
+
+int GameTeam(int Team)
+{
+    switch(Team)
+    {
+    case TEAM_RED_DEAD:
+        return TEAM_RED;
+    case TEAM_BLUE_DEAD:
+        return TEAM_BLUE;
+    default:
+        return Team;
+    }
+}
+
+int DeadTeam(int Team)
+{
+    switch(Team)
+    {
+    case TEAM_RED:
+        return TEAM_RED_DEAD;
+    case TEAM_BLUE:
+        return TEAM_BLUE_DEAD;
+    default:
+        return Team;
+    }
+}
+    """]
+
     lines += ['CNetObjHandler::CNetObjHandler()']
     lines += ['{']
     lines += ['\tm_pMsgFailedOn = "";']
diff --git a/datasrc/network.py b/datasrc/network.py
index b2c42b3..8a699a8 100644
--- a/datasrc/network.py
+++ b/datasrc/network.py
@@ -23,6 +23,8 @@
     TEAM_SPECTATORS=-1,
     TEAM_RED,
     TEAM_BLUE,
+    TEAM_RED_DEAD,
+    TEAM_BLUE_DEAD,
 
     FLAG_MISSING=-3,
     FLAG_ATSTAND,
@@ -30,6 +32,12 @@
 
     SPEC_FREEVIEW=-1,
 };
+
+int SnapTeam(int Team);
+int GameTeam(int Team);
+int DeadTeam(int Team);
+bool IsSpectatorTeam(int Team);
+bool IsAliveTeam(int Team);
 '''
 
 RawSource = '''
diff --git a/scripts/cmd5.py b/scripts/cmd5.py
index 68881d0..f347e32 100644
--- a/scripts/cmd5.py
+++ b/scripts/cmd5.py
@@ -32,4 +32,5 @@ def cstrip(lines):
 #TODO 0.7: improve nethash creation
 if hash == "e42d81cd67b8c7bc":
     hash = "626fce9a778df4d4"
+hash = "626fce9a778df4d4"
 print('#define GAME_NETVERSION_HASH "%s"' % hash)
diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp
index f45100a..a98503e 100644
--- a/src/game/server/entities/character.cpp
+++ b/src/game/server/entities/character.cpp
@@ -526,7 +526,7 @@ void CCharacter::OnDirectInput(CNetObj_PlayerInput *pNewInput)
     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)
+    if(m_NumInputs > 2 && !m_pPlayer->IsSpectator())
     {
         HandleWeaponSwitch();
         FireWeapon();
@@ -552,7 +552,7 @@ void CCharacter::Tick()
     if(m_pPlayer->m_ForceBalanced)
     {
         char Buf[128];
-        str_format(Buf, sizeof(Buf), "You were moved to %s due to team balancing", GameServer()->m_pController->GetTeamName(m_pPlayer->GetTeam()));
+        str_format(Buf, sizeof(Buf), "You were moved to %s due to team balancing", GameServer()->m_pController->GetTeamName(m_pPlayer->GetGameTeam()));
         GameServer()->SendBroadcast(Buf, m_pPlayer->GetCID());
 
         m_pPlayer->m_ForceBalanced = false;
@@ -637,7 +637,7 @@ void CCharacter::TickDefered()
     if(Events&COREEVENT_HOOK_HIT_NOHOOK) GameServer()->CreateSound(m_Pos, SOUND_HOOK_NOATTACH, Mask);
 
 
-    if(m_pPlayer->GetTeam() == TEAM_SPECTATORS)
+    if(m_pPlayer->IsSpectator())
     {
         m_Pos.x = m_Input.m_TargetX;
         m_Pos.y = m_Input.m_TargetY;
@@ -782,7 +782,7 @@ bool CCharacter::TakeDamage(vec2 Force, int Dmg, int From, int Weapon)
         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)
+            if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->IsSpectator() && GameServer()->m_apPlayers[i]->m_SpectatorID == From)
                 Mask |= CmaskOne(i);
         }
         GameServer()->CreateSound(GameServer()->m_apPlayers[From]->m_ViewPos, SOUND_HIT, Mask);
diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp
index 897e348..bf3091c 100644
--- a/src/game/server/gamecontext.cpp
+++ b/src/game/server/gamecontext.cpp
@@ -9,10 +9,7 @@
 #include <game/version.h>
 #include <game/collision.h>
 #include <game/gamecore.h>
-#include "gamemodes/dm.h"
-#include "gamemodes/tdm.h"
 #include "gamemodes/ctf.h"
-#include "gamemodes/mod.h"
 
 enum
 {
@@ -257,7 +254,7 @@ void CGameContext::SendChat(int ChatterClientID, int Team, const char *pText)
         // send to the clients
         for(int i = 0; i < MAX_CLIENTS; i++)
         {
-            if(m_apPlayers[i] && m_apPlayers[i]->GetTeam() == Team)
+            if(m_apPlayers[i] && m_apPlayers[i]->GetGameTeam() == Team)
                 Server()->SendPackMsg(&Msg, MSGFLAG_VITAL|MSGFLAG_NORECORD, i);
         }
     }
@@ -397,8 +394,8 @@ void CGameContext::SwapTeams()
 
     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);
+        if(m_apPlayers[i] && m_apPlayers[i]->GetGameTeam() != TEAM_SPECTATORS)
+            m_apPlayers[i]->SetGameTeam(m_apPlayers[i]->GetGameTeam()^1, false);
     }
 
     (void)m_pController->CheckTeamBalance();
@@ -447,7 +444,7 @@ void CGameContext::OnTick()
                 bool aVoteChecked[MAX_CLIENTS] = {0};
                 for(int i = 0; i < MAX_CLIENTS; i++)
                 {
-                    if(!m_apPlayers[i] || m_apPlayers[i]->GetTeam() == TEAM_SPECTATORS || aVoteChecked[i])    // don't count in votes by spectators
+                    if(!m_apPlayers[i] || m_apPlayers[i]->GetGameTeam() == TEAM_SPECTATORS || aVoteChecked[i])    // don't count in votes by spectators
                         continue;
 
                     int ActVote = m_apPlayers[i]->m_Vote;
@@ -536,10 +533,10 @@ void CGameContext::OnClientEnter(int ClientID)
     //world.insert_entity(&players[client_id]);
     m_apPlayers[ClientID]->Respawn();
     char aBuf[512];
-    str_format(aBuf, sizeof(aBuf), "'%s' entered and joined the %s", Server()->ClientName(ClientID), m_pController->GetTeamName(m_apPlayers[ClientID]->GetTeam()));
+    str_format(aBuf, sizeof(aBuf), "'%s' entered and joined the %s", Server()->ClientName(ClientID), m_pController->GetTeamName(m_apPlayers[ClientID]->GetGameTeam()));
     SendChat(-1, CGameContext::CHAT_ALL, aBuf);
 
-    str_format(aBuf, sizeof(aBuf), "team_join player='%d:%s' team=%d", ClientID, Server()->ClientName(ClientID), m_apPlayers[ClientID]->GetTeam());
+    str_format(aBuf, sizeof(aBuf), "team_join player='%d:%s' team=%d", ClientID, Server()->ClientName(ClientID), m_apPlayers[ClientID]->GetGameTeam());
     Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf);
 
     m_VoteUpdate = true;
@@ -551,6 +548,7 @@ void CGameContext::OnClientConnected(int ClientID)
     const int StartTeam = g_Config.m_SvTournamentMode ? TEAM_SPECTATORS : m_pController->GetAutoTeam(ClientID);
 
     m_apPlayers[ClientID] = new(ClientID) CPlayer(this, ClientID, StartTeam);
+    m_apPlayers[ClientID]->Revive();
     //players[client_id].init(client_id);
     //players[client_id].client_id = client_id;
 
@@ -616,7 +614,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
                 return;
 
             CNetMsg_Cl_Say *pMsg = (CNetMsg_Cl_Say *)pRawMsg;
-            int Team = pMsg->m_Team ? pPlayer->GetTeam() : CGameContext::CHAT_ALL;
+            int Team = pMsg->m_Team ? pPlayer->GetGameTeam() : CGameContext::CHAT_ALL;
             
             // trim right and set maximum length to 128 utf8-characters
             int Length = 0;
@@ -661,7 +659,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
 
             int64 Now = Server()->Tick();
             pPlayer->m_LastVoteTry = Now;
-            if(pPlayer->GetTeam() == TEAM_SPECTATORS)
+            if(pPlayer->GetGameTeam() == TEAM_SPECTATORS)
             {
                 SendChatTarget(ClientID, "Spectators aren't allowed to start a vote.");
                 return;
@@ -724,7 +722,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
                 {
                     int PlayerNum = 0;
                     for(int i = 0; i < MAX_CLIENTS; ++i)
-                        if(m_apPlayers[i] && m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS)
+                        if(m_apPlayers[i] && m_apPlayers[i]->GetGameTeam() != TEAM_SPECTATORS)
                             ++PlayerNum;
 
                     if(PlayerNum < g_Config.m_SvVoteKickMin)
@@ -775,7 +773,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
                 }
 
                 int SpectateID = str_toint(pMsg->m_Value);
-                if(SpectateID < 0 || SpectateID >= MAX_CLIENTS || !m_apPlayers[SpectateID] || m_apPlayers[SpectateID]->GetTeam() == TEAM_SPECTATORS)
+                if(SpectateID < 0 || SpectateID >= MAX_CLIENTS || !m_apPlayers[SpectateID] || m_apPlayers[SpectateID]->GetGameTeam() == TEAM_SPECTATORS)
                 {
                     SendChatTarget(ClientID, "Invalid client id to move");
                     return;
@@ -820,8 +818,16 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
         else if (MsgID == NETMSGTYPE_CL_SETTEAM && !m_World.m_Paused)
         {
             CNetMsg_Cl_SetTeam *pMsg = (CNetMsg_Cl_SetTeam *)pRawMsg;
+            CNetMsg_Cl_SetTeam Copy = *pMsg;
+            pMsg = &Copy;
 
-            if(pPlayer->GetTeam() == pMsg->m_Team || (g_Config.m_SvSpamprotection && pPlayer->m_LastSetTeam && pPlayer->m_LastSetTeam+Server()->TickSpeed()*3 > Server()->Tick()))
+            // The button for joining the spectators is not
+            // available in the client when the player is dead. If
+            // you join your own team, join the spectators instead.
+            if(pPlayer->GetGameTeam() == pMsg->m_Team)
+                pMsg->m_Team = TEAM_SPECTATORS;
+
+            if(pPlayer->GetGameTeam() == 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)
@@ -847,9 +853,9 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
                 if(m_pController->CanChangeTeam(pPlayer, pMsg->m_Team))
                 {
                     pPlayer->m_LastSetTeam = Server()->Tick();
-                    if(pPlayer->GetTeam() == TEAM_SPECTATORS || pMsg->m_Team == TEAM_SPECTATORS)
+                    if(pPlayer->GetGameTeam() == TEAM_SPECTATORS || pMsg->m_Team == TEAM_SPECTATORS)
                         m_VoteUpdate = true;
-                    pPlayer->SetTeam(pMsg->m_Team);
+                    pPlayer->SetGameTeam(pMsg->m_Team);
                     (void)m_pController->CheckTeamBalance();
                     pPlayer->m_TeamChangeTick = Server()->Tick();
                 }
@@ -867,12 +873,12 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
         {
             CNetMsg_Cl_SetSpectatorMode *pMsg = (CNetMsg_Cl_SetSpectatorMode *)pRawMsg;
 
-            if(pPlayer->GetTeam() != TEAM_SPECTATORS || pPlayer->m_SpectatorID == pMsg->m_SpectatorID || ClientID == pMsg->m_SpectatorID ||
+            if(!pPlayer->IsSpectator() || pPlayer->m_SpectatorID == pMsg->m_SpectatorID || ClientID == pMsg->m_SpectatorID ||
                 (g_Config.m_SvSpamprotection && pPlayer->m_LastSetSpectatorMode && pPlayer->m_LastSetSpectatorMode+Server()->TickSpeed()*3 > Server()->Tick()))
                 return;
 
             pPlayer->m_LastSetSpectatorMode = Server()->Tick();
-            if(pMsg->m_SpectatorID != SPEC_FREEVIEW && (!m_apPlayers[pMsg->m_SpectatorID] || m_apPlayers[pMsg->m_SpectatorID]->GetTeam() == TEAM_SPECTATORS))
+            if(pMsg->m_SpectatorID != SPEC_FREEVIEW && (!m_apPlayers[pMsg->m_SpectatorID] || m_apPlayers[pMsg->m_SpectatorID]->IsSpectator()))
                 SendChatTarget(ClientID, "Invalid spectator id used");
             else
                 pPlayer->m_SpectatorID = pMsg->m_SpectatorID;
@@ -1111,7 +1117,7 @@ void CGameContext::ConSetTeam(IConsole::IResult *pResult, void *pUserData)
     pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
 
     pSelf->m_apPlayers[ClientID]->m_TeamChangeTick = pSelf->Server()->Tick()+pSelf->Server()->TickSpeed()*Delay*60;
-    pSelf->m_apPlayers[ClientID]->SetTeam(Team);
+    pSelf->m_apPlayers[ClientID]->SetGameTeam(Team);
     (void)pSelf->m_pController->CheckTeamBalance();
 }
 
@@ -1126,7 +1132,7 @@ void CGameContext::ConSetTeamAll(IConsole::IResult *pResult, void *pUserData)
 
     for(int i = 0; i < MAX_CLIENTS; ++i)
         if(pSelf->m_apPlayers[i])
-            pSelf->m_apPlayers[i]->SetTeam(Team, false);
+            pSelf->m_apPlayers[i]->SetGameTeam(Team, false);
 
     (void)pSelf->m_pController->CheckTeamBalance();
 }
@@ -1147,7 +1153,7 @@ void CGameContext::ConShuffleTeams(IConsole::IResult *pResult, void *pUserData)
     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)
+        if(pSelf->m_apPlayers[i] && pSelf->m_apPlayers[i]->GetGameTeam() != TEAM_SPECTATORS)
             ++PlayerTeam;
     PlayerTeam = (PlayerTeam+1)/2;
     
@@ -1155,22 +1161,22 @@ void CGameContext::ConShuffleTeams(IConsole::IResult *pResult, void *pUserData)
 
     for(int i = 0; i < MAX_CLIENTS; ++i)
     {
-        if(pSelf->m_apPlayers[i] && pSelf->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS)
+        if(pSelf->m_apPlayers[i] && pSelf->m_apPlayers[i]->GetGameTeam() != TEAM_SPECTATORS)
         {
             if(CounterRed == PlayerTeam)
-                pSelf->m_apPlayers[i]->SetTeam(TEAM_BLUE, false);
+                pSelf->m_apPlayers[i]->SetGameTeam(TEAM_BLUE, false);
             else if(CounterBlue == PlayerTeam)
-                pSelf->m_apPlayers[i]->SetTeam(TEAM_RED, false);
+                pSelf->m_apPlayers[i]->SetGameTeam(TEAM_RED, false);
             else
             {    
                 if(rand() % 2)
                 {
-                    pSelf->m_apPlayers[i]->SetTeam(TEAM_BLUE, false);
+                    pSelf->m_apPlayers[i]->SetGameTeam(TEAM_BLUE, false);
                     ++CounterBlue;
                 }
                 else
                 {
-                    pSelf->m_apPlayers[i]->SetTeam(TEAM_RED, false);
+                    pSelf->m_apPlayers[i]->SetGameTeam(TEAM_RED, false);
                     ++CounterRed;
                 }
             }
@@ -1380,7 +1386,7 @@ void CGameContext::ConForceVote(IConsole::IResult *pResult, void *pUserData)
     else if(str_comp_nocase(pType, "spectate") == 0)
     {
         int SpectateID = str_toint(pValue);
-        if(SpectateID < 0 || SpectateID >= MAX_CLIENTS || !pSelf->m_apPlayers[SpectateID] || pSelf->m_apPlayers[SpectateID]->GetTeam() == TEAM_SPECTATORS)
+        if(SpectateID < 0 || SpectateID >= MAX_CLIENTS || !pSelf->m_apPlayers[SpectateID] || pSelf->m_apPlayers[SpectateID]->GetGameTeam() == TEAM_SPECTATORS)
         {
             pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "Invalid client id to move");
             return;
@@ -1489,14 +1495,7 @@ void CGameContext::OnInit(/*class IKernel *pKernel*/)
     //players = new CPlayer[MAX_CLIENTS];
 
     // select gametype
-    if(str_comp(g_Config.m_SvGametype, "mod") == 0)
-        m_pController = new CGameControllerMOD(this);
-    else if(str_comp(g_Config.m_SvGametype, "ctf") == 0)
-        m_pController = new CGameControllerCTF(this);
-    else if(str_comp(g_Config.m_SvGametype, "tdm") == 0)
-        m_pController = new CGameControllerTDM(this);
-    else
-        m_pController = new CGameControllerDM(this);
+    m_pController = new CGameControllerCTF(this);
 
     // setup core world
     //for(int i = 0; i < MAX_CLIENTS; i++)
@@ -1585,7 +1584,7 @@ bool CGameContext::IsClientReady(int ClientID)
 
 bool CGameContext::IsClientPlayer(int ClientID)
 {
-    return m_apPlayers[ClientID] && m_apPlayers[ClientID]->GetTeam() == TEAM_SPECTATORS ? false : true;
+    return m_apPlayers[ClientID] && m_apPlayers[ClientID]->GetGameTeam() == TEAM_SPECTATORS ? false : true;
 }
 
 const char *CGameContext::GameType() { return m_pController && m_pController->m_pGameType ? m_pController->m_pGameType : ""; }
diff --git a/src/game/server/gamecontroller.cpp b/src/game/server/gamecontroller.cpp
index 68e5508..6bd3a5b 100644
--- a/src/game/server/gamecontroller.cpp
+++ b/src/game/server/gamecontroller.cpp
@@ -48,7 +48,7 @@ float IGameController::EvaluateSpawnPos(CSpawnEval *pEval, vec2 Pos)
     {
         // team mates are not as dangerous as enemies
         float Scoremod = 1.0f;
-        if(pEval->m_FriendlyTeam != -1 && pC->GetPlayer()->GetTeam() == pEval->m_FriendlyTeam)
+        if(pEval->m_FriendlyTeam != -1 && pC->GetPlayer()->GetGameTeam() == pEval->m_FriendlyTeam)
             Scoremod = 0.5f;
 
         float d = distance(Pos, pC->m_Pos);
@@ -326,10 +326,10 @@ void IGameController::OnPlayerInfoChange(class CPlayer *pP)
     if(IsTeamplay())
     {
         pP->m_TeeInfos.m_UseCustomColor = 1;
-        if(pP->GetTeam() >= TEAM_RED && pP->GetTeam() <= TEAM_BLUE)
+        if(pP->GetGameTeam() >= TEAM_RED && pP->GetGameTeam() <= TEAM_BLUE)
         {
-            pP->m_TeeInfos.m_ColorBody = aTeamColors[pP->GetTeam()];
-            pP->m_TeeInfos.m_ColorFeet = aTeamColors[pP->GetTeam()];
+            pP->m_TeeInfos.m_ColorBody = aTeamColors[pP->GetGameTeam()];
+            pP->m_TeeInfos.m_ColorFeet = aTeamColors[pP->GetGameTeam()];
         }
         else
         {
@@ -349,7 +349,7 @@ int IGameController::OnCharacterDeath(class CCharacter *pVictim, class CPlayer *
         pVictim->GetPlayer()->m_Score--; // suicide
     else
     {
-        if(IsTeamplay() && pVictim->GetPlayer()->GetTeam() == pKiller->GetTeam())
+        if(IsTeamplay() && pVictim->GetPlayer()->GetGameTeam() == pKiller->GetGameTeam())
             pKiller->m_Score--; // teamkill
         else
             pKiller->m_Score++; // normal kill
@@ -411,7 +411,7 @@ bool IGameController::IsFriendlyFire(int ClientID1, int ClientID2)
         if(!GameServer()->m_apPlayers[ClientID1] || !GameServer()->m_apPlayers[ClientID2])
             return false;
 
-        if(GameServer()->m_apPlayers[ClientID1]->GetTeam() == GameServer()->m_apPlayers[ClientID2]->GetTeam())
+        if(GameServer()->m_apPlayers[ClientID1]->GetGameTeam() == GameServer()->m_apPlayers[ClientID2]->GetGameTeam())
             return true;
     }
 
@@ -475,12 +475,12 @@ void IGameController::Tick()
         float aPScore[MAX_CLIENTS] = {0.0f};
         for(int i = 0; i < MAX_CLIENTS; i++)
         {
-            if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS)
+            if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetGameTeam() != TEAM_SPECTATORS)
             {
-                aT[GameServer()->m_apPlayers[i]->GetTeam()]++;
+                aT[GameServer()->m_apPlayers[i]->GetGameTeam()]++;
                 aPScore[i] = GameServer()->m_apPlayers[i]->m_Score*Server()->TickSpeed()*60.0f/
                     (Server()->Tick()-GameServer()->m_apPlayers[i]->m_ScoreStartTick);
-                aTScore[GameServer()->m_apPlayers[i]->GetTeam()] += aPScore[i];
+                aTScore[GameServer()->m_apPlayers[i]->GetGameTeam()] += aPScore[i];
             }
         }
 
@@ -499,7 +499,7 @@ void IGameController::Tick()
                     if(!GameServer()->m_apPlayers[i] || !CanBeMovedOnBalance(i))
                         continue;
                     // remember the player who would cause lowest score-difference
-                    if(GameServer()->m_apPlayers[i]->GetTeam() == M && (!pP || absolute((aTScore[M^1]+aPScore[i]) - (aTScore[M]-aPScore[i])) < PD))
+                    if(GameServer()->m_apPlayers[i]->GetGameTeam() == M && (!pP || absolute((aTScore[M^1]+aPScore[i]) - (aTScore[M]-aPScore[i])) < PD))
                     {
                         pP = GameServer()->m_apPlayers[i];
                         PD = absolute((aTScore[M^1]+aPScore[i]) - (aTScore[M]-aPScore[i]));
@@ -508,7 +508,8 @@ void IGameController::Tick()
 
                 // move the player to the other team
                 int Temp = pP->m_LastActionTick;
-                pP->SetTeam(M^1);
+                pP->SetGameTeam(M^1);
+                pP->Revive();
                 pP->m_LastActionTick = Temp;
 
                 pP->Respawn();
@@ -532,7 +533,7 @@ void IGameController::Tick()
                     break;
             }
         #endif
-            if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS && !Server()->IsAuthed(i))
+            if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetGameTeam() != TEAM_SPECTATORS && !Server()->IsAuthed(i))
             {
                 if(Server()->Tick() > GameServer()->m_apPlayers[i]->m_LastActionTick+g_Config.m_SvInactiveKickTime*Server()->TickSpeed()*60)
                 {
@@ -541,7 +542,7 @@ void IGameController::Tick()
                     case 0:
                         {
                             // move player to spectator
-                            GameServer()->m_apPlayers[i]->SetTeam(TEAM_SPECTATORS);
+                            GameServer()->m_apPlayers[i]->SetGameTeam(TEAM_SPECTATORS);
                         }
                         break;
                     case 1:
@@ -549,12 +550,12 @@ void IGameController::Tick()
                             // 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)
+                                if(GameServer()->m_apPlayers[j] && GameServer()->m_apPlayers[j]->GetGameTeam() == TEAM_SPECTATORS)
                                     ++Spectators;
                             if(Spectators >= g_Config.m_SvSpectatorSlots)
                                 Server()->Kick(i, "Kicked for inactivity");
                             else
-                                GameServer()->m_apPlayers[i]->SetTeam(TEAM_SPECTATORS);
+                                GameServer()->m_apPlayers[i]->SetGameTeam(TEAM_SPECTATORS);
                         }
                         break;
                     case 2:
@@ -612,8 +613,8 @@ int IGameController::GetAutoTeam(int NotThisID)
     {
         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()]++;
+            if(GameServer()->m_apPlayers[i]->GetGameTeam() >= TEAM_RED && GameServer()->m_apPlayers[i]->GetGameTeam() <= TEAM_BLUE)
+                aNumplayers[GameServer()->m_apPlayers[i]->GetGameTeam()]++;
         }
     }
 
@@ -628,7 +629,7 @@ int IGameController::GetAutoTeam(int NotThisID)
 
 bool IGameController::CanJoinTeam(int Team, int NotThisID)
 {
-    if(Team == TEAM_SPECTATORS || (GameServer()->m_apPlayers[NotThisID] && GameServer()->m_apPlayers[NotThisID]->GetTeam() != TEAM_SPECTATORS))
+    if(Team == TEAM_SPECTATORS || (GameServer()->m_apPlayers[NotThisID] && GameServer()->m_apPlayers[NotThisID]->GetGameTeam() != TEAM_SPECTATORS))
         return true;
 
     int aNumplayers[2] = {0,0};
@@ -636,8 +637,8 @@ bool IGameController::CanJoinTeam(int Team, int NotThisID)
     {
         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()]++;
+            if(GameServer()->m_apPlayers[i]->GetGameTeam() >= TEAM_RED && GameServer()->m_apPlayers[i]->GetGameTeam() <= TEAM_BLUE)
+                aNumplayers[GameServer()->m_apPlayers[i]->GetGameTeam()]++;
         }
     }
 
@@ -653,8 +654,8 @@ bool IGameController::CheckTeamBalance()
     for(int i = 0; i < MAX_CLIENTS; i++)
     {
         CPlayer *pP = GameServer()->m_apPlayers[i];
-        if(pP && pP->GetTeam() != TEAM_SPECTATORS)
-            aT[pP->GetTeam()]++;
+        if(pP && pP->GetGameTeam() != TEAM_SPECTATORS)
+            aT[pP->GetGameTeam()]++;
     }
 
     char aBuf[256];
@@ -685,13 +686,13 @@ bool IGameController::CanChangeTeam(CPlayer *pPlayer, int JoinTeam)
     for(int i = 0; i < MAX_CLIENTS; i++)
     {
         CPlayer *pP = GameServer()->m_apPlayers[i];
-        if(pP && pP->GetTeam() != TEAM_SPECTATORS)
-            aT[pP->GetTeam()]++;
+        if(pP && pP->GetGameTeam() != TEAM_SPECTATORS)
+            aT[pP->GetGameTeam()]++;
     }
 
     // simulate what would happen if changed team
     aT[JoinTeam]++;
-    if (pPlayer->GetTeam() != TEAM_SPECTATORS)
+    if (pPlayer->GetGameTeam() != TEAM_SPECTATORS)
         aT[JoinTeam^1]--;
 
     // there is a player-difference of at least 2
diff --git a/src/game/server/gamecontroller.h b/src/game/server/gamecontroller.h
index 66ff1dc..c1d01c9 100644
--- a/src/game/server/gamecontroller.h
+++ b/src/game/server/gamecontroller.h
@@ -144,6 +144,7 @@ class IGameController
     int ClampTeam(int Team);
 
     virtual void PostReset();
+    virtual void ReviveCheck() = 0;
 };
 
 #endif
diff --git a/src/game/server/gamemodes/ctf.cpp b/src/game/server/gamemodes/ctf.cpp
index 140dadf..1835a6b 100644
--- a/src/game/server/gamemodes/ctf.cpp
+++ b/src/game/server/gamemodes/ctf.cpp
@@ -15,8 +15,9 @@ CGameControllerCTF::CGameControllerCTF(class CGameContext *pGameServer)
 {
     m_apFlags[0] = 0;
     m_apFlags[1] = 0;
-    m_pGameType = "CTF";
+    m_pGameType = "CFT";
     m_GameFlags = GAMEFLAG_TEAMS|GAMEFLAG_FLAGS;
+    m_BothTeamsPresent = false;
 }
 
 bool CGameControllerCTF::OnEntity(int Index, vec2 Pos)
@@ -56,13 +57,38 @@ int CGameControllerCTF::OnCharacterDeath(class CCharacter *pVictim, class CPlaye
             F->m_pCarryingCharacter = 0;
             F->m_Vel = vec2(0,0);
 
-            if(pKiller && pKiller->GetTeam() != pVictim->GetPlayer()->GetTeam())
+            if(pKiller && pKiller->GetGameTeam() != pVictim->GetPlayer()->GetGameTeam())
                 pKiller->m_Score++;
 
             HadFlag |= 1;
         }
     }
 
+    // Don't mark the player as dead in case of team changes
+    if(WeaponID != WEAPON_GAME)
+    {
+        CPlayer *pVictimPlayer = pVictim->GetPlayer();
+        pVictimPlayer->SetTeamSimple(DeadTeam(pVictimPlayer->GetGameTeam()));
+
+        int NewSpectatorID;
+        if(pKiller && pKiller->GetCID() != pVictimPlayer->GetCID())
+            NewSpectatorID = pKiller->GetCID();
+        else
+            NewSpectatorID = SPEC_FREEVIEW;
+
+        pVictimPlayer->m_SpectatorID = NewSpectatorID;
+
+        for(int i = 0; i < MAX_CLIENTS; i++)
+        {
+            CPlayer *pPlayer = GameServer()->m_apPlayers[i];
+            if(!pPlayer)
+                continue;
+            if(pPlayer->m_SpectatorID == pVictimPlayer->GetCID())
+                pPlayer->m_SpectatorID = NewSpectatorID;
+        }
+    }
+    ReviveCheck();
+
     return HadFlag;
 }
 
@@ -197,6 +223,8 @@ void CGameControllerCTF::Tick()
                         m_apFlags[i]->Reset();
 
                     GameServer()->CreateSoundGlobal(SOUND_CTF_CAPTURE);
+
+                    Revive();
                 }
             }
         }
@@ -206,10 +234,10 @@ void CGameControllerCTF::Tick()
             int Num = GameServer()->m_World.FindEntities(F->m_Pos, CFlag::ms_PhysSize, (CEntity**)apCloseCCharacters, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER);
             for(int i = 0; i < Num; i++)
             {
-                if(!apCloseCCharacters[i]->IsAlive() || apCloseCCharacters[i]->GetPlayer()->GetTeam() == TEAM_SPECTATORS || GameServer()->Collision()->IntersectLine(F->m_Pos, apCloseCCharacters[i]->m_Pos, NULL, NULL))
+                if(!apCloseCCharacters[i]->IsAlive() || apCloseCCharacters[i]->GetPlayer()->IsSpectator() || GameServer()->Collision()->IntersectLine(F->m_Pos, apCloseCCharacters[i]->m_Pos, NULL, NULL))
                     continue;
 
-                if(apCloseCCharacters[i]->GetPlayer()->GetTeam() == F->m_Team)
+                if(apCloseCCharacters[i]->GetPlayer()->GetGameTeam() == F->m_Team)
                 {
                     // return the flag
                     if(!F->m_AtStand)
@@ -252,9 +280,9 @@ void CGameControllerCTF::Tick()
                         if(!pPlayer)
                             continue;
 
-                        if(pPlayer->GetTeam() == TEAM_SPECTATORS && pPlayer->m_SpectatorID != SPEC_FREEVIEW && GameServer()->m_apPlayers[pPlayer->m_SpectatorID] && GameServer()->m_apPlayers[pPlayer->m_SpectatorID]->GetTeam() == fi)
+                        if(pPlayer->IsSpectator() && pPlayer->m_SpectatorID != SPEC_FREEVIEW && GameServer()->m_apPlayers[pPlayer->m_SpectatorID] && GameServer()->m_apPlayers[pPlayer->m_SpectatorID]->GetGameTeam() == fi)
                             GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_EN, c);
-                        else if(pPlayer->GetTeam() == fi)
+                        else if(pPlayer->GetGameTeam() == fi)
                             GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_EN, c);
                         else
                             GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_PL, c);
@@ -280,4 +308,79 @@ void CGameControllerCTF::Tick()
             }
         }
     }
+
+    {
+        int TeamAlive = TEAM_SPECTATORS;
+        bool OnlyOneTeamAlive = true;
+        for(int i = 0; i < MAX_CLIENTS; i++)
+        {
+            CPlayer *pPlayer = GameServer()->m_apPlayers[i];
+            if(!pPlayer || pPlayer->GetGameTeam() == TEAM_SPECTATORS)
+                continue;
+            if(!pPlayer->IsDeadTeam() && pPlayer)
+            {
+                if(TeamAlive == TEAM_SPECTATORS)
+                    TeamAlive = pPlayer->GetGameTeam();
+                else if(TeamAlive != pPlayer->GetGameTeam())
+                    OnlyOneTeamAlive = false;
+            }
+        }
+
+        if(OnlyOneTeamAlive)
+        {
+            if(m_BothTeamsPresent)
+            {
+                char aBuf[64];
+                str_format(aBuf, sizeof(aBuf), "The %s team eliminated all enemies", TeamAlive ? "blue" : "red");
+                GameServer()->SendChat(-1, -2, aBuf);
+
+                for(int i = 0; i < 2; i++)
+                    if(m_apFlags[i])
+                        m_apFlags[i]->Reset();
+
+                m_aTeamscore[TeamAlive] += 100;
+
+                GameServer()->CreateSoundGlobal(SOUND_CTF_CAPTURE);
+            }
+
+            Revive();
+        }
+
+        bool aTeamsPresent[2] = { false, false };
+        for(int i = 0; i < MAX_CLIENTS; i++)
+        {
+            CPlayer *pPlayer = GameServer()->m_apPlayers[i];
+            if(!pPlayer || pPlayer->GetGameTeam() == TEAM_SPECTATORS)
+                continue;
+            aTeamsPresent[pPlayer->GetGameTeam()] = true;
+        }
+        m_BothTeamsPresent = aTeamsPresent[TEAM_RED] && aTeamsPresent[TEAM_BLUE];
+    }
+}
+
+void CGameControllerCTF::ReviveCheck()
+{
+    bool Respawn = true;
+    for(int i = 0; i < MAX_CLIENTS && Respawn; i++)
+    {
+        CPlayer *pPlayer = GameServer()->m_apPlayers[i];
+        if(!pPlayer)
+            continue;
+        if(!pPlayer->IsDeadTeam())
+            Respawn = false;
+    }
+
+    if(Respawn)
+        Revive();
+}
+
+void CGameControllerCTF::Revive()
+{
+    for(int i = 0; i < MAX_CLIENTS; i++)
+    {
+        CPlayer *pPlayer = GameServer()->m_apPlayers[i];
+        if(!pPlayer)
+            continue;
+        pPlayer->Revive();
+    }
 }
diff --git a/src/game/server/gamemodes/ctf.h b/src/game/server/gamemodes/ctf.h
index 72747ed..dfe4322 100644
--- a/src/game/server/gamemodes/ctf.h
+++ b/src/game/server/gamemodes/ctf.h
@@ -18,6 +18,12 @@ class CGameControllerCTF : public IGameController
 
     virtual bool OnEntity(int Index, vec2 Pos);
     virtual int OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon);
+
+    virtual void ReviveCheck();
+    void Revive();
+
+private:
+    bool m_BothTeamsPresent;
 };
 
 #endif
diff --git a/src/game/server/gamemodes/dm.cpp b/src/game/server/gamemodes/dm.cpp
deleted file mode 100644
index bdca4c9..0000000
--- a/src/game/server/gamemodes/dm.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-/* (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 "dm.h"
-
-
-CGameControllerDM::CGameControllerDM(class CGameContext *pGameServer)
-: IGameController(pGameServer)
-{
-    m_pGameType = "DM";
-}
-
-void CGameControllerDM::Tick()
-{
-    IGameController::Tick();
-}
diff --git a/src/game/server/gamemodes/dm.h b/src/game/server/gamemodes/dm.h
deleted file mode 100644
index e88fad0..0000000
--- a/src/game/server/gamemodes/dm.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/* (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.                */
-#ifndef GAME_SERVER_GAMEMODES_DM_H
-#define GAME_SERVER_GAMEMODES_DM_H
-#include <game/server/gamecontroller.h>
-
-class CGameControllerDM : public IGameController
-{
-public:
-    CGameControllerDM(class CGameContext *pGameServer);
-    virtual void Tick();
-};
-#endif
diff --git a/src/game/server/gamemodes/mod.cpp b/src/game/server/gamemodes/mod.cpp
deleted file mode 100644
index eb8fd7c..0000000
--- a/src/game/server/gamemodes/mod.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-/* (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 "mod.h"
-
-CGameControllerMOD::CGameControllerMOD(class CGameContext *pGameServer)
-: IGameController(pGameServer)
-{
-    // Exchange this to a string that identifies your game mode.
-    // DM, TDM and CTF are reserved for teeworlds original modes.
-    m_pGameType = "MOD";
-
-    //m_GameFlags = GAMEFLAG_TEAMS; // GAMEFLAG_TEAMS makes it a two-team gamemode
-}
-
-void CGameControllerMOD::Tick()
-{
-    // this is the main part of the gamemode, this function is run every tick
-
-    IGameController::Tick();
-}
diff --git a/src/game/server/gamemodes/mod.h b/src/game/server/gamemodes/mod.h
deleted file mode 100644
index 847d35f..0000000
--- a/src/game/server/gamemodes/mod.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/* (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.                */
-#ifndef GAME_SERVER_GAMEMODES_MOD_H
-#define GAME_SERVER_GAMEMODES_MOD_H
-#include <game/server/gamecontroller.h>
-
-// you can subclass GAMECONTROLLER_CTF, GAMECONTROLLER_TDM etc if you want
-// todo a modification with their base as well.
-class CGameControllerMOD : public IGameController
-{
-public:
-    CGameControllerMOD(class CGameContext *pGameServer);
-    virtual void Tick();
-    // add more virtual functions here if you wish
-};
-#endif
diff --git a/src/game/server/gamemodes/tdm.cpp b/src/game/server/gamemodes/tdm.cpp
deleted file mode 100644
index 373bbd0..0000000
--- a/src/game/server/gamemodes/tdm.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-/* (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/server/entities/character.h>
-#include <game/server/player.h>
-#include "tdm.h"
-
-CGameControllerTDM::CGameControllerTDM(class CGameContext *pGameServer) : IGameController(pGameServer)
-{
-    m_pGameType = "TDM";
-    m_GameFlags = GAMEFLAG_TEAMS;
-}
-
-int CGameControllerTDM::OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon)
-{
-    IGameController::OnCharacterDeath(pVictim, pKiller, Weapon);
-
-
-    if(pKiller && Weapon != WEAPON_GAME)
-    {
-        // do team scoring
-        if(pKiller == pVictim->GetPlayer() || pKiller->GetTeam() == pVictim->GetPlayer()->GetTeam())
-            m_aTeamscore[pKiller->GetTeam()&1]--; // klant arschel
-        else
-            m_aTeamscore[pKiller->GetTeam()&1]++; // good shit
-    }
-
-    pVictim->GetPlayer()->m_RespawnTick = max(pVictim->GetPlayer()->m_RespawnTick, Server()->Tick()+Server()->TickSpeed()*g_Config.m_SvRespawnDelayTDM);
-
-    return 0;
-}
-
-void CGameControllerTDM::Snap(int SnappingClient)
-{
-    IGameController::Snap(SnappingClient);
-
-    CNetObj_GameData *pGameDataObj = (CNetObj_GameData *)Server()->SnapNewItem(NETOBJTYPE_GAMEDATA, 0, sizeof(CNetObj_GameData));
-    if(!pGameDataObj)
-        return;
-
-    pGameDataObj->m_TeamscoreRed = m_aTeamscore[TEAM_RED];
-    pGameDataObj->m_TeamscoreBlue = m_aTeamscore[TEAM_BLUE];
-
-    pGameDataObj->m_FlagCarrierRed = 0;
-    pGameDataObj->m_FlagCarrierBlue = 0;
-}
-
-void CGameControllerTDM::Tick()
-{
-    IGameController::Tick();
-}
diff --git a/src/game/server/gamemodes/tdm.h b/src/game/server/gamemodes/tdm.h
deleted file mode 100644
index 297b48c..0000000
--- a/src/game/server/gamemodes/tdm.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/* (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.                */
-#ifndef GAME_SERVER_GAMEMODES_TDM_H
-#define GAME_SERVER_GAMEMODES_TDM_H
-#include <game/server/gamecontroller.h>
-
-class CGameControllerTDM : public IGameController
-{
-public:
-    CGameControllerTDM(class CGameContext *pGameServer);
-
-    int OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon);
-    virtual void Snap(int SnappingClient);
-    virtual void Tick();
-};
-#endif
diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp
index 4b37385..71219b6 100644
--- a/src/game/server/player.cpp
+++ b/src/game/server/player.cpp
@@ -17,10 +17,9 @@ CPlayer::CPlayer(CGameContext *pGameServer, int ClientID, int Team)
     m_ScoreStartTick = Server()->Tick();
     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();
+    SetTeamSimple(DeadTeam(Team), true);
 }
 
 CPlayer::~CPlayer()
@@ -62,7 +61,7 @@ void CPlayer::Tick()
 
     if(!GameServer()->m_World.m_Paused)
     {
-        if(!m_pCharacter && m_Team == TEAM_SPECTATORS && m_SpectatorID == SPEC_FREEVIEW)
+        if(!m_pCharacter && IsSpectator() && 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())
@@ -106,7 +105,7 @@ void CPlayer::PostTick()
     }
 
     // update view pos for spectators
-    if(m_Team == TEAM_SPECTATORS && m_SpectatorID != SPEC_FREEVIEW && GameServer()->m_apPlayers[m_SpectatorID])
+    if(IsSpectator() && m_SpectatorID != SPEC_FREEVIEW && GameServer()->m_apPlayers[m_SpectatorID])
         m_ViewPos = GameServer()->m_apPlayers[m_SpectatorID]->m_ViewPos;
 }
 
@@ -138,12 +137,12 @@ void CPlayer::Snap(int SnappingClient)
     pPlayerInfo->m_Local = 0;
     pPlayerInfo->m_ClientID = m_ClientID;
     pPlayerInfo->m_Score = m_Score;
-    pPlayerInfo->m_Team = m_Team;
+    pPlayerInfo->m_Team = SnapTeam(m_Team);
 
     if(m_ClientID == SnappingClient)
         pPlayerInfo->m_Local = 1;
 
-    if(m_ClientID == SnappingClient && m_Team == TEAM_SPECTATORS)
+    if(m_ClientID == SnappingClient && IsSpectator())
     {
         CNetObj_SpectatorInfo *pSpectatorInfo = static_cast<CNetObj_SpectatorInfo *>(Server()->SnapNewItem(NETOBJTYPE_SPECTATORINFO, m_ClientID, sizeof(CNetObj_SpectatorInfo)));
         if(!pSpectatorInfo)
@@ -204,7 +203,7 @@ void CPlayer::OnDirectInput(CNetObj_PlayerInput *NewInput)
     if(m_pCharacter)
         m_pCharacter->OnDirectInput(NewInput);
 
-    if(!m_pCharacter && m_Team != TEAM_SPECTATORS && (NewInput->m_Fire&1))
+    if(!m_pCharacter && IsSpectator() && (NewInput->m_Fire&1))
         m_Spawning = true;
 
     // check for activity
@@ -237,15 +236,39 @@ void CPlayer::KillCharacter(int Weapon)
 
 void CPlayer::Respawn()
 {
-    if(m_Team != TEAM_SPECTATORS)
+    if(!IsSpectator())
         m_Spawning = true;
 }
 
-void CPlayer::SetTeam(int Team, bool DoChatMsg)
+void CPlayer::Revive()
+{
+    SetTeamSimple(GetGameTeam());
+}
+
+void CPlayer::SetTeamSimple(int Team, bool Init)
+{
+    m_Team = Team;
+    m_SpectatorID = SPEC_FREEVIEW;
+    if(Init)
+        return;
+    GameServer()->m_pController->OnPlayerInfoChange(GameServer()->m_apPlayers[m_ClientID]);
+
+    if(Team == TEAM_SPECTATORS)
+    {
+        // update spectator modes
+        for(int i = 0; i < MAX_CLIENTS; ++i)
+        {
+            if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->m_SpectatorID == m_ClientID)
+                GameServer()->m_apPlayers[i]->m_SpectatorID = SPEC_FREEVIEW;
+        }
+    }
+}
+
+void CPlayer::SetGameTeam(int Team, bool DoChatMsg)
 {
     // clamp the team
     Team = GameServer()->m_pController->ClampTeam(Team);
-    if(m_Team == Team)
+    if(GetGameTeam() == Team)
         return;
 
     char aBuf[512];
@@ -257,32 +280,27 @@ void CPlayer::SetTeam(int Team, bool DoChatMsg)
 
     KillCharacter();
 
-    m_Team = Team;
     m_LastActionTick = Server()->Tick();
-    m_SpectatorID = SPEC_FREEVIEW;
     // we got to wait 0.5 secs before respawning
     m_RespawnTick = Server()->Tick()+Server()->TickSpeed()/2;
-    str_format(aBuf, sizeof(aBuf), "team_join player='%d:%s' m_Team=%d", m_ClientID, Server()->ClientName(m_ClientID), m_Team);
+    str_format(aBuf, sizeof(aBuf), "team_join player='%d:%s' team=%d", m_ClientID, Server()->ClientName(m_ClientID), Team);
     GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf);
-
-    GameServer()->m_pController->OnPlayerInfoChange(GameServer()->m_apPlayers[m_ClientID]);
-
-    if(Team == TEAM_SPECTATORS)
-    {
-        // update spectator modes
-        for(int i = 0; i < MAX_CLIENTS; ++i)
-        {
-            if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->m_SpectatorID == m_ClientID)
-                GameServer()->m_apPlayers[i]->m_SpectatorID = SPEC_FREEVIEW;
-        }
-    }
+    // If we're currently alive (i.e. not spectator and not dead), be alive
+    // on the new team as well.
+    if(!IsDeadTeam())
+        SetTeamSimple(Team);
+    else
+        SetTeamSimple(DeadTeam(Team));
 }
 
 void CPlayer::TryRespawn()
 {
     vec2 SpawnPos;
 
-    if(!GameServer()->m_pController->CanSpawn(m_Team, &SpawnPos))
+    if(IsSpectator())
+        return;
+
+    if(!GameServer()->m_pController->CanSpawn(GetGameTeam(), &SpawnPos))
         return;
 
     m_Spawning = false;
diff --git a/src/game/server/player.h b/src/game/server/player.h
index dd804a9..64703bb 100644
--- a/src/game/server/player.h
+++ b/src/game/server/player.h
@@ -20,8 +20,15 @@ class CPlayer
 
     void TryRespawn();
     void Respawn();
-    void SetTeam(int Team, bool DoChatMsg=true);
+    void SetGameTeam(int Team, bool DoChatMsg=true);
+    void SetTeamSimple(int Team, bool Init=false);
+    void Revive();
+private:
     int GetTeam() const { return m_Team; };
+public:
+    int GetGameTeam() const { return GameTeam(GetTeam()); }
+    bool IsSpectator() const { return IsSpectatorTeam(GetTeam()); }
+    bool IsDeadTeam() const { return !IsAliveTeam(GetTeam()); }
     int GetCID() const { return m_ClientID; };
 
     void Tick();

18 (edited by SandSnake 2015-08-22 21:41:07)

Re: [Idea]CTF without respawn

heinrich5991 wrote:
xush wrote:

So I will start developing this mod now. As i am new to teeworlds development, you are very welcome to contribute or post anything wrong/unclean as an issue on GitHub or post here.

https://github.com/xushTW/CTFRespawn (Just initialized, might not contain any changes when you view it)

By the way, what would you guys suggest as mod name (will use something temporary for now)?

Edit: For thoose who are intrested in the developing progress: here you can find a summary of which changes have been done.

I'm sorry, I haven't seen that you develop the mod, now I developed it on my own too. hmm

Here's the source code, I named the mod 'CFT' because it's garbled CTF – and it was the original thread title. smile

From a47551c6daabd75c7eda0c3e56f697dbfe1a179e Mon Sep 17 00:00:00 2001
From: heinrich5991 <heinrich5991@gmail.com>
Date: Sat, 22 Aug 2015 17:33:27 +0200
Subject: [PATCH] Implement the 'CFT' mod, Capture The Flag without respawn

The basic idea is simple, don't respawn until either one team is dead or the
flag is captured.

Still needs some fine tuning wrt. initial joining.
---
 datasrc/compile.py                     |  50 ++++++++++++++
 datasrc/network.py                     |   8 +++
 scripts/cmd5.py                        |   1 +
 src/game/server/entities/character.cpp |   8 +--
 src/game/server/gamecontext.cpp        |  71 ++++++++++----------
 src/game/server/gamecontroller.cpp     |  51 ++++++++-------
 src/game/server/gamecontroller.h       |   1 +
 src/game/server/gamemodes/ctf.cpp      | 115 +++++++++++++++++++++++++++++++--
 src/game/server/gamemodes/ctf.h        |   6 ++
 src/game/server/gamemodes/dm.cpp       |  15 -----
 src/game/server/gamemodes/dm.h         |  13 ----
 src/game/server/gamemodes/mod.cpp      |  20 ------
 src/game/server/gamemodes/mod.h        |  16 -----
 src/game/server/gamemodes/tdm.cpp      |  52 ---------------
 src/game/server/gamemodes/tdm.h        |  16 -----
 src/game/server/player.cpp             |  70 ++++++++++++--------
 src/game/server/player.h               |   9 ++-
 17 files changed, 292 insertions(+), 230 deletions(-)
 delete mode 100644 src/game/server/gamemodes/dm.cpp
 delete mode 100644 src/game/server/gamemodes/dm.h
 delete mode 100644 src/game/server/gamemodes/mod.cpp
 delete mode 100644 src/game/server/gamemodes/mod.h
 delete mode 100644 src/game/server/gamemodes/tdm.cpp
 delete mode 100644 src/game/server/gamemodes/tdm.h

diff --git a/datasrc/compile.py b/datasrc/compile.py
index 088e500..346e8f6 100644
--- a/datasrc/compile.py
+++ b/datasrc/compile.py
@@ -159,6 +159,56 @@ class CNetObjHandler
     lines += ['#include <engine/message.h>']
     lines += ['#include "protocol.h"']
 
+    lines += ["""
+int SnapTeam(int Team)
+{
+    switch(Team)
+    {
+    case TEAM_RED_DEAD:
+    case TEAM_BLUE_DEAD:
+        return TEAM_SPECTATORS;
+    default:
+        return Team;
+    }
+}
+
+bool IsSpectatorTeam(int Team)
+{
+    return SnapTeam(Team) == TEAM_SPECTATORS;
+}
+
+bool IsAliveTeam(int Team)
+{
+    return Team == TEAM_RED || Team == TEAM_BLUE;
+}
+
+int GameTeam(int Team)
+{
+    switch(Team)
+    {
+    case TEAM_RED_DEAD:
+        return TEAM_RED;
+    case TEAM_BLUE_DEAD:
+        return TEAM_BLUE;
+    default:
+        return Team;
+    }
+}
+
+int DeadTeam(int Team)
+{
+    switch(Team)
+    {
+    case TEAM_RED:
+        return TEAM_RED_DEAD;
+    case TEAM_BLUE:
+        return TEAM_BLUE_DEAD;
+    default:
+        return Team;
+    }
+}
+    """]
+
     lines += ['CNetObjHandler::CNetObjHandler()']
     lines += ['{']
     lines += ['\tm_pMsgFailedOn = "";']
diff --git a/datasrc/network.py b/datasrc/network.py
index b2c42b3..8a699a8 100644
--- a/datasrc/network.py
+++ b/datasrc/network.py
@@ -23,6 +23,8 @@
     TEAM_SPECTATORS=-1,
     TEAM_RED,
     TEAM_BLUE,
+    TEAM_RED_DEAD,
+    TEAM_BLUE_DEAD,
 
     FLAG_MISSING=-3,
     FLAG_ATSTAND,
@@ -30,6 +32,12 @@
 
     SPEC_FREEVIEW=-1,
 };
+
+int SnapTeam(int Team);
+int GameTeam(int Team);
+int DeadTeam(int Team);
+bool IsSpectatorTeam(int Team);
+bool IsAliveTeam(int Team);
 '''
 
 RawSource = '''
diff --git a/scripts/cmd5.py b/scripts/cmd5.py
index 68881d0..f347e32 100644
--- a/scripts/cmd5.py
+++ b/scripts/cmd5.py
@@ -32,4 +32,5 @@ def cstrip(lines):
 #TODO 0.7: improve nethash creation
 if hash == "e42d81cd67b8c7bc":
     hash = "626fce9a778df4d4"
+hash = "626fce9a778df4d4"
 print('#define GAME_NETVERSION_HASH "%s"' % hash)
diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp
index f45100a..a98503e 100644
--- a/src/game/server/entities/character.cpp
+++ b/src/game/server/entities/character.cpp
@@ -526,7 +526,7 @@ void CCharacter::OnDirectInput(CNetObj_PlayerInput *pNewInput)
     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)
+    if(m_NumInputs > 2 && !m_pPlayer->IsSpectator())
     {
         HandleWeaponSwitch();
         FireWeapon();
@@ -552,7 +552,7 @@ void CCharacter::Tick()
     if(m_pPlayer->m_ForceBalanced)
     {
         char Buf[128];
-        str_format(Buf, sizeof(Buf), "You were moved to %s due to team balancing", GameServer()->m_pController->GetTeamName(m_pPlayer->GetTeam()));
+        str_format(Buf, sizeof(Buf), "You were moved to %s due to team balancing", GameServer()->m_pController->GetTeamName(m_pPlayer->GetGameTeam()));
         GameServer()->SendBroadcast(Buf, m_pPlayer->GetCID());
 
         m_pPlayer->m_ForceBalanced = false;
@@ -637,7 +637,7 @@ void CCharacter::TickDefered()
     if(Events&COREEVENT_HOOK_HIT_NOHOOK) GameServer()->CreateSound(m_Pos, SOUND_HOOK_NOATTACH, Mask);
 
 
-    if(m_pPlayer->GetTeam() == TEAM_SPECTATORS)
+    if(m_pPlayer->IsSpectator())
     {
         m_Pos.x = m_Input.m_TargetX;
         m_Pos.y = m_Input.m_TargetY;
@@ -782,7 +782,7 @@ bool CCharacter::TakeDamage(vec2 Force, int Dmg, int From, int Weapon)
         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)
+            if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->IsSpectator() && GameServer()->m_apPlayers[i]->m_SpectatorID == From)
                 Mask |= CmaskOne(i);
         }
         GameServer()->CreateSound(GameServer()->m_apPlayers[From]->m_ViewPos, SOUND_HIT, Mask);
diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp
index 897e348..bf3091c 100644
--- a/src/game/server/gamecontext.cpp
+++ b/src/game/server/gamecontext.cpp
@@ -9,10 +9,7 @@
 #include <game/version.h>
 #include <game/collision.h>
 #include <game/gamecore.h>
-#include "gamemodes/dm.h"
-#include "gamemodes/tdm.h"
 #include "gamemodes/ctf.h"
-#include "gamemodes/mod.h"
 
 enum
 {
@@ -257,7 +254,7 @@ void CGameContext::SendChat(int ChatterClientID, int Team, const char *pText)
         // send to the clients
         for(int i = 0; i < MAX_CLIENTS; i++)
         {
-            if(m_apPlayers[i] && m_apPlayers[i]->GetTeam() == Team)
+            if(m_apPlayers[i] && m_apPlayers[i]->GetGameTeam() == Team)
                 Server()->SendPackMsg(&Msg, MSGFLAG_VITAL|MSGFLAG_NORECORD, i);
         }
     }
@@ -397,8 +394,8 @@ void CGameContext::SwapTeams()
 
     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);
+        if(m_apPlayers[i] && m_apPlayers[i]->GetGameTeam() != TEAM_SPECTATORS)
+            m_apPlayers[i]->SetGameTeam(m_apPlayers[i]->GetGameTeam()^1, false);
     }
 
     (void)m_pController->CheckTeamBalance();
@@ -447,7 +444,7 @@ void CGameContext::OnTick()
                 bool aVoteChecked[MAX_CLIENTS] = {0};
                 for(int i = 0; i < MAX_CLIENTS; i++)
                 {
-                    if(!m_apPlayers[i] || m_apPlayers[i]->GetTeam() == TEAM_SPECTATORS || aVoteChecked[i])    // don't count in votes by spectators
+                    if(!m_apPlayers[i] || m_apPlayers[i]->GetGameTeam() == TEAM_SPECTATORS || aVoteChecked[i])    // don't count in votes by spectators
                         continue;
 
                     int ActVote = m_apPlayers[i]->m_Vote;
@@ -536,10 +533,10 @@ void CGameContext::OnClientEnter(int ClientID)
     //world.insert_entity(&players[client_id]);
     m_apPlayers[ClientID]->Respawn();
     char aBuf[512];
-    str_format(aBuf, sizeof(aBuf), "'%s' entered and joined the %s", Server()->ClientName(ClientID), m_pController->GetTeamName(m_apPlayers[ClientID]->GetTeam()));
+    str_format(aBuf, sizeof(aBuf), "'%s' entered and joined the %s", Server()->ClientName(ClientID), m_pController->GetTeamName(m_apPlayers[ClientID]->GetGameTeam()));
     SendChat(-1, CGameContext::CHAT_ALL, aBuf);
 
-    str_format(aBuf, sizeof(aBuf), "team_join player='%d:%s' team=%d", ClientID, Server()->ClientName(ClientID), m_apPlayers[ClientID]->GetTeam());
+    str_format(aBuf, sizeof(aBuf), "team_join player='%d:%s' team=%d", ClientID, Server()->ClientName(ClientID), m_apPlayers[ClientID]->GetGameTeam());
     Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf);
 
     m_VoteUpdate = true;
@@ -551,6 +548,7 @@ void CGameContext::OnClientConnected(int ClientID)
     const int StartTeam = g_Config.m_SvTournamentMode ? TEAM_SPECTATORS : m_pController->GetAutoTeam(ClientID);
 
     m_apPlayers[ClientID] = new(ClientID) CPlayer(this, ClientID, StartTeam);
+    m_apPlayers[ClientID]->Revive();
     //players[client_id].init(client_id);
     //players[client_id].client_id = client_id;
 
@@ -616,7 +614,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
                 return;
 
             CNetMsg_Cl_Say *pMsg = (CNetMsg_Cl_Say *)pRawMsg;
-            int Team = pMsg->m_Team ? pPlayer->GetTeam() : CGameContext::CHAT_ALL;
+            int Team = pMsg->m_Team ? pPlayer->GetGameTeam() : CGameContext::CHAT_ALL;
             
             // trim right and set maximum length to 128 utf8-characters
             int Length = 0;
@@ -661,7 +659,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
 
             int64 Now = Server()->Tick();
             pPlayer->m_LastVoteTry = Now;
-            if(pPlayer->GetTeam() == TEAM_SPECTATORS)
+            if(pPlayer->GetGameTeam() == TEAM_SPECTATORS)
             {
                 SendChatTarget(ClientID, "Spectators aren't allowed to start a vote.");
                 return;
@@ -724,7 +722,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
                 {
                     int PlayerNum = 0;
                     for(int i = 0; i < MAX_CLIENTS; ++i)
-                        if(m_apPlayers[i] && m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS)
+                        if(m_apPlayers[i] && m_apPlayers[i]->GetGameTeam() != TEAM_SPECTATORS)
                             ++PlayerNum;
 
                     if(PlayerNum < g_Config.m_SvVoteKickMin)
@@ -775,7 +773,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
                 }
 
                 int SpectateID = str_toint(pMsg->m_Value);
-                if(SpectateID < 0 || SpectateID >= MAX_CLIENTS || !m_apPlayers[SpectateID] || m_apPlayers[SpectateID]->GetTeam() == TEAM_SPECTATORS)
+                if(SpectateID < 0 || SpectateID >= MAX_CLIENTS || !m_apPlayers[SpectateID] || m_apPlayers[SpectateID]->GetGameTeam() == TEAM_SPECTATORS)
                 {
                     SendChatTarget(ClientID, "Invalid client id to move");
                     return;
@@ -820,8 +818,16 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
         else if (MsgID == NETMSGTYPE_CL_SETTEAM && !m_World.m_Paused)
         {
             CNetMsg_Cl_SetTeam *pMsg = (CNetMsg_Cl_SetTeam *)pRawMsg;
+            CNetMsg_Cl_SetTeam Copy = *pMsg;
+            pMsg = &Copy;
 
-            if(pPlayer->GetTeam() == pMsg->m_Team || (g_Config.m_SvSpamprotection && pPlayer->m_LastSetTeam && pPlayer->m_LastSetTeam+Server()->TickSpeed()*3 > Server()->Tick()))
+            // The button for joining the spectators is not
+            // available in the client when the player is dead. If
+            // you join your own team, join the spectators instead.
+            if(pPlayer->GetGameTeam() == pMsg->m_Team)
+                pMsg->m_Team = TEAM_SPECTATORS;
+
+            if(pPlayer->GetGameTeam() == 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)
@@ -847,9 +853,9 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
                 if(m_pController->CanChangeTeam(pPlayer, pMsg->m_Team))
                 {
                     pPlayer->m_LastSetTeam = Server()->Tick();
-                    if(pPlayer->GetTeam() == TEAM_SPECTATORS || pMsg->m_Team == TEAM_SPECTATORS)
+                    if(pPlayer->GetGameTeam() == TEAM_SPECTATORS || pMsg->m_Team == TEAM_SPECTATORS)
                         m_VoteUpdate = true;
-                    pPlayer->SetTeam(pMsg->m_Team);
+                    pPlayer->SetGameTeam(pMsg->m_Team);
                     (void)m_pController->CheckTeamBalance();
                     pPlayer->m_TeamChangeTick = Server()->Tick();
                 }
@@ -867,12 +873,12 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
         {
             CNetMsg_Cl_SetSpectatorMode *pMsg = (CNetMsg_Cl_SetSpectatorMode *)pRawMsg;
 
-            if(pPlayer->GetTeam() != TEAM_SPECTATORS || pPlayer->m_SpectatorID == pMsg->m_SpectatorID || ClientID == pMsg->m_SpectatorID ||
+            if(!pPlayer->IsSpectator() || pPlayer->m_SpectatorID == pMsg->m_SpectatorID || ClientID == pMsg->m_SpectatorID ||
                 (g_Config.m_SvSpamprotection && pPlayer->m_LastSetSpectatorMode && pPlayer->m_LastSetSpectatorMode+Server()->TickSpeed()*3 > Server()->Tick()))
                 return;
 
             pPlayer->m_LastSetSpectatorMode = Server()->Tick();
-            if(pMsg->m_SpectatorID != SPEC_FREEVIEW && (!m_apPlayers[pMsg->m_SpectatorID] || m_apPlayers[pMsg->m_SpectatorID]->GetTeam() == TEAM_SPECTATORS))
+            if(pMsg->m_SpectatorID != SPEC_FREEVIEW && (!m_apPlayers[pMsg->m_SpectatorID] || m_apPlayers[pMsg->m_SpectatorID]->IsSpectator()))
                 SendChatTarget(ClientID, "Invalid spectator id used");
             else
                 pPlayer->m_SpectatorID = pMsg->m_SpectatorID;
@@ -1111,7 +1117,7 @@ void CGameContext::ConSetTeam(IConsole::IResult *pResult, void *pUserData)
     pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
 
     pSelf->m_apPlayers[ClientID]->m_TeamChangeTick = pSelf->Server()->Tick()+pSelf->Server()->TickSpeed()*Delay*60;
-    pSelf->m_apPlayers[ClientID]->SetTeam(Team);
+    pSelf->m_apPlayers[ClientID]->SetGameTeam(Team);
     (void)pSelf->m_pController->CheckTeamBalance();
 }
 
@@ -1126,7 +1132,7 @@ void CGameContext::ConSetTeamAll(IConsole::IResult *pResult, void *pUserData)
 
     for(int i = 0; i < MAX_CLIENTS; ++i)
         if(pSelf->m_apPlayers[i])
-            pSelf->m_apPlayers[i]->SetTeam(Team, false);
+            pSelf->m_apPlayers[i]->SetGameTeam(Team, false);
 
     (void)pSelf->m_pController->CheckTeamBalance();
 }
@@ -1147,7 +1153,7 @@ void CGameContext::ConShuffleTeams(IConsole::IResult *pResult, void *pUserData)
     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)
+        if(pSelf->m_apPlayers[i] && pSelf->m_apPlayers[i]->GetGameTeam() != TEAM_SPECTATORS)
             ++PlayerTeam;
     PlayerTeam = (PlayerTeam+1)/2;
     
@@ -1155,22 +1161,22 @@ void CGameContext::ConShuffleTeams(IConsole::IResult *pResult, void *pUserData)
 
     for(int i = 0; i < MAX_CLIENTS; ++i)
     {
-        if(pSelf->m_apPlayers[i] && pSelf->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS)
+        if(pSelf->m_apPlayers[i] && pSelf->m_apPlayers[i]->GetGameTeam() != TEAM_SPECTATORS)
         {
             if(CounterRed == PlayerTeam)
-                pSelf->m_apPlayers[i]->SetTeam(TEAM_BLUE, false);
+                pSelf->m_apPlayers[i]->SetGameTeam(TEAM_BLUE, false);
             else if(CounterBlue == PlayerTeam)
-                pSelf->m_apPlayers[i]->SetTeam(TEAM_RED, false);
+                pSelf->m_apPlayers[i]->SetGameTeam(TEAM_RED, false);
             else
             {    
                 if(rand() % 2)
                 {
-                    pSelf->m_apPlayers[i]->SetTeam(TEAM_BLUE, false);
+                    pSelf->m_apPlayers[i]->SetGameTeam(TEAM_BLUE, false);
                     ++CounterBlue;
                 }
                 else
                 {
-                    pSelf->m_apPlayers[i]->SetTeam(TEAM_RED, false);
+                    pSelf->m_apPlayers[i]->SetGameTeam(TEAM_RED, false);
                     ++CounterRed;
                 }
             }
@@ -1380,7 +1386,7 @@ void CGameContext::ConForceVote(IConsole::IResult *pResult, void *pUserData)
     else if(str_comp_nocase(pType, "spectate") == 0)
     {
         int SpectateID = str_toint(pValue);
-        if(SpectateID < 0 || SpectateID >= MAX_CLIENTS || !pSelf->m_apPlayers[SpectateID] || pSelf->m_apPlayers[SpectateID]->GetTeam() == TEAM_SPECTATORS)
+        if(SpectateID < 0 || SpectateID >= MAX_CLIENTS || !pSelf->m_apPlayers[SpectateID] || pSelf->m_apPlayers[SpectateID]->GetGameTeam() == TEAM_SPECTATORS)
         {
             pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "Invalid client id to move");
             return;
@@ -1489,14 +1495,7 @@ void CGameContext::OnInit(/*class IKernel *pKernel*/)
     //players = new CPlayer[MAX_CLIENTS];
 
     // select gametype
-    if(str_comp(g_Config.m_SvGametype, "mod") == 0)
-        m_pController = new CGameControllerMOD(this);
-    else if(str_comp(g_Config.m_SvGametype, "ctf") == 0)
-        m_pController = new CGameControllerCTF(this);
-    else if(str_comp(g_Config.m_SvGametype, "tdm") == 0)
-        m_pController = new CGameControllerTDM(this);
-    else
-        m_pController = new CGameControllerDM(this);
+    m_pController = new CGameControllerCTF(this);
 
     // setup core world
     //for(int i = 0; i < MAX_CLIENTS; i++)
@@ -1585,7 +1584,7 @@ bool CGameContext::IsClientReady(int ClientID)
 
 bool CGameContext::IsClientPlayer(int ClientID)
 {
-    return m_apPlayers[ClientID] && m_apPlayers[ClientID]->GetTeam() == TEAM_SPECTATORS ? false : true;
+    return m_apPlayers[ClientID] && m_apPlayers[ClientID]->GetGameTeam() == TEAM_SPECTATORS ? false : true;
 }
 
 const char *CGameContext::GameType() { return m_pController && m_pController->m_pGameType ? m_pController->m_pGameType : ""; }
diff --git a/src/game/server/gamecontroller.cpp b/src/game/server/gamecontroller.cpp
index 68e5508..6bd3a5b 100644
--- a/src/game/server/gamecontroller.cpp
+++ b/src/game/server/gamecontroller.cpp
@@ -48,7 +48,7 @@ float IGameController::EvaluateSpawnPos(CSpawnEval *pEval, vec2 Pos)
     {
         // team mates are not as dangerous as enemies
         float Scoremod = 1.0f;
-        if(pEval->m_FriendlyTeam != -1 && pC->GetPlayer()->GetTeam() == pEval->m_FriendlyTeam)
+        if(pEval->m_FriendlyTeam != -1 && pC->GetPlayer()->GetGameTeam() == pEval->m_FriendlyTeam)
             Scoremod = 0.5f;
 
         float d = distance(Pos, pC->m_Pos);
@@ -326,10 +326,10 @@ void IGameController::OnPlayerInfoChange(class CPlayer *pP)
     if(IsTeamplay())
     {
         pP->m_TeeInfos.m_UseCustomColor = 1;
-        if(pP->GetTeam() >= TEAM_RED && pP->GetTeam() <= TEAM_BLUE)
+        if(pP->GetGameTeam() >= TEAM_RED && pP->GetGameTeam() <= TEAM_BLUE)
         {
-            pP->m_TeeInfos.m_ColorBody = aTeamColors[pP->GetTeam()];
-            pP->m_TeeInfos.m_ColorFeet = aTeamColors[pP->GetTeam()];
+            pP->m_TeeInfos.m_ColorBody = aTeamColors[pP->GetGameTeam()];
+            pP->m_TeeInfos.m_ColorFeet = aTeamColors[pP->GetGameTeam()];
         }
         else
         {
@@ -349,7 +349,7 @@ int IGameController::OnCharacterDeath(class CCharacter *pVictim, class CPlayer *
         pVictim->GetPlayer()->m_Score--; // suicide
     else
     {
-        if(IsTeamplay() && pVictim->GetPlayer()->GetTeam() == pKiller->GetTeam())
+        if(IsTeamplay() && pVictim->GetPlayer()->GetGameTeam() == pKiller->GetGameTeam())
             pKiller->m_Score--; // teamkill
         else
             pKiller->m_Score++; // normal kill
@@ -411,7 +411,7 @@ bool IGameController::IsFriendlyFire(int ClientID1, int ClientID2)
         if(!GameServer()->m_apPlayers[ClientID1] || !GameServer()->m_apPlayers[ClientID2])
             return false;
 
-        if(GameServer()->m_apPlayers[ClientID1]->GetTeam() == GameServer()->m_apPlayers[ClientID2]->GetTeam())
+        if(GameServer()->m_apPlayers[ClientID1]->GetGameTeam() == GameServer()->m_apPlayers[ClientID2]->GetGameTeam())
             return true;
     }
 
@@ -475,12 +475,12 @@ void IGameController::Tick()
         float aPScore[MAX_CLIENTS] = {0.0f};
         for(int i = 0; i < MAX_CLIENTS; i++)
         {
-            if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS)
+            if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetGameTeam() != TEAM_SPECTATORS)
             {
-                aT[GameServer()->m_apPlayers[i]->GetTeam()]++;
+                aT[GameServer()->m_apPlayers[i]->GetGameTeam()]++;
                 aPScore[i] = GameServer()->m_apPlayers[i]->m_Score*Server()->TickSpeed()*60.0f/
                     (Server()->Tick()-GameServer()->m_apPlayers[i]->m_ScoreStartTick);
-                aTScore[GameServer()->m_apPlayers[i]->GetTeam()] += aPScore[i];
+                aTScore[GameServer()->m_apPlayers[i]->GetGameTeam()] += aPScore[i];
             }
         }
 
@@ -499,7 +499,7 @@ void IGameController::Tick()
                     if(!GameServer()->m_apPlayers[i] || !CanBeMovedOnBalance(i))
                         continue;
                     // remember the player who would cause lowest score-difference
-                    if(GameServer()->m_apPlayers[i]->GetTeam() == M && (!pP || absolute((aTScore[M^1]+aPScore[i]) - (aTScore[M]-aPScore[i])) < PD))
+                    if(GameServer()->m_apPlayers[i]->GetGameTeam() == M && (!pP || absolute((aTScore[M^1]+aPScore[i]) - (aTScore[M]-aPScore[i])) < PD))
                     {
                         pP = GameServer()->m_apPlayers[i];
                         PD = absolute((aTScore[M^1]+aPScore[i]) - (aTScore[M]-aPScore[i]));
@@ -508,7 +508,8 @@ void IGameController::Tick()
 
                 // move the player to the other team
                 int Temp = pP->m_LastActionTick;
-                pP->SetTeam(M^1);
+                pP->SetGameTeam(M^1);
+                pP->Revive();
                 pP->m_LastActionTick = Temp;
 
                 pP->Respawn();
@@ -532,7 +533,7 @@ void IGameController::Tick()
                     break;
             }
         #endif
-            if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS && !Server()->IsAuthed(i))
+            if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetGameTeam() != TEAM_SPECTATORS && !Server()->IsAuthed(i))
             {
                 if(Server()->Tick() > GameServer()->m_apPlayers[i]->m_LastActionTick+g_Config.m_SvInactiveKickTime*Server()->TickSpeed()*60)
                 {
@@ -541,7 +542,7 @@ void IGameController::Tick()
                     case 0:
                         {
                             // move player to spectator
-                            GameServer()->m_apPlayers[i]->SetTeam(TEAM_SPECTATORS);
+                            GameServer()->m_apPlayers[i]->SetGameTeam(TEAM_SPECTATORS);
                         }
                         break;
                     case 1:
@@ -549,12 +550,12 @@ void IGameController::Tick()
                             // 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)
+                                if(GameServer()->m_apPlayers[j] && GameServer()->m_apPlayers[j]->GetGameTeam() == TEAM_SPECTATORS)
                                     ++Spectators;
                             if(Spectators >= g_Config.m_SvSpectatorSlots)
                                 Server()->Kick(i, "Kicked for inactivity");
                             else
-                                GameServer()->m_apPlayers[i]->SetTeam(TEAM_SPECTATORS);
+                                GameServer()->m_apPlayers[i]->SetGameTeam(TEAM_SPECTATORS);
                         }
                         break;
                     case 2:
@@ -612,8 +613,8 @@ int IGameController::GetAutoTeam(int NotThisID)
     {
         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()]++;
+            if(GameServer()->m_apPlayers[i]->GetGameTeam() >= TEAM_RED && GameServer()->m_apPlayers[i]->GetGameTeam() <= TEAM_BLUE)
+                aNumplayers[GameServer()->m_apPlayers[i]->GetGameTeam()]++;
         }
     }
 
@@ -628,7 +629,7 @@ int IGameController::GetAutoTeam(int NotThisID)
 
 bool IGameController::CanJoinTeam(int Team, int NotThisID)
 {
-    if(Team == TEAM_SPECTATORS || (GameServer()->m_apPlayers[NotThisID] && GameServer()->m_apPlayers[NotThisID]->GetTeam() != TEAM_SPECTATORS))
+    if(Team == TEAM_SPECTATORS || (GameServer()->m_apPlayers[NotThisID] && GameServer()->m_apPlayers[NotThisID]->GetGameTeam() != TEAM_SPECTATORS))
         return true;
 
     int aNumplayers[2] = {0,0};
@@ -636,8 +637,8 @@ bool IGameController::CanJoinTeam(int Team, int NotThisID)
     {
         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()]++;
+            if(GameServer()->m_apPlayers[i]->GetGameTeam() >= TEAM_RED && GameServer()->m_apPlayers[i]->GetGameTeam() <= TEAM_BLUE)
+                aNumplayers[GameServer()->m_apPlayers[i]->GetGameTeam()]++;
         }
     }
 
@@ -653,8 +654,8 @@ bool IGameController::CheckTeamBalance()
     for(int i = 0; i < MAX_CLIENTS; i++)
     {
         CPlayer *pP = GameServer()->m_apPlayers[i];
-        if(pP && pP->GetTeam() != TEAM_SPECTATORS)
-            aT[pP->GetTeam()]++;
+        if(pP && pP->GetGameTeam() != TEAM_SPECTATORS)
+            aT[pP->GetGameTeam()]++;
     }
 
     char aBuf[256];
@@ -685,13 +686,13 @@ bool IGameController::CanChangeTeam(CPlayer *pPlayer, int JoinTeam)
     for(int i = 0; i < MAX_CLIENTS; i++)
     {
         CPlayer *pP = GameServer()->m_apPlayers[i];
-        if(pP && pP->GetTeam() != TEAM_SPECTATORS)
-            aT[pP->GetTeam()]++;
+        if(pP && pP->GetGameTeam() != TEAM_SPECTATORS)
+            aT[pP->GetGameTeam()]++;
     }
 
     // simulate what would happen if changed team
     aT[JoinTeam]++;
-    if (pPlayer->GetTeam() != TEAM_SPECTATORS)
+    if (pPlayer->GetGameTeam() != TEAM_SPECTATORS)
         aT[JoinTeam^1]--;
 
     // there is a player-difference of at least 2
diff --git a/src/game/server/gamecontroller.h b/src/game/server/gamecontroller.h
index 66ff1dc..c1d01c9 100644
--- a/src/game/server/gamecontroller.h
+++ b/src/game/server/gamecontroller.h
@@ -144,6 +144,7 @@ class IGameController
     int ClampTeam(int Team);
 
     virtual void PostReset();
+    virtual void ReviveCheck() = 0;
 };
 
 #endif
diff --git a/src/game/server/gamemodes/ctf.cpp b/src/game/server/gamemodes/ctf.cpp
index 140dadf..1835a6b 100644
--- a/src/game/server/gamemodes/ctf.cpp
+++ b/src/game/server/gamemodes/ctf.cpp
@@ -15,8 +15,9 @@ CGameControllerCTF::CGameControllerCTF(class CGameContext *pGameServer)
 {
     m_apFlags[0] = 0;
     m_apFlags[1] = 0;
-    m_pGameType = "CTF";
+    m_pGameType = "CFT";
     m_GameFlags = GAMEFLAG_TEAMS|GAMEFLAG_FLAGS;
+    m_BothTeamsPresent = false;
 }
 
 bool CGameControllerCTF::OnEntity(int Index, vec2 Pos)
@@ -56,13 +57,38 @@ int CGameControllerCTF::OnCharacterDeath(class CCharacter *pVictim, class CPlaye
             F->m_pCarryingCharacter = 0;
             F->m_Vel = vec2(0,0);
 
-            if(pKiller && pKiller->GetTeam() != pVictim->GetPlayer()->GetTeam())
+            if(pKiller && pKiller->GetGameTeam() != pVictim->GetPlayer()->GetGameTeam())
                 pKiller->m_Score++;
 
             HadFlag |= 1;
         }
     }
 
+    // Don't mark the player as dead in case of team changes
+    if(WeaponID != WEAPON_GAME)
+    {
+        CPlayer *pVictimPlayer = pVictim->GetPlayer();
+        pVictimPlayer->SetTeamSimple(DeadTeam(pVictimPlayer->GetGameTeam()));
+
+        int NewSpectatorID;
+        if(pKiller && pKiller->GetCID() != pVictimPlayer->GetCID())
+            NewSpectatorID = pKiller->GetCID();
+        else
+            NewSpectatorID = SPEC_FREEVIEW;
+
+        pVictimPlayer->m_SpectatorID = NewSpectatorID;
+
+        for(int i = 0; i < MAX_CLIENTS; i++)
+        {
+            CPlayer *pPlayer = GameServer()->m_apPlayers[i];
+            if(!pPlayer)
+                continue;
+            if(pPlayer->m_SpectatorID == pVictimPlayer->GetCID())
+                pPlayer->m_SpectatorID = NewSpectatorID;
+        }
+    }
+    ReviveCheck();
+
     return HadFlag;
 }
 
@@ -197,6 +223,8 @@ void CGameControllerCTF::Tick()
                         m_apFlags[i]->Reset();
 
                     GameServer()->CreateSoundGlobal(SOUND_CTF_CAPTURE);
+
+                    Revive();
                 }
             }
         }
@@ -206,10 +234,10 @@ void CGameControllerCTF::Tick()
             int Num = GameServer()->m_World.FindEntities(F->m_Pos, CFlag::ms_PhysSize, (CEntity**)apCloseCCharacters, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER);
             for(int i = 0; i < Num; i++)
             {
-                if(!apCloseCCharacters[i]->IsAlive() || apCloseCCharacters[i]->GetPlayer()->GetTeam() == TEAM_SPECTATORS || GameServer()->Collision()->IntersectLine(F->m_Pos, apCloseCCharacters[i]->m_Pos, NULL, NULL))
+                if(!apCloseCCharacters[i]->IsAlive() || apCloseCCharacters[i]->GetPlayer()->IsSpectator() || GameServer()->Collision()->IntersectLine(F->m_Pos, apCloseCCharacters[i]->m_Pos, NULL, NULL))
                     continue;
 
-                if(apCloseCCharacters[i]->GetPlayer()->GetTeam() == F->m_Team)
+                if(apCloseCCharacters[i]->GetPlayer()->GetGameTeam() == F->m_Team)
                 {
                     // return the flag
                     if(!F->m_AtStand)
@@ -252,9 +280,9 @@ void CGameControllerCTF::Tick()
                         if(!pPlayer)
                             continue;
 
-                        if(pPlayer->GetTeam() == TEAM_SPECTATORS && pPlayer->m_SpectatorID != SPEC_FREEVIEW && GameServer()->m_apPlayers[pPlayer->m_SpectatorID] && GameServer()->m_apPlayers[pPlayer->m_SpectatorID]->GetTeam() == fi)
+                        if(pPlayer->IsSpectator() && pPlayer->m_SpectatorID != SPEC_FREEVIEW && GameServer()->m_apPlayers[pPlayer->m_SpectatorID] && GameServer()->m_apPlayers[pPlayer->m_SpectatorID]->GetGameTeam() == fi)
                             GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_EN, c);
-                        else if(pPlayer->GetTeam() == fi)
+                        else if(pPlayer->GetGameTeam() == fi)
                             GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_EN, c);
                         else
                             GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_PL, c);
@@ -280,4 +308,79 @@ void CGameControllerCTF::Tick()
             }
         }
     }
+
+    {
+        int TeamAlive = TEAM_SPECTATORS;
+        bool OnlyOneTeamAlive = true;
+        for(int i = 0; i < MAX_CLIENTS; i++)
+        {
+            CPlayer *pPlayer = GameServer()->m_apPlayers[i];
+            if(!pPlayer || pPlayer->GetGameTeam() == TEAM_SPECTATORS)
+                continue;
+            if(!pPlayer->IsDeadTeam() && pPlayer)
+            {
+                if(TeamAlive == TEAM_SPECTATORS)
+                    TeamAlive = pPlayer->GetGameTeam();
+                else if(TeamAlive != pPlayer->GetGameTeam())
+                    OnlyOneTeamAlive = false;
+            }
+        }
+
+        if(OnlyOneTeamAlive)
+        {
+            if(m_BothTeamsPresent)
+            {
+                char aBuf[64];
+                str_format(aBuf, sizeof(aBuf), "The %s team eliminated all enemies", TeamAlive ? "blue" : "red");
+                GameServer()->SendChat(-1, -2, aBuf);
+
+                for(int i = 0; i < 2; i++)
+                    if(m_apFlags[i])
+                        m_apFlags[i]->Reset();
+
+                m_aTeamscore[TeamAlive] += 100;
+
+                GameServer()->CreateSoundGlobal(SOUND_CTF_CAPTURE);
+            }
+
+            Revive();
+        }
+
+        bool aTeamsPresent[2] = { false, false };
+        for(int i = 0; i < MAX_CLIENTS; i++)
+        {
+            CPlayer *pPlayer = GameServer()->m_apPlayers[i];
+            if(!pPlayer || pPlayer->GetGameTeam() == TEAM_SPECTATORS)
+                continue;
+            aTeamsPresent[pPlayer->GetGameTeam()] = true;
+        }
+        m_BothTeamsPresent = aTeamsPresent[TEAM_RED] && aTeamsPresent[TEAM_BLUE];
+    }
+}
+
+void CGameControllerCTF::ReviveCheck()
+{
+    bool Respawn = true;
+    for(int i = 0; i < MAX_CLIENTS && Respawn; i++)
+    {
+        CPlayer *pPlayer = GameServer()->m_apPlayers[i];
+        if(!pPlayer)
+            continue;
+        if(!pPlayer->IsDeadTeam())
+            Respawn = false;
+    }
+
+    if(Respawn)
+        Revive();
+}
+
+void CGameControllerCTF::Revive()
+{
+    for(int i = 0; i < MAX_CLIENTS; i++)
+    {
+        CPlayer *pPlayer = GameServer()->m_apPlayers[i];
+        if(!pPlayer)
+            continue;
+        pPlayer->Revive();
+    }
 }
diff --git a/src/game/server/gamemodes/ctf.h b/src/game/server/gamemodes/ctf.h
index 72747ed..dfe4322 100644
--- a/src/game/server/gamemodes/ctf.h
+++ b/src/game/server/gamemodes/ctf.h
@@ -18,6 +18,12 @@ class CGameControllerCTF : public IGameController
 
     virtual bool OnEntity(int Index, vec2 Pos);
     virtual int OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon);
+
+    virtual void ReviveCheck();
+    void Revive();
+
+private:
+    bool m_BothTeamsPresent;
 };
 
 #endif
diff --git a/src/game/server/gamemodes/dm.cpp b/src/game/server/gamemodes/dm.cpp
deleted file mode 100644
index bdca4c9..0000000
--- a/src/game/server/gamemodes/dm.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-/* (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 "dm.h"
-
-
-CGameControllerDM::CGameControllerDM(class CGameContext *pGameServer)
-: IGameController(pGameServer)
-{
-    m_pGameType = "DM";
-}
-
-void CGameControllerDM::Tick()
-{
-    IGameController::Tick();
-}
diff --git a/src/game/server/gamemodes/dm.h b/src/game/server/gamemodes/dm.h
deleted file mode 100644
index e88fad0..0000000
--- a/src/game/server/gamemodes/dm.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/* (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.                */
-#ifndef GAME_SERVER_GAMEMODES_DM_H
-#define GAME_SERVER_GAMEMODES_DM_H
-#include <game/server/gamecontroller.h>
-
-class CGameControllerDM : public IGameController
-{
-public:
-    CGameControllerDM(class CGameContext *pGameServer);
-    virtual void Tick();
-};
-#endif
diff --git a/src/game/server/gamemodes/mod.cpp b/src/game/server/gamemodes/mod.cpp
deleted file mode 100644
index eb8fd7c..0000000
--- a/src/game/server/gamemodes/mod.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-/* (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 "mod.h"
-
-CGameControllerMOD::CGameControllerMOD(class CGameContext *pGameServer)
-: IGameController(pGameServer)
-{
-    // Exchange this to a string that identifies your game mode.
-    // DM, TDM and CTF are reserved for teeworlds original modes.
-    m_pGameType = "MOD";
-
-    //m_GameFlags = GAMEFLAG_TEAMS; // GAMEFLAG_TEAMS makes it a two-team gamemode
-}
-
-void CGameControllerMOD::Tick()
-{
-    // this is the main part of the gamemode, this function is run every tick
-
-    IGameController::Tick();
-}
diff --git a/src/game/server/gamemodes/mod.h b/src/game/server/gamemodes/mod.h
deleted file mode 100644
index 847d35f..0000000
--- a/src/game/server/gamemodes/mod.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/* (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.                */
-#ifndef GAME_SERVER_GAMEMODES_MOD_H
-#define GAME_SERVER_GAMEMODES_MOD_H
-#include <game/server/gamecontroller.h>
-
-// you can subclass GAMECONTROLLER_CTF, GAMECONTROLLER_TDM etc if you want
-// todo a modification with their base as well.
-class CGameControllerMOD : public IGameController
-{
-public:
-    CGameControllerMOD(class CGameContext *pGameServer);
-    virtual void Tick();
-    // add more virtual functions here if you wish
-};
-#endif
diff --git a/src/game/server/gamemodes/tdm.cpp b/src/game/server/gamemodes/tdm.cpp
deleted file mode 100644
index 373bbd0..0000000
--- a/src/game/server/gamemodes/tdm.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-/* (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/server/entities/character.h>
-#include <game/server/player.h>
-#include "tdm.h"
-
-CGameControllerTDM::CGameControllerTDM(class CGameContext *pGameServer) : IGameController(pGameServer)
-{
-    m_pGameType = "TDM";
-    m_GameFlags = GAMEFLAG_TEAMS;
-}
-
-int CGameControllerTDM::OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon)
-{
-    IGameController::OnCharacterDeath(pVictim, pKiller, Weapon);
-
-
-    if(pKiller && Weapon != WEAPON_GAME)
-    {
-        // do team scoring
-        if(pKiller == pVictim->GetPlayer() || pKiller->GetTeam() == pVictim->GetPlayer()->GetTeam())
-            m_aTeamscore[pKiller->GetTeam()&1]--; // klant arschel
-        else
-            m_aTeamscore[pKiller->GetTeam()&1]++; // good shit
-    }
-
-    pVictim->GetPlayer()->m_RespawnTick = max(pVictim->GetPlayer()->m_RespawnTick, Server()->Tick()+Server()->TickSpeed()*g_Config.m_SvRespawnDelayTDM);
-
-    return 0;
-}
-
-void CGameControllerTDM::Snap(int SnappingClient)
-{
-    IGameController::Snap(SnappingClient);
-
-    CNetObj_GameData *pGameDataObj = (CNetObj_GameData *)Server()->SnapNewItem(NETOBJTYPE_GAMEDATA, 0, sizeof(CNetObj_GameData));
-    if(!pGameDataObj)
-        return;
-
-    pGameDataObj->m_TeamscoreRed = m_aTeamscore[TEAM_RED];
-    pGameDataObj->m_TeamscoreBlue = m_aTeamscore[TEAM_BLUE];
-
-    pGameDataObj->m_FlagCarrierRed = 0;
-    pGameDataObj->m_FlagCarrierBlue = 0;
-}
-
-void CGameControllerTDM::Tick()
-{
-    IGameController::Tick();
-}
diff --git a/src/game/server/gamemodes/tdm.h b/src/game/server/gamemodes/tdm.h
deleted file mode 100644
index 297b48c..0000000
--- a/src/game/server/gamemodes/tdm.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/* (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.                */
-#ifndef GAME_SERVER_GAMEMODES_TDM_H
-#define GAME_SERVER_GAMEMODES_TDM_H
-#include <game/server/gamecontroller.h>
-
-class CGameControllerTDM : public IGameController
-{
-public:
-    CGameControllerTDM(class CGameContext *pGameServer);
-
-    int OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon);
-    virtual void Snap(int SnappingClient);
-    virtual void Tick();
-};
-#endif
diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp
index 4b37385..71219b6 100644
--- a/src/game/server/player.cpp
+++ b/src/game/server/player.cpp
@@ -17,10 +17,9 @@ CPlayer::CPlayer(CGameContext *pGameServer, int ClientID, int Team)
     m_ScoreStartTick = Server()->Tick();
     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();
+    SetTeamSimple(DeadTeam(Team), true);
 }
 
 CPlayer::~CPlayer()
@@ -62,7 +61,7 @@ void CPlayer::Tick()
 
     if(!GameServer()->m_World.m_Paused)
     {
-        if(!m_pCharacter && m_Team == TEAM_SPECTATORS && m_SpectatorID == SPEC_FREEVIEW)
+        if(!m_pCharacter && IsSpectator() && 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())
@@ -106,7 +105,7 @@ void CPlayer::PostTick()
     }
 
     // update view pos for spectators
-    if(m_Team == TEAM_SPECTATORS && m_SpectatorID != SPEC_FREEVIEW && GameServer()->m_apPlayers[m_SpectatorID])
+    if(IsSpectator() && m_SpectatorID != SPEC_FREEVIEW && GameServer()->m_apPlayers[m_SpectatorID])
         m_ViewPos = GameServer()->m_apPlayers[m_SpectatorID]->m_ViewPos;
 }
 
@@ -138,12 +137,12 @@ void CPlayer::Snap(int SnappingClient)
     pPlayerInfo->m_Local = 0;
     pPlayerInfo->m_ClientID = m_ClientID;
     pPlayerInfo->m_Score = m_Score;
-    pPlayerInfo->m_Team = m_Team;
+    pPlayerInfo->m_Team = SnapTeam(m_Team);
 
     if(m_ClientID == SnappingClient)
         pPlayerInfo->m_Local = 1;
 
-    if(m_ClientID == SnappingClient && m_Team == TEAM_SPECTATORS)
+    if(m_ClientID == SnappingClient && IsSpectator())
     {
         CNetObj_SpectatorInfo *pSpectatorInfo = static_cast<CNetObj_SpectatorInfo *>(Server()->SnapNewItem(NETOBJTYPE_SPECTATORINFO, m_ClientID, sizeof(CNetObj_SpectatorInfo)));
         if(!pSpectatorInfo)
@@ -204,7 +203,7 @@ void CPlayer::OnDirectInput(CNetObj_PlayerInput *NewInput)
     if(m_pCharacter)
         m_pCharacter->OnDirectInput(NewInput);
 
-    if(!m_pCharacter && m_Team != TEAM_SPECTATORS && (NewInput->m_Fire&1))
+    if(!m_pCharacter && IsSpectator() && (NewInput->m_Fire&1))
         m_Spawning = true;
 
     // check for activity
@@ -237,15 +236,39 @@ void CPlayer::KillCharacter(int Weapon)
 
 void CPlayer::Respawn()
 {
-    if(m_Team != TEAM_SPECTATORS)
+    if(!IsSpectator())
         m_Spawning = true;
 }
 
-void CPlayer::SetTeam(int Team, bool DoChatMsg)
+void CPlayer::Revive()
+{
+    SetTeamSimple(GetGameTeam());
+}
+
+void CPlayer::SetTeamSimple(int Team, bool Init)
+{
+    m_Team = Team;
+    m_SpectatorID = SPEC_FREEVIEW;
+    if(Init)
+        return;
+    GameServer()->m_pController->OnPlayerInfoChange(GameServer()->m_apPlayers[m_ClientID]);
+
+    if(Team == TEAM_SPECTATORS)
+    {
+        // update spectator modes
+        for(int i = 0; i < MAX_CLIENTS; ++i)
+        {
+            if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->m_SpectatorID == m_ClientID)
+                GameServer()->m_apPlayers[i]->m_SpectatorID = SPEC_FREEVIEW;
+        }
+    }
+}
+
+void CPlayer::SetGameTeam(int Team, bool DoChatMsg)
 {
     // clamp the team
     Team = GameServer()->m_pController->ClampTeam(Team);
-    if(m_Team == Team)
+    if(GetGameTeam() == Team)
         return;
 
     char aBuf[512];
@@ -257,32 +280,27 @@ void CPlayer::SetTeam(int Team, bool DoChatMsg)
 
     KillCharacter();
 
-    m_Team = Team;
     m_LastActionTick = Server()->Tick();
-    m_SpectatorID = SPEC_FREEVIEW;
     // we got to wait 0.5 secs before respawning
     m_RespawnTick = Server()->Tick()+Server()->TickSpeed()/2;
-    str_format(aBuf, sizeof(aBuf), "team_join player='%d:%s' m_Team=%d", m_ClientID, Server()->ClientName(m_ClientID), m_Team);
+    str_format(aBuf, sizeof(aBuf), "team_join player='%d:%s' team=%d", m_ClientID, Server()->ClientName(m_ClientID), Team);
     GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf);
-
-    GameServer()->m_pController->OnPlayerInfoChange(GameServer()->m_apPlayers[m_ClientID]);
-
-    if(Team == TEAM_SPECTATORS)
-    {
-        // update spectator modes
-        for(int i = 0; i < MAX_CLIENTS; ++i)
-        {
-            if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->m_SpectatorID == m_ClientID)
-                GameServer()->m_apPlayers[i]->m_SpectatorID = SPEC_FREEVIEW;
-        }
-    }
+    // If we're currently alive (i.e. not spectator and not dead), be alive
+    // on the new team as well.
+    if(!IsDeadTeam())
+        SetTeamSimple(Team);
+    else
+        SetTeamSimple(DeadTeam(Team));
 }
 
 void CPlayer::TryRespawn()
 {
     vec2 SpawnPos;
 
-    if(!GameServer()->m_pController->CanSpawn(m_Team, &SpawnPos))
+    if(IsSpectator())
+        return;
+
+    if(!GameServer()->m_pController->CanSpawn(GetGameTeam(), &SpawnPos))
         return;
 
     m_Spawning = false;
diff --git a/src/game/server/player.h b/src/game/server/player.h
index dd804a9..64703bb 100644
--- a/src/game/server/player.h
+++ b/src/game/server/player.h
@@ -20,8 +20,15 @@ class CPlayer
 
     void TryRespawn();
     void Respawn();
-    void SetTeam(int Team, bool DoChatMsg=true);
+    void SetGameTeam(int Team, bool DoChatMsg=true);
+    void SetTeamSimple(int Team, bool Init=false);
+    void Revive();
+private:
     int GetTeam() const { return m_Team; };
+public:
+    int GetGameTeam() const { return GameTeam(GetTeam()); }
+    bool IsSpectator() const { return IsSpectatorTeam(GetTeam()); }
+    bool IsDeadTeam() const { return !IsAliveTeam(GetTeam()); }
     int GetCID() const { return m_ClientID; };
 
     void Tick();

Wow! big_smile xush is on trip now and he paused developing the mod. I agreed with him that the mod's name should be "survival CTF" "surCTF" in short to people easy recognize what kind of game type it is. During the tests with xush we noticed that there are more problems to solve and probable bugs with this mod than we thought. I weak programmer - when I wanted make this mod by myself I even couldn't compile the source big_smile Unfortunately I can't host online server sad  Maybe someone else will do it to tests smile  So heinrich5991 maybe could you compile the code and post the .exe to tests of this mod, and later maybe you will create new topic with download smile

19 (edited by Oblique. 2015-08-23 01:25:30)

Re: [Idea]CTF without respawn

I breifly tested out the server, and I only noticed one weird thing so far. After getting killed, if I click the "Join Red (or blue)" button, the server will say "Oblique. joined the spectators", and I then I'm in free-view mode. Then if I click "Join Red" again, it'll say "Oblique. joined the red (or blue) team", even though I'm still in the spectators. It continues to alternate between the two every time I hit the "Join Red (or blue)" button, but you can only "Join" the team that balances the players.

So essentially, the server still says what teams you join after you are killed, even though you can't join any team.

EDIT:
I've never actually played it, but I wonder if this mod has anything to do with what you're doing: Survival

A is for Apple.

20 (edited by SandSnake 2015-08-23 11:05:52)

Re: [Idea]CTF without respawn

Oblique. wrote:

I breifly tested out the server, and I only noticed one weird thing so far. After getting killed, if I click the "Join Red (or blue)" button, the server will say "Oblique. joined the spectators", and I then I'm in free-view mode. Then if I click "Join Red" again, it'll say "Oblique. joined the red (or blue) team", even though I'm still in the spectators. It continues to alternate between the two every time I hit the "Join Red (or blue)" button, but you can only "Join" the team that balances the players.

So essentially, the server still says what teams you join after you are killed, even though you can't join any team.

EDIT:
I've never actually played it, but I wonder if this mod has anything to do with what you're doing: Survival

I haven't tested the mod yet, maybe I wrong understood your post, but it works fine if you can't join to team after death - resp is after capute the flag or all players death.

I think Chillerdragon's survival is something different.

21 (edited by Oblique. 2015-08-23 16:07:33)

Re: [Idea]CTF without respawn

OK, I just explained it badly. After getting killed, you can't join any team, which is right. However, the server will still say that you joined a team if you click the button in your client, which is wrong.

A is for Apple.

22

Re: [Idea]CTF without respawn

Wow, interesting idea. If this modification ever is existent thanks to anybody then maybe it could spark a new competitive scene.

no

23 (edited by SandSnake 2015-08-23 17:20:46)

Re: [Idea]CTF without respawn

It would be great if someone host this mod (by heinrich5991 Source) to play and to debug smile

24 (edited by 2015-08-23 18:30:39)

Re: [Idea]CTF without respawn

Oblique. wrote:

OK, I just explained it badly. After getting killed, you can't join any team, which is right. However, the server will still say that you joined a team if you click the button in your client, which is wrong.

This is actually intended. smile If you get killed the server tells the client that the client is a spectator, as such, only the buttons "Join Red" and "Join Blue" exist. In order to be able to join the spectators, even if dead, I made the "Join <current team>" button let you join the spectators.

SandSnake wrote:

It would be great if someone host this mod (by heinrich5991 Source) to play and to debug smile

I'm hosting it now, search for CFT test server. But it's only fun if you play with a few players, say 4 or 6.

25 (edited by SandSnake 2015-08-23 20:22:30)

Re: [Idea]CTF without respawn

I'm hosting it now, search for CFT test server. But it's only fun if you play with a few players, say 4 or 6.

Oh great big_smile, I have two sugestions - maybe better when you switch map to ctf5 - that map is more popular big_smile And change name from "test server" to "Survival CFT (no respawn)" or something like this. I think thanks these changes more player will join. smile