From 21d5b479e4dc6509811f9fd75b9bc842ce86009a Mon Sep 17 00:00:00 2001 From: blub0 Date: Thu, 24 Jul 2008 18:48:31 +0000 Subject: [PATCH] CSQC part for the new score system (not really finished yet, needs cleaning) Added a few calls to PlayerScore_Add for testing purposes... Added default scoreboard layout for and KH, CTF Lots of the old csqc code for the scoreboard can be removed (parts of the sortlist code too) git-svn-id: svn://svn.icculus.org/nexuiz/trunk@3903 f962a42d-fe04-0410-a3ab-8c8b0445ebaa --- data/qcsrc/client/Defs.qc | 3 + data/qcsrc/client/Main.qc | 107 ++++- data/qcsrc/client/View.qc | 6 + data/qcsrc/client/main.qh | 27 +- data/qcsrc/client/mapvoting.qc | 1 - data/qcsrc/client/miscfunctions.qc | 105 +++++ data/qcsrc/client/progs.src | 2 +- data/qcsrc/client/sbar.qc | 712 +++++++++++------------------ data/qcsrc/client/sortlist.qc | 24 +- data/qcsrc/client/teamplay.qc | 2 + data/qcsrc/common/constants.qh | 39 ++ data/qcsrc/server/cl_weapons.qc | 2 + data/qcsrc/server/defs.qh | 9 +- data/qcsrc/server/ent_cs.qc | 7 +- data/qcsrc/server/g_damage.qc | 4 + data/qcsrc/server/keyhunt.qc | 3 +- data/qcsrc/server/scores.qc | 31 +- data/qcsrc/server/scores.qh | 44 +- 18 files changed, 610 insertions(+), 518 deletions(-) diff --git a/data/qcsrc/client/Defs.qc b/data/qcsrc/client/Defs.qc index e0a4c5966..714c1bdc9 100644 --- a/data/qcsrc/client/Defs.qc +++ b/data/qcsrc/client/Defs.qc @@ -167,6 +167,9 @@ vector dmg_origin; // Basic variables .float enttype; // entity type sent from server .float sv_entnum; // entity number sent from server +.float team; +.float team_size; + float vid_conwidth, vid_conheight; float caps_team1, caps_team2; float configdb; diff --git a/data/qcsrc/client/Main.qc b/data/qcsrc/client/Main.qc index 1d2d1195c..1a7b41726 100644 --- a/data/qcsrc/client/Main.qc +++ b/data/qcsrc/client/Main.qc @@ -56,7 +56,7 @@ void CSQC_Init(void) menu_action = menu_sub_null; using_gps = false; //ctf_temp_1 = ""; - localcmd("alias order \"cmd order $*\""); + // localcmd("alias order \"cmd order $*\""); enable if ctf-command thingy is used //registercmd("ctf_menu"); registercmd("ons_map"); //registercmd("menu_action"); @@ -76,7 +76,10 @@ void CSQC_Init(void) postinit = false; - Sbar_Init(); + teams = Sort_Spawn(); + players = Sort_Spawn(); + + teamspec = AddTeam(COLOR_SPECTATOR); // add specs first } // CSQC_Shutdown : Called every time the CSQC code is shutdown (changing maps, quitting, etc) @@ -88,6 +91,9 @@ void CSQC_Shutdown(void) return 0; #pragma TARGET fte #endif + + remove(teams); + remove(players); db_close(configdb); buf_del(databuf); } @@ -185,7 +191,7 @@ float CSQC_InputEvent(float bInputType, float nPrimary, float nSecondary) // -------------------------------------------------------------------------- // BEGIN OPTIONAL CSQC FUNCTIONS -void ReadONS() +void Ent_ReadONS() { entity gps; using_gps = true; @@ -207,7 +213,7 @@ void ReadONS() } } -void RemoveONS() +void Ent_RemoveONS() { if(gps_start == self) gps_start = self.chain; @@ -223,6 +229,69 @@ void RemoveONS() } } +void Ent_ReadScoresInfo() +{ + float i; + for(i = 0; i < MAX_SCORE; ++i) + { + scores_label[i] = strzone(ReadString()); + scores_flags[i] = ReadByte(); + } + for(i = 0; i < MAX_TEAMSCORE; ++i) + { + teamscores_label[i] = strzone(ReadString()); + teamscores_flags[i] = ReadByte(); + } + Sbar_InitScores(); +} + +void Ent_ReadPlayerScore(float isNew) +{ + float i, Team; + entity tm; + + // damnit -.- don't want to go change every single .sv_entnum in sbar.qc AGAIN + // (no I've never heard of M-x replace-string, sed, or anything like that) + self.sv_entnum = ReadByte()-1; + Team = GetPlayerColor(self.sv_entnum); + + if(isNew) + RegisterPlayer(self); + + if(isNew || Team != self.team) + { + if(!isNew) + { + tm = GetTeam(self.team, false); + tm.team_size -= 1; + } + + self.team = Team; + tm = GetTeam(Team, true); + tm.team_size += 1; + } + + for(i = 0; i < MAX_SCORE; ++i) + self.(scores[i]) = ReadShort(); + + Sbar_UpdatePlayerPos(self); +} + +void Ent_ReadTeamScore(float isNew) +{ + float i; + + self.team = ReadByte(); + + if(isNew) + RegisterTeam(self); + + for(i = 0; i < MAX_TEAMSCORE; ++i) + self.(teamscores[i]) = ReadShort(); + + Sbar_UpdateTeamPos(self); +} + // CSQC_Ent_Update : Called every frame that the server has indicated an update to the SSQC / CSQC entity has occured. // The only parameter reflects if the entity is "new" to the client, meaning it just came into the client's PVS. void(float bIsNewEntity) CSQC_Ent_Update = @@ -237,13 +306,19 @@ void(float bIsNewEntity) CSQC_Ent_Update = { switch(msg) { - case ENTCS_MSG_ONS_GPS: ReadONS(); break; - case ENTCS_MSG_ONS_REMOVE: RemoveONS(); break; + case ENTCS_MSG_ONS_GPS: Ent_ReadONS(); break; + case ENTCS_MSG_ONS_REMOVE: Ent_RemoveONS(); break; default: error("unknown ENTCS_MSG type\n"); } } } + else if(self.enttype == ENT_CLIENT_SCORES_INFO) + Ent_ReadScoresInfo(); + else if(self.enttype == ENT_CLIENT_SCORES) + Ent_ReadPlayerScore(bIsNewEntity); + else if(self.enttype == ENT_CLIENT_TEAMSCORES) + Ent_ReadTeamScore(bIsNewEntity); else error("unknown entity type in CSQC_Ent_Update\n"); @@ -268,6 +343,21 @@ void CSQC_Ent_Remove() ent.chain = self.chain; } } + } else if(self.enttype == ENT_CLIENT_SCORES_INFO) + { + // OH NOES!! WE LOST DA SCORES INFO ENTITY + print("The world is going to explode."); + // kkthxbai + } else if(self.enttype == ENT_CLIENT_SCORES) + { + entity tm; + print("lost a client score\n"); + tm = GetTeam(self.team, false); + tm.team_size -= 1; + RemovePlayer(self); + } else if(self.enttype == ENT_CLIENT_TEAMSCORES) + { + RemoveTeam(self); } remove(self); } @@ -333,11 +423,6 @@ void Gamemode_Init() void CSQC_Parse_StuffCmd(string strMessage) { localcmd(strMessage); - // watch for gametype changes! - if(gametype != cvar("gametype")) - { - Gamemode_Init(); - } } // CSQC_Parse_Print : Provides the print string in the first parameter that the server provided. To execute standard behavior, simply execute print with the string. void CSQC_Parse_Print(string strMessage) diff --git a/data/qcsrc/client/View.qc b/data/qcsrc/client/View.qc index f82075f98..dd7a9404f 100644 --- a/data/qcsrc/client/View.qc +++ b/data/qcsrc/client/View.qc @@ -7,6 +7,12 @@ void CSQC_ctf_hud(void); void PostInit(void); void CSQC_UpdateView(void) { + // watch for gametype changes here... + // in ParseStuffCMD the cmd isn't executed yet :/ + // might even be better to add the gametype to TE_CSQC_INIT...? + if(gametype != cvar("gametype")) + Gamemode_Init(); + if(!postinit) PostInit(); diff --git a/data/qcsrc/client/main.qh b/data/qcsrc/client/main.qh index 886c2d0b1..7744ea400 100644 --- a/data/qcsrc/client/main.qh +++ b/data/qcsrc/client/main.qh @@ -67,23 +67,32 @@ const float COLOR_SPECTATOR = 1337; #define MAX_SBAR_FIELDS 16 -#define SBF_END 0 -#define SBF_PING 1 -#define SBF_NAME 2 -#define SBF_CAPS 3 -#define SBF_RETS 4 -#define SBF_FRAGS 5 -#define SBF_DEATHS 6 -#define SBF_KDRATIO 7 +#define SP_END -1 -#define SBF_SEPARATOR 100 +#define SP_PING -2 +#define SP_NAME -3 +#define SP_KDRATIO -4 +#define SP_CLRATIO -5 + +#define SP_SEPARATOR -100 float sbar_field[MAX_SBAR_FIELDS + 1]; float sbar_size[MAX_SBAR_FIELDS + 1]; string sbar_title[MAX_SBAR_FIELDS + 1]; float sbar_num_fields; + float sbar_font; +string scores_label[MAX_SCORE]; +float scores_flags[MAX_SCORE]; +string teamscores_label[MAX_SCORE]; +float teamscores_flags[MAX_SCORE]; +.float scores[MAX_SCORE]; +.float teamscores[MAX_TEAMSCORE]; + +#define IS_INCREASING(x) ( (x)&SFL_DECREASING ) +#define IS_DECREASING(x) ( !((x)&SFL_DECREASING) ) + float csqc_flags; #define CSQC_FLAG_READPICTURE 1 diff --git a/data/qcsrc/client/mapvoting.qc b/data/qcsrc/client/mapvoting.qc index e9c2c1d11..5d62d7f03 100644 --- a/data/qcsrc/client/mapvoting.qc +++ b/data/qcsrc/client/mapvoting.qc @@ -105,7 +105,6 @@ void MapVote_Draw() vector pos; float isize; float center; - float margin; center = (vid_conwidth - 1)/2; xmin = vid_conwidth*0.2; diff --git a/data/qcsrc/client/miscfunctions.qc b/data/qcsrc/client/miscfunctions.qc index abea2e0da..97142fa47 100644 --- a/data/qcsrc/client/miscfunctions.qc +++ b/data/qcsrc/client/miscfunctions.qc @@ -1,6 +1,111 @@ float databuf; var float(string text, float handleColors) stringwidth; +entity players; +entity teams; + +float RegisterPlayer(entity player) +{ + entity pl; + for(pl = players.sort_next; pl; pl = pl.sort_next) + if(pl == player) + return false; + player.sort_next = players.sort_next; + player.sort_prev = players; + if(players.sort_next) + players.sort_next.sort_prev = player; + players.sort_next = player; + return true; +} + +void RemovePlayer(entity player) +{ + entity pl, parent; + parent = players; + for(pl = players.sort_next; pl && pl != player; pl = pl.sort_next) + parent = pl; + + if(!pl) + { + print("Trying to remove a player which is not in the playerlist!"); + return; + } + parent.sort_next = player.sort_next; + if(player.sort_next) + player.sort_next.sort_prev = parent; +} + +entity AddTeam(float num) +{ + entity tm; + tm = spawn(); + tm.team = num; + tm.sort_next = teams.sort_next; + tm.sort_prev = teams; + if(teams.sort_next) + teams.sort_next.sort_prev = tm; + teams.sort_next = tm; + return tm; +} + +void MoveToLast(entity e) +{ + other = e.sort_next; + while(other) + { + SORT_SWAP(other, e); + other = e.sort_next; + } +} + +// warning: Local "team" defined with name of a global +// FU FTEQCC, .float team is a ENTVAR shitty piece of crap!!! +float RegisterTeam(entity Team) +{ + entity tm; + for(tm = teams.sort_next; tm; tm = tm.sort_next) + if(tm == Team) + return false; + Team.sort_next = teams.sort_next; + Team.sort_prev = teams; + if(teams.sort_next) + teams.sort_next.sort_prev = Team; + teams.sort_next = Team; + return true; +} + +void RemoveTeam(entity Team) +{ + entity tm, parent; + parent = teams; + for(tm = teams.sort_next; tm && tm != Team; tm = tm.sort_next) + parent = tm; + + if(!tm) + { + print("Trying to remove a team which is not in the teamlist!"); + return; + } + parent.sort_next = Team.sort_next; + if(Team.sort_next) + Team.sort_next.sort_prev = parent; +} + +entity GetTeam(float num, float add) +{ + entity tm; + for(tm = teams.sort_next; tm; tm = tm.sort_next) + { + if(tm.team == num) + return tm; + } + + if(add) + return AddTeam(num); + + return NULL; +} + float stringwidth_oldfont(string text, float handleColors) { float i, len, ch, width; diff --git a/data/qcsrc/client/progs.src b/data/qcsrc/client/progs.src index be67db42e..680eeef10 100644 --- a/data/qcsrc/client/progs.src +++ b/data/qcsrc/client/progs.src @@ -11,8 +11,8 @@ csqc_builtins.qc main.qh -miscfunctions.qc sortlist.qc +miscfunctions.qc teamplay.qc ons.qc diff --git a/data/qcsrc/client/sbar.qc b/data/qcsrc/client/sbar.qc index 35c4dc813..7117742d8 100644 --- a/data/qcsrc/client/sbar.qc +++ b/data/qcsrc/client/sbar.qc @@ -9,7 +9,7 @@ vector sbar; vector sbar_fontsize; float sbar_alpha_fg; float sbar_hudselector; - +/* entity sortedPlayers; entity sortedTeams; @@ -17,6 +17,9 @@ entity sortedTeams; .float sb_team; .float sb_player; .float sb_caps; +*/ +float ps_primary, ps_secondary; +float ts_primary, ts_secondary; entity team1, team2, team3, team4, teamspec; @@ -128,446 +131,281 @@ void Sbar_DrawXNum (vector pos, float num, float digits, float lettersize, vecto } } -float Sbar_PlayerCmp(entity l, entity r) +void Cmd_Sbar_SetFields(float argc); +void Sbar_InitScores() { - if(teamplay) + float i, f, primary_prio, secondary_prio; + + primary_prio = secondary_prio = -1; + for(i = 0; i < MAX_SCORE; ++i) { - if(l.sb_team > r.sb_team) - return true; - else if(l.sb_team > r.sb_team) - return false; - if(gametype == GAME_CTF) - { - if(l.sb_caps > r.sb_caps) - return true; - else if(l.sb_caps < r.sb_caps) - return false; - } + f = (scores_flags[i] & SFL_SORT_PRIO_MASK); + if(f > primary_prio) { + ps_secondary = ps_primary; + ps_primary = i; + } else if(f > secondary_prio) + ps_secondary = i; } - if(l.sb_frags > r.sb_frags) - return true; - else if(l.sb_frags < r.sb_frags) - return false; - return (l.sb_player > r.sb_player); -} -float Sbar_TeamCmp(entity l, entity r) -{ - if(gametype == GAME_CTF) + + primary_prio = secondary_prio = -1; + for(i = 0; i < MAX_TEAMSCORE; ++i) { - if(l.sb_caps > r.sb_caps) - return true; - else if(l.sb_caps < r.sb_caps) - return false; + f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK); + if(f > primary_prio) { + ts_secondary = ts_primary; + ts_primary = i; + } else if(f > secondary_prio) + ts_secondary = i; } - if(l.sb_frags > r.sb_frags) - return true; - else if(l.sb_frags < r.sb_frags) - return false; - return (l.sb_player > r.sb_player); -} -void Sbar_Init() -{ - sortedPlayers = Sort_New(Sbar_PlayerCmp); - sortedTeams = Sort_New(Sbar_TeamCmp); - team1 = Sort_Next(sortedTeams); - team1.sb_team = COLOR_TEAM1; - team2 = Sort_Next(sortedTeams); - team2.sb_team = COLOR_TEAM2; - team3 = Sort_Next(sortedTeams); - team3.sb_team = COLOR_TEAM3; - team4 = Sort_Next(sortedTeams); - team4.sb_team = COLOR_TEAM4; - teamspec = Sort_Next(sortedTeams); - teamspec.sb_team = COLOR_SPECTATOR; + Cmd_Sbar_SetFields(0); } -void Sbar_UpdatePosFrags(entity player) +void Sbar_UpdatePlayerPos(entity pl); +void Sbar_UpdatePlayerTeams() { - other = player.sort_prev; - while(other != sortedPlayers && player.sb_frags > other.sb_frags) - { - SORT_SWAP(other, player); - other = player.sort_prev; - } + float Team; + entity pl, tmp; + float num; - other = player.sort_next; - while(other && player.sb_frags < other.sb_frags) + num = 0; + for(pl = players.sort_next; pl; pl = pl.sort_next) { - SORT_SWAP(player, other); - other = player.sort_next; + num += 1; + Team = GetPlayerColor(pl.sv_entnum); + if(pl.team != Team) + { + tmp = GetTeam(pl.team, false); + tmp.team_size -= 1; + tmp = GetTeam(Team, true); + tmp.team_size += 1; + + pl.team = Team; + + tmp = pl.sort_prev; + Sbar_UpdatePlayerPos(pl); + if(tmp) + pl = tmp; + else + pl = players.sort_next; + } } + //print(strcat("PNUM: ", ftos(num), "\n")); } -void Sbar_UpdatePosFragsCTF(entity player) + +float Sbar_ComparePlayerScores(entity left, entity right) { - other = player.sort_prev; - while(other != sortedPlayers && player.sb_frags > other.sb_frags && player.sb_caps == other.sb_caps) - { - SORT_SWAP(other, player); - other = player.sort_prev; - } + float vl, vr; + vl = GetPlayerColor(left.sv_entnum); + vr = GetPlayerColor(right.sv_entnum); + + if(vl > vr) + return true; + if(vl < vr) + return false; - other = player.sort_next; - while(other && player.sb_frags < other.sb_frags && player.sb_caps == other.sb_caps) - { - SORT_SWAP(player, other); - other = player.sort_next; - } + vl = left.scores[ps_primary]; + vr = right.scores[ps_primary]; + if(vl > vr) + return IS_INCREASING(scores_flags[ps_primary]); + if(vl < vr) + return IS_DECREASING(scores_flags[ps_primary]); + + vl = left.scores[ps_secondary]; + vr = right.scores[ps_secondary]; + if(vl > vr) + return IS_INCREASING(scores_flags[ps_secondary]); + if(vl < vr) + return IS_DECREASING(scores_flags[ps_secondary]); + + return false; } -void Sbar_UpdatePosCaps(entity player) + +void Sbar_UpdatePlayerPos(entity player) { - other = player.sort_prev; - while(other != sortedPlayers && player.sb_caps > other.sb_caps) - { - SORT_SWAP(other, player); - other = player.sort_prev; - } - other = player.sort_next; - while(other && player.sb_caps < other.sb_caps) + for(other = player.sort_next; other && Sbar_ComparePlayerScores(player, other); other = player.sort_next) { SORT_SWAP(player, other); - other = player.sort_next; } - // let it update the frags now too, so if we have more frags then the next guy with the same caps - // we beat his ass :) - player.sb_frags -= 1; -} - -void Sbar_UpdatePosTeam(entity player) -{ - player.sb_caps -= 1; // team change needs a full update - other = player.sort_prev; - while(other != sortedPlayers && player.sb_team > other.sb_team) + for(other = player.sort_prev; other != players && Sbar_ComparePlayerScores(other, player); other = player.sort_prev) { SORT_SWAP(other, player); - other = player.sort_prev; - } - - other = player.sort_next; - while(other && player.sb_team < other.sb_team) - { - SORT_SWAP(player, other); - other = player.sort_next; } } -void Sbar_UpdatePlayer(entity player) +float Sbar_CompareTeamScores(entity left, entity right) { - float i; - - if(player.sb_frags == -666) - i = COLOR_SPECTATOR; - else - i = GetPlayerColor(player.sb_player); + float vl, vr; - if(player.sb_team != i) - { - player.sb_team = i; - Sbar_UpdatePosTeam(player); - } + vl = left.teamscores[ts_primary]; + vr = right.teamscores[ts_primary]; + if(vl > vr) + return IS_INCREASING(teamscores_flags[ts_primary]); + if(vl < vr) + return IS_DECREASING(teamscores_flags[ts_primary]); - if(gametype == GAME_CTF) - { - i = stof(bufstr_get(databuf, DATABUF_CAPTURES + player.sb_player)); - if(player.sb_caps != i) - { - player.sb_caps = i; - Sbar_UpdatePosCaps(player); - } - i = stof(getplayerkey(player.sb_player, "frags")); - if(player.sb_frags != i) - { - player.sb_frags = i; - Sbar_UpdatePosFragsCTF(player); - } - } else { - i = stof(getplayerkey(player.sb_player, "frags")); - if(player.sb_frags != i) - { - player.sb_frags = i; - Sbar_UpdatePosFrags(player); - } - } -} - -void Sbar_UpdateTeamPosCaps(entity tm) -{ - other = tm.sort_prev; - while(other != sortedTeams && tm.sb_caps > other.sb_caps) - { - SORT_SWAP(other, tm); - other = tm.sort_prev; - } + vl = left.teamscores[ts_secondary]; + vr = right.teamscores[ts_secondary]; + if(vl > vr) + return IS_INCREASING(teamscores_flags[ts_secondary]); + if(vl < vr) + return IS_DECREASING(teamscores_flags[ts_secondary]); - other = tm.sort_next; - while(other && tm.sb_caps < tm.sb_caps) - { - SORT_SWAP(tm, other); - other = tm.sort_next; - } + return false; } -void Sbar_UpdateTeamPosFrags(entity tm) + +void Sbar_UpdateTeamPos(entity Team) { - other = tm.sort_prev; - while(other != sortedTeams && tm.sb_caps == other.sb_caps && tm.sb_frags > other.sb_frags) + for(other = Team.sort_next; other && Sbar_ComparePlayerScores(Team, other); other = Team.sort_next) { - SORT_SWAP(other, tm); - other = tm.sort_prev; + if(other.team == COLOR_SPECTATOR) + break; + SORT_SWAP(Team, other); } - - other = tm.sort_next; - while(other && tm.sb_caps == other.sb_caps && tm.sb_frags < other.sb_frags) + for(other = Team.sort_prev; other != teams && Sbar_ComparePlayerScores(other, Team); other = Team.sort_prev) { - SORT_SWAP(tm, other); - other = tm.sort_next; + SORT_SWAP(other, Team); } } -void Sbar_SortFrags() -{ - float i; - entity tmp; - float t1f, t2f, t3f, t4f; - - Sort_Reset(sortedPlayers); - - numteams = 0; - if(teamplay) - { - Sort_Reset(sortedTeams); - tmp = Sort_Next(sortedTeams); - - team1.sb_player = 0; - team2.sb_player = 0; - team3.sb_player = 0; - team4.sb_player = 0; - teamspec.sb_player = 0; - - t1f = team1.sb_frags; - t2f = team2.sb_frags; - t3f = team3.sb_frags; - t4f = team4.sb_frags; - - team1.sb_frags = 0; - team2.sb_frags = 0; - team3.sb_frags = 0; - team4.sb_frags = 0; - - for(i = 0; i < maxclients; ++i) - { - if(strlen(getplayerkey(i, "name")) <= 0) - continue; - - Sort_Reset(sortedPlayers); - - tmp = NULL; - while(Sort_HasNext(sortedPlayers)) - { - tmp = Sort_Next(sortedPlayers); - if(tmp.sb_player == i) - break; - } - if(!tmp || tmp.sb_player != i) - tmp = Sort_Next(sortedPlayers); - - tmp.sb_player = i; - tmp.frame = time; - Sbar_UpdatePlayer(tmp); - - switch(tmp.sb_team) - { - case COLOR_TEAM1: team1.sb_frags += tmp.sb_frags; team1.sb_player++; break; - case COLOR_TEAM2: team2.sb_frags += tmp.sb_frags; team2.sb_player++; break; - case COLOR_TEAM3: team3.sb_frags += tmp.sb_frags; team3.sb_player++; break; - case COLOR_TEAM4: team4.sb_frags += tmp.sb_frags; team4.sb_player++; break; - case COLOR_SPECTATOR: teamspec.sb_frags += tmp.sb_frags; teamspec.sb_player++; break; - } - - if(i == player_localentnum-1) - myteam = tmp.sb_team; - } - if(team1.sb_player) ++numteams; - if(team2.sb_player) ++numteams; - if(team3.sb_player) ++numteams; - if(team4.sb_player) ++numteams; - - if(team1.sb_caps != caps_team1) - { - team1.sb_caps = caps_team1; - Sbar_UpdateTeamPosCaps(team1); - } - if(team2.sb_caps != caps_team2) - { - team2.sb_caps = caps_team2; - Sbar_UpdateTeamPosCaps(team2); - } - if(team1.sb_frags != t1f) Sbar_UpdateTeamPosFrags(team1); - if(team2.sb_frags != t2f) Sbar_UpdateTeamPosFrags(team2); - if(team3.sb_frags != t3f) Sbar_UpdateTeamPosFrags(team3); - if(team4.sb_frags != t4f) Sbar_UpdateTeamPosFrags(team4); - } else { - for(i = 0; i < maxclients; ++i) - { - if(strlen(getplayerkey(i, "name")) <= 0) - continue; - - Sort_Reset(sortedPlayers); - tmp = NULL; - while(Sort_HasNext(sortedPlayers)) - { - tmp = Sort_Next(sortedPlayers); - if(tmp.sb_player == i) - break; - } - if(!tmp || tmp.sb_player != i) - tmp = Sort_Next(sortedPlayers); - - tmp.sb_player = i; - tmp.frame = time; - Sbar_UpdatePlayer(tmp); - } - } - Sort_RemoveOld(sortedPlayers); -} - void Cmd_Sbar_Help(float argc) { print("You can modify the scoreboard using the\n"); print("^3|---------------------------------------------------------------|\n"); - print("^2sbar_columns^7 cvar and the ^2sbar_columns_set command.\n"); - print("^2sbar_columns^7 specifies the default layout and\n"); - print("^2sbar_columns_set^7 actually changes the layout.\n"); - print("You can call ^2sbar_columns_set^7 with the new layout\n"); - print("as parameters, or eithout parameters it will read the cvar.\n\n"); + print("^1 TO BE DONE\n"); print("Usage:\n"); print("^2sbar_columns_set ^7filed1 field2 ...\n"); - print("Fields which are not relevant to the current gametype\n"); - print("won't be displayed\n\n"); print("The following field names are recognized (case INsensitive):\n"); + print("You can use a ^3|^7 to start the right-aligned fields.\n"); + print("^3name^7 or ^3nick^7 Name of a player\n"); + print("^3ping^7 Ping time\n\n"); + print("^3kd^7 or ^3kdr^7 or ^3kdratio^7 or ^3k/d\n"); + print(" The kill-death ratio\n"); + +/* print("^3caps^7 or ^3captures^7 Number of flags captured\n"); print("^3rets^7 or ^3returns^7 Number of flags returned\n"); print("^3frags^7 or ^3kills^7 Frags\n"); print("^3deaths^7 or ^3dths^7 Number of deaths\n"); - print("^3kd^7 or ^3kdr^7 or ^3kdratio^7 or ^3k/d\n"); - print(" The kill-death ratio\n"); - print("^3ping^7 Ping time\n\n"); - print("You can use a ^3|^7 to start the right-aligned fields.\n"); - print("Example: ping name | caps rets frags k/d\n"); - print("This will put the ping and the name on the left side.\n"); - print("The captures, returns, frags and kill-death ratio will be\n"); - print("rendered beginning on the right side.\n"); - + */ + local float i; + print("Additional columns:\n"); + for(i = 0; i < MAX_SCORE; ++i) + { + if(scores_label[i]) + print(strcat(scores_label[i], "\n")); + } } #define MIN_NAMELEN 24 #define MAX_NAMELEN 24 +string Sbar_DefaultColumnLayout() +{ + switch(gametype) + { + case GAME_CTF: return "ping name | caps frags"; + case GAME_KEYHUNT: return "ping name | score caps kills"; + default: return "ping name | frags"; + // TODO: add other gametypes + } +} + void Cmd_Sbar_SetFields(float argc) { - float i; + float i, j; string str; float digit; - if(argc < 2) + // TODO: re enable with gametype dependant cvars? + if(argc < 2) // no arguments provided argc = tokenize(strcat("x ", cvar_string("sbar_columns"))); + + if(argc < 2) + argc = tokenize(strcat("x ", Sbar_DefaultColumnLayout())); argc = min(MAX_SBAR_FIELDS, argc); sbar_num_fields = 0; + drawfont = sbar_font; digit = stringwidth("0123456789", FALSE) / 10; - for(i = 0; i < argc-1; ++i) + + argc = min(argc-1, MAX_SBAR_FIELDS-1); + for(i = 0; i < argc; ++i) { str = argv(i+1); strunzone(sbar_title[i]); sbar_title[i] = strzone(str); sbar_size[i] = stringwidth(str, FALSE); str = strtolower(str); + + + if(str == "ping") { - sbar_field[i] = SBF_PING; + sbar_field[i] = SP_PING; } else if(str == "name" || str == "nick") { - sbar_field[i] = SBF_NAME; + sbar_field[i] = SP_NAME; sbar_size[i] = MIN_NAMELEN; // minimum size? any use? - } else if(str == "caps" || str == "captures") { - if(sbar_size[i] < 3*digit) - sbar_size[i] = 3*digit; - sbar_field[i] = SBF_CAPS; - } else if(str == "rets" || str == "returns") { - if(sbar_size[i] < 3*digit) - sbar_size[i] = 3*digit; - sbar_field[i] = SBF_RETS; - } else if(str == "frags" || str == "kills") { - if(sbar_size[i] < 5*digit) - sbar_size[i] = 5*digit; - sbar_field[i] = SBF_FRAGS; - } else if(str == "deaths" || str == "dths") { - if(sbar_size[i] < 5*digit) - sbar_size[i] = 5*digit; - sbar_field[i] = SBF_DEATHS; - } else if(str == "kdratio") { - sbar_field[i] = SBF_KDRATIO; - } else if(str == "kdr" || str == "k/d") { - sbar_field[i] = SBF_KDRATIO; - } else if(str == "kd") { - sbar_field[i] = SBF_KDRATIO; } else if(str == "|") { - sbar_field[i] = SBF_SEPARATOR; + sbar_field[i] = SP_SEPARATOR; } else { - print(strcat("^1Error:^7 Unknown score field: '", str, "'\n")); - --sbar_num_fields; + for(j = 0; j < MAX_SCORE; ++j) + { + if(str == strtolower(scores_label[j])) + break; + } + if(j == MAX_SCORE) { + print(strcat("^1Error:^7 Unknown score field: '", str, "'\n")); + --sbar_num_fields; + } else + sbar_field[i] = j; } ++sbar_num_fields; } - sbar_field[i] = SBF_END; + sbar_field[i] = SP_END; } +// MOVEUP:: vector sbar_field_rgb; string Sbar_GetField(entity pl, float field) { - float tmp; + float tmp, num, denom; string str; sbar_field_rgb = '1 1 1'; switch(field) { - case SBF_PING: - str = bufstr_get(databuf, DATABUF_PING + pl.sb_player); - tmp = max(0, min(220, stof(str)-80)) / 220; - sbar_field_rgb = '1 1 1' - '0 1 1'*tmp; - return str; - case SBF_NAME: return getplayerkey(pl.sb_player, "name"); - case SBF_CAPS: return ftos(pl.sb_caps); - case SBF_RETS: return bufstr_get(databuf, DATABUF_RETURNS + pl.sb_player); - case SBF_FRAGS: return ftos(pl.sb_frags); - case SBF_DEATHS: return bufstr_get(databuf, DATABUF_DEATHS + pl.sb_player); - case SBF_KDRATIO: - tmp = stof(bufstr_get(databuf, DATABUF_DEATHS + pl.sb_player)); - if(tmp == 0) { - sbar_field_rgb = '0 1 0'; - str = ftos(pl.sb_frags); - } else if(pl.sb_frags <= 0) { - sbar_field_rgb = '1 0 0'; - str = ftos(pl.sb_frags / tmp); - } else - str = ftos(pl.sb_frags / tmp); + case SP_PING: + str = bufstr_get(databuf, DATABUF_PING + pl.sv_entnum); + tmp = max(0, min(220, stof(str)-80)) / 220; + sbar_field_rgb = '1 1 1' - '0 1 1'*tmp; + return str; - tmp = strstrofs(str, ".", 0); - if(tmp > 0) - str = substring(str, 0, tmp+2); - return str; - } -} -#define SBAR_DEFAULT_MASK 0 -#define SBAR_MASK_SPECTATORS 1 -float Sbar_IsFieldMasked(float field, float mask) -{ - if(mask&1) // spectator - return (field != SBF_NAME && field != SBF_PING); - if(gametype != GAME_CTF) - { - if(field == SBF_CAPS || field == SBF_RETS) - return true; + case SP_NAME: + return getplayerkey(pl.sv_entnum, "name"); + + case SP_KDRATIO: + num = pl.(scores[SP_KILLS]); + denom = pl.(scores[SP_DEATHS]); + + if(denom == 0) { + sbar_field_rgb = '0 1 0'; + str = ftos(num); + } else if(num <= 0) { + sbar_field_rgb = '1 0 0'; + str = ftos(num/denom); + } else + str = ftos(num/denom); + + tmp = strstrofs(str, ".", 0); + if(tmp > 0) + str = substring(str, 0, tmp+2); + return str; + + default: + return ftos(pl.(scores[field])); } - return false; + return "error"; } // shamelessly stolen from menu QC :P <- as if I would steal YOUR code pfft ;) @@ -612,11 +450,14 @@ string textShortenToWidth(string theText, float maxWidth, float handleColors) } float xmin, xmax, ymin, ymax, sbwidth, sbheight; -void Sbar_PrintScoreboardItem(vector pos, entity pl, float is_self, float mask) + +void Sbar_PrintScoreboardItem(vector pos, entity pl, float is_self) { vector tmp; string str; float i, field, len; + float is_spec; + is_spec = (GetPlayerColor(pl.sv_entnum) == COLOR_SPECTATOR); // Layout: tmp_z = 0; @@ -631,23 +472,25 @@ void Sbar_PrintScoreboardItem(vector pos, entity pl, float is_self, float mask) for(i = 0; i < sbar_num_fields; ++i) { field = sbar_field[i]; - if(field == SBF_SEPARATOR) + if(field == SP_SEPARATOR) break; - if(Sbar_IsFieldMasked(field, mask)) - continue; + if(is_spec && field != SP_NAME && field != SP_PING) { + pos_x += sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x; + continue; + } str = Sbar_GetField(pl, field); - if(field == SBF_NAME) + if(field == SP_NAME) { float realsize; float j; realsize = sbar_size[i]; if(i+1 < sbar_num_fields) - if(sbar_field[i+1] == SBF_SEPARATOR) + if(sbar_field[i+1] == SP_SEPARATOR) { realsize = (xmax - xmin) / sbar_fontsize_x; - for(j = 0; j < sbar_num_fields; ++j) if(j != i) if(sbar_field[j] != SBF_SEPARATOR) + for(j = 0; j < sbar_num_fields; ++j) if(j != i) if(sbar_field[j] != SP_SEPARATOR) realsize -= sbar_size[j] + 1; realsize += 1; } @@ -659,7 +502,8 @@ void Sbar_PrintScoreboardItem(vector pos, entity pl, float is_self, float mask) sbar_size[i] = len; pos_x += sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x; - if(field == SBF_NAME) { + + if(field == SP_NAME) { tmp_x = sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x; drawcolorcodedstring(pos - tmp, str, sbar_fontsize, 1, DRAWFLAG_NORMAL); } else { @@ -668,27 +512,30 @@ void Sbar_PrintScoreboardItem(vector pos, entity pl, float is_self, float mask) } } - if(sbar_field[i] == SBF_SEPARATOR) + if(sbar_field[i] == SP_SEPARATOR) { pos_x = xmax; for(i = sbar_num_fields-1; i > 0; --i) { field = sbar_field[i]; - if(field == SBF_SEPARATOR) + if(field == SP_SEPARATOR) break; - if(Sbar_IsFieldMasked(field, mask)) + + if(is_spec && field != SP_NAME && field != SP_PING) { + pos_x -= sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x; continue; + } str = Sbar_GetField(pl, field); - if(field == SBF_NAME) + if(field == SP_NAME) str = textShortenToWidth(str, sbar_size[i], TRUE); len = stringwidth(str, TRUE); if(sbar_size[i] < len) sbar_size[i] = len; - - if(field == SBF_NAME) { + + if(field == SP_NAME) { tmp_x = sbar_fontsize_x*len; // left or right aligned? let's put it right... drawcolorcodedstring(pos - tmp, str, sbar_fontsize, 1, DRAWFLAG_NORMAL); } else { @@ -746,23 +593,19 @@ void Sbar_DrawScoreboard() for(i = 0; i < sbar_num_fields; ++i) { - if(Sbar_IsFieldMasked(sbar_field[i], SBAR_DEFAULT_MASK)) - continue; - if(sbar_field[i] == SBF_SEPARATOR) + if(sbar_field[i] == SP_SEPARATOR) break; drawstring(pos, sbar_title[i], sbar_fontsize, '1 1 1', 1, DRAWFLAG_NORMAL); pos_x += sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x; } - if(sbar_field[i] == SBF_SEPARATOR) + if(sbar_field[i] == SP_SEPARATOR) { pos_x = xmax + sbar_fontsize_x; tmp_y = tmp_z = 0; for(i = sbar_num_fields-1; i > 0; --i) { - if(Sbar_IsFieldMasked(sbar_field[i], SBAR_DEFAULT_MASK)) - continue; - if(sbar_field[i] == SBF_SEPARATOR) + if(sbar_field[i] == SP_SEPARATOR) break; pos_x -= sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x; @@ -786,49 +629,33 @@ void Sbar_DrawScoreboard() if(teamplay) { - for(tm = sortedTeams.sort_next; tm; tm = tm.sort_next) + //for(tm = sortedTeams.sort_next; tm; tm = tm.sort_next) + for(tm = teams.sort_next; tm; tm = tm.sort_next) { - if(!tm.sb_player || tm.sb_team == COLOR_SPECTATOR) // no players in it? + if(!tm.team_size || tm.team == COLOR_SPECTATOR) continue; - rgb = GetTeamRGB(tm.sb_team); + rgb = GetTeamRGB(tm.team); pos_x = xmin - 4*24; - if(gametype == GAME_CTF) - { - if(tm.sb_team == COLOR_TEAM1) - Sbar_DrawXNum(pos, caps_team1, 4, 24, rgb, 1, DRAWFLAG_NORMAL); - else if(tm.sb_team == COLOR_TEAM2) - Sbar_DrawXNum(pos, caps_team2, 4, 24, rgb, 1, DRAWFLAG_NORMAL); - pos_x = xmin - 4*10; - Sbar_DrawXNum(pos + '0 24', tm.sb_frags, 4, 10, rgb, 1, DRAWFLAG_NORMAL); - pos_x = xmin; - } else - Sbar_DrawXNum(pos, tm.sb_frags, 4, 24, rgb, 1, DRAWFLAG_NORMAL); + // TODO: Print primary and secondary scores! + pos_x = xmin; - // abuse specs as playerounter - specs = 0; - for(pl = sortedPlayers.sort_next; pl; pl = pl.sort_next) - { - if(pl.sb_team == tm.sb_team) - ++specs; - } + specs = tm.team_size; if(specs < 2) specs = 2; - if(gametype == GAME_CTF && specs < 4) - specs = 4; tmp_x = sbwidth; tmp_y = 1.25 * sbar_fontsize_y * specs; drawfill(pos - '1 1', tmp + '2 0', rgb, 0.2, DRAWFLAG_NORMAL); - for(pl = sortedPlayers.sort_next; pl; pl = pl.sort_next) + for(pl = players.sort_next; pl; pl = pl.sort_next) { - if(pl.sb_team != tm.sb_team) + if(pl.team != tm.team) continue; - Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), SBAR_DEFAULT_MASK); + Sbar_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1)); pos_y += 1.25 * sbar_fontsize_y; tmp_y -= 1.25 * sbar_fontsize_y; } @@ -838,12 +665,12 @@ void Sbar_DrawScoreboard() rgb = pos + '0 1.5 0' * sbar_fontsize_y; pos_y += 3 * sbar_fontsize_y; specs = 0; - for(pl = sortedPlayers.sort_next; pl; pl = pl.sort_next) + for(pl = players.sort_next; pl; pl = pl.sort_next) { - if(pl.sb_team != COLOR_SPECTATOR) + if(pl.team != COLOR_SPECTATOR) continue; //drawcolorcodedstring(pos, getplayerkey(pl.sb_player, "name"), '8 8 0', 1, 0); - Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), SBAR_MASK_SPECTATORS); + Sbar_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1)); pos += '0 1.25 0' * sbar_fontsize_y; ++specs; } @@ -852,11 +679,11 @@ void Sbar_DrawScoreboard() drawstring(rgb, "Spectators", sbar_fontsize, '1 1 1', 1, 0); } else { pos_x = xmin; - for(pl = sortedPlayers.sort_next; pl; pl = pl.sort_next) + for(pl = players.sort_next; pl; pl = pl.sort_next) { - if(pl.sb_team == COLOR_SPECTATOR) + if(pl.team == COLOR_SPECTATOR) continue; - Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), SBAR_DEFAULT_MASK); + Sbar_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1)); pos_y += 1.25 * sbar_fontsize_y; tmp_y -= 1.25 * sbar_fontsize_y; } @@ -865,12 +692,11 @@ void Sbar_DrawScoreboard() rgb = pos + '0 1.5 0' * sbar_fontsize_y; pos_y += 3 * sbar_fontsize_y; specs = 0; - for(pl = sortedPlayers.sort_next; pl; pl = pl.sort_next) + for(pl = players.sort_next; pl; pl = pl.sort_next) { - if(pl.sb_team != COLOR_SPECTATOR) + if(pl.team != COLOR_SPECTATOR) continue; - //drawcolorcodedstring(pos, getplayerkey(pl.sb_player, "name"), '8 8 0', 1, 0); - Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), SBAR_MASK_SPECTATORS); + Sbar_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1)); pos += '0 1.25 0' * sbar_fontsize_y; ++specs; } @@ -883,7 +709,7 @@ void Sbar_DrawScoreboard() void Sbar_Score(float margin) { - float timelimit, timeleft, minutes, seconds, distribution, myplace; + float timelimit, timeleft, minutes, seconds, distribution, myplace, score; vector sbar_save, place; entity tm, pl, me; sbar_save = sbar; @@ -900,38 +726,36 @@ void Sbar_Score(float margin) // // TEAM2 //for(i = 0; i < 4; ++i) - for(tm = sortedTeams.sort_next; tm; tm = tm.sort_next) + for(tm = teams.sort_next; tm; tm = tm.sort_next) { - if(tm.sb_team == COLOR_SPECTATOR || !tm.sb_player) // no players? don't display + if(tm.team == COLOR_SPECTATOR || !tm.team_size) // no players? don't display continue; // -32*4 = -128 - if(tm.sb_team == myteam) - Sbar_DrawXNum('-128 0', tm.sb_frags, 4, 32, GetTeamRGB(tm.sb_team), 1, DRAWFLAG_NORMAL); + score = tm.(teamscores[ts_primary]); + if(tm.team == myteam) + Sbar_DrawXNum('-128 0', score, 4, 32, GetTeamRGB(tm.team), 1, DRAWFLAG_NORMAL); else { - Sbar_DrawXNum(place, tm.sb_frags, 4, 12, GetTeamRGB(tm.sb_team), 1, DRAWFLAG_NORMAL); + Sbar_DrawXNum(place, score, 4, 12, GetTeamRGB(tm.team), 1, DRAWFLAG_NORMAL); place_x -= 4*12; } } } else { // me vector := [team/connected frags id] myplace = 0; - for(me = sortedPlayers.sort_next; me; me = me.sort_next) + for(me = players.sort_next; me; me = me.sort_next) { - if(me.sb_team != COLOR_SPECTATOR) + if(me.team != COLOR_SPECTATOR) ++myplace; - if(me.sb_player == player_localentnum - 1) + if(me.sv_entnum == player_localentnum - 1) break; } - pl = sortedPlayers.sort_next; + pl = players; if(pl == me) pl = pl.sort_next; - if(pl && myplace != 1) - { - distribution = me.sb_frags - pl.sb_frags; - } else if(pl) { - distribution = me.sb_frags - pl.sb_frags; + if(pl) { + distribution = me.(scores[ps_primary]) - pl.(scores[ps_primary]); } else distribution = 0; @@ -942,17 +766,18 @@ void Sbar_Score(float margin) else Sbar_DrawXNum('-36 -12', myplace, 3, 12, '1 0 0', 1, DRAWFLAG_NORMAL); + score = me.(scores[ps_primary]); if(distribution >= 0) { Sbar_DrawXNum('-84 -12', distribution, 4, 12, ' 1 1 1', 1, DRAWFLAG_NORMAL); - Sbar_DrawXNum('-128 0', me.sb_frags, 4, 32, '1 1 1', 1, DRAWFLAG_NORMAL); + Sbar_DrawXNum('-128 0', score, 4, 32, '1 1 1', 1, DRAWFLAG_NORMAL); } else if(distribution >= -5) { Sbar_DrawXNum('-84 -12', distribution, 4, 12, ' 1 1 0', 1, DRAWFLAG_NORMAL); - Sbar_DrawXNum('-128 0', me.sb_frags, 4, 32, '1 1 0', 1, DRAWFLAG_NORMAL); + Sbar_DrawXNum('-128 0', score, 4, 32, '1 1 0', 1, DRAWFLAG_NORMAL); } else { Sbar_DrawXNum('-84 -12', distribution, 4, 12, ' 1 0 0', 1, DRAWFLAG_NORMAL); - Sbar_DrawXNum('-128 0', me.sb_frags, 4, 32, '1 0 0', 1, DRAWFLAG_NORMAL); + Sbar_DrawXNum('-128 0', score, 4, 32, '1 0 0', 1, DRAWFLAG_NORMAL); } } timelimit = getstatf(STAT_TIMELIMIT); @@ -986,17 +811,18 @@ void Sbar_Score(float margin) void Sbar_MiniscoreItem(vector pos, entity pl, float is_self) { - float x; + float x, score; pos_x += 72; if(teamplay) - drawfill(pos + '0 1 0', '40 6 0', GetTeamRGB(pl.sb_team)*0.5, 1, DRAWFLAG_NORMAL); + drawfill(pos + '0 1 0', '40 6 0', GetTeamRGB(pl.team)*0.5, 1, DRAWFLAG_NORMAL); else drawfill(pos + '0 1 0', '40 6 0', '0.5 0.5 0.5', 0.5, DRAWFLAG_NORMAL); x = pos_x; pos_x += 5*8; - pos_x -= stringwidth(ftos(pl.sb_frags), FALSE)*8; - drawstring(pos, ftos(pl.sb_frags), '8 8 0', '1 1 1', 1, DRAWFLAG_NORMAL); + score = pl.(scores[ps_primary]); + pos_x -= stringwidth(ftos(score), FALSE)*8; + drawstring(pos, ftos(score), '8 8 0', '1 1 1', 1, DRAWFLAG_NORMAL); pos_x = x; if(is_self) { @@ -1005,7 +831,7 @@ void Sbar_MiniscoreItem(vector pos, entity pl, float is_self) pos_x += 8; } else pos_x += 56; - drawcolorcodedstring(pos, getplayerkey(pl.sb_player, "name"), '8 8 0', 1, 0); + drawcolorcodedstring(pos, getplayerkey(pl.sv_entnum, "name"), '8 8 0', 1, 0); } void Sbar_MiniscoreTeamItem(vector pos, float color, float frags, float is_self) @@ -1034,7 +860,7 @@ void Sbar_MiniscoreTeamItem(vector pos, float color, float frags, float is_self) void Sbar_MiniDeathmatchOverlay(vector pos) { - float numlines, up, down; + float numlines, up, down, score; entity me, tm, pl; float miniscoreboard_size; miniscoreboard_size = cvar("sbar_miniscoreboard_size"); @@ -1053,9 +879,9 @@ void Sbar_MiniDeathmatchOverlay(vector pos) return; // me vector := [team/connected frags id] - for(me = sortedPlayers.sort_next; me; me = me.sort_next) + for(me = players.sort_next; me; me = me.sort_next) { - if(me.sb_player == player_localentnum - 1) + if(me.sv_entnum == player_localentnum - 1) break; } @@ -1071,7 +897,7 @@ void Sbar_MiniDeathmatchOverlay(vector pos) // render bottom-up for(pl = me.sort_next; pl && down > 0; pl = pl.sort_next) { - if(pl.sb_team == COLOR_SPECTATOR) + if(pl.team == COLOR_SPECTATOR) continue; Sbar_MiniscoreItem(pos, pl, false); pos_y -= 9; @@ -1080,9 +906,9 @@ void Sbar_MiniDeathmatchOverlay(vector pos) Sbar_MiniscoreItem(pos, me, true); pos_y -= 9; up += down; // if there weren't enough lines below... add them - for(pl = me.sort_prev; pl != sortedPlayers && up > 0; pl = pl.sort_prev) + for(pl = me.sort_prev; pl && up > 0; pl = pl.sort_prev) { - if(pl.sb_team == COLOR_SPECTATOR) + if(pl.team == COLOR_SPECTATOR) continue; Sbar_MiniscoreItem(pos, pl, false); pos_y -= 9; @@ -1091,12 +917,13 @@ void Sbar_MiniDeathmatchOverlay(vector pos) if(teamplay) { - for(tm = sortedTeams.sort_next; tm.sort_next; tm = tm.sort_next); - for(; tm != sortedTeams; tm = tm.sort_prev) + for(tm = teams.sort_next; tm.sort_next; tm = tm.sort_next); + for(; tm; tm = tm.sort_prev) { - if(!tm.sb_player || tm.sb_team == COLOR_SPECTATOR) // no players? + if(!tm.team_size || tm.team == COLOR_SPECTATOR) continue; - Sbar_MiniscoreTeamItem(pos, tm.sb_team, tm.sb_frags, (tm.sb_team == me.sb_team)); + score = tm.(teamscores[ts_primary]); + Sbar_MiniscoreTeamItem(pos, tm.team, score, (tm.team == me.team)); pos_y -= 9; } } @@ -1108,7 +935,8 @@ void Sbar_Draw (void) float x, fade; float stat_items; - Sbar_SortFrags(); + //Sbar_SortFrags(); + Sbar_UpdatePlayerTeams(); sb_lines = 24; diff --git a/data/qcsrc/client/sortlist.qc b/data/qcsrc/client/sortlist.qc index 991d869d3..3f0a51ab6 100644 --- a/data/qcsrc/client/sortlist.qc +++ b/data/qcsrc/client/sortlist.qc @@ -1,6 +1,15 @@ .float(entity,entity) sort_cmp; .entity sort_next, sort_prev; +entity Sort_Spawn() +{ + entity sort; + sort = spawn(); + sort.sort_next = NULL; + sort.chain = sort; + return sort; +} + entity Sort_New(float(entity,entity) cmp) { entity sort; @@ -88,12 +97,17 @@ entity Sort_Get(entity sort, float i) return sort; } -#define SORT_SWAP(a,b) \ - b.sort_prev = a.sort_prev; \ - a.sort_next = b.sort_next; \ +/** + * Swap two neighbours in a sortlist. + * @param a FIRST entity + * @param b entity after a + */ +#define SORT_SWAP(a,b) \ + b.sort_prev = a.sort_prev; \ + a.sort_next = b.sort_next; \ if(b.sort_next) b.sort_next.sort_prev = a; \ - a.sort_prev.sort_next = b; \ - a.sort_prev = b; \ + if(a.sort_prev) a.sort_prev.sort_next = b; \ + a.sort_prev = b; \ b.sort_next = a void Sort_Erase(entity ent) diff --git a/data/qcsrc/client/teamplay.qc b/data/qcsrc/client/teamplay.qc index 39d6e52c8..3f98c9cda 100644 --- a/data/qcsrc/client/teamplay.qc +++ b/data/qcsrc/client/teamplay.qc @@ -16,6 +16,8 @@ float TeamByColor(float color) float GetPlayerColor(float i) { + if(getplayerkey(i, "frags") == "-666") + return COLOR_SPECTATOR; return stof(getplayerkey(i, "colors")) & 15; } diff --git a/data/qcsrc/common/constants.qh b/data/qcsrc/common/constants.qh index 00e28c90f..29a0e8982 100644 --- a/data/qcsrc/common/constants.qh +++ b/data/qcsrc/common/constants.qh @@ -169,6 +169,7 @@ float CVAR_READONLY = 4; const float ENTCS_MSG_END = 0; const float ENTCS_MSG_ONS_GPS = 1; const float ENTCS_MSG_ONS_REMOVE = 2; +const float ENTCS_MSG_INIT = 3; const float TE_CSQC_INIT = 100; const float TE_CSQC_PING = 101; @@ -192,3 +193,41 @@ const float MAPVOTE_NET_INIT = 0; const float MAPVOTE_NET_UPDATE = 1; const float MAPVOTE_NET_PIC = 2; const float MAPVOTE_NET_OWNVOTE = 3; + +/** + * Lower scores are better (e.g. deaths) + */ +#define SFL_DECREASING 1 + +/** + * Scoring priority (NOTE: PRIMARY is used for fraglimit) + */ +#define SFL_SORT_PRIO_LOW 2 +#define SFL_SORT_PRIO_MED 4 +#define SFL_SORT_PRIO_HIGH 8 +#define SFL_SORT_PRIO_PRIMARY 14 +#define SFL_SORT_PRIO_MASK 14 + +/** + * Score indices + */ +#define MAX_SCORE 10 +#define MAX_TEAMSCORE 2 + +#define SP_KILLS 0 +#define SP_DEATHS 1 +#define SP_SUICIDES 2 +#define SP_SCORE 3 // personal score, game modes can set it their own way +#define ST_SCORE 0 + +#define SP_CTF_CAPS 4 +#define SP_CTF_RETURNS 5 +#define ST_CTF_CAPS 1 + +#define SP_KH_COLLECT 4 +#define SP_KH_LOSEKEY 5 +#define SP_KH_CAPS 6 +#define SP_KH_PUSH 7 +#define SP_KH_DESTROYED 8 +#define SP_KH_KCFRAG 9 +#define ST_KH_CAPS 1 diff --git a/data/qcsrc/server/cl_weapons.qc b/data/qcsrc/server/cl_weapons.qc index fbad50ee9..51aa6a0bd 100644 --- a/data/qcsrc/server/cl_weapons.qc +++ b/data/qcsrc/server/cl_weapons.qc @@ -35,6 +35,7 @@ string W_Name(float weaponid) if(weaponid == WEP_HAGAR) return "Hagar"; if(weaponid == WEP_ROCKET_LAUNCHER) return "Rocket Launcher"; if(weaponid == WEP_CRYLINK) return "Crylink"; + if(weaponid == WEP_ANTINEX) return "Antinex"; return "@!#%'n Tuba"; } @@ -67,6 +68,7 @@ float W_AmmoItemCode(float wpn) case WEP_NEX: return IT_CELLS; case WEP_HAGAR: return IT_ROCKETS; case WEP_ROCKET_LAUNCHER: return IT_ROCKETS; + case WEP_ANTINEX: return IT_ROCKETS; default: return 0; } } diff --git a/data/qcsrc/server/defs.qh b/data/qcsrc/server/defs.qh index 69d3a6b8c..4da1a1f83 100644 --- a/data/qcsrc/server/defs.qh +++ b/data/qcsrc/server/defs.qh @@ -208,11 +208,11 @@ float WEP_CRYLINK = 6; // float IT_CRYLINK = 16; float WEP_NEX = 7; // float IT_NEX = 32; float WEP_HAGAR = 8; // float IT_HAGAR = 64; float WEP_ROCKET_LAUNCHER = 9; // float IT_ROCKET_LAUNCHER = 128; - +float WEP_ANTINEX = 10; // For weapon cycling commands float WEP_FIRST = 1; -float WEP_LAST = 9; -float WEP_COUNT = 10; +float WEP_LAST = 10; +float WEP_COUNT = 11; void(entity client, string s) centerprint_builtin = #73; .vector dest1, dest2; @@ -442,6 +442,9 @@ float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end) float next_pingtime; +.float Version; +.float(entity to) SendEntity; + .float captures; .float returns; float caps_team1, caps_team2; diff --git a/data/qcsrc/server/ent_cs.qc b/data/qcsrc/server/ent_cs.qc index f2e7f3114..30dfb3c7e 100644 --- a/data/qcsrc/server/ent_cs.qc +++ b/data/qcsrc/server/ent_cs.qc @@ -13,9 +13,6 @@ // it did when I added this) But you have to use .Version // Capital V -.float(entity to) SendEntity; -.float Version; - entity entcs_start; void entcs_init() @@ -26,7 +23,7 @@ void entcs_init() entcs_start.chain = world; }; -entity get_entcs_ent(float num) +entity get_entcs_ent() { entity entcs; entcs = spawn(); @@ -104,7 +101,7 @@ entity attach_entcs() print("Attaching ENTCS entity\n"); num = num_for_edict(self); - ent = get_entcs_ent(num); + ent = get_entcs_ent(); ent.classname = "entcs_sender"; ent.health = num; diff --git a/data/qcsrc/server/g_damage.qc b/data/qcsrc/server/g_damage.qc index 76a7314ec..0b031b36d 100644 --- a/data/qcsrc/server/g_damage.qc +++ b/data/qcsrc/server/g_damage.qc @@ -193,6 +193,7 @@ void Obituary (entity attacker, entity targ, float deathtype) if(deathtype != DEATH_TEAMCHANGE) { LogDeath("suicide", deathtype, targ, targ); + PlayerScore_Add(attacker, SP_SUICIDES, 1); GiveFrags(attacker, targ, -1); } if (targ.killcount > 2) @@ -224,6 +225,7 @@ void Obituary (entity attacker, entity targ, float deathtype) if(deathtype != DEATH_TEAMCHANGE) { LogDeath("suicide", deathtype, targ, targ); + PlayerScore_Add(attacker, SP_SUICIDES, 1); GiveFrags(attacker, targ, -1); } if (targ.killcount > 2) @@ -242,6 +244,7 @@ void Obituary (entity attacker, entity targ, float deathtype) bprint ("^1", a, "^1 mows down a teammate\n"); } GiveFrags(attacker, targ, -1); + PlayerScore_Add(attacker, -1); // teamkills DO cound as -1 kill right? if (targ.killcount > 2) { if(sv_gentle) bprint ("^1",s,"'s ^1",ftos(targ.killcount)," scoring spree was ended by a teammate!\n"); @@ -444,6 +447,7 @@ void Obituary (entity attacker, entity targ, float deathtype) else bprint ("^1",s, "^1 died\n"); GiveFrags(targ, targ, -1); + PlayerScore_Add(targ, SP_DEATHS, 1); if(targ.frags == -5) { announce(targ, "announcer/male/botlike.ogg"); } diff --git a/data/qcsrc/server/keyhunt.qc b/data/qcsrc/server/keyhunt.qc index 7842dcd7e..eef30eabf 100644 --- a/data/qcsrc/server/keyhunt.qc +++ b/data/qcsrc/server/keyhunt.qc @@ -473,7 +473,8 @@ void kh_Key_DropAll(entity player, float suicide) // runs whenever a player dies while((key = player.kh_next)) { kh_Scores_Event(player, key, "losekey", 0, 0); - PlayerScore_Add(player, SP_KH_LOSEKEY, 1); + if(PlayerScore_IsValid(player)) + PlayerScore_Add(player, SP_KH_LOSEKEY, 1); bprint(player.netname, "^7 died and lost the ", key.netname, "\n"); kh_Key_AssignTo(key, world); makevectors('-1 0 0' * (45 + 45 * random()) + '0 360 0' * random()); diff --git a/data/qcsrc/server/scores.qc b/data/qcsrc/server/scores.qc index e02a53b31..32c8135dc 100644 --- a/data/qcsrc/server/scores.qc +++ b/data/qcsrc/server/scores.qc @@ -12,8 +12,12 @@ var .float scores_primary; var .float teamscores_primary; float scores_initialized; -.float Version; -.float(entity to) SendEntity; +void Net_LinkEntity(entity e) +{ + e.model = "net_entity"; + e.modelindex = 1; + e.effects = EF_NODEPTHTEST | EF_LOWPRECISION; +} vector ScoreField_Compare(entity t1, entity t2, .float field, float fieldflags, vector previous) // returns: cmp value, best prio { @@ -38,7 +42,7 @@ vector ScoreField_Compare(entity t1, entity t2, .float field, float fieldflags, * teamscore entities */ -void TeamScore_SendEntity(entity to) +float TeamScore_SendEntity(entity to) { float i; @@ -46,6 +50,8 @@ void TeamScore_SendEntity(entity to) WriteByte(MSG_ENTITY, self.team); for(i = 0; i < MAX_TEAMSCORE; ++i) WriteShort(MSG_ENTITY, self.teamscores[i]); + + return TRUE; } void TeamScore_Spawn(float t, string name) @@ -57,6 +63,7 @@ void TeamScore_Spawn(float t, string name) ts.netname = name; // not used yet, FIXME ts.Version = 1; // immediately send, so csqc knows about the team ts.team = t; + Net_LinkEntity(ts); teamscorekeepers[t] = ts; ++teamscores_entities_count; } @@ -103,7 +110,7 @@ void ScoreInfo_SetLabel_TeamScore(float i, string label, float scoreflags) teamscores_primary = teamscores[i]; } -void ScoreInfo_SendEntity(entity to) +float ScoreInfo_SendEntity(entity to) { WriteByte(MSG_ENTITY, ENT_CLIENT_SCORES_INFO); float i; @@ -117,6 +124,7 @@ void ScoreInfo_SendEntity(entity to) WriteString(MSG_ENTITY, teamscores_label[i]); WriteByte(MSG_ENTITY, teamscores_flags[i]); } + return TRUE; } void ScoreInfo_Init(float teams) @@ -132,6 +140,7 @@ void ScoreInfo_Init(float teams) TeamScore_Spawn(COLOR_TEAM4, "Pink"); entity si; si = spawn(); + Net_LinkEntity(si); si.classname = "csqc_score_info"; si.SendEntity = ScoreInfo_SendEntity; si.Version = 1; @@ -141,7 +150,7 @@ void ScoreInfo_Init(float teams) * per-player score entities */ -void PlayerScore_SendEntity() +float PlayerScore_SendEntity() { float i; @@ -149,6 +158,8 @@ void PlayerScore_SendEntity() WriteByte(MSG_ENTITY, num_for_edict(self.owner)); for(i = 0; i < MAX_SCORE; ++i) WriteShort(MSG_ENTITY, self.scores[i]); + + return TRUE; } void PlayerScore_Clear(entity player) @@ -189,6 +200,14 @@ void Score_ClearAll() } } +float PlayerScore_IsValid(entity player) +{ + if(!player.scorekeeper) + return FALSE; + + return TRUE; +} + void PlayerScore_Attach(entity player) { entity sk; @@ -196,7 +215,9 @@ void PlayerScore_Attach(entity player) error("player already has a scorekeeper"); sk = spawn(); sk.owner = player; + sk.Version = 1; sk.SendEntity = PlayerScore_SendEntity; + Net_LinkEntity(sk); player.scorekeeper = sk; } diff --git a/data/qcsrc/server/scores.qh b/data/qcsrc/server/scores.qh index 68a8801c8..14631a223 100644 --- a/data/qcsrc/server/scores.qh +++ b/data/qcsrc/server/scores.qh @@ -1,6 +1,3 @@ -#define MAX_SCORE 10 -#define MAX_TEAMSCORE 2 - /** * Attaches a PlayerScore entity to a player. Use that in ClientConnect. * Remember to detach it in ClientDisconnect! @@ -19,6 +16,15 @@ void PlayerScore_Detach(entity player); */ void PlayerScore_Add(entity player, float scorefield, float score); +/** + * Check if the player can have scores. + * This is needed for example in keyhunt, when the carrier disconnects. + * The key-losing happens too late... which should probably be fixed + * but I'm just not in the mood to do that now as I'm busy making the + * csqc part work <.< + */ +float PlayerScore_IsValid(entity player); + /** * Initialize the score of this player if needed. * Does nothing in teamplay. @@ -68,20 +74,6 @@ void Score_ClearAll(); */ void Score_DebugPrint(); -/** - * Lower scores are better (e.g. deaths) - */ -#define SFL_DECREASING 1 - -/** - * Scoring priority (NOTE: PRIMARY is used for fraglimit) - */ -#define SFL_SORT_PRIO_LOW 2 -#define SFL_SORT_PRIO_MED 4 -#define SFL_SORT_PRIO_HIGH 8 -#define SFL_SORT_PRIO_PRIMARY 14 -#define SFL_SORT_PRIO_MASK 14 - /** * Sets the following results for the current scores entities. */ @@ -90,21 +82,3 @@ float WinningConditionHelper_topscore; ///< highest score float WinningConditionHelper_equality; ///< 1 if and only if the top two have equal scores float WinningConditionHelper_winnerteam; ///< the color of the winning team, or -1 if none entity WinningConditionHelper_winner; ///< the winning player, or world if none - -#define SP_KILLS 0 -#define SP_DEATHS 1 -#define SP_SUICIDES 2 -#define SP_SCORE 3 // personal score, game modes can set it their own way -#define ST_SCORE 0 - -#define SP_CTF_CAPS 4 -#define SP_CTF_RETURNS 5 -#define ST_CTF_CAPS 1 - -#define SP_KH_COLLECT 4 -#define SP_KH_LOSEKEY 5 -#define SP_KH_CAPS 6 -#define SP_KH_PUSH 7 -#define SP_KH_DESTROYED 8 -#define SP_KH_KCFRAG 9 -#define ST_KH_CAPS 1 -- 2.39.2