Posted by: omega
Date posted: Jan 06 2005 User Rating: 4.8 out of 5.0 | Number of views: 9929 Number of comments: 18 | Description: Setting up a basic scoreboard (with teams!) in the HL2 FROM SCRATCH MP SDK. |
There's a lot of copy and paste code here, but note: make sure you check over everything and read the comments BEFORE you start blindly pasting, or you may royally screw up your scoreboard :P
with that said.. This can be optomized a bit, using a struct for the team info, but when i did it, i was doing one piece at a time, so i haven't re-written mine yet. there are a few things still not done with this scoreboard. 1) if you add playerclass (i've commented out everything regarding class, for this article), it will show all teams everyones playerclass (easy enough to fix) 2) you can see your own voice icon and muting isn't added yet.
The major changes to the scoreboard file are as follows:
shareddefs.h ------------ =============================================================================================== i changed my team defines to an enum, like so: ===============================================================================================
| | | enum eteams_list { TEAM_UNASSIGNED = 0, TEAM_SPECTATOR, TEAM_REBELS, TEAM_COMMANDOS, TEAMS_COUNT };
|
ClientScoreBoardDialog.h ------------------------ =============================================================================================== i added another 2 types, but note that only one of the new ones is used currently: ===============================================================================================
| | | #define TYPE_NOTEAM 0 #define TYPE_TEAM 1 #define TYPE_SPECTATORS 2 #define TYPE_BLANK 3 #define TYPE_HEADER 4 #define TYPE_UNASSIGNED 5
|
=============================================================================================== add the following variables to the class: ===============================================================================================
| | | private: int iTeamSections[TEAMS_COUNT]; int numPlayersOnTeam[TEAMS_COUNT]; int teamLatency[TEAMS_COUNT]; |
=============================================================================================== and change AddSection to: ===============================================================================================
| | | | virtual int AddSection(int teamType, int teamNumber); |
ClientScoreBoardDialog.cpp --------------------------
| | | | #include "sdk_gamerules.h" |
uncomment memset(s_VoiceImage, 0x0, sizeof( s_VoiceImage )); in the scoreboard constructor
=============================================================================================== add AddHeader(); into the InitScoreboardSections() function: ===============================================================================================
| | | void CClientScoreBoardDialog::InitScoreboardSections() { AddHeader(); } |
=============================================================================================== modify ApplySchemeSettings so that the voice icon stuff is being setup ===============================================================================================
| | | void CClientScoreBoardDialog::ApplySchemeSettings( IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); ImageList *imageList = new ImageList(false);
s_VoiceImage[0] = 0; s_VoiceImage[CVoiceStatus::VOICE_NEVERSPOKEN] = imageList->AddImage(scheme()->GetImage("speaker1", true)); s_VoiceImage[CVoiceStatus::VOICE_NOTTALKING] = imageList->AddImage(scheme()->GetImage("speaker2", true)); s_VoiceImage[CVoiceStatus::VOICE_TALKING] = imageList->AddImage(scheme()->GetImage( "speaker3", true)); s_VoiceImage[CVoiceStatus::VOICE_BANNED] = imageList->AddImage(scheme()->GetImage("voiceblocked", true)); <<---SNIP--->>
|
=============================================================================================== you will need to modify this, and you likely won't need any of the attacking/defending stuff unless you're copying flf ;p ===============================================================================================
| | |
void CClientScoreBoardDialog::UpdateTeamInfo() { int sectionId; int i; char cstring[256]; char plural[2]; wchar_t tn[256]; IGameResources *gr = GameResources(); if ( !gr ) return;
for (i=TEAM_UNASSIGNED;i<TEAMS_COUNT;i++) { sectionId = iTeamSections[i];
if (numPlayersOnTeam[i] == 1) sprintf(plural,""); else sprintf(plural,"s");
sprintf(cstring, "%s - (%i player%s)", gr->GetTeamName(i), numPlayersOnTeam[i], plural); localize()->ConvertANSIToUnicode(cstring, tn, sizeof(tn)); m_pPlayerList->ModifyColumn(sectionId, "name", tn);
if ( numPlayersOnTeam[i] > 0 ) teamLatency[i] /= numPlayersOnTeam[i]; else teamLatency[i] = 0;
wchar_t sz[6]; swprintf(sz, L"%d", gr->GetTeamScore(i)); m_pPlayerList->ModifyColumn(sectionId, "score", sz); if (teamLatency[i] < 1) { m_pPlayerList->ModifyColumn(sectionId, "ping", L""); } else { swprintf(sz, L"%i", teamLatency[i]); m_pPlayerList->ModifyColumn(sectionId, "ping", sz); } teamLatency[i] = 0;
m_pPlayerList->SetSectionFgColor(sectionId, gr->GetTeamColor(i)); } } |
=============================================================================================== modify UpdatePlayerInfo so it adds the player to the proper section: ===============================================================================================
| | | <<-----SNIP--->>> playerData->SetString("name", newName);
int itemID = FindItemIDForPlayerIndex( i ); int playerTeam = gr->GetTeam(i); int sectionID = iTeamSections[playerTeam];
numPlayersOnTeam[playerTeam]++; teamLatency[playerTeam]+=playerData->GetInt("ping"); if ( gr->IsLocalPlayer( i ) ) { selectedRow = itemID; } if (itemID == -1) { itemID = m_pPlayerList->AddItem( sectionID, playerData ); } else { m_pPlayerList->ModifyItem( itemID, sectionID, playerData ); }
m_pPlayerList->SetItemFgColor( itemID, gr->GetTeamColor( playerTeam ) );
playerData->deleteThis(); <<----SNIP---->>> |
=============================================================================================== modify AddHeader to create the proper sections / labels ===============================================================================================
| | | void CClientScoreBoardDialog::AddHeader() { m_iSectionId = 0; m_pPlayerList->AddSection(m_iSectionId, ""); m_pPlayerList->SetSectionAlwaysVisible(m_iSectionId); m_pPlayerList->AddColumnToSection(m_iSectionId, "name", "", 0, scheme()->GetProportionalScaledValue(NAME_WIDTH) );
m_iSectionId = 1; m_pPlayerList->AddSection(m_iSectionId, ""); m_pPlayerList->SetSectionAlwaysVisible(m_iSectionId); m_pPlayerList->AddColumnToSection(m_iSectionId, "name", "", 0, scheme()->GetProportionalScaledValue(NAME_WIDTH) );
m_pPlayerList->AddColumnToSection(m_iSectionId, "score", "#PlayerScore", 0, scheme()->GetProportionalScaledValue(SCORE_WIDTH) ); m_pPlayerList->AddColumnToSection(m_iSectionId, "ping", "#PlayerPing", 0, scheme()->GetProportionalScaledValue(PING_WIDTH) ); m_pPlayerList->AddColumnToSection(m_iSectionId, "voice", "#PlayerVoice", SectionedListPanel::COLUMN_IMAGE | SectionedListPanel::COLUMN_CENTER, scheme()->GetProportionalScaledValue(VOICE_WIDTH) );
m_iSectionId = 2; iTeamSections[TEAM_REBELS] = AddSection(TYPE_TEAM, TEAM_REBELS); iTeamSections[TEAM_COMMANDOS] = AddSection(TYPE_TEAM, TEAM_COMMANDOS); iTeamSections[TEAM_SPECTATOR] = AddSection(TYPE_SPECTATORS, TEAM_SPECTATOR); iTeamSections[TEAM_UNASSIGNED] = AddSection(TYPE_UNASSIGNED, TEAM_UNASSIGNED); } |
=============================================================================================== here is my changed AddSection. if you compare, it's not THAT much different from the original: ===============================================================================================
| | |
int CClientScoreBoardDialog::AddSection(int teamType, int teamNumber) { if ( teamType == TYPE_TEAM ) { IGameResources *gr = GameResources();
if ( !gr ) return -1;
wchar_t *teamName = localize()->Find( gr->GetTeamName(teamNumber) ); wchar_t name[64]; if (!teamName) { localize()->ConvertANSIToUnicode(gr->GetTeamName(teamNumber), name, sizeof(name)); teamName = name; }
m_pPlayerList->AddSection(m_iSectionId, "", StaticPlayerSortFunc);
m_pPlayerList->SetFgColor(gr->GetTeamColor(teamNumber));
m_pPlayerList->AddColumnToSection(m_iSectionId, "name", teamName, 0, scheme()->GetProportionalScaledValue(NAME_WIDTH) );
m_pPlayerList->AddColumnToSection(m_iSectionId, "score", "0", 0, scheme()->GetProportionalScaledValue(SCORE_WIDTH) ); m_pPlayerList->AddColumnToSection(m_iSectionId, "ping", "", 0, scheme()->GetProportionalScaledValue(PING_WIDTH) ); m_pPlayerList->AddColumnToSection(m_iSectionId, "voice", "", SectionedListPanel::COLUMN_IMAGE | SectionedListPanel::COLUMN_CENTER, scheme()->GetProportionalScaledValue(VOICE_WIDTH) ); } else if ( teamType == TYPE_SPECTATORS ) { m_pPlayerList->AddSection(m_iSectionId, ""); m_pPlayerList->AddColumnToSection(m_iSectionId, "name", "#Spectators", 0, scheme()->GetProportionalScaledValue(NAME_WIDTH)); m_pPlayerList->AddColumnToSection(m_iSectionId, "score", "", 0, scheme()->GetProportionalScaledValue(SCORE_WIDTH) ); m_pPlayerList->AddColumnToSection(m_iSectionId, "ping", "", 0, scheme()->GetProportionalScaledValue(PING_WIDTH) ); } else if ( teamType == TYPE_UNASSIGNED ) { m_pPlayerList->AddSection(m_iSectionId, ""); m_pPlayerList->AddColumnToSection(m_iSectionId, "name", "#Unassigned", 0, scheme()->GetProportionalScaledValue(NAME_WIDTH)); m_pPlayerList->AddColumnToSection(m_iSectionId, "score", "", 0, scheme()->GetProportionalScaledValue(SCORE_WIDTH) ); m_pPlayerList->AddColumnToSection(m_iSectionId, "ping", "", 0, scheme()->GetProportionalScaledValue(PING_WIDTH) ); } m_iSectionId++; return m_iSectionId-1; } |
=============================================================================================== remove "deaths" from the StaticPlayerSortFunc function ===============================================================================================
=============================================================================================== modify GetPlayerScoreInfo to set the following: ===============================================================================================
| | | <<---SNIP----->>> kv->SetInt("score", gr->GetFrags( playerIndex ) ); kv->SetInt("ping", gr->GetPing( playerIndex ) ) ; kv->SetString("name", gr->GetPlayerName( playerIndex ) );
kv->SetInt("playerIndex", playerIndex);
kv->SetInt("voice", s_VoiceImage[GetClientVoiceMgr()->GetSpeakerStatus( playerIndex - 1) ]); <<---SNIP----->>> |
=============================================================================================== Change FillScoreBoard around to this: ===============================================================================================
| | | void CClientScoreBoardDialog::FillScoreBoard() { for (int i = TEAM_UNASSIGNED; i < TEAMS_COUNT; i++) { numPlayersOnTeam[i] = 0; } UpdatePlayerInfo();
UpdateTeamInfo(); } |
=============================================================================================== Finally: replace all "frags" tags with "score" (for the columns) ===============================================================================================
c_playerresource.h ------------------ =============================================================================================== modify the GetFrags function to: ===============================================================================================
| | | int C_PlayerResource::GetFrags(int index ) { if ( !IsConnected( index ) ) return 0;
return m_iScore[index]; } |
voice_status.h -------------- =============================================================================================== Under UpdateSpeakerStatus, add: ===============================================================================================
| | | enum speakerIcons { VOICE_NEVERSPOKEN, VOICE_NOTTALKING, VOICE_TALKING, VOICE_BANNED }; int GetSpeakerStatus(int entindex); |
voice_status.cpp ---------------- =============================================================================================== Under UpdateSpeakerStatus, add: ===============================================================================================
| | | int CVoiceStatus::GetSpeakerStatus(int entindex) { player_info_t pi; if ( !engine->GetPlayerInfo( entindex, &pi ) ) return 0;
bool bTalking = !!m_VoicePlayers[entindex]; bool bBanned = m_BanMgr.GetPlayerBan(pi.guid); bool bNeverSpoken = !m_VoiceEnabledPlayers[entindex];
if (bBanned) return VOICE_BANNED; else if (bTalking) return VOICE_TALKING; else if (bNeverSpoken) return VOICE_NEVERSPOKEN; else return VOICE_NOTTALKING; } |
That should be everything to make the scoreboard function like mine. if it isn't, i'm sure you can figure it out, it's not that complicated.
Additional --------------
oh and you'll need to make the voice images, i took mine from HL1 (gfx\vgui\640_xx) speaker1.tga, speaker2.tga, speaker3.tga, speaker4.tga, voiceblocked.tga pop those tga's in materialsrc\VGUI and run vtex on them (shader unlitgeneric) to make your vmt/vtf's and edit the vmt's for these tags: "$translucent" 1 "$vertexcolor" 1 "$vertexalpha" 1 |
|
User Comments
Showing comments 1-18
Thank you Omega :D Niiiice Article :)
Some things that should help out:
1) Uncomment "#include "voice_status.h" in ClientScoreBoardDialog.cpp
2) Modify #define TYPE_NOTEAM 0 // NOTEAM must be zero :) to #define TYPE_UNASSIGNED 0 // NOTEAM must be zero :) in clientscoreboarddialog.h
3) Change if ( !engine->GetPlayerInfo( entindex, p ) ) to if ( !engine->GetPlayerInfo( entindex, &pi ) ) in voice_status.cpp
Hope have helped someone too :) |
|
Thanks for you tutorial, I started looking for docs about creating a multiplayer mod recently and it helped me. But do you have some code example about how to assign players to teams, did you use modified VGUI from counterstrike source or is it possible to build it from scratch?
I also noticed the 2)define TYPE_NOTEAM 0... but not problem 1) and 3) but maybe that is because I updated to the latest SDK. |
|
never mention leaked code. if you have it, and ask about it, you will be banned.
as for TYPE_NOTEAM, i renaemd it, forgot to mention it in the article. |
|
Hi, great stuff but my compiler have problem with the line if (FLFGameRules()->GetAttackingTeam() == i)
FLFGameRules is not declared ..... |
|
so take it out? since you obviously don't have FLF's gamerules but rather SDKGameRules.
i forgot to rip that out apparently, but i'm glad. read the first line of the post :P
| | | | There's a lot of copy and paste code here, but note: make sure you check over everything and read the comments BEFORE you start blindly pasting, or you may royally screw up your scoreboard :P | |
|
lol i just noticed rkzad must have made a parser for !pi to result to the pi symbol ;x bulk needs to fix it. |
|
|
how do you make the scoreboard appear? |
|
|
bind a key to +showscores |
|
|
Your tutorial doesn't work. The team names dont get displayed at the top, and the spectator team section doesn't get created properly. Ive run through the tutorial about eight times to no avail.Edited by RJMacReady on Apr 01 2005, 08:09:47
|
|
i'll edit the tutorial later becaause it's confusing now that valve has released hl2dm and it's called "hl mp"
this is for the "bare bones multiplayer from scratch configuration" |
|
|
Ahh right! Is that why it doesn't work then? Or is it a case of just editing bits? Would it be easier to just start a barebones multiplayer from scratch? |
|
if you're dealing with the hl2dm source you need to edit a different file. look in the hl2mp folder for the scoreboard file.
the file i edit here, is the base one, hl2dm inherits off of it. |
|
|
I already tried usin your code with the hl2dmscoreboard file, but it still didn't seem to work. Should I just use the multiplayer code from scratch and not modify HL2DM? |
|
It works I just did it
Some code needs to go into hl2dmclientscoreboard and some in the file that it inherits from, clientscoreboarddialog |
|
you're not creating the headers.
as for choosing team, you need to make it call the team panel at the same time. the motd should show up above it. |
|
i added the addheader :s
and for the choosing team where ? in teammenu.cpp ?Edited by Arsyesis on Apr 20 2005, 08:28:20
|
|
lol I wish I would have know this didn't work for hl2dm mods BEFORE I did all of this..now I gotta read the tutorial backwards to fix it :/. Good tutorial though.
Ms..would you be willing to share what you did? |
|
You must register to post a comment. If you have already registered, you must login.
|
296 Approved Articless
5 Pending Articles
3940 Registered Members
0 People Online (33 guests)
|
|