QuakeZone
MOD Coding Tutorials
Quake II Coding Tutorials!

Introduction
Pre-requisites
Code
Notes
Exercises
Quake II Coding Tutorials

16 Teamplay Info on Hud / Scoreboard

Introduction Introduction

This tutorial builds on the Quakestyle Deathmatch scoreboard tutorial to show team information on the scorebaord, and the player's team on the status bar.

This tutorial enhances the presentation for the previous teamplay tutorial, the team name and switching tutorial.


Amendments

This tutorial has been amended to only update the team names after a certain time interval. Previously, calling gi.configstring every player frame in p_hud.c caused connecting players to overflow, so this code was moved to G_RunFrame in g_main.c. [31.03.2001]

Pre-requisites Pre-requisites

  • the Quakestyle Deathmatch scoreboard tutorial
  • the QDevels teamplay addon tutorial
  • my teamplay enhancements tutorial no. 15
Code Code

q_shared.h

Add the following lines as indicated. If CS_TR_STRINGS already exists from a previous tutorial, this insert can be ignored.

#define STAT_TEAM_NAME          23			// PJT - team play - team name

#define	MAX_STATS		32	

		......

#define MAX_CONFIGSTRINGS	(CS_GENERAL+MAX_GENERAL)
#define CS_TR_STRINGS       	(CS_ITEMS+200)		// PJT - team play
		


p_hud.c

In procedure DeathmatchScoreboardMessage, which was created in the Quakestyle deathmatch scoreboard tutorial, add the following lines as indicated:
    	int        i, j, k;
	int	   maxteams=2;			// PJT - team play
		
Then ..
    // print level name and exit rules
    string[0] = 0;
    stringlength = strlen(string);

		// <-- insert starts here
		// PJT - team play - show team scores

		// header
		Com_sprintf(entry, sizeof(entry),
			"xv 32 yv -32 string2 \"Teams\" "
			"xv 32 yv -24 string2 \"----------------\" ");

        j = strlen(entry);
        if (stringlength + j < 1024)
        {
            strcpy (string + stringlength, entry);
            stringlength += j;
        }

		// team 1
		Com_sprintf(entry, sizeof(entry),
			"xv 32 yv -16 string \"%s\" "
			"xv 216 yv -16 string \"%4i\" ",
			team_table[1].name,
			team_table[1].score);

        j = strlen(entry);
        if (stringlength + j < 1024)
        {
            strcpy (string + stringlength, entry);
            stringlength += j;
		}

		// team 2
		Com_sprintf(entry, sizeof(entry),
			"xv 32 yv -8 string \"%s\" "
			"xv 216 yv -8 string \"%4i\" ",
			team_table[2].name,
			team_table[2].score);

        j = strlen(entry);
        if (stringlength + j < 1024)
        {
            strcpy (string + stringlength, entry);
            stringlength += j;
		}

		// PJT  <-- end of insert
		
Then just below add and amend the following lines as indicated:
        // Deathmatch scoreboard
        // make a header for the data
        Com_sprintf(entry, sizeof(entry),
        	"xv 32 yv 16 string2 \"Player\" "
        	"xv 168 yv 16 string2 \"Frags\" "
        	"xv 216 yv 16 string2 \"Ping\" "
        	"xv 256 yv 16 string2 \"Time\" "
		"xv 296 yv 16 string2 \"Team\" "			// PJT <-- team play
        	"xv 32 yv 24 string2 \"--------------------------------------------------------\" ");	// PJT <-- team play
		
Then, further down still in the same procedure, amend the following section of code as indicated:
            // send the layout
            Com_sprintf(entry, sizeof(entry),
            	"xv 32 yv %i string \"%s\" "
            	"xv 152 yv %i string \"%7i %4i %4i %s\" ",		// PJT - team play
            	y, cl->pers.netname,
            	y, cl->resp.score,
            	cl->ping,
            	(level.framenum - cl->resp.enterframe)/600,
				team_table[cl->resp.team].name);	// PJT - team play
		
Then, at the end of procedure G_GetStats add the following lines:
	// PJT - new Hud
	update_hud_rockets(ent);

	// PJT - team scores
	update_team_scores();
		
At the end of the file insert the following procedure. If the procedure already exists from previous tutorials of mine, do NOT replace the procedure but insert the code lines to the end of the existing procedure.
// PJT - new HUD (TR 1.1.0)
void update_hud_rockets (edict_t *ent)
{
	// PJT - team play - team name for
	ent->client->ps.stats[STAT_TEAM_NAME] = CS_TR_STRINGS + 39 + ent->client->resp.team;
}
		
Then add the following further procedure to the end of the file:
// PJT - teamplay - update scores
void update_team_scores(void)
{
	int		i, maxteams;
	gclient_t	*cl;
    	edict_t     	*cl_ent;

	maxteams = 2;

	// PJT - commented out to resolve overflow problems - now in g_main.c G_RunFrame 
	// update team names
	// gi.configstring (CS_TR_STRINGS +40, sv_teamname1->string);
	// gi.configstring (CS_TR_STRINGS +41, sv_teamname2->string);
	// PJT

	// zeroise team scores
	for (i = 1; i <= maxteams; i++)
	{
		team_table[i].score = 0;
	}

	// scan thru clients, incrementing team scores
    	for (i=0 ; i < game.maxclients ; i++)
    	{
        	cl_ent = g_edicts + 1 + i;
        	if (!cl_ent->inuse)
            		continue;
        	team_table[game.clients[i].resp.team].score = game.clients[i].resp.score;
    	}

}
// PJT
		

g_spawn.c

Add the following lines as indicated to structure dm_statusbar :

//  frags
"xr	-50 "
"yt 2 "
"num 3 14 "

// PJT - team play - team name(23)
"xr -200 "
"yt 10 "
"string \"Team\" "
"xr -152 "
"stat_string 23 "
// PJT
		

g_main.c

Add the following lines to G_RunFrame as indicated:
	// PJT - update team names
	if (level.framenum % 35 == 0)
	{
		gi.configstring (CS_TR_STRINGS +40, sv_teamname1->string);
		gi.configstring (CS_TR_STRINGS +41, sv_teamname2->string);
		team_table[1].name = sv_teamname1->string;
		team_table[2].name = sv_teamname2->string;
	}
	// PJT <-- end of insert

	// see if needpass needs updated
	CheckNeedPass ();
		
Notes Notes to code


q_shared.h
  • STAT_TEAM_NAME specifies the "slot" in the client data stream for the field to be sent to the client holding the team name
  • CS_TR_STRINGS represents the position in gi.configstring where the team names are stored

p_hud.c
  • DeathmatchScoreboardMessage displays the scores when a player presses F1:
    • firstly, the team names and scores form the team table are displayed above the individual scores
    • on the player display, a heading has been added for team names and each player's team is displayed
  • G_SetStats is called every client frame to update the players hud:
    • update_hud_rockets is called to get the latest team names
    • update_team_scores is called to calculate the latest team scores
  • update_hud_rockets makes sure the player always get the latest team name
  • update_team_scores adds up all the scores for the players to calculate the current team scores

g_spawn.c
  • dm_statusbar is a string that contains information that displays the player's hud as processed internally by Quake:
    • the player's team name is displayed at the top of the screen

g_main.c
  • G_RunFrame is called every game frame to update all the entities in the game:
    • every 35 game frames (3.5 seconds) the team names to be sent to clients are updated
Exercises Exercises

  • move update_team_scores to g_save.c so that it is only called once per frame, not for each client
  • use a level timer so that update_team_scores is only called once per second to reduce overhead


Page last updated 31st March 2001