Holy Strike Teamplay Engineering
International Game Developers Association
Table of contents |
[edit] Research
Searching through the Game_SDK.sln file in /trunk/mod reveals a whopping 1314 references to the word "team". Many of these files are obviously false-positives (the word "steam" also matches the word "team") but there is a lot of great, pre-existing team code. Let's take a look at some of the more important files.
[edit] base/dlls/team.h and .cpp
// Purpose: Team management class. Contains all the details for a specific team
This class, CTeam, is the server-side representation of one particular team. Notice that it inherits from CBaseEntity though it's purely logical. At a cursory glance, it seems like the server's CTeam holds a CUtlVector of all spawn points and another of players for that team. CTeam also contains a name and a score (which are broadcast to all clients) and a count of deaths and the last spawn point used (which are kept solely on the server).
[edit] base/cl_dll/c_team.h and .cpp
// Purpose: Client side CTeam class
This class, C_Team, is the client-side representation of one particular team. The client's C_Team knows about the other team players, the teamname, and the score (these are broadcast from the server), as well as his own deaths, ping, packet loss, and team number (these are for scoreboard parsing).
NOTE: I would like to know if the client only knows the player's C_Team or if it knows all players' C_Teams.
[edit] base/game_shared/teamplay_gamerules.h and .cpp
This server-side class, CTeamplayRules, contains the information for how the server handles team-based multiplayer. There is little of importance in the header, teamplay_gamerules.cpp contains all the meat of this class. For example, here is how the level is ended in CTeamplayRules::Think(void):
float flFragLimit = fraglimit.GetFloat();
if ( flFragLimit )
{
// check if any team is over the frag limit
for ( int i = 0; i < num_teams; i++ )
{
if ( team_scores[i] >= flFragLimit )
{
ChangeLevel();
return;
}
}
}
[edit] base/dlls/sdk/sdk_team.h and .cpp
This server-side class, CSDKTeam, is an empty class obviously derived from CTeam for the purpose of allowing easy additions for teamplay mods. This is a very likely candidate for Holy Strike-specific team changes. Since almost everything in CTeam is virtual, it seems that CSDKTeam or some class derived herefrom should implement such data as controlled capture points as well as points accrued (and other data necessary for our scoreboard).
[edit] base/cl_dll/game_controls/teammenu.h and .cpp
Here we have, in the client-side code, a class that handles the panel commands for a teammenu. It starts out very bare with some methods already commented out. Presumably, these methods were part of an automatic initialization that created join buttons for all available teams. We'll try and do this using a resource file instead.
[edit] Implementation
There are two key implementation tasks here: create a way for the player to join a team (either via vgui or using a console command).
[edit] base/cl_dll/game_controls/teammenu.h and .cpp
In teammenu.h and .cpp we will need to add an interface between button presses in the TeamMenu panel and the CTeamMenu class created here. We will do this in a similar way as other descendents from vgui::Frame do, by adding a method declaration:
virtual void OnCommand(const char* pCommand);
And then an implementation like:
void CTeamMenu::OnCommand(const char* pCommand)
{
if (Q_stricmp(pCommand, "vguicancel"))
{
engine->ClientCmd(const_cast<char *>(pCommand));
}
Close();
gViewPortInterface->ShowBackGround(false);
BaseClass::OnCommand(pCommand);
}
Note that if the received command is "vguicancel", we pass it up to the engine to handle. Otherwise, we close the panel and tell our BaseClass to handle the command. All we're doing here is acting as a pass-through for panel handling.
[edit] base/game_shared/teamplay_gamerules.cpp
We will need to add a pass through from the console to the player to handle the "jointeam" command. In bool CTeamplayRules::ClientCommand( const char *pcmd, CBaseEntity *pEdict ) we need to add these lines immediately following the BaseClass::ClientCommand(...) call:
if (pEdict->IsPlayer() && static_cast<CBasePlayer*>(pEdict)->ClientCommand(pcmd))
{
return true;
}
HACK: This seems like a total hack to me. Shouldn't the player request team assignment from the server? Why is a local client in charge of this...
[edit] base/dlls/player.cpp
Inside bool CBasePlayer::ClientCommand(const char *cmd), at the end of the giant else if block, we will add some handling for the "jointeam" command:
else if (stricmp(cmd, "jointeam") == 0)
{
if (engine->Cmd_Argc() < 2)
{
return true;
}
int team = atoi(engine->Cmd_Argv(1));
// don't do anything if you join your own team
if (team == GetTeamNumber())
{
return true;
}
if (team == 0)
{
// TODO: auto-assignment or spectator mode
}
if (!IsDead())
{
if (GetTeamNumber() != TEAM_UNASSIGNED)
{
CommitSuicide();
// add 1 to frags to balance out the 1 subtracted for killing yourself
IncrementFragCount(1);
}
}
ChangeTeam(team);
return true;
}
[edit] mod/cl_dll/game_controls/vgui_TeamFortressViewport.cpp
In void CBaseViewport::CreateDefaultPanels(void) we need to make two changes. First, let's create a console command to bring up our teammenu (this may be replaced later with a level and entity-affecting brushes). Find the CON_COMMANDs for "showpanel" and "hidepanel". Immediately after them add:
CON_COMMAND(chooseteam, "Opens the TeamMenu")
{
if (!gViewPortInterface)
{
return;
}
gViewPortInterface->ShowPanel("team", true);
}
We also see a method that appears to create a large number of default panels. The line for creating a PANEL_TEAM has been commented out. Let's enable this:
AddNewPanel(CreatePanelByName(PANEL_TEAM));
Note that PANEL_TEAM is defined as "team" in mod/game_shared/viewport_panel_names.h.
NOTE: Is this strictly necessary or is the panel created when the player (or game) chooses to create it using the "chooseteam" command?
[edit] resource/ui/TeamMenu.res
This resource file needs to be created. Below is our implementation:
"Resource/UI/TeamMenu.res"
{
"team"
{
"ControlName" "CTeamMenu"
"fieldName" "team"
"xpos" "100"
"ypos" "60"
"wide" "240"
"tall" "290"
"autoResize" "0"
"pinCorner" "0"
"visible" "1"
"enabled" "1"
"tabPosition" "0"
"settitlebarvisible" "0"
"title" "#Frame_Untitled"
}
"chooseTeam"
{
"ControlName" "Label"
"fieldName" "joinTeam"
"xpos" "20"
"ypos" "20"
"wide" "200"
"tall" "30"
"autoResize" "0"
"pinCorner" "0"
"visible" "1"
"enabled" "1"
"tabPosition" "0"
"labelText" "#Tm_ChooseTeam"
"textAlignment" "center"
"dulltext" "0"
"brighttext" "0"
"font" "MenuTitle"
"wrap" "0"
}
"joinTeam1"
{
"ControlName" "Button"
"fieldName" "jointeam1"
"xpos" "20"
"ypos" "60"
"wide" "200"
"tall" "30"
"autoResize" "0"
"pinCorner" "2"
"visible" "1"
"enabled" "1"
"tabPosition" "3"
"labelText" "#Tm_JoinTeam1"
"textAlignment" "west"
"dulltext" "0"
"brighttext" "0"
"wrap" "0"
"Command" "jointeam 2"
"Default" "0"
}
"joinTeam2"
{
"ControlName" "Button"
"fieldName" "jointeam2"
"xpos" "20"
"ypos" "100"
"wide" "200"
"tall" "30"
"autoResize" "0"
"pinCorner" "2"
"visible" "1"
"enabled" "1"
"tabPosition" "4"
"labelText" "#Tm_JoinTeam2"
"textAlignment" "west"
"dulltext" "0"
"brighttext" "0"
"wrap" "0"
"Command" "jointeam 3"
"Default" "0"
}
}
TODO: This panel needs to be drastically cleaned up... unless we replace it with a level as per the GDD.
[edit] resource/HolyStrike_english.txt
Tokens will need to be added to the English localization for the three strings used in the above TeamMenu.res (Tm_ChooseTeam, Tm_JoinTeam1, and Tm_JoinTeam2) like so:
...
"Token"
{
...
"Tm_ChooseTeam" "Choose Team"
"Tm_JoinTeam1" "Join Team 1"
"Tm_JoinTeam2" "Join Team 2"
...
[edit] Changes so far
basecomatweapon_shared.h: added two virtual functions GetClip1 and GetClip2
sdk_weapon_parse.cpp: sets the member var m_iMaxAmmo1 to the keyvalue data called MaxAmmo1 sets the member var m_iMaxAmmo2 to the keyvalue data called MaxAmmo2
vgui_TeamFortressViewport.cpp: included classmen.h added a new command to the menu system called "Opens the TeamMenu" added a new command to the menu system called "Opens the Class menu"
new added files: hud_sniperscope.cpp hs_class.cpp hs_class.h
Lots of new weapon files
[edit] References
All this work owes big thanks to these two tutorials:
