6 float sb_lines; // still don't know what to do with that NOTE: check dp's sbar.c to see what that should be
11 float sbar_hudselector;
21 entity team1, team2, team3, team4, teamspec;
26 void Sbar_FinaleOverlay()
29 pos_x = (vid_conwidth - 1)/2;
33 //drawpic(pos, "gfx/finale", '0 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL);
35 //drawstring(pos, "END", sbar_fontsize, '1 1 1', 1, DRAWFLAG_NORMAL);
39 void Sbar_DrawWeapon(float nr, float fade, float active)
41 vector pos, vsize, color;
44 value = (active) ? 1 : 0.6;
45 color_x = color_y = color_z = value;
47 if(sbar_hudselector == 1)
49 // width = 300, height = 100
50 const float w_width = 32, w_height = 12, w_space = 2, font_size = 8;
52 pos_x = (vid_conwidth - w_width * 9) * 0.5 + w_width * nr;
53 pos_y = (vid_conheight - w_height);
58 drawpic(pos, strcat("gfx/inv_weapon", ftos(nr)), vsize, color, value * fade * sbar_alpha_fg, 0);
64 drawstring(pos, ftos(nr+1), vsize, '1 1 0', sbar_alpha_fg, 0);
69 // width = 300, height = 100
70 const float w2_width = 300, w2_height = 100, w2_space = 10;
71 const float w2_scale = 0.4;
73 pos_x = vid_conwidth - (w2_width + w2_space) * w2_scale;
74 pos_y = (w2_height + w2_space) * w2_scale * nr + w2_space;
76 vsize_x = w2_width * w2_scale;
77 vsize_y = w2_height * w2_scale;
80 drawpic(pos, strcat("gfx/inv_weapon", ftos(nr)), vsize, color, value * fade * sbar_alpha_fg, 0);
83 void Sbar_DrawXNum (vector pos, float num, float digits, float lettersize, vector rgb, float a, float dflags)
90 vsize_x = vsize_y = lettersize;
105 str = strcat(substring("0000000000", 0, digits - strlen(tmp)), tmp);
113 str = substring(str, l-digits, 999);
115 } else if(l < digits)
116 pos_x += (digits-l) * lettersize;
120 drawpic(sbar + pos, "gfx/num_minus", vsize, rgb, a * sbar_alpha_fg, dflags);
124 for(i = 0; i < l; ++i)
126 drawpic(sbar + pos, strcat("gfx/num_", substring(str, i, 1)), vsize, rgb, a * sbar_alpha_fg, dflags);
131 float Sbar_PlayerCmp(entity l, entity r)
135 if(l.sb_team > r.sb_team)
137 else if(l.sb_team > r.sb_team)
139 if(gametype == GAME_CTF)
141 if(l.sb_caps > r.sb_caps)
143 else if(l.sb_caps < r.sb_caps)
147 if(l.sb_frags > r.sb_frags)
149 else if(l.sb_frags < r.sb_frags)
151 return (l.sb_player > r.sb_player);
153 float Sbar_TeamCmp(entity l, entity r)
155 if(gametype == GAME_CTF)
157 if(l.sb_caps > r.sb_caps)
159 else if(l.sb_caps < r.sb_caps)
162 if(l.sb_frags > r.sb_frags)
164 else if(l.sb_frags < r.sb_frags)
166 return (l.sb_player > r.sb_player);
171 sortedPlayers = Sort_New(Sbar_PlayerCmp);
172 sortedTeams = Sort_New(Sbar_TeamCmp);
173 team1 = Sort_Next(sortedTeams);
174 team1.sb_team = COLOR_TEAM1;
175 team2 = Sort_Next(sortedTeams);
176 team2.sb_team = COLOR_TEAM2;
177 team3 = Sort_Next(sortedTeams);
178 team3.sb_team = COLOR_TEAM3;
179 team4 = Sort_Next(sortedTeams);
180 team4.sb_team = COLOR_TEAM4;
181 teamspec = Sort_Next(sortedTeams);
182 teamspec.sb_team = COLOR_SPECTATOR;
185 void Sbar_UpdatePosFrags(entity player)
187 other = player.sort_prev;
188 while(other != sortedPlayers && player.sb_frags > other.sb_frags)
190 SORT_SWAP(other, player);
191 other = player.sort_prev;
194 other = player.sort_next;
195 while(other && player.sb_frags < other.sb_frags)
197 SORT_SWAP(player, other);
198 other = player.sort_next;
201 void Sbar_UpdatePosFragsCTF(entity player)
203 other = player.sort_prev;
204 while(other != sortedPlayers && player.sb_frags > other.sb_frags && player.sb_caps == other.sb_caps)
206 SORT_SWAP(other, player);
207 other = player.sort_prev;
210 other = player.sort_next;
211 while(other && player.sb_frags < other.sb_frags && player.sb_caps == other.sb_caps)
213 SORT_SWAP(player, other);
214 other = player.sort_next;
217 void Sbar_UpdatePosCaps(entity player)
219 other = player.sort_prev;
220 while(other != sortedPlayers && player.sb_caps > other.sb_caps)
222 SORT_SWAP(other, player);
223 other = player.sort_prev;
225 other = player.sort_next;
226 while(other && player.sb_caps < other.sb_caps)
228 SORT_SWAP(player, other);
229 other = player.sort_next;
231 // let it update the frags now too, so if we have more frags then the next guy with the same caps
232 // we beat his ass :)
233 player.sb_frags -= 1;
236 void Sbar_UpdatePosTeam(entity player)
238 player.sb_caps -= 1; // team change needs a full update
239 other = player.sort_prev;
240 while(other != sortedPlayers && player.sb_team > other.sb_team)
242 SORT_SWAP(other, player);
243 other = player.sort_prev;
246 other = player.sort_next;
247 while(other && player.sb_team < other.sb_team)
249 SORT_SWAP(player, other);
250 other = player.sort_next;
254 void Sbar_UpdatePlayer(entity player)
258 if(player.sb_frags == -666)
261 i = GetPlayerColor(player.sb_player);
263 if(player.sb_team != i)
266 Sbar_UpdatePosTeam(player);
269 if(gametype == GAME_CTF)
271 i = stof(bufstr_get(databuf, DATABUF_CAPTURES + player.sb_player));
272 if(player.sb_caps != i)
275 Sbar_UpdatePosCaps(player);
277 i = stof(getplayerkey(player.sb_player, "frags"));
278 if(player.sb_frags != i)
281 Sbar_UpdatePosFragsCTF(player);
284 i = stof(getplayerkey(player.sb_player, "frags"));
285 if(player.sb_frags != i)
288 Sbar_UpdatePosFrags(player);
293 void Sbar_UpdateTeamPosCaps(entity tm)
295 other = tm.sort_prev;
296 while(other != sortedTeams && tm.sb_caps > other.sb_caps)
298 SORT_SWAP(other, tm);
299 other = tm.sort_prev;
302 other = tm.sort_next;
303 while(other && tm.sb_caps < tm.sb_caps)
305 SORT_SWAP(tm, other);
306 other = tm.sort_next;
309 void Sbar_UpdateTeamPosFrags(entity tm)
311 other = tm.sort_prev;
312 while(other != sortedTeams && tm.sb_caps == other.sb_caps && tm.sb_frags > other.sb_frags)
314 SORT_SWAP(other, tm);
315 other = tm.sort_prev;
318 other = tm.sort_next;
319 while(other && tm.sb_caps == other.sb_caps && tm.sb_frags < other.sb_frags)
321 SORT_SWAP(tm, other);
322 other = tm.sort_next;
326 void Sbar_SortFrags()
330 float t1f, t2f, t3f, t4f;
332 Sort_Reset(sortedPlayers);
337 Sort_Reset(sortedTeams);
338 tmp = Sort_Next(sortedTeams);
344 teamspec.sb_player = 0;
346 t1f = team1.sb_frags;
347 t2f = team2.sb_frags;
348 t3f = team3.sb_frags;
349 t4f = team4.sb_frags;
356 for(i = 0; i < maxclients; ++i)
358 if(strlen(getplayerkey(i, "name")) <= 0)
361 Sort_Reset(sortedPlayers);
364 while(Sort_HasNext(sortedPlayers))
366 tmp = Sort_Next(sortedPlayers);
367 if(tmp.sb_player == i)
370 if(!tmp || tmp.sb_player != i)
371 tmp = Sort_Next(sortedPlayers);
375 Sbar_UpdatePlayer(tmp);
379 case COLOR_TEAM1: team1.sb_frags += tmp.sb_frags; team1.sb_player++; break;
380 case COLOR_TEAM2: team2.sb_frags += tmp.sb_frags; team2.sb_player++; break;
381 case COLOR_TEAM3: team3.sb_frags += tmp.sb_frags; team3.sb_player++; break;
382 case COLOR_TEAM4: team4.sb_frags += tmp.sb_frags; team4.sb_player++; break;
383 case COLOR_SPECTATOR: teamspec.sb_frags += tmp.sb_frags; teamspec.sb_player++; break;
386 if(i == player_localentnum-1)
387 myteam = tmp.sb_team;
389 if(team1.sb_player) ++numteams;
390 if(team2.sb_player) ++numteams;
391 if(team3.sb_player) ++numteams;
392 if(team4.sb_player) ++numteams;
394 if(team1.sb_caps != caps_team1)
396 team1.sb_caps = caps_team1;
397 Sbar_UpdateTeamPosCaps(team1);
399 if(team2.sb_caps != caps_team2)
401 team2.sb_caps = caps_team2;
402 Sbar_UpdateTeamPosCaps(team2);
404 if(team1.sb_frags != t1f) Sbar_UpdateTeamPosFrags(team1);
405 if(team2.sb_frags != t2f) Sbar_UpdateTeamPosFrags(team2);
406 if(team3.sb_frags != t3f) Sbar_UpdateTeamPosFrags(team3);
407 if(team4.sb_frags != t4f) Sbar_UpdateTeamPosFrags(team4);
409 for(i = 0; i < maxclients; ++i)
411 if(strlen(getplayerkey(i, "name")) <= 0)
414 Sort_Reset(sortedPlayers);
416 while(Sort_HasNext(sortedPlayers))
418 tmp = Sort_Next(sortedPlayers);
419 if(tmp.sb_player == i)
422 if(!tmp || tmp.sb_player != i)
423 tmp = Sort_Next(sortedPlayers);
427 Sbar_UpdatePlayer(tmp);
430 Sort_RemoveOld(sortedPlayers);
433 void Cmd_Sbar_Help(float argc)
435 print("You can modify the scoreboard using the\n");
436 print("^3|---------------------------------------------------------------|\n");
437 print("^2sbar_columns^7 cvar and the ^2sbar_columns_set command.\n");
438 print("^2sbar_columns^7 specifies the default layout and\n");
439 print("^2sbar_columns_set^7 actually changes the layout.\n");
440 print("You can call ^2sbar_columns_set^7 with the new layout\n");
441 print("as parameters, or eithout parameters it will read the cvar.\n\n");
443 print("^2sbar_columns_set ^7filed1 field2 ...\n");
444 print("Fields which are not relevant to the current gametype\n");
445 print("won't be displayed\n\n");
446 print("The following field names are recognized (case INsensitive):\n");
447 print("^3name^7 or ^3nick^7 Name of a player\n");
448 print("^3caps^7 or ^3captures^7 Number of flags captured\n");
449 print("^3rets^7 or ^3returns^7 Number of flags returned\n");
450 print("^3frags^7 or ^3kills^7 Frags\n");
451 print("^3deaths^7 or ^3dths^7 Number of deaths\n");
452 print("^3kd^7 or ^3kdr^7 or ^3kdratio^7 or ^3k/d\n");
453 print(" The kill-death ratio\n");
454 print("^3ping^7 Ping time\n\n");
455 print("You can use a ^3|^7 to start the right-aligned fields.\n");
456 print("Example: ping name | caps rets frags k/d\n");
457 print("This will put the ping and the name on the left side.\n");
458 print("The captures, returns, frags and kill-death ratio will be\n");
459 print("rendered beginning on the right side.\n");
463 #define MIN_NAMELEN 24
464 #define MAX_NAMELEN 24
466 void Cmd_Sbar_SetFields(float argc)
473 argc = tokenize(strcat("x ", cvar_string("sbar_columns")));
475 argc = min(MAX_SBAR_FIELDS, argc);
477 drawfont = sbar_font;
478 digit = stringwidth("0123456789", FALSE) / 10;
479 for(i = 0; i < argc-1; ++i)
482 strunzone(sbar_title[i]);
483 sbar_title[i] = strzone(str);
484 sbar_size[i] = stringwidth(str, FALSE);
485 str = strtolower(str);
487 sbar_field[i] = SBF_PING;
488 } else if(str == "name" || str == "nick") {
489 sbar_field[i] = SBF_NAME;
490 sbar_size[i] = MIN_NAMELEN; // minimum size? any use?
491 } else if(str == "caps" || str == "captures") {
492 if(sbar_size[i] < 3*digit)
493 sbar_size[i] = 3*digit;
494 sbar_field[i] = SBF_CAPS;
495 } else if(str == "rets" || str == "returns") {
496 if(sbar_size[i] < 3*digit)
497 sbar_size[i] = 3*digit;
498 sbar_field[i] = SBF_RETS;
499 } else if(str == "frags" || str == "kills") {
500 if(sbar_size[i] < 5*digit)
501 sbar_size[i] = 5*digit;
502 sbar_field[i] = SBF_FRAGS;
503 } else if(str == "deaths" || str == "dths") {
504 if(sbar_size[i] < 5*digit)
505 sbar_size[i] = 5*digit;
506 sbar_field[i] = SBF_DEATHS;
507 } else if(str == "kdratio") {
508 sbar_field[i] = SBF_KDRATIO;
509 } else if(str == "kdr" || str == "k/d") {
510 sbar_field[i] = SBF_KDRATIO;
511 } else if(str == "kd") {
512 sbar_field[i] = SBF_KDRATIO;
513 } else if(str == "|") {
514 sbar_field[i] = SBF_SEPARATOR;
516 print(strcat("^1Error:^7 Unknown score field: '", str, "'\n"));
521 sbar_field[i] = SBF_END;
524 vector sbar_field_rgb;
525 string Sbar_GetField(entity pl, float field)
529 sbar_field_rgb = '1 1 1';
533 str = bufstr_get(databuf, DATABUF_PING + pl.sb_player);
534 tmp = max(0, min(220, stof(str)-80)) / 220;
535 sbar_field_rgb = '1 1 1' - '0 1 1'*tmp;
537 case SBF_NAME: return getplayerkey(pl.sb_player, "name");
538 case SBF_CAPS: return ftos(pl.sb_caps);
539 case SBF_RETS: return bufstr_get(databuf, DATABUF_RETURNS + pl.sb_player);
540 case SBF_FRAGS: return ftos(pl.sb_frags);
541 case SBF_DEATHS: return bufstr_get(databuf, DATABUF_DEATHS + pl.sb_player);
543 tmp = stof(bufstr_get(databuf, DATABUF_DEATHS + pl.sb_player));
545 sbar_field_rgb = '0 1 0';
546 str = ftos(pl.sb_frags);
547 } else if(pl.sb_frags <= 0) {
548 sbar_field_rgb = '1 0 0';
549 str = ftos(pl.sb_frags / tmp);
551 str = ftos(pl.sb_frags / tmp);
553 tmp = strstrofs(str, ".", 0);
555 str = substring(str, 0, tmp+2);
559 #define SBAR_DEFAULT_MASK 0
560 #define SBAR_MASK_SPECTATORS 1
561 float Sbar_IsFieldMasked(float field, float mask)
563 if(mask&1) // spectator
564 return (field != SBF_NAME && field != SBF_PING);
565 if(gametype != GAME_CTF)
567 if(field == SBF_CAPS || field == SBF_RETS)
573 // shamelessly stolen from menu QC :P <- as if I would steal YOUR code pfft ;)
574 float textLengthUpToWidth(string theText, float maxWidth, float handleColors)
577 // The following function is SLOW.
578 // For your safety and for the protection of those around you...
579 // DO NOT CALL THIS AT HOME.
581 if(stringwidth(theText, handleColors) <= maxWidth)
582 return strlen(theText); // yeah!
584 // binary search for right place to cut string
585 float left, right, middle; // this always works
587 right = strlen(theText); // this always fails
590 middle = floor((left + right) / 2);
591 if(stringwidth(substring(theText, 0, middle), handleColors) <= maxWidth)
596 while(left < right - 1);
598 // NOTE: when color codes are involved, this binary search is,
599 // mathematically, BROKEN. However, it is obviously guaranteed to
600 // terminate, as the range still halves each time - but nevertheless, it is
601 // guaranteed that it finds ONE valid cutoff place (where "left" is in
602 // range, and "right" is outside).
606 string textShortenToWidth(string theText, float maxWidth, float handleColors)
608 if(stringwidth(theText, handleColors) <= maxWidth)
611 return strcat(substring(theText, 0, textLengthUpToWidth(theText, maxWidth - stringwidth("...", handleColors), handleColors)), "...");
614 float xmin, xmax, ymin, ymax, sbwidth, sbheight;
615 void Sbar_PrintScoreboardItem(vector pos, entity pl, float is_self, float mask)
626 tmp_y = sbar_fontsize_y;
627 drawfill(pos - '1 1', tmp + '2 2', '1 1 1', 0.3, DRAWFLAG_NORMAL);
631 for(i = 0; i < sbar_num_fields; ++i)
633 field = sbar_field[i];
634 if(field == SBF_SEPARATOR)
636 if(Sbar_IsFieldMasked(field, mask))
639 str = Sbar_GetField(pl, field);
641 if(field == SBF_NAME)
645 realsize = sbar_size[i];
646 if(i+1 < sbar_num_fields)
647 if(sbar_field[i+1] == SBF_SEPARATOR)
649 realsize = (xmax - xmin) / sbar_fontsize_x;
650 for(j = 0; j < sbar_num_fields; ++j) if(j != i) if(sbar_field[j] != SBF_SEPARATOR)
651 realsize -= sbar_size[j] + 1;
654 str = textShortenToWidth(str, realsize, TRUE);
656 len = stringwidth(str, TRUE);
658 if(sbar_size[i] < len)
661 pos_x += sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
662 if(field == SBF_NAME) {
663 tmp_x = sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
664 drawcolorcodedstring(pos - tmp, str, sbar_fontsize, 1, DRAWFLAG_NORMAL);
666 tmp_x = len*sbar_fontsize_x + sbar_fontsize_x;
667 drawstring(pos - tmp, str, sbar_fontsize, sbar_field_rgb, 1, DRAWFLAG_NORMAL);
671 if(sbar_field[i] == SBF_SEPARATOR)
674 for(i = sbar_num_fields-1; i > 0; --i)
676 field = sbar_field[i];
677 if(field == SBF_SEPARATOR)
679 if(Sbar_IsFieldMasked(field, mask))
682 str = Sbar_GetField(pl, field);
684 if(field == SBF_NAME)
685 str = textShortenToWidth(str, sbar_size[i], TRUE);
686 len = stringwidth(str, TRUE);
688 if(sbar_size[i] < len)
691 if(field == SBF_NAME) {
692 tmp_x = sbar_fontsize_x*len; // left or right aligned? let's put it right...
693 drawcolorcodedstring(pos - tmp, str, sbar_fontsize, 1, DRAWFLAG_NORMAL);
695 tmp_x = sbar_fontsize_x*len; //strlen(str);
696 drawstring(pos - tmp, str, sbar_fontsize, sbar_field_rgb, 1, DRAWFLAG_NORMAL);
698 pos_x -= sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
703 void Sbar_DrawScoreboard()
705 //float xmin, ymin, xmax, ymax;
706 vector rgb, pos, tmp, sbar_save;
711 sbar_fontsize = Sbar_GetFontsize();
712 if(sbar_fontsize_x == 0)
713 sbar_fontsize = '8 8 0';
714 if(sbar_fontsize_y == 0)
715 sbar_fontsize_y = sbar_fontsize_x;
717 xmin = vid_conwidth / 5;
720 xmax = vid_conwidth - xmin;
721 ymax = vid_conheight - 0.2*vid_conheight;
723 sbwidth = xmax - xmin;
724 sbheight = ymax - ymin;
726 center_x = xmin + 0.5*sbwidth;
728 //Sbar_UpdateFields();
730 // Initializes position
736 drawfont = sbar_font;
737 pos_x = center_x - stringwidth("Scoreboard", TRUE)*0.5*24;
738 drawstring(pos, "Scoreboard", '24 24', '1 1 1', 1, DRAWFLAG_NORMAL);
742 // Titlebar background:
744 tmp_y = sbar_fontsize_y;
745 drawfill(pos - '1 1', tmp + '2 2', '0.5 0.5 0.5', 0.5, DRAWFLAG_NORMAL);
747 for(i = 0; i < sbar_num_fields; ++i)
749 if(Sbar_IsFieldMasked(sbar_field[i], SBAR_DEFAULT_MASK))
751 if(sbar_field[i] == SBF_SEPARATOR)
753 drawstring(pos, sbar_title[i], sbar_fontsize, '1 1 1', 1, DRAWFLAG_NORMAL);
754 pos_x += sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
757 if(sbar_field[i] == SBF_SEPARATOR)
759 pos_x = xmax + sbar_fontsize_x;
761 for(i = sbar_num_fields-1; i > 0; --i)
763 if(Sbar_IsFieldMasked(sbar_field[i], SBAR_DEFAULT_MASK))
765 if(sbar_field[i] == SBF_SEPARATOR)
768 pos_x -= sbar_fontsize_x*sbar_size[i] + sbar_fontsize_x;
771 * Using the following line will fuck it all up:
773 * tmp_x = sbar_size[i] - strlen(sbar_title[i])*8;
775 tmp_x = sbar_fontsize_x*sbar_size[i];
776 tmp_x -= stringwidth(sbar_title[i], FALSE)*sbar_fontsize_x;
777 drawstring(pos + tmp, sbar_title[i], sbar_fontsize, '1 1 1', 1, DRAWFLAG_NORMAL);
782 pos_y += 1.5 * sbar_fontsize_y;
789 for(tm = sortedTeams.sort_next; tm; tm = tm.sort_next)
791 if(!tm.sb_player || tm.sb_team == COLOR_SPECTATOR) // no players in it?
794 rgb = GetTeamRGB(tm.sb_team);
797 if(gametype == GAME_CTF)
799 if(tm.sb_team == COLOR_TEAM1)
800 Sbar_DrawXNum(pos, caps_team1, 4, 24, rgb, 1, DRAWFLAG_NORMAL);
801 else if(tm.sb_team == COLOR_TEAM2)
802 Sbar_DrawXNum(pos, caps_team2, 4, 24, rgb, 1, DRAWFLAG_NORMAL);
804 Sbar_DrawXNum(pos + '0 24', tm.sb_frags, 4, 10, rgb, 1, DRAWFLAG_NORMAL);
807 Sbar_DrawXNum(pos, tm.sb_frags, 4, 24, rgb, 1, DRAWFLAG_NORMAL);
810 // abuse specs as playerounter
812 for(pl = sortedPlayers.sort_next; pl; pl = pl.sort_next)
814 if(pl.sb_team == tm.sb_team)
820 if(gametype == GAME_CTF && specs < 4)
824 tmp_y = 1.25 * sbar_fontsize_y * specs;
825 drawfill(pos - '1 1', tmp + '2 0', rgb, 0.2, DRAWFLAG_NORMAL);
827 for(pl = sortedPlayers.sort_next; pl; pl = pl.sort_next)
829 if(pl.sb_team != tm.sb_team)
831 Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), SBAR_DEFAULT_MASK);
832 pos_y += 1.25 * sbar_fontsize_y;
833 tmp_y -= 1.25 * sbar_fontsize_y;
835 pos_y += tmp_y + 1.5 * sbar_fontsize_y;
837 // rgb := tempvector :)
838 rgb = pos + '0 1.5 0' * sbar_fontsize_y;
839 pos_y += 3 * sbar_fontsize_y;
841 for(pl = sortedPlayers.sort_next; pl; pl = pl.sort_next)
843 if(pl.sb_team != COLOR_SPECTATOR)
845 //drawcolorcodedstring(pos, getplayerkey(pl.sb_player, "name"), '8 8 0', 1, 0);
846 Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), SBAR_MASK_SPECTATORS);
847 pos += '0 1.25 0' * sbar_fontsize_y;
852 drawstring(rgb, "Spectators", sbar_fontsize, '1 1 1', 1, 0);
855 for(pl = sortedPlayers.sort_next; pl; pl = pl.sort_next)
857 if(pl.sb_team == COLOR_SPECTATOR)
859 Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), SBAR_DEFAULT_MASK);
860 pos_y += 1.25 * sbar_fontsize_y;
861 tmp_y -= 1.25 * sbar_fontsize_y;
864 // rgb := tempvector :)
865 rgb = pos + '0 1.5 0' * sbar_fontsize_y;
866 pos_y += 3 * sbar_fontsize_y;
868 for(pl = sortedPlayers.sort_next; pl; pl = pl.sort_next)
870 if(pl.sb_team != COLOR_SPECTATOR)
872 //drawcolorcodedstring(pos, getplayerkey(pl.sb_player, "name"), '8 8 0', 1, 0);
873 Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), SBAR_MASK_SPECTATORS);
874 pos += '0 1.25 0' * sbar_fontsize_y;
879 drawstring(rgb, "Spectators", sbar_fontsize, '1 1 1', 1, 0);
884 void Sbar_Score(float margin)
886 float timelimit, timeleft, minutes, seconds, distribution, myplace;
887 vector sbar_save, place;
891 sbar_y = vid_conheight - (32+12);
902 //for(i = 0; i < 4; ++i)
903 for(tm = sortedTeams.sort_next; tm; tm = tm.sort_next)
905 if(tm.sb_team == COLOR_SPECTATOR || !tm.sb_player) // no players? don't display
908 if(tm.sb_team == myteam)
909 Sbar_DrawXNum('-128 0', tm.sb_frags, 4, 32, GetTeamRGB(tm.sb_team), 1, DRAWFLAG_NORMAL);
912 Sbar_DrawXNum(place, tm.sb_frags, 4, 12, GetTeamRGB(tm.sb_team), 1, DRAWFLAG_NORMAL);
917 // me vector := [team/connected frags id]
919 for(me = sortedPlayers.sort_next; me; me = me.sort_next)
921 if(me.sb_team != COLOR_SPECTATOR)
923 if(me.sb_player == player_localentnum - 1)
926 pl = sortedPlayers.sort_next;
930 if(pl && myplace != 1)
932 distribution = me.sb_frags - pl.sb_frags;
934 distribution = me.sb_frags - pl.sb_frags;
939 Sbar_DrawXNum('-36 -12', myplace, 3, 12, '1 1 1', 1, DRAWFLAG_NORMAL);
940 else if(myplace == 2)
941 Sbar_DrawXNum('-36 -12', myplace, 3, 12, '1 1 0', 1, DRAWFLAG_NORMAL);
943 Sbar_DrawXNum('-36 -12', myplace, 3, 12, '1 0 0', 1, DRAWFLAG_NORMAL);
945 if(distribution >= 0)
947 Sbar_DrawXNum('-84 -12', distribution, 4, 12, ' 1 1 1', 1, DRAWFLAG_NORMAL);
948 Sbar_DrawXNum('-128 0', me.sb_frags, 4, 32, '1 1 1', 1, DRAWFLAG_NORMAL);
949 } else if(distribution >= -5)
951 Sbar_DrawXNum('-84 -12', distribution, 4, 12, ' 1 1 0', 1, DRAWFLAG_NORMAL);
952 Sbar_DrawXNum('-128 0', me.sb_frags, 4, 32, '1 1 0', 1, DRAWFLAG_NORMAL);
954 Sbar_DrawXNum('-84 -12', distribution, 4, 12, ' 1 0 0', 1, DRAWFLAG_NORMAL);
955 Sbar_DrawXNum('-128 0', me.sb_frags, 4, 32, '1 0 0', 1, DRAWFLAG_NORMAL);
958 timelimit = getstatf(STAT_TIMELIMIT);
961 timeleft = max(0, timelimit * 60 - time);
962 minutes = floor(timeleft / 60);
963 seconds = floor(timeleft - minutes*60);
966 Sbar_DrawXNum('-72 32', minutes, 3, 12, '1 1 1', 1, DRAWFLAG_NORMAL);
967 drawpic(sbar + '-36 32', "gfx/num_colon", '12 12', '1 1 1', sbar_alpha_fg, 0);
968 Sbar_DrawXNum('-24 32', seconds, -2, 12, '1 1 1', 1, DRAWFLAG_NORMAL);
969 } else if(minutes >= 1)
971 Sbar_DrawXNum('-72 32', minutes, 3, 12, '1 1 0', 1, DRAWFLAG_NORMAL);
972 drawpic(sbar + '-36 32', "gfx/num_colon", '12 12', '1 1 0', sbar_alpha_fg, 0);
973 Sbar_DrawXNum('-24 32', seconds, -2, 12, '1 1 0', 1, DRAWFLAG_NORMAL);
975 Sbar_DrawXNum('-24 32', seconds, -2, 12, '1 0 0', 1, DRAWFLAG_NORMAL);
978 minutes = floor(time / 60);
979 seconds = floor(time - minutes*60);
980 Sbar_DrawXNum('-72 32', minutes, 3, 12, '1 1 1', 1, DRAWFLAG_NORMAL);
981 drawpic(sbar + '-36 32', "gfx/num_colon", '12 12', '1 1 1', sbar_alpha_fg, 0);
982 Sbar_DrawXNum('-24 32', seconds, -2, 12, '1 1 1', 1, DRAWFLAG_NORMAL);
987 void Sbar_MiniscoreItem(vector pos, entity pl, float is_self)
993 drawfill(pos + '0 1 0', '40 6 0', GetTeamRGB(pl.sb_team)*0.5, 1, DRAWFLAG_NORMAL);
995 drawfill(pos + '0 1 0', '40 6 0', '0.5 0.5 0.5', 0.5, DRAWFLAG_NORMAL);
998 pos_x -= stringwidth(ftos(pl.sb_frags), FALSE)*8;
999 drawstring(pos, ftos(pl.sb_frags), '8 8 0', '1 1 1', 1, DRAWFLAG_NORMAL);
1004 drawstring(pos, "\x0D", '8 8 0', '1 1 1', 1, DRAWFLAG_NORMAL);
1008 drawcolorcodedstring(pos, getplayerkey(pl.sb_player, "name"), '8 8 0', 1, 0);
1011 void Sbar_MiniscoreTeamItem(vector pos, float color, float frags, float is_self)
1017 drawfill(pos + '0 1 0', '40 6 0', GetTeamRGB(color)*0.5, 1, DRAWFLAG_NORMAL);
1019 drawfill(pos + '0 1 0', '40 6 0', '0.5 0.5 0.5', 0.5, DRAWFLAG_NORMAL);
1022 pos_x -= stringwidth(ftos(frags), FALSE)*8;
1023 drawstring(pos, ftos(frags), '8 8 0', '1 1 1', 1, DRAWFLAG_NORMAL);
1028 drawstring(pos, "\x0D", '8 8 0', '1 1 1', 1, DRAWFLAG_NORMAL);
1032 drawstring(pos, GetTeamName(color), '8 8 0', '1 1 1', 1, DRAWFLAG_NORMAL);
1035 void Sbar_MiniDeathmatchOverlay(vector pos)
1037 float numlines, up, down;
1039 float miniscoreboard_size;
1040 miniscoreboard_size = cvar("sbar_miniscoreboard_size");
1042 if(miniscoreboard_size == 0)
1044 pos_y = vid_conheight - 8;
1046 if(miniscoreboard_size < 0)
1047 numlines = (vid_conheight - sbar_y + 7) / 8;
1049 numlines = miniscoreboard_size;
1051 // give up if there isn't enough room
1052 if(pos_x >= vid_conwidth || pos_y >= vid_conheight || numlines < 1)
1055 // me vector := [team/connected frags id]
1056 for(me = sortedPlayers.sort_next; me; me = me.sort_next)
1058 if(me.sb_player == player_localentnum - 1)
1063 numlines -= numteams;
1065 // figure out how many players above and below we can show
1066 up = floor(numlines/2);
1068 if((up + down) > numlines)
1069 down = numlines - up;
1072 for(pl = me.sort_next; pl && down > 0; pl = pl.sort_next)
1074 if(pl.sb_team == COLOR_SPECTATOR)
1076 Sbar_MiniscoreItem(pos, pl, false);
1080 Sbar_MiniscoreItem(pos, me, true);
1082 up += down; // if there weren't enough lines below... add them
1083 for(pl = me.sort_prev; pl != sortedPlayers && up > 0; pl = pl.sort_prev)
1085 if(pl.sb_team == COLOR_SPECTATOR)
1087 Sbar_MiniscoreItem(pos, pl, false);
1094 for(tm = sortedTeams.sort_next; tm.sort_next; tm = tm.sort_next);
1095 for(; tm != sortedTeams; tm = tm.sort_prev)
1097 if(!tm.sb_player || tm.sb_team == COLOR_SPECTATOR) // no players?
1099 Sbar_MiniscoreTeamItem(pos, tm.sb_team, tm.sb_frags, (tm.sb_team == me.sb_team));
1105 void Sbar_Draw (void)
1116 Sbar_DrawScoreboard();
1117 else if (intermission == 1)
1119 Sbar_DrawScoreboard();
1122 else if (intermission == 2)
1123 Sbar_FinaleOverlay();
1126 if (sb_showscores || (getstati(STAT_HEALTH) <= 0 && cvar("cl_deathscoreboard")))
1128 sbar_x = (vid_conwidth - 640.0)*0.5;
1129 sbar_y = vid_conheight - 47;
1130 //Sbar_DrawAlphaPic (sbar_x, sbar_y, sb_scorebar, sbar_alpha_bg.value);
1131 //drawpic('0 0', "gfx/scorebar", '0 0 0', '1 1 1', cvar("sbar_alpha_bg"), 0);
1132 Sbar_DrawScoreboard ();
1136 if (sb_lines && sbar_hudselector == 1)
1138 stat_items = getstati(STAT_ITEMS);
1140 sbar_x = (vid_conwidth - 320.0)*0.5;
1141 sbar_y = vid_conheight - 24.0 - 16.0;
1144 fade = 3.2 - 2 * (time - weapontime);
1145 fade = bound(0.7, fade, 1);
1148 for(i = 0; i < 8; ++i)
1152 Sbar_DrawWeapon(i+1, fade, (i + 2 == activeweapon));
1159 Sbar_DrawWeapon(0, fade, (activeweapon == 1));
1163 x = getstati(STAT_ARMOR);
1167 //Sbar_DrawStretchPic (72, 0, sb_armor[0], sbar_alpha_fg.value, 24, 24);
1168 drawpic(sbar + '72 0', "gfx/sb_armor", '24 24 0', '1 1 1', sbar_alpha_fg, 0);
1170 Sbar_DrawXNum('0 0', x, 3, 24, '0 1 0', 1, 0);
1172 Sbar_DrawXNum('0 0', x, 3, 24, '0.2 1 0', 1, 0);
1174 Sbar_DrawXNum('0 0', x, 3, 24, '0.6 0.7 0.8', 1, 0);
1176 Sbar_DrawXNum('0 0', x, 3, 24, '1 1 0.2', 1, 0);
1178 Sbar_DrawXNum('0 0', x, 3, 24, '0.7 0 0', 1, 0);
1182 x = getstati(STAT_HEALTH);
1186 //Sbar_DrawStretchPic (184, 0, sb_health, sbar_alpha_fg.value, 24, 24);
1187 drawpic(sbar + '184 0', "gfx/sb_health", '24 24 0', '1 1 1', sbar_alpha_fg, 0);
1189 Sbar_DrawXNum('112 0', x, 3, 24, '0 1 0', 1, 0);
1191 Sbar_DrawXNum('112 0', x, 3, 24, '0.2 1 0', 1, 0);
1193 Sbar_DrawXNum('112 0', x, 3, 24, '0.6 0.7 0.8', 1, 0);
1195 Sbar_DrawXNum('112 0', x, 3, 24, '1 1 0.2', 1, 0);
1197 Sbar_DrawXNum('112 0', x, 3, 24, '0.7 0 0', 1, 0);
1201 x = getstati(STAT_AMMO);
1202 if ((stat_items & (NEX_IT_SHELLS | NEX_IT_BULLETS | NEX_IT_ROCKETS | NEX_IT_CELLS)) || x != 0)
1204 if (stat_items & NEX_IT_SHELLS)
1205 drawpic(sbar + '296 0', "gfx/sb_shells", '24 24 0', '1 1 1', sbar_alpha_fg, 0);
1206 else if (stat_items & NEX_IT_BULLETS)
1207 drawpic(sbar + '296 0', "gfx/sb_bullets", '24 24 0', '1 1 1', sbar_alpha_fg, 0);
1208 else if (stat_items & NEX_IT_ROCKETS)
1209 drawpic(sbar + '296 0', "gfx/sb_rocket", '24 24 0', '1 1 1', sbar_alpha_fg, 0);
1210 else if (stat_items & NEX_IT_CELLS)
1211 drawpic(sbar + '296 0', "gfx/sb_cells", '24 24 0', '1 1 1', sbar_alpha_fg, 0);
1213 Sbar_DrawXNum('224 0', x, 3, 24, '0.6 0.7 0.8', 1, 0);
1215 Sbar_DrawXNum('224 0', x, 3, 24, '0.7 0 0', 1, 0);
1218 if (sbar_x + 320 + 160 <= vid_conwidth)
1219 Sbar_MiniDeathmatchOverlay(sbar + '320 0');
1222 // The margin can be at most 8 to support 640x480 console size:
1223 // 320 + 2 * (144 + 16) = 640
1228 stat_items = getstati(STAT_ITEMS);
1230 sbar_x = (vid_conwidth - 640.0)*0.5;
1231 sbar_y = vid_conheight - 47;
1234 fade = 3 - 2 * (time - weapontime);
1237 for(i = 0; i < 8; ++i)
1241 Sbar_DrawWeapon(i+1, fade, (i + 2 == activeweapon));
1248 Sbar_DrawWeapon(0, fade, (activeweapon == 1));
1252 drawpic(sbar, "gfx/sbar", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
1254 drawpic(sbar, "gfx/sbar_minimal", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
1258 Sbar_DrawXNum('268 12', getstati(STAT_ARMOR), 3, 24, '0.6 0.7 0.8', 1, 0);
1262 x = getstati(STAT_HEALTH);
1264 Sbar_DrawXNum('82 12', x, 3, 24, '1 1 1', 1, 0);
1265 else if(x <= 25 && time - floor(time) > 0.5)
1266 Sbar_DrawXNum('82 12', x, 3, 24, '0.7 0 0', 1, 0);
1268 Sbar_DrawXNum('81 12', x, 3, 24, '0.6 0.7 0.8', 1, 0);
1270 // AK dont draw ammo for the laser
1271 x = getstati(STAT_AMMO);
1272 if(activeweapon != 12)
1275 if (stat_items & NEX_IT_SHELLS)
1276 drawpic(sbar + '519 0', "gfx/sb_shells", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
1277 else if (stat_items & NEX_IT_BULLETS)
1278 drawpic(sbar + '519 0', "gfx/sb_bullets", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
1279 else if (stat_items & NEX_IT_ROCKETS)
1280 drawpic(sbar + '519 0', "gfx/sb_rocket", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
1281 else if (stat_items & NEX_IT_CELLS)
1282 drawpic(sbar + '519 0', "gfx/sb_cells", '0 0 0', '1 1 1', sbar_alpha_fg, 0);
1284 Sbar_DrawXNum('447 12', x, 3, 24, '0.6 0.7 0.8', 1, 0);
1286 Sbar_DrawXNum('447 12', x, 3, 24, '0.7 0 0', 1, 0);
1290 drawpic(sbar, "gfx/sbar_overlay", '0 0 0', '1 1 1', 1, DRAWFLAG_MODULATE);
1292 if (sbar_x + 600 + 160 <= vid_conwidth)
1293 Sbar_MiniDeathmatchOverlay (sbar + '600 0');
1298 // Mini scoreboard uses 12*4 per other team, that is, 144
1299 // pixels when there are four teams...
1300 // Nexuiz by default sets vid_conwidth to 800... makes
1302 // so we need to shift it by 64 pixels to the right to fit
1303 // BUT: then it overlaps with the image that gets drawn
1304 // for viewsize 100! Therefore, just account for 3 teams,
1305 // that is, 96 pixels mini scoreboard size, needing 16 pixels
1310 if(gametype == GAME_KEYHUNT)
1313 } else if(gametype == GAME_CTF)
1321 void CSQC_ctf_hud(void)
1323 // cvar("sbar_flagstatus_right") move the flag icons right
1324 // cvar("sbar_flagstatus_pos") pixel position of the nexuiz flagstatus icons
1325 float redflag, blueflag;
1329 stat_items = getstati(STAT_ITEMS);
1330 redflag = (stat_items/32768) & 3;
1331 blueflag = (stat_items/131072) & 3;
1335 * For some reason now not even THAT works there...
1336 * Maybe the minus' precedence screws it up? The last one there, maybe I should use brackets
1338 * pos_x = (cvar("sbar_flagstatus_right")) ? vid_conwidth - 10 - sbar_x - 64 : 10 - sbar_x;
1339 ** Should try those later:
1340 * pos_x = (cvar("sbar_flagstatus_right")) ? (vid_conwidth - 10 - sbar_x - 64) : (10 - sbar_x);
1341 * pos_x = ( (cvar("sbar_flagstatus_right")) ? vid_conwidth - 10 - 64 : 10 ) - sbar_x;
1344 if(cvar("sbar_flagstatus_right"))
1345 pos_x = vid_conwidth - 10 - sbar_x - 64;
1347 pos_x = 10 - sbar_x;
1351 if(sbar_hudselector == 1)
1352 pos_y = (vid_conheight - sbar_y) - cvar("sbar_flagstatus_pos") - 64;
1360 case 1: drawpic(pos, "gfx/sb_flag_red_taken", '0 0 0', '1 1 1', 1, DRAWFLAG_NORMAL); break;
1361 case 2: drawpic(pos, "gfx/sb_flag_red_lost", '0 0 0', '1 1 1', 1, DRAWFLAG_NORMAL); break;
1362 case 3: drawpic(pos, "gfx/sb_flag_red_carrying", '0 0 0', '1 1 1', 1, DRAWFLAG_NORMAL); break;
1369 case 1: drawpic(pos, "gfx/sb_flag_blue_taken", '0 0 0', '1 1 1', 1, 0); break;
1370 case 2: drawpic(pos, "gfx/sb_flag_blue_lost", '0 0 0', '1 1 1', 1, 0); break;
1371 case 3: drawpic(pos, "gfx/sb_flag_blue_carrying", '0 0 0', '1 1 1', 1, 0); break;